diff --git a/loans/android/build.gradle b/loans/android/build.gradle index 5243d376..d6f2cad1 100644 --- a/loans/android/build.gradle +++ b/loans/android/build.gradle @@ -64,5 +64,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.smallcase.loans:sdk:5.1.3' + implementation 'com.smallcase.loans:sdk-sourav-native-dark-theme-cb54bf9:5.1.4-92-release' } diff --git a/loans/android/src/main/kotlin/com/example/scloans/ScLoanFlutterPlugin.kt b/loans/android/src/main/kotlin/com/example/scloans/ScLoanFlutterPlugin.kt index 5725d509..697581b9 100644 --- a/loans/android/src/main/kotlin/com/example/scloans/ScLoanFlutterPlugin.kt +++ b/loans/android/src/main/kotlin/com/example/scloans/ScLoanFlutterPlugin.kt @@ -58,6 +58,21 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } + // colorScheme rides the bridge as a primitive string and is mapped back to the native + // enum here. Unknown/absent → null so the SDK keeps its light-default behaviour. + private fun parseColorScheme(value: String?): ScLoanColorScheme? = when (value) { + "dark" -> ScLoanColorScheme.DARK + "light" -> ScLoanColorScheme.LIGHT + "system" -> ScLoanColorScheme.SYSTEM + else -> null + } + + private fun buildLoanInfo(call: MethodCall): ScLoanInfo { + val interactionToken: String = call.argument("interactionToken") ?: "" + val colorScheme = parseColorScheme(call.argument("colorScheme")) + return ScLoanInfo(interactionToken, colorScheme) + } + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { val safeResult = ScLoanMethodChannelResult(result, activity) @@ -88,11 +103,9 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { ) } "apply" -> { - val interactionToken: String? = call.argument("interactionToken") - ScLoan.apply( activity, - ScLoanInfo(interactionToken ?: ""), + buildLoanInfo(call), listener = object : ScLoanResult { override fun onFailure(error: ScLoanError) { safeResult.error(error.code.toString(), error.message, error.toString()) @@ -106,11 +119,9 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { ) } "pay" -> { - val interactionToken: String? = call.argument("interactionToken") - ScLoan.pay( activity, - ScLoanInfo(interactionToken ?: ""), + buildLoanInfo(call), listener = object : ScLoanResult { override fun onFailure(error: ScLoanError) { safeResult.error(error.code.toString(), error.message, error.toString()) @@ -124,11 +135,9 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { ) } "withdraw" -> { - val interactionToken: String? = call.argument("interactionToken") - ScLoan.withdraw( activity, - ScLoanInfo(interactionToken ?: ""), + buildLoanInfo(call), listener = object : ScLoanResult { override fun onFailure(error: ScLoanError) { safeResult.error(error.code.toString(), error.message, error.toString()) @@ -142,11 +151,9 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { ) } "service" -> { - val interactionToken: String? = call.argument("interactionToken") - ScLoan.service( activity, - ScLoanInfo(interactionToken ?: ""), + buildLoanInfo(call), listener = object : ScLoanResult { override fun onFailure(error: ScLoanError) { safeResult.error(error.code.toString(), error.message, error.toString()) @@ -160,11 +167,9 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { ) } "triggerInteraction" -> { - val interactionToken: String? = call.argument("interactionToken") - ScLoan.triggerInteraction( activity, - ScLoanInfo(interactionToken ?: ""), + buildLoanInfo(call), listener = object : ScLoanResult { override fun onFailure(error: ScLoanError) { safeResult.error(error.code.toString(), error.message, error.toString()) diff --git a/loans/ios/Classes/ScLoanFlutterPlugin.swift b/loans/ios/Classes/ScLoanFlutterPlugin.swift index b388c93e..0c167d7c 100644 --- a/loans/ios/Classes/ScLoanFlutterPlugin.swift +++ b/loans/ios/Classes/ScLoanFlutterPlugin.swift @@ -57,28 +57,46 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { registrar.addMethodCallDelegate(instance, channel: channel) } + // colorScheme rides the bridge as a primitive string and is mapped back to the native + // enum here. Unknown/absent → token-only initializer so the SDK keeps its light-default. + private func loanInfo(from args: Dictionary) -> ScLoanInfo? { + guard let interactionToken = args["interactionToken"] as? String else { return nil } + if let scheme = args["colorScheme"] as? String { + switch scheme { + case "dark": + return ScLoanInfo(interactionToken: interactionToken, colorScheme: .dark) + case "light": + return ScLoanInfo(interactionToken: interactionToken, colorScheme: .light) + case "system": + return ScLoanInfo(interactionToken: interactionToken, colorScheme: .system) + default: + break + } + } + return ScLoanInfo(interactionToken: interactionToken) + } + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - + guard let args = call.arguments as? Dictionary else { return } - + switch (call.method) { - + case "setup": do { guard let gateway = args["gateway"] as? String else { - //SDK setup failure print("ScLoan setup failed: No gateway name provided") return } - + var env = SCLoanEnvironment.production - + if let hostEnv = args["env"] as? Int { env = SCLoanEnvironment(rawValue: hostEnv) ?? .production } - + ScLoan.instance.setup( config: ScLoanConfig(gatewayName: gateway, environment: env)) { success, error in - + if let err = error { result(self.convertErrorToFlutterError(error: err)) } else { @@ -86,18 +104,17 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { } } } - + case "apply": do { - guard let interactionToken = args["interactionToken"] as? String else { - //SDK setup failure + guard let info = loanInfo(from: args) else { print("ScLoan apply failed: No interactionToken provided") return } - + ScLoan.instance.apply( presentingController: currentViewController, - loanInfo: ScLoanInfo(interactionToken: interactionToken)) { success, error in - + loanInfo: info) { success, error in + if let err = error { result(self.convertErrorToFlutterError(error: err)) } else { @@ -105,18 +122,17 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { } } } - + case "pay": do { - guard let interactionToken = args["interactionToken"] as? String else { - //SDK setup failure + guard let info = loanInfo(from: args) else { print("ScLoan pay failed: No interactionToken provided") return } - + ScLoan.instance.pay( presentingController: currentViewController, - loanInfo: ScLoanInfo(interactionToken: interactionToken)) { success, error in - + loanInfo: info) { success, error in + if let err = error { result(self.convertErrorToFlutterError(error: err)) } else { @@ -124,18 +140,17 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { } } } - + case "withdraw": do { - guard let interactionToken = args["interactionToken"] as? String else { - //SDK setup failure + guard let info = loanInfo(from: args) else { print("ScLoan withdraw failed: No interactionToken provided") return } - + ScLoan.instance.withdraw( presentingController: currentViewController, - loanInfo: ScLoanInfo(interactionToken: interactionToken)) { success, error in - + loanInfo: info) { success, error in + if let err = error { result(self.convertErrorToFlutterError(error: err)) } else { @@ -143,18 +158,17 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { } } } - + case "service": do { - guard let interactionToken = args["interactionToken"] as? String else { - //SDK setup failure + guard let info = loanInfo(from: args) else { print("ScLoan service failed: No interactionToken provided") return } - + ScLoan.instance.service( presentingController: currentViewController, - loanInfo: ScLoanInfo(interactionToken: interactionToken)) { success, error in - + loanInfo: info) { success, error in + if let err = error { result(self.convertErrorToFlutterError(error: err)) } else { @@ -163,16 +177,15 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { } } case "triggerInteraction": do { - guard let interactionToken = args["interactionToken"] as? String else { - //SDK setup failure + guard let info = loanInfo(from: args) else { print("ScLoan triggerInteraction failed: No interactionToken provided") return } - + ScLoan.instance.triggerInteraction( presentingController: currentViewController, - loanInfo: ScLoanInfo(interactionToken: interactionToken)) { success, error in - + loanInfo: info) { success, error in + if let err = error { result(self.convertErrorToFlutterError(error: err)) } else { @@ -180,7 +193,7 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { } } } - + default: result("Flutter method not implemented on iOS") } diff --git a/loans/ios/scloans.podspec b/loans/ios/scloans.podspec index 955c072d..cd118af4 100644 --- a/loans/ios/scloans.podspec +++ b/loans/ios/scloans.podspec @@ -19,5 +19,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } s.swift_version = '5.0' - s.dependency 'SCLoans', '7.2.0' + # INTERNAL TEST PIN: dark-theme branch build (mirrors the android/build.gradle pin). + # Revert to: s.dependency 'SCLoans', '7.2.0' + s.dependency 'SCLoans-sourav-native-dark-theme-37c97d9', '7.1.2-45-release' end diff --git a/loans/lib/sc_loan.dart b/loans/lib/sc_loan.dart index 32c8fbf9..bf8ceff5 100644 --- a/loans/lib/sc_loan.dart +++ b/loans/lib/sc_loan.dart @@ -22,12 +22,19 @@ class ScLoan { } } + static Map _loanInfoArgs(ScLoanInfo loanInfo) { + return { + "interactionToken": loanInfo.interactionToken, + // TODO: use explicit value mapping, not enum .name (fragile to renames). + if (loanInfo.colorScheme != null) + "colorScheme": loanInfo.colorScheme!.name, + }; + } + static Future apply(ScLoanInfo loanInfo) async { try { String? applyResponse = - await _channel.invokeMethod('apply', { - "interactionToken": loanInfo.interactionToken, - }); + await _channel.invokeMethod('apply', _loanInfoArgs(loanInfo)); return ScLoanSuccess.fromJson(applyResponse ?? "{}"); } on PlatformException catch (e) { throw e.toScLoanError; @@ -37,9 +44,7 @@ class ScLoan { static Future pay(ScLoanInfo loanInfo) async { try { String? payResponse = - await _channel.invokeMethod('pay', { - "interactionToken": loanInfo.interactionToken, - }); + await _channel.invokeMethod('pay', _loanInfoArgs(loanInfo)); return ScLoanSuccess.fromJson(payResponse ?? "{}"); } on PlatformException catch (e) { throw e.toScLoanError; @@ -49,9 +54,7 @@ class ScLoan { static Future withdraw(ScLoanInfo loanInfo) async { try { String? withdrawResponse = - await _channel.invokeMethod('withdraw', { - "interactionToken": loanInfo.interactionToken, - }); + await _channel.invokeMethod('withdraw', _loanInfoArgs(loanInfo)); return ScLoanSuccess.fromJson(withdrawResponse ?? "{}"); } on PlatformException catch (e) { throw e.toScLoanError; @@ -61,9 +64,7 @@ class ScLoan { static Future service(ScLoanInfo loanInfo) async { try { String? serviceResponse = - await _channel.invokeMethod('service', { - "interactionToken": loanInfo.interactionToken, - }); + await _channel.invokeMethod('service', _loanInfoArgs(loanInfo)); return ScLoanSuccess.fromJson(serviceResponse ?? "{}"); } on PlatformException catch (e) { throw e.toScLoanError; @@ -72,10 +73,8 @@ class ScLoan { static Future triggerInteraction(ScLoanInfo loanInfo) async { try { - String? triggerInteractionResponse = - await _channel.invokeMethod('triggerInteraction', { - "interactionToken": loanInfo.interactionToken, - }); + String? triggerInteractionResponse = await _channel.invokeMethod( + 'triggerInteraction', _loanInfoArgs(loanInfo)); return ScLoanSuccess.fromJson(triggerInteractionResponse ?? "{}"); } on PlatformException catch (e) { throw e.toScLoanError; diff --git a/loans/lib/sc_loan_props.dart b/loans/lib/sc_loan_props.dart index 0d7c7a85..8b51e60e 100644 --- a/loans/lib/sc_loan_props.dart +++ b/loans/lib/sc_loan_props.dart @@ -1,6 +1,8 @@ // ignore: constant_identifier_names enum ScLoanEnvironment { DEVELOPMENT, PRODUCTION, STAGING } +enum ScLoanColorScheme { dark, light, system } + class ScLoanConfig { final ScLoanEnvironment environment; final String gateway; @@ -10,6 +12,7 @@ class ScLoanConfig { class ScLoanInfo { final String interactionToken; + final ScLoanColorScheme? colorScheme; - const ScLoanInfo(this.interactionToken); -} \ No newline at end of file + const ScLoanInfo(this.interactionToken, {this.colorScheme}); +} diff --git a/smart_investing/lib/app/SILoansPage.dart b/smart_investing/lib/app/SILoansPage.dart index 2c9bee26..40f323d0 100644 --- a/smart_investing/lib/app/SILoansPage.dart +++ b/smart_investing/lib/app/SILoansPage.dart @@ -10,9 +10,30 @@ import 'widgets/SISwitch.dart'; import 'widgets/SIText.dart'; import 'widgets/SITextField.dart'; -class SILoansPage extends StatelessWidget { +class SILoansPage extends StatefulWidget { const SILoansPage({super.key}); + @override + State createState() => _SILoansPageState(); +} + +class _SILoansPageState extends State { + static const _colorSchemeLabels = ['None', 'Light', 'Dark', 'System']; + static const _colorSchemeValues = [ + null, + ScLoanColorScheme.light, + ScLoanColorScheme.dark, + ScLoanColorScheme.system, + ]; + + int _colorSchemeIndex = 0; + + ScLoanInfo _buildLoanInfo() { + final token = repository.scLoanConfig.value.customInteractionToken ?? ""; + final scheme = _colorSchemeValues[_colorSchemeIndex]; + return ScLoanInfo(token, colorScheme: scheme); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -58,6 +79,31 @@ class SILoansPage extends StatelessWidget { ); }, ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SIText.large(text: "Color Scheme"), + const SizedBox(height: 6), + ToggleButtons( + isSelected: List.generate( + _colorSchemeLabels.length, + (i) => i == _colorSchemeIndex, + ), + onPressed: (i) => setState(() => _colorSchemeIndex = i), + borderRadius: BorderRadius.circular(8), + children: _colorSchemeLabels + .map((label) => Padding( + padding: + const EdgeInsets.symmetric(horizontal: 14), + child: Text(label), + )) + .toList(), + ), + ], + ), + ), Wrap( spacing: 12, children: [ @@ -78,9 +124,7 @@ class SILoansPage extends StatelessWidget { label: "Apply", onPressed: () async { try { - final response = await ScLoan.apply(ScLoanInfo(repository - .scLoanConfig.value.customInteractionToken ?? - "")); + final response = await ScLoan.apply(_buildLoanInfo()); repository.showAlertDialog(response.toString(), context); } on ScLoanError catch (e) { repository.showAlertDialog(e.toString(), context); @@ -91,9 +135,7 @@ class SILoansPage extends StatelessWidget { label: "Pay", onPressed: () async { try { - final response = await ScLoan.pay(ScLoanInfo(repository - .scLoanConfig.value.customInteractionToken ?? - "")); + final response = await ScLoan.pay(_buildLoanInfo()); repository.showAlertDialog( response.toString(), context); } on ScLoanError catch (e) { @@ -104,10 +146,8 @@ class SILoansPage extends StatelessWidget { label: "Withdraw", onPressed: () async { try { - final response = await ScLoan.withdraw(ScLoanInfo( - repository.scLoanConfig.value - .customInteractionToken ?? - "")); + final response = + await ScLoan.withdraw(_buildLoanInfo()); repository.showAlertDialog( response.toString(), context); } on ScLoanError catch (e) { @@ -118,24 +158,20 @@ class SILoansPage extends StatelessWidget { label: "Service", onPressed: () async { try { - final response = await ScLoan.service(ScLoanInfo( - repository.scLoanConfig.value - .customInteractionToken ?? - "")); + final response = + await ScLoan.service(_buildLoanInfo()); repository.showAlertDialog( response.toString(), context); } on ScLoanError catch (e) { repository.showAlertDialog(e.toString(), context); } }), - SIButton( + SIButton( label: "Trigger Interaction", onPressed: () async { try { - final response = await ScLoan.triggerInteraction(ScLoanInfo( - repository.scLoanConfig.value - .customInteractionToken ?? - "")); + final response = + await ScLoan.triggerInteraction(_buildLoanInfo()); repository.showAlertDialog( response.toString(), context); } on ScLoanError catch (e) {