From 5c9ad0f17d04b67a4ebedf382bad16ff3e00dedd Mon Sep 17 00:00:00 2001 From: PuPha Date: Fri, 15 Nov 2024 11:26:10 +0700 Subject: [PATCH 1/2] Update retry login on foreground and open setting --- lib/screen/onboarding_page.dart | 39 +++++++++++++++++-- lib/service/navigation_service.dart | 16 ++++++-- pubspec.lock | 58 ++++++++++++++++++----------- pubspec.yaml | 1 + 4 files changed, 86 insertions(+), 28 deletions(-) diff --git a/lib/screen/onboarding_page.dart b/lib/screen/onboarding_page.dart index ff3f6464d..5a3a0315c 100644 --- a/lib/screen/onboarding_page.dart +++ b/lib/screen/onboarding_page.dart @@ -34,6 +34,7 @@ import 'package:autonomy_flutter/view/user_agent_utils.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:feralfile_app_theme/feral_file_app_theme.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_fgbg/flutter_fgbg.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sentry/sentry.dart'; @@ -55,6 +56,8 @@ class _OnboardingPageState extends State final _passkeyService = injector.get(); final _userAccountChannel = injector.get(); + bool? _isLoginSuccess; + late StreamSubscription _fgbgSubscription; final _onboardingLogo = Semantics( label: 'onboarding_logo', @@ -65,6 +68,31 @@ class _OnboardingPageState extends State ), ); + @override + void initState() { + super.initState(); + log.info('OnboardingPage initState'); + // on foreground listener + _fgbgSubscription = FGBGEvents.stream.listen(_handleForeBackground); + } + + void _handleForeBackground(FGBGType event) { + if (event == FGBGType.foreground) { + if (didRunSetup && _isLoginSuccess == false) { + // if setup is done and login is failed, try to login again + unawaited(_fetchRuntimeCache()); + } + } else { + log.info('App is in background'); + } + } + + @override + void dispose() { + unawaited(_fgbgSubscription.cancel()); + super.dispose(); + } + @override void afterFirstLayout(BuildContext context) { _timer = Timer(const Duration(seconds: 10), () { @@ -139,6 +167,7 @@ class _OnboardingPageState extends State Future _fetchRuntimeCache() async { log.info('[_fetchRuntimeCache] start'); final isSuccess = await _loginProcess(); + _isLoginSuccess = isSuccess; if (!isSuccess) { log.info('Login process failed'); unawaited(Sentry.captureMessage('Login process failed')); @@ -171,15 +200,17 @@ class _OnboardingPageState extends State if (!doesOSSupport) { log.info('OS does not support passkey'); _passkeyService.isShowingLoginDialog.value = true; - await _showBackupRecoveryPhraseDialog(); - _passkeyService.isShowingLoginDialog.value = false; + unawaited(_showBackupRecoveryPhraseDialog().then((_) { + _passkeyService.isShowingLoginDialog.value = false; + })); return false; } if (!canAuthenticate) { log.info('OS supports passkey but cannot authenticate'); _passkeyService.isShowingLoginDialog.value = true; - await _showAuthenticationUpdateRequired(); - _passkeyService.isShowingLoginDialog.value = false; + unawaited(_showAuthenticationUpdateRequired().then((_) { + _passkeyService.isShowingLoginDialog.value = false; + })); return false; } return false; diff --git a/lib/service/navigation_service.dart b/lib/service/navigation_service.dart index f456b573d..8a777d4a8 100644 --- a/lib/service/navigation_service.dart +++ b/lib/service/navigation_service.dart @@ -54,8 +54,8 @@ import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:libauk_dart/libauk_dart.dart'; import 'package:nft_collection/database/nft_collection_database.dart'; import 'package:nft_collection/models/asset_token.dart'; // ignore_for_file: implementation_imports +import 'package:open_settings_plus/open_settings_plus.dart'; import 'package:overlay_support/src/overlay_state_finder.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:sentry/sentry.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -180,6 +180,16 @@ class NavigationService { } } + Future openAuthenticationSettings() async { + if (Platform.isAndroid) { + final settings = OpenSettingsPlus.shared! as OpenSettingsPlusAndroid; + await settings.biometricEnroll(); + } else { + final settings = OpenSettingsPlus.shared! as OpenSettingsPlusIOS; + await settings.faceIDAndPasscode(); + } + } + Future showAppLoadError() async { if (navigatorKey.currentState?.mounted == true && navigatorKey.currentContext != null) { @@ -1186,8 +1196,8 @@ class NavigationService { children: [ PrimaryButton( text: 'go_to_settings'.tr(), - onTap: () async { - await openAppSettings(); + onTap: () { + openAuthenticationSettings(); }, ), const SizedBox(height: 20), diff --git a/pubspec.lock b/pubspec.lock index 74e160924..3abbc1e29 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -365,10 +365,10 @@ packages: dependency: "direct main" description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.18.0" confetti: dependency: "direct main" description: @@ -1691,18 +1691,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -1864,6 +1864,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + mixpanel_flutter: + dependency: "direct main" + description: + name: mixpanel_flutter + sha256: c1e35c54d8cfc1c50d35d2e8cc11416295efc45bb6dcaa88aec8f82219274833 + url: "https://pub.dev" + source: hosted + version: "2.3.3" mockito: dependency: "direct dev" description: @@ -1969,6 +1977,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + open_settings_plus: + dependency: "direct main" + description: + name: open_settings_plus + sha256: "5f813700e62bc77db830e0225e09e77c6573c67784a4e7b00483f3a16341c081" + url: "https://pub.dev" + source: hosted + version: "0.3.3" openapi_generator_annotations: dependency: transitive description: @@ -2549,7 +2565,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" slidable_button: dependency: "direct main" description: @@ -2666,10 +2682,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.1" stream_channel: dependency: transitive description: @@ -2690,10 +2706,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" strings: dependency: transitive description: @@ -2746,26 +2762,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.25.8" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.4" tezart: dependency: "direct main" description: @@ -2995,10 +3011,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.2.5" wakelock_plus: dependency: "direct main" description: @@ -3067,10 +3083,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.3" webkit_inspection_protocol: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a90e2c6a9..5c43c8971 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -77,6 +77,7 @@ dependencies: mixpanel_flutter: ^2.0.0 onesignal_flutter: ^5.2.6 open_settings: ^2.0.2 + open_settings_plus: ^0.3.3 overlay_support: ^2.0.0 package_info_plus: ^8.0.3 page_transition: ^2.0.5 From a52f95f3930dfa9ac3fe261ad4e1c0597698fd18 Mon Sep 17 00:00:00 2001 From: PuPha Date: Fri, 15 Nov 2024 14:09:29 +0700 Subject: [PATCH 2/2] retry authenticate --- lib/screen/onboarding_page.dart | 41 ++++++++++++++++++++++---------- lib/service/passkey_service.dart | 5 +++- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/lib/screen/onboarding_page.dart b/lib/screen/onboarding_page.dart index 5a3a0315c..28f10493c 100644 --- a/lib/screen/onboarding_page.dart +++ b/lib/screen/onboarding_page.dart @@ -80,6 +80,7 @@ class _OnboardingPageState extends State if (event == FGBGType.foreground) { if (didRunSetup && _isLoginSuccess == false) { // if setup is done and login is failed, try to login again + injector().goBack(result: false); unawaited(_fetchRuntimeCache()); } } else { @@ -166,7 +167,12 @@ class _OnboardingPageState extends State Future _fetchRuntimeCache() async { log.info('[_fetchRuntimeCache] start'); - final isSuccess = await _loginProcess(); + bool isSuccess = false; + try { + isSuccess = await _loginProcess(); + } catch (e, s) { + log.info('Failed to login process: $e'); + } _isLoginSuccess = isSuccess; if (!isSuccess) { log.info('Login process failed'); @@ -249,18 +255,27 @@ class _OnboardingPageState extends State Future _loginAndMigrate() async { log.info('Login and migrate'); - await injector().migrateAccount(() async { - try { - log.info('[_loginAndMigrate] create JWT token'); - final localResponse = await _passkeyService.logInInitiate(); - await _passkeyService.logInFinalize(localResponse); - log.info('[_loginAndMigrate] create JWT token done'); - } catch (e, s) { - log.info('Failed to create login JWT: $e'); - unawaited(Sentry.captureException(e, stackTrace: s)); - rethrow; - } - }); + _isLoginSuccess = null; + try { + await injector().migrateAccount(() async { + try { + log.info('[_loginAndMigrate] create JWT token'); + final localResponse = await _passkeyService.logInInitiate(); + await _passkeyService.logInFinalize(localResponse); + log.info('[_loginAndMigrate] create JWT token done'); + } catch (e, s) { + log.info('Failed to create login JWT: $e'); + unawaited(Sentry.captureException(e, stackTrace: s)); + rethrow; + } + }); + _isLoginSuccess = true; + } catch (e, s) { + _isLoginSuccess = false; + log.info('Failed to migrate account: $e'); + unawaited(Sentry.captureException(e, stackTrace: s)); + rethrow; + } log.info('Login and migrate done'); } diff --git a/lib/service/passkey_service.dart b/lib/service/passkey_service.dart index 224145f53..044192a92 100644 --- a/lib/service/passkey_service.dart +++ b/lib/service/passkey_service.dart @@ -75,7 +75,10 @@ class PasskeyServiceImpl implements PasskeyService { @override Future canAuthenticate() async => - await _passkeyAuthenticator.canAuthenticate(); + Platform.isAndroid ? await _passkeyAuthenticator.canAuthenticate() : true; + + // passkey available always return true for iOS + // if no verification method is available, the user will be prompted to set up passcode or faceID @override Future logInInitiate() async {