Skip to content

Commit

Permalink
feat(#661): only enable DWA to PharMe flow in login
Browse files Browse the repository at this point in the history
  • Loading branch information
tamslo committed Nov 14, 2024
1 parent 160128f commit 9e248d8
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 123 deletions.
1 change: 0 additions & 1 deletion app/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class PharMeApp extends StatelessWidget {
if (_appRouter.currentPath != '/') {
return DeepLink.path(_appRouter.currentPath);
}
// default route
return getInitialRoute();
},
),
Expand Down
3 changes: 0 additions & 3 deletions app/lib/common/models/metadata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ class MetaData {

@HiveField(6)
String? deepLinkSharePublishUrl;

@HiveField(7)
bool? awaitingDeepLinkSharePublishUrl;
}

/// Initializes the user's metadata by registering all necessary adapters and
Expand Down
21 changes: 14 additions & 7 deletions app/lib/common/widgets/full_width_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,35 @@ class FullWidthButton extends StatelessWidget {
this.action, {
super.key,
this.enabled = true,
this.secondaryColor = false,
this.color,
});

final bool enabled;
final String text;
final void Function() action;
final bool secondaryColor;
final Color? color;

@override
Widget build(BuildContext context) {
final buttonBaseColor = color ?? PharMeTheme.primaryColor;
final buttonColor = enabled
? buttonBaseColor
: darkenColor(buttonBaseColor, -0.4);
return SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: enabled ? action : null,
style: ButtonStyle(
backgroundColor:
WidgetStateProperty.all<Color>(secondaryColor
? PharMeTheme.secondaryColor
: PharMeTheme.primaryColor
),
WidgetStateProperty.all<Color>(buttonColor),
),
child: Text(text, style: PharMeTheme.textTheme.bodyLarge),
child: Text(
text,
style: PharMeTheme.textTheme.bodyLarge!.copyWith(
color: enabled
? PharMeTheme.textTheme.bodyLarge!.color
: Colors.grey.shade400,
)),
),
);
}
Expand Down
2 changes: 1 addition & 1 deletion app/lib/error/pages/error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ErrorPage extends StatelessWidget {
FullWidthButton(
context.l10n.error_close_app,
() => exit(0),
secondaryColor: true,
color: PharMeTheme.secondaryColor,
),
SizedBox(height: PharMeTheme.mediumSpace),
],
Expand Down
20 changes: 5 additions & 15 deletions app/lib/login/cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import 'models/lab.dart';
part 'cubit.freezed.dart';

class LoginCubit extends Cubit<LoginState> {
LoginCubit(this.activeDrugs): super(LoginState.initial());
LoginCubit(this.activeDrugs, LoginState? initialState):
super(initialState ?? LoginState.initial());

ActiveDrugs activeDrugs;

Expand All @@ -15,10 +16,7 @@ class LoginCubit extends Cubit<LoginState> {
// signInAndLoadUserData authenticates a user with a Lab and fetches their
// genomic data from it's endpoint.
Future<void> signInAndLoadUserData(BuildContext context, Lab lab) async {
emit(LoginState.loadingUserData(
lab.preparationLoadingMessage(),
cancelable: lab.cancelPreparationInApp,
));
emit(LoginState.loadingUserData(null));
try {
await lab.prepareDataLoad();
} on LabProcessCanceled {
Expand All @@ -32,12 +30,6 @@ class LoginCubit extends Cubit<LoginState> {
return;
}

if (lab.preparationWasCanceled) {
lab.preparationWasCanceled = false;
MetaData.instance.awaitingDeepLinkSharePublishUrl = false;
return;
}

try {
final loadingMessage = shouldFetchDiplotypes()
// ignore: use_build_context_synchronously
Expand Down Expand Up @@ -68,10 +60,8 @@ class LoginCubit extends Cubit<LoginState> {
@freezed
class LoginState with _$LoginState {
const factory LoginState.initial() = _InitialState;
const factory LoginState.loadingUserData(
String? loadingMessage,
{bool? cancelable}
) = _LoadingUserDataState;
const factory LoginState.loadingUserData(String? loadingMessage) =
_LoadingUserDataState;
const factory LoginState.loadedUserData() = _LoadedUserDataState;
const factory LoginState.error(String string) = _ErrorState;
}
31 changes: 1 addition & 30 deletions app/lib/login/models/deep_link_share_flow_lab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,13 @@ class DeepLinkShareFlowLab extends Lab {
late Uri publishUrl;
late Map<String, String>? publishHeaders;

@override
// ignore: overridden_fields
bool cancelPreparationInApp= true;

@override
String? preparationLoadingMessage() =>
'Please open the $shareAppName and share your data with PharMe';

Future<void> _waitForDeepLinkSharePublishUrl() async {
var waitingForDeepLinkSharePublishUrl = true;
while (waitingForDeepLinkSharePublishUrl) {
waitingForDeepLinkSharePublishUrl =
MetaData.instance.deepLinkSharePublishUrl == null;
await Future.delayed(Duration(seconds: 1));
}
}

Future<void> _setAwaitingDeepLinkSharePublishUrl(bool newValue) async {
MetaData.instance.awaitingDeepLinkSharePublishUrl = newValue;
await MetaData.save();
}

@override
Future<void> prepareDataLoad() async {
await _setAwaitingDeepLinkSharePublishUrl(true);
await _waitForDeepLinkSharePublishUrl();
}

@override
Future<(List<LabResult>, List<String>)> loadData() async {
await _setAwaitingDeepLinkSharePublishUrl(false);
publishUrl = Uri.parse(
MetaData.instance.deepLinkSharePublishUrl!,
);
publishHeaders = null;
return fetchData(
return Lab.fetchData(
publishUrl,
headers: publishHeaders,
);
Expand Down
39 changes: 1 addition & 38 deletions app/lib/login/models/lab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:convert';

import 'package:http/http.dart' as http;

import '../../app.dart';
import '../../common/module.dart';

class LabProcessCanceled implements Exception {
Expand All @@ -19,10 +18,6 @@ class Lab {
});

String name;
bool cancelPreparationInApp = false;
bool preparationWasCanceled = false;

String? preparationLoadingMessage() => null;

String? preparationErrorMessage(BuildContext context) => null;

Expand All @@ -31,43 +26,11 @@ class Lab {
throw UnimplementedError();
}

Future<(List<LabResult>, List<String>)> fetchData(
static Future<(List<LabResult>, List<String>)> fetchData(
Uri dataUrl,
{
Map<String,String>? 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<String, dynamic>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ class OAuthAuthorizationCodeFlowLab extends Lab {

@override
Future<(List<LabResult>, List<String>)> loadData() async {
return fetchData(dataUrl, headers: {'Authorization': 'Bearer $token'});
return Lab.fetchData(dataUrl, headers: {'Authorization': 'Bearer $token'});
}
}
71 changes: 44 additions & 27 deletions app/lib/login/pages/login.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import '../models/lab.dart';
import '../models/oauth_authorization_code_flow_lab.dart';

final labs = [
DeepLinkShareFlowLab(
name: 'Health-X via Data Wallet App',
shareAppName: 'Data Wallet App',
),
OAuthAuthorizationCodeFlowLab(
name: 'Mount Sinai Health System',
authUrl: Uri.http('vm-slosarek01.dhclab.i.hpi.de:28080', 'realms/pharme/protocol/openid-connect/auth'),
Expand All @@ -20,33 +16,44 @@ final labs = [
)
];

final healthXLab = DeepLinkShareFlowLab(
name: 'Health-X via Data Wallet App',
shareAppName: 'Data Wallet App',
);

@RoutePage()
class LoginPage extends HookWidget {
const LoginPage({
super.key,
this.initialState,
@visibleForTesting this.cubit,
});

final LoginCubit? cubit;
final LoginState? initialState;

Lab _getSelectedLab(ValueNotifier<String> dropdownValue) => labs.firstWhere(
(lab) => lab.name == dropdownValue.value,
);

Future<void> _getDataFromLab(Lab lab, BuildContext context) async => context
.read<LoginCubit>()
.signInAndLoadUserData(context, lab);

@override
Widget build(BuildContext context) {
final dropdownValue = useState(labs.first.name);

return Consumer<ActiveDrugs>(
builder: (context, activeDrugs, child) => BlocProvider(
create: (context) => cubit ?? LoginCubit(activeDrugs),
create: (context) => cubit ?? LoginCubit(activeDrugs, initialState),
child: BlocBuilder<LoginCubit, LoginState>(
builder: (context, state) {
return PharMeLogoPage(
child: state.when(
initial: () =>
_buildInitialScreen(context, dropdownValue),
loadingUserData: (loadingMessage, cancelable) => Padding(
loadingUserData: (loadingMessage) => Padding(
padding: EdgeInsets.all(PharMeTheme.largeSpace),
child: Column(
children: [
Expand All @@ -59,19 +66,6 @@ class LoginPage extends HookWidget {
textAlign: TextAlign.center,
),
],
if (cancelable ?? false) ...[
SizedBox(height: PharMeTheme.largeSpace),
FullWidthButton(
context.l10n.action_cancel,
() {
final selectedLab = _getSelectedLab(dropdownValue);
selectedLab.preparationWasCanceled = true;
context
.read<LoginCubit>()
.revertToInitialState();
}
)
],
],
),
),
Expand All @@ -90,14 +84,8 @@ class LoginPage extends HookWidget {
BuildContext context,
ValueNotifier<String> dropdownValue,
) {
Future<void> action() async {
await context
.read<LoginCubit>()
.signInAndLoadUserData(context, _getSelectedLab(dropdownValue));
}

return _buildColumnWrapper(
action: action,
action: () => _getDataFromLab(_getSelectedLab(dropdownValue), context),
actionText: context.l10n.auth_sign_in,
children: [
Text(
Expand Down Expand Up @@ -140,9 +128,33 @@ class LoginPage extends HookWidget {
),
),
],
bottomWidget: Column(
children: _buildHealthXShareContent(context),
)
);
}

List<Widget> _buildHealthXShareContent(BuildContext context) {
final loadingPossible = MetaData.instance.deepLinkSharePublishUrl != null;
return [
FullWidthButton(
'Import data from ${healthXLab.shareAppName}',
() => _getDataFromLab(healthXLab, context),
color: Colors.orange,
enabled: loadingPossible,
),
if (!loadingPossible) ...[
SizedBox(height: PharMeTheme.smallSpace),
Text(
'Please first share your data with PharMe using the '
'${healthXLab.shareAppName}, so that PharMe can import them.',
style: PharMeTheme.textTheme.labelMedium,
textAlign: TextAlign.center,
),
]
];
}

Widget _buildLoadedScreen(BuildContext context) {
return _buildColumnWrapper(
action: () => overwriteRoutes(
Expand Down Expand Up @@ -190,14 +202,19 @@ class LoginPage extends HookWidget {
required void Function()? action,
required String actionText,
required List<Widget> children,
Widget? bottomWidget,
}) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
...children,
SizedBox(height: PharMeTheme.mediumSpace),
SizedBox(height: PharMeTheme.smallSpace),
FullWidthButton(actionText, action ?? () {}),
SizedBox(height: PharMeTheme.mediumSpace),
if (bottomWidget != null) ...[
bottomWidget,
SizedBox(height: PharMeTheme.mediumSpace),
],
],
);
}
Expand Down
1 change: 1 addition & 0 deletions pharme.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"fluorouracil",
"fullscreen",
"gentamicin",
"greyscale",
"haplotype",
"haplotypes",
"Hasso",
Expand Down

0 comments on commit 9e248d8

Please sign in to comment.