diff --git a/app/lib/login/cubit.dart b/app/lib/login/cubit.dart index 8237a67b..4e64e667 100644 --- a/app/lib/login/cubit.dart +++ b/app/lib/login/cubit.dart @@ -15,10 +15,13 @@ class LoginCubit extends Cubit { // signInAndLoadUserData authenticates a user with a Lab and fetches their // genomic data from it's endpoint. Future signInAndLoadUserData(BuildContext context, Lab lab) async { - emit(LoginState.loadingUserData(null)); + emit(LoginState.loadingUserData( + lab.authLoadingMessage(), + cancelable: lab.cancelAuthInApp, + )); try { await lab.authenticate(); - } on LabAuthenticationCanceled { + } on LabProcessCanceled { revertToInitialState(); return; } on LabAuthenticationError { @@ -29,13 +32,18 @@ class LoginCubit extends Cubit { return; } + if (lab.authenticationWasCanceled) { + lab.authenticationWasCanceled = false; + return; + } + try { final loadingMessage = shouldFetchDiplotypes() // ignore: use_build_context_synchronously ? context.l10n.auth_loading_data // ignore: use_build_context_synchronously : context.l10n.auth_updating_data; - emit(LoginState.loadingUserData(loadingMessage)); + emit(LoginState.loadingUserData(loadingMessage, )); if (shouldFetchDiplotypes()) { final (labData, activeDrugList) = await lab.loadData(); await saveDiplotypesAndActiveDrugs( @@ -59,8 +67,10 @@ class LoginCubit extends Cubit { @freezed class LoginState with _$LoginState { const factory LoginState.initial() = _InitialState; - const factory LoginState.loadingUserData(String? loadingMessage) = - _LoadingUserDataState; + const factory LoginState.loadingUserData( + String? loadingMessage, + {bool? cancelable} + ) = _LoadingUserDataState; const factory LoginState.loadedUserData() = _LoadedUserDataState; const factory LoginState.error(String string) = _ErrorState; } diff --git a/app/lib/login/models/app_share_flow_lab.dart b/app/lib/login/models/app_share_flow_lab.dart index 4d8e4f32..245d0f13 100644 --- a/app/lib/login/models/app_share_flow_lab.dart +++ b/app/lib/login/models/app_share_flow_lab.dart @@ -11,17 +11,29 @@ class AppShareFlowLab extends Lab { late Uri publishUrl; late Map? publishHeaders; + @override + // ignore: overridden_fields + bool cancelAuthInApp = true; + + @override + String? authLoadingMessage() => + 'Please open the $shareAppName and share your data with PharMe'; + @override Future authenticate() async { + // THIS IS FOR TESTING, SHOULD WAIT UNTIL A DEEP LINK IS RECEIVED + // (if possible like this but could set metadata field when deep link + // caught and wait here until it was set) + await Future.delayed(Duration(seconds: 7)); + } + + @override + Future<(List, List)> loadData() async { // THIS IS FOR TESTING, SHOULD GET FROM DWA publishUrl = Uri.parse( 'https://hpi-datastore.duckdns.org/userdata?id=1e006a69-b693-43d2-a318-22904e305b5c', ); publishHeaders = null; - } - - @override - Future<(List, List)> loadData() async { return fetchData( publishUrl, headers: publishHeaders, diff --git a/app/lib/login/models/lab.dart b/app/lib/login/models/lab.dart index 26dd20ab..e50e5849 100644 --- a/app/lib/login/models/lab.dart +++ b/app/lib/login/models/lab.dart @@ -4,8 +4,8 @@ import 'package:http/http.dart' as http; import '../../common/module.dart'; -class LabAuthenticationCanceled implements Exception { - LabAuthenticationCanceled(); +class LabProcessCanceled implements Exception { + LabProcessCanceled(); } class LabAuthenticationError implements Exception { @@ -18,6 +18,10 @@ class Lab { }); String name; + bool cancelAuthInApp = false; + bool authenticationWasCanceled = false; + + String? authLoadingMessage() => null; Future authenticate() async {} Future<(List, List)> loadData() async { 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 dc97f373..7eaf4636 100644 --- a/app/lib/login/models/oauth_authorization_code_flow_lab.dart +++ b/app/lib/login/models/oauth_authorization_code_flow_lab.dart @@ -46,7 +46,7 @@ class OAuthAuthorizationCodeFlowLab extends Lab { token = jsonDecode(response.body)['access_token'] as String; } on PlatformException catch (e) { if (e.code == 'CANCELED') { - throw LabAuthenticationCanceled(); + throw LabProcessCanceled(); } } if (token == null) { diff --git a/app/lib/login/pages/login.dart b/app/lib/login/pages/login.dart index 17682569..6e1853ca 100644 --- a/app/lib/login/pages/login.dart +++ b/app/lib/login/pages/login.dart @@ -4,6 +4,7 @@ import 'package:provider/provider.dart'; import '../../../common/module.dart'; import '../cubit.dart'; import '../models/app_share_flow_lab.dart'; +import '../models/lab.dart'; import '../models/oauth_authorization_code_flow_lab.dart'; final labs = [ @@ -28,6 +29,10 @@ class LoginPage extends HookWidget { final LoginCubit? cubit; + Lab _getSelectedLab(ValueNotifier dropdownValue) => labs.firstWhere( + (lab) => lab.name == dropdownValue.value, + ); + @override Widget build(BuildContext context) { final dropdownValue = useState(labs.first.name); @@ -41,7 +46,7 @@ class LoginPage extends HookWidget { child: state.when( initial: () => _buildInitialScreen(context, dropdownValue), - loadingUserData: (loadingMessage) => Padding( + loadingUserData: (loadingMessage, cancelable) => Padding( padding: EdgeInsets.all(PharMeTheme.largeSpace), child: Column( children: [ @@ -54,6 +59,19 @@ class LoginPage extends HookWidget { textAlign: TextAlign.center, ), ], + if (cancelable ?? false) ...[ + SizedBox(height: PharMeTheme.largeSpace), + FullWidthButton( + context.l10n.action_cancel, + () { + final selectedLab = _getSelectedLab(dropdownValue); + selectedLab.authenticationWasCanceled = true; + context + .read() + .revertToInitialState(); + } + ) + ], ], ), ), @@ -73,12 +91,9 @@ class LoginPage extends HookWidget { ValueNotifier dropdownValue, ) { Future action() async { - final selectedLab = labs.firstWhere( - (el) => el.name == dropdownValue.value, - ); await context .read() - .signInAndLoadUserData(context, selectedLab); + .signInAndLoadUserData(context, _getSelectedLab(dropdownValue)); } return _buildColumnWrapper(