diff --git a/app/lib/app.dart b/app/lib/app.dart index 3311f6de..d17020c2 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -13,6 +13,13 @@ class PharMeApp extends StatelessWidget { final _appRouter = AppRouter(); + Future _setDeepLinkSharePublishUrl(PlatformDeepLink deepLink) async { + final queryParameters = deepLink.uri.queryParameters; + MetaData.instance.deepLinkSharePublishUrl = + queryParameters['provider_url']; + await MetaData.save(); + } + @override Widget build(BuildContext context) { return ErrorHandler( @@ -23,16 +30,11 @@ class PharMeApp extends StatelessWidget { debugShowCheckedModeBanner: false, routerConfig: _appRouter.config( deepLinkBuilder: (deepLink) async { - final queryParameters = deepLink.uri.queryParameters; - final isDeepLinkShareRequest = deepLink.path == '' && - queryParameters.containsKey('provider_url'); - if (isDeepLinkShareRequest) { - MetaData.instance.deepLinkSharePublishUrl = - queryParameters['provider_url']; - await MetaData.save(); - if (_appRouter.currentPath != '/') { - return DeepLink.path(_appRouter.currentPath); - } + if (deepLink.path.startsWith('/open_file')) { + await _setDeepLinkSharePublishUrl(deepLink); + } + if (_appRouter.currentPath != '/') { + return DeepLink.path(_appRouter.currentPath); } // default route return getInitialRoute(); diff --git a/app/lib/common/models/metadata.dart b/app/lib/common/models/metadata.dart index 1e6d3034..d535c833 100644 --- a/app/lib/common/models/metadata.dart +++ b/app/lib/common/models/metadata.dart @@ -49,6 +49,9 @@ class MetaData { @HiveField(6) String? deepLinkSharePublishUrl; + + @HiveField(7) + bool? awaitingDeepLinkSharePublishUrl; } /// Initializes the user's metadata by registering all necessary adapters and diff --git a/app/lib/login/cubit.dart b/app/lib/login/cubit.dart index 4e64e667..7fd1bb7c 100644 --- a/app/lib/login/cubit.dart +++ b/app/lib/login/cubit.dart @@ -16,15 +16,15 @@ class LoginCubit extends Cubit { // genomic data from it's endpoint. Future signInAndLoadUserData(BuildContext context, Lab lab) async { emit(LoginState.loadingUserData( - lab.authLoadingMessage(), - cancelable: lab.cancelAuthInApp, + lab.preparationLoadingMessage(), + cancelable: lab.cancelPreparationInApp, )); try { - await lab.authenticate(); + await lab.prepareDataLoad(); } on LabProcessCanceled { revertToInitialState(); return; - } on LabAuthenticationError { + } on Exception { emit(LoginState.error( // ignore: use_build_context_synchronously context.l10n.err_could_not_retrieve_access_token, @@ -32,8 +32,9 @@ class LoginCubit extends Cubit { return; } - if (lab.authenticationWasCanceled) { - lab.authenticationWasCanceled = false; + if (lab.preparationWasCanceled) { + lab.preparationWasCanceled = false; + MetaData.instance.awaitingDeepLinkSharePublishUrl = false; return; } diff --git a/app/lib/login/models/deep_link_share_flow_lab.dart b/app/lib/login/models/deep_link_share_flow_lab.dart index 355e0f0f..6c5d8d51 100644 --- a/app/lib/login/models/deep_link_share_flow_lab.dart +++ b/app/lib/login/models/deep_link_share_flow_lab.dart @@ -13,10 +13,10 @@ class DeepLinkShareFlowLab extends Lab { @override // ignore: overridden_fields - bool cancelAuthInApp = true; + bool cancelPreparationInApp= true; @override - String? authLoadingMessage() => + String? preparationLoadingMessage() => 'Please open the $shareAppName and share your data with PharMe'; Future _waitForDeepLinkSharePublishUrl() async { @@ -28,13 +28,20 @@ class DeepLinkShareFlowLab extends Lab { } } + Future _setAwaitingDeepLinkSharePublishUrl(bool newValue) async { + MetaData.instance.awaitingDeepLinkSharePublishUrl = newValue; + await MetaData.save(); + } + @override - Future authenticate() async { + Future prepareDataLoad() async { + await _setAwaitingDeepLinkSharePublishUrl(true); await _waitForDeepLinkSharePublishUrl(); } @override Future<(List, List)> loadData() async { + await _setAwaitingDeepLinkSharePublishUrl(false); publishUrl = Uri.parse( MetaData.instance.deepLinkSharePublishUrl!, ); diff --git a/app/lib/login/models/lab.dart b/app/lib/login/models/lab.dart index e50e5849..002d8d80 100644 --- a/app/lib/login/models/lab.dart +++ b/app/lib/login/models/lab.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import '../../app.dart'; import '../../common/module.dart'; class LabProcessCanceled implements Exception { @@ -18,12 +19,14 @@ class Lab { }); String name; - bool cancelAuthInApp = false; - bool authenticationWasCanceled = false; + bool cancelPreparationInApp = false; + bool preparationWasCanceled = false; - String? authLoadingMessage() => null; + String? preparationLoadingMessage() => null; + + String? preparationErrorMessage(BuildContext context) => null; - Future authenticate() async {} + Future prepareDataLoad() async {} Future<(List, List)> loadData() async { throw UnimplementedError(); } @@ -33,6 +36,38 @@ class Lab { { Map? headers, }) async { + final awaitingOpenFile = + MetaData.instance.awaitingDeepLinkSharePublishUrl ?? false; + final loggedIn = MetaData.instance.isLoggedIn ?? false; + final needsConfirmation = !awaitingOpenFile || loggedIn; + final context = PharMeApp.navigatorKey.currentContext; + if (context == null && needsConfirmation) throw Exception(); + if (needsConfirmation) { + final dialogTitle = loggedIn + ? 'Confirm data overwrite' + : 'Received data'; + final dialogText = 'PharMe received data from another app. ${ + loggedIn + ? 'Overwrite existing data?' + : 'Continue if you want to import the data.' + }'; + await showAdaptiveDialog( + context: PharMeApp.navigatorKey.currentContext!, + builder: (context) => DialogWrapper( + title: dialogTitle, + content: DialogContentText(dialogText), + actions: [ + DialogAction( + onPressed: () => Navigator.pop(context), + text: context.l10n.action_cancel, + ), + DialogAction( + onPressed: () => throw LabProcessCanceled(), + text: context.l10n.action_understood, + ), + ], + ),); + } final response = await http.get(dataUrl, headers: headers); if (response.statusCode != 200) throw Exception(); final json = jsonDecode(response.body) as Map; diff --git a/app/lib/login/models/oauth_authorization_code_flow_lab.dart b/app/lib/login/models/oauth_authorization_code_flow_lab.dart index 7eaf4636..da3ac1bb 100644 --- a/app/lib/login/models/oauth_authorization_code_flow_lab.dart +++ b/app/lib/login/models/oauth_authorization_code_flow_lab.dart @@ -22,7 +22,11 @@ class OAuthAuthorizationCodeFlowLab extends Lab { late String? token; @override - Future authenticate() async { + String? preparationErrorMessage(BuildContext context) => + context.l10n.err_fetch_user_data_failed; + + @override + Future prepareDataLoad() async { const clientId = 'pharme-app'; const callbackUrlScheme = 'localhost'; final url = authUrl.replace(queryParameters: { diff --git a/app/lib/login/pages/login.dart b/app/lib/login/pages/login.dart index e46bd5d6..2ef6679c 100644 --- a/app/lib/login/pages/login.dart +++ b/app/lib/login/pages/login.dart @@ -65,7 +65,7 @@ class LoginPage extends HookWidget { context.l10n.action_cancel, () { final selectedLab = _getSelectedLab(dropdownValue); - selectedLab.authenticationWasCanceled = true; + selectedLab.preparationWasCanceled = true; context .read() .revertToInitialState();