Skip to content

Commit

Permalink
Merge pull request #574 from hpi-dhc/issue/479-show-active-medication…
Browse files Browse the repository at this point in the history
…s-with-switch

Show active medications with switch
  • Loading branch information
jannis-baum authored Feb 28, 2023
2 parents db57fdc + 5148954 commit 3e9d649
Show file tree
Hide file tree
Showing 18 changed files with 295 additions and 71 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defaults:
env:
JAVA_VERSION: 12.x
FLUTTER_CHANNEL: stable
FLUTTER_VERSION: 3.0.3
FLUTTER_VERSION: 3.3.4

jobs:
lint:
Expand All @@ -24,7 +24,7 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: ${{ env.JAVA_VERSION }}
- uses: subosito/flutter-action@v1
- uses: subosito/flutter-action@v2
with:
channel: ${{ env.FLUTTER_CHANNEL }}
flutter-version: ${{ env.FLUTTER_VERSION }}
Expand Down
4 changes: 2 additions & 2 deletions app/integration_test/search_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'package:mocktail/mocktail.dart';

class MockSearchCubit extends MockCubit<SearchState> implements SearchCubit {
@override
bool get filterActive => false;
FilterState get filter => FilterState.initial();
}

void main() {
Expand Down Expand Up @@ -71,7 +71,7 @@ void main() {

testWidgets('test search page in loaded state', (tester) async {
when(() => mockSearchCubit.state)
.thenReturn(SearchState.loaded(loadedDrugs, loadedDrugs));
.thenReturn(SearchState.loaded(loadedDrugs, FilterState.initial()));

await tester.pumpWidget(BlocProvider.value(
value: mockSearchCubit,
Expand Down
2 changes: 1 addition & 1 deletion app/integration_test/settings_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void main() {
);

// close dialog
await tester.tap(find.text(context.l10n.settings_page_cancel));
await tester.tap(find.text(context.l10n.action_cancel));
await tester.pumpAndSettle();

// test onboarding button
Expand Down
4 changes: 3 additions & 1 deletion app/lib/common/models/drug/warning_level.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ enum WarningLevel {
@HiveField(1)
yellow,
@HiveField(2)
green
green,
@HiveField(3)
none,
}

extension WarningLevelIcon on WarningLevel {
Expand Down
1 change: 1 addition & 0 deletions app/lib/common/models/userdata/userdata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class UserData {
return UserData.instance.lookups?[gene]?.lookupkey;
}

// hive can't deal with sets so we have to use a list :(
@HiveField(2)
List<String>? activeDrugNames;
}
Expand Down
18 changes: 10 additions & 8 deletions app/lib/common/pages/drug/cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ class DrugCubit extends Cubit<DrugState> {

final Drug _drug;

Future<void> toggleActive() async {
// ignore: avoid_positional_boolean_parameters
Future<void> setActivity(bool? value) async {
if (value == null) return;
final drug = state.whenOrNull(loaded: (drug, _) => drug);
if (drug == null) return;

final active = UserData.instance.activeDrugNames ?? [];
if (drug.isActive()) {
UserData.instance.activeDrugNames =
active.filter((element) => element != _drug.name).toList();
} else {
UserData.instance.activeDrugNames = active + [_drug.name];
final active = (UserData.instance.activeDrugNames ?? [])
.filter((name) => name != _drug.name)
.toList();
if (value) {
active.add(_drug.name);
}
UserData.instance.activeDrugNames = active;
await UserData.save();
emit(DrugState.loaded(drug, isActive: drug.isActive()));
emit(DrugState.loaded(drug, isActive: value));
}
}

Expand Down
12 changes: 6 additions & 6 deletions app/lib/common/pages/drug/drug.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ class DrugPage extends StatelessWidget {
loaded: (drug, isActive) => pageScaffold(
title: drugName,
actions: [
IconButton(
onPressed: () => context.read<DrugCubit>().toggleActive(),
icon: PharMeTheme.activeDrugIcon(isActive: isActive),
),
IconButton(
onPressed: () => sharePdf(drug),
icon: Icon(
Expand Down Expand Up @@ -64,9 +60,13 @@ class DrugPage extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SubHeader(context.l10n.drugs_page_header_druginfo),
SubHeader(context.l10n.drugs_page_header_drug),
SizedBox(height: 12),
DrugAnnotationCard(drug),
DrugAnnotationCard(
drug,
isActive: isActive,
setActivity: context.read<DrugCubit>().setActivity,
),
SizedBox(height: 20),
SubHeader(
context.l10n.drugs_page_header_guideline,
Expand Down
45 changes: 44 additions & 1 deletion app/lib/common/pages/drug/widgets/annotation_cards/drug.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import 'package:flutter/cupertino.dart';

import '../../../../module.dart';
import '../sub_header.dart';

class DrugAnnotationCard extends StatelessWidget {
const DrugAnnotationCard(this.drug);
const DrugAnnotationCard(
this.drug, {
required this.isActive,
required this.setActivity,
});

final Drug drug;
final bool isActive;
final void Function(bool?) setActivity;

@override
Widget build(BuildContext context) {
Expand All @@ -12,6 +21,8 @@ class DrugAnnotationCard extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SubHeader(context.l10n.drugs_page_header_druginfo),
SizedBox(height: 12),
Text(drug.annotations.indication),
SizedBox(height: 8),
Table(defaultColumnWidth: IntrinsicColumnWidth(), children: [
Expand All @@ -22,6 +33,38 @@ class DrugAnnotationCard extends StatelessWidget {
drug.annotations.brandNames.join(', ')),
]
]),
SizedBox(height: 4),
Divider(color: PharMeTheme.borderColor),
SizedBox(height: 4),
SubHeader(context.l10n.drugs_page_header_active),
CheckboxListTile(
activeColor: PharMeTheme.primaryColor,
title: Text(context.l10n.drugs_page_active),
value: isActive,
onChanged: (newValue) => showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoAlertDialog(
title: Text(context.l10n.drugs_page_active_warn_header),
content: Text(context.l10n.drugs_page_active_warn),
actions: <CupertinoDialogAction>[
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () => Navigator.pop(context, 'Cancel'),
child: Text(context.l10n.action_cancel),
),
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () {
Navigator.pop(context, 'OK');
setActivity(newValue);
},
child: Text(context.l10n.action_continue),
),
],
),
),
controlAffinity: ListTileControlAffinity.leading,
),
],
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ class GuidelineAnnotationCard extends StatelessWidget {
_buildHeader(context),
SizedBox(height: 12),
_buildCard(context),
SizedBox(height: 16),
SizedBox(height: 8),
Divider(color: PharMeTheme.borderColor),
SizedBox(height: 8),
_buildSourcesSection(context),
SizedBox(height: 12),
]),
Expand Down
5 changes: 0 additions & 5 deletions app/lib/common/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,6 @@ class PharMeTheme {
static const backgroundColor = Colors.white;
static const errorColor = Color(0xccf52a2a);
static final borderColor = Colors.black.withOpacity(.2);

static Icon activeDrugIcon({required bool isActive, double? size}) {
return Icon(isActive ? Icons.star_rounded : Icons.star_border_rounded,
size: size, color: primaryColor);
}
}

extension WarningLevelColor on WarningLevel {
Expand Down
100 changes: 100 additions & 0 deletions app/lib/common/widgets/context_menu.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import 'package:popover/popover.dart';

import '../module.dart';

class ContextMenu extends StatelessWidget {
const ContextMenu({
super.key,
required this.items,
required this.child,
});

final List<ContextMenuCheckmark> items;
final Widget child;

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
showPopover(
context: context,
bodyBuilder: (context) => Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Container(
decoration: BoxDecoration(
color: PharMeTheme.onSurfaceColor,
borderRadius: BorderRadius.circular(8),
),
child: IntrinsicWidth(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: items
.mapIndexed(
(index, item) => (index == items.count() - 1)
? item
: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: PharMeTheme.borderColor),
)),
child: item))
.toList(),
),
),
),
),
direction: PopoverDirection.bottom,
arrowHeight: 0,
arrowWidth: 0,
transitionDuration: Duration(milliseconds: 100),
barrierColor: Color.fromRGBO(0, 0, 0, 0.05),
backgroundColor: Color.fromRGBO(1, 1, 1, 0),
shadow: [],
);
},
child: child);
}
}

class ContextMenuCheckmark extends StatelessWidget {
const ContextMenuCheckmark(
{super.key,
required this.label,
required this.setState,
this.initialState = false});

final String label;
final void Function(bool) setState;
final bool initialState;

@override
Widget build(BuildContext context) {
var state = initialState;
return StatefulBuilder(
builder: (context, rebuild) => GestureDetector(
onTap: () {
rebuild(() {
state = !state;
setState(state);
});
},
child: Padding(
padding: EdgeInsets.all(12),
child: Row(
children: [
if (state)
Icon(Icons.check_rounded, size: 16)
else
SizedBox(width: 16, height: 16),
SizedBox(width: 8),
Expanded(child: Text(label)),
],
),
),
),
);
}
}
1 change: 1 addition & 0 deletions app/lib/common/widgets/module.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'app.dart';
export 'context_menu.dart';
export 'headings.dart';
export 'indicators.dart';
export 'page_scaffold.dart';
Expand Down
19 changes: 15 additions & 4 deletions app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
{
"action_cancel": "Cancel",
"action_continue": "Continue",

"auth_choose_lab": "Please select your lab",
"auth_sign_in": "Sign in",
"auth_success": "Successfully imported data",

"err_could_not_retrieve_access_token": "An unexpected error occurred while retrieving the access token",
"err_fetch_user_data_failed": "An unexpected error occurred while fetching your genomic data",
"err_generic": "Error!",
"err_no_active_drugs": "You have no active drugs! Try disabling the filter next to the search bar above.",
"err_no_drugs": "Try adjusting the filters next to the search bar above.",

"faq_page_description": "Here you can find answers to common questions about PGx",
"faq_pharmacogenomics": "Pharmacogenomics",
Expand All @@ -17,6 +20,11 @@
"general_retry": "Retry",

"search_page_tooltip_search": "Search for drugs by their name, brand name or class.",
"search_page_filter_inactive": "Inactive drugs",
"search_page_filter_green": "Green warning level",
"search_page_filter_yellow": "Yellow warning level",
"search_page_filter_red": "Red warning level",
"search_page_filter_gray": "No warning level",

"drugs_page_disclaimer": "This assessment is ONLY based on your genetics. Important factors like weight, age and pre-existing conditions are not considered.",
"drugs_page_overwritten_phenotype": "adjusted based on you taking {drugName}",
Expand All @@ -33,7 +41,12 @@
"drugs_page_header_further_info": "Further Information",
"drugs_page_header_synonyms": "Synonyms",
"drugs_page_header_drugclass": "Drug class",
"drugs_page_header_druginfo": "Drug Information",
"drugs_page_header_drug": "Drug",
"drugs_page_header_druginfo": "Information",
"drugs_page_header_active": "Activity",
"drugs_page_active": "I am actively taking this drug.",
"drugs_page_active_warn_header": "Are you sure you want to change the activity status?",
"drugs_page_active_warn": "This may adjust the guidelines you receive for other drugs.",
"drugs_page_header_guideline": "Clinical Guideline",
"drugs_page_header_recommendation": "Recommendation",
"drugs_page_no_guidelines_for_phenotype": "We couldn't find a guideline matching your genetics.",
Expand Down Expand Up @@ -105,8 +118,6 @@
"onboarding_5_text": "After signing in to the lab, your genetic data is sent straight onto your phone.\n\nOur servers know nothing about you, neither your identity nor your genetics.",

"settings_page_account_settings": "Account Settings",
"settings_page_cancel": "Cancel",
"settings_page_continue": "Continue",
"settings_page_delete_data": "Delete App Data",
"settings_page_delete_data_text": "Are you sure that you want to delete all app data? This also includes your genomic data.",
"settings_page_more": "More",
Expand Down
Loading

0 comments on commit 3e9d649

Please sign in to comment.