From 7a557ad3fa54e5a9561ff1680828772fe1c67140 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 12:47:07 +0800 Subject: [PATCH 01/14] activity section added waiting for final design --- .../lib/v2/screens/home/activity_section.dart | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/mobile-app/lib/v2/screens/home/activity_section.dart b/mobile-app/lib/v2/screens/home/activity_section.dart index 1be9c4aa..82ec1db1 100644 --- a/mobile-app/lib/v2/screens/home/activity_section.dart +++ b/mobile-app/lib/v2/screens/home/activity_section.dart @@ -12,6 +12,7 @@ import 'package:resonance_network_wallet/v2/screens/activity/transaction_detail_ import 'package:resonance_network_wallet/v2/screens/activity/tx_item.dart'; import 'package:resonance_network_wallet/v2/theme/app_colors.dart'; import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart'; +import 'package:url_launcher/url_launcher.dart'; class ActivitySection extends ConsumerWidget { final AsyncValue txAsync; @@ -44,12 +45,8 @@ class ActivitySection extends ConsumerWidget { children: [ const SizedBox(height: 40), _header(colors, text, context), - const SizedBox(height: 48), - Icon(Icons.receipt_long_outlined, size: 48, color: colors.textTertiary), - const SizedBox(height: 16), - Text('No transactions yet', style: text.paragraph?.copyWith(color: colors.textSecondary)), - const SizedBox(height: 8), - Text('Your activity will appear here', style: text.detail?.copyWith(color: colors.textTertiary)), + const SizedBox(height: 24), + _getStartedLinks(text, colors), ], ); } @@ -123,6 +120,37 @@ class ActivitySection extends ConsumerWidget { ); } + Widget _getStartedLinks(AppTextTheme text, AppColorsV2 colors) { + final linkStyle = text.smallParagraph?.copyWith(color: colors.textPrimary); + final links = [ + ('Get Testnet Tokens →', AppConstants.faucetBotUrl), + ('Tutorials & Guides →', AppConstants.tutorialsAndGuidesUrl), + ('Community →', AppConstants.communityUrl), + ('Tech Support →', AppConstants.helpAndSupportUrl), + ]; + + return Container( + width: double.infinity, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: const Color(0x3F000000), + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (var i = 0; i < links.length; i++) ...[ + GestureDetector( + onTap: () => launchUrl(Uri.parse(links[i].$2)), + child: Text(links[i].$1, style: linkStyle), + ), + if (i < links.length - 1) const SizedBox(height: 25), + ], + ], + ), + ); + } + Widget _header(AppColorsV2 colors, AppTextTheme text, BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, From b0b5f2d29e48b4f4b3a44d33a920bbce85794676 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 12:55:27 +0800 Subject: [PATCH 02/14] better tech support url --- mobile-app/lib/features/components/get_started.dart | 2 +- mobile-app/lib/features/main/screens/settings_screen.dart | 2 +- mobile-app/lib/v2/screens/home/activity_section.dart | 3 +-- mobile-app/lib/v2/screens/settings/settings_screen.dart | 2 +- quantus_sdk/lib/src/constants/app_constants.dart | 6 +++++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mobile-app/lib/features/components/get_started.dart b/mobile-app/lib/features/components/get_started.dart index a981323b..3b3b0161 100644 --- a/mobile-app/lib/features/components/get_started.dart +++ b/mobile-app/lib/features/components/get_started.dart @@ -52,7 +52,7 @@ class GetStarted extends StatelessWidget { const SizedBox(height: 25), GestureDetector( onTap: () { - final Uri url = Uri.parse(AppConstants.helpAndSupportUrl); + final Uri url = Uri.parse(AppConstants.techSupportUrl); launchUrl(url); }, child: Text('Tech Support →', style: context.themeText.smallParagraph), diff --git a/mobile-app/lib/features/main/screens/settings_screen.dart b/mobile-app/lib/features/main/screens/settings_screen.dart index d83680f2..624199cb 100644 --- a/mobile-app/lib/features/main/screens/settings_screen.dart +++ b/mobile-app/lib/features/main/screens/settings_screen.dart @@ -203,7 +203,7 @@ class _SettingsScreenState extends ConsumerState { ListItem( title: 'Help & Support', onTap: () { - final Uri url = Uri.parse(AppConstants.helpAndSupportUrl); + final Uri url = Uri.parse(AppConstants.techSupportUrl); launchUrl(url); }, trailing: const Icon(Icons.arrow_outward_sharp), diff --git a/mobile-app/lib/v2/screens/home/activity_section.dart b/mobile-app/lib/v2/screens/home/activity_section.dart index 82ec1db1..eaad0626 100644 --- a/mobile-app/lib/v2/screens/home/activity_section.dart +++ b/mobile-app/lib/v2/screens/home/activity_section.dart @@ -124,9 +124,8 @@ class ActivitySection extends ConsumerWidget { final linkStyle = text.smallParagraph?.copyWith(color: colors.textPrimary); final links = [ ('Get Testnet Tokens →', AppConstants.faucetBotUrl), - ('Tutorials & Guides →', AppConstants.tutorialsAndGuidesUrl), ('Community →', AppConstants.communityUrl), - ('Tech Support →', AppConstants.helpAndSupportUrl), + ('Tech Support →', AppConstants.techSupportUrl), ]; return Container( diff --git a/mobile-app/lib/v2/screens/settings/settings_screen.dart b/mobile-app/lib/v2/screens/settings/settings_screen.dart index 5ca3374f..ca5d463e 100644 --- a/mobile-app/lib/v2/screens/settings/settings_screen.dart +++ b/mobile-app/lib/v2/screens/settings/settings_screen.dart @@ -244,7 +244,7 @@ class _SettingsScreenV2State extends ConsumerState { null, colors, text, - onTap: () => launchUrl(Uri.parse(AppConstants.helpAndSupportUrl)), + onTap: () => launchUrl(Uri.parse(AppConstants.techSupportUrl)), ), _divider(colors), _externalItem( diff --git a/quantus_sdk/lib/src/constants/app_constants.dart b/quantus_sdk/lib/src/constants/app_constants.dart index 131d070e..12f31dfb 100644 --- a/quantus_sdk/lib/src/constants/app_constants.dart +++ b/quantus_sdk/lib/src/constants/app_constants.dart @@ -26,7 +26,11 @@ class AppConstants { static const String senotiEndpoint = 'http://localhost:3100/api'; static const String explorerEndpoint = 'https://explorer.quantus.com'; - static const String helpAndSupportUrl = 'https://t.me/c/quantusnetwork/2457'; + + // internal group URL is this (note the /c) + // https://t.me/c/quantusnetwork/2457 + // removing the c, we get a better preview page though so we use it without c... + static const String techSupportUrl = 'https://t.me/quantusnetwork/2457'; static const String termsOfServiceUrl = 'https://www.quantus.com/terms-and-privacy'; static const String tutorialsAndGuidesUrl = 'https://github.com/Quantus-Network/chain'; static const String shillQuestsPageUrl = 'https://www.quantus.com/quests/shill'; From b2efa323ac824812f8070b23b5af6b7e2326012d Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 12:56:18 +0800 Subject: [PATCH 03/14] format --- mobile-app/lib/v2/screens/home/activity_section.dart | 5 +---- quantus_sdk/lib/src/constants/app_constants.dart | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mobile-app/lib/v2/screens/home/activity_section.dart b/mobile-app/lib/v2/screens/home/activity_section.dart index eaad0626..72e6aef6 100644 --- a/mobile-app/lib/v2/screens/home/activity_section.dart +++ b/mobile-app/lib/v2/screens/home/activity_section.dart @@ -131,10 +131,7 @@ class ActivitySection extends ConsumerWidget { return Container( width: double.infinity, padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: const Color(0x3F000000), - borderRadius: BorderRadius.circular(5), - ), + decoration: BoxDecoration(color: const Color(0x3F000000), borderRadius: BorderRadius.circular(5)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/quantus_sdk/lib/src/constants/app_constants.dart b/quantus_sdk/lib/src/constants/app_constants.dart index 12f31dfb..b0bf8831 100644 --- a/quantus_sdk/lib/src/constants/app_constants.dart +++ b/quantus_sdk/lib/src/constants/app_constants.dart @@ -29,7 +29,7 @@ class AppConstants { // internal group URL is this (note the /c) // https://t.me/c/quantusnetwork/2457 - // removing the c, we get a better preview page though so we use it without c... + // removing the c, we get a better preview page though so we use it without c... static const String techSupportUrl = 'https://t.me/quantusnetwork/2457'; static const String termsOfServiceUrl = 'https://www.quantus.com/terms-and-privacy'; static const String tutorialsAndGuidesUrl = 'https://github.com/Quantus-Network/chain'; From 54a5fb12358479795ee7762c434dd243f2632ae4 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 13:23:35 +0800 Subject: [PATCH 04/14] biometrics refactor --- .../authentication_settings_screen.dart | 271 ------- .../main/screens/settings_screen.dart | 8 - .../lib/providers/local_auth_provider.dart | 4 - .../lib/services/local_auth_service.dart | 158 +--- .../v2/screens/settings/auto_lock_screen.dart | 122 --- .../screens/settings/change_pin_screen.dart | 245 ------ .../settings/recovery_phrase_screen.dart | 11 +- .../v2/screens/settings/settings_screen.dart | 80 +- .../widget/send_screen_widget_test.mocks.dart | 746 +++++++++++++----- .../lib/src/services/settings_service.dart | 56 -- 10 files changed, 584 insertions(+), 1117 deletions(-) delete mode 100644 mobile-app/lib/features/main/screens/authentication_settings_screen.dart delete mode 100644 mobile-app/lib/v2/screens/settings/auto_lock_screen.dart delete mode 100644 mobile-app/lib/v2/screens/settings/change_pin_screen.dart diff --git a/mobile-app/lib/features/main/screens/authentication_settings_screen.dart b/mobile-app/lib/features/main/screens/authentication_settings_screen.dart deleted file mode 100644 index 5ff36376..00000000 --- a/mobile-app/lib/features/main/screens/authentication_settings_screen.dart +++ /dev/null @@ -1,271 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/features/components/scaffold_base.dart'; -import 'package:resonance_network_wallet/features/components/sphere.dart'; -import 'package:resonance_network_wallet/features/components/wallet_app_bar.dart'; -import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart'; -import 'package:resonance_network_wallet/features/styles/app_text_theme.dart'; -import 'package:resonance_network_wallet/services/local_auth_service.dart'; -import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart'; - -class AuthConfigItem { - final int value; // should be time in minutes - final String label; - - AuthConfigItem({required this.value, required this.label}); -} - -class AuthenticationSettingsScreen extends StatefulWidget { - const AuthenticationSettingsScreen({super.key}); - - @override - State createState() => _AuthenticationSettingsScreenState(); -} - -class _AuthenticationSettingsScreenState extends State { - final LocalAuthService _localAuthService = LocalAuthService(); - final _authConfigList = [ - AuthConfigItem(value: 0, label: 'Immediately'), - AuthConfigItem(value: 1, label: '1 minute'), - AuthConfigItem(value: 5, label: '5 minutes'), - AuthConfigItem(value: 15, label: '15 minutes'), - AuthConfigItem(value: 30, label: '30 minutes'), - AuthConfigItem(value: 60, label: '1 hour'), - ]; - - bool _isDeviceAuthEnabled = false; - bool _isLoading = true; - String _biometricDescription = 'Use Device Authentication'; - late int _authTimeout; - - @override - void initState() { - super.initState(); - _authTimeout = _localAuthService.getAuthTimeoutMinutes(); - _loadAuthenticationSettings(); - } - - Future _loadAuthenticationSettings() async { - try { - final isEnabled = _localAuthService.isLocalAuthEnabled(); - final authTimeout = _localAuthService.getAuthTimeoutMinutes(); - final isAvailable = await _localAuthService.isBiometricAvailable(); - final description = await _localAuthService.getBiometricDescription(); - - if (mounted) { - setState(() { - _isDeviceAuthEnabled = isEnabled; - _biometricDescription = isAvailable ? description : 'Biometric authentication not available'; - _isLoading = false; - _authTimeout = authTimeout; - }); - } - } catch (e) { - debugPrint('Error loading authentication settings: $e'); - if (mounted) { - setState(() { - _isLoading = false; - }); - } - } - } - - Future _toggleAuthentication(bool enable) async { - setState(() { - _isLoading = true; - }); - - try { - // on enable, check if the device supports biometrics. - if (enable) { - final isAvailable = await _localAuthService.isBiometricAvailable(); - debugPrint('Biometric available: $isAvailable'); - - if (!isAvailable) { - debugPrint('Biometric authentication not available'); - if (mounted) { - setState(() { - _isLoading = false; - }); - } - _showSnackBar('Biometric authentication is not available on this device', isSuccess: false); - return; - } - } - - debugPrint('Attempting to authenticate...'); - final didAuthenticate = await _localAuthService.authenticate( - localizedReason: - 'Authenticate to ${enable ? 'enable' : 'disable'} device ' - 'authentication for your wallet', - biometricOnly: false, // Allow fallback to device PIN if needed - forSetup: true, // This is a setup flow, so bypass the enabled check - ); - - debugPrint('Authentication result: $didAuthenticate'); - - if (didAuthenticate) { - _localAuthService.setLocalAuthEnabled(enable); - if (mounted) { - setState(() { - _isDeviceAuthEnabled = enable; - _isLoading = false; - }); - } - _showSnackBar( - 'Device authentication ${enable ? 'enabled' : 'disabled'} ' - 'successfully', - isSuccess: true, - ); - } else { - debugPrint('Authentication failed or was cancelled'); - if (mounted) { - setState(() { - _isLoading = false; - }); - } - _showSnackBar( - 'Authentication failed. Device authentication not ' - '${enable ? 'enabled' : 'disabled'}.', - isSuccess: false, - ); - } - } catch (e) { - debugPrint('Error in authentication toggle: $e'); - if (mounted) { - setState(() { - _isLoading = false; - }); - } - _showSnackBar('Failed to toggle authentication: $e', isSuccess: false); - } - } - - void _setAuthTimeout(int timeoutDurationInMinutes) { - _localAuthService.setAuthTimeoutMinutes(timeoutDurationInMinutes); - setState(() { - _authTimeout = timeoutDurationInMinutes; - }); - } - - void _showSnackBar(String message, {required bool isSuccess}) { - if (!mounted) return; - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - backgroundColor: isSuccess ? Colors.green : Colors.red, - duration: const Duration(seconds: 3), - ), - ); - } - - @override - Widget build(BuildContext context) { - return ScaffoldBase( - decorations: [ - Positioned(top: context.containerHalfHeight * 0.9, right: -100, child: const Sphere(variant: 7, size: 311.489)), - Positioned(top: context.containerHalfHeight * 0.45, right: 10, child: const Sphere(variant: 2, size: 194)), - ], - appBar: WalletAppBar(title: 'Authentication Settings'), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: EdgeInsets.symmetric(vertical: 12, horizontal: context.isTablet ? 18 : 12), - decoration: ShapeDecoration( - color: context.themeColors.buttonGlass, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset('assets/finger_print_icon.svg'), - const SizedBox(width: 12), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Authentication', style: context.themeText.largeTag), - const SizedBox(height: 4), - Text( - _isLoading ? 'Loading...' : _biometricDescription, - style: context.themeText.detail?.copyWith(color: context.themeColors.textMuted), - ), - ], - ), - ), - _isLoading - ? SizedBox( - width: context.isTablet ? 28 : 20, - height: context.isTablet ? 28 : 20, - child: const CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Color(0xFF16CECE)), - ), - ) - : CupertinoSwitch( - value: _isDeviceAuthEnabled, - onChanged: _toggleAuthentication, - activeTrackColor: context.themeColors.buttonSuccess, - inactiveTrackColor: context.themeColors.textMuted, - thumbColor: context.themeColors.buttonNeutral, - ), - ], - ), - ), - if (!_isLoading && _isDeviceAuthEnabled) ...[ - const SizedBox(height: 29), - Text('Require Authentication', style: context.themeText.smallParagraph), - const SizedBox(height: 9), - Container( - padding: const EdgeInsets.symmetric(vertical: 16), - decoration: ShapeDecoration( - color: context.themeColors.buttonGlass, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), - ), - child: Column( - children: _authConfigList.map((item) { - final isFirstItem = item.value == 0; - final isLastItem = item.value == 60; - - final double topPadding = isFirstItem ? 0 : 15; - final double bottomPadding = isLastItem ? 0 : 15; - - final border = BorderSide(color: Colors.black.useOpacity(0.65), width: 0.6); - final topBorder = isFirstItem ? BorderSide.none : border; - final bottomBorder = isLastItem ? BorderSide.none : border; - - return InkWell( - onTap: () { - _setAuthTimeout(item.value); - }, - child: Container( - decoration: BoxDecoration( - border: Border(top: topBorder, bottom: bottomBorder), - ), - padding: EdgeInsets.only(top: topPadding, bottom: bottomPadding, left: 18, right: 18), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(item.label, style: context.themeText.smallParagraph), - if (item.value == _authTimeout) - Icon(Icons.check_circle, color: context.themeColors.buttonSuccess, size: 16), - ], - ), - ), - ); - }).toList(), - ), - ), - ], - ], - ), - ); - } -} diff --git a/mobile-app/lib/features/main/screens/settings_screen.dart b/mobile-app/lib/features/main/screens/settings_screen.dart index 624199cb..55949f03 100644 --- a/mobile-app/lib/features/main/screens/settings_screen.dart +++ b/mobile-app/lib/features/main/screens/settings_screen.dart @@ -9,7 +9,6 @@ import 'package:resonance_network_wallet/features/components/scaffold_base.dart' import 'package:resonance_network_wallet/features/components/sphere.dart'; import 'package:resonance_network_wallet/features/components/wallet_app_bar.dart'; import 'package:resonance_network_wallet/v2/screens/home/accounts_sheet.dart'; -import 'package:resonance_network_wallet/features/main/screens/authentication_settings_screen.dart'; import 'package:resonance_network_wallet/features/main/screens/notifications_settings_screen.dart'; import 'package:resonance_network_wallet/features/main/screens/select_wallet_for_recovery_phrase_screen.dart'; import 'package:resonance_network_wallet/features/main/screens/show_recovery_phrase_screen.dart'; @@ -170,13 +169,6 @@ class _SettingsScreenState extends ConsumerState { }, ), const SizedBox(height: 22), - ListItem( - title: 'Authentication', - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const AuthenticationSettingsScreen())); - }, - ), - const SizedBox(height: 22), ListItem( title: 'Show Recovery Phrase', onTap: () { diff --git a/mobile-app/lib/providers/local_auth_provider.dart b/mobile-app/lib/providers/local_auth_provider.dart index f2531fb1..0493b61e 100644 --- a/mobile-app/lib/providers/local_auth_provider.dart +++ b/mobile-app/lib/providers/local_auth_provider.dart @@ -38,20 +38,16 @@ class LocalAuthController extends StateNotifier { state = state.copyWith(isAuthenticated: didAuthenticate, isAuthenticating: false); } - /// Checks if auth is required by user settings and triggers it. - /// If auth is not required, it sets the state to authenticated. void checkAuthentication() { if (_localAuthService.shouldRequireAuthentication()) { state = state.copyWith(isAuthenticated: false); authenticate(); } else { - // If user has biometrics disabled, just let them in. state = state.copyWith(isAuthenticated: true); } } void lockApp() { - print('lockApp CALLED HERE'); _localAuthService.updateLastPausedTime(); state = state.copyWith(isAuthenticated: false); } diff --git a/mobile-app/lib/services/local_auth_service.dart b/mobile-app/lib/services/local_auth_service.dart index 31a5b6f2..55707025 100644 --- a/mobile-app/lib/services/local_auth_service.dart +++ b/mobile-app/lib/services/local_auth_service.dart @@ -11,31 +11,24 @@ class LocalAuthService { final LocalAuthentication _localAuth = LocalAuthentication(); final SettingsService _settingsService = SettingsService(); - /// Check if biometric authentication is available on the device + static const _authTimeout = Duration(seconds: 30); + Future isBiometricAvailable() async { try { - final bool isDeviceSupported = await _localAuth.isDeviceSupported(); - debugPrint('Device supported: $isDeviceSupported'); - + final isDeviceSupported = await _localAuth.isDeviceSupported(); if (!isDeviceSupported) return false; - final bool canCheckBiometrics = await _localAuth.canCheckBiometrics; - debugPrint('Can check biometrics: $canCheckBiometrics'); - - if (!canCheckBiometrics) return false; - - // Additional check for enrolled biometrics - final List availableBiometrics = await getAvailableBiometrics(); - debugPrint('Available biometric types: $availableBiometrics'); + final canCheck = await _localAuth.canCheckBiometrics; + if (!canCheck) return false; - return availableBiometrics.isNotEmpty; + final available = await getAvailableBiometrics(); + return available.isNotEmpty; } catch (e) { debugPrint('Error checking biometric availability: $e'); return false; } } - /// Get list of available biometric types Future> getAvailableBiometrics() async { try { return await _localAuth.getAvailableBiometrics(); @@ -45,151 +38,41 @@ class LocalAuthService { } } - /// Check if local authentication is enabled in app settings - bool isLocalAuthEnabled() { - return _settingsService.isAuthEnabled(); - } - - /// Enable or disable local authentication in app settings - void setLocalAuthEnabled(bool enabled) { - _settingsService.setAuthEnabled(enabled); - } - - int getAuthTimeoutMinutes() { - return _settingsService.getAuthTimeout() ?? 1; - } - - void setAuthTimeoutMinutes(int timeoutDurationInMinutes) { - return _settingsService.setAuthTimeout(timeoutDurationInMinutes); - } - - /// Authenticate using biometrics Future authenticate({ String localizedReason = 'Please authenticate to access your wallet', - bool biometricOnly = false, - bool forSetup = false, // Add this parameter for setup flows }) async { try { - // Check if biometrics are available - final bool isAvailable = await isBiometricAvailable(); - if (!isAvailable) { - debugPrint('Biometric authentication is not available'); - // don't authenticate if auth is enabled but someone ripped out the - //FaceID cam or whatever? - // On a device that doesn't have biometrics, you also can't turn it on. - // Both auth cases should return false in this case. - return false; - } + final isAvailable = await isBiometricAvailable(); + if (!isAvailable) return true; - if (!forSetup) { - final bool isEnabled = isLocalAuthEnabled(); - if (!isEnabled) { - debugPrint('Local authentication is disabled in settings'); - return true; // Allow access if not enabled - } - } - - final bool didAuthenticate = await _localAuth.authenticate( + final didAuthenticate = await _localAuth.authenticate( localizedReason: localizedReason, - options: AuthenticationOptions(biometricOnly: biometricOnly, stickyAuth: true, sensitiveTransaction: true), + options: const AuthenticationOptions( + biometricOnly: false, + stickyAuth: true, + sensitiveTransaction: true, + ), ); - if (didAuthenticate) { - _cleanLastPausedTime(); - } - + if (didAuthenticate) _cleanLastPausedTime(); return didAuthenticate; } on PlatformException catch (e) { debugPrint('Platform exception during authentication: $e'); - - // Handle specific error cases - switch (e.code) { - case 'NotAvailable': - case 'NotEnrolled': - return false; - case 'LockedOut': - case 'PermanentlyLockedOut': - return false; - case 'UserCancel': - case 'SystemCancel': - return false; - default: - return false; - } + return false; } catch (e) { debugPrint('Error during authentication: $e'); return false; } } - /// Get a user-friendly description of available biometric types - Future getBiometricDescription() async { - try { - final List availableBiometrics = await getAvailableBiometrics(); - - if (availableBiometrics.isEmpty) { - return 'No biometric authentication available'; - } - - final List descriptions = []; - - for (final biometric in availableBiometrics) { - switch (biometric) { - case BiometricType.face: - descriptions.add('Face ID'); - break; - case BiometricType.fingerprint: - descriptions.add('Fingerprint'); - break; - case BiometricType.iris: - descriptions.add('Iris'); - break; - case BiometricType.weak: - descriptions.add('Device PIN/Pattern'); - break; - case BiometricType.strong: - descriptions.add('Strong Biometrics'); - break; - } - } - - if (descriptions.length == 1) { - return descriptions.first; - } else if (descriptions.length == 2) { - return '${descriptions[0]} or ${descriptions[1]}'; - } else { - // ignore: lines_longer_than_80_chars - return '${descriptions.take(descriptions.length - 1).join(', ')}, or ${descriptions.last}'; - } - } catch (e) { - debugPrint('Error getting biometric description: $e'); - return 'Biometric Authentication'; - } - } - - /// Check if authentication is required (based on app lifecycle and settings) bool shouldRequireAuthentication() { try { - final bool isEnabled = isLocalAuthEnabled(); - if (!isEnabled) return false; - - final DateTime? lastPausedTime = _settingsService.getLastPausedTime(); - + final lastPausedTime = _settingsService.getLastPausedTime(); if (lastPausedTime == null) return false; - - final int timeoutDurationInMinutes = getAuthTimeoutMinutes(); - - final Duration authTimeout = Duration(minutes: timeoutDurationInMinutes); - - print('lastPausedTime: $lastPausedTime'); - print('now: ${DateTime.now()}'); - print('auth time difference: ${DateTime.now().difference(lastPausedTime).inSeconds}'); - - final isTimeout = DateTime.now().difference(lastPausedTime) > authTimeout; - return isTimeout; + return DateTime.now().difference(lastPausedTime) > _authTimeout; } catch (e) { debugPrint('Error checking if authentication is required: $e'); - return true; // Err on the side of caution + return true; } } @@ -201,7 +84,6 @@ class LocalAuthService { _settingsService.cleanLastPausedTime(); } - /// Stop local authentication (useful for cleanup) Future stopAuthentication() async { try { await _localAuth.stopAuthentication(); diff --git a/mobile-app/lib/v2/screens/settings/auto_lock_screen.dart b/mobile-app/lib/v2/screens/settings/auto_lock_screen.dart deleted file mode 100644 index 14e19f54..00000000 --- a/mobile-app/lib/v2/screens/settings/auto_lock_screen.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:resonance_network_wallet/services/local_auth_service.dart'; -import 'package:resonance_network_wallet/v2/components/back_button.dart'; -import 'package:resonance_network_wallet/v2/components/glass_container.dart'; -import 'package:resonance_network_wallet/v2/components/gradient_background.dart'; -import 'package:resonance_network_wallet/v2/theme/app_colors.dart'; -import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart'; - -class AutoLockScreen extends StatefulWidget { - const AutoLockScreen({super.key}); - - @override - State createState() => _AutoLockScreenState(); -} - -class _AutoLockScreenState extends State { - final _authService = LocalAuthService(); - late int _selected; - - static const _options = [ - (value: 0, label: '30 Seconds'), - (value: 1, label: '1 minute'), - (value: 5, label: '5 minutes'), - (value: 15, label: '15 minutes'), - (value: 60, label: '1 hour'), - (value: -1, label: 'Never'), - ]; - - @override - void initState() { - super.initState(); - _selected = _authService.getAuthTimeoutMinutes(); - } - - void _confirm() { - _authService.setAuthTimeoutMinutes(_selected); - Navigator.pop(context); - } - - @override - Widget build(BuildContext context) { - final colors = context.colors; - final text = context.themeText; - - return Scaffold( - backgroundColor: colors.background, - body: GradientBackground( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Column( - children: [ - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const AppBackButton(), - Text('Auto-Lock', style: text.smallTitle?.copyWith(color: colors.textPrimary, fontSize: 20)), - const SizedBox(width: 24), - ], - ), - const SizedBox(height: 80), - Text( - 'Automatically lock wallet after\nperiod of inactivity', - style: text.paragraph?.copyWith(color: colors.textPrimary, fontWeight: FontWeight.w500, height: 1.35), - textAlign: TextAlign.center, - ), - const SizedBox(height: 32), - Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration(color: colors.surfaceGlass, borderRadius: BorderRadius.circular(14)), - child: Column( - children: [ - for (var i = 0; i < _options.length; i++) ...[ - if (i > 0) Divider(color: colors.separator, height: 1), - _optionRow(_options[i].value, _options[i].label, colors, text), - ], - ], - ), - ), - const Spacer(), - GlassContainer( - asset: GlassContainer.wideAsset, - onTap: _confirm, - child: Center( - child: Text( - 'Confirm', - style: text.paragraph?.copyWith(color: colors.textPrimary, fontWeight: FontWeight.w500), - ), - ), - ), - const SizedBox(height: 24), - ], - ), - ), - ), - ), - ); - } - - Widget _optionRow(int value, String label, AppColorsV2 colors, AppTextTheme text) { - final selected = _selected == value; - return GestureDetector( - onTap: () => setState(() => _selected = value), - behavior: HitTestBehavior.opaque, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Row( - children: [ - Icon( - selected ? Icons.radio_button_checked : Icons.radio_button_unchecked, - color: selected ? colors.textPrimary : colors.textSecondary, - size: 24, - ), - const SizedBox(width: 8), - Text(label, style: text.paragraph?.copyWith(color: colors.textPrimary)), - ], - ), - ), - ); - } -} diff --git a/mobile-app/lib/v2/screens/settings/change_pin_screen.dart b/mobile-app/lib/v2/screens/settings/change_pin_screen.dart deleted file mode 100644 index 192d9137..00000000 --- a/mobile-app/lib/v2/screens/settings/change_pin_screen.dart +++ /dev/null @@ -1,245 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/v2/components/back_button.dart'; -import 'package:resonance_network_wallet/v2/components/glass_container.dart'; -import 'package:resonance_network_wallet/v2/components/gradient_background.dart'; -import 'package:resonance_network_wallet/v2/components/success_check.dart'; -import 'package:resonance_network_wallet/v2/theme/app_colors.dart'; -import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart'; - -enum _Step { enterCurrent, enterNew, confirmNew, success } - -class ChangePinScreen extends StatefulWidget { - const ChangePinScreen({super.key}); - - @override - State createState() => _ChangePinScreenState(); -} - -class _ChangePinScreenState extends State { - final _settingsService = SettingsService(); - final _focusNode = FocusNode(); - final _controller = TextEditingController(); - var _step = _Step.enterCurrent; - String _newPin = ''; - String? _error; - - @override - void initState() { - super.initState(); - _checkExistingPin(); - _controller.addListener(() { - setState(() => _error = null); - if (_controller.text.length == 6) _onContinue(); - }); - } - - @override - void dispose() { - _focusNode.dispose(); - _controller.dispose(); - super.dispose(); - } - - Future _checkExistingPin() async { - final has = await _settingsService.hasPin(); - if (!mounted) return; - setState(() => _step = has ? _Step.enterCurrent : _Step.enterNew); - WidgetsBinding.instance.addPostFrameCallback((_) => _focusNode.requestFocus()); - } - - Future _onContinue() async { - final entry = _controller.text; - if (entry.length != 6) return; - - switch (_step) { - case _Step.enterCurrent: - final ok = await _settingsService.verifyPin(entry); - if (!ok) { - setState(() => _error = 'Incorrect PIN'); - HapticFeedback.heavyImpact(); - return; - } - _controller.clear(); - setState(() => _step = _Step.enterNew); - _focusNode.requestFocus(); - case _Step.enterNew: - _newPin = entry; - _controller.clear(); - setState(() => _step = _Step.confirmNew); - _focusNode.requestFocus(); - case _Step.confirmNew: - if (entry != _newPin) { - setState(() => _error = 'PINs do not match'); - HapticFeedback.heavyImpact(); - return; - } - await _settingsService.setPin(_newPin); - _focusNode.unfocus(); - setState(() => _step = _Step.success); - HapticFeedback.mediumImpact(); - case _Step.success: - break; - } - } - - String get _stepTitle { - switch (_step) { - case _Step.enterCurrent: - return 'Enter Current PIN'; - case _Step.enterNew: - return 'Enter New PIN'; - case _Step.confirmNew: - return 'Confirm New PIN'; - case _Step.success: - return ''; - } - } - - @override - Widget build(BuildContext context) { - final colors = context.colors; - final text = context.themeText; - - return Scaffold( - backgroundColor: colors.background, - resizeToAvoidBottomInset: false, - body: GradientBackground( - child: SafeArea( - child: Stack( - children: [ - Opacity( - opacity: 0, - child: SizedBox( - height: 0, - child: TextField( - controller: _controller, - focusNode: _focusNode, - keyboardType: TextInputType.number, - keyboardAppearance: Brightness.dark, - maxLength: 6, - inputFormatters: [FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(6)], - decoration: const InputDecoration(counterText: ''), - ), - ), - ), - Column( - children: [ - const SizedBox(height: 16), - _header(colors, text), - Expanded(child: _step == _Step.success ? _successBody(colors, text) : _pinBody(colors, text)), - if (_step == _Step.success) ...[_doneButton(colors, text), const SizedBox(height: 24)], - ], - ), - ], - ), - ), - ), - ); - } - - Widget _header(AppColorsV2 colors, AppTextTheme text) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const AppBackButton(), - Text('Change PIN', style: text.smallTitle?.copyWith(color: colors.textPrimary, fontSize: 20)), - const SizedBox(width: 24), - ], - ), - ); - } - - Widget _pinBody(AppColorsV2 colors, AppTextTheme text) { - return GestureDetector( - onTap: () => _focusNode.requestFocus(), - behavior: HitTestBehavior.opaque, - child: Column( - children: [ - const SizedBox(height: 80), - Text( - _stepTitle, - style: text.smallTitle?.copyWith(color: colors.textPrimary, fontSize: 24, fontWeight: FontWeight.w400), - ), - const SizedBox(height: 32), - _pinDots(colors), - if (_error != null) ...[ - const SizedBox(height: 16), - Text(_error!, style: text.detail?.copyWith(color: colors.error)), - ], - const Spacer(), - ], - ), - ); - } - - Widget _pinDots(AppColorsV2 colors) { - final entry = _controller.text; - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: List.generate(6, (i) { - final filled = i < entry.length; - return Container( - width: 40, - height: 48, - margin: EdgeInsets.only(left: i == 0 ? 0 : 8), - child: Stack( - alignment: Alignment.center, - children: [ - Image.asset('assets/v2/pin_number_background.png', width: 40, height: 48), - if (filled) - Container( - width: 8, - height: 8, - decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle), - ), - ], - ), - ); - }), - ); - } - - Widget _successBody(AppColorsV2 colors, AppTextTheme text) { - return Column( - children: [ - const Spacer(flex: 2), - const SuccessCheck(), - const SizedBox(height: 64), - Text('PIN Changed', style: text.smallTitle?.copyWith(color: colors.textPrimary, fontSize: 20)), - const SizedBox(height: 24), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Text( - 'Your PIN has been updated successfully', - style: text.paragraph?.copyWith(color: colors.textSecondary), - textAlign: TextAlign.center, - ), - ), - const Spacer(flex: 3), - ], - ); - } - - Widget _doneButton(AppColorsV2 colors, AppTextTheme text) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: GestureDetector( - onTap: () => Navigator.pop(context), - child: GlassContainer( - asset: GlassContainer.wideAsset, - filled: true, - child: Center( - child: Text( - 'Done', - style: text.paragraph?.copyWith(color: colors.textPrimary, fontWeight: FontWeight.w500), - ), - ), - ), - ), - ); - } -} diff --git a/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart b/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart index be3f5b1b..9e7b1c2c 100644 --- a/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart +++ b/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart @@ -32,13 +32,10 @@ class _RecoveryPhraseScreenState extends State { }); return; } - if (_authService.isLocalAuthEnabled()) { - final ok = await _authService.authenticate( - localizedReason: 'Authenticate to reveal recovery phrase', - biometricOnly: false, - ); - if (!ok || !mounted) return; - } + final ok = await _authService.authenticate( + localizedReason: 'Authenticate to reveal recovery phrase', + ); + if (!ok || !mounted) return; final mnemonic = await _settingsService.getMnemonic(widget.walletIndex); if (mnemonic != null && mounted) { setState(() { diff --git a/mobile-app/lib/v2/screens/settings/settings_screen.dart b/mobile-app/lib/v2/screens/settings/settings_screen.dart index ca5d463e..24cf5dfb 100644 --- a/mobile-app/lib/v2/screens/settings/settings_screen.dart +++ b/mobile-app/lib/v2/screens/settings/settings_screen.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/features/components/reset_confirmation_bottom_sheet.dart'; -import 'package:resonance_network_wallet/shared/extensions/toaster_extensions.dart'; import 'package:resonance_network_wallet/v2/screens/settings/recovery_phrase_screen.dart'; import 'package:resonance_network_wallet/v2/screens/settings/select_wallet_screen.dart'; import 'package:resonance_network_wallet/v2/screens/welcome/welcome_screen.dart'; @@ -11,12 +10,9 @@ import 'package:resonance_network_wallet/providers/account_associations_provider import 'package:resonance_network_wallet/providers/account_providers.dart'; import 'package:resonance_network_wallet/providers/notification_config_provider.dart'; import 'package:resonance_network_wallet/providers/pending_transactions_provider.dart'; -import 'package:resonance_network_wallet/services/local_auth_service.dart'; import 'package:resonance_network_wallet/shared/utils/account_utils.dart'; import 'package:resonance_network_wallet/v2/components/back_button.dart'; import 'package:resonance_network_wallet/v2/components/gradient_background.dart'; -import 'package:resonance_network_wallet/v2/screens/settings/auto_lock_screen.dart'; -import 'package:resonance_network_wallet/v2/screens/settings/change_pin_screen.dart'; import 'package:resonance_network_wallet/v2/theme/app_colors.dart'; import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -29,61 +25,19 @@ class SettingsScreenV2 extends ConsumerStatefulWidget { } class _SettingsScreenV2State extends ConsumerState { - final _authService = LocalAuthService(); final _settingsService = SettingsService(); - bool _biometricEnabled = false; - String _biometricDesc = 'Face ID Disabled'; - int _autoLockMinutes = 5; - // bool _reversibleEnabled = false; int _reversibleTimeSeconds = 600; - bool _hasPinSet = false; @override void initState() { super.initState(); _loadSettings(); - _loadPinState(); - } - - Future _loadPinState() async { - final has = await _settingsService.hasPin(); - if (mounted) setState(() => _hasPinSet = has); } Future _loadSettings() async { - final bioEnabled = _authService.isLocalAuthEnabled(); - final bioDesc = await _authService.getBiometricDescription(); - final timeout = _authService.getAuthTimeoutMinutes(); final revTime = await _settingsService.getReversibleTimeSeconds() ?? 600; - // final revEnabled = _settingsService.isReversibleEnabled(); - if (!mounted) return; - setState(() { - _biometricEnabled = bioEnabled; - _biometricDesc = bioEnabled ? bioDesc : 'Face ID Disabled'; - _autoLockMinutes = timeout; - _reversibleTimeSeconds = revTime; - // _reversibleEnabled = revEnabled; - }); - } - - Future _toggleBiometric(bool enable) async { - if (enable) { - final available = await _authService.isBiometricAvailable(); - if (!available) { - if (mounted) context.showErrorToaster(message: 'Biometric not available on this device'); - return; - } - } - final ok = await _authService.authenticate( - localizedReason: 'Authenticate to ${enable ? 'enable' : 'disable'} biometric', - biometricOnly: false, - forSetup: true, - ); - if (ok) { - _authService.setLocalAuthEnabled(enable); - _loadSettings(); - } + setState(() => _reversibleTimeSeconds = revTime); } void _toggleNotifications(bool enable) { @@ -127,12 +81,6 @@ class _SettingsScreenV2State extends ConsumerState { ); } - String _autoLockLabel() { - if (_autoLockMinutes == 0) return 'Immediately'; - if (_autoLockMinutes == 60) return '1 hour'; - return '$_autoLockMinutes mins'; - } - String _timeLimitLabel() { if (_reversibleTimeSeconds <= 0) return 'Disabled'; final mins = _reversibleTimeSeconds ~/ 60; @@ -171,32 +119,6 @@ class _SettingsScreenV2State extends ConsumerState { child: ListView( padding: const EdgeInsets.symmetric(horizontal: 24), children: [ - _section('Security', colors, text, [ - _toggleItem('Biometric Lock', _biometricDesc, _biometricEnabled, _toggleBiometric, colors, text), - _divider(colors), - _chevronItem( - 'PIN Code', - _hasPinSet ? '6-digit code' : 'Not set', - colors, - text, - onTap: () async { - await Navigator.push(context, MaterialPageRoute(builder: (_) => const ChangePinScreen())); - _loadPinState(); - }, - ), - _divider(colors), - _chevronItem( - 'Auto-Lock', - _autoLockLabel(), - colors, - text, - onTap: () async { - await Navigator.push(context, MaterialPageRoute(builder: (_) => const AutoLockScreen())); - _loadSettings(); - }, - ), - ]), - const SizedBox(height: 40), _section('Wallet', colors, text, [ _chevronItem('Recovery Phase', 'View Backup', colors, text, onTap: _navigateToRecoveryPhrase), ]), diff --git a/mobile-app/test/widget/send_screen_widget_test.mocks.dart b/mobile-app/test/widget/send_screen_widget_test.mocks.dart index 961db841..62cd7d92 100644 --- a/mobile-app/test/widget/send_screen_widget_test.mocks.dart +++ b/mobile-app/test/widget/send_screen_widget_test.mocks.dart @@ -4,17 +4,20 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; -import 'dart:typed_data' as _i6; +import 'dart:typed_data' as _i7; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i5; -import 'package:polkadart/polkadart.dart' as _i8; +import 'package:mockito/src/dummies.dart' as _i6; +import 'package:polkadart/polkadart.dart' as _i9; import 'package:quantus_sdk/generated/schrodinger/types/pallet_reversible_transfers/high_security_account_data.dart' - as _i9; -import 'package:quantus_sdk/generated/schrodinger/types/pallet_reversible_transfers/pending_transfer.dart' as _i10; -import 'package:quantus_sdk/generated/schrodinger/types/qp_scheduler/block_number_or_timestamp.dart' as _i7; + as _i10; +import 'package:quantus_sdk/generated/schrodinger/types/pallet_reversible_transfers/pending_transfer.dart' + as _i11; +import 'package:quantus_sdk/generated/schrodinger/types/qp_scheduler/block_number_or_timestamp.dart' + as _i8; import 'package:quantus_sdk/quantus_sdk.dart' as _i2; import 'package:quantus_sdk/src/models/account.dart' as _i4; +import 'package:quantus_sdk/src/models/display_account.dart' as _i5; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -31,27 +34,36 @@ import 'package:quantus_sdk/src/models/account.dart' as _i4; // ignore_for_file: subtype_of_sealed_class class _FakeKeypair_0 extends _i1.SmartFake implements _i2.Keypair { - _FakeKeypair_0(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); + _FakeKeypair_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } -class _FakeExtrinsicFeeData_1 extends _i1.SmartFake implements _i2.ExtrinsicFeeData { - _FakeExtrinsicFeeData_1(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); +class _FakeExtrinsicFeeData_1 extends _i1.SmartFake + implements _i2.ExtrinsicFeeData { + _FakeExtrinsicFeeData_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeExtrinsicData_2 extends _i1.SmartFake implements _i2.ExtrinsicData { - _FakeExtrinsicData_2(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); + _FakeExtrinsicData_2(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } -class _FakeUnsignedTransactionData_3 extends _i1.SmartFake implements _i2.UnsignedTransactionData { - _FakeUnsignedTransactionData_3(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); +class _FakeUnsignedTransactionData_3 extends _i1.SmartFake + implements _i2.UnsignedTransactionData { + _FakeUnsignedTransactionData_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeBalances_4 extends _i1.SmartFake implements _i2.Balances { - _FakeBalances_4(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); + _FakeBalances_4(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } -class _FakeReversibleTransfers_5 extends _i1.SmartFake implements _i2.ReversibleTransfers { - _FakeReversibleTransfers_5(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); +class _FakeReversibleTransfers_5 extends _i1.SmartFake + implements _i2.ReversibleTransfers { + _FakeReversibleTransfers_5(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } /// A class which mocks [SettingsService]. @@ -99,7 +111,10 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override List<_i4.Account> getAccountsToMigrate() => - (super.noSuchMethod(Invocation.method(#getAccountsToMigrate, []), returnValue: <_i4.Account>[]) + (super.noSuchMethod( + Invocation.method(#getAccountsToMigrate, []), + returnValue: <_i4.Account>[], + ) as List<_i4.Account>); @override @@ -138,24 +153,41 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { ) as _i3.Future); - // @override - // _i3.Future setActiveAccount(_i4.Account? account) => - // (super.noSuchMethod( - // Invocation.method(#setActiveAccount, [account]), - // returnValue: _i3.Future.value(), - // returnValueForMissingStub: _i3.Future.value(), - // ) - // as _i3.Future); + @override + _i3.Future setActiveAccount(_i5.DisplayAccount? account) => + (super.noSuchMethod( + Invocation.method(#setActiveAccount, [account]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); - // @override - // _i3.Future<_i4.Account?> getActiveAccount() => - // (super.noSuchMethod(Invocation.method(#getActiveAccount, []), returnValue: _i3.Future<_i4.Account?>.value()) - // as _i3.Future<_i4.Account?>); + @override + _i3.Future<_i5.DisplayAccount?> getActiveAccount() => + (super.noSuchMethod( + Invocation.method(#getActiveAccount, []), + returnValue: _i3.Future<_i5.DisplayAccount?>.value(), + ) + as _i3.Future<_i5.DisplayAccount?>); @override - _i3.Future<_i4.Account?> getAccount({required int? walletIndex, required int? index}) => + _i3.Future<_i4.Account?> getActiveRegularAccount() => (super.noSuchMethod( - Invocation.method(#getAccount, [], {#walletIndex: walletIndex, #index: index}), + Invocation.method(#getActiveRegularAccount, []), + returnValue: _i3.Future<_i4.Account?>.value(), + ) + as _i3.Future<_i4.Account?>); + + @override + _i3.Future<_i4.Account?> getAccount({ + required int? walletIndex, + required int? index, + }) => + (super.noSuchMethod( + Invocation.method(#getAccount, [], { + #walletIndex: walletIndex, + #index: index, + }), returnValue: _i3.Future<_i4.Account?>.value(), ) as _i3.Future<_i4.Account?>); @@ -168,21 +200,75 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { ) as _i3.Future); + @override + _i3.Future> getAddressBook() => + (super.noSuchMethod( + Invocation.method(#getAddressBook, []), + returnValue: _i3.Future>.value( + {}, + ), + ) + as _i3.Future>); + + @override + _i3.Future saveAddressBook(Map? addressBook) => + (super.noSuchMethod( + Invocation.method(#saveAddressBook, [addressBook]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + + @override + _i3.Future setAddressName(String? address, String? name) => + (super.noSuchMethod( + Invocation.method(#setAddressName, [address, name]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + + @override + _i3.Future getAddressName(String? address) => + (super.noSuchMethod( + Invocation.method(#getAddressName, [address]), + returnValue: _i3.Future.value(), + ) + as _i3.Future); + + @override + _i3.Future removeAddressName(String? address) => + (super.noSuchMethod( + Invocation.method(#removeAddressName, [address]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + @override _i3.Future getHasWallet() => - (super.noSuchMethod(Invocation.method(#getHasWallet, []), returnValue: _i3.Future.value(false)) + (super.noSuchMethod( + Invocation.method(#getHasWallet, []), + returnValue: _i3.Future.value(false), + ) as _i3.Future); @override _i3.Future isWalletLoggedOut() => - (super.noSuchMethod(Invocation.method(#isWalletLoggedOut, []), returnValue: _i3.Future.value(false)) + (super.noSuchMethod( + Invocation.method(#isWalletLoggedOut, []), + returnValue: _i3.Future.value(false), + ) as _i3.Future); @override String getMnemonicKey(int? walletIndex) => (super.noSuchMethod( Invocation.method(#getMnemonicKey, [walletIndex]), - returnValue: _i5.dummyValue(this, Invocation.method(#getMnemonicKey, [walletIndex])), + returnValue: _i6.dummyValue( + this, + Invocation.method(#getMnemonicKey, [walletIndex]), + ), ) as String); @@ -197,9 +283,29 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getMnemonic(int? walletIndex) => - (super.noSuchMethod(Invocation.method(#getMnemonic, [walletIndex]), returnValue: _i3.Future.value()) + (super.noSuchMethod( + Invocation.method(#getMnemonic, [walletIndex]), + returnValue: _i3.Future.value(), + ) as _i3.Future); + @override + _i3.Future setReversibleEnabled(bool? enabled) => + (super.noSuchMethod( + Invocation.method(#setReversibleEnabled, [enabled]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + + @override + bool isReversibleEnabled() => + (super.noSuchMethod( + Invocation.method(#isReversibleEnabled, []), + returnValue: false, + ) + as bool); + @override _i3.Future setReversibleTimeSeconds(int? seconds) => (super.noSuchMethod( @@ -211,12 +317,35 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getReversibleTimeSeconds() => - (super.noSuchMethod(Invocation.method(#getReversibleTimeSeconds, []), returnValue: _i3.Future.value()) + (super.noSuchMethod( + Invocation.method(#getReversibleTimeSeconds, []), + returnValue: _i3.Future.value(), + ) as _i3.Future); + @override + _i3.Future setBalanceHidden(bool? hidden) => + (super.noSuchMethod( + Invocation.method(#setBalanceHidden, [hidden]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + + @override + bool isBalanceHidden() => + (super.noSuchMethod( + Invocation.method(#isBalanceHidden, []), + returnValue: false, + ) + as bool); + @override _i3.Future getBool(String? key) => - (super.noSuchMethod(Invocation.method(#getBool, [key]), returnValue: _i3.Future.value()) + (super.noSuchMethod( + Invocation.method(#getBool, [key]), + returnValue: _i3.Future.value(), + ) as _i3.Future); @override @@ -230,40 +359,39 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getString(String? key) => - (super.noSuchMethod(Invocation.method(#getString, [key]), returnValue: _i3.Future.value()) + (super.noSuchMethod( + Invocation.method(#getString, [key]), + returnValue: _i3.Future.value(), + ) as _i3.Future); @override - void setLastSuccessfulAuthTime(DateTime? time) => - super.noSuchMethod(Invocation.method(#setLastSuccessfulAuthTime, [time]), returnValueForMissingStub: null); - - @override - void setLastPausedTime(DateTime? time) => - super.noSuchMethod(Invocation.method(#setLastPausedTime, [time]), returnValueForMissingStub: null); - - @override - void cleanLastPausedTime() => - super.noSuchMethod(Invocation.method(#cleanLastPausedTime, []), returnValueForMissingStub: null); - - @override - void setAuthTimeout(int? timeoutDurationInMinutes) => super.noSuchMethod( - Invocation.method(#setAuthTimeout, [timeoutDurationInMinutes]), + void setLastPausedTime(DateTime? time) => super.noSuchMethod( + Invocation.method(#setLastPausedTime, [time]), returnValueForMissingStub: null, ); @override - void setAuthEnabled(bool? enabled) => - super.noSuchMethod(Invocation.method(#setAuthEnabled, [enabled]), returnValueForMissingStub: null); - - @override - bool isAuthEnabled() => (super.noSuchMethod(Invocation.method(#isAuthEnabled, []), returnValue: false) as bool); + void cleanLastPausedTime() => super.noSuchMethod( + Invocation.method(#cleanLastPausedTime, []), + returnValueForMissingStub: null, + ); @override - bool hasOldAccounts() => (super.noSuchMethod(Invocation.method(#hasOldAccounts, []), returnValue: false) as bool); + bool hasOldAccounts() => + (super.noSuchMethod( + Invocation.method(#hasOldAccounts, []), + returnValue: false, + ) + as bool); @override List<_i4.Account> getOldAccounts() => - (super.noSuchMethod(Invocation.method(#getOldAccounts, []), returnValue: <_i4.Account>[]) as List<_i4.Account>); + (super.noSuchMethod( + Invocation.method(#getOldAccounts, []), + returnValue: <_i4.Account>[], + ) + as List<_i4.Account>); @override _i3.Future clearOldAccounts() => @@ -284,7 +412,10 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { as _i3.Future); @override - void resetForTest() => super.noSuchMethod(Invocation.method(#resetForTest, []), returnValueForMissingStub: null); + void resetForTest() => super.noSuchMethod( + Invocation.method(#resetForTest, []), + returnValueForMissingStub: null, + ); @override _i3.Future clearAll() => @@ -297,43 +428,69 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override bool referralCheckCompleted() => - (super.noSuchMethod(Invocation.method(#referralCheckCompleted, []), returnValue: false) as bool); + (super.noSuchMethod( + Invocation.method(#referralCheckCompleted, []), + returnValue: false, + ) + as bool); @override - void setReferralCheckCompleted() => - super.noSuchMethod(Invocation.method(#setReferralCheckCompleted, []), returnValueForMissingStub: null); + void setReferralCheckCompleted() => super.noSuchMethod( + Invocation.method(#setReferralCheckCompleted, []), + returnValueForMissingStub: null, + ); @override - void clearReferralCheckCompletedFlag() => - super.noSuchMethod(Invocation.method(#clearReferralCheckCompletedFlag, []), returnValueForMissingStub: null); + void clearReferralCheckCompletedFlag() => super.noSuchMethod( + Invocation.method(#clearReferralCheckCompletedFlag, []), + returnValueForMissingStub: null, + ); @override - void setReferralCode(String? code) => - super.noSuchMethod(Invocation.method(#setReferralCode, [code]), returnValueForMissingStub: null); + void setReferralCode(String? code) => super.noSuchMethod( + Invocation.method(#setReferralCode, [code]), + returnValueForMissingStub: null, + ); @override bool hasWatchedQuestsPromo() => - (super.noSuchMethod(Invocation.method(#hasWatchedQuestsPromo, []), returnValue: false) as bool); + (super.noSuchMethod( + Invocation.method(#hasWatchedQuestsPromo, []), + returnValue: false, + ) + as bool); @override - void setQuestsPromoWatched() => - super.noSuchMethod(Invocation.method(#setQuestsPromoWatched, []), returnValueForMissingStub: null); + void setQuestsPromoWatched() => super.noSuchMethod( + Invocation.method(#setQuestsPromoWatched, []), + returnValueForMissingStub: null, + ); @override - void clearQuestsPromoWatchedFlag() => - super.noSuchMethod(Invocation.method(#clearQuestsPromoWatchedFlag, []), returnValueForMissingStub: null); + void clearQuestsPromoWatchedFlag() => super.noSuchMethod( + Invocation.method(#clearQuestsPromoWatchedFlag, []), + returnValueForMissingStub: null, + ); @override bool existingUserSeenPromoVideo() => - (super.noSuchMethod(Invocation.method(#existingUserSeenPromoVideo, []), returnValue: false) as bool); + (super.noSuchMethod( + Invocation.method(#existingUserSeenPromoVideo, []), + returnValue: false, + ) + as bool); @override - void setExistingUserSeenPromoVideo() => - super.noSuchMethod(Invocation.method(#setExistingUserSeenPromoVideo, []), returnValueForMissingStub: null); + void setExistingUserSeenPromoVideo() => super.noSuchMethod( + Invocation.method(#setExistingUserSeenPromoVideo, []), + returnValueForMissingStub: null, + ); @override - void clearExistingUserSeenPromoVideoFlag() => - super.noSuchMethod(Invocation.method(#clearExistingUserSeenPromoVideoFlag, []), returnValueForMissingStub: null); + void clearExistingUserSeenPromoVideoFlag() => super.noSuchMethod( + Invocation.method(#clearExistingUserSeenPromoVideoFlag, []), + returnValueForMissingStub: null, + ); } /// A class which mocks [SubstrateService]. @@ -345,11 +502,14 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { } @override - _i3.Future getFee(_i6.Uint8List? signedExtrinsic) => + _i3.Future getFee(_i7.Uint8List? signedExtrinsic) => (super.noSuchMethod( Invocation.method(#getFee, [signedExtrinsic]), returnValue: _i3.Future.value( - _i5.dummyValue(this, Invocation.method(#getFee, [signedExtrinsic])), + _i6.dummyValue( + this, + Invocation.method(#getFee, [signedExtrinsic]), + ), ), ) as _i3.Future); @@ -359,7 +519,10 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { (super.noSuchMethod( Invocation.method(#queryUserBalance, []), returnValue: _i3.Future.value( - _i5.dummyValue(this, Invocation.method(#queryUserBalance, [])), + _i6.dummyValue( + this, + Invocation.method(#queryUserBalance, []), + ), ), ) as _i3.Future); @@ -369,7 +532,10 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { (super.noSuchMethod( Invocation.method(#queryBalance, [address]), returnValue: _i3.Future.value( - _i5.dummyValue(this, Invocation.method(#queryBalance, [address])), + _i6.dummyValue( + this, + Invocation.method(#queryBalance, [address]), + ), ), ) as _i3.Future); @@ -378,27 +544,46 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { _i2.Keypair nonHDdilithiumKeypairFromMnemonic(String? senderSeed) => (super.noSuchMethod( Invocation.method(#nonHDdilithiumKeypairFromMnemonic, [senderSeed]), - returnValue: _FakeKeypair_0(this, Invocation.method(#nonHDdilithiumKeypairFromMnemonic, [senderSeed])), + returnValue: _FakeKeypair_0( + this, + Invocation.method(#nonHDdilithiumKeypairFromMnemonic, [ + senderSeed, + ]), + ), ) as _i2.Keypair); @override - _i3.Future<_i2.ExtrinsicFeeData> getFeeForCall(_i4.Account? account, _i2.RuntimeCall? call) => + _i3.Future<_i2.ExtrinsicFeeData> getFeeForCall( + _i4.Account? account, + _i2.RuntimeCall? call, + ) => (super.noSuchMethod( Invocation.method(#getFeeForCall, [account, call]), returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( - _FakeExtrinsicFeeData_1(this, Invocation.method(#getFeeForCall, [account, call])), + _FakeExtrinsicFeeData_1( + this, + Invocation.method(#getFeeForCall, [account, call]), + ), ), ) as _i3.Future<_i2.ExtrinsicFeeData>); @override - _i3.Future<_i6.Uint8List> submitExtrinsic(_i4.Account? account, _i2.RuntimeCall? call, {int? maxRetries = 3}) => + _i3.Future<_i7.Uint8List> submitExtrinsic( + _i4.Account? account, + _i2.RuntimeCall? call, { + int? maxRetries = 3, + }) => (super.noSuchMethod( - Invocation.method(#submitExtrinsic, [account, call], {#maxRetries: maxRetries}), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + Invocation.method( + #submitExtrinsic, + [account, call], + {#maxRetries: maxRetries}, + ), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) - as _i3.Future<_i6.Uint8List>); + as _i3.Future<_i7.Uint8List>); @override _i3.Future<_i2.ExtrinsicData> getExtrinsicPayload( @@ -407,37 +592,58 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { bool? isSigned = true, }) => (super.noSuchMethod( - Invocation.method(#getExtrinsicPayload, [account, call], {#isSigned: isSigned}), + Invocation.method( + #getExtrinsicPayload, + [account, call], + {#isSigned: isSigned}, + ), returnValue: _i3.Future<_i2.ExtrinsicData>.value( _FakeExtrinsicData_2( this, - Invocation.method(#getExtrinsicPayload, [account, call], {#isSigned: isSigned}), + Invocation.method( + #getExtrinsicPayload, + [account, call], + {#isSigned: isSigned}, + ), ), ), ) as _i3.Future<_i2.ExtrinsicData>); @override - _i3.Future<_i2.UnsignedTransactionData> getUnsignedTransactionPayload(_i4.Account? account, _i2.RuntimeCall? call) => + _i3.Future<_i2.UnsignedTransactionData> getUnsignedTransactionPayload( + _i4.Account? account, + _i2.RuntimeCall? call, + ) => (super.noSuchMethod( Invocation.method(#getUnsignedTransactionPayload, [account, call]), returnValue: _i3.Future<_i2.UnsignedTransactionData>.value( - _FakeUnsignedTransactionData_3(this, Invocation.method(#getUnsignedTransactionPayload, [account, call])), + _FakeUnsignedTransactionData_3( + this, + Invocation.method(#getUnsignedTransactionPayload, [ + account, + call, + ]), + ), ), ) as _i3.Future<_i2.UnsignedTransactionData>); @override - _i3.Future<_i6.Uint8List> submitExtrinsicWithExternalSignature( + _i3.Future<_i7.Uint8List> submitExtrinsicWithExternalSignature( _i2.UnsignedTransactionData? unsignedData, - _i6.Uint8List? signature, - _i6.Uint8List? publicKey, + _i7.Uint8List? signature, + _i7.Uint8List? publicKey, ) => (super.noSuchMethod( - Invocation.method(#submitExtrinsicWithExternalSignature, [unsignedData, signature, publicKey]), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + Invocation.method(#submitExtrinsicWithExternalSignature, [ + unsignedData, + signature, + publicKey, + ]), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) - as _i3.Future<_i6.Uint8List>); + as _i3.Future<_i7.Uint8List>); @override _i3.Future logout() => @@ -453,31 +659,45 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { (super.noSuchMethod( Invocation.method(#generateMnemonic, []), returnValue: _i3.Future.value( - _i5.dummyValue(this, Invocation.method(#generateMnemonic, [])), + _i6.dummyValue( + this, + Invocation.method(#generateMnemonic, []), + ), ), ) as _i3.Future); @override bool isValidSS58Address(String? address) => - (super.noSuchMethod(Invocation.method(#isValidSS58Address, [address]), returnValue: false) as bool); + (super.noSuchMethod( + Invocation.method(#isValidSS58Address, [address]), + returnValue: false, + ) + as bool); @override - String bytesToHex(_i6.Uint8List? bytes) => + String bytesToHex(_i7.Uint8List? bytes) => (super.noSuchMethod( Invocation.method(#bytesToHex, [bytes]), - returnValue: _i5.dummyValue(this, Invocation.method(#bytesToHex, [bytes])), + returnValue: _i6.dummyValue( + this, + Invocation.method(#bytesToHex, [bytes]), + ), ) as String); @override - void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); + void dispose() => super.noSuchMethod( + Invocation.method(#dispose, []), + returnValueForMissingStub: null, + ); } /// A class which mocks [HumanReadableChecksumService]. /// /// See the documentation for Mockito's code generation for more information. -class MockHumanReadableChecksumService extends _i1.Mock implements _i2.HumanReadableChecksumService { +class MockHumanReadableChecksumService extends _i1.Mock + implements _i2.HumanReadableChecksumService { MockHumanReadableChecksumService() { _i1.throwOnMissingStub(this); } @@ -492,20 +712,34 @@ class MockHumanReadableChecksumService extends _i1.Mock implements _i2.HumanRead as _i3.Future); @override - _i3.Future getHumanReadableName(String? address, {dynamic upperCase = true}) => + _i3.Future getHumanReadableName( + String? address, { + dynamic upperCase = true, + }) => (super.noSuchMethod( - Invocation.method(#getHumanReadableName, [address], {#upperCase: upperCase}), + Invocation.method( + #getHumanReadableName, + [address], + {#upperCase: upperCase}, + ), returnValue: _i3.Future.value( - _i5.dummyValue( + _i6.dummyValue( this, - Invocation.method(#getHumanReadableName, [address], {#upperCase: upperCase}), + Invocation.method( + #getHumanReadableName, + [address], + {#upperCase: upperCase}, + ), ), ), ) as _i3.Future); @override - void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); + void dispose() => super.noSuchMethod( + Invocation.method(#dispose, []), + returnValueForMissingStub: null, + ); } /// A class which mocks [BalancesService]. @@ -517,21 +751,41 @@ class MockBalancesService extends _i1.Mock implements _i2.BalancesService { } @override - _i3.Future<_i6.Uint8List> balanceTransfer(_i4.Account? account, String? targetAddress, BigInt? amount) => + _i3.Future<_i7.Uint8List> balanceTransfer( + _i4.Account? account, + String? targetAddress, + BigInt? amount, + ) => (super.noSuchMethod( - Invocation.method(#balanceTransfer, [account, targetAddress, amount]), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + Invocation.method(#balanceTransfer, [ + account, + targetAddress, + amount, + ]), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) - as _i3.Future<_i6.Uint8List>); + as _i3.Future<_i7.Uint8List>); @override - _i3.Future<_i2.ExtrinsicFeeData> getBalanceTransferFee(_i4.Account? account, String? targetAddress, BigInt? amount) => + _i3.Future<_i2.ExtrinsicFeeData> getBalanceTransferFee( + _i4.Account? account, + String? targetAddress, + BigInt? amount, + ) => (super.noSuchMethod( - Invocation.method(#getBalanceTransferFee, [account, targetAddress, amount]), + Invocation.method(#getBalanceTransferFee, [ + account, + targetAddress, + amount, + ]), returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( _FakeExtrinsicFeeData_1( this, - Invocation.method(#getBalanceTransferFee, [account, targetAddress, amount]), + Invocation.method(#getBalanceTransferFee, [ + account, + targetAddress, + amount, + ]), ), ), ) @@ -541,7 +795,35 @@ class MockBalancesService extends _i1.Mock implements _i2.BalancesService { _i2.Balances getBalanceTransferCall(String? targetAddress, BigInt? amount) => (super.noSuchMethod( Invocation.method(#getBalanceTransferCall, [targetAddress, amount]), - returnValue: _FakeBalances_4(this, Invocation.method(#getBalanceTransferCall, [targetAddress, amount])), + returnValue: _FakeBalances_4( + this, + Invocation.method(#getBalanceTransferCall, [ + targetAddress, + amount, + ]), + ), + ) + as _i2.Balances); + + @override + _i2.Balances getTransferAllCall( + String? targetAddress, { + bool? keepAlive = false, + }) => + (super.noSuchMethod( + Invocation.method( + #getTransferAllCall, + [targetAddress], + {#keepAlive: keepAlive}, + ), + returnValue: _FakeBalances_4( + this, + Invocation.method( + #getTransferAllCall, + [targetAddress], + {#keepAlive: keepAlive}, + ), + ), ) as _i2.Balances); } @@ -549,25 +831,14 @@ class MockBalancesService extends _i1.Mock implements _i2.BalancesService { /// A class which mocks [ReversibleTransfersService]. /// /// See the documentation for Mockito's code generation for more information. -class MockReversibleTransfersService extends _i1.Mock implements _i2.ReversibleTransfersService { +class MockReversibleTransfersService extends _i1.Mock + implements _i2.ReversibleTransfersService { MockReversibleTransfersService() { _i1.throwOnMissingStub(this); } - // @override - // _i3.Future<_i6.Uint8List> setHighSecurity({ - // required _i4.Account? account, - // required _i4.Account? guardian, - // required _i7.BlockNumberOrTimestamp? delay, - // }) => - // (super.noSuchMethod( - // Invocation.method(#setHighSecurity, [], {#account: account, #guardian: guardian, #delay: delay}), - // returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), - // ) - // as _i3.Future<_i6.Uint8List>); - @override - _i3.Future<_i6.Uint8List> scheduleReversibleTransfer({ + _i3.Future<_i7.Uint8List> scheduleReversibleTransfer({ required _i4.Account? account, required String? recipientAddress, required BigInt? amount, @@ -578,17 +849,17 @@ class MockReversibleTransfersService extends _i1.Mock implements _i2.ReversibleT #recipientAddress: recipientAddress, #amount: amount, }), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) - as _i3.Future<_i6.Uint8List>); + as _i3.Future<_i7.Uint8List>); @override - _i3.Future<_i6.Uint8List> scheduleReversibleTransferWithDelay({ + _i3.Future<_i7.Uint8List> scheduleReversibleTransferWithDelay({ required _i4.Account? account, required String? recipientAddress, required BigInt? amount, - required _i7.BlockNumberOrTimestamp? delay, - void Function(_i8.ExtrinsicStatus)? onStatus, + required _i8.BlockNumberOrTimestamp? delay, + void Function(_i9.ExtrinsicStatus)? onStatus, }) => (super.noSuchMethod( Invocation.method(#scheduleReversibleTransferWithDelay, [], { @@ -598,9 +869,9 @@ class MockReversibleTransfersService extends _i1.Mock implements _i2.ReversibleT #delay: delay, #onStatus: onStatus, }), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) - as _i3.Future<_i6.Uint8List>); + as _i3.Future<_i7.Uint8List>); @override _i3.Future<_i2.ExtrinsicFeeData> getReversibleTransferWithDelayFeeEstimate({ @@ -619,12 +890,16 @@ class MockReversibleTransfersService extends _i1.Mock implements _i2.ReversibleT returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( _FakeExtrinsicFeeData_1( this, - Invocation.method(#getReversibleTransferWithDelayFeeEstimate, [], { - #account: account, - #recipientAddress: recipientAddress, - #amount: amount, - #delaySeconds: delaySeconds, - }), + Invocation.method( + #getReversibleTransferWithDelayFeeEstimate, + [], + { + #account: account, + #recipientAddress: recipientAddress, + #amount: amount, + #delaySeconds: delaySeconds, + }, + ), ), ), ) @@ -634,24 +909,32 @@ class MockReversibleTransfersService extends _i1.Mock implements _i2.ReversibleT _i2.ReversibleTransfers getReversibleTransferCall( String? recipientAddress, BigInt? amount, - _i7.BlockNumberOrTimestamp? delay, + _i8.BlockNumberOrTimestamp? delay, ) => (super.noSuchMethod( - Invocation.method(#getReversibleTransferCall, [recipientAddress, amount, delay]), + Invocation.method(#getReversibleTransferCall, [ + recipientAddress, + amount, + delay, + ]), returnValue: _FakeReversibleTransfers_5( this, - Invocation.method(#getReversibleTransferCall, [recipientAddress, amount, delay]), + Invocation.method(#getReversibleTransferCall, [ + recipientAddress, + amount, + delay, + ]), ), ) as _i2.ReversibleTransfers); @override - _i3.Future<_i6.Uint8List> scheduleReversibleTransferWithDelaySeconds({ + _i3.Future<_i7.Uint8List> scheduleReversibleTransferWithDelaySeconds({ required _i4.Account? account, required String? recipientAddress, required BigInt? amount, required int? delaySeconds, - void Function(_i8.ExtrinsicStatus)? onStatus, + void Function(_i9.ExtrinsicStatus)? onStatus, }) => (super.noSuchMethod( Invocation.method(#scheduleReversibleTransferWithDelaySeconds, [], { @@ -661,79 +944,159 @@ class MockReversibleTransfersService extends _i1.Mock implements _i2.ReversibleT #delaySeconds: delaySeconds, #onStatus: onStatus, }), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) - as _i3.Future<_i6.Uint8List>); + as _i3.Future<_i7.Uint8List>); @override - _i3.Future<_i6.Uint8List> cancelReversibleTransfer({ + _i3.Future<_i7.Uint8List> cancelReversibleTransfer({ required _i4.Account? account, required List? transactionId, }) => (super.noSuchMethod( - Invocation.method(#cancelReversibleTransfer, [], {#account: account, #transactionId: transactionId}), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), - ) - as _i3.Future<_i6.Uint8List>); - - // @override - _i3.Future<_i6.Uint8List> executeTransfer({required _i4.Account? account, required List? transactionId}) => - (super.noSuchMethod( - Invocation.method(#executeTransfer, [], {#account: account, #transactionId: transactionId}), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + Invocation.method(#cancelReversibleTransfer, [], { + #account: account, + #transactionId: transactionId, + }), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) - as _i3.Future<_i6.Uint8List>); + as _i3.Future<_i7.Uint8List>); @override - _i3.Future<_i9.HighSecurityAccountData?> getHighSecurityConfig(String? address) => + _i3.Future<_i10.HighSecurityAccountData?> getHighSecurityConfig( + String? address, + ) => (super.noSuchMethod( - Invocation.method(#getAccountReversibilityConfig, [address]), - returnValue: _i3.Future<_i9.HighSecurityAccountData?>.value(), + Invocation.method(#getHighSecurityConfig, [address]), + returnValue: _i3.Future<_i10.HighSecurityAccountData?>.value(), ) - as _i3.Future<_i9.HighSecurityAccountData?>); + as _i3.Future<_i10.HighSecurityAccountData?>); @override - _i3.Future<_i10.PendingTransfer?> getPendingTransfer(List? transactionId) => + _i3.Future<_i11.PendingTransfer?> getPendingTransfer( + List? transactionId, + ) => (super.noSuchMethod( Invocation.method(#getPendingTransfer, [transactionId]), - returnValue: _i3.Future<_i10.PendingTransfer?>.value(), + returnValue: _i3.Future<_i11.PendingTransfer?>.value(), ) - as _i3.Future<_i10.PendingTransfer?>); + as _i3.Future<_i11.PendingTransfer?>); @override _i3.Future getAccountPendingIndex(String? address) => - (super.noSuchMethod(Invocation.method(#getAccountPendingIndex, [address]), returnValue: _i3.Future.value(0)) - as _i3.Future); - - // @override - _i3.Future isReversibilityEnabled(String? address) => (super.noSuchMethod( - Invocation.method(#isReversibilityEnabled, [address]), - returnValue: _i3.Future.value(false), + Invocation.method(#getAccountPendingIndex, [address]), + returnValue: _i3.Future.value(0), ) - as _i3.Future); + as _i3.Future); @override - _i3.Future> getAccountPendingTransfers(String? address) => + _i3.Future> getAccountPendingTransfers( + String? address, + ) => (super.noSuchMethod( Invocation.method(#getAccountPendingTransfers, [address]), - returnValue: _i3.Future>.value(<_i10.PendingTransfer>[]), + returnValue: _i3.Future>.value( + <_i11.PendingTransfer>[], + ), ) - as _i3.Future>); + as _i3.Future>); @override _i3.Future> getConstants() => (super.noSuchMethod( Invocation.method(#getConstants, []), - returnValue: _i3.Future>.value({}), + returnValue: _i3.Future>.value( + {}, + ), ) as _i3.Future>); + + @override + _i3.Future<_i7.Uint8List> setHighSecurity({ + required _i4.Account? account, + required String? guardianAccountId, + required _i8.BlockNumberOrTimestamp? delay, + }) => + (super.noSuchMethod( + Invocation.method(#setHighSecurity, [], { + #account: account, + #guardianAccountId: guardianAccountId, + #delay: delay, + }), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), + ) + as _i3.Future<_i7.Uint8List>); + + @override + _i3.Future isHighSecurity(String? address) => + (super.noSuchMethod( + Invocation.method(#isHighSecurity, [address]), + returnValue: _i3.Future.value(false), + ) + as _i3.Future); + + @override + _i3.Future<_i7.Uint8List> interceptTransaction({ + required _i4.Account? guardianAccount, + required List? transactionId, + }) => + (super.noSuchMethod( + Invocation.method(#interceptTransaction, [], { + #guardianAccount: guardianAccount, + #transactionId: transactionId, + }), + returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), + ) + as _i3.Future<_i7.Uint8List>); + + @override + _i3.Future isGuardian(String? address) => + (super.noSuchMethod( + Invocation.method(#isGuardian, [address]), + returnValue: _i3.Future.value(false), + ) + as _i3.Future); + + @override + _i3.Future> getInterceptedAccounts(String? guardianAddress) => + (super.noSuchMethod( + Invocation.method(#getInterceptedAccounts, [guardianAddress]), + returnValue: _i3.Future>.value([]), + ) + as _i3.Future>); + + @override + _i3.Future<_i2.ExtrinsicFeeData> getHighSecuritySetupFee( + _i4.Account? account, + String? guardianAccountId, + Duration? safeguardDuration, + ) => + (super.noSuchMethod( + Invocation.method(#getHighSecuritySetupFee, [ + account, + guardianAccountId, + safeguardDuration, + ]), + returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( + _FakeExtrinsicFeeData_1( + this, + Invocation.method(#getHighSecuritySetupFee, [ + account, + guardianAccountId, + safeguardDuration, + ]), + ), + ), + ) + as _i3.Future<_i2.ExtrinsicFeeData>); } /// A class which mocks [NumberFormattingService]. /// /// See the documentation for Mockito's code generation for more information. -class MockNumberFormattingService extends _i1.Mock implements _i2.NumberFormattingService { +class MockNumberFormattingService extends _i1.Mock + implements _i2.NumberFormattingService { MockNumberFormattingService() { _i1.throwOnMissingStub(this); } @@ -741,7 +1104,7 @@ class MockNumberFormattingService extends _i1.Mock implements _i2.NumberFormatti @override String formatBalance( BigInt? balance, { - int? maxDecimals = 4, + int? maxDecimals = 2, bool? addThousandsSeparators = true, bool? addSymbol = false, }) => @@ -749,14 +1112,22 @@ class MockNumberFormattingService extends _i1.Mock implements _i2.NumberFormatti Invocation.method( #formatBalance, [balance], - {#maxDecimals: maxDecimals, #addThousandsSeparators: addThousandsSeparators, #addSymbol: addSymbol}, + { + #maxDecimals: maxDecimals, + #addThousandsSeparators: addThousandsSeparators, + #addSymbol: addSymbol, + }, ), - returnValue: _i5.dummyValue( + returnValue: _i6.dummyValue( this, Invocation.method( #formatBalance, [balance], - {#maxDecimals: maxDecimals, #addThousandsSeparators: addThousandsSeparators, #addSymbol: addSymbol}, + { + #maxDecimals: maxDecimals, + #addThousandsSeparators: addThousandsSeparators, + #addSymbol: addSymbol, + }, ), ), ) @@ -764,5 +1135,6 @@ class MockNumberFormattingService extends _i1.Mock implements _i2.NumberFormatti @override BigInt? parseAmount(String? formattedAmount) => - (super.noSuchMethod(Invocation.method(#parseAmount, [formattedAmount])) as BigInt?); + (super.noSuchMethod(Invocation.method(#parseAmount, [formattedAmount])) + as BigInt?); } diff --git a/quantus_sdk/lib/src/services/settings_service.dart b/quantus_sdk/lib/src/services/settings_service.dart index f9c04416..ce2495ca 100644 --- a/quantus_sdk/lib/src/services/settings_service.dart +++ b/quantus_sdk/lib/src/services/settings_service.dart @@ -26,11 +26,7 @@ class SettingsService { static const String _activeDisplayAccountKey = 'active_display_account'; static const String _balanceHiddenKey = 'balance_hidden'; - // Local authentication keys - static const String _isLocalAuthEnabledKey = 'is_local_auth_enabled'; - static const String _lastSuccessfulAuthKey = 'last_successful_auth'; static const String _lastPausedTimeKey = 'last_paused_time'; - static const String _authTimeoutKey = 'auth_timeout'; // referral status static const String hasCheckedReferralKey = 'referral_check'; @@ -265,25 +261,6 @@ class SettingsService { return await _secureStorage.read(key: getMnemonicKey(walletIndex)); } - // PIN Code Settings - Future setPin(String pin) async { - await _secureStorage.write(key: 'wallet_pin', value: pin); - } - - Future getPin() async { - return await _secureStorage.read(key: 'wallet_pin'); - } - - Future hasPin() async { - final pin = await _secureStorage.read(key: 'wallet_pin'); - return pin != null && pin.isNotEmpty; - } - - Future verifyPin(String pin) async { - final stored = await _secureStorage.read(key: 'wallet_pin'); - return stored == pin; - } - // Reversible Transaction Settings Future setReversibleEnabled(bool enabled) async { await _prefs.setBool('reversible_enabled', enabled); @@ -327,18 +304,6 @@ class SettingsService { return _prefs.getString(key); } - DateTime? getLastSuccessfulAuthTime() { - final String? lastAuthString = _prefs.getString(_lastSuccessfulAuthKey); - if (lastAuthString == null) return null; - - final DateTime lastAuth = DateTime.parse(lastAuthString); - return lastAuth; - } - - void setLastSuccessfulAuthTime(DateTime time) { - _prefs.setString(_lastSuccessfulAuthKey, time.toIso8601String()); - } - DateTime? getLastPausedTime() { final String? lastPausedString = _prefs.getString(_lastPausedTimeKey); if (lastPausedString == null) return null; @@ -355,27 +320,6 @@ class SettingsService { _prefs.remove(_lastPausedTimeKey); } - /// Do not call this directly - call local auth service getAuthTimeoutMinutes() instead. - int? getAuthTimeout() { - final int? authTimeout = _prefs.getInt(_authTimeoutKey); - if (authTimeout == null) return null; - - return authTimeout; - } - - /// Do not call this directly - call local auth service setAuthTimeoutMinutes() instead. - void setAuthTimeout(int timeoutDurationInMinutes) { - _prefs.setInt(_authTimeoutKey, timeoutDurationInMinutes); - } - - void setAuthEnabled(bool enabled) { - _prefs.setBool(_isLocalAuthEnabledKey, enabled); - } - - bool isAuthEnabled() { - return _prefs.getBool(_isLocalAuthEnabledKey) ?? false; - } - // --- Migration Methods --- /// Check if old accounts exist in legacy storage From 717bbf23f846d721f4932fe0beef4a0aac4eb5e9 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 13:25:32 +0800 Subject: [PATCH 05/14] build 74 --- mobile-app/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile-app/pubspec.yaml b/mobile-app/pubspec.yaml index 72fb2a20..6c924285 100644 --- a/mobile-app/pubspec.yaml +++ b/mobile-app/pubspec.yaml @@ -2,7 +2,7 @@ name: resonance_network_wallet description: A Flutter wallet for the Quantus blockchain. publish_to: "none" -version: 1.2.0+73 +version: 1.2.0+74 environment: sdk: ">=3.8.0 <4.0.0" From 3fcf108df938446a49c33dd7a47ad7458c6f9542 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 20:14:27 +0800 Subject: [PATCH 06/14] test script --- scripts/debug_subsquid.sh | 68 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100755 scripts/debug_subsquid.sh diff --git a/scripts/debug_subsquid.sh b/scripts/debug_subsquid.sh new file mode 100755 index 00000000..fd8ee209 --- /dev/null +++ b/scripts/debug_subsquid.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +ENDPOINT="https://subsquid.quantus.com/graphql" +LIMIT=20 +OFFSET=0 + +SINGLE_ACCOUNT='["qzmNaLjPU7hcvkjHpGmrVDPD9y12vdAFimCSrP1GkhVFJMaUq"]' +ALL_ACCOUNTS='["qzmNaLjPU7hcvkjHpGmrVDPD9y12vdAFimCSrP1GkhVFJMaUq","qzn5St24cMsjE4JKYdXLBctusWj5zom67dnrW22SweAahLGeG","qznUDF2JJD8XCnVAXSzVUA7TncMEmagfd4UEzGDwFwHszV29E","qzmuHhD8p2dvndwkbjw5htDvaptyis5rEVc7v5BmqR3pfQ7QN","qzpmyBs51FJNbyv9YgtdhrVCiGX2tgY9iaeGZUHKsx57wDWuT","qzqBzwLQAZ4G5eKF8TYGjmHcENm6hbiTLGLPJhMmFquWbG19u","qzq5yUyndQZJeMaNqxDQkBQpf44kRthNSfo9Tv9fBeBuDtbUh","qzjqcZbyemACctJ7rQi2G63v461CzVrDSRu7LVEivXDxHi4dJ"]' + +SCHEDULED_QUERY='query ScheduledTransfersByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { events( limit: $limit offset: $offset where: { reversibleTransfer: { AND: [ { status_eq: SCHEDULED }, { OR: [ { from: { id_in: $accounts } }, { to: { id_in: $accounts } } ] } ] } } orderBy: reversibleTransfer_scheduledAt_DESC ) { id reversibleTransfer { id amount timestamp from { id } to { id } txId scheduledAt status block { height hash } extrinsicHash timestamp } } }' + +# Original combined query (SLOW) +EVENTS_QUERY='query EventsByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { events( limit: $limit, offset: $offset, where: { OR: [ { AND: [ { extrinsicHash_isNull: false }, { transfer: { OR: [ { from: { id_in: $accounts } }, { to: { id_in: $accounts } } ] } } ] }, { AND: [ { extrinsicHash_isNull: false }, { reversibleTransfer: { AND: [ { status_not_eq: SCHEDULED }, { OR: [ { from: { id_in: $accounts } }, { to: { id_in: $accounts } } ] } ] } } ] }, { minerReward: { miner: { id_in: $accounts } } } ] }, orderBy: timestamp_DESC ) { id transfer { id amount timestamp from { id } to { id } block { height hash } extrinsicHash timestamp fee } reversibleTransfer { id amount timestamp from { id } to { id } txId scheduledAt status block { height hash } extrinsicHash timestamp } minerReward { id reward timestamp miner { id } block { height hash } } extrinsicHash } }' + +# Split query 1: Transfers only +TRANSFERS_QUERY='query TransfersByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { events( limit: $limit, offset: $offset, where: { extrinsicHash_isNull: false, transfer: { OR: [ { from: { id_in: $accounts } }, { to: { id_in: $accounts } } ] } }, orderBy: timestamp_DESC ) { id transfer { id amount timestamp from { id } to { id } block { height hash } extrinsicHash timestamp fee } extrinsicHash } }' + +# Split query 2: Reversible transfers only (non-scheduled) +REVERSIBLE_QUERY='query ReversibleByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { events( limit: $limit, offset: $offset, where: { extrinsicHash_isNull: false, reversibleTransfer: { AND: [ { status_not_eq: SCHEDULED }, { OR: [ { from: { id_in: $accounts } }, { to: { id_in: $accounts } } ] } ] } }, orderBy: timestamp_DESC ) { id reversibleTransfer { id amount timestamp from { id } to { id } txId scheduledAt status block { height hash } extrinsicHash timestamp } extrinsicHash } }' + +# Split query 3: Miner rewards only +REWARDS_QUERY='query RewardsByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { events( limit: $limit, offset: $offset, where: { minerReward: { miner: { id_in: $accounts } } }, orderBy: timestamp_DESC ) { id minerReward { id reward timestamp miner { id } block { height hash } } extrinsicHash } }' + +FMT=" DNS: %{time_namelookup}s | Connect: %{time_connect}s | TLS: %{time_appconnect}s | FirstByte: %{time_starttransfer}s | Total: %{time_total}s | Size: %{size_download} bytes | HTTP: %{http_code}\n" + +run_query() { + local label="$1" + local query="$2" + local accounts="$3" + local body='{"query":"'"$query"'","variables":{"accounts":'"$accounts"',"limit":'"$LIMIT"',"offset":'"$OFFSET"'}}' + + echo "$label" + curl -s -o /dev/null -w "$FMT" -X POST "$ENDPOINT" -H 'Content-Type: application/json' -d "$body" +} + +echo "============================================" +echo "Subsquid Debug - $(date)" +echo "============================================" +echo "" + +echo "==== ORIGINAL COMBINED QUERY ====" +echo "" +run_query "Combined Events (1 account):" "$EVENTS_QUERY" "$SINGLE_ACCOUNT" +echo "" +run_query "Combined Events (8 accounts):" "$EVENTS_QUERY" "$ALL_ACCOUNTS" +echo "" + +echo "==== SPLIT QUERIES (1 account) ====" +echo "" +run_query "Transfers only (1 account):" "$TRANSFERS_QUERY" "$SINGLE_ACCOUNT" +echo "" +run_query "Reversible only (1 account):" "$REVERSIBLE_QUERY" "$SINGLE_ACCOUNT" +echo "" +run_query "Miner rewards only (1 account):" "$REWARDS_QUERY" "$SINGLE_ACCOUNT" +echo "" + +echo "==== SPLIT QUERIES (8 accounts) ====" +echo "" +run_query "Transfers only (8 accounts):" "$TRANSFERS_QUERY" "$ALL_ACCOUNTS" +echo "" +run_query "Reversible only (8 accounts):" "$REVERSIBLE_QUERY" "$ALL_ACCOUNTS" +echo "" +run_query "Miner rewards only (8 accounts):" "$REWARDS_QUERY" "$ALL_ACCOUNTS" +echo "" + +echo "============================================" +echo "Done" +echo "============================================" From 26f303da38f78eb7d9c92e06518b6e0f502b1838 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 20:22:20 +0800 Subject: [PATCH 07/14] query speedup by 500% --- .../unified_pagination_controller.dart | 6 +- .../lib/services/local_auth_service.dart | 10 +- .../lib/v2/screens/home/home_screen.dart | 9 + .../settings/recovery_phrase_screen.dart | 4 +- .../widget/send_screen_widget_test.mocks.dart | 472 ++++-------------- .../src/services/chain_history_service.dart | 292 ++++++----- 6 files changed, 276 insertions(+), 517 deletions(-) diff --git a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart index 15fa0e1b..4b97ab69 100644 --- a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart +++ b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart @@ -69,6 +69,7 @@ class UnifiedPaginationController extends StateNotifier { } Future _fetchPage(List targetAccountIds) async { + final sw = Stopwatch()..start(); try { print( 'UnifiedPaginationController: Fetching page for accounts:' @@ -79,12 +80,14 @@ class UnifiedPaginationController extends StateNotifier { .read(chainHistoryServiceProvider) .fetchAllTransactionTypes(accountIds: targetAccountIds, limit: _limit, offset: state.offset); + sw.stop(); final newItems = newTransactions.otherTransfers; print( 'UnifiedPaginationController: Fetched ${newItems.length} ' 'transactions, ${newTransactions.reversibleTransfers.length} ' 'reversible', ); + print('[TIMING] _fetchPage TOTAL: ${sw.elapsedMilliseconds}ms'); state = state.copyWith( items: [...state.items, ...newItems], reversibleTransfers: state.offset == 0 ? newTransactions.reversibleTransfers : state.reversibleTransfers, @@ -95,7 +98,8 @@ class UnifiedPaginationController extends StateNotifier { stackTrace: null, ); } catch (e, st) { - print('Fetch page failed: $e\n$st'); + sw.stop(); + print('Fetch page failed after ${sw.elapsedMilliseconds}ms: $e\n$st'); state = state.copyWith(error: e, stackTrace: st, isFetching: false); } } diff --git a/mobile-app/lib/services/local_auth_service.dart b/mobile-app/lib/services/local_auth_service.dart index 55707025..e9fb5ee2 100644 --- a/mobile-app/lib/services/local_auth_service.dart +++ b/mobile-app/lib/services/local_auth_service.dart @@ -38,20 +38,14 @@ class LocalAuthService { } } - Future authenticate({ - String localizedReason = 'Please authenticate to access your wallet', - }) async { + Future authenticate({String localizedReason = 'Please authenticate to access your wallet'}) async { try { final isAvailable = await isBiometricAvailable(); if (!isAvailable) return true; final didAuthenticate = await _localAuth.authenticate( localizedReason: localizedReason, - options: const AuthenticationOptions( - biometricOnly: false, - stickyAuth: true, - sensitiveTransaction: true, - ), + options: const AuthenticationOptions(biometricOnly: false, stickyAuth: true, sensitiveTransaction: true), ); if (didAuthenticate) _cleanLastPausedTime(); diff --git a/mobile-app/lib/v2/screens/home/home_screen.dart b/mobile-app/lib/v2/screens/home/home_screen.dart index 0cb59e46..0e02f62d 100644 --- a/mobile-app/lib/v2/screens/home/home_screen.dart +++ b/mobile-app/lib/v2/screens/home/home_screen.dart @@ -35,6 +35,7 @@ class _HomeScreenState extends ConsumerState { static const _actionButtonBgAsset = 'assets/v2/glass_104_x_80.png'; final NumberFormattingService _fmt = NumberFormattingService(); + Stopwatch? _txLoadingStopwatch; Future _refresh() async { final active = ref.read(activeAccountProvider).value; @@ -71,6 +72,14 @@ class _HomeScreenState extends ConsumerState { final accountAsync = ref.watch(activeAccountProvider); final balanceAsync = ref.watch(balanceProvider); final txAsync = ref.watch(activeAccountTransactionsProvider); + if (txAsync.isLoading && _txLoadingStopwatch == null) { + _txLoadingStopwatch = Stopwatch()..start(); + print('[UI-TIMING] Transaction loading spinner STARTED'); + } else if (!txAsync.isLoading && _txLoadingStopwatch != null) { + _txLoadingStopwatch!.stop(); + print('[UI-TIMING] Transaction loading spinner ENDED after ${_txLoadingStopwatch!.elapsedMilliseconds}ms'); + _txLoadingStopwatch = null; + } final colors = context.colors; final text = context.themeText; diff --git a/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart b/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart index 9e7b1c2c..726b4cbd 100644 --- a/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart +++ b/mobile-app/lib/v2/screens/settings/recovery_phrase_screen.dart @@ -32,9 +32,7 @@ class _RecoveryPhraseScreenState extends State { }); return; } - final ok = await _authService.authenticate( - localizedReason: 'Authenticate to reveal recovery phrase', - ); + final ok = await _authService.authenticate(localizedReason: 'Authenticate to reveal recovery phrase'); if (!ok || !mounted) return; final mnemonic = await _settingsService.getMnemonic(widget.walletIndex); if (mnemonic != null && mounted) { diff --git a/mobile-app/test/widget/send_screen_widget_test.mocks.dart b/mobile-app/test/widget/send_screen_widget_test.mocks.dart index 62cd7d92..33ce9df4 100644 --- a/mobile-app/test/widget/send_screen_widget_test.mocks.dart +++ b/mobile-app/test/widget/send_screen_widget_test.mocks.dart @@ -11,10 +11,8 @@ import 'package:mockito/src/dummies.dart' as _i6; import 'package:polkadart/polkadart.dart' as _i9; import 'package:quantus_sdk/generated/schrodinger/types/pallet_reversible_transfers/high_security_account_data.dart' as _i10; -import 'package:quantus_sdk/generated/schrodinger/types/pallet_reversible_transfers/pending_transfer.dart' - as _i11; -import 'package:quantus_sdk/generated/schrodinger/types/qp_scheduler/block_number_or_timestamp.dart' - as _i8; +import 'package:quantus_sdk/generated/schrodinger/types/pallet_reversible_transfers/pending_transfer.dart' as _i11; +import 'package:quantus_sdk/generated/schrodinger/types/qp_scheduler/block_number_or_timestamp.dart' as _i8; import 'package:quantus_sdk/quantus_sdk.dart' as _i2; import 'package:quantus_sdk/src/models/account.dart' as _i4; import 'package:quantus_sdk/src/models/display_account.dart' as _i5; @@ -34,36 +32,27 @@ import 'package:quantus_sdk/src/models/display_account.dart' as _i5; // ignore_for_file: subtype_of_sealed_class class _FakeKeypair_0 extends _i1.SmartFake implements _i2.Keypair { - _FakeKeypair_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeKeypair_0(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } -class _FakeExtrinsicFeeData_1 extends _i1.SmartFake - implements _i2.ExtrinsicFeeData { - _FakeExtrinsicFeeData_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); +class _FakeExtrinsicFeeData_1 extends _i1.SmartFake implements _i2.ExtrinsicFeeData { + _FakeExtrinsicFeeData_1(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } class _FakeExtrinsicData_2 extends _i1.SmartFake implements _i2.ExtrinsicData { - _FakeExtrinsicData_2(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeExtrinsicData_2(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } -class _FakeUnsignedTransactionData_3 extends _i1.SmartFake - implements _i2.UnsignedTransactionData { - _FakeUnsignedTransactionData_3(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); +class _FakeUnsignedTransactionData_3 extends _i1.SmartFake implements _i2.UnsignedTransactionData { + _FakeUnsignedTransactionData_3(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } class _FakeBalances_4 extends _i1.SmartFake implements _i2.Balances { - _FakeBalances_4(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeBalances_4(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } -class _FakeReversibleTransfers_5 extends _i1.SmartFake - implements _i2.ReversibleTransfers { - _FakeReversibleTransfers_5(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); +class _FakeReversibleTransfers_5 extends _i1.SmartFake implements _i2.ReversibleTransfers { + _FakeReversibleTransfers_5(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } /// A class which mocks [SettingsService]. @@ -111,10 +100,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override List<_i4.Account> getAccountsToMigrate() => - (super.noSuchMethod( - Invocation.method(#getAccountsToMigrate, []), - returnValue: <_i4.Account>[], - ) + (super.noSuchMethod(Invocation.method(#getAccountsToMigrate, []), returnValue: <_i4.Account>[]) as List<_i4.Account>); @override @@ -179,15 +165,9 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { as _i3.Future<_i4.Account?>); @override - _i3.Future<_i4.Account?> getAccount({ - required int? walletIndex, - required int? index, - }) => + _i3.Future<_i4.Account?> getAccount({required int? walletIndex, required int? index}) => (super.noSuchMethod( - Invocation.method(#getAccount, [], { - #walletIndex: walletIndex, - #index: index, - }), + Invocation.method(#getAccount, [], {#walletIndex: walletIndex, #index: index}), returnValue: _i3.Future<_i4.Account?>.value(), ) as _i3.Future<_i4.Account?>); @@ -204,9 +184,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { _i3.Future> getAddressBook() => (super.noSuchMethod( Invocation.method(#getAddressBook, []), - returnValue: _i3.Future>.value( - {}, - ), + returnValue: _i3.Future>.value({}), ) as _i3.Future>); @@ -230,10 +208,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getAddressName(String? address) => - (super.noSuchMethod( - Invocation.method(#getAddressName, [address]), - returnValue: _i3.Future.value(), - ) + (super.noSuchMethod(Invocation.method(#getAddressName, [address]), returnValue: _i3.Future.value()) as _i3.Future); @override @@ -247,28 +222,19 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getHasWallet() => - (super.noSuchMethod( - Invocation.method(#getHasWallet, []), - returnValue: _i3.Future.value(false), - ) + (super.noSuchMethod(Invocation.method(#getHasWallet, []), returnValue: _i3.Future.value(false)) as _i3.Future); @override _i3.Future isWalletLoggedOut() => - (super.noSuchMethod( - Invocation.method(#isWalletLoggedOut, []), - returnValue: _i3.Future.value(false), - ) + (super.noSuchMethod(Invocation.method(#isWalletLoggedOut, []), returnValue: _i3.Future.value(false)) as _i3.Future); @override String getMnemonicKey(int? walletIndex) => (super.noSuchMethod( Invocation.method(#getMnemonicKey, [walletIndex]), - returnValue: _i6.dummyValue( - this, - Invocation.method(#getMnemonicKey, [walletIndex]), - ), + returnValue: _i6.dummyValue(this, Invocation.method(#getMnemonicKey, [walletIndex])), ) as String); @@ -283,10 +249,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getMnemonic(int? walletIndex) => - (super.noSuchMethod( - Invocation.method(#getMnemonic, [walletIndex]), - returnValue: _i3.Future.value(), - ) + (super.noSuchMethod(Invocation.method(#getMnemonic, [walletIndex]), returnValue: _i3.Future.value()) as _i3.Future); @override @@ -300,11 +263,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override bool isReversibleEnabled() => - (super.noSuchMethod( - Invocation.method(#isReversibleEnabled, []), - returnValue: false, - ) - as bool); + (super.noSuchMethod(Invocation.method(#isReversibleEnabled, []), returnValue: false) as bool); @override _i3.Future setReversibleTimeSeconds(int? seconds) => @@ -317,10 +276,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getReversibleTimeSeconds() => - (super.noSuchMethod( - Invocation.method(#getReversibleTimeSeconds, []), - returnValue: _i3.Future.value(), - ) + (super.noSuchMethod(Invocation.method(#getReversibleTimeSeconds, []), returnValue: _i3.Future.value()) as _i3.Future); @override @@ -333,19 +289,11 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { as _i3.Future); @override - bool isBalanceHidden() => - (super.noSuchMethod( - Invocation.method(#isBalanceHidden, []), - returnValue: false, - ) - as bool); + bool isBalanceHidden() => (super.noSuchMethod(Invocation.method(#isBalanceHidden, []), returnValue: false) as bool); @override _i3.Future getBool(String? key) => - (super.noSuchMethod( - Invocation.method(#getBool, [key]), - returnValue: _i3.Future.value(), - ) + (super.noSuchMethod(Invocation.method(#getBool, [key]), returnValue: _i3.Future.value()) as _i3.Future); @override @@ -359,39 +307,23 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override _i3.Future getString(String? key) => - (super.noSuchMethod( - Invocation.method(#getString, [key]), - returnValue: _i3.Future.value(), - ) + (super.noSuchMethod(Invocation.method(#getString, [key]), returnValue: _i3.Future.value()) as _i3.Future); @override - void setLastPausedTime(DateTime? time) => super.noSuchMethod( - Invocation.method(#setLastPausedTime, [time]), - returnValueForMissingStub: null, - ); + void setLastPausedTime(DateTime? time) => + super.noSuchMethod(Invocation.method(#setLastPausedTime, [time]), returnValueForMissingStub: null); @override - void cleanLastPausedTime() => super.noSuchMethod( - Invocation.method(#cleanLastPausedTime, []), - returnValueForMissingStub: null, - ); + void cleanLastPausedTime() => + super.noSuchMethod(Invocation.method(#cleanLastPausedTime, []), returnValueForMissingStub: null); @override - bool hasOldAccounts() => - (super.noSuchMethod( - Invocation.method(#hasOldAccounts, []), - returnValue: false, - ) - as bool); + bool hasOldAccounts() => (super.noSuchMethod(Invocation.method(#hasOldAccounts, []), returnValue: false) as bool); @override List<_i4.Account> getOldAccounts() => - (super.noSuchMethod( - Invocation.method(#getOldAccounts, []), - returnValue: <_i4.Account>[], - ) - as List<_i4.Account>); + (super.noSuchMethod(Invocation.method(#getOldAccounts, []), returnValue: <_i4.Account>[]) as List<_i4.Account>); @override _i3.Future clearOldAccounts() => @@ -412,10 +344,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { as _i3.Future); @override - void resetForTest() => super.noSuchMethod( - Invocation.method(#resetForTest, []), - returnValueForMissingStub: null, - ); + void resetForTest() => super.noSuchMethod(Invocation.method(#resetForTest, []), returnValueForMissingStub: null); @override _i3.Future clearAll() => @@ -428,69 +357,43 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { @override bool referralCheckCompleted() => - (super.noSuchMethod( - Invocation.method(#referralCheckCompleted, []), - returnValue: false, - ) - as bool); + (super.noSuchMethod(Invocation.method(#referralCheckCompleted, []), returnValue: false) as bool); @override - void setReferralCheckCompleted() => super.noSuchMethod( - Invocation.method(#setReferralCheckCompleted, []), - returnValueForMissingStub: null, - ); + void setReferralCheckCompleted() => + super.noSuchMethod(Invocation.method(#setReferralCheckCompleted, []), returnValueForMissingStub: null); @override - void clearReferralCheckCompletedFlag() => super.noSuchMethod( - Invocation.method(#clearReferralCheckCompletedFlag, []), - returnValueForMissingStub: null, - ); + void clearReferralCheckCompletedFlag() => + super.noSuchMethod(Invocation.method(#clearReferralCheckCompletedFlag, []), returnValueForMissingStub: null); @override - void setReferralCode(String? code) => super.noSuchMethod( - Invocation.method(#setReferralCode, [code]), - returnValueForMissingStub: null, - ); + void setReferralCode(String? code) => + super.noSuchMethod(Invocation.method(#setReferralCode, [code]), returnValueForMissingStub: null); @override bool hasWatchedQuestsPromo() => - (super.noSuchMethod( - Invocation.method(#hasWatchedQuestsPromo, []), - returnValue: false, - ) - as bool); + (super.noSuchMethod(Invocation.method(#hasWatchedQuestsPromo, []), returnValue: false) as bool); @override - void setQuestsPromoWatched() => super.noSuchMethod( - Invocation.method(#setQuestsPromoWatched, []), - returnValueForMissingStub: null, - ); + void setQuestsPromoWatched() => + super.noSuchMethod(Invocation.method(#setQuestsPromoWatched, []), returnValueForMissingStub: null); @override - void clearQuestsPromoWatchedFlag() => super.noSuchMethod( - Invocation.method(#clearQuestsPromoWatchedFlag, []), - returnValueForMissingStub: null, - ); + void clearQuestsPromoWatchedFlag() => + super.noSuchMethod(Invocation.method(#clearQuestsPromoWatchedFlag, []), returnValueForMissingStub: null); @override bool existingUserSeenPromoVideo() => - (super.noSuchMethod( - Invocation.method(#existingUserSeenPromoVideo, []), - returnValue: false, - ) - as bool); + (super.noSuchMethod(Invocation.method(#existingUserSeenPromoVideo, []), returnValue: false) as bool); @override - void setExistingUserSeenPromoVideo() => super.noSuchMethod( - Invocation.method(#setExistingUserSeenPromoVideo, []), - returnValueForMissingStub: null, - ); + void setExistingUserSeenPromoVideo() => + super.noSuchMethod(Invocation.method(#setExistingUserSeenPromoVideo, []), returnValueForMissingStub: null); @override - void clearExistingUserSeenPromoVideoFlag() => super.noSuchMethod( - Invocation.method(#clearExistingUserSeenPromoVideoFlag, []), - returnValueForMissingStub: null, - ); + void clearExistingUserSeenPromoVideoFlag() => + super.noSuchMethod(Invocation.method(#clearExistingUserSeenPromoVideoFlag, []), returnValueForMissingStub: null); } /// A class which mocks [SubstrateService]. @@ -506,10 +409,7 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { (super.noSuchMethod( Invocation.method(#getFee, [signedExtrinsic]), returnValue: _i3.Future.value( - _i6.dummyValue( - this, - Invocation.method(#getFee, [signedExtrinsic]), - ), + _i6.dummyValue(this, Invocation.method(#getFee, [signedExtrinsic])), ), ) as _i3.Future); @@ -519,10 +419,7 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { (super.noSuchMethod( Invocation.method(#queryUserBalance, []), returnValue: _i3.Future.value( - _i6.dummyValue( - this, - Invocation.method(#queryUserBalance, []), - ), + _i6.dummyValue(this, Invocation.method(#queryUserBalance, [])), ), ) as _i3.Future); @@ -532,10 +429,7 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { (super.noSuchMethod( Invocation.method(#queryBalance, [address]), returnValue: _i3.Future.value( - _i6.dummyValue( - this, - Invocation.method(#queryBalance, [address]), - ), + _i6.dummyValue(this, Invocation.method(#queryBalance, [address])), ), ) as _i3.Future); @@ -544,43 +438,24 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { _i2.Keypair nonHDdilithiumKeypairFromMnemonic(String? senderSeed) => (super.noSuchMethod( Invocation.method(#nonHDdilithiumKeypairFromMnemonic, [senderSeed]), - returnValue: _FakeKeypair_0( - this, - Invocation.method(#nonHDdilithiumKeypairFromMnemonic, [ - senderSeed, - ]), - ), + returnValue: _FakeKeypair_0(this, Invocation.method(#nonHDdilithiumKeypairFromMnemonic, [senderSeed])), ) as _i2.Keypair); @override - _i3.Future<_i2.ExtrinsicFeeData> getFeeForCall( - _i4.Account? account, - _i2.RuntimeCall? call, - ) => + _i3.Future<_i2.ExtrinsicFeeData> getFeeForCall(_i4.Account? account, _i2.RuntimeCall? call) => (super.noSuchMethod( Invocation.method(#getFeeForCall, [account, call]), returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( - _FakeExtrinsicFeeData_1( - this, - Invocation.method(#getFeeForCall, [account, call]), - ), + _FakeExtrinsicFeeData_1(this, Invocation.method(#getFeeForCall, [account, call])), ), ) as _i3.Future<_i2.ExtrinsicFeeData>); @override - _i3.Future<_i7.Uint8List> submitExtrinsic( - _i4.Account? account, - _i2.RuntimeCall? call, { - int? maxRetries = 3, - }) => + _i3.Future<_i7.Uint8List> submitExtrinsic(_i4.Account? account, _i2.RuntimeCall? call, {int? maxRetries = 3}) => (super.noSuchMethod( - Invocation.method( - #submitExtrinsic, - [account, call], - {#maxRetries: maxRetries}, - ), + Invocation.method(#submitExtrinsic, [account, call], {#maxRetries: maxRetries}), returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i3.Future<_i7.Uint8List>); @@ -592,39 +467,22 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { bool? isSigned = true, }) => (super.noSuchMethod( - Invocation.method( - #getExtrinsicPayload, - [account, call], - {#isSigned: isSigned}, - ), + Invocation.method(#getExtrinsicPayload, [account, call], {#isSigned: isSigned}), returnValue: _i3.Future<_i2.ExtrinsicData>.value( _FakeExtrinsicData_2( this, - Invocation.method( - #getExtrinsicPayload, - [account, call], - {#isSigned: isSigned}, - ), + Invocation.method(#getExtrinsicPayload, [account, call], {#isSigned: isSigned}), ), ), ) as _i3.Future<_i2.ExtrinsicData>); @override - _i3.Future<_i2.UnsignedTransactionData> getUnsignedTransactionPayload( - _i4.Account? account, - _i2.RuntimeCall? call, - ) => + _i3.Future<_i2.UnsignedTransactionData> getUnsignedTransactionPayload(_i4.Account? account, _i2.RuntimeCall? call) => (super.noSuchMethod( Invocation.method(#getUnsignedTransactionPayload, [account, call]), returnValue: _i3.Future<_i2.UnsignedTransactionData>.value( - _FakeUnsignedTransactionData_3( - this, - Invocation.method(#getUnsignedTransactionPayload, [ - account, - call, - ]), - ), + _FakeUnsignedTransactionData_3(this, Invocation.method(#getUnsignedTransactionPayload, [account, call])), ), ) as _i3.Future<_i2.UnsignedTransactionData>); @@ -636,11 +494,7 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { _i7.Uint8List? publicKey, ) => (super.noSuchMethod( - Invocation.method(#submitExtrinsicWithExternalSignature, [ - unsignedData, - signature, - publicKey, - ]), + Invocation.method(#submitExtrinsicWithExternalSignature, [unsignedData, signature, publicKey]), returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i3.Future<_i7.Uint8List>); @@ -659,45 +513,31 @@ class MockSubstrateService extends _i1.Mock implements _i2.SubstrateService { (super.noSuchMethod( Invocation.method(#generateMnemonic, []), returnValue: _i3.Future.value( - _i6.dummyValue( - this, - Invocation.method(#generateMnemonic, []), - ), + _i6.dummyValue(this, Invocation.method(#generateMnemonic, [])), ), ) as _i3.Future); @override bool isValidSS58Address(String? address) => - (super.noSuchMethod( - Invocation.method(#isValidSS58Address, [address]), - returnValue: false, - ) - as bool); + (super.noSuchMethod(Invocation.method(#isValidSS58Address, [address]), returnValue: false) as bool); @override String bytesToHex(_i7.Uint8List? bytes) => (super.noSuchMethod( Invocation.method(#bytesToHex, [bytes]), - returnValue: _i6.dummyValue( - this, - Invocation.method(#bytesToHex, [bytes]), - ), + returnValue: _i6.dummyValue(this, Invocation.method(#bytesToHex, [bytes])), ) as String); @override - void dispose() => super.noSuchMethod( - Invocation.method(#dispose, []), - returnValueForMissingStub: null, - ); + void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); } /// A class which mocks [HumanReadableChecksumService]. /// /// See the documentation for Mockito's code generation for more information. -class MockHumanReadableChecksumService extends _i1.Mock - implements _i2.HumanReadableChecksumService { +class MockHumanReadableChecksumService extends _i1.Mock implements _i2.HumanReadableChecksumService { MockHumanReadableChecksumService() { _i1.throwOnMissingStub(this); } @@ -712,34 +552,20 @@ class MockHumanReadableChecksumService extends _i1.Mock as _i3.Future); @override - _i3.Future getHumanReadableName( - String? address, { - dynamic upperCase = true, - }) => + _i3.Future getHumanReadableName(String? address, {dynamic upperCase = true}) => (super.noSuchMethod( - Invocation.method( - #getHumanReadableName, - [address], - {#upperCase: upperCase}, - ), + Invocation.method(#getHumanReadableName, [address], {#upperCase: upperCase}), returnValue: _i3.Future.value( _i6.dummyValue( this, - Invocation.method( - #getHumanReadableName, - [address], - {#upperCase: upperCase}, - ), + Invocation.method(#getHumanReadableName, [address], {#upperCase: upperCase}), ), ), ) as _i3.Future); @override - void dispose() => super.noSuchMethod( - Invocation.method(#dispose, []), - returnValueForMissingStub: null, - ); + void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); } /// A class which mocks [BalancesService]. @@ -751,41 +577,21 @@ class MockBalancesService extends _i1.Mock implements _i2.BalancesService { } @override - _i3.Future<_i7.Uint8List> balanceTransfer( - _i4.Account? account, - String? targetAddress, - BigInt? amount, - ) => + _i3.Future<_i7.Uint8List> balanceTransfer(_i4.Account? account, String? targetAddress, BigInt? amount) => (super.noSuchMethod( - Invocation.method(#balanceTransfer, [ - account, - targetAddress, - amount, - ]), + Invocation.method(#balanceTransfer, [account, targetAddress, amount]), returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i3.Future<_i7.Uint8List>); @override - _i3.Future<_i2.ExtrinsicFeeData> getBalanceTransferFee( - _i4.Account? account, - String? targetAddress, - BigInt? amount, - ) => + _i3.Future<_i2.ExtrinsicFeeData> getBalanceTransferFee(_i4.Account? account, String? targetAddress, BigInt? amount) => (super.noSuchMethod( - Invocation.method(#getBalanceTransferFee, [ - account, - targetAddress, - amount, - ]), + Invocation.method(#getBalanceTransferFee, [account, targetAddress, amount]), returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( _FakeExtrinsicFeeData_1( this, - Invocation.method(#getBalanceTransferFee, [ - account, - targetAddress, - amount, - ]), + Invocation.method(#getBalanceTransferFee, [account, targetAddress, amount]), ), ), ) @@ -795,34 +601,17 @@ class MockBalancesService extends _i1.Mock implements _i2.BalancesService { _i2.Balances getBalanceTransferCall(String? targetAddress, BigInt? amount) => (super.noSuchMethod( Invocation.method(#getBalanceTransferCall, [targetAddress, amount]), - returnValue: _FakeBalances_4( - this, - Invocation.method(#getBalanceTransferCall, [ - targetAddress, - amount, - ]), - ), + returnValue: _FakeBalances_4(this, Invocation.method(#getBalanceTransferCall, [targetAddress, amount])), ) as _i2.Balances); @override - _i2.Balances getTransferAllCall( - String? targetAddress, { - bool? keepAlive = false, - }) => + _i2.Balances getTransferAllCall(String? targetAddress, {bool? keepAlive = false}) => (super.noSuchMethod( - Invocation.method( - #getTransferAllCall, - [targetAddress], - {#keepAlive: keepAlive}, - ), + Invocation.method(#getTransferAllCall, [targetAddress], {#keepAlive: keepAlive}), returnValue: _FakeBalances_4( this, - Invocation.method( - #getTransferAllCall, - [targetAddress], - {#keepAlive: keepAlive}, - ), + Invocation.method(#getTransferAllCall, [targetAddress], {#keepAlive: keepAlive}), ), ) as _i2.Balances); @@ -831,8 +620,7 @@ class MockBalancesService extends _i1.Mock implements _i2.BalancesService { /// A class which mocks [ReversibleTransfersService]. /// /// See the documentation for Mockito's code generation for more information. -class MockReversibleTransfersService extends _i1.Mock - implements _i2.ReversibleTransfersService { +class MockReversibleTransfersService extends _i1.Mock implements _i2.ReversibleTransfersService { MockReversibleTransfersService() { _i1.throwOnMissingStub(this); } @@ -890,16 +678,12 @@ class MockReversibleTransfersService extends _i1.Mock returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( _FakeExtrinsicFeeData_1( this, - Invocation.method( - #getReversibleTransferWithDelayFeeEstimate, - [], - { - #account: account, - #recipientAddress: recipientAddress, - #amount: amount, - #delaySeconds: delaySeconds, - }, - ), + Invocation.method(#getReversibleTransferWithDelayFeeEstimate, [], { + #account: account, + #recipientAddress: recipientAddress, + #amount: amount, + #delaySeconds: delaySeconds, + }), ), ), ) @@ -912,18 +696,10 @@ class MockReversibleTransfersService extends _i1.Mock _i8.BlockNumberOrTimestamp? delay, ) => (super.noSuchMethod( - Invocation.method(#getReversibleTransferCall, [ - recipientAddress, - amount, - delay, - ]), + Invocation.method(#getReversibleTransferCall, [recipientAddress, amount, delay]), returnValue: _FakeReversibleTransfers_5( this, - Invocation.method(#getReversibleTransferCall, [ - recipientAddress, - amount, - delay, - ]), + Invocation.method(#getReversibleTransferCall, [recipientAddress, amount, delay]), ), ) as _i2.ReversibleTransfers); @@ -954,18 +730,13 @@ class MockReversibleTransfersService extends _i1.Mock required List? transactionId, }) => (super.noSuchMethod( - Invocation.method(#cancelReversibleTransfer, [], { - #account: account, - #transactionId: transactionId, - }), + Invocation.method(#cancelReversibleTransfer, [], {#account: account, #transactionId: transactionId}), returnValue: _i3.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i3.Future<_i7.Uint8List>); @override - _i3.Future<_i10.HighSecurityAccountData?> getHighSecurityConfig( - String? address, - ) => + _i3.Future<_i10.HighSecurityAccountData?> getHighSecurityConfig(String? address) => (super.noSuchMethod( Invocation.method(#getHighSecurityConfig, [address]), returnValue: _i3.Future<_i10.HighSecurityAccountData?>.value(), @@ -973,9 +744,7 @@ class MockReversibleTransfersService extends _i1.Mock as _i3.Future<_i10.HighSecurityAccountData?>); @override - _i3.Future<_i11.PendingTransfer?> getPendingTransfer( - List? transactionId, - ) => + _i3.Future<_i11.PendingTransfer?> getPendingTransfer(List? transactionId) => (super.noSuchMethod( Invocation.method(#getPendingTransfer, [transactionId]), returnValue: _i3.Future<_i11.PendingTransfer?>.value(), @@ -984,21 +753,14 @@ class MockReversibleTransfersService extends _i1.Mock @override _i3.Future getAccountPendingIndex(String? address) => - (super.noSuchMethod( - Invocation.method(#getAccountPendingIndex, [address]), - returnValue: _i3.Future.value(0), - ) + (super.noSuchMethod(Invocation.method(#getAccountPendingIndex, [address]), returnValue: _i3.Future.value(0)) as _i3.Future); @override - _i3.Future> getAccountPendingTransfers( - String? address, - ) => + _i3.Future> getAccountPendingTransfers(String? address) => (super.noSuchMethod( Invocation.method(#getAccountPendingTransfers, [address]), - returnValue: _i3.Future>.value( - <_i11.PendingTransfer>[], - ), + returnValue: _i3.Future>.value(<_i11.PendingTransfer>[]), ) as _i3.Future>); @@ -1006,9 +768,7 @@ class MockReversibleTransfersService extends _i1.Mock _i3.Future> getConstants() => (super.noSuchMethod( Invocation.method(#getConstants, []), - returnValue: _i3.Future>.value( - {}, - ), + returnValue: _i3.Future>.value({}), ) as _i3.Future>); @@ -1030,10 +790,7 @@ class MockReversibleTransfersService extends _i1.Mock @override _i3.Future isHighSecurity(String? address) => - (super.noSuchMethod( - Invocation.method(#isHighSecurity, [address]), - returnValue: _i3.Future.value(false), - ) + (super.noSuchMethod(Invocation.method(#isHighSecurity, [address]), returnValue: _i3.Future.value(false)) as _i3.Future); @override @@ -1052,10 +809,7 @@ class MockReversibleTransfersService extends _i1.Mock @override _i3.Future isGuardian(String? address) => - (super.noSuchMethod( - Invocation.method(#isGuardian, [address]), - returnValue: _i3.Future.value(false), - ) + (super.noSuchMethod(Invocation.method(#isGuardian, [address]), returnValue: _i3.Future.value(false)) as _i3.Future); @override @@ -1073,19 +827,11 @@ class MockReversibleTransfersService extends _i1.Mock Duration? safeguardDuration, ) => (super.noSuchMethod( - Invocation.method(#getHighSecuritySetupFee, [ - account, - guardianAccountId, - safeguardDuration, - ]), + Invocation.method(#getHighSecuritySetupFee, [account, guardianAccountId, safeguardDuration]), returnValue: _i3.Future<_i2.ExtrinsicFeeData>.value( _FakeExtrinsicFeeData_1( this, - Invocation.method(#getHighSecuritySetupFee, [ - account, - guardianAccountId, - safeguardDuration, - ]), + Invocation.method(#getHighSecuritySetupFee, [account, guardianAccountId, safeguardDuration]), ), ), ) @@ -1095,8 +841,7 @@ class MockReversibleTransfersService extends _i1.Mock /// A class which mocks [NumberFormattingService]. /// /// See the documentation for Mockito's code generation for more information. -class MockNumberFormattingService extends _i1.Mock - implements _i2.NumberFormattingService { +class MockNumberFormattingService extends _i1.Mock implements _i2.NumberFormattingService { MockNumberFormattingService() { _i1.throwOnMissingStub(this); } @@ -1112,22 +857,14 @@ class MockNumberFormattingService extends _i1.Mock Invocation.method( #formatBalance, [balance], - { - #maxDecimals: maxDecimals, - #addThousandsSeparators: addThousandsSeparators, - #addSymbol: addSymbol, - }, + {#maxDecimals: maxDecimals, #addThousandsSeparators: addThousandsSeparators, #addSymbol: addSymbol}, ), returnValue: _i6.dummyValue( this, Invocation.method( #formatBalance, [balance], - { - #maxDecimals: maxDecimals, - #addThousandsSeparators: addThousandsSeparators, - #addSymbol: addSymbol, - }, + {#maxDecimals: maxDecimals, #addThousandsSeparators: addThousandsSeparators, #addSymbol: addSymbol}, ), ), ) @@ -1135,6 +872,5 @@ class MockNumberFormattingService extends _i1.Mock @override BigInt? parseAmount(String? formattedAmount) => - (super.noSuchMethod(Invocation.method(#parseAmount, [formattedAmount])) - as BigInt?); + (super.noSuchMethod(Invocation.method(#parseAmount, [formattedAmount])) as BigInt?); } diff --git a/quantus_sdk/lib/src/services/chain_history_service.dart b/quantus_sdk/lib/src/services/chain_history_service.dart index 2ff990d5..4dd35ac8 100644 --- a/quantus_sdk/lib/src/services/chain_history_service.dart +++ b/quantus_sdk/lib/src/services/chain_history_service.dart @@ -130,85 +130,44 @@ query TransactionsByHash($transactionHashes: [String!]!, $limit: Int!, $offset: } }'''; - // Events for accounts including transfers, reversible transfers and miner rewards. - final String _eventsByAccountsWithRewardsQuery = r''' -query EventsByAccounts( - $accounts: [String!]!, - $limit: Int!, - $offset: Int! -) { + final String _transfersByAccountsQuery = r''' +query TransfersByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { events( - limit: $limit, - offset: $offset, - where: { - OR: [ - { - AND: [ - { extrinsicHash_isNull: false }, - { transfer: { - OR: [ - { from: { id_in: $accounts } }, - { to: { id_in: $accounts } } - ] - } } - ] - }, - { - AND: [ - { extrinsicHash_isNull: false }, - { reversibleTransfer: { - AND: [ - { status_not_eq: SCHEDULED }, - { OR: [ - { from: { id_in: $accounts } }, - { to: { id_in: $accounts } } - ] - } - ] - } } - ] - }, - { minerReward: { miner: { id_in: $accounts } } } - ] - }, + limit: $limit, offset: $offset, + where: { extrinsicHash_isNull: false, transfer: { OR: [{ from: { id_in: $accounts } }, { to: { id_in: $accounts } }] } }, orderBy: timestamp_DESC ) { id - transfer { - id - amount - timestamp - from { id } - to { id } - block { height hash } - extrinsicHash - timestamp - fee - } - reversibleTransfer { - id - amount - timestamp - from { id } - to { id } - txId - scheduledAt - status - block { height hash } - extrinsicHash - timestamp - } - minerReward { - id - reward - timestamp - miner { id } - block { height hash } - } + transfer { id amount timestamp from { id } to { id } block { height hash } extrinsicHash timestamp fee } extrinsicHash } -} -'''; +}'''; + + final String _reversibleByAccountsQuery = r''' +query ReversibleByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { + events( + limit: $limit, offset: $offset, + where: { extrinsicHash_isNull: false, reversibleTransfer: { AND: [{ status_not_eq: SCHEDULED }, { OR: [{ from: { id_in: $accounts } }, { to: { id_in: $accounts } }] }] } }, + orderBy: timestamp_DESC + ) { + id + reversibleTransfer { id amount timestamp from { id } to { id } txId scheduledAt status block { height hash } extrinsicHash timestamp } + extrinsicHash + } +}'''; + + final String _rewardsByAccountsQuery = r''' +query RewardsByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { + events( + limit: $limit, offset: $offset, + where: { minerReward: { miner: { id_in: $accounts } } }, + orderBy: timestamp_DESC + ) { + id + minerReward { id reward timestamp miner { id } block { height hash } } + extrinsicHash + } +}'''; final String _reversibleTransactionsInBlockQuery = r''' query TransactionsAndBlockInBlock( @@ -390,14 +349,33 @@ query SearchPendingTransaction( int offset = 0, String? printName, }) async { - final scheduled = await fetchScheduledTransfers(accountIds: accountIds); - final other = await _fetchOtherTransfers( - accountIds: accountIds, - limit: limit, - offset: offset, - printName: printName, - ); - + final totalSw = Stopwatch()..start(); + + final results = await Future.wait([ + () async { + final sw = Stopwatch()..start(); + final r = await fetchScheduledTransfers(accountIds: accountIds); + print('[TIMING] fetchScheduledTransfers: ${sw.elapsedMilliseconds}ms'); + return r; + }(), + () async { + final sw = Stopwatch()..start(); + final r = await _fetchOtherTransfers( + accountIds: accountIds, + limit: limit, + offset: offset, + printName: printName, + ); + print('[TIMING] _fetchOtherTransfers: ${sw.elapsedMilliseconds}ms'); + return r; + }(), + ]); + + totalSw.stop(); + print('[TIMING] fetchAllTransactionTypes TOTAL: ${totalSw.elapsedMilliseconds}ms'); + + final scheduled = results[0] as List; + final other = results[1] as TransferList; return SortedTransactionsList(reversibleTransfers: scheduled, otherTransfers: other.transfers); } @@ -490,8 +468,13 @@ query SearchPendingTransaction( 'variables': {'accounts': accountIds, 'limit': limit, 'offset': offset}, }; + final jsonBody = jsonEncode(requestBody); + + final sw = Stopwatch()..start(); try { - final http.Response response = await _graphQlEndpointService.post(body: jsonEncode(requestBody)); + final http.Response response = await _graphQlEndpointService.post(body: jsonBody); + sw.stop(); + print('[TIMING] fetchScheduledTransfers HTTP: ${sw.elapsedMilliseconds}ms (status: ${response.statusCode})'); if (response.statusCode != 200) { throw Exception('GraphQL request failed with status: ${response.statusCode}. Body: ${response.body}'); @@ -511,78 +494,113 @@ query SearchPendingTransaction( return result; } catch (e, stackTrace) { + sw.stop(); + print('[TIMING] fetchScheduledTransfers FAILED after ${sw.elapsedMilliseconds}ms'); print('Error fetching scheduled transfers: $e'); print(stackTrace); rethrow; } } - Future _fetchOtherTransfers({ + Future> _fetchSingleType({ + required String query, + required String label, required List accountIds, - int limit = 10, - int offset = 0, - String? printName, + required int limit, + required int offset, + required TransactionEvent? Function(Map event) parser, }) async { - print( - '${printName ?? ''} ' - ' fetchTransfers for account: $accountIds (limit: $limit, offset: $offset)', - ); - - // Construct the GraphQL request body - final Map requestBody = { - 'query': _eventsByAccountsWithRewardsQuery, + final body = jsonEncode({ + 'query': query, 'variables': {'accounts': accountIds, 'limit': limit, 'offset': offset}, - }; - - try { - final http.Response response = await _graphQlEndpointService.post(body: jsonEncode(requestBody)); - - if (response.statusCode != 200) { - throw Exception('GraphQL request failed with status: ${response.statusCode}. Body: ${response.body}'); - } + }); - final Map responseBody = jsonDecode(response.body); - - if (responseBody['errors'] != null) { - print('GraphQL errors in response: ${responseBody['errors']}'); - throw Exception('GraphQL errors: ${responseBody['errors'].toString()}'); - } - - final Map? data = responseBody['data']; - if (data == null) { - throw Exception('GraphQL response data is null.'); - } + final sw = Stopwatch()..start(); + final http.Response response = await _graphQlEndpointService.post(body: body); + print('[TIMING] $label HTTP: ${sw.elapsedMilliseconds}ms'); - final List? events = data['events']; + if (response.statusCode != 200) { + throw Exception('GraphQL $label failed: ${response.statusCode}. Body: ${response.body}'); + } - if (events == null || events.isEmpty) { - return TransferList(transfers: [], hasMore: false, nextOffset: offset); - } + final responseBody = jsonDecode(response.body) as Map; + if (responseBody['errors'] != null) { + throw Exception('GraphQL $label errors: ${responseBody['errors']}'); + } - final List transactions = []; - for (var eventJson in events) { - final event = eventJson as Map; + final events = responseBody['data']?['events'] as List?; + if (events == null || events.isEmpty) return []; - if (event['transfer'] != null) { - final transferData = event['transfer'] as Map; - transferData['extrinsicHash'] ??= event['extrinsicHash']; - transactions.add(TransferEvent.fromJson(transferData)); - } else if (event['reversibleTransfer'] != null) { - final reversibleTransferData = event['reversibleTransfer'] as Map; - reversibleTransferData['extrinsicHash'] ??= event['extrinsicHash']; - transactions.add(ReversibleTransferEvent.fromJson(reversibleTransferData)); - } else if (event['minerReward'] != null) { - final minerRewardData = event['minerReward'] as Map; - minerRewardData['extrinsicHash'] ??= event['extrinsicHash']; - transactions.add(MinerRewardEvent.fromJson(minerRewardData)); - } - } + return events.map((e) => parser(e as Map)).whereType().toList(); + } - final bool hasMore = events.length == limit; - final int nextOffset = offset + events.length; + Future _fetchOtherTransfers({ + required List accountIds, + int limit = 10, + int offset = 0, + String? printName, + }) async { + print('${printName ?? ''} fetchTransfers for account: $accountIds (limit: $limit, offset: $offset)'); - return TransferList(transfers: transactions, hasMore: hasMore, nextOffset: nextOffset); + final sw = Stopwatch()..start(); + try { + final results = await Future.wait([ + _fetchSingleType( + query: _transfersByAccountsQuery, + label: 'transfers', + accountIds: accountIds, + limit: limit, + offset: offset, + parser: (event) { + if (event['transfer'] == null) return null; + final d = event['transfer'] as Map; + d['extrinsicHash'] ??= event['extrinsicHash']; + return TransferEvent.fromJson(d); + }, + ), + _fetchSingleType( + query: _reversibleByAccountsQuery, + label: 'reversible', + accountIds: accountIds, + limit: limit, + offset: offset, + parser: (event) { + if (event['reversibleTransfer'] == null) return null; + final d = event['reversibleTransfer'] as Map; + d['extrinsicHash'] ??= event['extrinsicHash']; + return ReversibleTransferEvent.fromJson(d); + }, + ), + _fetchSingleType( + query: _rewardsByAccountsQuery, + label: 'rewards', + accountIds: accountIds, + limit: limit, + offset: offset, + parser: (event) { + if (event['minerReward'] == null) return null; + final d = event['minerReward'] as Map; + d['extrinsicHash'] ??= event['extrinsicHash']; + return MinerRewardEvent.fromJson(d); + }, + ), + ]); + + final all = [...results[0], ...results[1], ...results[2]]; + all.sort((a, b) => b.timestamp.compareTo(a.timestamp)); + final trimmed = all.take(limit).toList(); + final totalFetched = results[0].length + results[1].length + results[2].length; + + sw.stop(); + print( + '[TIMING] _fetchOtherTransfers TOTAL: ${sw.elapsedMilliseconds}ms ' + '(${results[0].length} transfers, ${results[1].length} reversible, ${results[2].length} rewards)', + ); + + return TransferList(transfers: trimmed, hasMore: totalFetched >= limit, nextOffset: offset + trimmed.length); } catch (e, stackTrace) { + sw.stop(); + print('[TIMING] _fetchOtherTransfers FAILED after ${sw.elapsedMilliseconds}ms'); print('Error fetching transfers: $e'); print(stackTrace); rethrow; From 1f33fdc3cc9cf31d5b2163a8f89df6049e6d3c83 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 20:30:20 +0800 Subject: [PATCH 08/14] cleanup - debug timers under feature flag, off by default --- .../lib/src/constants/app_constants.dart | 3 ++ .../src/services/chain_history_service.dart | 32 ++++++++----------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/quantus_sdk/lib/src/constants/app_constants.dart b/quantus_sdk/lib/src/constants/app_constants.dart index b0bf8831..5870ad19 100644 --- a/quantus_sdk/lib/src/constants/app_constants.dart +++ b/quantus_sdk/lib/src/constants/app_constants.dart @@ -61,6 +61,9 @@ class AppConstants { // hardware wallet flow without using a hardware wallet. static const debugHardwareWallet = false; + // Debug the timing of subsquid queries + static const bool debugQueryTiming = false; + static const String accountSettingsRouteName = 'account-settings'; static const int highSecurityStepsCount = 3; } diff --git a/quantus_sdk/lib/src/services/chain_history_service.dart b/quantus_sdk/lib/src/services/chain_history_service.dart index 4dd35ac8..7b3833ae 100644 --- a/quantus_sdk/lib/src/services/chain_history_service.dart +++ b/quantus_sdk/lib/src/services/chain_history_service.dart @@ -343,6 +343,12 @@ query SearchPendingTransaction( } '''; + void printTiming(String label, int milliseconds) { + if (AppConstants.debugQueryTiming) { + print('[TIMING] $label: $milliseconds ms'); + } + } + Future fetchAllTransactionTypes({ required List accountIds, int limit = 20, @@ -355,7 +361,7 @@ query SearchPendingTransaction( () async { final sw = Stopwatch()..start(); final r = await fetchScheduledTransfers(accountIds: accountIds); - print('[TIMING] fetchScheduledTransfers: ${sw.elapsedMilliseconds}ms'); + printTiming('fetchScheduledTransfers', sw.elapsedMilliseconds); return r; }(), () async { @@ -366,13 +372,13 @@ query SearchPendingTransaction( offset: offset, printName: printName, ); - print('[TIMING] _fetchOtherTransfers: ${sw.elapsedMilliseconds}ms'); + printTiming('_fetchOtherTransfers', sw.elapsedMilliseconds); return r; }(), ]); totalSw.stop(); - print('[TIMING] fetchAllTransactionTypes TOTAL: ${totalSw.elapsedMilliseconds}ms'); + printTiming('fetchAllTransactionTypes', totalSw.elapsedMilliseconds); final scheduled = results[0] as List; final other = results[1] as TransferList; @@ -390,10 +396,6 @@ query SearchPendingTransaction( return []; } - // print( - // 'Fetching transactions by hash: $transactionHashes (limit: $limit, offset: $offset)', - // ); - final Map requestBody = { 'query': _transactionsByHashQuery, 'variables': {'transactionHashes': transactionHashes, 'limit': limit, 'offset': offset}, @@ -444,9 +446,6 @@ query SearchPendingTransaction( } } - // print( - // 'Found ${transactions.length} transactions for ${transactionHashes.length} hashes', - // ); for (final t in transactions) { print('${t.id} ${t.extrinsicHash} ${(t as ReversibleTransferEvent).status}'); } @@ -474,7 +473,7 @@ query SearchPendingTransaction( try { final http.Response response = await _graphQlEndpointService.post(body: jsonBody); sw.stop(); - print('[TIMING] fetchScheduledTransfers HTTP: ${sw.elapsedMilliseconds}ms (status: ${response.statusCode})'); + printTiming('fetchScheduledTransfers HTTP', sw.elapsedMilliseconds); if (response.statusCode != 200) { throw Exception('GraphQL request failed with status: ${response.statusCode}. Body: ${response.body}'); @@ -495,7 +494,7 @@ query SearchPendingTransaction( return result; } catch (e, stackTrace) { sw.stop(); - print('[TIMING] fetchScheduledTransfers FAILED after ${sw.elapsedMilliseconds}ms'); + printTiming('fetchScheduledTransfers FAILED', sw.elapsedMilliseconds); print('Error fetching scheduled transfers: $e'); print(stackTrace); rethrow; @@ -517,7 +516,7 @@ query SearchPendingTransaction( final sw = Stopwatch()..start(); final http.Response response = await _graphQlEndpointService.post(body: body); - print('[TIMING] $label HTTP: ${sw.elapsedMilliseconds}ms'); + printTiming('$label HTTP', sw.elapsedMilliseconds); if (response.statusCode != 200) { throw Exception('GraphQL $label failed: ${response.statusCode}. Body: ${response.body}'); @@ -592,15 +591,12 @@ query SearchPendingTransaction( final totalFetched = results[0].length + results[1].length + results[2].length; sw.stop(); - print( - '[TIMING] _fetchOtherTransfers TOTAL: ${sw.elapsedMilliseconds}ms ' - '(${results[0].length} transfers, ${results[1].length} reversible, ${results[2].length} rewards)', - ); + printTiming('_fetchOtherTransfers TOTAL', sw.elapsedMilliseconds); return TransferList(transfers: trimmed, hasMore: totalFetched >= limit, nextOffset: offset + trimmed.length); } catch (e, stackTrace) { sw.stop(); - print('[TIMING] _fetchOtherTransfers FAILED after ${sw.elapsedMilliseconds}ms'); + printTiming('_fetchOtherTransfers FAILED', sw.elapsedMilliseconds); print('Error fetching transfers: $e'); print(stackTrace); rethrow; From 8048019d2cea860efdf2212158943047868c9820 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 20 Feb 2026 21:01:13 +0800 Subject: [PATCH 09/14] remove unused code --- .../src/services/chain_history_service.dart | 149 ------------------ 1 file changed, 149 deletions(-) diff --git a/quantus_sdk/lib/src/services/chain_history_service.dart b/quantus_sdk/lib/src/services/chain_history_service.dart index 7b3833ae..0451f451 100644 --- a/quantus_sdk/lib/src/services/chain_history_service.dart +++ b/quantus_sdk/lib/src/services/chain_history_service.dart @@ -169,84 +169,6 @@ query RewardsByAccounts($accounts: [String!]!, $limit: Int!, $offset: Int!) { } }'''; - final String _reversibleTransactionsInBlockQuery = r''' -query TransactionsAndBlockInBlock( - $blockHash: String!, - $from: String!, - $to: String! -) { - blocks(where: { hash_eq: $blockHash }, limit: 1) { - id - } - events( - limit: 500 - offset: 0 - where: { - block: { hash_eq: $blockHash }, - reversibleTransfer: { - from: { id_eq: $from }, - to: { id_eq: $to } - } - } - orderBy: timestamp_DESC - ) { - id - reversibleTransfer { - id - amount - timestamp - from { id } - to { id } - txId - scheduledAt - status - block { height hash } - extrinsicHash - timestamp - } - extrinsicHash - } -} -'''; - - final String _transferInBlockQuery = r''' -query TransactionsAndBlockInBlock( - $blockHash: String!, - $from: String!, - $to: String! -) { - blocks(where: { hash_eq: $blockHash }, limit: 1) { - id - } - events( - limit: 500 - offset: 0 - where: { - block: { hash_eq: $blockHash }, - transfer: { - from: { id_eq: $from }, - to: { id_eq: $to } - } - } - orderBy: timestamp_DESC - ) { - id - transfer { - id - amount - timestamp - from { id } - to { id } - block { height hash } - extrinsicHash - timestamp - fee - } - extrinsicHash - } -} -'''; - // GraphQL query to search for transactions matching pending transaction criteria final String _searchPendingTransferQuery = r''' query SearchPendingTransaction( @@ -603,77 +525,6 @@ query SearchPendingTransaction( } } - // Add other methods for fetching historical data as needed - - Future getTransactionsInBlock({ - required String blockHash, - required String from, - required String to, - required bool isReversible, - }) async { - print('Fetching transactions in block: $blockHash'); - - final Map requestBody = { - 'query': isReversible ? _reversibleTransactionsInBlockQuery : _transferInBlockQuery, - 'variables': {'blockHash': blockHash, 'from': from, 'to': to}, - }; - - try { - final http.Response response = await _graphQlEndpointService.post(body: jsonEncode(requestBody)); - - if (response.statusCode != 200) { - throw Exception('GraphQL request failed with status: ${response.statusCode}. Body: ${response.body}'); - } - - final Map responseBody = jsonDecode(response.body); - - if (responseBody['errors'] != null) { - print('GraphQL errors in response: ${responseBody['errors']}'); - throw Exception('GraphQL errors: ${responseBody['errors'].toString()}'); - } - - final Map? data = responseBody['data']; - if (data == null) { - throw Exception('GraphQL response data is null.'); - } - - final List? blocks = data['blocks']; - final bool blockExists = blocks != null && blocks.isNotEmpty; - - final List? events = data['events']; - - if (events == null || events.isEmpty) { - return BlockQueryResponse(blockExists: blockExists, transactions: []); - } - - final List transactions = []; - for (var eventJson in events) { - final event = eventJson as Map; - - if (event['transfer'] != null) { - final transferData = event['transfer'] as Map; - transferData['extrinsicHash'] ??= event['extrinsicHash']; - transactions.add(TransferEvent.fromJson(transferData)); - } else if (event['reversibleTransfer'] != null) { - final reversibleTransferData = event['reversibleTransfer'] as Map; - reversibleTransferData['extrinsicHash'] ??= event['extrinsicHash']; - transactions.add(ReversibleTransferEvent.fromJson(reversibleTransferData)); - } else if (event['minerReward'] != null) { - final minerRewardData = event['minerReward'] as Map; - minerRewardData['extrinsicHash'] ??= event['extrinsicHash']; - transactions.add(MinerRewardEvent.fromJson(minerRewardData)); - } - } - - print('Found ${transactions.length} transactions in block $blockHash'); - return BlockQueryResponse(blockExists: blockExists, transactions: transactions); - } catch (e, stackTrace) { - print('Error fetching transactions by block hash: $e'); - print(stackTrace); - rethrow; - } - } - /// Searches for transactions matching the criteria of a pending transaction. /// This is used to find if a broadcast transaction has been confirmed. /// Searches both transfer and reversibleTransfer types. From 4c065754063d176c7e225750827b3a45b4e772c4 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Sat, 21 Feb 2026 11:27:34 +0800 Subject: [PATCH 10/14] each request has its own offset --- mobile-app/lib/models/pagination_state.dart | 18 +++- .../unified_pagination_controller.dart | 49 ++++----- .../lib/src/models/sorted_transactions.dart | 13 ++- .../src/services/chain_history_service.dart | 102 +++++++++--------- 4 files changed, 98 insertions(+), 84 deletions(-) diff --git a/mobile-app/lib/models/pagination_state.dart b/mobile-app/lib/models/pagination_state.dart index 068263f2..d12419c5 100644 --- a/mobile-app/lib/models/pagination_state.dart +++ b/mobile-app/lib/models/pagination_state.dart @@ -4,7 +4,9 @@ import 'package:quantus_sdk/quantus_sdk.dart'; class PaginationState { final List items; final List reversibleTransfers; - final int offset; + final int transfersOffset; + final int reversibleOffset; + final int rewardsOffset; final bool hasMore; final bool isFetching; final Object? error; @@ -13,7 +15,9 @@ class PaginationState { PaginationState({ required this.items, required this.reversibleTransfers, - required this.offset, + this.transfersOffset = 0, + this.reversibleOffset = 0, + this.rewardsOffset = 0, required this.hasMore, required this.isFetching, this.error, @@ -21,12 +25,14 @@ class PaginationState { }); factory PaginationState.initial() => - PaginationState(items: [], reversibleTransfers: [], offset: 0, hasMore: true, isFetching: false); + PaginationState(items: [], reversibleTransfers: [], hasMore: true, isFetching: false); PaginationState copyWith({ List? items, List? reversibleTransfers, - int? offset, + int? transfersOffset, + int? reversibleOffset, + int? rewardsOffset, bool? hasMore, bool? isFetching, Object? error, @@ -35,7 +41,9 @@ class PaginationState { return PaginationState( items: items ?? this.items, reversibleTransfers: reversibleTransfers ?? this.reversibleTransfers, - offset: offset ?? this.offset, + transfersOffset: transfersOffset ?? this.transfersOffset, + reversibleOffset: reversibleOffset ?? this.reversibleOffset, + rewardsOffset: rewardsOffset ?? this.rewardsOffset, hasMore: hasMore ?? this.hasMore, isFetching: isFetching ?? this.isFetching, error: error ?? this.error, diff --git a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart index 4b97ab69..a7d00042 100644 --- a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart +++ b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart @@ -69,37 +69,34 @@ class UnifiedPaginationController extends StateNotifier { } Future _fetchPage(List targetAccountIds) async { - final sw = Stopwatch()..start(); try { - print( - 'UnifiedPaginationController: Fetching page for accounts:' - ' $targetAccountIds, offset: ${state.offset}', - ); state = state.copyWith(isFetching: true); - final newTransactions = await ref - .read(chainHistoryServiceProvider) - .fetchAllTransactionTypes(accountIds: targetAccountIds, limit: _limit, offset: state.offset); + final newTransactions = await ref.read(chainHistoryServiceProvider).fetchAllTransactionTypes( + accountIds: targetAccountIds, + limit: _limit, + transfersOffset: state.transfersOffset, + reversibleOffset: state.reversibleOffset, + rewardsOffset: state.rewardsOffset, + ); - sw.stop(); final newItems = newTransactions.otherTransfers; - print( - 'UnifiedPaginationController: Fetched ${newItems.length} ' - 'transactions, ${newTransactions.reversibleTransfers.length} ' - 'reversible', - ); - print('[TIMING] _fetchPage TOTAL: ${sw.elapsedMilliseconds}ms'); + final isFirstPage = state.transfersOffset == 0 && + state.reversibleOffset == 0 && + state.rewardsOffset == 0; + state = state.copyWith( items: [...state.items, ...newItems], - reversibleTransfers: state.offset == 0 ? newTransactions.reversibleTransfers : state.reversibleTransfers, - offset: state.offset + newItems.length, - hasMore: newItems.length == _limit, + reversibleTransfers: isFirstPage ? newTransactions.reversibleTransfers : state.reversibleTransfers, + transfersOffset: newTransactions.nextTransfersOffset, + reversibleOffset: newTransactions.nextReversibleOffset, + rewardsOffset: newTransactions.nextRewardsOffset, + hasMore: newTransactions.hasMore, isFetching: false, error: null, stackTrace: null, ); } catch (e, st) { - sw.stop(); - print('Fetch page failed after ${sw.elapsedMilliseconds}ms: $e\n$st'); + print('Fetch page failed: $e\n$st'); state = state.copyWith(error: e, stackTrace: st, isFetching: false); } } @@ -152,25 +149,23 @@ class UnifiedPaginationController extends StateNotifier { Future _silentFetchFirstPage(List targetAccountIds) async { try { - // Fetch without setting isFetching to avoid loading indicators final newTransactions = await ref .read(chainHistoryServiceProvider) - .fetchAllTransactionTypes(accountIds: targetAccountIds, limit: _limit, offset: 0); + .fetchAllTransactionTypes(accountIds: targetAccountIds, limit: _limit); final newItems = newTransactions.otherTransfers; - // Replace existing items with fresh data state = state.copyWith( items: newItems, reversibleTransfers: newTransactions.reversibleTransfers, - offset: newItems.length, - hasMore: newItems.length == _limit, + transfersOffset: newTransactions.nextTransfersOffset, + reversibleOffset: newTransactions.nextReversibleOffset, + rewardsOffset: newTransactions.nextRewardsOffset, + hasMore: newTransactions.hasMore, error: null, stackTrace: null, ); } catch (e, st) { - // Silently handle errors - don't update UI state for automatic polling - // failures print('Silent refresh failed: $e, $st'); } } diff --git a/quantus_sdk/lib/src/models/sorted_transactions.dart b/quantus_sdk/lib/src/models/sorted_transactions.dart index 397c00a0..d7abf4a8 100644 --- a/quantus_sdk/lib/src/models/sorted_transactions.dart +++ b/quantus_sdk/lib/src/models/sorted_transactions.dart @@ -4,8 +4,19 @@ import 'package:quantus_sdk/src/models/transaction_event.dart'; class SortedTransactionsList { final List reversibleTransfers; final List otherTransfers; + final int nextTransfersOffset; + final int nextReversibleOffset; + final int nextRewardsOffset; + final bool hasMore; - const SortedTransactionsList({required this.reversibleTransfers, required this.otherTransfers}); + const SortedTransactionsList({ + required this.reversibleTransfers, + required this.otherTransfers, + this.nextTransfersOffset = 0, + this.nextReversibleOffset = 0, + this.nextRewardsOffset = 0, + this.hasMore = false, + }); static const SortedTransactionsList empty = SortedTransactionsList(reversibleTransfers: [], otherTransfers: []); diff --git a/quantus_sdk/lib/src/services/chain_history_service.dart b/quantus_sdk/lib/src/services/chain_history_service.dart index 0451f451..49074ac0 100644 --- a/quantus_sdk/lib/src/services/chain_history_service.dart +++ b/quantus_sdk/lib/src/services/chain_history_service.dart @@ -6,17 +6,17 @@ import 'package:quantus_sdk/quantus_sdk.dart'; class TransferList { final List transfers; final bool hasMore; - final int nextOffset; - - TransferList({required this.transfers, required this.hasMore, required this.nextOffset}); -} - -class TransferResult { - final List combinedTransfers; - final bool hasMore; - final int nextOffset; - - TransferResult({required this.combinedTransfers, required this.hasMore, required this.nextOffset}); + final int nextTransfersOffset; + final int nextReversibleOffset; + final int nextRewardsOffset; + + TransferList({ + required this.transfers, + required this.hasMore, + required this.nextTransfersOffset, + required this.nextReversibleOffset, + required this.nextRewardsOffset, + }); } class BlockQueryResponse { @@ -274,37 +274,31 @@ query SearchPendingTransaction( Future fetchAllTransactionTypes({ required List accountIds, int limit = 20, - int offset = 0, - String? printName, + int transfersOffset = 0, + int reversibleOffset = 0, + int rewardsOffset = 0, }) async { - final totalSw = Stopwatch()..start(); - final results = await Future.wait([ - () async { - final sw = Stopwatch()..start(); - final r = await fetchScheduledTransfers(accountIds: accountIds); - printTiming('fetchScheduledTransfers', sw.elapsedMilliseconds); - return r; - }(), - () async { - final sw = Stopwatch()..start(); - final r = await _fetchOtherTransfers( - accountIds: accountIds, - limit: limit, - offset: offset, - printName: printName, - ); - printTiming('_fetchOtherTransfers', sw.elapsedMilliseconds); - return r; - }(), + fetchScheduledTransfers(accountIds: accountIds), + _fetchOtherTransfers( + accountIds: accountIds, + limit: limit, + transfersOffset: transfersOffset, + reversibleOffset: reversibleOffset, + rewardsOffset: rewardsOffset, + ), ]); - totalSw.stop(); - printTiming('fetchAllTransactionTypes', totalSw.elapsedMilliseconds); - final scheduled = results[0] as List; final other = results[1] as TransferList; - return SortedTransactionsList(reversibleTransfers: scheduled, otherTransfers: other.transfers); + return SortedTransactionsList( + reversibleTransfers: scheduled, + otherTransfers: other.transfers, + nextTransfersOffset: other.nextTransfersOffset, + nextReversibleOffset: other.nextReversibleOffset, + nextRewardsOffset: other.nextRewardsOffset, + hasMore: other.hasMore, + ); } // Make a graphQL query for specific transaction hashes, get the results back @@ -458,12 +452,10 @@ query SearchPendingTransaction( Future _fetchOtherTransfers({ required List accountIds, int limit = 10, - int offset = 0, - String? printName, + int transfersOffset = 0, + int reversibleOffset = 0, + int rewardsOffset = 0, }) async { - print('${printName ?? ''} fetchTransfers for account: $accountIds (limit: $limit, offset: $offset)'); - - final sw = Stopwatch()..start(); try { final results = await Future.wait([ _fetchSingleType( @@ -471,7 +463,7 @@ query SearchPendingTransaction( label: 'transfers', accountIds: accountIds, limit: limit, - offset: offset, + offset: transfersOffset, parser: (event) { if (event['transfer'] == null) return null; final d = event['transfer'] as Map; @@ -484,7 +476,7 @@ query SearchPendingTransaction( label: 'reversible', accountIds: accountIds, limit: limit, - offset: offset, + offset: reversibleOffset, parser: (event) { if (event['reversibleTransfer'] == null) return null; final d = event['reversibleTransfer'] as Map; @@ -497,7 +489,7 @@ query SearchPendingTransaction( label: 'rewards', accountIds: accountIds, limit: limit, - offset: offset, + offset: rewardsOffset, parser: (event) { if (event['minerReward'] == null) return null; final d = event['minerReward'] as Map; @@ -510,15 +502,23 @@ query SearchPendingTransaction( final all = [...results[0], ...results[1], ...results[2]]; all.sort((a, b) => b.timestamp.compareTo(a.timestamp)); final trimmed = all.take(limit).toList(); - final totalFetched = results[0].length + results[1].length + results[2].length; - - sw.stop(); - printTiming('_fetchOtherTransfers TOTAL', sw.elapsedMilliseconds); - return TransferList(transfers: trimmed, hasMore: totalFetched >= limit, nextOffset: offset + trimmed.length); + final usedTransfers = trimmed.whereType().length; + final usedReversible = trimmed.whereType().length; + final usedRewards = trimmed.whereType().length; + + final anyHasMore = results[0].length == limit || + results[1].length == limit || + results[2].length == limit; + + return TransferList( + transfers: trimmed, + hasMore: anyHasMore, + nextTransfersOffset: transfersOffset + usedTransfers, + nextReversibleOffset: reversibleOffset + usedReversible, + nextRewardsOffset: rewardsOffset + usedRewards, + ); } catch (e, stackTrace) { - sw.stop(); - printTiming('_fetchOtherTransfers FAILED', sw.elapsedMilliseconds); print('Error fetching transfers: $e'); print(stackTrace); rethrow; From fc25ddee2f0b7955bca62d1beed4cee1a647070f Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Sat, 21 Feb 2026 11:28:22 +0800 Subject: [PATCH 11/14] format --- .../unified_pagination_controller.dart | 20 +++++++++---------- .../src/services/chain_history_service.dart | 4 +--- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart index a7d00042..ebcf25cd 100644 --- a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart +++ b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart @@ -71,18 +71,18 @@ class UnifiedPaginationController extends StateNotifier { Future _fetchPage(List targetAccountIds) async { try { state = state.copyWith(isFetching: true); - final newTransactions = await ref.read(chainHistoryServiceProvider).fetchAllTransactionTypes( - accountIds: targetAccountIds, - limit: _limit, - transfersOffset: state.transfersOffset, - reversibleOffset: state.reversibleOffset, - rewardsOffset: state.rewardsOffset, - ); + final newTransactions = await ref + .read(chainHistoryServiceProvider) + .fetchAllTransactionTypes( + accountIds: targetAccountIds, + limit: _limit, + transfersOffset: state.transfersOffset, + reversibleOffset: state.reversibleOffset, + rewardsOffset: state.rewardsOffset, + ); final newItems = newTransactions.otherTransfers; - final isFirstPage = state.transfersOffset == 0 && - state.reversibleOffset == 0 && - state.rewardsOffset == 0; + final isFirstPage = state.transfersOffset == 0 && state.reversibleOffset == 0 && state.rewardsOffset == 0; state = state.copyWith( items: [...state.items, ...newItems], diff --git a/quantus_sdk/lib/src/services/chain_history_service.dart b/quantus_sdk/lib/src/services/chain_history_service.dart index 49074ac0..35e5dce4 100644 --- a/quantus_sdk/lib/src/services/chain_history_service.dart +++ b/quantus_sdk/lib/src/services/chain_history_service.dart @@ -507,9 +507,7 @@ query SearchPendingTransaction( final usedReversible = trimmed.whereType().length; final usedRewards = trimmed.whereType().length; - final anyHasMore = results[0].length == limit || - results[1].length == limit || - results[2].length == limit; + final anyHasMore = results[0].length == limit || results[1].length == limit || results[2].length == limit; return TransferList( transfers: trimmed, From d97400983d5c9dd0085cc7bf0fc1d5297aabb3d1 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Sat, 21 Feb 2026 13:22:25 +0800 Subject: [PATCH 12/14] fix scheduled offset --- mobile-app/lib/models/pagination_state.dart | 4 ++++ .../controllers/unified_pagination_controller.dart | 6 ++++-- quantus_sdk/lib/src/models/sorted_transactions.dart | 2 ++ quantus_sdk/lib/src/services/chain_history_service.dart | 4 +++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/mobile-app/lib/models/pagination_state.dart b/mobile-app/lib/models/pagination_state.dart index d12419c5..9ea8ab90 100644 --- a/mobile-app/lib/models/pagination_state.dart +++ b/mobile-app/lib/models/pagination_state.dart @@ -7,6 +7,7 @@ class PaginationState { final int transfersOffset; final int reversibleOffset; final int rewardsOffset; + final int scheduledOffset; final bool hasMore; final bool isFetching; final Object? error; @@ -18,6 +19,7 @@ class PaginationState { this.transfersOffset = 0, this.reversibleOffset = 0, this.rewardsOffset = 0, + this.scheduledOffset = 0, required this.hasMore, required this.isFetching, this.error, @@ -33,6 +35,7 @@ class PaginationState { int? transfersOffset, int? reversibleOffset, int? rewardsOffset, + int? scheduledOffset, bool? hasMore, bool? isFetching, Object? error, @@ -44,6 +47,7 @@ class PaginationState { transfersOffset: transfersOffset ?? this.transfersOffset, reversibleOffset: reversibleOffset ?? this.reversibleOffset, rewardsOffset: rewardsOffset ?? this.rewardsOffset, + scheduledOffset: scheduledOffset ?? this.scheduledOffset, hasMore: hasMore ?? this.hasMore, isFetching: isFetching ?? this.isFetching, error: error ?? this.error, diff --git a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart index ebcf25cd..3fdd0f76 100644 --- a/mobile-app/lib/providers/controllers/unified_pagination_controller.dart +++ b/mobile-app/lib/providers/controllers/unified_pagination_controller.dart @@ -79,17 +79,18 @@ class UnifiedPaginationController extends StateNotifier { transfersOffset: state.transfersOffset, reversibleOffset: state.reversibleOffset, rewardsOffset: state.rewardsOffset, + scheduledOffset: state.scheduledOffset, ); final newItems = newTransactions.otherTransfers; - final isFirstPage = state.transfersOffset == 0 && state.reversibleOffset == 0 && state.rewardsOffset == 0; state = state.copyWith( items: [...state.items, ...newItems], - reversibleTransfers: isFirstPage ? newTransactions.reversibleTransfers : state.reversibleTransfers, + reversibleTransfers: [...state.reversibleTransfers, ...newTransactions.reversibleTransfers], transfersOffset: newTransactions.nextTransfersOffset, reversibleOffset: newTransactions.nextReversibleOffset, rewardsOffset: newTransactions.nextRewardsOffset, + scheduledOffset: newTransactions.nextScheduledOffset, hasMore: newTransactions.hasMore, isFetching: false, error: null, @@ -161,6 +162,7 @@ class UnifiedPaginationController extends StateNotifier { transfersOffset: newTransactions.nextTransfersOffset, reversibleOffset: newTransactions.nextReversibleOffset, rewardsOffset: newTransactions.nextRewardsOffset, + scheduledOffset: newTransactions.nextScheduledOffset, hasMore: newTransactions.hasMore, error: null, stackTrace: null, diff --git a/quantus_sdk/lib/src/models/sorted_transactions.dart b/quantus_sdk/lib/src/models/sorted_transactions.dart index d7abf4a8..df4b2113 100644 --- a/quantus_sdk/lib/src/models/sorted_transactions.dart +++ b/quantus_sdk/lib/src/models/sorted_transactions.dart @@ -7,6 +7,7 @@ class SortedTransactionsList { final int nextTransfersOffset; final int nextReversibleOffset; final int nextRewardsOffset; + final int nextScheduledOffset; final bool hasMore; const SortedTransactionsList({ @@ -15,6 +16,7 @@ class SortedTransactionsList { this.nextTransfersOffset = 0, this.nextReversibleOffset = 0, this.nextRewardsOffset = 0, + this.nextScheduledOffset = 0, this.hasMore = false, }); diff --git a/quantus_sdk/lib/src/services/chain_history_service.dart b/quantus_sdk/lib/src/services/chain_history_service.dart index 35e5dce4..db1a1eac 100644 --- a/quantus_sdk/lib/src/services/chain_history_service.dart +++ b/quantus_sdk/lib/src/services/chain_history_service.dart @@ -277,9 +277,10 @@ query SearchPendingTransaction( int transfersOffset = 0, int reversibleOffset = 0, int rewardsOffset = 0, + int scheduledOffset = 0, }) async { final results = await Future.wait([ - fetchScheduledTransfers(accountIds: accountIds), + fetchScheduledTransfers(accountIds: accountIds, limit: limit, offset: scheduledOffset), _fetchOtherTransfers( accountIds: accountIds, limit: limit, @@ -297,6 +298,7 @@ query SearchPendingTransaction( nextTransfersOffset: other.nextTransfersOffset, nextReversibleOffset: other.nextReversibleOffset, nextRewardsOffset: other.nextRewardsOffset, + nextScheduledOffset: scheduledOffset + scheduled.length, hasMore: other.hasMore, ); } From b744a131fbdddd1b3ce8baad4ebbc3046f8769d5 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Sat, 21 Feb 2026 13:22:32 +0800 Subject: [PATCH 13/14] fix pending state --- .../activity/transaction_detail_sheet.dart | 12 ++++++++--- .../lib/v2/screens/activity/tx_item.dart | 20 +++++++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart b/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart index 3166dbff..51a5201f 100644 --- a/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart +++ b/mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart @@ -32,7 +32,10 @@ class _TransactionDetailSheetState extends State<_TransactionDetailSheet> { bool get _isSend => widget.tx.from == widget.activeAccountId; String get _counterparty => _isSend ? widget.tx.to : widget.tx.from; + bool get _isPending => widget.tx is PendingTransactionEvent; + String get _title { + if (_isPending) return 'Sending'; if (widget.tx.isReversibleScheduled) return _isSend ? 'Pending' : 'Receiving'; return _isSend ? 'Sent' : 'Received'; } @@ -177,8 +180,11 @@ class _TransactionDetailSheetState extends State<_TransactionDetailSheet> { } Widget _explorerButton(AppColorsV2 colors, AppTextTheme text) { + final isPending = widget.tx is PendingTransactionEvent; + final color = isPending ? colors.textPrimary.withValues(alpha: 0.3) : colors.textPrimary; + return GestureDetector( - onTap: _openExplorer, + onTap: isPending ? null : _openExplorer, child: GlassContainer( asset: GlassContainer.wideAsset, filled: false, @@ -187,10 +193,10 @@ class _TransactionDetailSheetState extends State<_TransactionDetailSheet> { children: [ Text( 'View in Explorer', - style: text.paragraph?.copyWith(color: colors.textPrimary, fontWeight: FontWeight.w500), + style: text.paragraph?.copyWith(color: color, fontWeight: FontWeight.w500), ), const SizedBox(width: 8), - Icon(Icons.open_in_new, size: 16, color: colors.textPrimary), + Icon(Icons.open_in_new, size: 16, color: color), ], ), ), diff --git a/mobile-app/lib/v2/screens/activity/tx_item.dart b/mobile-app/lib/v2/screens/activity/tx_item.dart index 4bf1ed79..725637f3 100644 --- a/mobile-app/lib/v2/screens/activity/tx_item.dart +++ b/mobile-app/lib/v2/screens/activity/tx_item.dart @@ -25,24 +25,32 @@ class TxItemData { factory TxItemData.from(TransactionEvent tx, String accountId) { final isSend = tx.from == accountId; + final isPending = tx is PendingTransactionEvent; final isScheduled = tx.isReversibleScheduled; + final isHighlighted = isPending || isScheduled; final fmt = NumberFormattingService(); return TxItemData( - label: isScheduled + label: isPending + ? 'Sending' + : isScheduled ? (isSend ? 'Pending' : 'Receiving') : isSend ? 'Sent' : 'Received', - timeLabel: isScheduled ? _formatDuration(tx.timeRemaining) : _timeAgo(tx.timestamp), - iconBg: isScheduled && !isSend + timeLabel: isPending + ? 'now' + : isScheduled + ? _formatDuration(tx.timeRemaining) + : _timeAgo(tx.timestamp), + iconBg: isHighlighted && !isSend ? const Color(0x2927F027) - : isScheduled && isSend + : isHighlighted && isSend ? const Color(0x29FFBC42) : const Color(0xFF292929), - iconColor: isScheduled && !isSend + iconColor: isHighlighted && !isSend ? const Color(0xFF27F027) - : isScheduled && isSend + : isHighlighted && isSend ? const Color(0xFFFFBC42) : const Color(0x80FFFFFF), isSend: isSend, From 330d4d8808d58c7f1258737a21c3d98a6b0fd2bf Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Sat, 21 Feb 2026 13:22:41 +0800 Subject: [PATCH 14/14] fix home screen refresh --- mobile-app/lib/v2/screens/home/home_screen.dart | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/mobile-app/lib/v2/screens/home/home_screen.dart b/mobile-app/lib/v2/screens/home/home_screen.dart index 0e02f62d..c623aff1 100644 --- a/mobile-app/lib/v2/screens/home/home_screen.dart +++ b/mobile-app/lib/v2/screens/home/home_screen.dart @@ -35,18 +35,17 @@ class _HomeScreenState extends ConsumerState { static const _actionButtonBgAsset = 'assets/v2/glass_104_x_80.png'; final NumberFormattingService _fmt = NumberFormattingService(); - Stopwatch? _txLoadingStopwatch; Future _refresh() async { final active = ref.read(activeAccountProvider).value; + ref.invalidate(balanceProviderFamily); + ref.invalidate(balanceProviderRaw); + ref.invalidate(activeAccountTransactionsProvider); if (active != null) { - ref.invalidate(balanceProviderFamily); await ref .read(filteredPaginationControllerProviderFamily(AccountIdListCache.get([active.account.accountId])).notifier) .loadingRefresh(); } - ref.invalidate(balanceProviderRaw); - ref.invalidate(activeAccountTransactionsProvider); } void _processIntentIfAvailable() { @@ -72,14 +71,6 @@ class _HomeScreenState extends ConsumerState { final accountAsync = ref.watch(activeAccountProvider); final balanceAsync = ref.watch(balanceProvider); final txAsync = ref.watch(activeAccountTransactionsProvider); - if (txAsync.isLoading && _txLoadingStopwatch == null) { - _txLoadingStopwatch = Stopwatch()..start(); - print('[UI-TIMING] Transaction loading spinner STARTED'); - } else if (!txAsync.isLoading && _txLoadingStopwatch != null) { - _txLoadingStopwatch!.stop(); - print('[UI-TIMING] Transaction loading spinner ENDED after ${_txLoadingStopwatch!.elapsedMilliseconds}ms'); - _txLoadingStopwatch = null; - } final colors = context.colors; final text = context.themeText;