diff --git a/loans/android/build.gradle b/loans/android/build.gradle index 3d16abf3..5a10b367 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:3.1.1" + implementation "com.smallcase.loans:sdk-release-scg-v5-0-0-loans-v4-0-0-1df0c05:3.1.1-81-release" } diff --git a/loans/android/src/main/kotlin/com/example/scloans/ScLoanEventsHandler.kt b/loans/android/src/main/kotlin/com/example/scloans/ScLoanEventsHandler.kt new file mode 100644 index 00000000..ab1bc49a --- /dev/null +++ b/loans/android/src/main/kotlin/com/example/scloans/ScLoanEventsHandler.kt @@ -0,0 +1,73 @@ +package com.example.scloans + +import android.util.Log +import com.smallcase.loans.data.listeners.Notification +import com.smallcase.loans.data.listeners.NotificationCenter +import com.smallcase.loans.core.external.ScLoanNotification +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.EventChannel + + +class ScLoanEventsHandler : FlutterPlugin { + + private val TAG = "ScLoanEventsHandler" + private lateinit var eventChannel: EventChannel + private lateinit var eventStreamHandler: ScLoanEventsStreamHandler + private var notificationObserver: ((Notification) -> Unit)? = null + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + setup(flutterPluginBinding) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + cleanup() + } + + private fun setup(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + eventStreamHandler = ScLoanEventsStreamHandler() + eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "scloans_events") + eventChannel.setStreamHandler(eventStreamHandler) + + setupNotificationListener() + Log.d(TAG, "ScLoan events channel setup complete") + } + + private fun setupNotificationListener() { + notificationObserver?.let { NotificationCenter.removeObserver(it) } + + notificationObserver = { notification -> + try { + val jsonString = notification.userInfo?.get(ScLoanNotification.STRINGIFIED_PAYLOAD_KEY) as? String + if (jsonString != null) { + Log.d(TAG, "Sending notification to Flutter: $jsonString") + eventStreamHandler.sendEvent(jsonString) + } else { + Log.w(TAG, "Notification has no JSON payload") + } + } catch (e: Exception) { + Log.e(TAG, "Error processing notification: ${e.message}") + eventStreamHandler.sendError("NOTIFICATION_PARSE_ERROR", e.message, e.toString()) + } + } + + notificationObserver?.let { + NotificationCenter.addObserver(it) + Log.d(TAG, "Notification observer added") + } + } + + private fun cleanup() { + try { + notificationObserver?.let { + NotificationCenter.removeObserver(it) + Log.d(TAG, "Notification observer removed") + } + } catch (e: Exception) { + Log.e(TAG, "Error removing notification observer: ${e.message}") + } + + notificationObserver = null + eventChannel.setStreamHandler(null) + Log.d(TAG, "SCGateway events cleanup complete") + } +} \ No newline at end of file diff --git a/loans/android/src/main/kotlin/com/example/scloans/ScLoanEventsStreamHandler.kt b/loans/android/src/main/kotlin/com/example/scloans/ScLoanEventsStreamHandler.kt new file mode 100644 index 00000000..9e8d9381 --- /dev/null +++ b/loans/android/src/main/kotlin/com/example/scloans/ScLoanEventsStreamHandler.kt @@ -0,0 +1,34 @@ +package com.example.scloans + +import android.os.Handler +import android.os.Looper +import io.flutter.plugin.common.EventChannel + +class ScLoanEventsStreamHandler: EventChannel.StreamHandler { + + private var eventSink: EventChannel.EventSink? = null + private val mainHandler = Handler(Looper.getMainLooper()) + + override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { + eventSink = events + } + + override fun onCancel(arguments: Any?) { + eventSink = null + } + + // Call this method to send data to Flutter + fun sendEvent(data: Any) { + mainHandler.post { + eventSink?.success(data) + } + } + + // Call this method to send error to Flutter + fun sendError(errorCode: String, errorMessage: String?, errorDetails: Any? = null) { + mainHandler.post { + eventSink?.error(errorCode, errorMessage, errorDetails) + } + } +} + 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..206318ca 100644 --- a/loans/android/src/main/kotlin/com/example/scloans/ScLoanFlutterPlugin.kt +++ b/loans/android/src/main/kotlin/com/example/scloans/ScLoanFlutterPlugin.kt @@ -11,9 +11,14 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin import com.google.gson.Gson import com.smallcase.loans.core.external.* import com.smallcase.loans.core.external.ScLoanEnvironment +import com.smallcase.loans.core.external.ScLoan +import com.smallcase.loans.core.external.ScLoanNotification +import com.smallcase.loans.data.listeners.Notification +import com.smallcase.loans.data.listeners.NotificationCenter import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.EventChannel import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import org.json.JSONObject @@ -26,6 +31,7 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { private lateinit var activity: Activity private lateinit var scLoansChannel: MethodChannel private val uiThreadHandler: Handler = Handler(Looper.getMainLooper()) + private var scLoanEventsHandler: ScLoanEventsHandler? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { @@ -33,13 +39,18 @@ class ScLoanFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { scLoansChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "scloans") scLoansChannel.setMethodCallHandler(this) - // scLoansChannel.setMethodCallHandler(ScLoanFlutterPlugin(getActivity = { - // activity - // })) + + + scLoanEventsHandler = ScLoanEventsHandler() + scLoanEventsHandler?.onAttachedToEngine(flutterPluginBinding) + } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { scLoansChannel.setMethodCallHandler(null) + + scLoanEventsHandler?.onDetachedFromEngine(binding) + scLoanEventsHandler = null } override fun onAttachedToActivity(binding: ActivityPluginBinding) { diff --git a/loans/ios/Classes/ScLoanEventsHandler.swift b/loans/ios/Classes/ScLoanEventsHandler.swift new file mode 100644 index 00000000..6ca7af7f --- /dev/null +++ b/loans/ios/Classes/ScLoanEventsHandler.swift @@ -0,0 +1,43 @@ +import Foundation +import Flutter +import Loans + +class ScLoanEventsHandler: NSObject { + + private var eventChannel: FlutterEventChannel? + private var eventStreamHandler: ScLoanEventsStreamHandler? + + func setup(with registrar: FlutterPluginRegistrar) { + print("Setting up SCLoans events channel...") + + eventStreamHandler = ScLoanEventsStreamHandler() + eventChannel = FlutterEventChannel(name: "scloans_events", binaryMessenger: registrar.messenger()) + eventChannel?.setStreamHandler(eventStreamHandler) + + setupNotificationListener() + print("SCLoans events channel setup complete") + } + + private func setupNotificationListener() { + NotificationCenter.default.addObserver( + forName: ScLoan.scLoansNotificationName, + object: nil, + queue: .main + ) { [weak self] notification in + self?.handleNotification(notification) + } + } + + private func handleNotification(_ notification: Notification) { + guard let eventStreamHandler = eventStreamHandler, + let userInfo = notification.userInfo, + let jsonString = userInfo[ScLoanNotification.strigifiedPayloadKey] as? String else { return } + + eventStreamHandler.sendEvent(jsonString) + } + + deinit { + NotificationCenter.default.removeObserver(self) + eventChannel?.setStreamHandler(nil) + } +} \ No newline at end of file diff --git a/loans/ios/Classes/ScLoanEventsStreamHandler.swift b/loans/ios/Classes/ScLoanEventsStreamHandler.swift new file mode 100644 index 00000000..446f5ccf --- /dev/null +++ b/loans/ios/Classes/ScLoanEventsStreamHandler.swift @@ -0,0 +1,22 @@ +import Foundation +import Flutter + +class ScLoanEventsStreamHandler: NSObject, FlutterStreamHandler { + private var eventSink: FlutterEventSink? + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + self.eventSink = events + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + self.eventSink = nil + return nil + } + + func sendEvent(_ event: String) { + DispatchQueue.main.async { [weak self] in + self?.eventSink?(event) + } + } +} \ No newline at end of file diff --git a/loans/ios/Classes/ScLoanFlutterPlugin.swift b/loans/ios/Classes/ScLoanFlutterPlugin.swift index 667778c0..90f20b36 100644 --- a/loans/ios/Classes/ScLoanFlutterPlugin.swift +++ b/loans/ios/Classes/ScLoanFlutterPlugin.swift @@ -14,12 +14,19 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { @MainActor let currentViewController: UIViewController = (UIApplication.shared.delegate?.window??.rootViewController)! + private var eventsHandler: ScLoanEventsHandler? + public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "scloans", binaryMessenger: registrar.messenger()) let instance = SwiftScLoanFlutterPlugin() registrar.addMethodCallDelegate(instance, channel: channel) + + instance.eventsHandler = ScLoanEventsHandler() + instance.eventsHandler?.setup(with: registrar) + + print("SwiftScLoansFlutterPlugin with events handler registered successfully") } - + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { guard let args = call.arguments as? Dictionary else { return } @@ -148,7 +155,7 @@ public class SwiftScLoanFlutterPlugin: NSObject, FlutterPlugin { result("Flutter method not implemented on iOS") } } - + // This conversion is vital because only by passing a FlutterError object to the result:FlutterResult call will the error be thrown in the catch block on the dart side private func convertErrorToFlutterError(error: ScLoanError) -> FlutterError { @@ -196,7 +203,7 @@ extension Dictionary { } catch let dictionaryError as NSError { - print("Unable to convert dictionary to json String :\(dictionaryError)") + print("Unable to convert dictionary to json String :\(dictionaryError)") return nil } diff --git a/loans/ios/scloans.podspec b/loans/ios/scloans.podspec index 3c3de3ff..195909b3 100644 --- a/loans/ios/scloans.podspec +++ b/loans/ios/scloans.podspec @@ -21,5 +21,5 @@ A new flutter plugin project. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } s.swift_version = '5.0' - s.dependency 'SCLoans', '6.0.0' + s.dependency 'SCLoans', '6.0.1' end diff --git a/loans/lib/sc_loan.dart b/loans/lib/sc_loan.dart index 32c8fbf9..b98e0abb 100644 --- a/loans/lib/sc_loan.dart +++ b/loans/lib/sc_loan.dart @@ -1,4 +1,5 @@ import 'package:flutter/services.dart'; +import 'dart:convert'; import 'sc_loan_props.dart'; import 'sc_loan_responses.dart'; import 'platform_exception_ext.dart'; diff --git a/loans/lib/sc_loan_events.dart b/loans/lib/sc_loan_events.dart new file mode 100644 index 00000000..fa1e9365 --- /dev/null +++ b/loans/lib/sc_loan_events.dart @@ -0,0 +1,52 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:flutter/services.dart'; + +class ScLoanEvents { + static const EventChannel _eventChannel = EventChannel('scloans_events'); + static StreamSubscription? _subscription; + static final StreamController _eventController = + StreamController.broadcast(); + + /// Stream of SCLoan events (raw JSON strings) + static Stream get eventStream => _eventController.stream; + + /// Start listening to SCLoan events + static void startListening() { + if (_subscription != null) return; + + _subscription = _eventChannel.receiveBroadcastStream().listen( + (dynamic event) { + if (event is String) { + print('SCLoan event stream: $event'); + _eventController.add(event); + } + }, + onError: (dynamic error) { + print('SCLoan event stream error: $error'); + }, + ); + } + + /// Stop listening to SCLoan events + static void stopListening() { + _subscription?.cancel(); + _subscription = null; + } + + /// Dispose resources + static void dispose() { + stopListening(); + _eventController.close(); + } + + /// Parse event data from JSON string + static Map? parseEvent(String jsonString) { + try { + return json.decode(jsonString) as Map; + } catch (e) { + print('Error parsing event JSON: $e'); + return null; + } + } +} diff --git a/scgateway/android/build.gradle b/scgateway/android/build.gradle index f9eeaa88..2d1add7d 100644 --- a/scgateway/android/build.gradle +++ b/scgateway/android/build.gradle @@ -65,5 +65,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "com.smallcase.gateway:sdk:4.3.0" + implementation "com.smallcase.gateway:sdk-release-scg-v5-0-0-loans-v4-0-0-1df0c05:4.3.0-3183-release" } diff --git a/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayEventsHandler.kt b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayEventsHandler.kt new file mode 100644 index 00000000..f828ef80 --- /dev/null +++ b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayEventsHandler.kt @@ -0,0 +1,72 @@ +package com.example.scgateway_flutter_plugin + +import android.util.Log +import com.smallcase.gateway.data.listeners.Notification +import com.smallcase.gateway.data.listeners.NotificationCenter +import com.smallcase.gateway.portal.ScgNotification +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.EventChannel + +class ScgatewayEventsHandler : FlutterPlugin { + + private val TAG = "ScgatewayEventsHandler" + private lateinit var eventChannel: EventChannel + private lateinit var eventStreamHandler: ScgatewayEventsStreamHandler + private var notificationObserver: ((Notification) -> Unit)? = null + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + setup(flutterPluginBinding) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + cleanup() + } + + private fun setup(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + eventStreamHandler = ScgatewayEventsStreamHandler() + eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "scgateway_events") + eventChannel.setStreamHandler(eventStreamHandler) + + setupNotificationListener() + Log.d(TAG, "SCGateway events channel setup complete") + } + + private fun setupNotificationListener() { + notificationObserver?.let { NotificationCenter.removeObserver(it) } + + notificationObserver = { notification -> + try { + val jsonString = notification.userInfo?.get(ScgNotification.STRINGIFIED_PAYLOAD_KEY) as? String + if (jsonString != null) { + Log.d(TAG, "Sending notification to Flutter: $jsonString") + eventStreamHandler.sendEvent(jsonString) + } else { + Log.w(TAG, "Notification has no JSON payload") + } + } catch (e: Exception) { + Log.e(TAG, "Error processing notification: ${e.message}") + eventStreamHandler.sendError("NOTIFICATION_PARSE_ERROR", e.message, e.toString()) + } + } + + notificationObserver?.let { + NotificationCenter.addObserver(it) + Log.d(TAG, "Notification observer added") + } + } + + private fun cleanup() { + try { + notificationObserver?.let { + NotificationCenter.removeObserver(it) + Log.d(TAG, "Notification observer removed") + } + } catch (e: Exception) { + Log.e(TAG, "Error removing notification observer: ${e.message}") + } + + notificationObserver = null + eventChannel.setStreamHandler(null) + Log.d(TAG, "SCGateway events cleanup complete") + } +} \ No newline at end of file diff --git a/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayEventsStreamHandler.kt b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayEventsStreamHandler.kt new file mode 100644 index 00000000..127a3014 --- /dev/null +++ b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayEventsStreamHandler.kt @@ -0,0 +1,34 @@ +package com.example.scgateway_flutter_plugin + +import android.os.Handler +import android.os.Looper +import io.flutter.plugin.common.EventChannel + +class ScgatewayEventsStreamHandler : EventChannel.StreamHandler { + + private var eventSink: EventChannel.EventSink? = null + private val mainHandler = Handler(Looper.getMainLooper()) + + override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { + eventSink = events + } + + override fun onCancel(arguments: Any?) { + eventSink = null + } + + // Call this method to send data to Flutter + fun sendEvent(data: Any) { + mainHandler.post { + eventSink?.success(data) + } + } + + // Call this method to send error to Flutter + fun sendError(errorCode: String, errorMessage: String?, errorDetails: Any? = null) { + mainHandler.post { + eventSink?.error(errorCode, errorMessage, errorDetails) + } + } +} + diff --git a/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt index d5dd4ec0..63215a32 100644 --- a/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt +++ b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt @@ -1,4 +1,4 @@ -package com.example.scgateway_flutter_plugin +package com.example.scgateway_flutter_plugin //native side ka like we had .kt file in RN import android.app.Activity import android.content.Context @@ -13,6 +13,9 @@ import com.smallcase.gateway.data.listeners.* import com.smallcase.gateway.data.models.* import com.smallcase.gateway.data.requests.InitRequest import com.smallcase.gateway.portal.SmallcaseGatewaySdk +import com.smallcase.gateway.portal.ScgNotification +import com.smallcase.gateway.data.listeners.Notification +import com.smallcase.gateway.data.listeners.NotificationCenter import com.smallcase.gateway.portal.SmallplugPartnerProps import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware @@ -21,6 +24,7 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.plugin.common.EventChannel import org.json.JSONObject /** ScgatewayFlutterPlugin */ @@ -36,7 +40,7 @@ class ScgatewayFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { private var replySubmitted = false internal var txnResult: String? = "" - + private var scGatewayEventsHandler: ScgatewayEventsHandler? = null private val leadGenMap by lazy { HashMap() } @@ -53,6 +57,9 @@ class ScgatewayFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "scgateway_flutter_plugin") channel.setMethodCallHandler(this) + + scGatewayEventsHandler = ScgatewayEventsHandler() + scGatewayEventsHandler?.onAttachedToEngine(flutterPluginBinding) } override fun onMethodCall(@NonNull call: MethodCall, @NonNull rawResult: Result) { @@ -441,6 +448,9 @@ class ScgatewayFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) + + scGatewayEventsHandler?.onDetachedFromEngine(binding) + scGatewayEventsHandler = null } override fun onAttachedToActivity(binding: ActivityPluginBinding) { diff --git a/scgateway/ios/Classes/ScgatewayEventsHandler.swift b/scgateway/ios/Classes/ScgatewayEventsHandler.swift new file mode 100644 index 00000000..1be7fa54 --- /dev/null +++ b/scgateway/ios/Classes/ScgatewayEventsHandler.swift @@ -0,0 +1,43 @@ +import Foundation +import Flutter +import SCGateway + +class ScgatewayEventsHandler: NSObject { + + private var eventChannel: FlutterEventChannel? + private var eventStreamHandler: ScgatewayEventsStreamHandler? + + func setup(with registrar: FlutterPluginRegistrar) { + print("Setting up SCGateway events channel...") + + eventStreamHandler = ScgatewayEventsStreamHandler() + eventChannel = FlutterEventChannel(name: "scgateway_events", binaryMessenger: registrar.messenger()) + eventChannel?.setStreamHandler(eventStreamHandler) + + setupNotificationListener() + print("SCGateway events channel setup complete") + } + + private func setupNotificationListener() { + NotificationCenter.default.addObserver( + forName: SCGateway.scgNotificationName, + object: nil, + queue: .main + ) { [weak self] notification in + self?.handleNotification(notification) + } + } + + private func handleNotification(_ notification: Notification) { + guard let eventStreamHandler = eventStreamHandler, + let userInfo = notification.userInfo, + let jsonString = userInfo[SCGNotification.strigifiedPayloadKey] as? String else { return } + + eventStreamHandler.sendEvent(jsonString) + } + + deinit { + NotificationCenter.default.removeObserver(self) + eventChannel?.setStreamHandler(nil) + } +} \ No newline at end of file diff --git a/scgateway/ios/Classes/ScgatewayEventsStreamHandler.swift b/scgateway/ios/Classes/ScgatewayEventsStreamHandler.swift new file mode 100644 index 00000000..b43ca656 --- /dev/null +++ b/scgateway/ios/Classes/ScgatewayEventsStreamHandler.swift @@ -0,0 +1,22 @@ +import Foundation +import Flutter + +class ScgatewayEventsStreamHandler: NSObject, FlutterStreamHandler { + private var eventSink: FlutterEventSink? + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + self.eventSink = events + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + self.eventSink = nil + return nil + } + + func sendEvent(_ event: String) { + DispatchQueue.main.async { [weak self] in + self?.eventSink?(event) + } + } +} \ No newline at end of file diff --git a/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift b/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift index d8f5733f..b6d7bd55 100644 --- a/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift +++ b/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift @@ -16,16 +16,19 @@ public class SwiftScgatewayFlutterPlugin: NSObject, FlutterPlugin { @MainActor let currentViewController: UIViewController = (UIApplication.shared.delegate?.window??.rootViewController)! - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "scgateway_flutter_plugin", binaryMessenger: registrar.messenger()) - let instance = SwiftScgatewayFlutterPlugin() - - // let scLoansChannel = FlutterMethodChannel(name: "scloans", binaryMessenger: registrar.messenger()) - // let scLoansInstance = ScLoanFlutterPlugin() - - registrar.addMethodCallDelegate(instance, channel: channel) - // registrar.addMethodCallDelegate(scLoansInstance, channel: scLoansChannel) - } + private var eventsHandler: ScgatewayEventsHandler? + +public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "scgateway_flutter_plugin", binaryMessenger: registrar.messenger()) + let instance = SwiftScgatewayFlutterPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + + // Setup events handler with same registrar + instance.eventsHandler = ScgatewayEventsHandler() + instance.eventsHandler?.setup(with: registrar) + + print("SwiftScgatewayFlutterPlugin with events handler registered successfully") +} public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { diff --git a/scgateway/ios/scgateway_flutter_plugin.podspec b/scgateway/ios/scgateway_flutter_plugin.podspec index 64ad6331..f030b0fc 100644 --- a/scgateway/ios/scgateway_flutter_plugin.podspec +++ b/scgateway/ios/scgateway_flutter_plugin.podspec @@ -15,7 +15,7 @@ Scgateway Flutter plugin. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'SCGateway', '6.0.0' + s.dependency 'SCGateway', '6.0.1' s.xcconfig = {'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES'} s.vendored_frameworks = 'SCGateway.xcframework' s.platform = :ios, '13.0' diff --git a/scgateway/lib/scgateway_events.dart b/scgateway/lib/scgateway_events.dart new file mode 100644 index 00000000..e95e1f07 --- /dev/null +++ b/scgateway/lib/scgateway_events.dart @@ -0,0 +1,52 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:flutter/services.dart'; + +class ScgatewayEvents { + static const EventChannel _eventChannel = EventChannel('scgateway_events'); + static StreamSubscription? _subscription; + static final StreamController _eventController = + StreamController.broadcast(); + + /// Stream of SCGateway events (raw JSON strings) + static Stream get eventStream => _eventController.stream; + + /// Start listening to SCGateway events + static void startListening() { + if (_subscription != null) return; + + _subscription = _eventChannel.receiveBroadcastStream().listen( + (dynamic event) { + if (event is String) { + print('SCGateway event stream: $event'); + _eventController.add(event); + } + }, + onError: (dynamic error) { + print('SCGateway event stream error: $error'); + }, + ); + } + + /// Stop listening to SCGateway events + static void stopListening() { + _subscription?.cancel(); + _subscription = null; + } + + /// Dispose resources + static void dispose() { + stopListening(); + _eventController.close(); + } + + /// Parse event data from JSON string + static Map? parseEvent(String jsonString) { + try { + return json.decode(jsonString) as Map; + } catch (e) { + print('Error parsing event JSON: $e'); + return null; + } + } +} diff --git a/scgateway/lib/scgateway_flutter_plugin.dart b/scgateway/lib/scgateway_flutter_plugin.dart index b9e776c2..2f58de0b 100644 --- a/scgateway/lib/scgateway_flutter_plugin.dart +++ b/scgateway/lib/scgateway_flutter_plugin.dart @@ -50,6 +50,8 @@ class ScgatewayFlutterPlugin { static const MethodChannel _channel = const MethodChannel('scgateway_flutter_plugin'); + // Events are handled by ScgatewayEvents class - import 'package:scgateway_flutter_plugin/scgateway_events.dart' + static const String _flutterPluginVersion = "4.0.0"; static Future getSdkVersion() async { @@ -340,4 +342,8 @@ class ScgatewayFlutterPlugin { return showOrdersResponse; } + + // Events are now handled by ScgatewayEvents class + // Use: import 'package:scgateway_flutter_plugin/scgateway_events.dart'; + // Then: ScgatewayEvents.startListening() and ScgatewayEvents.eventStream } diff --git a/smart_investing/android/app/src/main/AndroidManifest.xml b/smart_investing/android/app/src/main/AndroidManifest.xml index c8d5f6d3..bc782da0 100644 --- a/smart_investing/android/app/src/main/AndroidManifest.xml +++ b/smart_investing/android/app/src/main/AndroidManifest.xml @@ -2,12 +2,12 @@ + android:usesCleartextTraffic="true" + android:icon="@mipmap/ic_launcher"> + android:exported="true" + android:excludeFromRecents="true"> diff --git a/smart_investing/ios/Podfile b/smart_investing/ios/Podfile index a6ab13a0..8b78319c 100644 --- a/smart_investing/ios/Podfile +++ b/smart_investing/ios/Podfile @@ -3,7 +3,7 @@ platform :ios, '14.0' source 'https://cdn.cocoapods.org' # private podspec for smallcase -source 'https://github.com/smallcase/cocoapodspec-internal.git' +# source 'https://github.com/smallcase/cocoapodspec-internal.git' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/smart_investing/ios/Podfile.lock b/smart_investing/ios/Podfile.lock index 5688906b..82be2623 100644 --- a/smart_investing/ios/Podfile.lock +++ b/smart_investing/ios/Podfile.lock @@ -1,37 +1,27 @@ PODS: - Flutter (1.0.0) - - Mixpanel-swift (5.1.0): - - Mixpanel-swift/Complete (= 5.1.0) - - Mixpanel-swift/Complete (5.1.0) - - mixpanel_flutter (2.4.4): - - Flutter - - Mixpanel-swift (= 5.1.0) - - SCGateway (6.0.0) + - SCGateway (6.0.1) - scgateway_flutter_plugin (0.0.1): - Flutter - - SCGateway (= 6.0.0) + - SCGateway (= 6.0.1) - scloans (0.0.1): - Flutter - - SCLoans (= 6.0.0) - - SCLoans (6.0.0) + - SCLoans (= 6.0.1) + - SCLoans (6.0.1) DEPENDENCIES: - Flutter (from `Flutter`) - - mixpanel_flutter (from `.symlinks/plugins/mixpanel_flutter/ios`) - scgateway_flutter_plugin (from `.symlinks/plugins/scgateway_flutter_plugin/ios`) - scloans (from `.symlinks/plugins/scloans/ios`) SPEC REPOS: trunk: - - Mixpanel-swift - SCGateway - SCLoans EXTERNAL SOURCES: Flutter: :path: Flutter - mixpanel_flutter: - :path: ".symlinks/plugins/mixpanel_flutter/ios" scgateway_flutter_plugin: :path: ".symlinks/plugins/scgateway_flutter_plugin/ios" scloans: @@ -39,13 +29,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Mixpanel-swift: 7b26468fc0e2e521104e51d65c4bbf7cab8162f8 - mixpanel_flutter: a0b6b937035899cd01951735ad5f87718b2ffee5 - SCGateway: f502f44122537b777861093ef97f67ccc311d4d0 - scgateway_flutter_plugin: 6cc6404fbc946cbf9b87f40277d9a11ab3c97422 - SCLoans: 9ec42243b84ba0c04564ee99f27c423c82a6b723 - scloans: 167ea92ebc32e70aa3560dc671153d7f421f02ca + SCGateway: 696bb22c572fa466a9021a40395737639b825531 + scgateway_flutter_plugin: 4828d4cfced53981bdaf6fb573a5686856b78c57 + SCLoans: 212c5222ac01f177756daea58f9e06a6b8529b59 + scloans: 592cc0ed6e9c4fe6d276fe9e14b04a58f752803b -PODFILE CHECKSUM: 32b0659ca3529b1ef2c9e5c229290749c8c79710 +PODFILE CHECKSUM: a8f4d8cbc1db0bcad40230a4228adff2797395c4 COCOAPODS: 1.16.2 diff --git a/smart_investing/ios/Runner.xcodeproj/project.pbxproj b/smart_investing/ios/Runner.xcodeproj/project.pbxproj index b9bc85cd..629a0b2f 100644 --- a/smart_investing/ios/Runner.xcodeproj/project.pbxproj +++ b/smart_investing/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 5DA50F4CEE2701556BDA890E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F4D7609288D9DB983262EEE /* Pods_RunnerTests.framework */; }; + 7262B0E9A48C50C8E528A17B /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60CCDD23332026BF8F744D73 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7E29ED739A5B0D5E1D1B128D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE49B7DC57DED9D2965E166 /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - D4488AA8A7B8C32795138998 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B760BB7221A9C7DE47C8575 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,17 +42,19 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0306727E01CF6E9427974F4B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 6B760BB7221A9C7DE47C8575 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5483EF28CDD1A65BE186A690 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 57180091AF2E9634DCA812E5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 60CCDD23332026BF8F744D73 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7F4D7609288D9DB983262EEE /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8ACFE51AB455261C25928FC2 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 8B0345F9664094A3E2A8CA54 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -60,11 +62,9 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A64A611333E16F39785E8119 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - A891B9310A04F576BC07CDB5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - B66EE0CBC65E91FCEEA6DC8A /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - BEC9541377BF9DA30428C8C2 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - E5296D9F5CB96ACDFFF96346 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + CEE49B7DC57DED9D2965E166 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E57E5234C5E14AFB1BEEF4E0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F78897B437CAD2820DC92930 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,7 +72,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5DA50F4CEE2701556BDA890E /* Pods_RunnerTests.framework in Frameworks */, + 7262B0E9A48C50C8E528A17B /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,7 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D4488AA8A7B8C32795138998 /* Pods_Runner.framework in Frameworks */, + 7E29ED739A5B0D5E1D1B128D /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -90,12 +90,12 @@ 0A5B30E89B9C688A2E0D384B /* Pods */ = { isa = PBXGroup; children = ( - 0306727E01CF6E9427974F4B /* Pods-Runner.debug.xcconfig */, - BEC9541377BF9DA30428C8C2 /* Pods-Runner.release.xcconfig */, - A891B9310A04F576BC07CDB5 /* Pods-Runner.profile.xcconfig */, - A64A611333E16F39785E8119 /* Pods-RunnerTests.debug.xcconfig */, - E5296D9F5CB96ACDFFF96346 /* Pods-RunnerTests.release.xcconfig */, - B66EE0CBC65E91FCEEA6DC8A /* Pods-RunnerTests.profile.xcconfig */, + 57180091AF2E9634DCA812E5 /* Pods-Runner.debug.xcconfig */, + E57E5234C5E14AFB1BEEF4E0 /* Pods-Runner.release.xcconfig */, + F78897B437CAD2820DC92930 /* Pods-Runner.profile.xcconfig */, + 8B0345F9664094A3E2A8CA54 /* Pods-RunnerTests.debug.xcconfig */, + 5483EF28CDD1A65BE186A690 /* Pods-RunnerTests.release.xcconfig */, + 8ACFE51AB455261C25928FC2 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -108,6 +108,15 @@ path = RunnerTests; sourceTree = ""; }; + 5B65FFE21DC09FD595AF0065 /* Frameworks */ = { + isa = PBXGroup; + children = ( + CEE49B7DC57DED9D2965E166 /* Pods_Runner.framework */, + 60CCDD23332026BF8F744D73 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -127,7 +136,7 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, 0A5B30E89B9C688A2E0D384B /* Pods */, - D2036338D35C413926429278 /* Frameworks */, + 5B65FFE21DC09FD595AF0065 /* Frameworks */, ); sourceTree = ""; }; @@ -155,15 +164,6 @@ path = Runner; sourceTree = ""; }; - D2036338D35C413926429278 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 6B760BB7221A9C7DE47C8575 /* Pods_Runner.framework */, - 7F4D7609288D9DB983262EEE /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -171,7 +171,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 106C6815394A37F7EA2A1BD9 /* [CP] Check Pods Manifest.lock */, + 7C8CBF13AEC5DE08261CC337 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 5D50050576ACC42B5AA1F2A2 /* Frameworks */, @@ -190,14 +190,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - DEDA7907BDE32E8CD58D67B6 /* [CP] Check Pods Manifest.lock */, + B9A57D29FF83C3EAF64A7AB9 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - A65A22CF8499D3153DE3CB8F /* [CP] Embed Pods Frameworks */, + 5107F3C38A56BBA880B7B808 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -269,77 +269,77 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 106C6815394A37F7EA2A1BD9 /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); + name = "Thin Binary"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 5107F3C38A56BBA880B7B808 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "Thin Binary"; - outputPaths = ( + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 7C8CBF13AEC5DE08261CC337 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Run Script"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - A65A22CF8499D3153DE3CB8F /* [CP] Embed Pods Frameworks */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + inputPaths = ( ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + name = "Run Script"; + outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - DEDA7907BDE32E8CD58D67B6 /* [CP] Check Pods Manifest.lock */ = { + B9A57D29FF83C3EAF64A7AB9 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -489,7 +489,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A64A611333E16F39785E8119 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 8B0345F9664094A3E2A8CA54 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -507,7 +507,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E5296D9F5CB96ACDFFF96346 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 5483EF28CDD1A65BE186A690 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -523,7 +523,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B66EE0CBC65E91FCEEA6DC8A /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 8ACFE51AB455261C25928FC2 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/smart_investing/lib/app/SILoansPage.dart b/smart_investing/lib/app/SILoansPage.dart index 2c9bee26..c2705cb1 100644 --- a/smart_investing/lib/app/SILoansPage.dart +++ b/smart_investing/lib/app/SILoansPage.dart @@ -1,14 +1,104 @@ +import 'dart:async'; +import 'package:clipboard/clipboard.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:scloans/sc_loan.dart'; +import 'package:scloans/sc_loan_events.dart'; -import '../main.dart'; import 'global/SmartInvestingAppRepository.dart'; import 'widgets/SIButton.dart'; import 'widgets/SIEnvironmentController.dart'; import 'widgets/SISwitch.dart'; import 'widgets/SIText.dart'; import 'widgets/SITextField.dart'; +import 'package:flutter/widgets.dart'; + +// Loans Event data model +class ScLoanEventData { + final String rawJson; + final Map? parsedData; + final String eventType; + final DateTime timestamp; + + ScLoanEventData({ + required this.rawJson, + required this.parsedData, + required this.eventType, + required this.timestamp, + }); + + factory ScLoanEventData.fromJson(String jsonString) { + final parsedData = ScLoanEvents.parseEvent(jsonString); + final eventType = parsedData?['event'] ?? parsedData?['type'] ?? 'unknown'; + + return ScLoanEventData( + rawJson: jsonString, + parsedData: parsedData, + eventType: eventType.toString(), + timestamp: DateTime.now(), + ); + } + + Color get backgroundColor { + switch (eventType.toLowerCase()) { + case 'success': + case 'completed': + case 'approved': + case 'disbursed': + return Colors.green.shade50; + case 'error': + case 'failed': + case 'rejected': + case 'cancelled': + return Colors.red.shade50; + case 'started': + case 'initiated': + case 'pending': + case 'applied': + return Colors.blue.shade50; + case 'warning': + case 'retry': + case 'under_review': + return Colors.orange.shade50; + case 'info': + case 'progress': + case 'processing': + return Colors.grey.shade50; + default: + return Colors.lightBlue.shade50; + } + } + + Color get borderColor { + switch (eventType.toLowerCase()) { + case 'success': + case 'completed': + case 'approved': + case 'disbursed': + return Colors.green.shade200; + case 'error': + case 'failed': + case 'rejected': + case 'cancelled': + return Colors.red.shade200; + case 'started': + case 'initiated': + case 'pending': + case 'applied': + return Colors.blue.shade200; + case 'warning': + case 'retry': + case 'under_review': + return Colors.orange.shade200; + case 'info': + case 'progress': + case 'processing': + return Colors.grey.shade200; + default: + return Colors.lightBlue.shade200; + } + } +} class SILoansPage extends StatelessWidget { const SILoansPage({super.key}); @@ -23,9 +113,9 @@ class SILoansPage extends StatelessWidget { label: "Gateway", onPressed: () => { repository.appState.add("/"), - context.go(repository.appState.value) + context.go(repository.appState.value), }, - ) + ), ], ), body: SafeArea( @@ -43,17 +133,21 @@ class SILoansPage extends StatelessWidget { return Column( children: [ SITextField( - hint: "Enter Gateway Name", - onChanged: (value) { - repository.scLoanConfig.value = - data.copyWith(gatewayName: value); - }), + hint: "Enter Gateway Name", + onChanged: (value) { + repository.scLoanConfig.value = data.copyWith( + gatewayName: value, + ); + }, + ), SITextField( - hint: "Enter Custom Interaction Token", - onChanged: (value) { - repository.scLoanConfig.value = - data.copyWith(customInteractionToken: value); - }), + hint: "Enter Custom Interaction Token", + onChanged: (value) { + repository.scLoanConfig.value = data.copyWith( + customInteractionToken: value, + ); + }, + ), ], ); }, @@ -65,9 +159,12 @@ class SILoansPage extends StatelessWidget { label: "Setup", onPressed: () async { try { - final response = await ScLoan.setup(ScLoanConfig( + final response = await ScLoan.setup( + ScLoanConfig( repository.scLoanConfig.value.environment, - repository.scLoanConfig.value.gatewayName)); + repository.scLoanConfig.value.gatewayName, + ), + ); repository.showAlertDialog(response.toString(), context); } on ScLoanError catch (e) { repository.showAlertDialog(e.toString(), context); @@ -78,9 +175,34 @@ class SILoansPage extends StatelessWidget { label: "Apply", onPressed: () async { try { - final response = await ScLoan.apply(ScLoanInfo(repository - .scLoanConfig.value.customInteractionToken ?? - "")); + final response = await ScLoan.apply( + ScLoanInfo( + repository + .scLoanConfig + .value + .customInteractionToken ?? + "", + ), + ); + repository.showAlertDialog(response.toString(), context); + } on ScLoanError catch (e) { + repository.showAlertDialog(e.toString(), context); + } + }, + ), + SIButton( + label: "Pay", + onPressed: () async { + try { + final response = await ScLoan.pay( + ScLoanInfo( + repository + .scLoanConfig + .value + .customInteractionToken ?? + "", + ), + ); repository.showAlertDialog(response.toString(), context); } on ScLoanError catch (e) { repository.showAlertDialog(e.toString(), context); @@ -88,62 +210,66 @@ class SILoansPage extends StatelessWidget { }, ), SIButton( - label: "Pay", - onPressed: () async { - try { - final response = await ScLoan.pay(ScLoanInfo(repository - .scLoanConfig.value.customInteractionToken ?? - "")); - repository.showAlertDialog( - response.toString(), context); - } on ScLoanError catch (e) { - repository.showAlertDialog(e.toString(), context); - } - }), + label: "Withdraw", + onPressed: () async { + try { + final response = await ScLoan.withdraw( + ScLoanInfo( + repository + .scLoanConfig + .value + .customInteractionToken ?? + "", + ), + ); + repository.showAlertDialog(response.toString(), context); + } on ScLoanError catch (e) { + repository.showAlertDialog(e.toString(), context); + } + }, + ), SIButton( - label: "Withdraw", - onPressed: () async { - try { - final response = await ScLoan.withdraw(ScLoanInfo( - repository.scLoanConfig.value - .customInteractionToken ?? - "")); - repository.showAlertDialog( - response.toString(), context); - } on ScLoanError catch (e) { - repository.showAlertDialog(e.toString(), context); - } - }), + label: "Service", + onPressed: () async { + try { + final response = await ScLoan.service( + ScLoanInfo( + repository + .scLoanConfig + .value + .customInteractionToken ?? + "", + ), + ); + repository.showAlertDialog(response.toString(), context); + } on ScLoanError catch (e) { + repository.showAlertDialog(e.toString(), context); + } + }, + ), SIButton( - label: "Service", - onPressed: () async { - try { - final response = await ScLoan.service(ScLoanInfo( - repository.scLoanConfig.value - .customInteractionToken ?? - "")); - repository.showAlertDialog( - response.toString(), context); - } on ScLoanError catch (e) { - repository.showAlertDialog(e.toString(), context); - } - }), - SIButton( - label: "Trigger Interaction", - onPressed: () async { - try { - final response = await ScLoan.triggerInteraction(ScLoanInfo( - repository.scLoanConfig.value - .customInteractionToken ?? - "")); - repository.showAlertDialog( - response.toString(), context); - } on ScLoanError catch (e) { - repository.showAlertDialog(e.toString(), context); - } - }), + label: "Trigger Interaction", + onPressed: () async { + try { + final response = await ScLoan.triggerInteraction( + ScLoanInfo( + repository + .scLoanConfig + .value + .customInteractionToken ?? + "", + ), + ); + repository.showAlertDialog(response.toString(), context); + } on ScLoanError catch (e) { + repository.showAlertDialog(e.toString(), context); + } + }, + ), ], ), + const SizedBox(height: 16), + _EnhancedLoansEventsList(), StreamBuilder( stream: repository.siConfig, builder: (context, snapshot) { @@ -154,7 +280,7 @@ class SILoansPage extends StatelessWidget { text: data?.loansUserId, hint: "Enter SI user Id", ), - SIButton(label: "Get User") + SIButton(label: "Get User"), ], ); }, @@ -178,7 +304,223 @@ class SILoansPage extends StatelessWidget { ), ], ), - SIButton(label: "Register") + SIButton(label: "Register"), + ], + ), + ), + ); + } +} + +class _EnhancedLoansEventsList extends StatefulWidget { + @override + State<_EnhancedLoansEventsList> createState() => + _EnhancedLoansEventsListState(); +} + +class _EnhancedLoansEventsListState extends State<_EnhancedLoansEventsList> { + StreamSubscription? _scLoanEventsSubscription; + final List _scLoanEvents = []; + + @override + void initState() { + super.initState(); + _setupScLoanEvents(); + } + + void _setupScLoanEvents() { + // Start listening to loan events + ScLoanEvents.startListening(); + + _scLoanEventsSubscription = ScLoanEvents.eventStream.listen((jsonString) { + setState(() { + // Parse the event data + final eventData = ScLoanEventData.fromJson(jsonString); + _scLoanEvents.insert(0, eventData); + if (_scLoanEvents.length > 20) { + _scLoanEvents.removeLast(); + } + }); + }); + } + + @override + void dispose() { + _scLoanEventsSubscription?.cancel(); + ScLoanEvents.stopListening(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.symmetric(vertical: 8), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Loans Events (live)', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + Row( + children: [ + IconButton( + icon: Icon(Icons.delete_outline), + onPressed: () { + setState(() { + _scLoanEvents.clear(); + }); + }, + tooltip: 'Clear Events', + ), + Text('${_scLoanEvents.length}'), + ], + ), + ], + ), + SizedBox(height: 8), + Container( + height: 200, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(4), + ), + child: _scLoanEvents.isEmpty + ? Center( + child: Text( + 'No Loans events yet...', + style: TextStyle( + color: Colors.grey[600], + fontStyle: FontStyle.italic, + ), + ), + ) + : ListView.builder( + itemCount: _scLoanEvents.length, + itemBuilder: (context, index) { + final eventData = _scLoanEvents[index]; + return Container( + padding: EdgeInsets.all(8), + margin: EdgeInsets.symmetric(vertical: 2), + decoration: BoxDecoration( + color: eventData.backgroundColor, + border: Border.all( + color: eventData.borderColor, + width: 1, + ), + borderRadius: BorderRadius.circular(6), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Event header with type and number + Row( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + decoration: BoxDecoration( + color: eventData.borderColor, + borderRadius: BorderRadius.circular(12), + ), + child: Text( + eventData.eventType.toUpperCase(), + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + SizedBox(width: 8), + Text( + '${_scLoanEvents.length - index}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: eventData.borderColor, + ), + ), + Spacer(), + IconButton( + icon: Icon(Icons.copy, size: 16), + onPressed: () { + FlutterClipboard.copy(eventData.rawJson); + ScaffoldMessenger.of( + context, + ).showSnackBar( + SnackBar( + content: Text('Event copied!'), + ), + ); + }, + tooltip: 'Copy Raw JSON', + ), + ], + ), + SizedBox(height: 4), + // Event data (parsed or raw) + if (eventData.parsedData != null) ...[ + // Show parsed data in a structured way + for (final entry + in eventData.parsedData!.entries) + if (entry.key != 'event' && + entry.key != 'type') + Padding( + padding: EdgeInsets.only( + left: 16, + bottom: 2, + ), + child: RichText( + text: TextSpan( + style: TextStyle( + fontFamily: 'monospace', + fontSize: 11, + color: Colors.black87, + ), + children: [ + TextSpan( + text: '${entry.key}: ', + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.blue[800], + ), + ), + TextSpan( + text: entry.value.toString(), + style: TextStyle( + color: Colors.grey[800], + ), + ), + ], + ), + ), + ), + ] else + // Show raw JSON if parsing failed + Padding( + padding: EdgeInsets.only(left: 16), + child: SelectableText( + eventData.rawJson, + style: TextStyle( + fontFamily: 'monospace', + fontSize: 11, + color: Colors.red[700], + ), + ), + ), + ], + ), + ); + }, + ), + ), ], ), ), diff --git a/smart_investing/lib/app/features/ConnectScreen.dart b/smart_investing/lib/app/features/ConnectScreen.dart index 9f9642c8..01c48868 100644 --- a/smart_investing/lib/app/features/ConnectScreen.dart +++ b/smart_investing/lib/app/features/ConnectScreen.dart @@ -1,6 +1,8 @@ +import 'dart:async'; import 'package:clipboard/clipboard.dart'; import 'package:flutter/material.dart'; import 'package:scgateway_flutter_plugin/scgateway_flutter_plugin.dart'; +import 'package:scgateway_flutter_plugin/scgateway_events.dart'; import '../../smartinvesting.dart'; import '../global/SIConfigs.dart'; @@ -14,11 +16,88 @@ import '../widgets/SITextField.dart'; final gatewayEnvironments = [ ScGatewayConfig.prod(), ScGatewayConfig.dev(), - ScGatewayConfig.stag() + ScGatewayConfig.stag(), ]; SmartInvesting smartInvesting = SmartInvestingAppRepository.smartInvesting; +// Event data model +class ScGatewayEventData { + final String rawJson; + final Map? parsedData; + final String eventType; + final DateTime timestamp; + + ScGatewayEventData({ + required this.rawJson, + required this.parsedData, + required this.eventType, + required this.timestamp, + }); + + factory ScGatewayEventData.fromJson(String jsonString) { + final parsedData = ScgatewayEvents.parseEvent(jsonString); + final eventType = parsedData?['event'] ?? parsedData?['type'] ?? 'unknown'; + + return ScGatewayEventData( + rawJson: jsonString, + parsedData: parsedData, + eventType: eventType.toString(), + timestamp: DateTime.now(), + ); + } + + Color get backgroundColor { + switch (eventType.toLowerCase()) { + case 'success': + case 'completed': + case 'transaction_completed': + return Colors.green.shade50; + case 'error': + case 'failed': + case 'transaction_failed': + return Colors.red.shade50; + case 'started': + case 'initiated': + case 'transaction_started': + return Colors.blue.shade50; + case 'warning': + case 'retry': + return Colors.orange.shade50; + case 'info': + case 'progress': + return Colors.grey.shade50; + default: + return Colors.lightBlue.shade50; + } + } + + Color get borderColor { + switch (eventType.toLowerCase()) { + case 'success': + case 'completed': + case 'transaction_completed': + return Colors.green.shade200; + case 'error': + case 'failed': + case 'transaction_failed': + return Colors.red.shade200; + case 'started': + case 'initiated': + case 'transaction_started': + return Colors.blue.shade200; + case 'warning': + case 'retry': + return Colors.orange.shade200; + case 'info': + case 'progress': + return Colors.grey.shade200; + default: + return Colors.lightBlue.shade200; + } + } +} + class ConnectScreen extends StatefulWidget { const ConnectScreen({super.key}); @@ -27,11 +106,6 @@ class ConnectScreen extends StatefulWidget { } class _ConnectScreenState extends State { - @override - void dispose() { - // TODO: implement dispose - super.dispose(); - } @override Widget build(BuildContext context) { @@ -53,16 +127,18 @@ class _ConnectScreenState extends State { label: "Leprechaun Mode:", isEnabled: data.isLeprechaunEnabled, onChanged: (value) { - repository.scGatewayConfig.value = - data.copyWith(isLeprechaunEnabled: value); + repository.scGatewayConfig.value = data.copyWith( + isLeprechaunEnabled: value, + ); }, ), SISwitch( label: "Amo Mode:", isEnabled: data.isAmoEnabled, onChanged: (value) { - repository.scGatewayConfig.value = - data.copyWith(isAmoEnabled: value); + repository.scGatewayConfig.value = data.copyWith( + isAmoEnabled: value, + ); }, ), SITextField( @@ -75,50 +151,64 @@ class _ConnectScreenState extends State { label: "Fetch AuthToken From SmartInvesting", onPressed: () async { print( - "THE SMARTINVESTING USER ID INSIDE FETCH AUTH TOKEN IS ${repository.smartInvestingUserId.value}"); + "THE SMARTINVESTING USER ID INSIDE FETCH AUTH TOKEN IS ${repository.smartInvestingUserId.value}", + ); final loginResponse = await smartInvesting.userLogin( - userID: repository.smartInvestingUserId.value); + userID: repository.smartInvestingUserId.value, + ); repository.scGatewayConfig.value = data.copyWith( - customAuthToken: loginResponse["smallcaseAuthToken"]); + customAuthToken: loginResponse["smallcaseAuthToken"], + ); repository.showAlertDialog( - loginResponse.toString(), context); + loginResponse.toString(), + context, + ); }, ), SITextField( - hint: "Custom Auth Token (JwT)", - text: data.customAuthToken, - onChanged: (value) { - repository.scGatewayConfig.value = - data.copyWith(customAuthToken: value); - }), + hint: "Custom Auth Token (JwT)", + text: data.customAuthToken, + onChanged: (value) { + repository.scGatewayConfig.value = data.copyWith( + customAuthToken: value, + ); + }, + ), Wrap( children: [ SIButton( label: "SETUP", onPressed: () async { - final environmentResponse = - await ScgatewayFlutterPlugin.setConfigEnvironment( - repository.scGatewayConfig.value.environment, - repository.scGatewayConfig.value.gatewayName, - repository - .scGatewayConfig.value.isLeprechaunEnabled, - [], - isAmoenabled: repository - .scGatewayConfig.value.isAmoEnabled); + await ScgatewayFlutterPlugin.setConfigEnvironment( + repository.scGatewayConfig.value.environment, + repository.scGatewayConfig.value.gatewayName, + repository.scGatewayConfig.value.isLeprechaunEnabled, + [], + isAmoenabled: + repository.scGatewayConfig.value.isAmoEnabled, + ); if (repository.scGatewayConfig.value.customAuthToken == null) { final loginResponse = await smartInvesting.userLogin( - userID: repository.smartInvestingUserId.value); + userID: repository.smartInvestingUserId.value, + ); repository.scGatewayConfig.value = data.copyWith( - customAuthToken: - loginResponse["smallcaseAuthToken"]); + customAuthToken: + loginResponse["smallcaseAuthToken"], + ); } final initResponse = - await ScgatewayFlutterPlugin.initGateway(repository - .scGatewayConfig.value.customAuthToken ?? - ""); + await ScgatewayFlutterPlugin.initGateway( + repository + .scGatewayConfig + .value + .customAuthToken ?? + "", + ); repository.showAlertDialog( - initResponse.toString(), context); + initResponse.toString(), + context, + ); }, ), SIButton( @@ -137,7 +227,11 @@ class _ConnectScreenState extends State { // repository.showAlertDialog( // response.toString(), context); repository.triggerTransaction( - ScgatewayIntent.CONNECT, null, false, context); + ScgatewayIntent.CONNECT, + null, + false, + context, + ); }, ), ], @@ -146,14 +240,13 @@ class _ConnectScreenState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SIText.large( - text: "Trigger Txn with Id", - ), + SIText.large(text: "Trigger Txn with Id"), InputChip( checkmarkColor: Colors.white, onSelected: (value) { - repository.scGatewayConfig.value = - data.copyWith(isMFTransactionEnabled: value); + repository.scGatewayConfig.value = data.copyWith( + isMFTransactionEnabled: value, + ); }, label: Text("MF"), selected: data.isMFTransactionEnabled, @@ -163,10 +256,11 @@ class _ConnectScreenState extends State { ], ), SITextField( - hint: "Enter Transaction Id", - onChanged: (value) { - repository.transactionID.add(value); - }), + hint: "Enter Transaction Id", + onChanged: (value) { + repository.transactionID.add(value); + }, + ), Wrap( children: [ SIButton( @@ -178,8 +272,13 @@ class _ConnectScreenState extends State { SIButton( label: "Trigger", onPressed: () { - repository.triggerTransaction(null, null, true, context, - isMF: data.isMFTransactionEnabled); + repository.triggerTransaction( + null, + null, + true, + context, + isMF: data.isMFTransactionEnabled, + ); }, ), SIButton( @@ -189,7 +288,9 @@ class _ConnectScreenState extends State { }, ), ], - ) + ), + SizedBox(height: 20), + _EnhancedScGatewayEventsList(), ], ); }, @@ -197,4 +298,221 @@ class _ConnectScreenState extends State { ], ); } + +} + +class _EnhancedScGatewayEventsList extends StatefulWidget { + @override + State<_EnhancedScGatewayEventsList> createState() => + _EnhancedScGatewayEventsListState(); +} + +class _EnhancedScGatewayEventsListState extends State<_EnhancedScGatewayEventsList> { + StreamSubscription? _scGatewayEventsSubscription; + final List _scGatewayEvents = []; + + @override + void initState() { + super.initState(); + _setupScGatewayEvents(); + } + + void _setupScGatewayEvents() { + // Start listening to gateway events + ScgatewayEvents.startListening(); + + _scGatewayEventsSubscription = ScgatewayEvents.eventStream.listen((jsonString) { + setState(() { + // Parse the event data + final eventData = ScGatewayEventData.fromJson(jsonString); + _scGatewayEvents.insert(0, eventData); + if (_scGatewayEvents.length > 20) { + _scGatewayEvents.removeLast(); + } + }); + }); + } + + @override + void dispose() { + _scGatewayEventsSubscription?.cancel(); + ScgatewayEvents.stopListening(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.symmetric(vertical: 8), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'ScGateway Events', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + Row( + children: [ + IconButton( + icon: Icon(Icons.delete_outline), + onPressed: () { + setState(() { + _scGatewayEvents.clear(); + }); + }, + tooltip: 'Clear Events', + ), + Text('${_scGatewayEvents.length}'), + ], + ), + ], + ), + SizedBox(height: 8), + Container( + height: 200, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(4), + ), + child: _scGatewayEvents.isEmpty + ? Center( + child: Text( + 'No ScGateway events yet...', + style: TextStyle( + color: Colors.grey[600], + fontStyle: FontStyle.italic, + ), + ), + ) + : ListView.builder( + itemCount: _scGatewayEvents.length, + itemBuilder: (context, index) { + final eventData = _scGatewayEvents[index]; + return Container( + padding: EdgeInsets.all(8), + margin: EdgeInsets.symmetric(vertical: 2), + decoration: BoxDecoration( + color: eventData.backgroundColor, + border: Border.all( + color: eventData.borderColor, + width: 1, + ), + borderRadius: BorderRadius.circular(6), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Event header with type and number + Row( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + decoration: BoxDecoration( + color: eventData.borderColor, + borderRadius: BorderRadius.circular(12), + ), + child: Text( + eventData.eventType.toUpperCase(), + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + SizedBox(width: 8), + Text( + '${_scGatewayEvents.length - index}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: eventData.borderColor, + ), + ), + Spacer(), + IconButton( + icon: Icon(Icons.copy, size: 16), + onPressed: () { + FlutterClipboard.copy(eventData.rawJson); + ScaffoldMessenger.of( + context, + ).showSnackBar( + SnackBar( + content: Text('Event copied!'), + ), + ); + }, + tooltip: 'Copy Raw JSON', + ), + ], + ), + SizedBox(height: 4), + // Event data (parsed or raw) + if (eventData.parsedData != null) ...[ + // Show parsed data in a structured way + for (final entry + in eventData.parsedData!.entries) + if (entry.key != 'event' && + entry.key != 'type') + Padding( + padding: EdgeInsets.only( + left: 16, + bottom: 2, + ), + child: RichText( + text: TextSpan( + style: TextStyle( + fontFamily: 'monospace', + fontSize: 11, + color: Colors.black87, + ), + children: [ + TextSpan( + text: '${entry.key}: ', + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.blue[800], + ), + ), + TextSpan( + text: entry.value.toString(), + style: TextStyle( + color: Colors.grey[800], + ), + ), + ], + ), + ), + ), + ] else + // Show raw JSON if parsing failed + Padding( + padding: EdgeInsets.only(left: 16), + child: SelectableText( + eventData.rawJson, + style: TextStyle( + fontFamily: 'monospace', + fontSize: 11, + color: Colors.red[700], + ), + ), + ), + ], + ), + ); + }, + ), + ), + ], + ), + ), + ); + } } diff --git a/smart_investing/lib/app/global/SmartInvestingAppRepository.dart b/smart_investing/lib/app/global/SmartInvestingAppRepository.dart index 6a30cdb8..fb174446 100644 --- a/smart_investing/lib/app/global/SmartInvestingAppRepository.dart +++ b/smart_investing/lib/app/global/SmartInvestingAppRepository.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:clipboard/clipboard.dart'; @@ -5,9 +6,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:rxdart/rxdart.dart'; import 'package:scgateway_flutter_plugin/scgateway_flutter_plugin.dart'; +import 'package:scgateway_flutter_plugin/scgateway_events.dart'; +import 'package:scloans/sc_loan.dart'; +import 'package:scloans/sc_loan_events.dart'; import '../../smartinvesting.dart'; -import 'SIConfigs.dart'; +import 'SIConfigs.dart' as si; SmartInvestingAppRepository get repository { return SmartInvestingAppRepository.singleton(); @@ -20,6 +24,25 @@ class SmartInvestingAppRepository { scGatewayConfig.value = element.scGatewayConfig; scLoanConfig.value = element.scLoanConfig; }); + + // Subscribe to native event streams and pipe to BehaviorSubjects + _gatewaySub = ScgatewayEvents.eventStream.listen((jsonString) { + // Parse and append gateway events + final event = ScgatewayEvents.parseEvent(jsonString); + if (event != null) { + _appendGatewayEvent(event); + } else { + _appendGatewayEvent({"type": "raw", "data": jsonString, "timestamp": DateTime.now().millisecondsSinceEpoch}); + } + }, onError: (e) { + _appendGatewayEvent({"type": "error", "data": e.toString(), "timestamp": DateTime.now().millisecondsSinceEpoch}); + }); + + // _loansSub = ScLoanEvents.eventStream.listen((event) { + // _appendLoansEvent(event); + // }, onError: (e) { + // _appendLoansEvent({"type": "error", "data": e.toString(), "timestamp": DateTime.now().millisecondsSinceEpoch}); + // }); } static SmartInvestingAppRepository? _singleton; @@ -30,16 +53,22 @@ class SmartInvestingAppRepository { return SmartInvesting.fromEnvironment(_singleton!.scGatewayConfig.value); } - final environment = BehaviorSubject.seeded(SIEnvironment.PRODUCTION); + final environment = BehaviorSubject.seeded(si.SIEnvironment.PRODUCTION); - final siConfig = BehaviorSubject.seeded(SIConfig(loansUserId: "020896")); - final scGatewayConfig = BehaviorSubject.seeded(ScGatewayConfig.prod()); - final scLoanConfig = BehaviorSubject.seeded(ScLoanConfig.prod()); + final siConfig = BehaviorSubject.seeded(si.SIConfig(loansUserId: "020896")); + final scGatewayConfig = BehaviorSubject.seeded(si.ScGatewayConfig.prod()); + final scLoanConfig = BehaviorSubject.seeded(si.ScLoanConfig.prod()); final smartInvestingUserId = BehaviorSubject.seeded(null); final customAuthToken = BehaviorSubject.seeded(null); var appState = BehaviorSubject.seeded("/"); + // Live events log + final gatewayEvents = BehaviorSubject>>.seeded(const []); + final loansEvents = BehaviorSubject>>.seeded(const []); + StreamSubscription? _gatewaySub; + StreamSubscription? _loansSub; + //SMT Screen final headerColor = BehaviorSubject.seeded(null); final headerOpacity = BehaviorSubject.seeded(null); @@ -174,10 +203,30 @@ Future triggerTransaction( } dispose() { + _gatewaySub?.cancel(); + _loansSub?.cancel(); environment.close(); scGatewayConfig.close(); scLoanConfig.close(); transactionID.close(); + gatewayEvents.close(); + loansEvents.close(); _singleton = null; } } + +extension on SmartInvestingAppRepository { + void _appendGatewayEvent(Map event) { + final current = List>.from(gatewayEvents.value); + if (current.length >= 200) current.removeAt(0); + current.add(event); + gatewayEvents.add(current); + } + + void _appendLoansEvent(Map event) { + final current = List>.from(loansEvents.value); + if (current.length >= 200) current.removeAt(0); + current.add(event); + loansEvents.add(current); + } +} diff --git a/smart_investing/lib/app/widgets/SIEnvironmentController.dart b/smart_investing/lib/app/widgets/SIEnvironmentController.dart index 6d73ff3b..d18c011b 100644 --- a/smart_investing/lib/app/widgets/SIEnvironmentController.dart +++ b/smart_investing/lib/app/widgets/SIEnvironmentController.dart @@ -15,17 +15,21 @@ class SIEnvironmentController extends StatelessWidget { final data = snapshot.data; return Padding( padding: const EdgeInsets.all(8.0), - child: CupertinoSlidingSegmentedControl( - padding: const EdgeInsets.all(8), - groupValue: data?.index ?? 0, - children: SIEnvironment.values - .asMap() - .map((key, value) => MapEntry(key, Text(value.label))), - onValueChanged: (value) { - if (value == null) return; - repository.environment.value = - SIEnvironment.values.elementAt(value); - }, + child: SizedBox( + width: double.infinity, + child: CupertinoSlidingSegmentedControl( + padding: const EdgeInsets.all(8), + groupValue: data?.index ?? 0, + children: SIEnvironment.values.asMap().map( + (key, value) => MapEntry(key, Text(value.label)), + ), + onValueChanged: (value) { + if (value == null) return; + repository.environment.value = SIEnvironment.values.elementAt( + value, + ); + }, + ), ), ); }, diff --git a/smart_investing/lib/main.dart b/smart_investing/lib/main.dart index d68dbbc8..e4372060 100644 --- a/smart_investing/lib/main.dart +++ b/smart_investing/lib/main.dart @@ -1,6 +1,11 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:go_router/go_router.dart'; +import 'package:scgateway_flutter_plugin/scgateway_flutter_plugin.dart'; +import 'package:scgateway_flutter_plugin/scgateway_events.dart'; +import 'package:scloans/sc_loan.dart'; +import 'package:scloans/sc_loan_events.dart'; import 'app/SIGatewayPage.dart'; import 'app/SILoansPage.dart'; @@ -20,6 +25,8 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { late Brightness _brightness; + StreamSubscription? _gatewayEventsSub; + StreamSubscription? _loansEventsSub; @override void initState() { @@ -31,12 +38,54 @@ class _MyAppState extends State { _brightness = dispatcher.platformBrightness; }); }; + + ScgatewayEvents.startListening(); + _gatewayEventsSub = ScgatewayEvents.eventStream.listen( + (jsonString) { + // Parse the event if needed + final event = ScgatewayEvents.parseEvent(jsonString); + if (event != null) { + print('[Gateway Event] $event'); + } else { + print('[Gateway Event] Raw: $jsonString'); + } + }, + onError: (e) { + print('[Gateway Event][Error] $e'); + }, + ); + + ScLoanEvents.startListening(); + _loansEventsSub = ScLoanEvents.eventStream.listen( + (jsonString) { + // Parse the event if needed + final event = ScLoanEvents.parseEvent(jsonString); + if (event != null) { + print('[Loans Event] $event'); + } else { + print('[Loans Event] Raw: $jsonString'); + } + }, + onError: (e) { + print('[Loans Event][Error] $e'); + }, + ); + super.initState(); } @override void dispose() { + ScgatewayEvents.stopListening(); + ScLoanEvents.stopListening(); + + _gatewayEventsSub?.cancel(); + _loansEventsSub?.cancel(); + + ScgatewayEvents.dispose(); + ScLoanEvents.dispose(); repository.dispose(); + super.dispose(); } diff --git a/smart_investing/macos/Runner.xcodeproj/project.pbxproj b/smart_investing/macos/Runner.xcodeproj/project.pbxproj index 77779bb1..76812a18 100644 --- a/smart_investing/macos/Runner.xcodeproj/project.pbxproj +++ b/smart_investing/macos/Runner.xcodeproj/project.pbxproj @@ -23,10 +23,12 @@ /* Begin PBXBuildFile section */ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 3386FD4695F4F39F29F2078F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A3A7BAABF328B88C2154510 /* Pods_Runner.framework */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 8238E23E6427D595B8C9AC9F /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3604B5BADADDFB1B6BE8B29C /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,11 +62,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2E3E92C6997CEFC919FA6122 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* smart_investing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "smart_investing.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* smart_investing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = smart_investing.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -76,8 +79,15 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 3604B5BADADDFB1B6BE8B29C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4FCCE2AE9A63500777EBB03D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 5F6ADA05A07B064B2330652F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 602C800892E4F4B1FFEF2997 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 6A3A7BAABF328B88C2154510 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + BA5769FF9BD43904662D1C1A /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + BD1922BDA5FADA49B8AC7F6D /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,6 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8238E23E6427D595B8C9AC9F /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,12 +103,27 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3386FD4695F4F39F29F2078F /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 058AF04EF1A655DD9564D55E /* Pods */ = { + isa = PBXGroup; + children = ( + 602C800892E4F4B1FFEF2997 /* Pods-Runner.debug.xcconfig */, + 2E3E92C6997CEFC919FA6122 /* Pods-Runner.release.xcconfig */, + 5F6ADA05A07B064B2330652F /* Pods-Runner.profile.xcconfig */, + 4FCCE2AE9A63500777EBB03D /* Pods-RunnerTests.debug.xcconfig */, + BA5769FF9BD43904662D1C1A /* Pods-RunnerTests.release.xcconfig */, + BD1922BDA5FADA49B8AC7F6D /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -125,6 +151,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 058AF04EF1A655DD9564D55E /* Pods */, ); sourceTree = ""; }; @@ -175,6 +202,8 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + 6A3A7BAABF328B88C2154510 /* Pods_Runner.framework */, + 3604B5BADADDFB1B6BE8B29C /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -186,6 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 954B008C8DD06837A6B356DE /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -204,6 +234,7 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + D38F31643DB635B226C02CC5 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, @@ -329,6 +360,50 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 954B008C8DD06837A6B356DE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D38F31643DB635B226C02CC5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -380,6 +455,7 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4FCCE2AE9A63500777EBB03D /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -394,6 +470,7 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BA5769FF9BD43904662D1C1A /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -408,6 +485,7 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BD1922BDA5FADA49B8AC7F6D /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; diff --git a/smart_investing/macos/Runner.xcworkspace/contents.xcworkspacedata b/smart_investing/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/smart_investing/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/smart_investing/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/smart_investing/pubspec.lock b/smart_investing/pubspec.lock index 0e8f0e39..3c5cc4f0 100644 --- a/smart_investing/pubspec.lock +++ b/smart_investing/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: d93b0378aadce9c1388108067946276582c2ae89426c64c17920c74988508fed + sha256: f0bb5d1648339c8308cc0b9838d8456b3cfe5c91f9dc1a735b4d003269e5da9a url: "https://pub.dev" source: hosted - version: "22.0.0" + version: "88.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "581a0281129283e75d4d67d6ac6e391c0515cdce37eb6eb4bc8a52e65d2b16b6" + sha256: "0b7b9c329d2879f8f05d6c05b32ee9ec025f39b077864bdb5ac9a7b63418a98f" url: "https://pub.dev" source: hosted - version: "1.7.2" + version: "8.1.1" args: dependency: transitive description: @@ -45,50 +45,50 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: ce76b1d48875e3233fde17717c23d1f60a91cc631597e49a400c89b475395b1d url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "3.1.0" build_config: dependency: transitive description: name: build_config - sha256: ad77deb6e9c143a3f550fbb4c5c1e0c6aadabe24274898d06b9526c61b9cf4fb + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.2.0" build_daemon: dependency: transitive description: name: build_daemon - sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "4.0.4" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: a171129ff393d360a5ec9ba3a2277e0d7e713027709f08196e8192688b537074 + sha256: d1d57f7807debd7349b4726a19fd32ec8bc177c71ad0febf91a20f84cd2d4b46 url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "3.0.3" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "361d73f37cd48c47a81a61421eb1cc4cfd2324516fbb52f1bc4c9a01834ef2de" + sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.7.1" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "0db1b64c84fa803603fa406f8721959036e898cc9575d6ce4a3067581b9276c0" + sha256: "066dda7f73d8eb48ba630a55acb50c4a84a2e6b453b1cb4567f581729e794f7b" url: "https://pub.dev" source: hosted - version: "7.2.2" + version: "9.3.1" built_collection: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: built_value - sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27" + sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d url: "https://pub.dev" source: hosted - version: "8.10.1" + version: "8.12.0" characters: dependency: transitive description: @@ -117,42 +117,34 @@ packages: dependency: transitive description: name: checked_yaml - sha256: dd007e4fb8270916820a0d66e24f619266b60773cddd082c6439341645af2659 + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.4" chopper: dependency: "direct main" description: name: chopper - sha256: "813cabd029ad8c020874429a671f2c15f45cfc3ced66b566bfa181a9b61b89b8" + sha256: "35cde96292178809ca4290f42dbb83d832551333c2b18a2226a2538103ab40d3" url: "https://pub.dev" source: hosted - version: "4.0.6" + version: "8.3.0" chopper_generator: dependency: "direct dev" description: name: chopper_generator - sha256: a0e838255ac35d8dfd2f2d6f50c8a79fbc943832f0a996d0183ae96a44fa1970 - url: "https://pub.dev" - source: hosted - version: "4.0.1" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c" + sha256: "9a5d508a239f432ebb3293ec4b2df6ee201537cc87b21c3b2b21dc8c7736da66" url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "8.3.1" clipboard: dependency: "direct main" description: name: clipboard - sha256: "2ec38f0e59878008ceca0ab122e4bfde98847f88ef0f83331362ba4521f565a9" + sha256: "1920c0337f8808be4166c5f1b236301ff381ef69633b0757c502d97f1f740102" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "2.0.2" clock: dependency: transitive description: @@ -165,10 +157,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.11.0" collection: dependency: transitive description: @@ -205,18 +197,18 @@ packages: dependency: transitive description: name: dart_style - sha256: "7f5b48e6a448c4b46250a6113857a00eaa82821ef5a3d7f42e68eb69d1283fa3" + sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.1.2" dio: dependency: "direct main" description: name: dio - sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 url: "https://pub.dev" source: hosted - version: "5.8.0+1" + version: "5.9.0" dio_web_adapter: dependency: transitive description: @@ -225,6 +217,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + equatable: + dependency: transitive + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" fake_async: dependency: transitive description: @@ -258,10 +258,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -276,10 +276,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "4f4a162323c86ffc1245765cfe138872b8f069deb42f7dbb36115fa27f31469b" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "4.0.0" glob: dependency: transitive description: @@ -292,10 +292,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: c5fa45fa502ee880839e3b2152d987c44abae26d064a2376d4aad434cf0f7b15 + sha256: eb059dfe59f08546e9787f895bd01652076f996bcbf485a8609ef990419ad227 url: "https://pub.dev" source: hosted - version: "12.1.3" + version: "16.2.1" graphs: dependency: transitive description: @@ -316,10 +316,10 @@ packages: dependency: "direct main" description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.5.0" http_multi_server: dependency: transitive description: @@ -336,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" io: dependency: transitive description: @@ -344,30 +352,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" json_annotation: dependency: "direct main" description: name: json_annotation - sha256: "0aa7409f6c82acfab96853b8b0c7503de49918cbe705a57cfdeb477756b4521b" + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.9.0" json_serializable: dependency: "direct dev" description: name: json_serializable - sha256: "86d3edf6914d6562ed4c7d9288239fbf1a9ee3c498ed0089a535c0d3703bb323" + sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "6.11.1" leak_tracker: dependency: transitive description: @@ -396,10 +396,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "6.0.0" logging: dependency: "direct main" description: @@ -436,18 +436,10 @@ packages: dependency: transitive description: name: mime - sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.6" - mixpanel_flutter: - dependency: "direct main" - description: - name: mixpanel_flutter - sha256: "0ecd870cceed1cf4600c403e5544b88efa65b8fd3f30fad679702d1ae9e0ac15" - url: "https://pub.dev" - source: hosted - version: "2.4.4" + version: "2.0.0" package_config: dependency: transitive description: @@ -464,14 +456,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" - pedantic: - dependency: transitive - description: - name: pedantic - sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" - url: "https://pub.dev" - source: hosted - version: "1.11.1" plugin_platform_interface: dependency: transitive description: @@ -500,18 +484,26 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: "0e01f805457ef610ccaf8d18067596afc34107a27149778b06b2083edbc140c1" + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.5.0" + qs_dart: + dependency: transitive + description: + name: qs_dart + sha256: "23e435223d985630e3880fd667128f520237059ca3af0cc2dc029d5365ce9ec1" + url: "https://pub.dev" + source: hosted + version: "1.5.6" rxdart: dependency: "direct main" description: name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" url: "https://pub.dev" source: hosted - version: "0.27.7" + version: "0.28.0" scgateway_flutter_plugin: dependency: "direct main" description: @@ -538,10 +530,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "3.0.0" sky_engine: dependency: transitive description: flutter @@ -551,10 +543,18 @@ packages: dependency: transitive description: name: source_gen - sha256: ffb7124eb6752de71e87a122cc50a8a191044add69fd990d76958bc38ee552fd + sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "3.1.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + url: "https://pub.dev" + source: hosted + version: "1.3.8" source_span: dependency: transitive description: @@ -615,10 +615,10 @@ packages: dependency: transitive description: name: timing - sha256: c386d07d7f5efc613479a7c4d9d64b03710b03cfaa7e8ad5f2bfb295a1f0dfad + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.2" typed_data: dependency: transitive description: @@ -647,26 +647,42 @@ packages: dependency: transitive description: name: watcher - sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a" + sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" + weak_map: + dependency: transitive + description: + name: weak_map + sha256: "5f8e5d5ce57dc624db5fae814dd689ccae1f17f92b426e52f0a7cbe7f6f4ab97" + url: "https://pub.dev" + source: hosted + version: "4.0.1" web: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "3.0.3" yaml: dependency: transitive description: @@ -677,4 +693,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.8.1 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.29.0" diff --git a/smart_investing/pubspec.yaml b/smart_investing/pubspec.yaml index c5cb3c8f..74ee5aea 100644 --- a/smart_investing/pubspec.yaml +++ b/smart_investing/pubspec.yaml @@ -30,6 +30,7 @@ environment: dependencies: flutter: sdk: flutter + intl: ^0.20.2 scgateway_flutter_plugin: path: ../scgateway/ @@ -39,18 +40,15 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 - chopper: ^4.0.0 + chopper: ^8.3.0 json_annotation: ^4.0.1 logging: ^1.0.1 dio: ^5.4.0 - http: ^0.13.3 - clipboard: ^0.1.3 + http: ^1.5.0 + clipboard: ^2.0.2 hive: ^2.2.3 - rxdart: ^0.27.7 - go_router: ^12.0.0 - - # partner deps - mixpanel_flutter: ^2.4.4 + rxdart: ^0.28.0 + go_router: ^16.2.1 dev_dependencies: flutter_test: @@ -61,10 +59,10 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^5.0.0 + flutter_lints: ^6.0.0 build_runner: ^2.0.6 - chopper_generator: ^4.0.1 - json_serializable: ^4.1.4 + chopper_generator: ^8.3.1 + json_serializable: ^6.11.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec