From 4f83d2fe948e80edf4a9217e4a2c386e78c6b762 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Tue, 21 Nov 2023 17:19:32 +0100 Subject: [PATCH 1/8] chore(app): reformulate TODO --- app/lib/common/models/drug/drug_inhibitors.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/lib/common/models/drug/drug_inhibitors.dart b/app/lib/common/models/drug/drug_inhibitors.dart index 5c64a908..343b3766 100644 --- a/app/lib/common/models/drug/drug_inhibitors.dart +++ b/app/lib/common/models/drug/drug_inhibitors.dart @@ -21,9 +21,8 @@ const Map> strongDrugInhibitors = { }; // Inhibit phenotype for gene by adapting the activity score by a defined -// factor -// TODO(tamslo): implement inhibition (currently only showing the warning) -// https://github.com/hpi-dhc/PharMe/issues/667 +// factor; not implement yet, currently only showing the warning (see +// https://github.com/hpi-dhc/PharMe/issues/667) const Map> moderateDrugInhibitors = { 'CYP2D6': { 'abiraterone': 0.5, From 740bbddf18150083e2be1a4079983c0698e165e3 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Tue, 21 Nov 2023 17:54:18 +0100 Subject: [PATCH 2/8] feat(#668): add change notifier --- app/lib/common/models/userdata/userdata.dart | 21 ++++++++++ app/lib/common/pages/drug/cubit.dart | 22 ++++------ app/lib/common/pages/drug/drug.dart | 24 ++++++----- app/lib/drug_selection/pages/cubit.dart | 12 ++++-- .../drug_selection/pages/drug_selection.dart | 42 ++++++++++--------- app/lib/main.dart | 9 +++- app/pubspec.lock | 6 +-- app/pubspec.yaml | 1 + 8 files changed, 87 insertions(+), 50 deletions(-) diff --git a/app/lib/common/models/userdata/userdata.dart b/app/lib/common/models/userdata/userdata.dart index 34de155d..25759266 100644 --- a/app/lib/common/models/userdata/userdata.dart +++ b/app/lib/common/models/userdata/userdata.dart @@ -171,6 +171,27 @@ class UserData { } } +class ActiveDrugs extends ChangeNotifier { + List activeDrugs = []; + + Future _preserve() async { + UserData.instance.activeDrugNames = activeDrugs; + await UserData.save(); + } + + Future add(String drugName) async { + activeDrugs.add(drugName); + await _preserve(); + notifyListeners(); + } + + Future remove(String drugName) async { + activeDrugs = activeDrugs.filter((name) => name != drugName).toList(); + await _preserve(); + notifyListeners(); + } +} + /// Initializes the user's data by registering all necessary adapters and /// loading pre-existing data from local storage, if it exists. Future initUserData() async { diff --git a/app/lib/common/pages/drug/cubit.dart b/app/lib/common/pages/drug/cubit.dart index 64d5435e..c223fe29 100644 --- a/app/lib/common/pages/drug/cubit.dart +++ b/app/lib/common/pages/drug/cubit.dart @@ -9,13 +9,19 @@ import '../../utilities/pdf_utils.dart'; part 'cubit.freezed.dart'; class DrugCubit extends Cubit { - DrugCubit() : super(DrugState.loaded()); + DrugCubit(this.activeDrugs) : super(DrugState.loaded()); + + final ActiveDrugs activeDrugs; // ignore: avoid_positional_boolean_parameters Future setActivity(Drug drug, bool? value) async { if (value == null) return; emit(DrugState.loading()); - await setDrugActivity(drug, value); + if (value) { + await activeDrugs.add(drug.name); + } else { + await activeDrugs.remove(drug.name); + } emit(DrugState.loaded()); } @@ -32,18 +38,6 @@ class DrugCubit extends Cubit { } } -// ignore: avoid_positional_boolean_parameters -Future setDrugActivity(Drug drug, bool value) async { - final active = (UserData.instance.activeDrugNames ?? []) - .filter((name) => name != drug.name) - .toList(); - if (value) { - active.add(drug.name); - } - UserData.instance.activeDrugNames = active; - await UserData.save(); -} - @freezed class DrugState with _$DrugState { const factory DrugState.loading() = _LoadingState; diff --git a/app/lib/common/pages/drug/drug.dart b/app/lib/common/pages/drug/drug.dart index b4b46d17..ec2ea218 100644 --- a/app/lib/common/pages/drug/drug.dart +++ b/app/lib/common/pages/drug/drug.dart @@ -1,3 +1,5 @@ +import 'package:provider/provider.dart'; + import '../../module.dart'; import 'cubit.dart'; import 'widgets/module.dart'; @@ -13,16 +15,18 @@ class DrugPage extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => cubit ?? DrugCubit(), - child: BlocBuilder( - builder: (context, state) { - return state.when( - loaded: () => _buildDrugsPage(context, loading: false), - loading: () => _buildDrugsPage(context, loading: true), - ); - }, - ), + return Consumer( + builder: (context, activeDrugs, child) => BlocProvider( + create: (context) => cubit ?? DrugCubit(activeDrugs), + child: BlocBuilder( + builder: (context, state) { + return state.when( + loaded: () => _buildDrugsPage(context, loading: false), + loading: () => _buildDrugsPage(context, loading: true), + ); + }, + ), + ) ); } diff --git a/app/lib/drug_selection/pages/cubit.dart b/app/lib/drug_selection/pages/cubit.dart index ae481640..11ebc5cc 100644 --- a/app/lib/drug_selection/pages/cubit.dart +++ b/app/lib/drug_selection/pages/cubit.dart @@ -1,18 +1,24 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import '../../../common/module.dart'; -import '../../common/pages/drug/cubit.dart'; part 'cubit.freezed.dart'; class DrugSelectionPageCubit extends Cubit { - DrugSelectionPageCubit() : super(DrugSelectionPageState.stable()); + DrugSelectionPageCubit(this.activeDrugs) : + super(DrugSelectionPageState.stable()); + + final ActiveDrugs activeDrugs; // ignore: avoid_positional_boolean_parameters Future updateDrugActivity(Drug drug, bool? value) async { if (value == null) return; emit(DrugSelectionPageState.updating()); - await setDrugActivity(drug, value); + if (value) { + await activeDrugs.add(drug.name); + } else { + await activeDrugs.remove(drug.name); + } emit(DrugSelectionPageState.stable()); } } diff --git a/app/lib/drug_selection/pages/drug_selection.dart b/app/lib/drug_selection/pages/drug_selection.dart index 2f3bd91e..a9f07182 100644 --- a/app/lib/drug_selection/pages/drug_selection.dart +++ b/app/lib/drug_selection/pages/drug_selection.dart @@ -1,3 +1,5 @@ +import 'package:provider/provider.dart'; + import '../../common/models/drug/cached_drugs.dart'; import '../../common/module.dart' hide MetaData; import '../../common/widgets/drug_list/drug_items/drug_checkbox_list.dart'; @@ -15,25 +17,27 @@ class DrugSelectionPage extends HookWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => cubit ?? DrugSelectionPageCubit(), - child: BlocBuilder( - builder: (context, state) { - return unscrollablePageScaffold( - title: context.l10n.drug_selection_header, - padding: PharMeTheme.largeSpace, - body: Column( - children: [ - _buildDescription(context), - SizedBox(height: PharMeTheme.mediumSpace), - Expanded(child:_buildDrugList(context, state)), - SizedBox(height: PharMeTheme.mediumSpace), - _buildButton(context, state), - ], - ), - ); - } - ), + return Consumer( + builder: (context, activeDrugs, child) => BlocProvider( + create: (context) => cubit ?? DrugSelectionPageCubit(activeDrugs), + child: BlocBuilder( + builder: (context, state) { + return unscrollablePageScaffold( + title: context.l10n.drug_selection_header, + padding: PharMeTheme.largeSpace, + body: Column( + children: [ + _buildDescription(context), + SizedBox(height: PharMeTheme.mediumSpace), + Expanded(child:_buildDrugList(context, state)), + SizedBox(height: PharMeTheme.mediumSpace), + _buildButton(context, state), + ], + ), + ); + } + ), + ) ); } diff --git a/app/lib/main.dart b/app/lib/main.dart index 15e28650..9a4ad869 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,8 +1,15 @@ +import 'package:provider/provider.dart'; + import 'common/module.dart'; Future main() async { await initServices(); await fetchAndSaveLookups(); - runApp(PharMeApp()); + runApp( + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: PharMeApp(), + ), + ); await cleanupServices(); } diff --git a/app/pubspec.lock b/app/pubspec.lock index 06feadab..dc2e1e73 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -981,13 +981,13 @@ packages: source: hosted version: "4.2.4" provider: - dependency: transitive + dependency: "direct main" description: name: provider - sha256: e1e7413d70444ea3096815a60fe5da1b11bda8a9dc4769252cc82c53536f8bcc + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.4" + version: "6.1.1" pub_semver: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 8398d763..5a988ce5 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: pdf: ^3.8.1 popover: ^0.2.8+1 printing: ^5.9.3 + provider: ^6.1.1 shared_preferences: ^2.0.15 url_launcher: ^6.1.4 From 4f4ef7eca795d009ef553f15fbca8d768aad2b9b Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 22 Nov 2023 13:34:52 +0100 Subject: [PATCH 3/8] feat(#668): add change notifier to login --- app/lib/common/models/userdata/userdata.dart | 27 ++++++--- app/lib/common/pages/drug/cubit.dart | 6 +- app/lib/common/utilities/genome_data.dart | 13 ++-- app/lib/drug_selection/pages/cubit.dart | 8 +-- app/lib/login/pages/cubit.dart | 6 +- app/lib/login/pages/login.dart | 63 ++++++++++---------- 6 files changed, 68 insertions(+), 55 deletions(-) diff --git a/app/lib/common/models/userdata/userdata.dart b/app/lib/common/models/userdata/userdata.dart index 25759266..31ff88e5 100644 --- a/app/lib/common/models/userdata/userdata.dart +++ b/app/lib/common/models/userdata/userdata.dart @@ -174,21 +174,34 @@ class UserData { class ActiveDrugs extends ChangeNotifier { List activeDrugs = []; - Future _preserve() async { + Future _preserveAndNotify() async { UserData.instance.activeDrugNames = activeDrugs; await UserData.save(); + notifyListeners(); + } + + Future setList(List drugNames) async { + activeDrugs = drugNames; + await _preserveAndNotify(); } - Future add(String drugName) async { + Future _add(String drugName) async { activeDrugs.add(drugName); - await _preserve(); - notifyListeners(); + await _preserveAndNotify(); } - Future remove(String drugName) async { + Future _remove(String drugName) async { activeDrugs = activeDrugs.filter((name) => name != drugName).toList(); - await _preserve(); - notifyListeners(); + await _preserveAndNotify(); + } + + // ignore: avoid_positional_boolean_parameters + Future changeActivity(String drugName, bool value) async { + if (value) { + await _add(drugName); + } else { + await _remove(drugName); + } } } diff --git a/app/lib/common/pages/drug/cubit.dart b/app/lib/common/pages/drug/cubit.dart index c223fe29..1a92b593 100644 --- a/app/lib/common/pages/drug/cubit.dart +++ b/app/lib/common/pages/drug/cubit.dart @@ -17,11 +17,7 @@ class DrugCubit extends Cubit { Future setActivity(Drug drug, bool? value) async { if (value == null) return; emit(DrugState.loading()); - if (value) { - await activeDrugs.add(drug.name); - } else { - await activeDrugs.remove(drug.name); - } + await activeDrugs.changeActivity(drug.name, value); emit(DrugState.loaded()); } diff --git a/app/lib/common/utilities/genome_data.dart b/app/lib/common/utilities/genome_data.dart index a7e536f9..1f8d5f58 100644 --- a/app/lib/common/utilities/genome_data.dart +++ b/app/lib/common/utilities/genome_data.dart @@ -8,11 +8,11 @@ import '../models/drug/cached_drugs.dart'; import '../models/module.dart'; Future fetchAndSaveDiplotypesAndActiveDrugs( - String token, String url) async { + String token, String url, ActiveDrugs activeDrugs) async { if (!shouldFetchDiplotypes()) return; final response = await getDiplotypes(token, url); if (response.statusCode == 200) { - await _saveDiplotypeAndActiveDrugsResponse(response); + await _saveDiplotypeAndActiveDrugsResponse(response, activeDrugs); } else { throw Exception(); } @@ -22,15 +22,18 @@ Future getDiplotypes(String? token, String url) async { return get(Uri.parse(url), headers: {'Authorization': 'Bearer $token'}); } -Future _saveDiplotypeAndActiveDrugsResponse(Response response) async { +Future _saveDiplotypeAndActiveDrugsResponse( + Response response, + ActiveDrugs activeDrugs, +) async { // parse response to list of user's diplotypes final diplotypes = diplotypesFromHTTPResponse(response).filterValidDiplotypes(); - final activeDrugs = activeDrugsFromHTTPResponse(response); + final activeDrugList = activeDrugsFromHTTPResponse(response); UserData.instance.diplotypes = {for (var d in diplotypes) d.gene: d}; - UserData.instance.activeDrugNames = activeDrugs; await UserData.save(); + await activeDrugs.setList(activeDrugList); // invalidate cached drugs because lookups may have changed and we need to // refilter the matching guidelines await CachedDrugs.erase(); diff --git a/app/lib/drug_selection/pages/cubit.dart b/app/lib/drug_selection/pages/cubit.dart index 11ebc5cc..2ae4f0e2 100644 --- a/app/lib/drug_selection/pages/cubit.dart +++ b/app/lib/drug_selection/pages/cubit.dart @@ -7,18 +7,14 @@ part 'cubit.freezed.dart'; class DrugSelectionPageCubit extends Cubit { DrugSelectionPageCubit(this.activeDrugs) : super(DrugSelectionPageState.stable()); - + final ActiveDrugs activeDrugs; // ignore: avoid_positional_boolean_parameters Future updateDrugActivity(Drug drug, bool? value) async { if (value == null) return; emit(DrugSelectionPageState.updating()); - if (value) { - await activeDrugs.add(drug.name); - } else { - await activeDrugs.remove(drug.name); - } + await activeDrugs.changeActivity(drug.name, value); emit(DrugSelectionPageState.stable()); } } diff --git a/app/lib/login/pages/cubit.dart b/app/lib/login/pages/cubit.dart index da735af8..b360f773 100644 --- a/app/lib/login/pages/cubit.dart +++ b/app/lib/login/pages/cubit.dart @@ -11,7 +11,9 @@ import '../models/lab.dart'; part 'cubit.freezed.dart'; class LoginPageCubit extends Cubit { - LoginPageCubit() : super(LoginPageState.initial()); + LoginPageCubit(this.activeDrugs): super(LoginPageState.initial()); + + ActiveDrugs activeDrugs; void revertToInitialState() => emit(LoginPageState.initial()); @@ -46,7 +48,7 @@ class LoginPageCubit extends Cubit { try { // get data await fetchAndSaveDiplotypesAndActiveDrugs( - token, lab.starAllelesUrl.toString()); + token, lab.starAllelesUrl.toString(), activeDrugs); await fetchAndSaveLookups(); await updateCachedDrugs(); diff --git a/app/lib/login/pages/login.dart b/app/lib/login/pages/login.dart index fdf80a76..2f91bcfd 100644 --- a/app/lib/login/pages/login.dart +++ b/app/lib/login/pages/login.dart @@ -1,4 +1,5 @@ import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:provider/provider.dart'; import '../../../common/module.dart'; import '../../common/widgets/full_width_button.dart'; @@ -17,40 +18,42 @@ class LoginPage extends HookWidget { Widget build(BuildContext context) { final dropdownValue = useState(labs.first.name); - return BlocProvider( - create: (context) => cubit ?? LoginPageCubit(), - child: BlocBuilder( - builder: (context, state) { - return unscrollablePageScaffold( - padding: PharMeTheme.largeSpace, - body: Stack( - children: [ - Positioned.fill( - child: Align( - alignment: Alignment.center, - child: SvgPicture.asset( - 'assets/images/logo.svg', + return Consumer( + builder: (context, activeDrugs, child) => BlocProvider( + create: (context) => cubit ?? LoginPageCubit(activeDrugs), + child: BlocBuilder( + builder: (context, state) { + return unscrollablePageScaffold( + padding: PharMeTheme.largeSpace, + body: Stack( + children: [ + Positioned.fill( + child: Align( + alignment: Alignment.center, + child: SvgPicture.asset( + 'assets/images/logo.svg', + ), ), ), - ), - Positioned( - child: Align( - alignment: Alignment.bottomCenter, - child: state.when( - initial: () => - _buildInitialScreen(context, dropdownValue), - loadingUserData: CircularProgressIndicator.new, - loadedUserData: () => _buildLoadedScreen(context), - error: (message) => - _buildErrorScreen(context, message), + Positioned( + child: Align( + alignment: Alignment.bottomCenter, + child: state.when( + initial: () => + _buildInitialScreen(context, dropdownValue), + loadingUserData: CircularProgressIndicator.new, + loadedUserData: () => _buildLoadedScreen(context), + error: (message) => + _buildErrorScreen(context, message), + ), ), ), - ), - ], - ), - ); - }, - ), + ], + ), + ); + }, + ), + ) ); } From a98e4047145a31c3611bc033cfa631eca25de8c1 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 22 Nov 2023 14:56:41 +0100 Subject: [PATCH 4/8] feat(#668): use consumer on report page --- app/lib/common/models/userdata/userdata.dart | 10 +++++----- .../drug_list/drug_items/drug_checkbox_list.dart | 10 +++------- app/lib/report/pages/report.dart | 12 ++++++++++-- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/lib/common/models/userdata/userdata.dart b/app/lib/common/models/userdata/userdata.dart index 31ff88e5..e2a1f4a0 100644 --- a/app/lib/common/models/userdata/userdata.dart +++ b/app/lib/common/models/userdata/userdata.dart @@ -172,26 +172,26 @@ class UserData { } class ActiveDrugs extends ChangeNotifier { - List activeDrugs = []; + List names = []; Future _preserveAndNotify() async { - UserData.instance.activeDrugNames = activeDrugs; + UserData.instance.activeDrugNames = names; await UserData.save(); notifyListeners(); } Future setList(List drugNames) async { - activeDrugs = drugNames; + names = drugNames; await _preserveAndNotify(); } Future _add(String drugName) async { - activeDrugs.add(drugName); + names.add(drugName); await _preserveAndNotify(); } Future _remove(String drugName) async { - activeDrugs = activeDrugs.filter((name) => name != drugName).toList(); + names = names.filter((name) => name != drugName).toList(); await _preserveAndNotify(); } diff --git a/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart b/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart index 3ecda1d3..f1ca395e 100644 --- a/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart +++ b/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart @@ -1,10 +1,6 @@ import '../../../module.dart'; import 'utils.dart'; -bool isDrugSelected(Drug drug) { - return UserData.instance.activeDrugNames!.contains(drug.name); -} - List buildDrugCheckboxList( BuildContext context, List drugs, @@ -18,8 +14,8 @@ List buildDrugCheckboxList( final checkboxesEnabled = buildParams['checkboxesEnabled']; final sortedDrugs = List.from(drugs); sortedDrugs.sort((drugA, drugB) { - final drugASelected = isDrugSelected(drugA); - final drugBSelected = isDrugSelected(drugB); + final drugASelected = drugA.isActive(); + final drugBSelected = drugB.isActive(); if (drugASelected == drugBSelected) return drugA.name.compareTo(drugB.name); return drugASelected ? -1 : 1; }); @@ -27,7 +23,7 @@ List buildDrugCheckboxList( ...sortedDrugs.map( (drug) => CheckboxListTile( enabled: checkboxesEnabled, - value: isDrugSelected(drug), + value: drug.isActive(), onChanged: (value) => onCheckboxChange(drug, value), title: Text(formatDrugName(drug, showDrugInteractionIndicator)), subtitle: (drug.annotations.brandNames.isNotEmpty) ? diff --git a/app/lib/report/pages/report.dart b/app/lib/report/pages/report.dart index d5b6a065..8213a0c5 100644 --- a/app/lib/report/pages/report.dart +++ b/app/lib/report/pages/report.dart @@ -1,11 +1,19 @@ +import 'package:provider/provider.dart'; + import '../../common/module.dart'; import '../../common/utilities/guideline_utils.dart'; class ReportPage extends StatelessWidget { @override Widget build(BuildContext context) { - final hasActiveInhibitors = UserData.instance.activeDrugNames != null && - UserData.instance.activeDrugNames!.any(isInhibitor); + return Consumer( + builder: (context, activeDrugs, child) => + _buildReportPage(context, activeDrugs) + ); + } + + Widget _buildReportPage(BuildContext context, ActiveDrugs activeDrugs) { + final hasActiveInhibitors = activeDrugs.names.any(isInhibitor); final guidelineGenes = getGuidelineGenes(); final notTestedString = context.l10n.general_not_tested; From c61a234076d856545df7498c5cf44c91aaa9a243 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 22 Nov 2023 17:02:42 +0100 Subject: [PATCH 5/8] chore(app): add useful flutter aliases --- app/CONTRIBUTING.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 49cf1062..d6fda983 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -11,11 +11,23 @@ Please also see the [contribution guide in the root folder](../CONTRIBUTING.md). - Run `dart pub get` to fetch all dart dependencies - Run `flutter pub get` to fetch all flutter dependencies and setup all generated code - - Run `flutter pub run build_runner build --delete-conflicting-outputs` + - Run `flutter pub run build_runner build --delete-conflicting-outputs` or + `flutter pub run build_runner watch --delete-conflicting-outputs` to + re-generate code upon file changes while developing You should now be able to run the app by opening the debug panel on the left and pressing the green triangle at the top (or using the shortcut F5). +## Useful Shortcuts + +For (cleaning) generated code, you might want to add the following aliases to +your shell configuration: + +```bash +alias flutter-generate='flutter pub run build_runner build --delete-conflicting-outputs' +alias flutter-clean='find . -maxdepth 20 -type f \( -name "*.inject.summary" -o -name "*.inject.dart" -o -name "*.g.dart" \) -delete' +``` + ## Architecture The app consists of multiple so-called modules. Our main modules correspond to From abcff680b770281a9c2c2390275e0284c2706014 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 22 Nov 2023 18:34:09 +0100 Subject: [PATCH 6/8] feat(#668): pass consumer to drug list filter --- app/lib/common/models/userdata/userdata.dart | 4 + app/lib/common/widgets/drug_list/builder.dart | 8 +- app/lib/common/widgets/drug_list/cubit.dart | 7 +- app/lib/common/widgets/drug_search.dart | 8 +- app/lib/report/pages/gene.dart | 126 +++++++++--------- 5 files changed, 85 insertions(+), 68 deletions(-) diff --git a/app/lib/common/models/userdata/userdata.dart b/app/lib/common/models/userdata/userdata.dart index e2a1f4a0..2f94ec68 100644 --- a/app/lib/common/models/userdata/userdata.dart +++ b/app/lib/common/models/userdata/userdata.dart @@ -203,6 +203,10 @@ class ActiveDrugs extends ChangeNotifier { await _remove(drugName); } } + + bool contains(String drugName) { + return names.contains(drugName); + } } /// Initializes the user's data by registering all necessary adapters and diff --git a/app/lib/common/widgets/drug_list/builder.dart b/app/lib/common/widgets/drug_list/builder.dart index 40d05f7f..5883d4da 100644 --- a/app/lib/common/widgets/drug_list/builder.dart +++ b/app/lib/common/widgets/drug_list/builder.dart @@ -4,6 +4,7 @@ import 'drug_items/drug_cards.dart'; List buildDrugList( BuildContext context, DrugListState state, + ActiveDrugs activeDrugs, { String? noDrugsMessage, List Function( @@ -18,8 +19,11 @@ List buildDrugList( Map? drugItemsBuildParams, } ) { - List buildDrugList(List drugs, FilterState filter) { - final filteredDrugs = filter.filter(drugs); + List buildDrugList( + List drugs, + FilterState filter, + ) { + final filteredDrugs = filter.filter(drugs, activeDrugs); if (filteredDrugs.isEmpty && noDrugsMessage != null) { return [errorIndicator(noDrugsMessage)]; } diff --git a/app/lib/common/widgets/drug_list/cubit.dart b/app/lib/common/widgets/drug_list/cubit.dart index 90e278b7..272a3ccd 100644 --- a/app/lib/common/widgets/drug_list/cubit.dart +++ b/app/lib/common/widgets/drug_list/cubit.dart @@ -122,7 +122,7 @@ class FilterState { final Map showWarningLevel; final String gene; - bool isAccepted(Drug drug) { + bool isAccepted(Drug drug, ActiveDrugs activeDrugs) { final userGuideline = drug.userGuideline(); final guidelineGenes = drug.guidelines.isNotEmpty ? drug.guidelines.first.lookupkey.keys.toList() : @@ -137,13 +137,14 @@ class FilterState { showWarningLevel[WarningLevel.green]!; } final isDrugAccepted = drug.matches(query: query) && - (drug.isActive() || showInactive) && + (activeDrugs.contains(drug.name) || showInactive) && warningLevelMatches && (gene.isBlank || (guidelineGenes.contains(gene))); return isDrugAccepted; } - List filter(List drugs) => drugs.filter(isAccepted).toList(); + List filter(List drugs, ActiveDrugs activeDrugs) => + drugs.filter((drug) => isAccepted(drug, activeDrugs)).toList(); } @freezed diff --git a/app/lib/common/widgets/drug_search.dart b/app/lib/common/widgets/drug_search.dart index 4a08fa73..77df82e4 100644 --- a/app/lib/common/widgets/drug_search.dart +++ b/app/lib/common/widgets/drug_search.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; import '../../../common/module.dart'; import '../../common/pages/drug/widgets/tooltip_icon.dart'; @@ -35,7 +36,8 @@ class DrugSearch extends HookWidget { ? context.l10n.search_no_drugs_with_filter_amendment : ''; final noDrugsMessage = context.l10n.search_no_drugs(amendment); - return BlocProvider( + return Consumer( + builder: (context, activeDrugs, child) => BlocProvider( create: (context) => cubit, child: BlocBuilder( builder: (context, state) { @@ -61,6 +63,7 @@ class DrugSearch extends HookWidget { buildDrugList( context, state, + activeDrugs, buildDrugItems: buildDrugItems, noDrugsMessage: noDrugsMessage, drugItemsBuildParams: drugItemsBuildParams, @@ -71,7 +74,8 @@ class DrugSearch extends HookWidget { ..._maybeShowDrugInteractionExplanation(context), ], ); - } + } + ) ) ); } diff --git a/app/lib/report/pages/gene.dart b/app/lib/report/pages/gene.dart index 562a81e1..fae05a03 100644 --- a/app/lib/report/pages/gene.dart +++ b/app/lib/report/pages/gene.dart @@ -1,3 +1,5 @@ +import 'package:provider/provider.dart'; + import '../../common/module.dart'; import '../../common/pages/drug/widgets/sub_header.dart'; import '../../common/pages/drug/widgets/tooltip_icon.dart'; @@ -13,70 +15,72 @@ class GenePage extends HookWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => cubit, - child: BlocBuilder( - builder: (context, state) => pageScaffold( - title: context.l10n.gene_page_headline(phenotype.geneSymbol), - body: [ - Padding( - padding: EdgeInsets.symmetric( - horizontal: PharMeTheme.smallToMediumSpace, - vertical: PharMeTheme.mediumSpace - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SubHeader( - context.l10n.gene_page_your_variant(phenotype.geneSymbol), - tooltip: context.l10n - .gene_page_name_tooltip(phenotype.geneSymbol), - ), - SizedBox(height: PharMeTheme.smallToMediumSpace), - RoundedCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Table( - columnWidths: Map.from({ - 0: IntrinsicColumnWidth(), - 1: IntrinsicColumnWidth(flex: 1), - }), - children: [ - _buildRow( - context.l10n.gene_page_genotype, - phenotype.genotype, - tooltip: context.l10n.gene_page_genotype_tooltip - ), - _buildRow(context.l10n.gene_page_phenotype, - UserData.phenotypeFor( - phenotype.geneSymbol, - context, - ).phenotype, - tooltip: - context.l10n.gene_page_phenotype_tooltip - ), - ], - ), - if (inhibitableGenes.contains(phenotype.geneSymbol)) - ...buildDrugInteractionInfo( - context, - phenotype.geneSymbol, + return Consumer( + builder: (context, activeDrugs, child) => BlocProvider( + create: (context) => cubit, + child: BlocBuilder( + builder: (context, state) => pageScaffold( + title: context.l10n.gene_page_headline(phenotype.geneSymbol), + body: [ + Padding( + padding: EdgeInsets.symmetric( + horizontal: PharMeTheme.smallToMediumSpace, + vertical: PharMeTheme.mediumSpace + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SubHeader( + context.l10n.gene_page_your_variant(phenotype.geneSymbol), + tooltip: context.l10n + .gene_page_name_tooltip(phenotype.geneSymbol), + ), + SizedBox(height: PharMeTheme.smallToMediumSpace), + RoundedCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Table( + columnWidths: Map.from({ + 0: IntrinsicColumnWidth(), + 1: IntrinsicColumnWidth(flex: 1), + }), + children: [ + _buildRow( + context.l10n.gene_page_genotype, + phenotype.genotype, + tooltip: context.l10n.gene_page_genotype_tooltip + ), + _buildRow(context.l10n.gene_page_phenotype, + UserData.phenotypeFor( + phenotype.geneSymbol, + context, + ).phenotype, + tooltip: + context.l10n.gene_page_phenotype_tooltip + ), + ], ), - ], - )), - SizedBox(height: PharMeTheme.smallToMediumSpace), - SubHeader(context.l10n.gene_page_affected_drugs, - tooltip: context.l10n.gene_page_affected_drugs_tooltip), - SizedBox(height: PharMeTheme.smallSpace), - ...buildDrugList(context, state, - noDrugsMessage: context.l10n.gene_page_no_affected_drugs) - ], + if (inhibitableGenes.contains(phenotype.geneSymbol)) + ...buildDrugInteractionInfo( + context, + phenotype.geneSymbol, + ), + ], + )), + SizedBox(height: PharMeTheme.smallToMediumSpace), + SubHeader(context.l10n.gene_page_affected_drugs, + tooltip: context.l10n.gene_page_affected_drugs_tooltip), + SizedBox(height: PharMeTheme.smallSpace), + ...buildDrugList(context, state, activeDrugs, + noDrugsMessage: context.l10n.gene_page_no_affected_drugs) + ], + ), ), - ), - ], + ], + ), ), - ), + ) ); } From fc29ce29ddd02058b38d7a13da0bdbd37baa02c5 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 22 Nov 2023 18:49:33 +0100 Subject: [PATCH 7/8] feat(#668): add provider to drug page tests --- app/integration_test/drugs_test.dart | 86 ++++++++++++++++------------ 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/app/integration_test/drugs_test.dart b/app/integration_test/drugs_test.dart index cf9da165..bc87a03b 100644 --- a/app/integration_test/drugs_test.dart +++ b/app/integration_test/drugs_test.dart @@ -8,6 +8,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:provider/provider.dart'; class MockDrugsCubit extends MockCubit implements DrugCubit {} @@ -83,21 +84,24 @@ void main() { late BuildContext context; await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return DrugPage(testDrug, cubit: mockDrugsCubit); - }, + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: Scaffold( + body: Builder( + builder: (context) { + return DrugPage(testDrug, cubit: mockDrugsCubit); + }, + ), ), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], ), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], ), ); @@ -151,24 +155,27 @@ void main() { DrugState.loaded()); await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return DrugPage( - testDrugWithoutGuidelines, - cubit: mockDrugsCubit, - ); - }, + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: Scaffold( + body: Builder( + builder: (context) { + return DrugPage( + testDrugWithoutGuidelines, + cubit: mockDrugsCubit, + ); + }, + ), ), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], ), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], ), ); @@ -180,15 +187,18 @@ void main() { when(() => mockDrugsCubit.state).thenReturn(DrugState.loading()); await tester.pumpWidget( - MaterialApp( - home: DrugPage(testDrug, cubit: mockDrugsCubit), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: DrugPage(testDrug, cubit: mockDrugsCubit), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), ), ); From e08c9a4ebbba920503620d23de2226555b3aed77 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 22 Nov 2023 19:43:33 +0100 Subject: [PATCH 8/8] feat(#668): add provider to further tests --- app/integration_test/login_test.dart | 85 ++++++++++++++---------- app/integration_test/main_page_test.dart | 26 +++++--- app/integration_test/search_test.dart | 63 ++++++++++-------- 3 files changed, 101 insertions(+), 73 deletions(-) diff --git a/app/integration_test/login_test.dart b/app/integration_test/login_test.dart index b88a1376..ed077c69 100644 --- a/app/integration_test/login_test.dart +++ b/app/integration_test/login_test.dart @@ -6,6 +6,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:provider/provider.dart'; class MockLoginCubit extends MockCubit implements LoginPageCubit {} @@ -23,15 +24,18 @@ void main() { ); await tester.pumpWidget( - MaterialApp( - home: LoginPage(cubit: mockLoginCubit), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: LoginPage(cubit: mockLoginCubit), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), ), ); @@ -44,15 +48,18 @@ void main() { ); await tester.pumpWidget( - MaterialApp( - home: LoginPage(cubit: mockLoginCubit), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: LoginPage(cubit: mockLoginCubit), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), ), ); @@ -67,15 +74,18 @@ void main() { ); await tester.pumpWidget( - MaterialApp( - home: Scaffold(body: LoginPage(cubit: mockLoginCubit)), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: Scaffold(body: LoginPage(cubit: mockLoginCubit)), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), ), ); @@ -91,15 +101,18 @@ void main() { ); await tester.pumpWidget( - MaterialApp( - home: Scaffold(body: LoginPage(cubit: mockLoginCubit)), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: Scaffold(body: LoginPage(cubit: mockLoginCubit)), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), ), ); diff --git a/app/integration_test/main_page_test.dart b/app/integration_test/main_page_test.dart index fe7f193b..992acb46 100644 --- a/app/integration_test/main_page_test.dart +++ b/app/integration_test/main_page_test.dart @@ -3,6 +3,7 @@ import 'package:app/common/module.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:provider/provider.dart'; void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -19,18 +20,21 @@ void main() { await initServices(); final appRouter = AppRouter(); await tester.pumpWidget( - MaterialApp.router( - routeInformationParser: appRouter.defaultRouteParser(), - routerDelegate: appRouter.delegate( - initialDeepLink: 'main', + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp.router( + routeInformationParser: appRouter.defaultRouteParser(), + routerDelegate: appRouter.delegate( + initialDeepLink: 'main', + ), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], ), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], ), ); diff --git a/app/integration_test/search_test.dart b/app/integration_test/search_test.dart index 4e849b41..8f91c091 100644 --- a/app/integration_test/search_test.dart +++ b/app/integration_test/search_test.dart @@ -6,6 +6,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:provider/provider.dart'; class MockDrugListCubit extends MockCubit implements DrugListCubit { @override @@ -52,20 +53,25 @@ void main() { group('integration test for the search page', () { testWidgets('test search page in loading state', (tester) async { when(() => mockDrugListCubit.state).thenReturn(DrugListState.loading()); - await tester.pumpWidget(BlocProvider.value( - value: mockDrugListCubit, - child: MaterialApp( - debugShowCheckedModeBanner: false, - home: SearchPage(cubit: mockDrugListCubit), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], + await tester.pumpWidget( + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: BlocProvider.value( + value: mockDrugListCubit, + child: MaterialApp( + debugShowCheckedModeBanner: false, + home: SearchPage(cubit: mockDrugListCubit), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), + ), ), - )); + ); expect(find.byType(CircularProgressIndicator), findsOneWidget); }); @@ -74,20 +80,25 @@ void main() { when(() => mockDrugListCubit.state) .thenReturn(DrugListState.loaded(loadedDrugs, FilterState.initial())); - await tester.pumpWidget(BlocProvider.value( - value: mockDrugListCubit, - child: MaterialApp( - debugShowCheckedModeBanner: false, - home: SearchPage(cubit: mockDrugListCubit), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], + await tester.pumpWidget( + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: BlocProvider.value( + value: mockDrugListCubit, + child: MaterialApp( + debugShowCheckedModeBanner: false, + home: SearchPage(cubit: mockDrugListCubit), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), + ), ), - )); + ); await tester.pumpAndSettle(); expect(