diff --git a/example/lib/home_page.dart b/example/lib/home_page.dart index d76eed6a..575a4988 100644 --- a/example/lib/home_page.dart +++ b/example/lib/home_page.dart @@ -80,10 +80,9 @@ class _MyHomePageState extends State { // If you want to support just one chain uncomment this line and avoid using W3MNetworkSelectButton() // _w3mService.selectChain(W3MChainPresets.chains['137']); - // TODO this shouldn't be needed - _w3mService.addListener(_updateState); // _w3mService.onModalConnect.subscribe(_onModalConnect); + _w3mService.onModalNetworkChange.subscribe(_onModalNetworkChange); _w3mService.onModalDisconnect.subscribe(_onModalDisconnect); _w3mService.onModalError.subscribe(_onModalError); // @@ -97,9 +96,9 @@ class _MyHomePageState extends State { @override void dispose() { - _w3mService.removeListener(_updateState); // _w3mService.onModalConnect.unsubscribe(_onModalConnect); + _w3mService.onModalNetworkChange.unsubscribe(_onModalNetworkChange); _w3mService.onModalDisconnect.unsubscribe(_onModalDisconnect); _w3mService.onModalError.unsubscribe(_onModalError); // @@ -110,12 +109,13 @@ class _MyHomePageState extends State { super.dispose(); } - void _updateState() { + void _onModalConnect(ModalConnect? event) { + debugPrint('[ExampleApp] _onModalConnect ${event?.toString()}'); setState(() {}); } - void _onModalConnect(ModalConnect? event) { - debugPrint('[ExampleApp] _onModalConnect ${event?.toString()}'); + void _onModalNetworkChange(ModalNetworkChange? event) { + debugPrint('[ExampleApp] _onModalNetworkChange ${event?.toString()}'); setState(() {}); } @@ -215,9 +215,9 @@ class _ButtonsView extends StatelessWidget { const SizedBox.square(dimension: 8.0), Visibility( visible: !w3mService.isConnected, - child: W3MNetworkSelectButton(service: w3mService), + child: W3MNetworkSelectButton(service: w3mService, context: context), ), - W3MConnectWalletButton(service: w3mService), + W3MConnectWalletButton(service: w3mService, context: context), // W3MAccountButton(service: w3mService), const SizedBox.square(dimension: 8.0), ], @@ -276,7 +276,7 @@ class _ConnectedView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox.square(dimension: 12.0), - W3MAccountButton(service: w3mService), + W3MAccountButton(service: w3mService, context: context), SessionWidget( w3mService: w3mService, launchRedirect: () { diff --git a/example/lib/widgets/logger_widget.dart b/example/lib/widgets/logger_widget.dart index 736757c7..93d6433f 100644 --- a/example/lib/widgets/logger_widget.dart +++ b/example/lib/widgets/logger_widget.dart @@ -116,8 +116,8 @@ class _DraggableCardState extends State { class OverlayController extends AnimatedOverlay { OverlayController(super.duration); OverlayEntry? _entry; - final _defaultAlign = const Alignment(0.0, -2.0); - Alignment align = const Alignment(0.0, -2.0); + final _defaultAlign = const Alignment(0.0, -30.0); + Alignment align = const Alignment(0.0, -30.0); Animation? alignAnimation; OverlayEntry createAlignOverlay(Widget child) { diff --git a/lib/services/analytics_service/analytics_service.dart b/lib/services/analytics_service/analytics_service.dart index 2d64bf80..55433a5b 100644 --- a/lib/services/analytics_service/analytics_service.dart +++ b/lib/services/analytics_service/analytics_service.dart @@ -45,9 +45,9 @@ class AnalyticsService implements IAnalyticsService { _endpoint = kDebugMode ? _debugApiEndpoint : await coreUtils.instance.getAnalyticsUrl(); - loggerService.instance.i('[$runtimeType] init enabled: $_isEnabled'); + loggerService.instance.p('[$runtimeType] enabled: $_isEnabled'); } catch (e, s) { - loggerService.instance.e( + loggerService.instance.p( '[$runtimeType] init error', error: e, stackTrace: s, @@ -66,11 +66,10 @@ class AnalyticsService implements IAnalyticsService { ); final json = jsonDecode(response.body) as Map; final enabled = json['isAnalyticsEnabled'] as bool?; - loggerService.instance.i('[$runtimeType] fetchAnalyticsConfig $enabled'); return enabled ?? false; } catch (e, s) { - loggerService.instance.e( - '[$runtimeType] fetchAnalyticsConfig error', + loggerService.instance.p( + '[$runtimeType] fetch remote configuration error', error: e, stackTrace: s, ); @@ -99,11 +98,13 @@ class AnalyticsService implements IAnalyticsService { body: body, ); final code = response.statusCode; - loggerService.instance.i('[$runtimeType] sendEvent ::$body:: $code'); - _eventsController.sink.add(analyticsEvent.toMap()); + if (code == 200 || code == 202) { + _eventsController.sink.add(analyticsEvent.toMap()); + } + loggerService.instance.p('[$runtimeType] send event $code: $body'); } catch (e, s) { - loggerService.instance.e( - '[$runtimeType] sendEvent error', + loggerService.instance.p( + '[$runtimeType] send event error', error: e, stackTrace: s, ); diff --git a/lib/services/logger_service/i_logger_service.dart b/lib/services/logger_service/i_logger_service.dart index ce760de0..b8f9544d 100644 --- a/lib/services/logger_service/i_logger_service.dart +++ b/lib/services/logger_service/i_logger_service.dart @@ -58,6 +58,14 @@ abstract class ILoggerService { StackTrace? stackTrace, }); + /// Log a message at level private. + void p( + dynamic message, { + DateTime? time, + Object? error, + StackTrace? stackTrace, + }); + /// Closes the logger and releases all resources. Future close(); } diff --git a/lib/services/logger_service/logger_service.dart b/lib/services/logger_service/logger_service.dart index 4d8869dc..0cdfe09f 100644 --- a/lib/services/logger_service/logger_service.dart +++ b/lib/services/logger_service/logger_service.dart @@ -6,7 +6,13 @@ import 'package:web3modal_flutter/web3modal_flutter.dart'; class LoggerService implements ILoggerService { late Logger _logger; - LoggerService({required LogLevel level, bool debugMode = true}) { + late String _projectId; + LoggerService({ + required LogLevel level, + required String projectId, + bool debugMode = true, + }) { + _projectId = projectId; _logger = Logger( level: level.toLevel(), printer: PrettyPrinter(methodCount: null), @@ -20,6 +26,19 @@ class LoggerService implements ILoggerService { debugPrint('${event.message}'); } + @override + void p( + message, { + DateTime? time, + Object? error, + StackTrace? stackTrace, + }) { + // TODO fix this + if (_projectId == 'cad4956f31a5e40a00b62865b030c6f8') { + _logger.i(message, time: time, error: error, stackTrace: stackTrace); + } + } + @override void d( message, { diff --git a/lib/services/magic_service/magic_service.dart b/lib/services/magic_service/magic_service.dart index c02d9344..2db4c388 100644 --- a/lib/services/magic_service/magic_service.dart +++ b/lib/services/magic_service/magic_service.dart @@ -85,6 +85,7 @@ class MagicService implements IMagicService { }) : _web3app = web3app, _key = key ?? Key('magic_service') { isEnabled.value = enabled; + loggerService.instance.p('[$runtimeType] enabled $enabled'); if (isEnabled.value) { _webViewController = WebViewController(); _webview = WebViewWidget( @@ -142,7 +143,6 @@ class MagicService implements IMagicService { onPageFinished: (String url) async { _onLoadCount++; if (_onLoadCount < 2 && Platform.isAndroid) return; - loggerService.instance.d('[$runtimeType] onPageFinished $url'); await _runJavascript(_web3app.core.projectId); await Future.delayed(Duration(milliseconds: 200)); await _webViewController.enableZoom(false); @@ -321,7 +321,7 @@ class MagicService implements IMagicService { void _onFrameMessage(JavaScriptMessage jsMessage) async { if (Platform.isAndroid) { - loggerService.instance.i('[$runtimeType] jsMessage ${jsMessage.message}'); + loggerService.instance.p('[$runtimeType] jsMessage ${jsMessage.message}'); } try { final frameMessage = jsMessage.toFrameMessage(); @@ -469,7 +469,7 @@ class MagicService implements IMagicService { _error(SignOutErrorEvent()); } } catch (e, s) { - loggerService.instance.e('[$runtimeType] $jsMessage', stackTrace: s); + loggerService.instance.p('[$runtimeType] $jsMessage', stackTrace: s); } } @@ -547,7 +547,7 @@ class MagicService implements IMagicService { void _onDebugConsoleReceived(JavaScriptConsoleMessage message) { if (kDebugMode && Platform.isIOS) { - loggerService.instance.d('[$runtimeType] JS Console ${message.message}'); + loggerService.instance.p('[$runtimeType] JS Console ${message.message}'); } } diff --git a/lib/services/w3m_service/events/w3m_events.dart b/lib/services/w3m_service/events/w3m_events.dart index 7cce2b5c..a5c2466b 100644 --- a/lib/services/w3m_service/events/w3m_events.dart +++ b/lib/services/w3m_service/events/w3m_events.dart @@ -10,6 +10,20 @@ class ModalConnect extends EventArgs { } } +class ModalNetworkChange extends EventArgs { + final String? previous; + final String current; + ModalNetworkChange({ + required this.previous, + required this.current, + }); + + @override + String toString() { + return 'ModalNetworkChange(previous: $previous, current: $current)'; + } +} + class ModalDisconnect extends EventArgs { final String? topic; final int? id; diff --git a/lib/services/w3m_service/i_w3m_service.dart b/lib/services/w3m_service/i_w3m_service.dart index 335acdaf..7d29727d 100644 --- a/lib/services/w3m_service/i_w3m_service.dart +++ b/lib/services/w3m_service/i_w3m_service.dart @@ -52,12 +52,12 @@ abstract class IW3MService with ChangeNotifier { /// Sets up the explorer and the web3App if they already been initialized. Future init(); + Future openNetworks(BuildContext context); + /// Opens the modal with the provided [startWidget] (if any). /// If none is provided, the default state will be used based on platform. Future openModal(BuildContext context, [Widget? startWidget]); - Future openNetworks(BuildContext context); - /// Connects to the relay if not already connected. /// If the relay is already connected, this does nothing. Future reconnectRelay(); @@ -138,6 +138,7 @@ abstract class IW3MService with ChangeNotifier { /* EVENTS DECLARATIONS */ abstract final Event onModalConnect; + abstract final Event onModalNetworkChange; abstract final Event onModalDisconnect; abstract final Event onModalError; // diff --git a/lib/services/w3m_service/w3m_service.dart b/lib/services/w3m_service/w3m_service.dart index 48fd31e9..ae3bcec8 100644 --- a/lib/services/w3m_service/w3m_service.dart +++ b/lib/services/w3m_service/w3m_service.dart @@ -123,7 +123,11 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { } } - loggerService.instance = LoggerService(level: logLevel, debugMode: true); + loggerService.instance = LoggerService( + level: logLevel, + projectId: projectId ?? _web3App.core.projectId, + debugMode: true, + ); _web3App = web3App ?? Web3App( @@ -377,7 +381,13 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { } void _setEthChain(W3MChainInfo chainInfo, {bool logEvent = false}) async { - loggerService.instance.i('[$runtimeType] set chain ${chainInfo.namespace}'); + loggerService.instance.i( + '[$runtimeType] _setEthChain ${chainInfo.namespace}', + ); + onModalNetworkChange.broadcast(ModalNetworkChange( + previous: _currentSelectedChain?.namespace, + current: chainInfo.namespace, + )); _currentSelectedChain = chainInfo; // Store the chain for when we reload the app. @@ -486,12 +496,12 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { useRootNavigator: true, anchorPoint: Offset(0, 0), context: _context!, - builder: (context) { - final radiuses = Web3ModalTheme.radiusesOf(context); + builder: (_) { + final radiuses = Web3ModalTheme.radiusesOf(_context!); final maxRadius = min(radiuses.radiusM, 36.0); final borderRadius = BorderRadius.all(Radius.circular(maxRadius)); return Dialog( - backgroundColor: Web3ModalTheme.colorsOf(context).background125, + backgroundColor: Web3ModalTheme.colorsOf(_context!).background125, shape: RoundedRectangleBorder(borderRadius: borderRadius), clipBehavior: Clip.hardEdge, child: ConstrainedBox( @@ -750,8 +760,6 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { // If we aren't open, then we can't and shouldn't close _close(event: false); if (_context != null) { - // _isOpen and notify() are handled when we call Navigator.pop() - // by the open() method Navigator.of(_context!, rootNavigator: true).pop(); analyticsService.instance.sendEvent(ModalCloseEvent( connected: _isConnected, @@ -910,6 +918,9 @@ class W3MService with ChangeNotifier, CoinbaseService implements IW3MService { @override final Event onModalConnect = Event(); + @override + final Event onModalNetworkChange = Event(); + @override final Event onModalDisconnect = Event(); diff --git a/lib/widgets/w3m_account_button.dart b/lib/widgets/w3m_account_button.dart index c92edabf..1790dd26 100644 --- a/lib/widgets/w3m_account_button.dart +++ b/lib/widgets/w3m_account_button.dart @@ -21,11 +21,13 @@ class W3MAccountButton extends StatefulWidget { required this.service, this.size = BaseButtonSize.regular, this.avatar, + this.context, }); final IW3MService service; final BaseButtonSize size; final String? avatar; + final BuildContext? context; @override State createState() => _W3MAccountButtonState(); @@ -68,14 +70,17 @@ class _W3MAccountButtonState extends State { }); } - void _onTap() => widget.service.openModal(context); + void _onTap() => widget.service.openModal(widget.context ?? context); void _approveSign(MagicRequestEvent? args) async { if (args?.request != null) { if (widget.service.isOpen) { widgetStack.instance.popAllAndPush(ApproveTransactionPage()); } else { - widget.service.openModal(context, ApproveTransactionPage()); + widget.service.openModal( + widget.context ?? context, + ApproveTransactionPage(), + ); } } } @@ -84,7 +89,10 @@ class _W3MAccountButtonState extends State { if (widget.service.isOpen) { widgetStack.instance.popAllAndPush(ConfirmEmailPage()); } else { - widget.service.openModal(context, ConfirmEmailPage()); + widget.service.openModal( + widget.context ?? context, + ConfirmEmailPage(), + ); } } diff --git a/lib/widgets/w3m_connect_wallet_button.dart b/lib/widgets/w3m_connect_wallet_button.dart index 85153944..77c32e70 100644 --- a/lib/widgets/w3m_connect_wallet_button.dart +++ b/lib/widgets/w3m_connect_wallet_button.dart @@ -12,11 +12,13 @@ class W3MConnectWalletButton extends StatefulWidget { required this.service, this.size = BaseButtonSize.regular, this.state, + this.context, }); final IW3MService service; final BaseButtonSize size; final ConnectButtonState? state; + final BuildContext? context; @override State createState() => _W3MConnectWalletButtonState(); @@ -48,11 +50,12 @@ class _W3MConnectWalletButtonState extends State { @override Widget build(BuildContext context) { + final emailEnabled = magicService.instance.isEnabled.value; return Stack( alignment: AlignmentDirectional.center, children: [ // if (_state == ConnectButtonState.connected) - if (Platform.isIOS) + if (Platform.isIOS && emailEnabled) SizedBox( width: 1.0, height: 1.0, @@ -62,17 +65,17 @@ class _W3MConnectWalletButtonState extends State { serviceStatus: widget.service.status, state: _state, size: widget.size, - onTap: () => _onTap(context), + onTap: _onTap, ), ], ); } - void _onTap(BuildContext context) { + void _onTap() { if (widget.service.isConnected) { widget.service.disconnect(); } else { - widget.service.openModal(context); + widget.service.openModal(widget.context ?? context); _updateState(); } } diff --git a/lib/widgets/w3m_network_select_button.dart b/lib/widgets/w3m_network_select_button.dart index a77d048b..8a31954f 100644 --- a/lib/widgets/w3m_network_select_button.dart +++ b/lib/widgets/w3m_network_select_button.dart @@ -14,10 +14,12 @@ class W3MNetworkSelectButton extends StatefulWidget { super.key, required this.service, this.size = BaseButtonSize.regular, + this.context, }); final IW3MService service; final BaseButtonSize size; + final BuildContext? context; @override State createState() => _W3MNetworkSelectButtonState(); @@ -45,14 +47,14 @@ class _W3MNetworkSelectButtonState extends State { serviceStatus: widget.service.status, chainInfo: _selectedChain, size: widget.size, - onTap: () => _onConnectPressed(context), + onTap: () => _onConnectPressed(), ); } - void _onConnectPressed(BuildContext context) { + void _onConnectPressed() { analyticsService.instance.sendEvent(ClickNetworksEvent()); widget.service.openModal( - context, + widget.context ?? context, SelectNetworkPage( onTapNetwork: (info) { widget.service.selectChain(info); diff --git a/test/mock_classes.mocks.dart b/test/mock_classes.mocks.dart index eed8c230..55391c89 100644 --- a/test/mock_classes.mocks.dart +++ b/test/mock_classes.mocks.dart @@ -496,6 +496,15 @@ class MockW3MService extends _i1.Mock implements _i3.W3MService { ), ) as _i3.Event<_i3.ModalConnect>); @override + _i3.Event<_i3.ModalNetworkChange> get onModalNetworkChange => + (super.noSuchMethod( + Invocation.getter(#onModalNetworkChange), + returnValue: _FakeEvent_1<_i3.ModalNetworkChange>( + this, + Invocation.getter(#onModalNetworkChange), + ), + ) as _i3.Event<_i3.ModalNetworkChange>); + @override _i3.Event<_i3.ModalDisconnect> get onModalDisconnect => (super.noSuchMethod( Invocation.getter(#onModalDisconnect), returnValue: _FakeEvent_1<_i3.ModalDisconnect>(