From a1e3b8d1d4b0c2faf0f2cfa7cc34f7a5a58739ff Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Fri, 23 Jun 2023 09:45:14 -0700 Subject: [PATCH 01/38] HAPP-1534 - fixed bottom navigation bar icon, color & typography - moved colors to Colors.kt --- .../java/ca/bc/gov/bchealth/compose/Styles.kt | 19 ++++------------- .../bc/gov/bchealth/compose/theme/Colors.kt | 21 +++++++++++++++++++ .../ui/auth/BCServicesCardSessionScreen.kt | 2 +- .../gov/bchealth/ui/comment/CommentsScreen.kt | 8 +++---- .../bchealth/ui/comment/CommentsSummaryUI.kt | 6 +++--- .../ui/custom/MyHealthClickableText.kt | 2 +- .../gov/bchealth/ui/custom/MyHealthToolbar.kt | 4 ++-- .../dependents/profile/DependentProfileUI.kt | 6 +++--- .../bc/gov/bchealth/ui/feeback/FeedbackUI.kt | 8 +++---- .../ui/healthrecord/HealthRecordListItem.kt | 2 +- .../imaging/DiagnosticImagingDetailScreen.kt | 2 +- .../ui/profile/CommunicationPrefsUI.kt | 4 ++-- .../bc/gov/bchealth/ui/profile/ProfileUI.kt | 4 ++-- .../gov/bchealth/ui/resources/ResourcesUI.kt | 4 ++-- .../bc/gov/bchealth/widget/CommentInputUI.kt | 14 ++++++------- .../main/res/drawable/bottom_nav_selector.xml | 2 +- app/src/main/res/drawable/home_selector.xml | 5 +++++ app/src/main/res/drawable/ic_dependents.xml | 7 +++---- app/src/main/res/drawable/ic_home.xml | 12 +++++------ .../main/res/drawable/ic_home_un_selected.xml | 13 ++++++++++++ app/src/main/res/drawable/ic_services.xml | 1 - .../main/res/drawable/ic_services_filled.xml | 13 ++++++------ app/src/main/res/layout/activity_main.xml | 2 ++ .../res/menu/bottom_nav_services_menu.xml | 2 +- app/src/main/res/values/typography.xml | 15 +++++++++++++ 25 files changed, 110 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt create mode 100644 app/src/main/res/drawable/home_selector.xml create mode 100644 app/src/main/res/drawable/ic_home_un_selected.xml diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt index 6c57b60f8..a0111c7c3 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt @@ -3,7 +3,6 @@ package ca.bc.gov.bchealth.compose import androidx.compose.material.MaterialTheme import androidx.compose.material.Typography import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily @@ -12,6 +11,10 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.theme.darkText +import ca.bc.gov.bchealth.compose.theme.descriptionGrey +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.white // Consider making touch targets at least 48x48dp. https://support.google.com/accessibility/android/answer/7101858?hl=en val minButtonSize = 48.dp @@ -23,20 +26,6 @@ val fonts = FontFamily( Font(R.font.bc_sans_bold_italic, weight = FontWeight.Bold, style = FontStyle.Italic), ) -val darkText = Color(0xFF313132) - -val black = Color(0xFF000000) -val white = Color(0xFFFFFFFF) -val blue = Color(0xFF1A5A96) -val primaryBlue = Color(0xFF003366) -val lightBlue = Color(0xFFB2C1CF) -val statusBlue30 = Color(0x4D38598A) -val descriptionGrey = Color(0xFF6D757D) -val grey = Color(0xFF606060) -val greyBg = Color(0xFFF2F2F2) -val green = Color(0xFF2E8540) -val red = Color(0xFFD8292F) - val Typography.largeButton: TextStyle @Composable get() { diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt new file mode 100644 index 000000000..f91a87675 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt @@ -0,0 +1,21 @@ +package ca.bc.gov.bchealth.compose.theme + +import androidx.compose.ui.graphics.Color + +val darkText = Color(0xFF313132) +val black = Color(0xFF000000) + +val white = Color(0xFFFFFFFF) + +val blue = Color(0xFF1A5A96) +val primaryBlue = Color(0xFF003366) +val lightBlue = Color(0xFFB2C1CF) +val statusBlue30 = Color(0x4D38598A) +val statusBlue = Color(0xFF38598A) + +val descriptionGrey = Color(0xFF6D757D) +val grey = Color(0xFF606060) +val greyBg = Color(0xFFF2F2F2) + +val green = Color(0xFF2E8540) +val red = Color(0xFFD8292F) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/auth/BCServicesCardSessionScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/auth/BCServicesCardSessionScreen.kt index b82cac823..45a2d8074 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/auth/BCServicesCardSessionScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/auth/BCServicesCardSessionScreen.kt @@ -19,7 +19,7 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.greyBg +import ca.bc.gov.bchealth.compose.theme.greyBg @Composable fun BCServicesCardSessionScreen( diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsScreen.kt index ed74af7d7..74dd30621 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsScreen.kt @@ -44,11 +44,11 @@ import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.grey -import ca.bc.gov.bchealth.compose.greyBg import ca.bc.gov.bchealth.compose.minButtonSize -import ca.bc.gov.bchealth.compose.primaryBlue -import ca.bc.gov.bchealth.compose.red +import ca.bc.gov.bchealth.compose.theme.grey +import ca.bc.gov.bchealth.compose.theme.greyBg +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.red import ca.bc.gov.bchealth.widget.CommentInputUI import ca.bc.gov.bchealth.widget.EditableCommentInputUI import ca.bc.gov.common.model.SyncStatus diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsSummaryUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsSummaryUI.kt index 01a7ca4e1..2f8ed4124 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsSummaryUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsSummaryUI.kt @@ -18,9 +18,9 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.blue -import ca.bc.gov.bchealth.compose.grey -import ca.bc.gov.bchealth.compose.greyBg +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.grey +import ca.bc.gov.bchealth.compose.theme.greyBg import ca.bc.gov.common.model.SyncStatus import ca.bc.gov.common.utils.toDateTimeString import java.time.Instant diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthClickableText.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthClickableText.kt index 7aa1c9178..2c5b78812 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthClickableText.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthClickableText.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview -import ca.bc.gov.bchealth.compose.primaryBlue +import ca.bc.gov.bchealth.compose.theme.primaryBlue private const val TEXT_TAG = "TEXT_TAG" diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthToolbar.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthToolbar.kt index 8fecb0f8b..ac381eeb2 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthToolbar.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/custom/MyHealthToolbar.kt @@ -22,8 +22,8 @@ import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.primaryBlue -import ca.bc.gov.bchealth.compose.white +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.white @Composable fun MyHealthToolbar( diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileUI.kt index 74139a62c..0b640e8b3 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileUI.kt @@ -23,9 +23,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.primaryBlue -import ca.bc.gov.bchealth.compose.statusBlue30 -import ca.bc.gov.bchealth.compose.white +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.statusBlue30 +import ca.bc.gov.bchealth.compose.theme.white import ca.bc.gov.bchealth.ui.custom.DecorativeImage import ca.bc.gov.bchealth.ui.custom.MyHealthScaffold import ca.bc.gov.common.BuildConfig diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackUI.kt index 3abefc3bd..16181c007 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackUI.kt @@ -31,11 +31,11 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTypography import ca.bc.gov.bchealth.compose.SmallDevicePreview -import ca.bc.gov.bchealth.compose.black -import ca.bc.gov.bchealth.compose.grey -import ca.bc.gov.bchealth.compose.lightBlue import ca.bc.gov.bchealth.compose.minButtonSize -import ca.bc.gov.bchealth.compose.red +import ca.bc.gov.bchealth.compose.theme.black +import ca.bc.gov.bchealth.compose.theme.grey +import ca.bc.gov.bchealth.compose.theme.lightBlue +import ca.bc.gov.bchealth.compose.theme.red import ca.bc.gov.bchealth.model.validation.BaseTextValidation import ca.bc.gov.bchealth.model.validation.BaseTextValidation.BLANK import ca.bc.gov.bchealth.model.validation.BaseTextValidation.EXCEEDS_LENGTH diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/HealthRecordListItem.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/HealthRecordListItem.kt index fcfd5044f..caa2a4646 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/HealthRecordListItem.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/HealthRecordListItem.kt @@ -10,8 +10,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.compose.MyHealthTypography import ca.bc.gov.bchealth.compose.bold -import ca.bc.gov.bchealth.compose.descriptionGrey import ca.bc.gov.bchealth.compose.italic +import ca.bc.gov.bchealth.compose.theme.descriptionGrey @Composable fun HealthRecordListItem(modifier: Modifier = Modifier, label: String, value: String, footer: String? = null) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/imaging/DiagnosticImagingDetailScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/imaging/DiagnosticImagingDetailScreen.kt index e1a0e2e98..b43d9b0b6 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/imaging/DiagnosticImagingDetailScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/imaging/DiagnosticImagingDetailScreen.kt @@ -20,7 +20,7 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.descriptionGrey +import ca.bc.gov.bchealth.compose.theme.descriptionGrey import ca.bc.gov.bchealth.ui.component.HGLargeOutlinedButton import ca.bc.gov.bchealth.ui.healthrecord.HealthRecordDetailItem import ca.bc.gov.bchealth.ui.healthrecord.HealthRecordListItem diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/profile/CommunicationPrefsUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/profile/CommunicationPrefsUI.kt index 239822701..7cdc7da12 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/profile/CommunicationPrefsUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/profile/CommunicationPrefsUI.kt @@ -16,8 +16,8 @@ import androidx.compose.ui.unit.sp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.green -import ca.bc.gov.bchealth.compose.red +import ca.bc.gov.bchealth.compose.theme.green +import ca.bc.gov.bchealth.compose.theme.red import ca.bc.gov.bchealth.ui.custom.DecorativeImage import ca.bc.gov.bchealth.ui.custom.MyHealthClickableText diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileUI.kt index 040470a34..cda631c00 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileUI.kt @@ -21,8 +21,8 @@ import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.darkText -import ca.bc.gov.bchealth.compose.primaryBlue +import ca.bc.gov.bchealth.compose.theme.darkText +import ca.bc.gov.bchealth.compose.theme.primaryBlue import ca.bc.gov.bchealth.ui.custom.DecorativeImage import ca.bc.gov.bchealth.ui.custom.MyHealthClickableText import ca.bc.gov.bchealth.ui.custom.MyHealthScaffold diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesUI.kt index 55a0cd172..d03f2b3b6 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesUI.kt @@ -19,8 +19,8 @@ import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.blue -import ca.bc.gov.bchealth.compose.greyBg +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.greyBg import ca.bc.gov.bchealth.ui.custom.DecorativeImage import ca.bc.gov.bchealth.ui.custom.MyHealthScaffold diff --git a/app/src/main/java/ca/bc/gov/bchealth/widget/CommentInputUI.kt b/app/src/main/java/ca/bc/gov/bchealth/widget/CommentInputUI.kt index 4d909b947..39456deb3 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/widget/CommentInputUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/widget/CommentInputUI.kt @@ -41,14 +41,14 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.blue -import ca.bc.gov.bchealth.compose.darkText -import ca.bc.gov.bchealth.compose.grey -import ca.bc.gov.bchealth.compose.greyBg import ca.bc.gov.bchealth.compose.minButtonSize -import ca.bc.gov.bchealth.compose.primaryBlue -import ca.bc.gov.bchealth.compose.red -import ca.bc.gov.bchealth.compose.white +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.darkText +import ca.bc.gov.bchealth.compose.theme.grey +import ca.bc.gov.bchealth.compose.theme.greyBg +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.red +import ca.bc.gov.bchealth.compose.theme.white import ca.bc.gov.bchealth.ui.comment.Comment import ca.bc.gov.common.model.SyncStatus import java.time.Instant diff --git a/app/src/main/res/drawable/bottom_nav_selector.xml b/app/src/main/res/drawable/bottom_nav_selector.xml index 4c9e79216..8d7cdd158 100644 --- a/app/src/main/res/drawable/bottom_nav_selector.xml +++ b/app/src/main/res/drawable/bottom_nav_selector.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/home_selector.xml b/app/src/main/res/drawable/home_selector.xml new file mode 100644 index 000000000..14d44c159 --- /dev/null +++ b/app/src/main/res/drawable/home_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_dependents.xml b/app/src/main/res/drawable/ic_dependents.xml index 09fbd2ebe..725eb6e4b 100644 --- a/app/src/main/res/drawable/ic_dependents.xml +++ b/app/src/main/res/drawable/ic_dependents.xml @@ -3,8 +3,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - - + diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml index 8aa89af38..7f2d4b311 100644 --- a/app/src/main/res/drawable/ic_home.xml +++ b/app/src/main/res/drawable/ic_home.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M10.198,19.655V14.655H14.198V19.655C14.198,20.205 14.648,20.655 15.198,20.655H18.198C18.748,20.655 19.198,20.205 19.198,19.655V12.655H20.898C21.358,12.655 21.578,12.085 21.228,11.785L12.868,4.255C12.488,3.915 11.908,3.915 11.528,4.255L3.168,11.785C2.828,12.085 3.038,12.655 3.498,12.655H5.198V19.655C5.198,20.205 5.648,20.655 6.198,20.655H9.198C9.748,20.655 10.198,20.205 10.198,19.655Z" + android:fillColor="#38598A"/> diff --git a/app/src/main/res/drawable/ic_home_un_selected.xml b/app/src/main/res/drawable/ic_home_un_selected.xml new file mode 100644 index 000000000..52df99ec3 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_un_selected.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_services.xml b/app/src/main/res/drawable/ic_services.xml index 306d08805..ddcf7b98d 100644 --- a/app/src/main/res/drawable/ic_services.xml +++ b/app/src/main/res/drawable/ic_services.xml @@ -6,7 +6,6 @@ - - - - + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 31a75d1ca..38cabc3ef 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -29,6 +29,8 @@ android:filterTouchesWhenObscured="true" app:itemIconTint="@drawable/bottom_nav_selector" app:itemTextColor="@drawable/bottom_nav_selector" + app:itemTextAppearanceActive="@style/HealthGateway.BottomNavigationView.TextAppearanceActive" + app:itemTextAppearanceInactive="@style/HealthGateway.BottomNavigationView.TextAppearanceInactive" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> diff --git a/app/src/main/res/menu/bottom_nav_services_menu.xml b/app/src/main/res/menu/bottom_nav_services_menu.xml index 9ef8c8ce1..424cefc24 100644 --- a/app/src/main/res/menu/bottom_nav_services_menu.xml +++ b/app/src/main/res/menu/bottom_nav_services_menu.xml @@ -4,7 +4,7 @@ @font/bc_sans_font_family normal + + + + + \ No newline at end of file From cc3afcefc52d0e18118eba181dc6c9a34c3087dc Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Mon, 26 Jun 2023 09:20:01 -0700 Subject: [PATCH 02/38] HAPP-1535 - added home screen redesign 2.0 for non authenticated users. --- app/build.gradle | 1 - .../java/ca/bc/gov/bchealth/MainActivity.kt | 1 + .../ca/bc/gov/bchealth/SplashViewModel.kt | 46 ++- .../java/ca/bc/gov/bchealth/compose/Styles.kt | 3 +- .../compose/component/AnnouncementBannerUI.kt | 182 ++++++++++ .../gov/bchealth/compose/component/Button.kt | 313 ++++++++++++++++++ .../compose/component/LoginInfoCardUI.kt | 84 +++++ .../component/QuickAccessTileItemUI.kt | 123 +++++++ .../bchealth/compose/component/TopAppBar.kt | 58 ++++ .../compose/component/menu/ActionMenu.kt | 94 ++++++ .../component/menu/TopAppBarActionItem.kt | 31 ++ .../bc/gov/bchealth/compose/theme/Colors.kt | 1 + .../bc/gov/bchealth/compose/theme/FontType.kt | 67 ++++ .../ca/bc/gov/bchealth/compose/theme/Theme.kt | 16 + .../bc/gov/bchealth/ui/home/HomeBannerUI.kt | 4 +- .../ca/bc/gov/bchealth/ui/home/HomeCardUI.kt | 4 +- .../bchealth/ui/home/HomeComposeFragment.kt | 59 ++++ .../bchealth/ui/home/HomeComposeViewModel.kt | 45 +++ .../bc/gov/bchealth/ui/home/HomeFragment.kt | 14 +- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 159 +++++---- .../ui/notification/NotificationFragment.kt | 2 +- .../ui/notification/NotificationScreen.kt | 6 +- .../res/drawable/ic_tile_healt_resources.xml | 13 + .../ic_tile_immunization_schedules.xml | 18 + .../res/drawable/ic_tile_proof_of_vaccine.xml | 12 + .../res/drawable/icon_tile_health_record.xml | 27 ++ .../img_un_authenticated_home_screen.xml | 9 + app/src/main/res/navigation/home.xml | 24 +- app/src/main/res/values/strings.xml | 4 + .../common/model/settings/AppFeatureDto.kt | 10 + .../AppFeatureWithQuickAccessTilesDto.kt | 6 + .../model/settings/QuickAccessTileDto.kt | 9 + .../14.json | 124 ++++++- .../local/AppFeatureLocalDataSource.kt | 21 ++ .../data/datasource/local/MyHealthDataBase.kt | 10 + .../local/QuickActionTileLocalDataSource.kt | 15 + .../datasource/local/dao/AppFeatureDao.kt | 21 ++ .../local/dao/QuickAccessTileDao.kt | 7 + .../AppFeatureWithQuickAccessTiles.kt | 17 + .../local/entity/settings/AppFeatureEntity.kt | 25 ++ .../entity/settings/QuickAccessTileEntity.kt | 30 ++ .../bc/gov/data/di/LocalDataSourceModule.kt | 12 + .../data/model/mapper/DtoToEntityMapper.kt | 21 ++ .../data/model/mapper/EntityToDtoMapper.kt | 28 ++ .../gov/repository/di/RepositoriesModule.kt | 24 ++ .../settings/AppFeatureRepository.kt | 16 + ...ppFeatureWithQuickAccessTilesRepository.kt | 10 + .../settings/QuickAccessTileRepository.kt | 8 + scripts/versions.gradle | 1 + 49 files changed, 1729 insertions(+), 106 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/ActionMenu.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/TopAppBarActionItem.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/theme/FontType.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/theme/Theme.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt create mode 100644 app/src/main/res/drawable/ic_tile_healt_resources.xml create mode 100644 app/src/main/res/drawable/ic_tile_immunization_schedules.xml create mode 100644 app/src/main/res/drawable/ic_tile_proof_of_vaccine.xml create mode 100644 app/src/main/res/drawable/icon_tile_health_record.xml create mode 100644 app/src/main/res/drawable/img_un_authenticated_home_screen.xml create mode 100644 common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt create mode 100644 common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt create mode 100644 common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt create mode 100644 repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt create mode 100644 repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt create mode 100644 repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt diff --git a/app/build.gradle b/app/build.gradle index 0bc2d4019..921bb1ecf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -117,7 +117,6 @@ dependencies { debugImplementation 'androidx.compose.ui:ui-tooling' - //Navigation implementation "androidx.navigation:navigation-fragment-ktx:$versions.navigation" implementation "androidx.navigation:navigation-ui-ktx:$versions.navigation" diff --git a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt index 0008b94a1..4a0bcfc27 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt @@ -89,6 +89,7 @@ class MainActivity : AppCompatActivity() { R.id.vaccineRecordDetailFragment, R.id.addHealthRecordsFragment, R.id.homeFragment, + R.id.homeComposeFragment, R.id.bannerDetailFragment, R.id.newsfeedFragment, R.id.servicesFragment, diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index c7b6a00e8..b66f79075 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -5,12 +5,15 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import ca.bc.gov.common.BuildConfig.LOCAL_API_VERSION +import ca.bc.gov.common.model.settings.AppFeatureDto import ca.bc.gov.repository.OnBoardingRepository +import ca.bc.gov.repository.settings.AppFeatureRepository import ca.bc.gov.repository.worker.MobileConfigRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import javax.inject.Inject private const val MAX_SPLASH_DELAY = 2000L @@ -18,7 +21,8 @@ private const val MAX_SPLASH_DELAY = 2000L @HiltViewModel class SplashViewModel @Inject constructor( private val mobileConfigRepository: MobileConfigRepository, - onBoardingRepository: OnBoardingRepository + onBoardingRepository: OnBoardingRepository, + private val appFeatureRepository: AppFeatureRepository ) : ViewModel() { private val _updateType: MutableLiveData = MutableLiveData() @@ -27,6 +31,9 @@ class SplashViewModel @Inject constructor( init { onBoardingRepository.checkIfReOnBoardingRequired(BuildConfig.VERSION_CODE) + runBlocking { + initializeAppFeatureAndQuickAccessTileData() + } } fun checkAppVersion() { @@ -53,4 +60,41 @@ class SplashViewModel @Inject constructor( enum class UpdateType { FORCE_UPDATE, CHECK_SOFT_UPDATE } + + private suspend fun initializeAppFeatureAndQuickAccessTileData() { + val appFeatures = listOf( + AppFeatureDto( + featureNameId = R.string.health_records, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isEnabled = true, + isQuickAccessEnabled = true, + ), + AppFeatureDto( + featureNameId = R.string.immunization_schedules, + featureIconId = R.drawable.ic_tile_immunization_schedules, + destinationId = R.id.health_records, + isEnabled = true, + isQuickAccessEnabled = true, + ), + AppFeatureDto( + featureNameId = R.string.health_resources, + featureIconId = R.drawable.ic_tile_healt_resources, + destinationId = R.id.action_homeFragment_to_resources, + isEnabled = true, + isQuickAccessEnabled = true, + ), + AppFeatureDto( + featureNameId = R.string.health_passes, + featureIconId = R.drawable.ic_tile_proof_of_vaccine, + destinationId = R.id.action_homeFragment_to_health_pass, + isEnabled = true, + isQuickAccessEnabled = true, + ) + ) + + appFeatures.forEach { + appFeatureRepository.insert(it) + } + } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt index a0111c7c3..3c1d347df 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt @@ -121,11 +121,12 @@ fun MyHealthTheme(content: @Composable () -> Unit) = MaterialTheme( colors = MaterialTheme.colors.copy( primary = primaryBlue, primaryVariant = white, + onPrimary = white, background = white, surface = white, secondary = primaryBlue, secondaryVariant = primaryBlue, - onBackground = primaryBlue + onBackground = primaryBlue, ), typography = MyHealthTypography, shapes = MaterialTheme.shapes, diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt new file mode 100644 index 000000000..214829438 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt @@ -0,0 +1,182 @@ +package ca.bc.gov.bchealth.compose.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.Card +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintSet +import androidx.constraintlayout.compose.Dimension +import androidx.constraintlayout.compose.Visibility +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue +import ca.bc.gov.bchealth.compose.theme.blue + +private const val bannerIconId = "bannerIconId" +private const val bannerTitleId = "bannerTitleId" +private const val bannerArrowId = "bannerArrowId" +private const val bannerBodyId = "bannerBodyId" +private const val buttonLearnModeId = "buttonLearnMoreId" +private const val buttonDismissId = "buttonDismissId" + +@Composable +fun AnnouncementBannerUI( + modifier: Modifier = Modifier, + title: String, + description: String, + onLearnMoreClick: () -> Unit, + onDismissClick: () -> Unit +) { + var expanded by remember { mutableStateOf(false) } + Card( + modifier.clickable { expanded = !expanded }, + backgroundColor = bannerBackgroundBlue + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(16.dp) + ) { + val constraint = bannerConstraints(expanded) + ConstraintLayout(constraint, modifier = Modifier.fillMaxWidth()) { + Image( + modifier = Modifier.layoutId(bannerIconId), + painter = painterResource(id = R.drawable.ic_banner_icon), + contentDescription = null + ) + + Text( + modifier = Modifier.layoutId(bannerTitleId), + text = title, + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + color = blue + ) + + Image( + modifier = Modifier.layoutId(bannerArrowId), + painter = painterResource(id = R.drawable.ic_arrow_down), + contentDescription = null + ) + + Text( + modifier = Modifier.layoutId(bannerBodyId), + text = description, + style = MaterialTheme.typography.body1 + ) + + HGTextButton( + onClick = { onDismissClick() }, + text = stringResource(id = R.string.dismiss), + modifier = Modifier.layoutId(buttonDismissId), + defaultHeight = HGButtonDefaults.SmallButtonHeight, + leadingIcon = painterResource(id = R.drawable.ic_dismiss) + ) + + HGTextButton( + onClick = { onLearnMoreClick() }, + text = stringResource(id = R.string.learn_more), + modifier = Modifier.layoutId(buttonLearnModeId), + defaultHeight = HGButtonDefaults.SmallButtonHeight, + leadingIcon = painterResource(id = R.drawable.ic_external_link) + ) + } + } + } +} + +private fun bannerConstraints(expanded: Boolean): ConstraintSet { + return ConstraintSet { + val bannerIcon = createRefFor(bannerIconId) + val bannerTitle = createRefFor(bannerTitleId) + val bannerArrow = createRefFor(bannerArrowId) + val contentBody = createRefFor(bannerBodyId) + val buttonLearnMore = createRefFor(buttonLearnModeId) + val buttonDismiss = createRefFor(buttonDismissId) + + constrain(bannerIcon) { + top.linkTo(parent.top) + start.linkTo(parent.start) + } + + constrain(bannerTitle) { + start.linkTo(bannerIcon.end, 16.dp) + top.linkTo(bannerIcon.top) + end.linkTo(bannerArrow.start) + bottom.linkTo(bannerIcon.bottom) + width = Dimension.fillToConstraints + } + + constrain(bannerArrow) { + top.linkTo(bannerTitle.top) + end.linkTo(parent.end) + bottom.linkTo(bannerTitle.bottom) + } + + constrain(contentBody) { + start.linkTo(bannerTitle.start) + top.linkTo(bannerTitle.bottom, 16.dp) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + visibility = if (expanded) { + Visibility.Visible + } else { + Visibility.Gone + } + } + + constrain(buttonDismiss) { + top.linkTo(contentBody.bottom, 16.dp) + end.linkTo(parent.end) + bottom.linkTo(parent.bottom) + visibility = if (expanded) { + Visibility.Visible + } else { + Visibility.Gone + } + } + + constrain(buttonLearnMore) { + top.linkTo(buttonDismiss.top) + end.linkTo(buttonDismiss.start) + bottom.linkTo(buttonDismiss.bottom) + visibility = if (expanded) { + Visibility.Visible + } else { + Visibility.Gone + } + } + } +} + +@Composable +@BasePreview +private fun AnnouncementBannerUIPreview() { + HealthGatewayTheme { + AnnouncementBannerUI( + title = stringResource(id = R.string.news_feed), + description = stringResource(id = R.string.news_feed), + onDismissClick = {}, + onLearnMoreClick = {} + ) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt new file mode 100644 index 000000000..21cb51385 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt @@ -0,0 +1,313 @@ +package ca.bc.gov.bchealth.compose.component + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.sizeIn +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme + +@Composable +fun HGButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, + content: @Composable RowScope.() -> Unit +) { + Button( + onClick = onClick, + modifier = modifier.defaultMinSize(minHeight = defaultHeight), + enabled = enabled, + colors = ButtonDefaults.buttonColors( + disabledBackgroundColor = MaterialTheme.colors.primary.copy(alpha = 0.20f), + disabledContentColor = MaterialTheme.colors.onPrimary.copy(alpha = 0.20f) + ), + contentPadding = contentPadding, + content = content + ) +} + +@Composable +fun HGButton( + onClick: () -> Unit, + text: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, + leadingIcon: @Composable (() -> Unit)? = null +) { + HGButton( + onClick, + modifier, + enabled, + defaultHeight, + contentPadding = if (leadingIcon != null) { + ButtonDefaults.ButtonWithIconContentPadding + } else { + ButtonDefaults.ContentPadding + } + ) { + HGButtonContent( + { + Text( + text = text, + style = if (defaultHeight == HGButtonDefaults.SmallButtonHeight) { + MaterialTheme.typography.body2 + } else { + MaterialTheme.typography.subtitle2 + }, + fontWeight = FontWeight.Bold + ) + }, + leadingIcon + ) + } +} + +@Composable +fun HGButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, + content: @Composable () -> Unit, + leadingIcon: @Composable (() -> Unit)? = null +) { + HGButton( + onClick, + modifier, + enabled, + defaultHeight, + contentPadding = if (leadingIcon != null) { + ButtonDefaults.ButtonWithIconContentPadding + } else { + ButtonDefaults.ContentPadding + } + ) { + HGButtonContent( + content, + leadingIcon + ) + } +} + +@Composable +fun HGTextButton( + onClick: () -> Unit, + text: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, + leadingIcon: Painter +) { + + HGTextButton( + onClick, + text, + modifier, + enabled, + defaultHeight + ) { + Icon( + painter = leadingIcon, + contentDescription = text + ) + } +} + +@Composable +fun HGTextButton( + onClick: () -> Unit, + text: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, + leadingIcon: @Composable (() -> Unit)? = null +) { + HGTextButton( + onClick, + modifier, + enabled, + defaultHeight, + contentPadding = if (leadingIcon != null) { + ButtonDefaults.ButtonWithIconContentPadding + } else { + ButtonDefaults.ContentPadding + } + ) { + HGButtonContent( + { + Text( + text = text, + style = if (defaultHeight == HGButtonDefaults.SmallButtonHeight) { + MaterialTheme.typography.body2 + } else { + MaterialTheme.typography.subtitle2 + }, + fontWeight = FontWeight.Bold + ) + }, + leadingIcon + ) + } +} + +@Composable +fun HGTextButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, + content: @Composable () -> Unit, + leadingIcon: @Composable (() -> Unit)? = null +) { + HGTextButton( + onClick, + modifier, + enabled, + defaultHeight, + contentPadding = if (leadingIcon != null) { + ButtonDefaults.ButtonWithIconContentPadding + } else { + ButtonDefaults.ContentPadding + } + ) { + HGButtonContent( + content, + leadingIcon + ) + } +} + +@Composable +fun HGTextButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, + content: @Composable RowScope.() -> Unit +) { + + TextButton( + onClick = onClick, + modifier = modifier.defaultMinSize(minHeight = defaultHeight), + enabled = enabled, + colors = ButtonDefaults.textButtonColors( + disabledContentColor = MaterialTheme.colors.onPrimary.copy(alpha = 0.20f) + ), + contentPadding = contentPadding, + content = content + ) +} + +@Composable +private fun HGButtonContent( + content: @Composable () -> Unit, + leadingIcon: @Composable (() -> Unit)? = null +) { + if (leadingIcon != null) { + Box(Modifier.sizeIn(maxHeight = ButtonDefaults.IconSize)) { + leadingIcon() + } + } + Box( + Modifier + .padding( + start = if (leadingIcon != null) { + ButtonDefaults.IconSpacing + } else { + 0.dp + }, + ), + ) { + content() + } +} + +val ButtonDefaults.ButtonWithIconContentPadding: PaddingValues + get() = PaddingValues( + start = 16.dp, + top = 8.dp, + end = 8.dp, + bottom = 8.dp + ) + +object HGButtonDefaults { + + val SmallButtonHeight = 42.dp + + val LargeButtonHeight = 54.dp +} + +@Composable +@BasePreview +private fun HGButtonLargePreview() { + HealthGatewayTheme { + HGButton(onClick = { /*TODO*/ }) { + Text(text = "Hello") + } + } +} + +@Composable +@BasePreview +private fun HGButtonLargeDisabledPreview() { + HealthGatewayTheme { + HGButton(onClick = { /*TODO*/ }, enabled = false) { + Text(text = "Hello") + } + } +} + +@Composable +@BasePreview +private fun HGButtonSmallPreview() { + HealthGatewayTheme { + HGButton(onClick = { /*TODO*/ }, defaultHeight = HGButtonDefaults.SmallButtonHeight) { + Text(text = "Hello") + } + } +} + +@Composable +@BasePreview +private fun HGButtonLargeSmallPreview() { + HealthGatewayTheme { + HGButton( + onClick = { /*TODO*/ }, + enabled = false, + defaultHeight = HGButtonDefaults.SmallButtonHeight + ) { + Text(text = "Hello") + } + } +} + +@Composable +@BasePreview +private fun HGTextButtonWithIconPreview() { + HealthGatewayTheme { + HGTextButton(onClick = { /*TODO*/ }, text = "TextButton") { + Icon(painter = painterResource(id = R.drawable.ic_dismiss), contentDescription = "") + } + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt new file mode 100644 index 000000000..7b6263763 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt @@ -0,0 +1,84 @@ +package ca.bc.gov.bchealth.compose.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Card +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.greyBg + +@Composable +fun LoginInfoCardUI( + onClick: () -> Unit, + modifier: Modifier = Modifier, + title: String, + subTitle: String, + image: Painter = painterResource(id = R.drawable.img_un_authenticated_home_screen) +) { + Card( + modifier = modifier, + backgroundColor = greyBg + ) { + Box(modifier = modifier) { + Image( + modifier = Modifier.align(Alignment.BottomEnd), + painter = image, + contentDescription = null + ) + + Column( + modifier = modifier + .padding(start = 16.dp, top = 24.dp, end = 16.dp, bottom = 24.dp) + .fillMaxWidth() + ) { + + Text( + text = title, + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold, + color = blue + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = subTitle, + style = MaterialTheme.typography.body2 + ) + Spacer(modifier = Modifier.height(16.dp)) + HGButton( + onClick = onClick, + text = stringResource(id = R.string.get_started), + defaultHeight = HGButtonDefaults.SmallButtonHeight + ) + } + } + } +} + +@Composable +@BasePreview +private fun LoginInfoCardUIPreview() { + HealthGatewayTheme { + LoginInfoCardUI( + onClick = { /*TODO*/ }, + title = stringResource(id = R.string.log_in_with_bc_services_card), + subTitle = stringResource(id = R.string.login_to_view_hidden_records_msg) + ) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt new file mode 100644 index 000000000..ba4a684bb --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt @@ -0,0 +1,123 @@ +package ca.bc.gov.bchealth.compose.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.Card +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintSet +import androidx.constraintlayout.compose.Dimension +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme + +private const val tileIconId = "tileIconId" +private const val tileTitleId = "tileTitleId" +private const val tileArrowId = "tileArrowId" + +@Composable +fun QuickAccessTileItemUI( + onClick: () -> Unit, + modifier: Modifier = Modifier, + icon: Painter, + title: String +) { + Card( + modifier = modifier + .height(120.dp) + .clickable { onClick() }, + elevation = 15.dp, + backgroundColor = MaterialTheme.colors.background + ) { + + BoxWithConstraints( + modifier = modifier + .fillMaxSize() + ) { + ConstraintLayout( + tileConstraints(), + modifier = modifier + .fillMaxSize() + ) { + + Image( + modifier = Modifier + .size(32.dp) + .layoutId(tileIconId), + painter = icon, + contentDescription = null + ) + + Text( + modifier = Modifier + .wrapContentHeight(Alignment.Bottom) + .layoutId(tileTitleId), + text = title, + softWrap = true, + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.primary, + maxLines = 2 + ) + + Image( + modifier = Modifier.layoutId(tileArrowId), + painter = painterResource(id = R.drawable.ic_right_arrow), + contentDescription = null + ) + } + } + } +} + +private fun tileConstraints(): ConstraintSet { + + return ConstraintSet { + val tileIcon = createRefFor(tileIconId) + val tileTitle = createRefFor(tileTitleId) + val tileArrow = createRefFor(tileArrowId) + + constrain(tileIcon) { + start.linkTo(parent.start, 16.dp) + top.linkTo(parent.top, 16.dp) + } + + constrain(tileTitle) { + start.linkTo(parent.start, 16.dp) + bottom.linkTo(parent.bottom, 16.dp) + end.linkTo(tileArrow.start, 16.dp) + width = Dimension.fillToConstraints + } + + constrain(tileArrow) { + bottom.linkTo(tileTitle.bottom) + end.linkTo(parent.end, 16.dp) + } + } +} + +@Composable +@BasePreview +private fun QuickAccessTileItemUIPreview() { + + HealthGatewayTheme { + QuickAccessTileItemUI( + onClick = { /*TODO*/ }, + icon = painterResource(id = R.drawable.ic_tile_healt_resources), + title = stringResource(id = R.string.health_resources) + ) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt new file mode 100644 index 000000000..fea1aead3 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt @@ -0,0 +1,58 @@ +package ca.bc.gov.bchealth.compose.component + +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.component.menu.ActionMenu +import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme + +@Composable +fun HGTopAppBar( + modifier: Modifier = Modifier, + title: String, + actionItems: List = emptyList() +) { + var menuOpen by remember { + mutableStateOf(false) + } + + TopAppBar( + modifier = modifier, + title = { + Text( + title, + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold, + ) + }, + actions = { + ActionMenu( + items = actionItems, + isOpen = menuOpen, + onToggleOverflow = { menuOpen = !menuOpen }, + maxVisibleItems = 3 + ) + }, + backgroundColor = MaterialTheme.colors.surface, + contentColor = MaterialTheme.colors.primary + ) +} + +@Composable +@BasePreview +private fun HGTopAppBarPreview() { + HealthGatewayTheme { + HGTopAppBar(title = stringResource(id = R.string.home)) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/ActionMenu.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/ActionMenu.kt new file mode 100644 index 000000000..d4c61302b --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/ActionMenu.kt @@ -0,0 +1,94 @@ +package ca.bc.gov.bchealth.compose.component.menu + +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import ca.bc.gov.bchealth.R + +@Composable +fun ActionMenu( + items: List, + isOpen: Boolean, + onToggleOverflow: () -> Unit, + maxVisibleItems: Int, +) { + val menuItems = remember( + key1 = items, + key2 = maxVisibleItems, + ) { + splitMenuItems(items, maxVisibleItems) + } + + menuItems.alwaysShownItems.forEach { item -> + IconButton(onClick = item.onClick) { + Icon( + painter = painterResource(id = item.icon), + contentDescription = item.contentDescription, + tint = MaterialTheme.colors.primary + ) + } + } + + if (menuItems.overflowItems.isNotEmpty()) { + IconButton(onClick = onToggleOverflow) { + Icon( + imageVector = Icons.Filled.MoreVert, + contentDescription = stringResource(id = R.string.more_options), + tint = MaterialTheme.colors.primary + ) + } + DropdownMenu( + expanded = isOpen, + onDismissRequest = onToggleOverflow, + ) { + menuItems.overflowItems.forEach { item -> + DropdownMenuItem( + content = { + Text(item.title) + }, + onClick = item.onClick + ) + } + } + } +} + +private data class MenuItems( + val alwaysShownItems: List, + val overflowItems: List, +) + +private fun splitMenuItems( + items: List, + maxVisibleItems: Int, +): MenuItems { + val alwaysShownItems: MutableList = + items.filterIsInstance().toMutableList() + val ifRoomItems: MutableList = + items.filterIsInstance().toMutableList() + val overflowItems = items.filterIsInstance() + + val hasOverflow = overflowItems.isNotEmpty() || + (alwaysShownItems.size + ifRoomItems.size - 1) > maxVisibleItems + val usedSlots = alwaysShownItems.size + (if (hasOverflow) 1 else 0) + val availableSlots = maxVisibleItems - usedSlots + if (availableSlots > 0 && ifRoomItems.isNotEmpty()) { + val visible = ifRoomItems.subList(0, availableSlots.coerceAtMost(ifRoomItems.size)) + alwaysShownItems.addAll(visible) + ifRoomItems.removeAll(visible) + } + + return MenuItems( + alwaysShownItems = alwaysShownItems, + overflowItems = ifRoomItems + overflowItems, + ) +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/TopAppBarActionItem.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/TopAppBarActionItem.kt new file mode 100644 index 000000000..0086589ea --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/menu/TopAppBarActionItem.kt @@ -0,0 +1,31 @@ +package ca.bc.gov.bchealth.compose.component.menu + +import androidx.annotation.DrawableRes + +sealed interface TopAppBarActionItem { + val title: String + val onClick: () -> Unit + + sealed interface IconActionItem : TopAppBarActionItem { + @get:DrawableRes val icon: Int + val contentDescription: String + + data class AlwaysShown( + override val onClick: () -> Unit, + override val title: String, + override val icon: Int, + override val contentDescription: String + ) : IconActionItem + + data class ShowIfRoom( + override val onClick: () -> Unit, + override val title: String, + override val icon: Int, + override val contentDescription: String + ) : IconActionItem + } + data class NeverShown( + override val title: String, + override val onClick: () -> Unit, + ) : TopAppBarActionItem +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt index f91a87675..47d94a1d9 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt @@ -12,6 +12,7 @@ val primaryBlue = Color(0xFF003366) val lightBlue = Color(0xFFB2C1CF) val statusBlue30 = Color(0x4D38598A) val statusBlue = Color(0xFF38598A) +val bannerBackgroundBlue = Color(0xFFD9EAF7) val descriptionGrey = Color(0xFF6D757D) val grey = Color(0xFF606060) diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/FontType.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/FontType.kt new file mode 100644 index 000000000..261b38f13 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/FontType.kt @@ -0,0 +1,67 @@ +package ca.bc.gov.bchealth.compose.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import ca.bc.gov.bchealth.R + +val hgFonts = FontFamily( + Font(R.font.bc_sans_regular, weight = FontWeight.Normal, style = FontStyle.Normal), + Font(R.font.bc_sans_bold, weight = FontWeight.Bold, style = FontStyle.Normal), + Font(R.font.bc_sans_italic, weight = FontWeight.Normal, style = FontStyle.Italic), + Font(R.font.bc_sans_bold_italic, weight = FontWeight.Bold, style = FontStyle.Italic), +) + +internal val HealthGatewayTypography = Typography( + defaultFontFamily = hgFonts, + h1 = TextStyle( + fontSize = 60.sp, + lineHeight = 72.sp, + letterSpacing = 0.sp, + ), + h2 = TextStyle( + fontSize = 50.sp, + lineHeight = 64.sp, + letterSpacing = 0.sp, + ), + h3 = TextStyle( + fontSize = 40.sp, + lineHeight = 56.sp, + letterSpacing = 0.sp, + ), + h4 = TextStyle( + fontSize = 33.sp, + lineHeight = 40.sp, + letterSpacing = 0.sp, + ), + h5 = TextStyle( + fontSize = 24.sp, + lineHeight = 32.sp, + letterSpacing = 0.sp, + ), + subtitle1 = TextStyle( + fontSize = 20.sp, + lineHeight = 38.sp, + letterSpacing = 0.sp, + ), + subtitle2 = TextStyle( + fontSize = 17.sp, + lineHeight = 34.sp, + letterSpacing = 0.sp, + ), + body1 = TextStyle( + fontSize = 15.sp, + lineHeight = 24.sp, + letterSpacing = 0.sp, + ), + body2 = TextStyle( + fontSize = 13.sp, + lineHeight = 22.sp, + fontStyle = FontStyle.Normal, + letterSpacing = 0.sp, + ) +) diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Theme.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Theme.kt new file mode 100644 index 000000000..9ed32ed5d --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Theme.kt @@ -0,0 +1,16 @@ +package ca.bc.gov.bchealth.compose.theme + +import androidx.compose.material.MaterialTheme +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable + +private val LightColors = lightColors( + primary = primaryBlue, + onPrimary = white, + background = white, + surface = white +) + +@Composable +fun HealthGatewayTheme(content: @Composable () -> Unit) = + MaterialTheme(colors = LightColors, typography = HealthGatewayTypography, content = content) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt index ed56648b4..3deb163fb 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt @@ -33,10 +33,10 @@ import androidx.constraintlayout.compose.Dimension import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.blue import ca.bc.gov.bchealth.compose.bold import ca.bc.gov.bchealth.compose.minButtonSize -import ca.bc.gov.bchealth.compose.primaryBlue +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.primaryBlue import ca.bc.gov.bchealth.ui.custom.DecorativeImage import ca.bc.gov.bchealth.utils.fromHtml diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt index a8090f3be..5b86cf0ab 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt @@ -22,8 +22,8 @@ import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography import ca.bc.gov.bchealth.compose.bold -import ca.bc.gov.bchealth.compose.primaryBlue -import ca.bc.gov.bchealth.compose.white +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.white import ca.bc.gov.bchealth.ui.custom.DecorativeImage @Composable diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt new file mode 100644 index 000000000..02074f581 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt @@ -0,0 +1,59 @@ +package ca.bc.gov.bchealth.ui.home + +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.component.HGTopAppBar +import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.ui.BaseSecureFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class HomeComposeFragment : BaseSecureFragment(null) { + + private val viewModel: HomeComposeViewModel by viewModels() + + @Composable + override fun GetComposableLayout() { + val menuItems = listOf( + TopAppBarActionItem.IconActionItem.AlwaysShown( + title = getString(R.string.settings), + onClick = { findNavController().navigate(R.id.settingsFragment) }, + icon = R.drawable.ic_menu_settings, + contentDescription = getString(R.string.settings), + ) + ) + HealthGatewayTheme { + Scaffold( + topBar = { + HGTopAppBar( + title = stringResource(id = R.string.home), + actionItems = menuItems + ) + }, + content = { + HomeScreen( + Modifier + .statusBarsPadding() + .navigationBarsPadding() + .padding(it), + viewModel, + onQuickAccessTileClicked = ::onQuickAccessTileClicked + ) + } + ) + } + } + + private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { + findNavController().navigate(quickAccessTileItem.destinationId) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt new file mode 100644 index 000000000..728efae0e --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -0,0 +1,45 @@ +package ca.bc.gov.bchealth.ui.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class HomeComposeViewModel @Inject constructor( + private val appFeatureWithQuickAccessTilesRepository: AppFeatureWithQuickAccessTilesRepository +) : ViewModel() { + private val _uiState = MutableStateFlow(HomeComposeUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadQuickAccessTiles() = viewModelScope.launch { + val quickAccessTileItems = + appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() + .filter { it.appFeatureDto.isQuickAccessEnabled } + .map { + QuickAccessTileItem( + it.appFeatureDto.featureIconId, + it.appFeatureDto.featureNameId, + it.appFeatureDto.destinationId + ) + } + + _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } + } +} + +data class HomeComposeUiState( + val quickAccessTileItems: List = emptyList() +) + +data class QuickAccessTileItem( + val icon: Int, + val name: Int, + val destinationId: Int +) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt index 00e09831c..36d2f9204 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt @@ -51,6 +51,7 @@ class HomeFragment : BaseSecureFragment(null) { private val viewModel: HomeViewModel by activityViewModels() private val sharedViewModel: SharedViewModel by activityViewModels() private val bcscAuthViewModel: BcscAuthViewModel by viewModels() + private val homeViewModel: HomeComposeViewModel by viewModels() private var logoutResultLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() @@ -125,13 +126,12 @@ class HomeFragment : BaseSecureFragment(null) { .verticalScroll(rememberScrollState()), ) { HomeScreen( - homeUiState.patientFirstName, - bannerUiState, - viewModel::toggleBanner, - viewModel::dismissBanner, - ::onClickLearnMore, - homeItems, - ::navigateToDestination + Modifier + .statusBarsPadding() + .navigationBarsPadding() + .padding(it), + homeViewModel, + onQuickAccessTileClicked = {} ) } }, diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 7a7a28deb..ccf07b7f1 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -1,109 +1,98 @@ package ca.bc.gov.bchealth.ui.home -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview -import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.blue +import ca.bc.gov.bchealth.compose.component.AnnouncementBannerUI +import ca.bc.gov.bchealth.compose.component.LoginInfoCardUI +import ca.bc.gov.bchealth.compose.component.QuickAccessTileItemUI +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme @Composable fun HomeScreen( - patientFirstName: String?, - bannerUiState: BannerItem?, - onClickToggle: () -> Unit, - onClickDismiss: () -> Unit, - onClickLearnMore: (BannerItem) -> Unit, - homeItems: List, - onClickHomeCard: (HomeNavigationType) -> Unit, + modifier: Modifier = Modifier, + viewModel: HomeComposeViewModel, + onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit ) { - val greeting = if (!patientFirstName.isNullOrBlank()) { - stringResource(R.string.hi) - .plus(" ") - .plus(patientFirstName) - .plus(",") - } else { - stringResource(R.string.hello).plus(",") + val uiState = viewModel.uiState.collectAsState().value + LaunchedEffect(key1 = Unit) { + viewModel.loadQuickAccessTiles() } + HomeScreenContent(modifier, onQuickAccessTileClicked, uiState.quickAccessTileItems) +} + +@Composable +private fun HomeScreenContent( + modifier: Modifier = Modifier, + onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit, + quickAccessTileItems: List +) { + LazyVerticalGrid( + columns = GridCells.Fixed(2), + modifier = modifier.fillMaxSize(), + contentPadding = PaddingValues(32.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalArrangement = Arrangement.spacedBy(20.dp) + ) { - Text( - modifier = Modifier.padding(horizontal = 32.dp), - text = greeting, - style = MyHealthTypography.h2, - fontSize = 28.sp, - color = MaterialTheme.colors.primary - ) - Spacer(modifier = Modifier.height(8.dp)) + item(span = { GridItemSpan(maxLineSpan) }) { + AnnouncementBannerUI( + title = stringResource(id = R.string.home_banner_toolbar_title), + description = stringResource(id = R.string.home_banner_toolbar_title), + onLearnMoreClick = { /*TODO*/ }, + onDismissClick = { /*TODO*/ } + ) + } - Text( - modifier = Modifier.padding(horizontal = 32.dp), - text = stringResource(id = R.string.home_subtitle), - style = MyHealthTypography.h2, - fontSize = 20.sp, - color = blue - ) - Spacer(modifier = Modifier.height(16.dp)) + item(span = { GridItemSpan(maxLineSpan) }) { + LoginInfoCardUI( + onClick = { /*TODO*/ }, + title = stringResource(id = R.string.log_in_with_bc_services_card), + subTitle = stringResource(id = R.string.login_to_view_hidden_records_msg) + ) + } - bannerUiState?.let { - BannerUI(it, onClickToggle, onClickLearnMore, onClickDismiss) - } + item(span = { GridItemSpan(maxLineSpan) }) { + Text( + text = stringResource(id = R.string.quick_access), + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary + ) + } - homeItems.forEach { - HomeCardUI(uiItem = it, onClickItem = { onClickHomeCard.invoke(it.recordType) }) + items(quickAccessTileItems) { + QuickAccessTileItemUI( + onClick = { onQuickAccessTileClicked(it) }, + icon = painterResource(id = it.icon), + title = stringResource( + it.name + ) + ) + } } } -@BasePreview @Composable -private fun PreviewHomeScreen() { - HomeScreen( - "Bruno", - bannerUiState = BannerItem( - title = "Great news! Really Big Announcement", - body = "View and manage all your available health records, including dispensed medications, health visits, COVID-19 test results, immunizations and more.", - date = "", - displayReadMore = true, - isHidden = false, - ), - onClickToggle = {}, - onClickLearnMore = {}, - onClickDismiss = {}, - homeItems = listOf( - HomeRecordItem( - iconTitle = R.drawable.ic_login_info, - title = R.string.recommendations_home_title, - description = R.string.home_recommendations_body, - icon = R.drawable.ic_right_arrow, - btnTitle = R.string.get_started, - recordType = HomeNavigationType.RECOMMENDATIONS - - ), - HomeRecordItem( - iconTitle = R.drawable.ic_login_info, - title = R.string.recommendations_home_title, - description = R.string.home_recommendations_body, - icon = R.drawable.ic_right_arrow, - btnTitle = R.string.get_started, - recordType = HomeNavigationType.RECOMMENDATIONS - - ), - HomeRecordItem( - iconTitle = R.drawable.ic_login_info, - title = R.string.recommendations_home_title, - description = R.string.home_recommendations_body, - icon = R.drawable.ic_right_arrow, - btnTitle = R.string.get_started, - recordType = HomeNavigationType.RECOMMENDATIONS - ) - ), - onClickHomeCard = {} - ) +@BasePreview +private fun HomeScreenPreview() { + HealthGatewayTheme { + HomeScreenContent(onQuickAccessTileClicked = {}, quickAccessTileItems = emptyList()) + } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt index b36eeafe6..15e263efd 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt @@ -22,7 +22,7 @@ import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme -import ca.bc.gov.bchealth.compose.primaryBlue +import ca.bc.gov.bchealth.compose.theme.primaryBlue import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.ui.BaseViewModel import ca.bc.gov.bchealth.ui.auth.BCServicesCardSessionContent diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationScreen.kt index c5e171586..2f87e3d98 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationScreen.kt @@ -37,10 +37,10 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.grey -import ca.bc.gov.bchealth.compose.greyBg import ca.bc.gov.bchealth.compose.minButtonSize -import ca.bc.gov.bchealth.compose.primaryBlue +import ca.bc.gov.bchealth.compose.theme.grey +import ca.bc.gov.bchealth.compose.theme.greyBg +import ca.bc.gov.bchealth.compose.theme.primaryBlue import ca.bc.gov.bchealth.ui.component.HGLargeButton import ca.bc.gov.bchealth.ui.custom.DecorativeImage import ca.bc.gov.common.model.notification.NotificationActionTypeDto diff --git a/app/src/main/res/drawable/ic_tile_healt_resources.xml b/app/src/main/res/drawable/ic_tile_healt_resources.xml new file mode 100644 index 000000000..9731723ad --- /dev/null +++ b/app/src/main/res/drawable/ic_tile_healt_resources.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_tile_immunization_schedules.xml b/app/src/main/res/drawable/ic_tile_immunization_schedules.xml new file mode 100644 index 000000000..c812d2fec --- /dev/null +++ b/app/src/main/res/drawable/ic_tile_immunization_schedules.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_tile_proof_of_vaccine.xml b/app/src/main/res/drawable/ic_tile_proof_of_vaccine.xml new file mode 100644 index 000000000..f5b718541 --- /dev/null +++ b/app/src/main/res/drawable/ic_tile_proof_of_vaccine.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/icon_tile_health_record.xml b/app/src/main/res/drawable/icon_tile_health_record.xml new file mode 100644 index 000000000..3f92b8ed4 --- /dev/null +++ b/app/src/main/res/drawable/icon_tile_health_record.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/img_un_authenticated_home_screen.xml b/app/src/main/res/drawable/img_un_authenticated_home_screen.xml new file mode 100644 index 000000000..b2ba52dec --- /dev/null +++ b/app/src/main/res/drawable/img_un_authenticated_home_screen.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/navigation/home.xml b/app/src/main/res/navigation/home.xml index 59247c879..c813a521c 100644 --- a/app/src/main/res/navigation/home.xml +++ b/app/src/main/res/navigation/home.xml @@ -2,9 +2,31 @@ + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5f95df2e5..16345288e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -484,6 +484,8 @@ Learn more What do you want to focus on today? New updates + Quick access + Immunization schedules Terms of service @@ -587,4 +589,6 @@ Health Authority: Facility: + + More Options \ No newline at end of file diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt new file mode 100644 index 000000000..ac0c78860 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt @@ -0,0 +1,10 @@ +package ca.bc.gov.common.model.settings + +data class AppFeatureDto( + val id: Long = 0, + val featureNameId: Int, + val featureIconId: Int, + val destinationId: Int, + val isEnabled: Boolean = false, + val isQuickAccessEnabled: Boolean = false +) diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt new file mode 100644 index 000000000..815bcad36 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt @@ -0,0 +1,6 @@ +package ca.bc.gov.common.model.settings + +data class AppFeatureWithQuickAccessTilesDto( + val appFeatureDto: AppFeatureDto, + val quickAccessTiles: List +) diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt new file mode 100644 index 000000000..d43409d47 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt @@ -0,0 +1,9 @@ +package ca.bc.gov.common.model.settings + +data class QuickAccessTileDto( + val id: Long = 0, + val featureId: Long = 0, + val titleNameId: Int, + val titleIconId: Int, + val isEnabled: Boolean = false +) diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json index 499f8f603..b5a085fbd 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 14, - "identityHash": "cff70079a468f7fab21b2c8957aa411e", + "identityHash": "cd63a7ddb38d8002c82af9bc1156c3dc", "entities": [ { "tableName": "patient", @@ -1997,12 +1997,132 @@ }, "indices": [], "foreignKeys": [] + }, + { + "tableName": "app_feature", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name_id` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "featureNameId", + "columnName": "feature_name_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "featureIconId", + "columnName": "feature_icon_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "destinationId", + "columnName": "destination_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "isQuickAccessEnabled", + "columnName": "quick_access_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_app_feature_feature_name_id_feature_icon_id", + "unique": true, + "columnNames": [ + "feature_name_id", + "feature_icon_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_app_feature_feature_name_id_feature_icon_id` ON `${TABLE_NAME}` (`feature_name_id`, `feature_icon_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "quick_access_tile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_id` INTEGER NOT NULL, `tile_name_id` INTEGER NOT NULL, `tile_icon_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, FOREIGN KEY(`feature_id`) REFERENCES `app_feature`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "featureId", + "columnName": "feature_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tileNameId", + "columnName": "tile_name_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tileIconId", + "columnName": "tile_icon_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "app_feature", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "feature_id" + ], + "referencedColumns": [ + "id" + ] + } + ] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cff70079a468f7fab21b2c8957aa411e')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cd63a7ddb38d8002c82af9bc1156c3dc')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt new file mode 100644 index 000000000..b791475f3 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt @@ -0,0 +1,21 @@ +package ca.bc.gov.data.datasource.local + +import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.AppFeatureWithQuickAccessTilesDto +import ca.bc.gov.data.datasource.local.dao.AppFeatureDao +import ca.bc.gov.data.model.mapper.toDto +import ca.bc.gov.data.model.mapper.toEntity +import javax.inject.Inject + +class AppFeatureLocalDataSource @Inject constructor( + private val appFeatureDao: AppFeatureDao +) { + + suspend fun insert(appFeatureDto: AppFeatureDto): Long { + return appFeatureDao.insert(appFeatureDto.toEntity()) + } + + suspend fun getAppFeaturesWithQuickAccessTiles(): List { + return appFeatureDao.getAllFeatureWithQuickAccessTiles().map { it.toDto() } + } +} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt index 271141d43..7d65f74b5 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt @@ -9,6 +9,7 @@ import ca.bc.gov.data.datasource.local.converter.AuthenticationStatusTypeConvert import ca.bc.gov.data.datasource.local.converter.DateTimeConverter import ca.bc.gov.data.datasource.local.converter.PatientNameConverter import ca.bc.gov.data.datasource.local.converter.SyncStatusConverter +import ca.bc.gov.data.datasource.local.dao.AppFeatureDao import ca.bc.gov.data.datasource.local.dao.ClinicalDocumentDao import ca.bc.gov.data.datasource.local.dao.CommentDao import ca.bc.gov.data.datasource.local.dao.CovidOrderDao @@ -29,6 +30,7 @@ import ca.bc.gov.data.datasource.local.dao.MedicationSummaryDao import ca.bc.gov.data.datasource.local.dao.NotificationDao import ca.bc.gov.data.datasource.local.dao.OrganDonorDao import ca.bc.gov.data.datasource.local.dao.PatientDao +import ca.bc.gov.data.datasource.local.dao.QuickAccessTileDao import ca.bc.gov.data.datasource.local.dao.SpecialAuthorityDao import ca.bc.gov.data.datasource.local.dao.UserProfileDao import ca.bc.gov.data.datasource.local.dao.VaccineRecordDao @@ -54,6 +56,8 @@ import ca.bc.gov.data.datasource.local.entity.medication.MedicationSummaryEntity import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity +import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity @@ -87,6 +91,8 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity OrganDonorEntity::class, DiagnosticImagingDataEntity::class, NotificationEntity::class, + AppFeatureEntity::class, + QuickAccessTileEntity::class ], autoMigrations = [ AutoMigration(from = 8, to = 9), @@ -153,4 +159,8 @@ abstract class MyHealthDataBase : RoomDatabase() { abstract fun getDiagnosticImagingDataDao(): DiagnosticImagingDataDao abstract fun getNotificationDao(): NotificationDao + + abstract fun getAppFeatureDao(): AppFeatureDao + + abstract fun getQuickAccessTileDao(): QuickAccessTileDao } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt new file mode 100644 index 000000000..506d63e4d --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt @@ -0,0 +1,15 @@ +package ca.bc.gov.data.datasource.local + +import ca.bc.gov.common.model.settings.QuickAccessTileDto +import ca.bc.gov.data.datasource.local.dao.QuickAccessTileDao +import ca.bc.gov.data.model.mapper.toEntity +import javax.inject.Inject + +class QuickActionTileLocalDataSource @Inject constructor( + private val quickAccessTileDao: QuickAccessTileDao +) { + + suspend fun insert(quickAccessTileDto: QuickAccessTileDto): Long { + return quickAccessTileDao.insert(quickAccessTileDto.toEntity()) + } +} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt new file mode 100644 index 000000000..e924d43c2 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt @@ -0,0 +1,21 @@ +package ca.bc.gov.data.datasource.local.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import ca.bc.gov.data.datasource.local.entity.relations.AppFeatureWithQuickAccessTiles +import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity + +@Dao +interface AppFeatureDao { + + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insert(appFeatureEntity: AppFeatureEntity): Long + + @Query("DELETE FROM app_feature") + suspend fun deleteAll() + + @Query("SELECT * FROM app_feature") + suspend fun getAllFeatureWithQuickAccessTiles(): List +} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt new file mode 100644 index 000000000..42092ffc0 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt @@ -0,0 +1,7 @@ +package ca.bc.gov.data.datasource.local.dao + +import androidx.room.Dao +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity + +@Dao +interface QuickAccessTileDao : BaseDao diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt new file mode 100644 index 000000000..b27a645ac --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt @@ -0,0 +1,17 @@ +package ca.bc.gov.data.datasource.local.entity.relations + +import androidx.room.Embedded +import androidx.room.Relation +import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity + +data class AppFeatureWithQuickAccessTiles( + @Embedded + val appFeature: AppFeatureEntity, + + @Relation( + parentColumn = "id", + entityColumn = "feature_id" + ) + val quickAccessTiles: List = emptyList() +) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt new file mode 100644 index 000000000..2df369383 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt @@ -0,0 +1,25 @@ +package ca.bc.gov.data.datasource.local.entity.settings + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = "app_feature", + indices = [Index(value = ["feature_name_id", "feature_icon_id"], unique = true)] +) +data class AppFeatureEntity( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + @ColumnInfo(name = "feature_name_id") + val featureNameId: Int, + @ColumnInfo(name = "feature_icon_id") + val featureIconId: Int, + @ColumnInfo(name = "destination_id") + val destinationId: Int, + @ColumnInfo(name = "enabled", defaultValue = "false") + val isEnabled: Boolean = false, + @ColumnInfo(name = "quick_access_enabled", defaultValue = "false") + val isQuickAccessEnabled: Boolean = false +) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt new file mode 100644 index 000000000..49ffbb807 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt @@ -0,0 +1,30 @@ +package ca.bc.gov.data.datasource.local.entity.settings + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity( + tableName = "quick_access_tile", + foreignKeys = [ + androidx.room.ForeignKey( + entity = AppFeatureEntity::class, + parentColumns = ["id"], + childColumns = ["feature_id"], + onDelete = androidx.room.ForeignKey.CASCADE, + onUpdate = androidx.room.ForeignKey.CASCADE + ) + ] +) +data class QuickAccessTileEntity( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + @ColumnInfo(name = "feature_id") + val featureId: Long, + @ColumnInfo(name = "tile_name_id") + val tileNameId: Int, + @ColumnInfo(name = "tile_icon_id") + val tileIconId: Int, + @ColumnInfo(name = "enabled", defaultValue = "false") + val isEnabled: Boolean = false +) diff --git a/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt b/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt index 14093968f..a92657645 100644 --- a/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt +++ b/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt @@ -1,5 +1,6 @@ package ca.bc.gov.data.di +import ca.bc.gov.data.datasource.local.AppFeatureLocalDataSource import ca.bc.gov.data.datasource.local.ClinicalDocumentLocalDataSource import ca.bc.gov.data.datasource.local.CommentLocalDataSource import ca.bc.gov.data.datasource.local.CovidOrderLocalDataSource @@ -19,6 +20,7 @@ import ca.bc.gov.data.datasource.local.MyHealthDataBase import ca.bc.gov.data.datasource.local.NotificationLocalDataSource import ca.bc.gov.data.datasource.local.OrganDonorLocalDataSource import ca.bc.gov.data.datasource.local.PatientLocalDataSource +import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource import ca.bc.gov.data.datasource.local.SpecialAuthorityLocalDataSource import ca.bc.gov.data.datasource.local.UserProfileLocalDataSource import ca.bc.gov.data.datasource.local.VaccineRecordLocalDataSource @@ -153,4 +155,14 @@ class LocalDataSourceModule { @Singleton fun providesDiagnosticImagingLocalDataSource(db: MyHealthDataBase): DiagnosticImagingLocalDataSource = DiagnosticImagingLocalDataSource(db.getDiagnosticImagingDataDao()) + + @Provides + @Singleton + fun provideAppFeatureLocalDataSource(db: MyHealthDataBase): AppFeatureLocalDataSource = + AppFeatureLocalDataSource(db.getAppFeatureDao()) + + @Provides + @Singleton + fun provideQuickAccessTileLocalDataSource(db: MyHealthDataBase): QuickActionTileLocalDataSource = + QuickActionTileLocalDataSource(db.getQuickAccessTileDao()) } diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt index 5d272bcb1..dafafe440 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt @@ -22,6 +22,8 @@ import ca.bc.gov.common.model.patient.PatientDto import ca.bc.gov.common.model.patient.PatientNameDto import ca.bc.gov.common.model.services.DiagnosticImagingDataDto import ca.bc.gov.common.model.services.OrganDonorDto +import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.common.model.specialauthority.SpecialAuthorityDto import ca.bc.gov.common.model.test.CovidOrderDto import ca.bc.gov.common.model.test.CovidTestDto @@ -50,6 +52,8 @@ import ca.bc.gov.data.datasource.local.entity.medication.MedicationSummaryEntity import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity +import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity @@ -356,3 +360,20 @@ fun NotificationDto.toEntity() = NotificationEntity( actionType = actionType.value, date = date, ) + +fun AppFeatureDto.toEntity() = AppFeatureEntity( + id, + featureNameId, + featureIconId, + destinationId, + isEnabled, + isQuickAccessEnabled +) + +fun QuickAccessTileDto.toEntity() = QuickAccessTileEntity( + id, + featureId, + titleNameId, + titleIconId, + isEnabled +) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt index 56618001e..acbe1103c 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt @@ -41,6 +41,9 @@ import ca.bc.gov.common.model.relation.PatientWithVaccineAndDosesDto import ca.bc.gov.common.model.relation.VaccineWithDosesDto import ca.bc.gov.common.model.services.DiagnosticImagingDataDto import ca.bc.gov.common.model.services.OrganDonorDto +import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.AppFeatureWithQuickAccessTilesDto +import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.common.model.specialauthority.SpecialAuthorityDto import ca.bc.gov.common.model.test.CovidOrderDto import ca.bc.gov.common.model.test.CovidOrderWithCovidTestAndPatientDto @@ -75,6 +78,7 @@ import ca.bc.gov.data.datasource.local.entity.medication.DispensingPharmacyEntit import ca.bc.gov.data.datasource.local.entity.medication.MedicationRecordEntity import ca.bc.gov.data.datasource.local.entity.medication.MedicationSummaryEntity import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity +import ca.bc.gov.data.datasource.local.entity.relations.AppFeatureWithQuickAccessTiles import ca.bc.gov.data.datasource.local.entity.relations.MedicationWithSummaryAndPharmacy import ca.bc.gov.data.datasource.local.entity.relations.PatientWithClinicalDocuments import ca.bc.gov.data.datasource.local.entity.relations.PatientWithCovidOrderAndCovidTest @@ -89,6 +93,8 @@ import ca.bc.gov.data.datasource.local.entity.relations.PatientWithVaccineAndDos import ca.bc.gov.data.datasource.local.entity.relations.VaccineRecordWithDose import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity +import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity import java.time.Instant @@ -475,3 +481,25 @@ fun NotificationEntity.toDto() = NotificationDto( actionType = NotificationActionTypeDto.getByValue(actionType), date = date, ) + +fun AppFeatureEntity.toDto() = AppFeatureDto( + id, + featureNameId, + featureIconId, + destinationId, + isEnabled, + isQuickAccessEnabled +) + +fun QuickAccessTileEntity.toDto() = QuickAccessTileDto( + id, + featureId, + tileNameId, + tileIconId, + isEnabled +) + +fun AppFeatureWithQuickAccessTiles.toDto() = AppFeatureWithQuickAccessTilesDto( + appFeature.toDto(), + quickAccessTiles.map { it.toDto() } +) diff --git a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt index 9233bf6db..818de0fe4 100644 --- a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt +++ b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt @@ -1,6 +1,7 @@ package ca.bc.gov.repository.di import android.content.Context +import ca.bc.gov.data.datasource.local.AppFeatureLocalDataSource import ca.bc.gov.data.datasource.local.CommentLocalDataSource import ca.bc.gov.data.datasource.local.CovidOrderLocalDataSource import ca.bc.gov.data.datasource.local.CovidTestLocalDataSource @@ -15,6 +16,7 @@ import ca.bc.gov.data.datasource.local.MedicationRecordLocalDataSource import ca.bc.gov.data.datasource.local.NotificationLocalDataSource import ca.bc.gov.data.datasource.local.OrganDonorLocalDataSource import ca.bc.gov.data.datasource.local.PatientLocalDataSource +import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource import ca.bc.gov.data.datasource.local.VaccineRecordLocalDataSource import ca.bc.gov.data.datasource.remote.BannerRemoteDataSource import ca.bc.gov.data.datasource.remote.CommentRemoteDataSource @@ -56,6 +58,9 @@ import ca.bc.gov.repository.scanner.QrScanner import ca.bc.gov.repository.services.DiagnosticImagingRepository import ca.bc.gov.repository.services.OrganDonorRepository import ca.bc.gov.repository.services.PatientServicesRepository +import ca.bc.gov.repository.settings.AppFeatureRepository +import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository +import ca.bc.gov.repository.settings.QuickAccessTileRepository import ca.bc.gov.repository.testrecord.CovidOrderRepository import ca.bc.gov.repository.testrecord.CovidTestRepository import ca.bc.gov.repository.utils.Base64ToInputImageConverter @@ -348,4 +353,23 @@ class RepositoriesModule { fun provideDiagnosticImagingRepository( localDataSource: DiagnosticImagingLocalDataSource ): DiagnosticImagingRepository = DiagnosticImagingRepository(localDataSource) + + @Provides + @Singleton + fun provideAppFeatureRepository( + appFeatureLocalDataSource: AppFeatureLocalDataSource + ): AppFeatureRepository = AppFeatureRepository(appFeatureLocalDataSource) + + @Provides + @Singleton + fun providesQuickAccessTileRepository( + quickActionTileLocalDataSource: QuickActionTileLocalDataSource + ): QuickAccessTileRepository = QuickAccessTileRepository(quickActionTileLocalDataSource) + + @Provides + @Singleton + fun providesAppFeatureWithQuickAccessTilesRepository( + appFeatureRepository: AppFeatureRepository + ): AppFeatureWithQuickAccessTilesRepository = + AppFeatureWithQuickAccessTilesRepository(appFeatureRepository) } diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt new file mode 100644 index 000000000..6dad4ac46 --- /dev/null +++ b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt @@ -0,0 +1,16 @@ +package ca.bc.gov.repository.settings + +import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.data.datasource.local.AppFeatureLocalDataSource +import javax.inject.Inject + +class AppFeatureRepository @Inject constructor( + private val appFeatureLocalDataSource: AppFeatureLocalDataSource +) { + + suspend fun insert(appFeatureDto: AppFeatureDto) { + appFeatureLocalDataSource.insert(appFeatureDto) + } + + suspend fun getAppFeaturesWithQuickAccessTiles() = appFeatureLocalDataSource.getAppFeaturesWithQuickAccessTiles() +} diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt new file mode 100644 index 000000000..6ffb815f7 --- /dev/null +++ b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt @@ -0,0 +1,10 @@ +package ca.bc.gov.repository.settings + +import javax.inject.Inject + +class AppFeatureWithQuickAccessTilesRepository @Inject constructor( + private val appFeatureRepository: AppFeatureRepository +) { + + suspend fun getAppFeaturesWithQuickAccessTiles() = appFeatureRepository.getAppFeaturesWithQuickAccessTiles() +} diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt new file mode 100644 index 000000000..5527130d6 --- /dev/null +++ b/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt @@ -0,0 +1,8 @@ +package ca.bc.gov.repository.settings + +import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource +import javax.inject.Inject + +class QuickAccessTileRepository @Inject constructor( + private val quickActionTileLocalDataSource: QuickActionTileLocalDataSource +) diff --git a/scripts/versions.gradle b/scripts/versions.gradle index 5dd9abc7e..a2ed54cb7 100644 --- a/scripts/versions.gradle +++ b/scripts/versions.gradle @@ -25,6 +25,7 @@ versions.room = "2.5.1" versions.material = "1.8.0" versions.recycler = "1.3.0" versions.coordinator = "1.2.0" +versions.constraint_compose = "1.0.1" versions.navigation = "2.5.3" From 7f15b5251409783fa6e8a1c72c0025aa3e3f727e Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 5 Jul 2023 10:19:27 -0700 Subject: [PATCH 03/38] HAPP-1536: Immz schedule UI --- .../bc/gov/bchealth/compose/theme/Colors.kt | 2 + .../ImmunizationSchedulesFragment.kt | 50 +++++++++ .../ImmunizationSchedulesScreen.kt | 101 ++++++++++++++++++ .../ImmunizationSchedulesViewModel.kt | 33 ++++++ .../main/res/drawable/ic_arrow_right_24.xml | 13 +++ .../ic_immnz_schedules_adult_seniors.xml | 13 +++ .../drawable/ic_immnz_schedules_infant.xml | 13 +++ .../ic_immnz_schedules_school_age.xml | 13 +++ app/src/main/res/navigation/home.xml | 5 + app/src/main/res/values/configs.xml | 5 + app/src/main/res/values/strings.xml | 7 ++ 11 files changed, 255 insertions(+) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt create mode 100644 app/src/main/res/drawable/ic_arrow_right_24.xml create mode 100644 app/src/main/res/drawable/ic_immnz_schedules_adult_seniors.xml create mode 100644 app/src/main/res/drawable/ic_immnz_schedules_infant.xml create mode 100644 app/src/main/res/drawable/ic_immnz_schedules_school_age.xml diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt index 47d94a1d9..a963decb5 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt @@ -20,3 +20,5 @@ val greyBg = Color(0xFFF2F2F2) val green = Color(0xFF2E8540) val red = Color(0xFFD8292F) + +val bannerInfoBg = Color(0xFFD9EAF7) \ No newline at end of file diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt new file mode 100644 index 000000000..daa172ca1 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt @@ -0,0 +1,50 @@ +package ca.bc.gov.bchealth.ui.home.immunizationschedules + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.fragment.app.viewModels +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.MyHealthTheme +import ca.bc.gov.bchealth.ui.BaseFragment +import ca.bc.gov.bchealth.ui.custom.MyHealthToolbar +import ca.bc.gov.bchealth.utils.redirect + +class ImmunizationSchedulesFragment : BaseFragment(null) { + + private val viewModel: ImmunizationSchedulesViewModel by viewModels() + + @Composable + override fun GetComposableLayout() { + val uiList = viewModel.getUiList() + MyHealthTheme { + Scaffold( + topBar = { + MyHealthToolbar( + title = stringResource(id = R.string.immnz_schedules_title), + navigationAction = ::popNavigation + ) + }, + content = { + Box( + modifier = Modifier + .statusBarsPadding() + .navigationBarsPadding() + .padding(it) + ) { + ImmunizationSchedulesScreen(uiList, ::onClickUrl) + } + }, + ) + } + } + + private fun onClickUrl(url: String) { + context?.redirect(url) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt new file mode 100644 index 000000000..0f39cdc97 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt @@ -0,0 +1,101 @@ +package ca.bc.gov.bchealth.ui.home.immunizationschedules + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.MyHealthTypography +import ca.bc.gov.bchealth.compose.bold +import ca.bc.gov.bchealth.compose.theme.bannerInfoBg +import ca.bc.gov.bchealth.compose.theme.white +import ca.bc.gov.bchealth.ui.custom.DecorativeImage +import ca.bc.gov.bchealth.ui.home.immunizationschedules.ImmunizationSchedulesViewModel.ImmunizationSchedulesItem + +@Composable +fun ImmunizationSchedulesScreen( + uiList: List, + onClickItem: (String) -> Unit +) { + LazyColumn( + contentPadding = PaddingValues(horizontal = 32.dp, vertical = 22.dp), + content = { + item { + Text( + modifier = Modifier.padding(bottom = 16.dp), + text = stringResource(id = R.string.immnz_schedules_body), + style = MyHealthTypography.caption + ) + } + items(uiList) { + ImmunizationScheduleUI(it, onClickItem) + } + } + ) +} + +@Composable +private fun ImmunizationScheduleUI(item: ImmunizationSchedulesItem, onClickItem: (String) -> Unit) { + val url = stringResource(id = item.url) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + .shadow(elevation = 8.dp, shape = RoundedCornerShape(4.dp)) + .clip(RoundedCornerShape(4.dp)) + .clickable { onClickItem.invoke(url) } + .background(white) + .padding(start = 10.dp, end = 22.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .padding(top = 16.dp, bottom = 10.dp) + .size(48.dp) + .clip(RoundedCornerShape(4.dp)) + .background(bannerInfoBg), + contentAlignment = Alignment.Center + ) { + + DecorativeImage(resourceId = item.icon) + } + + Text( + modifier = Modifier + .padding(start = 22.dp) + .fillMaxWidth() + .weight(1f), + style = MyHealthTypography.body2.bold(), + text = stringResource(id = item.title) + ) + DecorativeImage( + resourceId = R.drawable.ic_arrow_right_24 + ) + } +} + +@BasePreview +@Composable +private fun PreviewImmunizationSchedulesScreen() { + val viewModel = ImmunizationSchedulesViewModel() + ImmunizationSchedulesScreen( + viewModel.getUiList() + ) {} +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt new file mode 100644 index 000000000..0d15cc2e1 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt @@ -0,0 +1,33 @@ +package ca.bc.gov.bchealth.ui.home.immunizationschedules + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.lifecycle.ViewModel +import ca.bc.gov.bchealth.R + +class ImmunizationSchedulesViewModel : ViewModel() { + + fun getUiList(): List = listOf( + ImmunizationSchedulesItem( + R.drawable.ic_immnz_schedules_infant, + R.string.immnz_schedules_infant, + R.string.url_immnz_schedules_infant, + ), + ImmunizationSchedulesItem( + R.drawable.ic_immnz_schedules_school_age, + R.string.immnz_schedules_school_age, + R.string.url_immnz_schedules_school_age, + ), + ImmunizationSchedulesItem( + R.drawable.ic_immnz_schedules_adult_seniors, + R.string.immnz_schedules_adult_seniors, + R.string.url_immnz_schedules_adult_seniors, + ) + ) + + data class ImmunizationSchedulesItem( + @DrawableRes val icon: Int, + @StringRes val title: Int, + @StringRes val url: Int, + ) +} diff --git a/app/src/main/res/drawable/ic_arrow_right_24.xml b/app/src/main/res/drawable/ic_arrow_right_24.xml new file mode 100644 index 000000000..54425b6b9 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_right_24.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_immnz_schedules_adult_seniors.xml b/app/src/main/res/drawable/ic_immnz_schedules_adult_seniors.xml new file mode 100644 index 000000000..28f8dd5ca --- /dev/null +++ b/app/src/main/res/drawable/ic_immnz_schedules_adult_seniors.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_immnz_schedules_infant.xml b/app/src/main/res/drawable/ic_immnz_schedules_infant.xml new file mode 100644 index 000000000..7793164d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_immnz_schedules_infant.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_immnz_schedules_school_age.xml b/app/src/main/res/drawable/ic_immnz_schedules_school_age.xml new file mode 100644 index 000000000..40ca6419c --- /dev/null +++ b/app/src/main/res/drawable/ic_immnz_schedules_school_age.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/navigation/home.xml b/app/src/main/res/navigation/home.xml index c813a521c..cff583cb2 100644 --- a/app/src/main/res/navigation/home.xml +++ b/app/src/main/res/navigation/home.xml @@ -65,6 +65,11 @@ app:argType="string" /> + + diff --git a/app/src/main/res/values/configs.xml b/app/src/main/res/values/configs.xml index e986277c2..2c7127316 100644 --- a/app/src/main/res/values/configs.xml +++ b/app/src/main/res/values/configs.xml @@ -6,6 +6,11 @@ https://news.gov.bc.ca/news-subscribe/covid-19/feed https://immunizebc.ca/ + + https://www.healthlinkbc.ca/bc-immunization-schedules#child + https://www.healthlinkbc.ca/bc-immunization-schedules#school + https://www.healthlinkbc.ca/bc-immunization-schedules#adult + https://immunizationrecord.gov.bc.ca/ https://www.healthlinkbc.ca/ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 16345288e..c7a7e495a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -487,6 +487,13 @@ Quick access Immunization schedules + + Immunization Schedules + Click on the tabs below to expand and view individual immunization schedules for different age groups and individuals at high risk. + Infant and children + School age children + Adult, seniors and individual with high risk + Terms of service Agree From c51b8260e53bf13fcb6c0770f3c8908ebaeab5fb Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 5 Jul 2023 12:02:10 -0700 Subject: [PATCH 04/38] Fix merge issues --- app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt index a963decb5..e9f28bb2b 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt @@ -21,4 +21,4 @@ val greyBg = Color(0xFFF2F2F2) val green = Color(0xFF2E8540) val red = Color(0xFFD8292F) -val bannerInfoBg = Color(0xFFD9EAF7) \ No newline at end of file +val bannerInfoBg = Color(0xFFD9EAF7) From 6330fbf1f55e6856d4fa5d771406176c8ea2bc67 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 5 Jul 2023 14:34:35 -0700 Subject: [PATCH 05/38] HAPP-1535: PR comments - Uses Card component - Receives modifier - Uses Arrangement spacedBy --- .../ImmunizationSchedulesFragment.kt | 9 +-- .../ImmunizationSchedulesScreen.kt | 74 ++++++++++--------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt index daa172ca1..d2b27811f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt @@ -1,6 +1,5 @@ package ca.bc.gov.bchealth.ui.home.immunizationschedules -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding @@ -31,14 +30,14 @@ class ImmunizationSchedulesFragment : BaseFragment(null) { ) }, content = { - Box( + ImmunizationSchedulesScreen( + uiList = uiList, + onClickItem = ::onClickUrl, modifier = Modifier .statusBarsPadding() .navigationBarsPadding() .padding(it) - ) { - ImmunizationSchedulesScreen(uiList, ::onClickUrl) - } + ) }, ) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt index 0f39cdc97..917dcf24b 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt @@ -2,6 +2,7 @@ package ca.bc.gov.bchealth.ui.home.immunizationschedules import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -11,12 +12,12 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.shadow import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R @@ -31,14 +32,17 @@ import ca.bc.gov.bchealth.ui.home.immunizationschedules.ImmunizationSchedulesVie @Composable fun ImmunizationSchedulesScreen( uiList: List, - onClickItem: (String) -> Unit + onClickItem: (String) -> Unit, + modifier: Modifier = Modifier ) { LazyColumn( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp), contentPadding = PaddingValues(horizontal = 32.dp, vertical = 22.dp), content = { item { Text( - modifier = Modifier.padding(bottom = 16.dp), + modifier = Modifier.padding(bottom = 4.dp), text = stringResource(id = R.string.immnz_schedules_body), style = MyHealthTypography.caption ) @@ -54,40 +58,42 @@ fun ImmunizationSchedulesScreen( private fun ImmunizationScheduleUI(item: ImmunizationSchedulesItem, onClickItem: (String) -> Unit) { val url = stringResource(id = item.url) - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 6.dp) - .shadow(elevation = 8.dp, shape = RoundedCornerShape(4.dp)) - .clip(RoundedCornerShape(4.dp)) - .clickable { onClickItem.invoke(url) } - .background(white) - .padding(start = 10.dp, end = 22.dp), - verticalAlignment = Alignment.CenterVertically + Card( + modifier = Modifier.clickable { onClickItem.invoke(url) }, + shape = RoundedCornerShape(4.dp), + backgroundColor = white, + elevation = 8.dp, ) { - Box( + Row( modifier = Modifier - .padding(top = 16.dp, bottom = 10.dp) - .size(48.dp) - .clip(RoundedCornerShape(4.dp)) - .background(bannerInfoBg), - contentAlignment = Alignment.Center + .fillMaxWidth() + .padding(start = 10.dp, end = 22.dp), + verticalAlignment = Alignment.CenterVertically ) { + Box( + modifier = Modifier + .padding(top = 16.dp, bottom = 10.dp) + .size(48.dp) + .clip(RoundedCornerShape(4.dp)) + .background(bannerInfoBg), + contentAlignment = Alignment.Center + ) { - DecorativeImage(resourceId = item.icon) - } + DecorativeImage(resourceId = item.icon) + } - Text( - modifier = Modifier - .padding(start = 22.dp) - .fillMaxWidth() - .weight(1f), - style = MyHealthTypography.body2.bold(), - text = stringResource(id = item.title) - ) - DecorativeImage( - resourceId = R.drawable.ic_arrow_right_24 - ) + Text( + modifier = Modifier + .padding(start = 22.dp) + .fillMaxWidth() + .weight(1f), + style = MyHealthTypography.body2.bold(), + text = stringResource(id = item.title) + ) + DecorativeImage( + resourceId = R.drawable.ic_arrow_right_24 + ) + } } } @@ -96,6 +102,6 @@ private fun ImmunizationScheduleUI(item: ImmunizationSchedulesItem, onClickItem: private fun PreviewImmunizationSchedulesScreen() { val viewModel = ImmunizationSchedulesViewModel() ImmunizationSchedulesScreen( - viewModel.getUiList() - ) {} + viewModel.getUiList(), {} + ) } From 8a38d18056b682a957da08edc7b84915ad46b0c8 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Thu, 6 Jul 2023 13:37:49 -0700 Subject: [PATCH 06/38] HAPP-1536: Use launched effect --- .../ImmunizationSchedulesFragment.kt | 3 +- .../ImmunizationSchedulesScreen.kt | 28 ++++++++-- .../ImmunizationSchedulesViewModel.kt | 51 +++++++++++++------ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt index d2b27811f..3be913a13 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt @@ -20,7 +20,6 @@ class ImmunizationSchedulesFragment : BaseFragment(null) { @Composable override fun GetComposableLayout() { - val uiList = viewModel.getUiList() MyHealthTheme { Scaffold( topBar = { @@ -31,7 +30,7 @@ class ImmunizationSchedulesFragment : BaseFragment(null) { }, content = { ImmunizationSchedulesScreen( - uiList = uiList, + viewModel = viewModel, onClickItem = ::onClickUrl, modifier = Modifier .statusBarsPadding() diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt index 917dcf24b..095ec6570 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt @@ -15,6 +15,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Card import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -31,6 +33,21 @@ import ca.bc.gov.bchealth.ui.home.immunizationschedules.ImmunizationSchedulesVie @Composable fun ImmunizationSchedulesScreen( + viewModel: ImmunizationSchedulesViewModel, + onClickItem: (String) -> Unit, + modifier: Modifier = Modifier +) { + val uiState = viewModel.uiState.collectAsState().value + + LaunchedEffect(Unit) { + viewModel.loadUiList() + } + + ImmunizationSchedulesContent(uiState.uiList, onClickItem, modifier) +} + +@Composable +private fun ImmunizationSchedulesContent( uiList: List, onClickItem: (String) -> Unit, modifier: Modifier = Modifier @@ -100,8 +117,13 @@ private fun ImmunizationScheduleUI(item: ImmunizationSchedulesItem, onClickItem: @BasePreview @Composable private fun PreviewImmunizationSchedulesScreen() { - val viewModel = ImmunizationSchedulesViewModel() - ImmunizationSchedulesScreen( - viewModel.getUiList(), {} + val item = ImmunizationSchedulesItem( + R.drawable.ic_immnz_schedules_infant, + R.string.immnz_schedules_infant, + R.string.url_immnz_schedules_infant + ) + + ImmunizationSchedulesContent( + listOf(item, item, item), {} ) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt index 0d15cc2e1..a3cc5e02f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesViewModel.kt @@ -3,26 +3,45 @@ package ca.bc.gov.bchealth.ui.home.immunizationschedules import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import ca.bc.gov.bchealth.R +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch class ImmunizationSchedulesViewModel : ViewModel() { - fun getUiList(): List = listOf( - ImmunizationSchedulesItem( - R.drawable.ic_immnz_schedules_infant, - R.string.immnz_schedules_infant, - R.string.url_immnz_schedules_infant, - ), - ImmunizationSchedulesItem( - R.drawable.ic_immnz_schedules_school_age, - R.string.immnz_schedules_school_age, - R.string.url_immnz_schedules_school_age, - ), - ImmunizationSchedulesItem( - R.drawable.ic_immnz_schedules_adult_seniors, - R.string.immnz_schedules_adult_seniors, - R.string.url_immnz_schedules_adult_seniors, - ) + private val _uiState = MutableStateFlow(ImmunizationSchedulesUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadUiList() = viewModelScope.launch { + _uiState.update { + ImmunizationSchedulesUiState( + uiList = listOf( + ImmunizationSchedulesItem( + R.drawable.ic_immnz_schedules_infant, + R.string.immnz_schedules_infant, + R.string.url_immnz_schedules_infant, + ), + ImmunizationSchedulesItem( + R.drawable.ic_immnz_schedules_school_age, + R.string.immnz_schedules_school_age, + R.string.url_immnz_schedules_school_age, + ), + ImmunizationSchedulesItem( + R.drawable.ic_immnz_schedules_adult_seniors, + R.string.immnz_schedules_adult_seniors, + R.string.url_immnz_schedules_adult_seniors, + ) + ) + ) + } + } + + data class ImmunizationSchedulesUiState( + val uiList: List = listOf() ) data class ImmunizationSchedulesItem( From df1654e8432031aa1a97939ff044398c25d7981c Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Fri, 7 Jul 2023 12:28:05 -0700 Subject: [PATCH 07/38] HAPP-1539: Create Tile list UI --- .../manage/QuickAccessManagementFragment.kt | 60 ++++++ .../manage/QuickAccessManagementScreen.kt | 194 ++++++++++++++++++ .../manage/QuickAccessManagementViewModel.kt | 73 +++++++ app/src/main/res/navigation/home.xml | 16 +- app/src/main/res/values/strings.xml | 5 + 5 files changed, 342 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt new file mode 100644 index 000000000..d43e74b2b --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt @@ -0,0 +1,60 @@ +package ca.bc.gov.bchealth.ui.home.manage + +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.fragment.app.viewModels +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.MyHealthTheme +import ca.bc.gov.bchealth.ui.BaseFragment +import ca.bc.gov.bchealth.ui.custom.MyHealthBackButton +import ca.bc.gov.bchealth.ui.custom.MyHealthToolBar + +class QuickAccessManagementFragment : BaseFragment(null) { + private val viewModel: QuickAccessManagementViewModel by viewModels() + + @Composable + override fun GetComposableLayout() { + MyHealthTheme { + Scaffold( + topBar = { + MyHealthToolBar( + title = stringResource(id = R.string.quick_access_management_title), + navigationIcon = { MyHealthBackButton(::popNavigation) }, + actions = { + IconButton(onClick = viewModel::saveSelection) { + Icon( + painter = painterResource(id = R.drawable.ic_check), + contentDescription = stringResource(id = R.string.quick_access_management_save), + tint = MaterialTheme.colors.primary + ) + } + } + ) + }, + content = { + QuickAccessManagementScreen( + viewModel = viewModel, + onClickItem = ::onClickItem, + modifier = Modifier + .statusBarsPadding() + .navigationBarsPadding() + .padding(it) + ) + }, + ) + } + } + + private fun onClickItem(item: QuickAccessManagementViewModel.QuickAccessManagementItem) { + viewModel.toggleItem(item) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt new file mode 100644 index 000000000..1172108c5 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt @@ -0,0 +1,194 @@ +package ca.bc.gov.bchealth.ui.home.manage + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Checkbox +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.MyHealthTheme +import ca.bc.gov.bchealth.compose.MyHealthTypography +import ca.bc.gov.bchealth.compose.bold +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.statusBlue +import ca.bc.gov.bchealth.compose.theme.white + +@Composable +fun QuickAccessManagementScreen( + viewModel: QuickAccessManagementViewModel, + onClickItem: (QuickAccessManagementViewModel.QuickAccessManagementItem) -> Unit, + modifier: Modifier = Modifier +) { + val uiState = viewModel.uiState.collectAsState().value + + LaunchedEffect(Unit) { + viewModel.loadUiList() + } + + QuickAccessManagementContent(uiState.uiList, onClickItem, modifier) +} + +@Composable +private fun QuickAccessManagementContent( + uiList: List, + onClickItem: (QuickAccessManagementViewModel.QuickAccessManagementItem) -> Unit, + modifier: Modifier = Modifier +) { + LazyColumn( + modifier = modifier, + contentPadding = PaddingValues(start = 32.dp, end = 32.dp, top = 20.dp, bottom = 64.dp), + content = { + item { + Text( + text = stringResource(id = R.string.quick_access_management_body), + style = MyHealthTypography.body1, + color = primaryBlue + ) + } + + item { Spacer(modifier = Modifier.size(16.dp)) } + + uiList.forEach { categories -> + + item { + Text( + text = categories.tileCategory, + style = MyHealthTypography.body1.bold(), + color = statusBlue + ) + } + + item { Spacer(modifier = Modifier.size(12.dp)) } + + items(categories.tiles) { tile -> + TileItemUi(tile, onClickItem) + Spacer(modifier = Modifier.size(10.dp)) + } + + item { Spacer(modifier = Modifier.size(6.dp)) } + } + } + ) +} + +@Composable +private fun TileItemUi( + item: QuickAccessManagementViewModel.QuickAccessManagementItem, + onClickItem: (QuickAccessManagementViewModel.QuickAccessManagementItem) -> Unit, +) { + val checkedState = remember { mutableStateOf(item.selected) } + + Card( + modifier = Modifier.clickable { + checkedState.value = checkedState.value.not() + onClickItem.invoke(item) + }, + shape = RoundedCornerShape(4.dp), + backgroundColor = white, + elevation = 5.dp, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 15.dp, end = 20.dp, top = 13.dp, bottom = 18.dp), + verticalAlignment = Alignment.CenterVertically + ) { + + Text( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + text = item.tileName, + style = MyHealthTypography.body1.bold(), + ) + + Checkbox( + modifier = Modifier.size(24.dp), + checked = checkedState.value, + onCheckedChange = { + checkedState.value = it + onClickItem.invoke(item) + } + ) + } + } +} + +@BasePreview +@Composable +private fun PreviewQuickAccessManagementContent() { + MyHealthTheme { + QuickAccessManagementContent( + listOf( + QuickAccessManagementViewModel.QuickAccessManagementList( + "Health record", + listOf( + QuickAccessManagementViewModel.QuickAccessManagementItem( + "My Notes", + false + ), + QuickAccessManagementViewModel.QuickAccessManagementItem( + "Immunization", + true + ), + QuickAccessManagementViewModel.QuickAccessManagementItem( + "Medications", + false + ), + QuickAccessManagementViewModel.QuickAccessManagementItem( + "Lab Results", + false + ), + QuickAccessManagementViewModel.QuickAccessManagementItem( + "Special authority", + false + ), + QuickAccessManagementViewModel.QuickAccessManagementItem( + "Health visit", + false + ), + QuickAccessManagementViewModel.QuickAccessManagementItem( + "Clinic documents", + false + ), + ) + ), + QuickAccessManagementViewModel.QuickAccessManagementList( + "Service", + listOf( + QuickAccessManagementViewModel.QuickAccessManagementItem( + "Organ donor", + false + ), + ) + ), + QuickAccessManagementViewModel.QuickAccessManagementList( + "Dependents’ records", + listOf( + QuickAccessManagementViewModel.QuickAccessManagementItem("Jane", false), + QuickAccessManagementViewModel.QuickAccessManagementItem("Anne", false), + ) + ) + ), + {} + ) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt new file mode 100644 index 000000000..b4f76b625 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt @@ -0,0 +1,73 @@ +package ca.bc.gov.bchealth.ui.home.manage + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class QuickAccessManagementViewModel : ViewModel() { + + private val _uiState = MutableStateFlow(QuickAccessManagementUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadUiList() = viewModelScope.launch { + _uiState.update { + QuickAccessManagementUiState( + listOf( + QuickAccessManagementList( + "Health record", + listOf( + QuickAccessManagementItem("My Notes", false), + QuickAccessManagementItem("Immunization", true), + QuickAccessManagementItem("Medications", false), + QuickAccessManagementItem("Lab Results", false), + QuickAccessManagementItem("Special authority", false), + QuickAccessManagementItem("Health visit", false), + QuickAccessManagementItem("Clinic documents", false), + ) + ), + QuickAccessManagementList( + "Service", + listOf( + QuickAccessManagementItem("Organ donor", false), + ) + ), + QuickAccessManagementList( + "Dependents’ records", + listOf( + QuickAccessManagementItem("Jane", false), + QuickAccessManagementItem("Anne", false), + ) + ) + ) + ) + } + } + + fun toggleItem(item: QuickAccessManagementItem) { + item.selected = item.selected.not() + } + + fun saveSelection() { + // todo + val result = _uiState.value.uiList + println(result) + } + + data class QuickAccessManagementUiState( + val uiList: List = listOf() + ) + + data class QuickAccessManagementList( + val tileCategory: String, + val tiles: List + ) + + data class QuickAccessManagementItem( + val tileName: String, + var selected: Boolean + ) +} diff --git a/app/src/main/res/navigation/home.xml b/app/src/main/res/navigation/home.xml index cff583cb2..0a2cf9882 100644 --- a/app/src/main/res/navigation/home.xml +++ b/app/src/main/res/navigation/home.xml @@ -1,14 +1,12 @@ + android:id="@+id/home" + app:startDestination="@id/homeComposeFragment"> + android:name="ca.bc.gov.bchealth.ui.home.HomeComposeFragment"> @@ -68,7 +66,13 @@ + android:label="@string/immnz_schedules_title" /> + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7a7e495a..ad25dd56a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -494,6 +494,11 @@ School age children Adult, seniors and individual with high risk + + Manage + Set a folder to display on your home page for quick and easy access. + Save + Terms of service Agree From 472f716d577ec5f21ebcbc1cb70883734170c8c2 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Mon, 10 Jul 2023 10:20:14 -0700 Subject: [PATCH 08/38] HAPP-1539: Rename isManagementEnabled field --- app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt | 8 ++++---- .../java/ca/bc/gov/common/model/settings/AppFeatureDto.kt | 2 +- .../java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index b66f79075..9703d5f4d 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -67,28 +67,28 @@ class SplashViewModel @Inject constructor( featureNameId = R.string.health_records, featureIconId = R.drawable.icon_tile_health_record, destinationId = R.id.health_records, - isEnabled = true, + isManagementEnabled = true, isQuickAccessEnabled = true, ), AppFeatureDto( featureNameId = R.string.immunization_schedules, featureIconId = R.drawable.ic_tile_immunization_schedules, destinationId = R.id.health_records, - isEnabled = true, + isManagementEnabled = true, isQuickAccessEnabled = true, ), AppFeatureDto( featureNameId = R.string.health_resources, featureIconId = R.drawable.ic_tile_healt_resources, destinationId = R.id.action_homeFragment_to_resources, - isEnabled = true, + isManagementEnabled = true, isQuickAccessEnabled = true, ), AppFeatureDto( featureNameId = R.string.health_passes, featureIconId = R.drawable.ic_tile_proof_of_vaccine, destinationId = R.id.action_homeFragment_to_health_pass, - isEnabled = true, + isManagementEnabled = true, isQuickAccessEnabled = true, ) ) diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt index ac0c78860..a0b7c3ea2 100644 --- a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt +++ b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt @@ -5,6 +5,6 @@ data class AppFeatureDto( val featureNameId: Int, val featureIconId: Int, val destinationId: Int, - val isEnabled: Boolean = false, + val isManagementEnabled: Boolean = false, val isQuickAccessEnabled: Boolean = false ) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt index dafafe440..a880cc720 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt @@ -366,7 +366,7 @@ fun AppFeatureDto.toEntity() = AppFeatureEntity( featureNameId, featureIconId, destinationId, - isEnabled, + isManagementEnabled, isQuickAccessEnabled ) From d888928814d2f7321a9c5cfce7674e87865a4c32 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Tue, 11 Jul 2023 11:57:42 -0700 Subject: [PATCH 09/38] HAPP-1539: Update data models --- .../ca/bc/gov/bchealth/SplashViewModel.kt | 108 ++++++++++++++++-- .../bchealth/ui/home/HomeComposeViewModel.kt | 38 ++++-- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 68 +++++++++-- app/src/main/res/values/strings.xml | 15 +++ .../common/model/settings/AppFeatureDto.kt | 4 +- .../14.json | 22 +++- .../local/entity/settings/AppFeatureEntity.kt | 10 +- .../data/model/mapper/DtoToEntityMapper.kt | 2 + .../data/model/mapper/EntityToDtoMapper.kt | 28 ++--- 9 files changed, 248 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index 9703d5f4d..cfc22e0d8 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -32,7 +32,7 @@ class SplashViewModel @Inject constructor( init { onBoardingRepository.checkIfReOnBoardingRequired(BuildConfig.VERSION_CODE) runBlocking { - initializeAppFeatureAndQuickAccessTileData() + initializeAppFeaturesData() } } @@ -61,36 +61,130 @@ class SplashViewModel @Inject constructor( FORCE_UPDATE, CHECK_SOFT_UPDATE } - private suspend fun initializeAppFeatureAndQuickAccessTileData() { + private suspend fun initializeAppFeaturesData() { val appFeatures = listOf( AppFeatureDto( featureNameId = R.string.health_records, + categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.icon_tile_health_record, destinationId = R.id.health_records, - isManagementEnabled = true, + isManagementEnabled = false, isQuickAccessEnabled = true, ), AppFeatureDto( featureNameId = R.string.immunization_schedules, + categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_tile_immunization_schedules, destinationId = R.id.health_records, - isManagementEnabled = true, + isManagementEnabled = false, isQuickAccessEnabled = true, ), AppFeatureDto( featureNameId = R.string.health_resources, + categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_tile_healt_resources, destinationId = R.id.action_homeFragment_to_resources, - isManagementEnabled = true, + isManagementEnabled = false, isQuickAccessEnabled = true, ), AppFeatureDto( featureNameId = R.string.health_passes, + categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_tile_proof_of_vaccine, destinationId = R.id.action_homeFragment_to_health_pass, - isManagementEnabled = true, + isManagementEnabled = false, isQuickAccessEnabled = true, - ) + ), + + AppFeatureDto( + featureNameId = R.string.feature_my_notes, + categoryId = R.string.feature_category_health_record, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureNameId = R.string.feature_immunization, + categoryId = R.string.feature_category_health_record, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureNameId = R.string.feature_medications, + categoryId = R.string.feature_category_health_record, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureNameId = R.string.feature_lab_results, + categoryId = R.string.feature_category_health_record, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureNameId = R.string.feature_special_authority, + categoryId = R.string.feature_category_health_record, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureNameId = R.string.feature_health_visit, + categoryId = R.string.feature_category_health_record, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureNameId = R.string.feature_clinic_documents, + categoryId = R.string.feature_category_health_record, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureNameId = R.string.feature_organ_donor, + categoryId = R.string.feature_category_service, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureName = "To-do: Jane", + categoryId = R.string.feature_category_dependents_records, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), + + AppFeatureDto( + featureName = "To-do: Anne", + categoryId = R.string.feature_category_dependents_records, + featureIconId = R.drawable.icon_tile_health_record, + destinationId = R.id.health_records, + isManagementEnabled = true, + isQuickAccessEnabled = false, + ), ) appFeatures.forEach { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt index 728efae0e..cb7148863 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -23,11 +23,20 @@ class HomeComposeViewModel @Inject constructor( appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() .filter { it.appFeatureDto.isQuickAccessEnabled } .map { - QuickAccessTileItem( - it.appFeatureDto.featureIconId, - it.appFeatureDto.featureNameId, - it.appFeatureDto.destinationId - ) + if (it.appFeatureDto.featureName == null) { + QuickAccessTileItem.PredefinedItem( + icon = it.appFeatureDto.featureIconId, + nameId = it.appFeatureDto.featureNameId ?: -1, + destinationId = it.appFeatureDto.destinationId + ) + } else { + QuickAccessTileItem.DynamicItem( + icon = it.appFeatureDto.featureIconId, + nameId = it.appFeatureDto.featureNameId, + text = it.appFeatureDto.featureName.orEmpty(), + destinationId = it.appFeatureDto.destinationId + ) + } } _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } @@ -38,8 +47,17 @@ data class HomeComposeUiState( val quickAccessTileItems: List = emptyList() ) -data class QuickAccessTileItem( - val icon: Int, - val name: Int, - val destinationId: Int -) +sealed class QuickAccessTileItem(open val destinationId: Int) { + data class PredefinedItem( + val icon: Int, + val nameId: Int, + override val destinationId: Int + ) : QuickAccessTileItem(destinationId) + + data class DynamicItem( + val icon: Int, + val nameId: Int?, + val text: String, + override val destinationId: Int + ) : QuickAccessTileItem(destinationId) +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index ccf07b7f1..da380f9d1 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -78,21 +78,73 @@ private fun HomeScreenContent( } items(quickAccessTileItems) { - QuickAccessTileItemUI( - onClick = { onQuickAccessTileClicked(it) }, - icon = painterResource(id = it.icon), - title = stringResource( - it.name - ) - ) + QuickAccessTileItemUi(it, onQuickAccessTileClicked) } } } +@Composable +private fun QuickAccessTileItemUi( + item: QuickAccessTileItem, + onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit +) { + val title: String + val icon: Int + + when (item) { + is QuickAccessTileItem.PredefinedItem -> { + icon = item.icon + title = stringResource(id = item.nameId) + } + + is QuickAccessTileItem.DynamicItem -> { + icon = item.icon + title = if (item.nameId == null) { + item.text + } else { + stringResource(item.nameId, item.text).replaceFirst("s’s", "s’") + } + } + } + + QuickAccessTileItemUI( + onClick = { onQuickAccessTileClicked(item) }, + icon = painterResource(id = icon), + title = title + ) +} + @Composable @BasePreview private fun HomeScreenPreview() { HealthGatewayTheme { - HomeScreenContent(onQuickAccessTileClicked = {}, quickAccessTileItems = emptyList()) + HomeScreenContent( + onQuickAccessTileClicked = {}, + quickAccessTileItems = listOf( + QuickAccessTileItem.DynamicItem( + icon = R.drawable.ic_health_record, + nameId = R.string.feature_quick_action_dependents, + text = "Jane", + destinationId = -1, + ), + QuickAccessTileItem.DynamicItem( + icon = R.drawable.ic_health_record, + nameId = R.string.feature_quick_action_dependents, + text = "James", + destinationId = -1, + ), + QuickAccessTileItem.DynamicItem( + icon = R.drawable.ic_health_record, + nameId = null, + text = "Dynamic text", + destinationId = -1, + ), + QuickAccessTileItem.PredefinedItem( + icon = R.drawable.ic_health_record, + nameId = R.string.immnz_schedules_infant, + destinationId = -1, + ), + ) + ) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ad25dd56a..fa3766c10 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,21 @@ Expand content Collapse content + + Health record + Service + Dependents’ records + %s’s record + + My Notes + Immunization + Medications + Lab Results + Special authority + Health visit + Clinic documents + Organ donor + Privacy Policy Allow camera access diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt index a0b7c3ea2..287c43d52 100644 --- a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt +++ b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt @@ -2,7 +2,9 @@ package ca.bc.gov.common.model.settings data class AppFeatureDto( val id: Long = 0, - val featureNameId: Int, + val featureName: String? = null, + val featureNameId: Int? = null, + val categoryId: Int, val featureIconId: Int, val destinationId: Int, val isManagementEnabled: Boolean = false, diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json index b5a085fbd..6f790b698 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 14, - "identityHash": "cd63a7ddb38d8002c82af9bc1156c3dc", + "identityHash": "0d2a4368494a9528fd6ea9995291028a", "entities": [ { "tableName": "patient", @@ -2000,7 +2000,7 @@ }, { "tableName": "app_feature", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name_id` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name` TEXT, `feature_name_id` INTEGER, `category` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `is_management_enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", "fields": [ { "fieldPath": "id", @@ -2008,10 +2008,22 @@ "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "featureName", + "columnName": "feature_name", + "affinity": "TEXT", + "notNull": false + }, { "fieldPath": "featureNameId", "columnName": "feature_name_id", "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "categoryId", + "columnName": "category", + "affinity": "INTEGER", "notNull": true }, { @@ -2027,8 +2039,8 @@ "notNull": true }, { - "fieldPath": "isEnabled", - "columnName": "enabled", + "fieldPath": "isManagementEnabled", + "columnName": "is_management_enabled", "affinity": "INTEGER", "notNull": true, "defaultValue": "false" @@ -2122,7 +2134,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cd63a7ddb38d8002c82af9bc1156c3dc')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0d2a4368494a9528fd6ea9995291028a')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt index 2df369383..7c91ee34a 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt @@ -12,14 +12,18 @@ import androidx.room.PrimaryKey data class AppFeatureEntity( @PrimaryKey(autoGenerate = true) val id: Long = 0, + @ColumnInfo(name = "feature_name") + val featureName: String?, @ColumnInfo(name = "feature_name_id") - val featureNameId: Int, + val featureNameId: Int?, + @ColumnInfo(name = "category_name_id") + val categoryNameId: Int, @ColumnInfo(name = "feature_icon_id") val featureIconId: Int, @ColumnInfo(name = "destination_id") val destinationId: Int, - @ColumnInfo(name = "enabled", defaultValue = "false") - val isEnabled: Boolean = false, + @ColumnInfo(name = "is_management_enabled", defaultValue = "false") + val isManagementEnabled: Boolean = false, @ColumnInfo(name = "quick_access_enabled", defaultValue = "false") val isQuickAccessEnabled: Boolean = false ) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt index a880cc720..ed48eca64 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt @@ -363,7 +363,9 @@ fun NotificationDto.toEntity() = NotificationEntity( fun AppFeatureDto.toEntity() = AppFeatureEntity( id, + featureName, featureNameId, + categoryId, featureIconId, destinationId, isManagementEnabled, diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt index acbe1103c..73f88aa70 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt @@ -483,23 +483,25 @@ fun NotificationEntity.toDto() = NotificationDto( ) fun AppFeatureEntity.toDto() = AppFeatureDto( - id, - featureNameId, - featureIconId, - destinationId, - isEnabled, - isQuickAccessEnabled + id = id, + featureName = featureName, + featureNameId = featureNameId, + categoryId = categoryNameId, + featureIconId = featureIconId, + destinationId = destinationId, + isManagementEnabled = isManagementEnabled, + isQuickAccessEnabled = isQuickAccessEnabled ) fun QuickAccessTileEntity.toDto() = QuickAccessTileDto( - id, - featureId, - tileNameId, - tileIconId, - isEnabled + id = id, + featureId = featureId, + titleNameId = tileNameId, + titleIconId = tileIconId, + isEnabled = isEnabled ) fun AppFeatureWithQuickAccessTiles.toDto() = AppFeatureWithQuickAccessTilesDto( - appFeature.toDto(), - quickAccessTiles.map { it.toDto() } + appFeatureDto = appFeature.toDto(), + quickAccessTiles = quickAccessTiles.map { it.toDto() } ) From 52be3cc167626daaaf5a68ffb2eee4a3539aa3d0 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Tue, 11 Jul 2023 14:30:22 -0700 Subject: [PATCH 10/38] HAPP-1539: Remove QuickAccessTileEntity --- .../bchealth/ui/home/HomeComposeViewModel.kt | 18 ++--- .../AppFeatureWithQuickAccessTilesDto.kt | 6 -- .../14.json | 67 ++----------------- .../local/AppFeatureLocalDataSource.kt | 7 +- .../data/datasource/local/MyHealthDataBase.kt | 5 -- .../local/QuickActionTileLocalDataSource.kt | 15 ----- .../datasource/local/dao/AppFeatureDao.kt | 5 +- .../local/dao/QuickAccessTileDao.kt | 7 -- .../AppFeatureWithQuickAccessTiles.kt | 17 ----- .../entity/settings/QuickAccessTileEntity.kt | 30 --------- .../bc/gov/data/di/LocalDataSourceModule.kt | 6 -- .../data/model/mapper/DtoToEntityMapper.kt | 10 --- .../data/model/mapper/EntityToDtoMapper.kt | 17 ----- .../gov/repository/di/RepositoriesModule.kt | 8 --- .../settings/QuickAccessTileRepository.kt | 8 --- 15 files changed, 20 insertions(+), 206 deletions(-) delete mode 100644 common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt delete mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt delete mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt delete mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt delete mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt delete mode 100644 repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt index cb7148863..ea85fb621 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -21,20 +21,20 @@ class HomeComposeViewModel @Inject constructor( fun loadQuickAccessTiles() = viewModelScope.launch { val quickAccessTileItems = appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() - .filter { it.appFeatureDto.isQuickAccessEnabled } + .filter { it.isQuickAccessEnabled } .map { - if (it.appFeatureDto.featureName == null) { + if (it.featureName == null) { QuickAccessTileItem.PredefinedItem( - icon = it.appFeatureDto.featureIconId, - nameId = it.appFeatureDto.featureNameId ?: -1, - destinationId = it.appFeatureDto.destinationId + icon = it.featureIconId, + nameId = it.featureNameId ?: -1, + destinationId = it.destinationId ) } else { QuickAccessTileItem.DynamicItem( - icon = it.appFeatureDto.featureIconId, - nameId = it.appFeatureDto.featureNameId, - text = it.appFeatureDto.featureName.orEmpty(), - destinationId = it.appFeatureDto.destinationId + icon = it.featureIconId, + nameId = it.featureNameId, + text = it.featureName.orEmpty(), + destinationId = it.destinationId ) } } diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt deleted file mode 100644 index 815bcad36..000000000 --- a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ca.bc.gov.common.model.settings - -data class AppFeatureWithQuickAccessTilesDto( - val appFeatureDto: AppFeatureDto, - val quickAccessTiles: List -) diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json index 6f790b698..d4d2f56c5 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 14, - "identityHash": "0d2a4368494a9528fd6ea9995291028a", + "identityHash": "c9795b9cfc67222bed0194b4722c64e4", "entities": [ { "tableName": "patient", @@ -2000,7 +2000,7 @@ }, { "tableName": "app_feature", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name` TEXT, `feature_name_id` INTEGER, `category` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `is_management_enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name` TEXT, `feature_name_id` INTEGER, `category_name_id` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `is_management_enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", "fields": [ { "fieldPath": "id", @@ -2021,8 +2021,8 @@ "notNull": false }, { - "fieldPath": "categoryId", - "columnName": "category", + "fieldPath": "categoryNameId", + "columnName": "category_name_id", "affinity": "INTEGER", "notNull": true }, @@ -2072,69 +2072,12 @@ } ], "foreignKeys": [] - }, - { - "tableName": "quick_access_tile", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_id` INTEGER NOT NULL, `tile_name_id` INTEGER NOT NULL, `tile_icon_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, FOREIGN KEY(`feature_id`) REFERENCES `app_feature`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "featureId", - "columnName": "feature_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tileNameId", - "columnName": "tile_name_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tileIconId", - "columnName": "tile_icon_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isEnabled", - "columnName": "enabled", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "false" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [ - { - "table": "app_feature", - "onDelete": "CASCADE", - "onUpdate": "CASCADE", - "columns": [ - "feature_id" - ], - "referencedColumns": [ - "id" - ] - } - ] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0d2a4368494a9528fd6ea9995291028a')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c9795b9cfc67222bed0194b4722c64e4')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt index b791475f3..13315b3c0 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt @@ -1,7 +1,6 @@ package ca.bc.gov.data.datasource.local import ca.bc.gov.common.model.settings.AppFeatureDto -import ca.bc.gov.common.model.settings.AppFeatureWithQuickAccessTilesDto import ca.bc.gov.data.datasource.local.dao.AppFeatureDao import ca.bc.gov.data.model.mapper.toDto import ca.bc.gov.data.model.mapper.toEntity @@ -15,7 +14,9 @@ class AppFeatureLocalDataSource @Inject constructor( return appFeatureDao.insert(appFeatureDto.toEntity()) } - suspend fun getAppFeaturesWithQuickAccessTiles(): List { - return appFeatureDao.getAllFeatureWithQuickAccessTiles().map { it.toDto() } + suspend fun getAppFeaturesWithQuickAccessTiles(): List { + return appFeatureDao.getAllFeatureWithQuickAccessTiles().map { + it.toDto() + } } } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt index 7d65f74b5..a76b72e7b 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt @@ -30,7 +30,6 @@ import ca.bc.gov.data.datasource.local.dao.MedicationSummaryDao import ca.bc.gov.data.datasource.local.dao.NotificationDao import ca.bc.gov.data.datasource.local.dao.OrganDonorDao import ca.bc.gov.data.datasource.local.dao.PatientDao -import ca.bc.gov.data.datasource.local.dao.QuickAccessTileDao import ca.bc.gov.data.datasource.local.dao.SpecialAuthorityDao import ca.bc.gov.data.datasource.local.dao.UserProfileDao import ca.bc.gov.data.datasource.local.dao.VaccineRecordDao @@ -57,7 +56,6 @@ import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity -import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity @@ -92,7 +90,6 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity DiagnosticImagingDataEntity::class, NotificationEntity::class, AppFeatureEntity::class, - QuickAccessTileEntity::class ], autoMigrations = [ AutoMigration(from = 8, to = 9), @@ -161,6 +158,4 @@ abstract class MyHealthDataBase : RoomDatabase() { abstract fun getNotificationDao(): NotificationDao abstract fun getAppFeatureDao(): AppFeatureDao - - abstract fun getQuickAccessTileDao(): QuickAccessTileDao } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt deleted file mode 100644 index 506d63e4d..000000000 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ca.bc.gov.data.datasource.local - -import ca.bc.gov.common.model.settings.QuickAccessTileDto -import ca.bc.gov.data.datasource.local.dao.QuickAccessTileDao -import ca.bc.gov.data.model.mapper.toEntity -import javax.inject.Inject - -class QuickActionTileLocalDataSource @Inject constructor( - private val quickAccessTileDao: QuickAccessTileDao -) { - - suspend fun insert(quickAccessTileDto: QuickAccessTileDto): Long { - return quickAccessTileDao.insert(quickAccessTileDto.toEntity()) - } -} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt index e924d43c2..b3a51c59c 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt @@ -4,7 +4,6 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import ca.bc.gov.data.datasource.local.entity.relations.AppFeatureWithQuickAccessTiles import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity @Dao @@ -16,6 +15,6 @@ interface AppFeatureDao { @Query("DELETE FROM app_feature") suspend fun deleteAll() - @Query("SELECT * FROM app_feature") - suspend fun getAllFeatureWithQuickAccessTiles(): List + @Query("SELECT * FROM app_feature WHERE quick_access_enabled = 1 ") + suspend fun getAllFeatureWithQuickAccessTiles(): List } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt deleted file mode 100644 index 42092ffc0..000000000 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt +++ /dev/null @@ -1,7 +0,0 @@ -package ca.bc.gov.data.datasource.local.dao - -import androidx.room.Dao -import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity - -@Dao -interface QuickAccessTileDao : BaseDao diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt deleted file mode 100644 index b27a645ac..000000000 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt +++ /dev/null @@ -1,17 +0,0 @@ -package ca.bc.gov.data.datasource.local.entity.relations - -import androidx.room.Embedded -import androidx.room.Relation -import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity -import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity - -data class AppFeatureWithQuickAccessTiles( - @Embedded - val appFeature: AppFeatureEntity, - - @Relation( - parentColumn = "id", - entityColumn = "feature_id" - ) - val quickAccessTiles: List = emptyList() -) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt deleted file mode 100644 index 49ffbb807..000000000 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt +++ /dev/null @@ -1,30 +0,0 @@ -package ca.bc.gov.data.datasource.local.entity.settings - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity( - tableName = "quick_access_tile", - foreignKeys = [ - androidx.room.ForeignKey( - entity = AppFeatureEntity::class, - parentColumns = ["id"], - childColumns = ["feature_id"], - onDelete = androidx.room.ForeignKey.CASCADE, - onUpdate = androidx.room.ForeignKey.CASCADE - ) - ] -) -data class QuickAccessTileEntity( - @PrimaryKey(autoGenerate = true) - val id: Long = 0, - @ColumnInfo(name = "feature_id") - val featureId: Long, - @ColumnInfo(name = "tile_name_id") - val tileNameId: Int, - @ColumnInfo(name = "tile_icon_id") - val tileIconId: Int, - @ColumnInfo(name = "enabled", defaultValue = "false") - val isEnabled: Boolean = false -) diff --git a/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt b/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt index a92657645..5e11957b9 100644 --- a/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt +++ b/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt @@ -20,7 +20,6 @@ import ca.bc.gov.data.datasource.local.MyHealthDataBase import ca.bc.gov.data.datasource.local.NotificationLocalDataSource import ca.bc.gov.data.datasource.local.OrganDonorLocalDataSource import ca.bc.gov.data.datasource.local.PatientLocalDataSource -import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource import ca.bc.gov.data.datasource.local.SpecialAuthorityLocalDataSource import ca.bc.gov.data.datasource.local.UserProfileLocalDataSource import ca.bc.gov.data.datasource.local.VaccineRecordLocalDataSource @@ -160,9 +159,4 @@ class LocalDataSourceModule { @Singleton fun provideAppFeatureLocalDataSource(db: MyHealthDataBase): AppFeatureLocalDataSource = AppFeatureLocalDataSource(db.getAppFeatureDao()) - - @Provides - @Singleton - fun provideQuickAccessTileLocalDataSource(db: MyHealthDataBase): QuickActionTileLocalDataSource = - QuickActionTileLocalDataSource(db.getQuickAccessTileDao()) } diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt index ed48eca64..e07862bcc 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt @@ -23,7 +23,6 @@ import ca.bc.gov.common.model.patient.PatientNameDto import ca.bc.gov.common.model.services.DiagnosticImagingDataDto import ca.bc.gov.common.model.services.OrganDonorDto import ca.bc.gov.common.model.settings.AppFeatureDto -import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.common.model.specialauthority.SpecialAuthorityDto import ca.bc.gov.common.model.test.CovidOrderDto import ca.bc.gov.common.model.test.CovidTestDto @@ -53,7 +52,6 @@ import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity -import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity @@ -371,11 +369,3 @@ fun AppFeatureDto.toEntity() = AppFeatureEntity( isManagementEnabled, isQuickAccessEnabled ) - -fun QuickAccessTileDto.toEntity() = QuickAccessTileEntity( - id, - featureId, - titleNameId, - titleIconId, - isEnabled -) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt index 73f88aa70..444417919 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt @@ -42,8 +42,6 @@ import ca.bc.gov.common.model.relation.VaccineWithDosesDto import ca.bc.gov.common.model.services.DiagnosticImagingDataDto import ca.bc.gov.common.model.services.OrganDonorDto import ca.bc.gov.common.model.settings.AppFeatureDto -import ca.bc.gov.common.model.settings.AppFeatureWithQuickAccessTilesDto -import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.common.model.specialauthority.SpecialAuthorityDto import ca.bc.gov.common.model.test.CovidOrderDto import ca.bc.gov.common.model.test.CovidOrderWithCovidTestAndPatientDto @@ -78,7 +76,6 @@ import ca.bc.gov.data.datasource.local.entity.medication.DispensingPharmacyEntit import ca.bc.gov.data.datasource.local.entity.medication.MedicationRecordEntity import ca.bc.gov.data.datasource.local.entity.medication.MedicationSummaryEntity import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity -import ca.bc.gov.data.datasource.local.entity.relations.AppFeatureWithQuickAccessTiles import ca.bc.gov.data.datasource.local.entity.relations.MedicationWithSummaryAndPharmacy import ca.bc.gov.data.datasource.local.entity.relations.PatientWithClinicalDocuments import ca.bc.gov.data.datasource.local.entity.relations.PatientWithCovidOrderAndCovidTest @@ -94,7 +91,6 @@ import ca.bc.gov.data.datasource.local.entity.relations.VaccineRecordWithDose import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity -import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity import java.time.Instant @@ -492,16 +488,3 @@ fun AppFeatureEntity.toDto() = AppFeatureDto( isManagementEnabled = isManagementEnabled, isQuickAccessEnabled = isQuickAccessEnabled ) - -fun QuickAccessTileEntity.toDto() = QuickAccessTileDto( - id = id, - featureId = featureId, - titleNameId = tileNameId, - titleIconId = tileIconId, - isEnabled = isEnabled -) - -fun AppFeatureWithQuickAccessTiles.toDto() = AppFeatureWithQuickAccessTilesDto( - appFeatureDto = appFeature.toDto(), - quickAccessTiles = quickAccessTiles.map { it.toDto() } -) diff --git a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt index 818de0fe4..785e0b35c 100644 --- a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt +++ b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt @@ -16,7 +16,6 @@ import ca.bc.gov.data.datasource.local.MedicationRecordLocalDataSource import ca.bc.gov.data.datasource.local.NotificationLocalDataSource import ca.bc.gov.data.datasource.local.OrganDonorLocalDataSource import ca.bc.gov.data.datasource.local.PatientLocalDataSource -import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource import ca.bc.gov.data.datasource.local.VaccineRecordLocalDataSource import ca.bc.gov.data.datasource.remote.BannerRemoteDataSource import ca.bc.gov.data.datasource.remote.CommentRemoteDataSource @@ -60,7 +59,6 @@ import ca.bc.gov.repository.services.OrganDonorRepository import ca.bc.gov.repository.services.PatientServicesRepository import ca.bc.gov.repository.settings.AppFeatureRepository import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository -import ca.bc.gov.repository.settings.QuickAccessTileRepository import ca.bc.gov.repository.testrecord.CovidOrderRepository import ca.bc.gov.repository.testrecord.CovidTestRepository import ca.bc.gov.repository.utils.Base64ToInputImageConverter @@ -360,12 +358,6 @@ class RepositoriesModule { appFeatureLocalDataSource: AppFeatureLocalDataSource ): AppFeatureRepository = AppFeatureRepository(appFeatureLocalDataSource) - @Provides - @Singleton - fun providesQuickAccessTileRepository( - quickActionTileLocalDataSource: QuickActionTileLocalDataSource - ): QuickAccessTileRepository = QuickAccessTileRepository(quickActionTileLocalDataSource) - @Provides @Singleton fun providesAppFeatureWithQuickAccessTilesRepository( diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt deleted file mode 100644 index 5527130d6..000000000 --- a/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ca.bc.gov.repository.settings - -import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource -import javax.inject.Inject - -class QuickAccessTileRepository @Inject constructor( - private val quickActionTileLocalDataSource: QuickActionTileLocalDataSource -) From 4287979416f356917172674ccb39c56cf9056b28 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Tue, 11 Jul 2023 17:32:59 -0700 Subject: [PATCH 11/38] HAPP-1539: Display manageable tiles from db --- .../bchealth/ui/home/HomeComposeViewModel.kt | 65 ++++--- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 8 + .../manage/QuickAccessManagementFragment.kt | 5 +- .../manage/QuickAccessManagementScreen.kt | 163 +++++++++--------- .../manage/QuickAccessManagementViewModel.kt | 70 ++------ .../14.json | 11 +- .../local/AppFeatureLocalDataSource.kt | 10 +- .../datasource/local/dao/AppFeatureDao.kt | 5 +- .../local/entity/settings/AppFeatureEntity.kt | 2 +- .../gov/repository/di/RepositoriesModule.kt | 8 - .../settings/AppFeatureRepository.kt | 5 +- ...ppFeatureWithQuickAccessTilesRepository.kt | 10 -- 12 files changed, 178 insertions(+), 184 deletions(-) delete mode 100644 repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt index ea85fb621..59e0219d1 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -2,7 +2,8 @@ package ca.bc.gov.bchealth.ui.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository +import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.repository.settings.AppFeatureRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -13,31 +14,15 @@ import javax.inject.Inject @HiltViewModel class HomeComposeViewModel @Inject constructor( - private val appFeatureWithQuickAccessTilesRepository: AppFeatureWithQuickAccessTilesRepository + private val appFeatureRepository: AppFeatureRepository ) : ViewModel() { private val _uiState = MutableStateFlow(HomeComposeUiState()) val uiState: StateFlow = _uiState.asStateFlow() fun loadQuickAccessTiles() = viewModelScope.launch { - val quickAccessTileItems = - appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() - .filter { it.isQuickAccessEnabled } - .map { - if (it.featureName == null) { - QuickAccessTileItem.PredefinedItem( - icon = it.featureIconId, - nameId = it.featureNameId ?: -1, - destinationId = it.destinationId - ) - } else { - QuickAccessTileItem.DynamicItem( - icon = it.featureIconId, - nameId = it.featureNameId, - text = it.featureName.orEmpty(), - destinationId = it.destinationId - ) - } - } + val quickAccessTileItems = appFeatureRepository.getQuickAccessTiles().map { + it.toUiItem() + } _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } } @@ -47,17 +32,45 @@ data class HomeComposeUiState( val quickAccessTileItems: List = emptyList() ) -sealed class QuickAccessTileItem(open val destinationId: Int) { +sealed class QuickAccessTileItem( + open val destinationId: Int, + open val categoryId: Int, + open var enabled: Boolean, +) { data class PredefinedItem( val icon: Int, val nameId: Int, - override val destinationId: Int - ) : QuickAccessTileItem(destinationId) + override val destinationId: Int, + override val categoryId: Int, + override var enabled: Boolean, + ) : QuickAccessTileItem(destinationId, categoryId, enabled) data class DynamicItem( val icon: Int, val nameId: Int?, val text: String, - override val destinationId: Int - ) : QuickAccessTileItem(destinationId) + override val destinationId: Int, + override val categoryId: Int, + override var enabled: Boolean, + ) : QuickAccessTileItem(destinationId, categoryId, enabled) } + +fun AppFeatureDto.toUiItem(): QuickAccessTileItem = + if (this.featureName == null) { + QuickAccessTileItem.PredefinedItem( + icon = this.featureIconId, + nameId = this.featureNameId ?: -1, + destinationId = this.destinationId, + categoryId = this.categoryId, + enabled = this.isQuickAccessEnabled + ) + } else { + QuickAccessTileItem.DynamicItem( + icon = this.featureIconId, + nameId = this.featureNameId, + text = this.featureName.orEmpty(), + destinationId = this.destinationId, + categoryId = this.categoryId, + enabled = this.isQuickAccessEnabled + ) + } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index da380f9d1..5127c5141 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -126,23 +126,31 @@ private fun HomeScreenPreview() { nameId = R.string.feature_quick_action_dependents, text = "Jane", destinationId = -1, + categoryId = -1, + enabled = true, ), QuickAccessTileItem.DynamicItem( icon = R.drawable.ic_health_record, nameId = R.string.feature_quick_action_dependents, text = "James", destinationId = -1, + categoryId = -1, + enabled = true, ), QuickAccessTileItem.DynamicItem( icon = R.drawable.ic_health_record, nameId = null, text = "Dynamic text", destinationId = -1, + categoryId = -1, + enabled = true, ), QuickAccessTileItem.PredefinedItem( icon = R.drawable.ic_health_record, nameId = R.string.immnz_schedules_infant, destinationId = -1, + categoryId = -1, + enabled = true, ), ) ) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt index d43e74b2b..877c755f7 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt @@ -17,7 +17,10 @@ import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.ui.custom.MyHealthBackButton import ca.bc.gov.bchealth.ui.custom.MyHealthToolBar +import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class QuickAccessManagementFragment : BaseFragment(null) { private val viewModel: QuickAccessManagementViewModel by viewModels() @@ -54,7 +57,7 @@ class QuickAccessManagementFragment : BaseFragment(null) { } } - private fun onClickItem(item: QuickAccessManagementViewModel.QuickAccessManagementItem) { + private fun onClickItem(item: QuickAccessTileItem) { viewModel.toggleItem(item) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt index 1172108c5..abfa4730f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt @@ -3,6 +3,7 @@ package ca.bc.gov.bchealth.ui.home.manage import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -23,18 +24,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.BasePreview -import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography import ca.bc.gov.bchealth.compose.bold import ca.bc.gov.bchealth.compose.theme.primaryBlue import ca.bc.gov.bchealth.compose.theme.statusBlue import ca.bc.gov.bchealth.compose.theme.white +import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem @Composable fun QuickAccessManagementScreen( viewModel: QuickAccessManagementViewModel, - onClickItem: (QuickAccessManagementViewModel.QuickAccessManagementItem) -> Unit, + onClickItem: (QuickAccessTileItem) -> Unit, modifier: Modifier = Modifier ) { val uiState = viewModel.uiState.collectAsState().value @@ -43,13 +43,13 @@ fun QuickAccessManagementScreen( viewModel.loadUiList() } - QuickAccessManagementContent(uiState.uiList, onClickItem, modifier) + QuickAccessManagementContent(uiState, onClickItem, modifier) } @Composable private fun QuickAccessManagementContent( - uiList: List, - onClickItem: (QuickAccessManagementViewModel.QuickAccessManagementItem) -> Unit, + uiState: Map>, + onClickItem: (QuickAccessTileItem) -> Unit, modifier: Modifier = Modifier ) { LazyColumn( @@ -66,11 +66,10 @@ private fun QuickAccessManagementContent( item { Spacer(modifier = Modifier.size(16.dp)) } - uiList.forEach { categories -> - + for ((category, tiles) in uiState) { item { Text( - text = categories.tileCategory, + text = stringResource(id = category), style = MyHealthTypography.body1.bold(), color = statusBlue ) @@ -78,7 +77,7 @@ private fun QuickAccessManagementContent( item { Spacer(modifier = Modifier.size(12.dp)) } - items(categories.tiles) { tile -> + items(tiles) { tile -> TileItemUi(tile, onClickItem) Spacer(modifier = Modifier.size(10.dp)) } @@ -91,10 +90,10 @@ private fun QuickAccessManagementContent( @Composable private fun TileItemUi( - item: QuickAccessManagementViewModel.QuickAccessManagementItem, - onClickItem: (QuickAccessManagementViewModel.QuickAccessManagementItem) -> Unit, + item: QuickAccessTileItem, + onClickItem: (QuickAccessTileItem) -> Unit, ) { - val checkedState = remember { mutableStateOf(item.selected) } + val checkedState = remember { mutableStateOf(item.enabled) } Card( modifier = Modifier.clickable { @@ -112,13 +111,7 @@ private fun TileItemUi( verticalAlignment = Alignment.CenterVertically ) { - Text( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - text = item.tileName, - style = MyHealthTypography.body1.bold(), - ) + TileName(item) Checkbox( modifier = Modifier.size(24.dp), @@ -132,63 +125,79 @@ private fun TileItemUi( } } -@BasePreview @Composable -private fun PreviewQuickAccessManagementContent() { - MyHealthTheme { - QuickAccessManagementContent( - listOf( - QuickAccessManagementViewModel.QuickAccessManagementList( - "Health record", - listOf( - QuickAccessManagementViewModel.QuickAccessManagementItem( - "My Notes", - false - ), - QuickAccessManagementViewModel.QuickAccessManagementItem( - "Immunization", - true - ), - QuickAccessManagementViewModel.QuickAccessManagementItem( - "Medications", - false - ), - QuickAccessManagementViewModel.QuickAccessManagementItem( - "Lab Results", - false - ), - QuickAccessManagementViewModel.QuickAccessManagementItem( - "Special authority", - false - ), - QuickAccessManagementViewModel.QuickAccessManagementItem( - "Health visit", - false - ), - QuickAccessManagementViewModel.QuickAccessManagementItem( - "Clinic documents", - false - ), - ) - ), - QuickAccessManagementViewModel.QuickAccessManagementList( - "Service", - listOf( - QuickAccessManagementViewModel.QuickAccessManagementItem( - "Organ donor", - false - ), - ) - ), - QuickAccessManagementViewModel.QuickAccessManagementList( - "Dependents’ records", - listOf( - QuickAccessManagementViewModel.QuickAccessManagementItem("Jane", false), - QuickAccessManagementViewModel.QuickAccessManagementItem("Anne", false), - ) - ) - ), - {} - ) +private fun RowScope.TileName(item: QuickAccessTileItem) { + val name = when (item) { + is QuickAccessTileItem.PredefinedItem -> stringResource(id = item.nameId) + is QuickAccessTileItem.DynamicItem -> item.text } + + Text( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + text = name, + style = MyHealthTypography.body1.bold(), + ) } + +// @BasePreview +// @Composable +// private fun PreviewQuickAccessManagementContent() { +// MyHealthTheme { +// QuickAccessManagementContent( +// listOf( +// QuickAccessManagementViewModel.QuickAccessManagementList( +// "Health record", +// listOf( +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "My Notes", +// false +// ), +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "Immunization", +// true +// ), +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "Medications", +// false +// ), +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "Lab Results", +// false +// ), +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "Special authority", +// false +// ), +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "Health visit", +// false +// ), +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "Clinic documents", +// false +// ), +// ) +// ), +// QuickAccessManagementViewModel.QuickAccessManagementList( +// "Service", +// listOf( +// QuickAccessManagementViewModel.QuickAccessManagementItem( +// "Organ donor", +// false +// ), +// ) +// ), +// QuickAccessManagementViewModel.QuickAccessManagementList( +// "Dependents’ records", +// listOf( +// QuickAccessManagementViewModel.QuickAccessManagementItem("Jane", false), +// QuickAccessManagementViewModel.QuickAccessManagementItem("Anne", false), +// ) +// ) +// ), +// {} +// ) +// } +// } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt index b4f76b625..c8d62d101 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt @@ -2,72 +2,40 @@ package ca.bc.gov.bchealth.ui.home.manage import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem +import ca.bc.gov.bchealth.ui.home.toUiItem +import ca.bc.gov.repository.settings.AppFeatureRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import javax.inject.Inject -class QuickAccessManagementViewModel : ViewModel() { +@HiltViewModel +class QuickAccessManagementViewModel @Inject constructor( + private val appFeatureRepository: AppFeatureRepository +) : ViewModel() { - private val _uiState = MutableStateFlow(QuickAccessManagementUiState()) - val uiState: StateFlow = _uiState.asStateFlow() + private val _uiState = MutableStateFlow(mapOf>()) + val uiState: StateFlow>> = _uiState.asStateFlow() fun loadUiList() = viewModelScope.launch { - _uiState.update { - QuickAccessManagementUiState( - listOf( - QuickAccessManagementList( - "Health record", - listOf( - QuickAccessManagementItem("My Notes", false), - QuickAccessManagementItem("Immunization", true), - QuickAccessManagementItem("Medications", false), - QuickAccessManagementItem("Lab Results", false), - QuickAccessManagementItem("Special authority", false), - QuickAccessManagementItem("Health visit", false), - QuickAccessManagementItem("Clinic documents", false), - ) - ), - QuickAccessManagementList( - "Service", - listOf( - QuickAccessManagementItem("Organ donor", false), - ) - ), - QuickAccessManagementList( - "Dependents’ records", - listOf( - QuickAccessManagementItem("Jane", false), - QuickAccessManagementItem("Anne", false), - ) - ) - ) - ) - } + val manageableTiles = appFeatureRepository.getManageableTiles() + .map { it.toUiItem() } + .groupBy { it.categoryId } + + _uiState.update { manageableTiles } } - fun toggleItem(item: QuickAccessManagementItem) { - item.selected = item.selected.not() + fun toggleItem(item: QuickAccessTileItem) { + item.enabled = item.enabled.not() } fun saveSelection() { // todo - val result = _uiState.value.uiList + val result = _uiState.value println(result) } - - data class QuickAccessManagementUiState( - val uiList: List = listOf() - ) - - data class QuickAccessManagementList( - val tileCategory: String, - val tiles: List - ) - - data class QuickAccessManagementItem( - val tileName: String, - var selected: Boolean - ) } diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json index d4d2f56c5..e76ef5684 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 14, - "identityHash": "c9795b9cfc67222bed0194b4722c64e4", + "identityHash": "60e44af13a556d67a2aed22c9e362661", "entities": [ { "tableName": "patient", @@ -2061,14 +2061,15 @@ }, "indices": [ { - "name": "index_app_feature_feature_name_id_feature_icon_id", + "name": "index_app_feature_feature_name_id_feature_icon_id_feature_name", "unique": true, "columnNames": [ "feature_name_id", - "feature_icon_id" + "feature_icon_id", + "feature_name" ], "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_app_feature_feature_name_id_feature_icon_id` ON `${TABLE_NAME}` (`feature_name_id`, `feature_icon_id`)" + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_app_feature_feature_name_id_feature_icon_id_feature_name` ON `${TABLE_NAME}` (`feature_name_id`, `feature_icon_id`, `feature_name`)" } ], "foreignKeys": [] @@ -2077,7 +2078,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c9795b9cfc67222bed0194b4722c64e4')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '60e44af13a556d67a2aed22c9e362661')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt index 13315b3c0..078aab0b6 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt @@ -14,8 +14,14 @@ class AppFeatureLocalDataSource @Inject constructor( return appFeatureDao.insert(appFeatureDto.toEntity()) } - suspend fun getAppFeaturesWithQuickAccessTiles(): List { - return appFeatureDao.getAllFeatureWithQuickAccessTiles().map { + suspend fun getQuickAccessTiles(): List { + return appFeatureDao.getQuickAccessTiles().map { + it.toDto() + } + } + + suspend fun getManageableTiles(): List { + return appFeatureDao.getManageableTiles().map { it.toDto() } } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt index b3a51c59c..78053950a 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt @@ -16,5 +16,8 @@ interface AppFeatureDao { suspend fun deleteAll() @Query("SELECT * FROM app_feature WHERE quick_access_enabled = 1 ") - suspend fun getAllFeatureWithQuickAccessTiles(): List + suspend fun getQuickAccessTiles(): List + + @Query("SELECT * FROM app_feature WHERE is_management_enabled = 1 ") + suspend fun getManageableTiles(): List } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt index 7c91ee34a..9d7f8e01a 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt @@ -7,7 +7,7 @@ import androidx.room.PrimaryKey @Entity( tableName = "app_feature", - indices = [Index(value = ["feature_name_id", "feature_icon_id"], unique = true)] + indices = [Index(value = ["feature_name_id", "feature_icon_id", "feature_name"], unique = true)] ) data class AppFeatureEntity( @PrimaryKey(autoGenerate = true) diff --git a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt index 785e0b35c..844984117 100644 --- a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt +++ b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt @@ -58,7 +58,6 @@ import ca.bc.gov.repository.services.DiagnosticImagingRepository import ca.bc.gov.repository.services.OrganDonorRepository import ca.bc.gov.repository.services.PatientServicesRepository import ca.bc.gov.repository.settings.AppFeatureRepository -import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository import ca.bc.gov.repository.testrecord.CovidOrderRepository import ca.bc.gov.repository.testrecord.CovidTestRepository import ca.bc.gov.repository.utils.Base64ToInputImageConverter @@ -357,11 +356,4 @@ class RepositoriesModule { fun provideAppFeatureRepository( appFeatureLocalDataSource: AppFeatureLocalDataSource ): AppFeatureRepository = AppFeatureRepository(appFeatureLocalDataSource) - - @Provides - @Singleton - fun providesAppFeatureWithQuickAccessTilesRepository( - appFeatureRepository: AppFeatureRepository - ): AppFeatureWithQuickAccessTilesRepository = - AppFeatureWithQuickAccessTilesRepository(appFeatureRepository) } diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt index 6dad4ac46..078058660 100644 --- a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt @@ -7,10 +7,11 @@ import javax.inject.Inject class AppFeatureRepository @Inject constructor( private val appFeatureLocalDataSource: AppFeatureLocalDataSource ) { - suspend fun insert(appFeatureDto: AppFeatureDto) { appFeatureLocalDataSource.insert(appFeatureDto) } - suspend fun getAppFeaturesWithQuickAccessTiles() = appFeatureLocalDataSource.getAppFeaturesWithQuickAccessTiles() + suspend fun getQuickAccessTiles() = appFeatureLocalDataSource.getQuickAccessTiles() + + suspend fun getManageableTiles() = appFeatureLocalDataSource.getManageableTiles() } diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt deleted file mode 100644 index 6ffb815f7..000000000 --- a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ca.bc.gov.repository.settings - -import javax.inject.Inject - -class AppFeatureWithQuickAccessTilesRepository @Inject constructor( - private val appFeatureRepository: AppFeatureRepository -) { - - suspend fun getAppFeaturesWithQuickAccessTiles() = appFeatureRepository.getAppFeaturesWithQuickAccessTiles() -} From 6b5e8b5183156e96bf30a5010bf93a64213e713d Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 12 Jul 2023 14:44:50 -0700 Subject: [PATCH 12/38] HAPP-1539: Update QuickAccess flag --- .../ca/bc/gov/bchealth/SplashViewModel.kt | 22 +-- .../bchealth/ui/home/HomeComposeViewModel.kt | 9 +- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 4 + .../manage/QuickAccessManagementFragment.kt | 1 + .../manage/QuickAccessManagementScreen.kt | 160 ++++++++---------- .../manage/QuickAccessManagementViewModel.kt | 35 +++- .../14.json | 18 +- .../local/AppFeatureLocalDataSource.kt | 17 +- .../datasource/local/dao/AppFeatureDao.kt | 6 + .../local/entity/settings/AppFeatureEntity.kt | 2 - .../settings/AppFeatureRepository.kt | 8 +- 11 files changed, 143 insertions(+), 139 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index cfc22e0d8..114097020 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -167,28 +167,8 @@ class SplashViewModel @Inject constructor( isManagementEnabled = true, isQuickAccessEnabled = false, ), - - AppFeatureDto( - featureName = "To-do: Jane", - categoryId = R.string.feature_category_dependents_records, - featureIconId = R.drawable.icon_tile_health_record, - destinationId = R.id.health_records, - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), - - AppFeatureDto( - featureName = "To-do: Anne", - categoryId = R.string.feature_category_dependents_records, - featureIconId = R.drawable.icon_tile_health_record, - destinationId = R.id.health_records, - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), ) - appFeatures.forEach { - appFeatureRepository.insert(it) - } + appFeatureRepository.insert(appFeatures) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt index 59e0219d1..4b7955b72 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -33,31 +33,35 @@ data class HomeComposeUiState( ) sealed class QuickAccessTileItem( + open val id: Long, open val destinationId: Int, open val categoryId: Int, open var enabled: Boolean, ) { data class PredefinedItem( + override val id: Long, val icon: Int, val nameId: Int, override val destinationId: Int, override val categoryId: Int, override var enabled: Boolean, - ) : QuickAccessTileItem(destinationId, categoryId, enabled) + ) : QuickAccessTileItem(id, destinationId, categoryId, enabled) data class DynamicItem( + override val id: Long, val icon: Int, val nameId: Int?, val text: String, override val destinationId: Int, override val categoryId: Int, override var enabled: Boolean, - ) : QuickAccessTileItem(destinationId, categoryId, enabled) + ) : QuickAccessTileItem(id, destinationId, categoryId, enabled) } fun AppFeatureDto.toUiItem(): QuickAccessTileItem = if (this.featureName == null) { QuickAccessTileItem.PredefinedItem( + id = this.id, icon = this.featureIconId, nameId = this.featureNameId ?: -1, destinationId = this.destinationId, @@ -66,6 +70,7 @@ fun AppFeatureDto.toUiItem(): QuickAccessTileItem = ) } else { QuickAccessTileItem.DynamicItem( + id = this.id, icon = this.featureIconId, nameId = this.featureNameId, text = this.featureName.orEmpty(), diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 5127c5141..6f82e1420 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -122,6 +122,7 @@ private fun HomeScreenPreview() { onQuickAccessTileClicked = {}, quickAccessTileItems = listOf( QuickAccessTileItem.DynamicItem( + id = 0, icon = R.drawable.ic_health_record, nameId = R.string.feature_quick_action_dependents, text = "Jane", @@ -130,6 +131,7 @@ private fun HomeScreenPreview() { enabled = true, ), QuickAccessTileItem.DynamicItem( + id = 0, icon = R.drawable.ic_health_record, nameId = R.string.feature_quick_action_dependents, text = "James", @@ -138,6 +140,7 @@ private fun HomeScreenPreview() { enabled = true, ), QuickAccessTileItem.DynamicItem( + id = 0, icon = R.drawable.ic_health_record, nameId = null, text = "Dynamic text", @@ -146,6 +149,7 @@ private fun HomeScreenPreview() { enabled = true, ), QuickAccessTileItem.PredefinedItem( + id = 0, icon = R.drawable.ic_health_record, nameId = R.string.immnz_schedules_infant, destinationId = -1, diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt index 877c755f7..aadfdc664 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt @@ -47,6 +47,7 @@ class QuickAccessManagementFragment : BaseFragment(null) { QuickAccessManagementScreen( viewModel = viewModel, onClickItem = ::onClickItem, + onUpdateCompleted = ::popNavigation, modifier = Modifier .statusBarsPadding() .navigationBarsPadding() diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt index abfa4730f..e1b82fcbf 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt @@ -1,6 +1,7 @@ package ca.bc.gov.bchealth.ui.home.manage import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope @@ -13,6 +14,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Card import androidx.compose.material.Checkbox +import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -24,6 +26,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography import ca.bc.gov.bchealth.compose.bold import ca.bc.gov.bchealth.compose.theme.primaryBlue @@ -35,57 +39,70 @@ import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem fun QuickAccessManagementScreen( viewModel: QuickAccessManagementViewModel, onClickItem: (QuickAccessTileItem) -> Unit, + onUpdateCompleted: () -> Unit, modifier: Modifier = Modifier ) { val uiState = viewModel.uiState.collectAsState().value LaunchedEffect(Unit) { - viewModel.loadUiList() + viewModel.loadTilesUi() } QuickAccessManagementContent(uiState, onClickItem, modifier) + + if (uiState.isUpdateCompleted) { + onUpdateCompleted.invoke() + } } @Composable private fun QuickAccessManagementContent( - uiState: Map>, + uiState: QuickAccessManagementViewModel.QuickAccessManagementUiState, onClickItem: (QuickAccessTileItem) -> Unit, modifier: Modifier = Modifier ) { - LazyColumn( - modifier = modifier, - contentPadding = PaddingValues(start = 32.dp, end = 32.dp, top = 20.dp, bottom = 64.dp), - content = { - item { - Text( - text = stringResource(id = R.string.quick_access_management_body), - style = MyHealthTypography.body1, - color = primaryBlue - ) - } - - item { Spacer(modifier = Modifier.size(16.dp)) } - - for ((category, tiles) in uiState) { + Box(modifier = modifier) { + LazyColumn( + modifier = modifier, + contentPadding = PaddingValues(start = 32.dp, end = 32.dp, top = 20.dp, bottom = 64.dp), + content = { item { Text( - text = stringResource(id = category), - style = MyHealthTypography.body1.bold(), - color = statusBlue + text = stringResource(id = R.string.quick_access_management_body), + style = MyHealthTypography.body1, + color = primaryBlue ) } - item { Spacer(modifier = Modifier.size(12.dp)) } + item { Spacer(modifier = Modifier.size(16.dp)) } - items(tiles) { tile -> - TileItemUi(tile, onClickItem) - Spacer(modifier = Modifier.size(10.dp)) - } + for ((category, tiles) in uiState.uiMap) { + item { + Text( + text = stringResource(id = category), + style = MyHealthTypography.body1.bold(), + color = statusBlue + ) + } + + item { Spacer(modifier = Modifier.size(12.dp)) } - item { Spacer(modifier = Modifier.size(6.dp)) } + items(tiles) { tile -> + TileItemUi(tile, onClickItem) + Spacer(modifier = Modifier.size(10.dp)) + } + + item { Spacer(modifier = Modifier.size(6.dp)) } + } } + ) + + if (uiState.isLoading) { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center), + ) } - ) + } } @Composable @@ -141,63 +158,32 @@ private fun RowScope.TileName(item: QuickAccessTileItem) { ) } -// @BasePreview -// @Composable -// private fun PreviewQuickAccessManagementContent() { -// MyHealthTheme { -// QuickAccessManagementContent( -// listOf( -// QuickAccessManagementViewModel.QuickAccessManagementList( -// "Health record", -// listOf( -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "My Notes", -// false -// ), -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "Immunization", -// true -// ), -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "Medications", -// false -// ), -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "Lab Results", -// false -// ), -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "Special authority", -// false -// ), -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "Health visit", -// false -// ), -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "Clinic documents", -// false -// ), -// ) -// ), -// QuickAccessManagementViewModel.QuickAccessManagementList( -// "Service", -// listOf( -// QuickAccessManagementViewModel.QuickAccessManagementItem( -// "Organ donor", -// false -// ), -// ) -// ), -// QuickAccessManagementViewModel.QuickAccessManagementList( -// "Dependents’ records", -// listOf( -// QuickAccessManagementViewModel.QuickAccessManagementItem("Jane", false), -// QuickAccessManagementViewModel.QuickAccessManagementItem("Anne", false), -// ) -// ) -// ), -// {} -// ) -// } -// } +@BasePreview +@Composable +private fun PreviewQuickAccessManagementContent() { + val sample = QuickAccessTileItem.PredefinedItem( + id = -1, + destinationId = -1, + categoryId = -1, + enabled = false, + icon = -1, + nameId = -1 + ) + + MyHealthTheme { + QuickAccessManagementContent( + QuickAccessManagementViewModel.QuickAccessManagementUiState( + mapOf( + R.string.health_records to listOf( + sample.copy(nameId = R.string.feature_medications, enabled = true), + sample.copy(nameId = R.string.feature_health_visit, enabled = false), + ), + R.string.services to listOf( + sample.copy(nameId = R.string.organ_donor, enabled = true), + ), + ) + ), + {}, + ) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt index c8d62d101..9c884ff01 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt @@ -6,6 +6,7 @@ import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem import ca.bc.gov.bchealth.ui.home.toUiItem import ca.bc.gov.repository.settings.AppFeatureRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -18,15 +19,17 @@ class QuickAccessManagementViewModel @Inject constructor( private val appFeatureRepository: AppFeatureRepository ) : ViewModel() { - private val _uiState = MutableStateFlow(mapOf>()) - val uiState: StateFlow>> = _uiState.asStateFlow() + private val _uiState = MutableStateFlow(QuickAccessManagementUiState()) + val uiState: StateFlow = _uiState.asStateFlow() - fun loadUiList() = viewModelScope.launch { - val manageableTiles = appFeatureRepository.getManageableTiles() + fun loadTilesUi() = viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + + val tiles = appFeatureRepository.getManageableTiles() .map { it.toUiItem() } .groupBy { it.categoryId } - _uiState.update { manageableTiles } + _uiState.update { it.copy(uiMap = tiles, isLoading = false) } } fun toggleItem(item: QuickAccessTileItem) { @@ -34,8 +37,24 @@ class QuickAccessManagementViewModel @Inject constructor( } fun saveSelection() { - // todo - val result = _uiState.value - println(result) + viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + + for ((_, features) in _uiState.value.uiMap) { + features.forEach { tileItem -> + appFeatureRepository.updateQuickAccessFlag(tileItem.id, tileItem.enabled) + } + } + + delay(300L) + + _uiState.update { it.copy(isLoading = false, isUpdateCompleted = true) } + } } + + data class QuickAccessManagementUiState( + val uiMap: Map> = mapOf(), + val isLoading: Boolean = false, + val isUpdateCompleted: Boolean = false, + ) } diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json index e76ef5684..af677ad19 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 14, - "identityHash": "60e44af13a556d67a2aed22c9e362661", + "identityHash": "c437306952b7aadf43512299ea481345", "entities": [ { "tableName": "patient", @@ -2059,26 +2059,14 @@ "id" ] }, - "indices": [ - { - "name": "index_app_feature_feature_name_id_feature_icon_id_feature_name", - "unique": true, - "columnNames": [ - "feature_name_id", - "feature_icon_id", - "feature_name" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_app_feature_feature_name_id_feature_icon_id_feature_name` ON `${TABLE_NAME}` (`feature_name_id`, `feature_icon_id`, `feature_name`)" - } - ], + "indices": [], "foreignKeys": [] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '60e44af13a556d67a2aed22c9e362661')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c437306952b7aadf43512299ea481345')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt index 078aab0b6..1c6533da8 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt @@ -10,8 +10,17 @@ class AppFeatureLocalDataSource @Inject constructor( private val appFeatureDao: AppFeatureDao ) { - suspend fun insert(appFeatureDto: AppFeatureDto): Long { - return appFeatureDao.insert(appFeatureDto.toEntity()) + suspend fun insertIfEmpty(appFeatures: List) { + val count = appFeatureDao.countRegisters() + if (count == 0) { + appFeatures.forEach { dto -> + appFeatureDao.insert(dto.toEntity()) + } + } + } + + suspend fun updateQuickAccessFlag(id: Long, enabled: Boolean) { + appFeatureDao.updateQuickAccessFlag(id, enabled) } suspend fun getQuickAccessTiles(): List { @@ -25,4 +34,8 @@ class AppFeatureLocalDataSource @Inject constructor( it.toDto() } } + + suspend fun deleteAll() { + appFeatureDao.deleteAll() + } } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt index 78053950a..16093e335 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt @@ -12,9 +12,15 @@ interface AppFeatureDao { @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insert(appFeatureEntity: AppFeatureEntity): Long + @Query("UPDATE app_feature SET quick_access_enabled = :enabled WHERE ID = :appFeatureId") + suspend fun updateQuickAccessFlag(appFeatureId: Long, enabled: Boolean) + @Query("DELETE FROM app_feature") suspend fun deleteAll() + @Query("SELECT count(*) FROM app_feature") + suspend fun countRegisters(): Int + @Query("SELECT * FROM app_feature WHERE quick_access_enabled = 1 ") suspend fun getQuickAccessTiles(): List diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt index 9d7f8e01a..d8d5b01c8 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt @@ -2,12 +2,10 @@ package ca.bc.gov.data.datasource.local.entity.settings import androidx.room.ColumnInfo import androidx.room.Entity -import androidx.room.Index import androidx.room.PrimaryKey @Entity( tableName = "app_feature", - indices = [Index(value = ["feature_name_id", "feature_icon_id", "feature_name"], unique = true)] ) data class AppFeatureEntity( @PrimaryKey(autoGenerate = true) diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt index 078058660..666b12e21 100644 --- a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt @@ -7,8 +7,12 @@ import javax.inject.Inject class AppFeatureRepository @Inject constructor( private val appFeatureLocalDataSource: AppFeatureLocalDataSource ) { - suspend fun insert(appFeatureDto: AppFeatureDto) { - appFeatureLocalDataSource.insert(appFeatureDto) + suspend fun insert(appFeatures: List) { + appFeatureLocalDataSource.insertIfEmpty(appFeatures) + } + + suspend fun updateQuickAccessFlag(id: Long, enabled: Boolean) { + appFeatureLocalDataSource.updateQuickAccessFlag(id, enabled) } suspend fun getQuickAccessTiles() = appFeatureLocalDataSource.getQuickAccessTiles() From 4282bd817e2800bf8049d5213d5448d5e0498b3f Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 12 Jul 2023 16:28:15 -0700 Subject: [PATCH 13/38] HAPP-1539: Update QuickAccess icons --- .../java/ca/bc/gov/bchealth/SplashViewModel.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index 114097020..07d665978 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -75,7 +75,7 @@ class SplashViewModel @Inject constructor( featureNameId = R.string.immunization_schedules, categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_tile_immunization_schedules, - destinationId = R.id.health_records, + destinationId = R.id.immunizationSchedulesFragment, isManagementEnabled = false, isQuickAccessEnabled = true, ), @@ -108,7 +108,7 @@ class SplashViewModel @Inject constructor( AppFeatureDto( featureNameId = R.string.feature_immunization, categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, + featureIconId = R.drawable.ic_health_record_vaccine, destinationId = R.id.health_records, isManagementEnabled = true, isQuickAccessEnabled = false, @@ -117,7 +117,7 @@ class SplashViewModel @Inject constructor( AppFeatureDto( featureNameId = R.string.feature_medications, categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, + featureIconId = R.drawable.ic_health_record_medication, destinationId = R.id.health_records, isManagementEnabled = true, isQuickAccessEnabled = false, @@ -126,7 +126,7 @@ class SplashViewModel @Inject constructor( AppFeatureDto( featureNameId = R.string.feature_lab_results, categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, + featureIconId = R.drawable.ic_lab_test, destinationId = R.id.health_records, isManagementEnabled = true, isQuickAccessEnabled = false, @@ -135,7 +135,7 @@ class SplashViewModel @Inject constructor( AppFeatureDto( featureNameId = R.string.feature_special_authority, categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, + featureIconId = R.drawable.ic_health_record_special_authority, destinationId = R.id.health_records, isManagementEnabled = true, isQuickAccessEnabled = false, @@ -144,7 +144,7 @@ class SplashViewModel @Inject constructor( AppFeatureDto( featureNameId = R.string.feature_health_visit, categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, + featureIconId = R.drawable.ic_health_record_health_visit, destinationId = R.id.health_records, isManagementEnabled = true, isQuickAccessEnabled = false, @@ -153,7 +153,7 @@ class SplashViewModel @Inject constructor( AppFeatureDto( featureNameId = R.string.feature_clinic_documents, categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, + featureIconId = R.drawable.ic_health_record_clinical_document, destinationId = R.id.health_records, isManagementEnabled = true, isQuickAccessEnabled = false, @@ -162,7 +162,7 @@ class SplashViewModel @Inject constructor( AppFeatureDto( featureNameId = R.string.feature_organ_donor, categoryId = R.string.feature_category_service, - featureIconId = R.drawable.icon_tile_health_record, + featureIconId = R.drawable.ic_organ_donor, destinationId = R.id.health_records, isManagementEnabled = true, isQuickAccessEnabled = false, From a83c494e364e46426031b68c03f4b1126dc3b028 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 12 Jul 2023 16:43:25 -0700 Subject: [PATCH 14/38] HAPP-1539: Redirect user to TileManagement --- .../bchealth/ui/home/HomeComposeFragment.kt | 5 +++ .../bc/gov/bchealth/ui/home/HomeFragment.kt | 1 + .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 40 +++++++++++++++---- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt index 02074f581..ab22a3380 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt @@ -46,6 +46,7 @@ class HomeComposeFragment : BaseSecureFragment(null) { .navigationBarsPadding() .padding(it), viewModel, + onClickManage = ::onClickManage, onQuickAccessTileClicked = ::onQuickAccessTileClicked ) } @@ -53,6 +54,10 @@ class HomeComposeFragment : BaseSecureFragment(null) { } } + private fun onClickManage() { + findNavController().navigate(R.id.quickAccessManagementFragment) + } + private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { findNavController().navigate(quickAccessTileItem.destinationId) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt index 36d2f9204..9f950a5b1 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt @@ -131,6 +131,7 @@ class HomeFragment : BaseSecureFragment(null) { .navigationBarsPadding() .padding(it), homeViewModel, + onClickManage = {}, onQuickAccessTileClicked = {} ) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 6f82e1420..690072269 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -1,8 +1,11 @@ package ca.bc.gov.bchealth.ui.home +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid @@ -28,19 +31,26 @@ import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme fun HomeScreen( modifier: Modifier = Modifier, viewModel: HomeComposeViewModel, - onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit + onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit, + onClickManage: () -> Unit, ) { val uiState = viewModel.uiState.collectAsState().value LaunchedEffect(key1 = Unit) { viewModel.loadQuickAccessTiles() } - HomeScreenContent(modifier, onQuickAccessTileClicked, uiState.quickAccessTileItems) + HomeScreenContent( + modifier, + onQuickAccessTileClicked, + onClickManage, + uiState.quickAccessTileItems + ) } @Composable private fun HomeScreenContent( modifier: Modifier = Modifier, onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit, + onClickManage: () -> Unit, quickAccessTileItems: List ) { LazyVerticalGrid( @@ -69,12 +79,25 @@ private fun HomeScreenContent( } item(span = { GridItemSpan(maxLineSpan) }) { - Text( - text = stringResource(id = R.string.quick_access), - style = MaterialTheme.typography.subtitle2, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.primary - ) + Row(Modifier.fillMaxWidth()) { + Text( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.quick_access), + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary + ) + + //todo: Replace it: HAPP-1537 + Text( + modifier = Modifier.clickable { onClickManage.invoke() }, + text = "Manage", + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary + ) + + } } items(quickAccessTileItems) { @@ -120,6 +143,7 @@ private fun HomeScreenPreview() { HealthGatewayTheme { HomeScreenContent( onQuickAccessTileClicked = {}, + onClickManage = {}, quickAccessTileItems = listOf( QuickAccessTileItem.DynamicItem( id = 0, From 3a245671fe33d4fe9512c30281e2b0ca2d74b862 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 12 Jul 2023 16:48:00 -0700 Subject: [PATCH 15/38] Apply spotless changes --- app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 690072269..2f7e23653 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -88,7 +88,7 @@ private fun HomeScreenContent( color = MaterialTheme.colors.primary ) - //todo: Replace it: HAPP-1537 + // todo: Replace it: HAPP-1537 Text( modifier = Modifier.clickable { onClickManage.invoke() }, text = "Manage", @@ -96,7 +96,6 @@ private fun HomeScreenContent( fontWeight = FontWeight.Bold, color = MaterialTheme.colors.primary ) - } } From 82b81c192f064180a707d1da4970a13fd394ade3 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Thu, 13 Jul 2023 10:52:14 -0700 Subject: [PATCH 16/38] HAPP-1539: Fix toolbar elevation --- .../bchealth/ui/home/manage/QuickAccessManagementFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt index aadfdc664..5f6fedc70 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt @@ -15,6 +15,7 @@ import androidx.fragment.app.viewModels import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.ui.BaseFragment +import ca.bc.gov.bchealth.ui.custom.AppBarDefaults import ca.bc.gov.bchealth.ui.custom.MyHealthBackButton import ca.bc.gov.bchealth.ui.custom.MyHealthToolBar import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem @@ -40,7 +41,8 @@ class QuickAccessManagementFragment : BaseFragment(null) { tint = MaterialTheme.colors.primary ) } - } + }, + elevation = AppBarDefaults.TopAppBarElevation ) }, content = { From 10aadf638b4f3b552737fbd91be2d4dbc64cd3f5 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Thu, 13 Jul 2023 10:52:44 -0700 Subject: [PATCH 17/38] HAPP-1539: Lift loading state --- .../manage/QuickAccessManagementScreen.kt | 92 +++++++++---------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt index e1b82fcbf..30560cf4f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt @@ -48,7 +48,15 @@ fun QuickAccessManagementScreen( viewModel.loadTilesUi() } - QuickAccessManagementContent(uiState, onClickItem, modifier) + Box { + QuickAccessManagementContent(uiState.uiMap, onClickItem, modifier) + + if (uiState.isLoading) { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center), + ) + } + } if (uiState.isUpdateCompleted) { onUpdateCompleted.invoke() @@ -57,52 +65,44 @@ fun QuickAccessManagementScreen( @Composable private fun QuickAccessManagementContent( - uiState: QuickAccessManagementViewModel.QuickAccessManagementUiState, + uiMap: Map>, onClickItem: (QuickAccessTileItem) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { - Box(modifier = modifier) { - LazyColumn( - modifier = modifier, - contentPadding = PaddingValues(start = 32.dp, end = 32.dp, top = 20.dp, bottom = 64.dp), - content = { + LazyColumn( + modifier = modifier, + contentPadding = PaddingValues(start = 32.dp, end = 32.dp, top = 20.dp, bottom = 64.dp), + content = { + item { + Text( + text = stringResource(id = R.string.quick_access_management_body), + style = MyHealthTypography.body1, + color = primaryBlue + ) + } + + item { Spacer(modifier = Modifier.size(16.dp)) } + + for ((category, tiles) in uiMap) { item { Text( - text = stringResource(id = R.string.quick_access_management_body), - style = MyHealthTypography.body1, - color = primaryBlue + text = stringResource(id = category), + style = MyHealthTypography.body1.bold(), + color = statusBlue ) } - item { Spacer(modifier = Modifier.size(16.dp)) } - - for ((category, tiles) in uiState.uiMap) { - item { - Text( - text = stringResource(id = category), - style = MyHealthTypography.body1.bold(), - color = statusBlue - ) - } + item { Spacer(modifier = Modifier.size(12.dp)) } - item { Spacer(modifier = Modifier.size(12.dp)) } - - items(tiles) { tile -> - TileItemUi(tile, onClickItem) - Spacer(modifier = Modifier.size(10.dp)) - } - - item { Spacer(modifier = Modifier.size(6.dp)) } + items(tiles) { tile -> + TileItemUi(tile, onClickItem) + Spacer(modifier = Modifier.size(10.dp)) } - } - ) - if (uiState.isLoading) { - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center), - ) + item { Spacer(modifier = Modifier.size(6.dp)) } + } } - } + ) } @Composable @@ -172,18 +172,16 @@ private fun PreviewQuickAccessManagementContent() { MyHealthTheme { QuickAccessManagementContent( - QuickAccessManagementViewModel.QuickAccessManagementUiState( - mapOf( - R.string.health_records to listOf( - sample.copy(nameId = R.string.feature_medications, enabled = true), - sample.copy(nameId = R.string.feature_health_visit, enabled = false), - ), - R.string.services to listOf( - sample.copy(nameId = R.string.organ_donor, enabled = true), - ), - ) + mapOf( + R.string.health_records to listOf( + sample.copy(nameId = R.string.feature_medications, enabled = true), + sample.copy(nameId = R.string.feature_health_visit, enabled = false), + ), + R.string.services to listOf( + sample.copy(nameId = R.string.organ_donor, enabled = true), + ), ), - {}, + {} ) } } From fc22092b7c7e0cae0f189e29b1dea7d8f699e894 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Thu, 13 Jul 2023 13:35:36 -0700 Subject: [PATCH 18/38] HAPP-1540: Redirect user to filtered records --- .../ca/bc/gov/bchealth/SplashViewModel.kt | 6 ++++ .../bchealth/ui/home/HomeComposeFragment.kt | 11 ++++++- .../bchealth/ui/home/HomeComposeViewModel.kt | 9 +++-- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 33 +++++++++---------- .../manage/QuickAccessManagementScreen.kt | 1 + .../common/model/settings/AppFeatureDto.kt | 1 + .../14.json | 12 +++++-- .../local/entity/settings/AppFeatureEntity.kt | 2 ++ .../data/model/mapper/DtoToEntityMapper.kt | 1 + .../data/model/mapper/EntityToDtoMapper.kt | 1 + 10 files changed, 53 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index 07d665978..52491b33a 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -110,6 +110,7 @@ class SplashViewModel @Inject constructor( categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_health_record_vaccine, destinationId = R.id.health_records, + destinationParam = "Immunization", isManagementEnabled = true, isQuickAccessEnabled = false, ), @@ -119,6 +120,7 @@ class SplashViewModel @Inject constructor( categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_health_record_medication, destinationId = R.id.health_records, + destinationParam = "Medications", isManagementEnabled = true, isQuickAccessEnabled = false, ), @@ -128,6 +130,7 @@ class SplashViewModel @Inject constructor( categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_lab_test, destinationId = R.id.health_records, + destinationParam = "Laboratory", isManagementEnabled = true, isQuickAccessEnabled = false, ), @@ -137,6 +140,7 @@ class SplashViewModel @Inject constructor( categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_health_record_special_authority, destinationId = R.id.health_records, + destinationParam = "SpecialAuthority", isManagementEnabled = true, isQuickAccessEnabled = false, ), @@ -146,6 +150,7 @@ class SplashViewModel @Inject constructor( categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_health_record_health_visit, destinationId = R.id.health_records, + destinationParam = "HealthVisit", isManagementEnabled = true, isQuickAccessEnabled = false, ), @@ -155,6 +160,7 @@ class SplashViewModel @Inject constructor( categoryId = R.string.feature_category_health_record, featureIconId = R.drawable.ic_health_record_clinical_document, destinationId = R.id.health_records, + destinationParam = "ClinicalDocument", isManagementEnabled = true, isQuickAccessEnabled = false, ), diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt index ab22a3380..cfadab7ac 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt @@ -7,6 +7,7 @@ import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R @@ -14,12 +15,14 @@ import ca.bc.gov.bchealth.compose.component.HGTopAppBar import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import ca.bc.gov.bchealth.ui.BaseSecureFragment +import ca.bc.gov.bchealth.ui.filter.TimelineTypeFilter +import ca.bc.gov.bchealth.ui.healthrecord.filter.PatientFilterViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class HomeComposeFragment : BaseSecureFragment(null) { - private val viewModel: HomeComposeViewModel by viewModels() + private val filterSharedViewModel: PatientFilterViewModel by activityViewModels() @Composable override fun GetComposableLayout() { @@ -59,6 +62,12 @@ class HomeComposeFragment : BaseSecureFragment(null) { } private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { + if (quickAccessTileItem.destinationId == R.id.health_records && quickAccessTileItem.destinationParam != null) { + filterSharedViewModel.updateFilter( + listOf(TimelineTypeFilter.findByFilterValue(quickAccessTileItem.destinationParam!!).name), + ) + } + findNavController().navigate(quickAccessTileItem.destinationId) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt index 4b7955b72..346e87549 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -35,6 +35,7 @@ data class HomeComposeUiState( sealed class QuickAccessTileItem( open val id: Long, open val destinationId: Int, + open val destinationParam: String?, open val categoryId: Int, open var enabled: Boolean, ) { @@ -43,9 +44,10 @@ sealed class QuickAccessTileItem( val icon: Int, val nameId: Int, override val destinationId: Int, + override val destinationParam: String?, override val categoryId: Int, override var enabled: Boolean, - ) : QuickAccessTileItem(id, destinationId, categoryId, enabled) + ) : QuickAccessTileItem(id, destinationId, destinationParam, categoryId, enabled) data class DynamicItem( override val id: Long, @@ -53,9 +55,10 @@ sealed class QuickAccessTileItem( val nameId: Int?, val text: String, override val destinationId: Int, + override val destinationParam: String?, override val categoryId: Int, override var enabled: Boolean, - ) : QuickAccessTileItem(id, destinationId, categoryId, enabled) + ) : QuickAccessTileItem(id, destinationId, destinationParam, categoryId, enabled) } fun AppFeatureDto.toUiItem(): QuickAccessTileItem = @@ -65,6 +68,7 @@ fun AppFeatureDto.toUiItem(): QuickAccessTileItem = icon = this.featureIconId, nameId = this.featureNameId ?: -1, destinationId = this.destinationId, + destinationParam = this.destinationParam, categoryId = this.categoryId, enabled = this.isQuickAccessEnabled ) @@ -75,6 +79,7 @@ fun AppFeatureDto.toUiItem(): QuickAccessTileItem = nameId = this.featureNameId, text = this.featureName.orEmpty(), destinationId = this.destinationId, + destinationParam = this.destinationParam, categoryId = this.categoryId, enabled = this.isQuickAccessEnabled ) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 2f7e23653..174d7a050 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -139,43 +139,40 @@ private fun QuickAccessTileItemUi( @Composable @BasePreview private fun HomeScreenPreview() { + val dynamicItemSample = QuickAccessTileItem.DynamicItem( + id = 0, + icon = R.drawable.ic_health_record, + nameId = -1, + text = "", + destinationId = -1, + destinationParam = null, + categoryId = -1, + enabled = true, + ) + HealthGatewayTheme { HomeScreenContent( onQuickAccessTileClicked = {}, onClickManage = {}, quickAccessTileItems = listOf( - QuickAccessTileItem.DynamicItem( - id = 0, - icon = R.drawable.ic_health_record, + dynamicItemSample.copy( nameId = R.string.feature_quick_action_dependents, text = "Jane", - destinationId = -1, - categoryId = -1, - enabled = true, ), - QuickAccessTileItem.DynamicItem( - id = 0, - icon = R.drawable.ic_health_record, + dynamicItemSample.copy( nameId = R.string.feature_quick_action_dependents, text = "James", - destinationId = -1, - categoryId = -1, - enabled = true, ), - QuickAccessTileItem.DynamicItem( - id = 0, - icon = R.drawable.ic_health_record, + dynamicItemSample.copy( nameId = null, text = "Dynamic text", - destinationId = -1, - categoryId = -1, - enabled = true, ), QuickAccessTileItem.PredefinedItem( id = 0, icon = R.drawable.ic_health_record, nameId = R.string.immnz_schedules_infant, destinationId = -1, + destinationParam = null, categoryId = -1, enabled = true, ), diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt index 30560cf4f..8a6345071 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt @@ -164,6 +164,7 @@ private fun PreviewQuickAccessManagementContent() { val sample = QuickAccessTileItem.PredefinedItem( id = -1, destinationId = -1, + destinationParam = null, categoryId = -1, enabled = false, icon = -1, diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt index 287c43d52..86657c082 100644 --- a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt +++ b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt @@ -7,6 +7,7 @@ data class AppFeatureDto( val categoryId: Int, val featureIconId: Int, val destinationId: Int, + val destinationParam: String? = null, val isManagementEnabled: Boolean = false, val isQuickAccessEnabled: Boolean = false ) diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json index af677ad19..c18c9065d 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/14.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 14, - "identityHash": "c437306952b7aadf43512299ea481345", + "identityHash": "276a9e926008aa206fc634d8899c0b77", "entities": [ { "tableName": "patient", @@ -2000,7 +2000,7 @@ }, { "tableName": "app_feature", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name` TEXT, `feature_name_id` INTEGER, `category_name_id` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `is_management_enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name` TEXT, `feature_name_id` INTEGER, `category_name_id` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `destination_param` TEXT, `is_management_enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", "fields": [ { "fieldPath": "id", @@ -2038,6 +2038,12 @@ "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "destinationParam", + "columnName": "destination_param", + "affinity": "TEXT", + "notNull": false + }, { "fieldPath": "isManagementEnabled", "columnName": "is_management_enabled", @@ -2066,7 +2072,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c437306952b7aadf43512299ea481345')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '276a9e926008aa206fc634d8899c0b77')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt index d8d5b01c8..ad58cc283 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt @@ -20,6 +20,8 @@ data class AppFeatureEntity( val featureIconId: Int, @ColumnInfo(name = "destination_id") val destinationId: Int, + @ColumnInfo(name = "destination_param") + val destinationParam: String?, @ColumnInfo(name = "is_management_enabled", defaultValue = "false") val isManagementEnabled: Boolean = false, @ColumnInfo(name = "quick_access_enabled", defaultValue = "false") diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt index e07862bcc..f4bf61c68 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt @@ -366,6 +366,7 @@ fun AppFeatureDto.toEntity() = AppFeatureEntity( categoryId, featureIconId, destinationId, + destinationParam, isManagementEnabled, isQuickAccessEnabled ) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt index 444417919..73872a6c7 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt @@ -485,6 +485,7 @@ fun AppFeatureEntity.toDto() = AppFeatureDto( categoryId = categoryNameId, featureIconId = featureIconId, destinationId = destinationId, + destinationParam = destinationParam, isManagementEnabled = isManagementEnabled, isQuickAccessEnabled = isQuickAccessEnabled ) From 652bde290ec7dcedf547fab7ba31b7bbfc000f36 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Fri, 14 Jul 2023 08:50:50 -0700 Subject: [PATCH 19/38] Version 2.0.0-201 --- scripts/versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/versions.gradle b/scripts/versions.gradle index a2ed54cb7..27a50a3b6 100644 --- a/scripts/versions.gradle +++ b/scripts/versions.gradle @@ -6,7 +6,7 @@ versions.targetSdkVersion = 33 versions.compileSdkVersion = 33 //App -versions.versionName = '1.10.0' +versions.versionName = '2.0.0' versions.versionCode = 209 versions.localApiVersion = 2 From fdcc436639d4e794abe40c586037c025564305a4 Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Mon, 26 Jun 2023 09:20:01 -0700 Subject: [PATCH 20/38] HAPP-1535 - added home screen redesign 2.0 for non authenticated users HAPP-1535 - added new home screen 2.0 UI design for non authenticated user. --- .../ca/bc/gov/bchealth/compose/theme/Type.kt | 67 ++++++++++ .../13.json | 124 +++++++++++++++++- 2 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt new file mode 100644 index 000000000..261b38f13 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt @@ -0,0 +1,67 @@ +package ca.bc.gov.bchealth.compose.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import ca.bc.gov.bchealth.R + +val hgFonts = FontFamily( + Font(R.font.bc_sans_regular, weight = FontWeight.Normal, style = FontStyle.Normal), + Font(R.font.bc_sans_bold, weight = FontWeight.Bold, style = FontStyle.Normal), + Font(R.font.bc_sans_italic, weight = FontWeight.Normal, style = FontStyle.Italic), + Font(R.font.bc_sans_bold_italic, weight = FontWeight.Bold, style = FontStyle.Italic), +) + +internal val HealthGatewayTypography = Typography( + defaultFontFamily = hgFonts, + h1 = TextStyle( + fontSize = 60.sp, + lineHeight = 72.sp, + letterSpacing = 0.sp, + ), + h2 = TextStyle( + fontSize = 50.sp, + lineHeight = 64.sp, + letterSpacing = 0.sp, + ), + h3 = TextStyle( + fontSize = 40.sp, + lineHeight = 56.sp, + letterSpacing = 0.sp, + ), + h4 = TextStyle( + fontSize = 33.sp, + lineHeight = 40.sp, + letterSpacing = 0.sp, + ), + h5 = TextStyle( + fontSize = 24.sp, + lineHeight = 32.sp, + letterSpacing = 0.sp, + ), + subtitle1 = TextStyle( + fontSize = 20.sp, + lineHeight = 38.sp, + letterSpacing = 0.sp, + ), + subtitle2 = TextStyle( + fontSize = 17.sp, + lineHeight = 34.sp, + letterSpacing = 0.sp, + ), + body1 = TextStyle( + fontSize = 15.sp, + lineHeight = 24.sp, + letterSpacing = 0.sp, + ), + body2 = TextStyle( + fontSize = 13.sp, + lineHeight = 22.sp, + fontStyle = FontStyle.Normal, + letterSpacing = 0.sp, + ) +) diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json index ce7c843eb..6ae4d3df8 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 13, - "identityHash": "d250a01adf6d2d797cb46b6b9b0a2178", + "identityHash": "cd63a7ddb38d8002c82af9bc1156c3dc", "entities": [ { "tableName": "patient", @@ -1998,12 +1998,132 @@ }, "indices": [], "foreignKeys": [] + }, + { + "tableName": "app_feature", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name_id` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "featureNameId", + "columnName": "feature_name_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "featureIconId", + "columnName": "feature_icon_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "destinationId", + "columnName": "destination_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "isQuickAccessEnabled", + "columnName": "quick_access_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_app_feature_feature_name_id_feature_icon_id", + "unique": true, + "columnNames": [ + "feature_name_id", + "feature_icon_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_app_feature_feature_name_id_feature_icon_id` ON `${TABLE_NAME}` (`feature_name_id`, `feature_icon_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "quick_access_tile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_id` INTEGER NOT NULL, `tile_name_id` INTEGER NOT NULL, `tile_icon_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, FOREIGN KEY(`feature_id`) REFERENCES `app_feature`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "featureId", + "columnName": "feature_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tileNameId", + "columnName": "tile_name_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tileIconId", + "columnName": "tile_icon_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "app_feature", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "feature_id" + ], + "referencedColumns": [ + "id" + ] + } + ] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd250a01adf6d2d797cb46b6b9b0a2178')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cd63a7ddb38d8002c82af9bc1156c3dc')" ] } } \ No newline at end of file From 05d7d6117ad914cbfd5a1e818217db5ba114be2f Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Thu, 6 Jul 2023 10:34:41 -0700 Subject: [PATCH 21/38] HAPP-1537 - added handling for authenticated user home screen UI 2.0 --- .../java/ca/bc/gov/bchealth/MainActivity.kt | 7 +- .../ca/bc/gov/bchealth/SplashViewModel.kt | 163 ++---- .../compose/component/AnnouncementBannerUI.kt | 54 +- .../gov/bchealth/compose/component/Button.kt | 6 +- .../compose/component/HGProgressIndicator.kt | 52 ++ .../compose/component/LoginInfoCardUI.kt | 25 +- .../ca/bc/gov/bchealth/compose/theme/Type.kt | 67 --- .../ca/bc/gov/bchealth/ui/BaseFragment.kt | 18 + .../bc/gov/bchealth/ui/home/HomeBannerUI.kt | 248 ---------- .../ca/bc/gov/bchealth/ui/home/HomeCardUI.kt | 137 ----- .../bchealth/ui/home/HomeComposeFragment.kt | 73 --- .../bchealth/ui/home/HomeComposeViewModel.kt | 86 ---- .../bc/gov/bchealth/ui/home/HomeFragment.kt | 287 +++-------- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 350 +++++++++---- .../bc/gov/bchealth/ui/home/HomeViewModel.kt | 467 +++++++++--------- .../manage/QuickAccessManagementFragment.kt | 3 +- .../manage/QuickAccessManagementScreen.kt | 69 +-- .../manage/QuickAccessManagementViewModel.kt | 51 +- .../bchealth/ui/login/BcscAuthViewModel.kt | 13 +- .../bchealth/ui/services/ServicesFragment.kt | 20 +- .../bchealth/ui/services/ServicesScreen.kt | 11 +- .../ca/bc/gov/bchealth/utils/Extensions.kt | 5 +- .../gov/bchealth/viewmodel/SharedViewModel.kt | 1 + app/src/main/res/drawable/ic_anchor.xml | 9 + app/src/main/res/drawable/ic_heart.xml | 15 + app/src/main/res/navigation/home.xml | 23 +- app/src/main/res/values/strings.xml | 4 + .../ca/bc/gov/common/model/AppFeatureName.kt | 14 + .../gov/common/model/QuickAccessLinkName.kt | 19 + .../common/model/settings/AppFeatureDto.kt | 13 +- .../AppFeatureWithQuickAccessTilesDto.kt | 6 + .../model/settings/QuickAccessTileDto.kt | 8 +- .../13.json | 124 +---- .../local/AppFeatureLocalDataSource.kt | 30 +- .../data/datasource/local/MyHealthDataBase.kt | 40 +- .../local/QuickActionTileLocalDataSource.kt | 15 + .../converter/AppFeatureNameConverter.kt | 13 + .../converter/QuickAccessLinkNameConverter.kt | 13 + .../datasource/local/dao/AppFeatureDao.kt | 15 +- .../local/dao/QuickAccessTileDao.kt | 7 + .../AppFeatureWithQuickAccessTiles.kt | 17 + .../local/entity/settings/AppFeatureEntity.kt | 23 +- .../entity/settings/QuickAccessTileEntity.kt | 33 ++ .../bc/gov/data/di/LocalDataSourceModule.kt | 6 + .../data/model/mapper/DtoToEntityMapper.kt | 21 +- .../data/model/mapper/EntityToDtoMapper.kt | 30 +- .../data/model/mapper/ResponseToDtoMapper.kt | 23 +- .../preference/EncryptedPreferenceStorage.kt | 9 + .../bc/gov/repository/OnBoardingRepository.kt | 9 +- .../gov/repository/di/RepositoriesModule.kt | 17 + .../settings/AppFeatureRepository.kt | 11 +- ...ppFeatureWithQuickAccessTilesRepository.kt | 18 + .../settings/QuickAccessTileRepository.kt | 13 + scripts/versions.gradle | 8 +- 54 files changed, 1195 insertions(+), 1624 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/component/HGProgressIndicator.kt delete mode 100644 app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt delete mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt delete mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt create mode 100644 app/src/main/res/drawable/ic_anchor.xml create mode 100644 app/src/main/res/drawable/ic_heart.xml create mode 100644 common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt create mode 100644 common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt create mode 100644 common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/converter/AppFeatureNameConverter.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/converter/QuickAccessLinkNameConverter.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt create mode 100644 repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt create mode 100644 repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt index 4a0bcfc27..6f85ec7a0 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt @@ -89,7 +89,6 @@ class MainActivity : AppCompatActivity() { R.id.vaccineRecordDetailFragment, R.id.addHealthRecordsFragment, R.id.homeFragment, - R.id.homeComposeFragment, R.id.bannerDetailFragment, R.id.newsfeedFragment, R.id.servicesFragment, @@ -144,7 +143,7 @@ class MainActivity : AppCompatActivity() { false ) if (started) { - binding.navHostFragment.showErrorSnackbar(getString(R.string.notification_title_while_fetching_data)) + binding.navHostFragment.showErrorSnackbar(getString(R.string.notification_title_while_fetching_data), binding.bottomNav) } } @@ -155,7 +154,7 @@ class MainActivity : AppCompatActivity() { WorkInfo.State.SUCCEEDED -> { if (isWorkerStarted) { inAppUpdate.checkForUpdate(AppUpdateType.FLEXIBLE) - binding.navHostFragment.showErrorSnackbar(getString(R.string.notification_title_on_success)) + binding.navHostFragment.showErrorSnackbar(getString(R.string.notification_title_on_success), binding.bottomNav) } } @@ -191,7 +190,7 @@ class MainActivity : AppCompatActivity() { false ) if (isRecordFetchFailed) { - binding.navHostFragment.showErrorSnackbar(getString(R.string.notification_title_on_failed)) + binding.navHostFragment.showErrorSnackbar(getString(R.string.notification_title_on_failed), binding.bottomNav) } } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index 52491b33a..87b44b100 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -5,15 +5,18 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import ca.bc.gov.common.BuildConfig.LOCAL_API_VERSION +import ca.bc.gov.common.model.AppFeatureName +import ca.bc.gov.common.model.QuickAccessLinkName import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.repository.OnBoardingRepository import ca.bc.gov.repository.settings.AppFeatureRepository +import ca.bc.gov.repository.settings.QuickAccessTileRepository import ca.bc.gov.repository.worker.MobileConfigRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import javax.inject.Inject private const val MAX_SPLASH_DELAY = 2000L @@ -22,7 +25,8 @@ private const val MAX_SPLASH_DELAY = 2000L class SplashViewModel @Inject constructor( private val mobileConfigRepository: MobileConfigRepository, onBoardingRepository: OnBoardingRepository, - private val appFeatureRepository: AppFeatureRepository + private val appFeatureRepository: AppFeatureRepository, + private val quickAccessTileRepository: QuickAccessTileRepository ) : ViewModel() { private val _updateType: MutableLiveData = MutableLiveData() @@ -31,9 +35,7 @@ class SplashViewModel @Inject constructor( init { onBoardingRepository.checkIfReOnBoardingRequired(BuildConfig.VERSION_CODE) - runBlocking { - initializeAppFeaturesData() - } + initializeAppData() } fun checkAppVersion() { @@ -57,124 +59,47 @@ class SplashViewModel @Inject constructor( } } - enum class UpdateType { - FORCE_UPDATE, CHECK_SOFT_UPDATE - } - - private suspend fun initializeAppFeaturesData() { - val appFeatures = listOf( - AppFeatureDto( - featureNameId = R.string.health_records, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, - destinationId = R.id.health_records, - isManagementEnabled = false, - isQuickAccessEnabled = true, - ), - AppFeatureDto( - featureNameId = R.string.immunization_schedules, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_tile_immunization_schedules, - destinationId = R.id.immunizationSchedulesFragment, - isManagementEnabled = false, - isQuickAccessEnabled = true, - ), - AppFeatureDto( - featureNameId = R.string.health_resources, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_tile_healt_resources, - destinationId = R.id.action_homeFragment_to_resources, - isManagementEnabled = false, - isQuickAccessEnabled = true, - ), - AppFeatureDto( - featureNameId = R.string.health_passes, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_tile_proof_of_vaccine, - destinationId = R.id.action_homeFragment_to_health_pass, - isManagementEnabled = false, - isQuickAccessEnabled = true, - ), - - AppFeatureDto( - featureNameId = R.string.feature_my_notes, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.icon_tile_health_record, - destinationId = R.id.health_records, - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), - - AppFeatureDto( - featureNameId = R.string.feature_immunization, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_health_record_vaccine, - destinationId = R.id.health_records, - destinationParam = "Immunization", - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), - - AppFeatureDto( - featureNameId = R.string.feature_medications, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_health_record_medication, - destinationId = R.id.health_records, - destinationParam = "Medications", - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), - - AppFeatureDto( - featureNameId = R.string.feature_lab_results, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_lab_test, - destinationId = R.id.health_records, - destinationParam = "Laboratory", - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), - - AppFeatureDto( - featureNameId = R.string.feature_special_authority, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_health_record_special_authority, - destinationId = R.id.health_records, - destinationParam = "SpecialAuthority", - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), + private fun initializeAppData() = viewModelScope.launch { + val healthRecord = AppFeatureDto( + name = AppFeatureName.HEALTH_RECORDS, + hasManageableQuickAccessLinks = true, + showAsQuickAccess = true + ) + val id = appFeatureRepository.insert(healthRecord) + + if (id > 0) { + val immunization = QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.IMMUNIZATIONS, + tilePayload = "Immunization", + showAsQuickAccess = true + ) + quickAccessTileRepository.insert(immunization) + } - AppFeatureDto( - featureNameId = R.string.feature_health_visit, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_health_record_health_visit, - destinationId = R.id.health_records, - destinationParam = "HealthVisit", - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), + val immunizationSchedule = AppFeatureDto( + name = AppFeatureName.IMMUNIZATION_SCHEDULES, + hasManageableQuickAccessLinks = false, + showAsQuickAccess = true + ) + appFeatureRepository.insert(immunizationSchedule) - AppFeatureDto( - featureNameId = R.string.feature_clinic_documents, - categoryId = R.string.feature_category_health_record, - featureIconId = R.drawable.ic_health_record_clinical_document, - destinationId = R.id.health_records, - destinationParam = "ClinicalDocument", - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), + val healthResources = AppFeatureDto( + name = AppFeatureName.HEALTH_RESOURCES, + hasManageableQuickAccessLinks = false, + showAsQuickAccess = true + ) + appFeatureRepository.insert(healthResources) - AppFeatureDto( - featureNameId = R.string.feature_organ_donor, - categoryId = R.string.feature_category_service, - featureIconId = R.drawable.ic_organ_donor, - destinationId = R.id.health_records, - isManagementEnabled = true, - isQuickAccessEnabled = false, - ), + val proofOfVaccine = AppFeatureDto( + name = AppFeatureName.PROOF_OF_VACCINE, + hasManageableQuickAccessLinks = false, + showAsQuickAccess = true ) + appFeatureRepository.insert(proofOfVaccine) + } - appFeatureRepository.insert(appFeatures) + enum class UpdateType { + FORCE_UPDATE, CHECK_SOFT_UPDATE } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt index 214829438..bdc0247fb 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/AnnouncementBannerUI.kt @@ -1,12 +1,15 @@ package ca.bc.gov.bchealth.compose.component +import android.text.method.LinkMovementMethod +import android.util.TypedValue +import android.widget.TextView import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.Card +import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -20,6 +23,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet import androidx.constraintlayout.compose.Dimension @@ -29,6 +33,7 @@ import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.utils.fromHtml private const val bannerIconId = "bannerIconId" private const val bannerTitleId = "bannerTitleId" @@ -42,12 +47,13 @@ fun AnnouncementBannerUI( modifier: Modifier = Modifier, title: String, description: String, + showReadMore: Boolean, onLearnMoreClick: () -> Unit, onDismissClick: () -> Unit ) { - var expanded by remember { mutableStateOf(false) } + var expanded by remember { mutableStateOf(true) } Card( - modifier.clickable { expanded = !expanded }, + modifier, backgroundColor = bannerBackgroundBlue ) { BoxWithConstraints( @@ -56,7 +62,7 @@ fun AnnouncementBannerUI( .wrapContentHeight() .padding(16.dp) ) { - val constraint = bannerConstraints(expanded) + val constraint = bannerConstraints(expanded, showReadMore) ConstraintLayout(constraint, modifier = Modifier.fillMaxWidth()) { Image( modifier = Modifier.layoutId(bannerIconId), @@ -71,17 +77,32 @@ fun AnnouncementBannerUI( fontWeight = FontWeight.Bold, color = blue ) - - Image( - modifier = Modifier.layoutId(bannerArrowId), - painter = painterResource(id = R.drawable.ic_arrow_down), - contentDescription = null - ) - - Text( + val toggleIcon = if (expanded) { + R.drawable.ic_content_short + } else { + R.drawable.ic_content_full + } + + IconButton( + onClick = { expanded = !expanded }, + modifier = Modifier + .layoutId(bannerArrowId) + ) { + Image(painter = painterResource(id = toggleIcon), contentDescription = null) + } + + AndroidView( modifier = Modifier.layoutId(bannerBodyId), - text = description, - style = MaterialTheme.typography.body1 + factory = { context -> + TextView(context).apply { + setTextAppearance(R.style.HealthGateway_TextAppearance_MaterialComponents_Headline4) + setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f) + } + }, + update = { + it.text = description.fromHtml().trimEnd().take(120) + it.movementMethod = LinkMovementMethod.getInstance() + } ) HGTextButton( @@ -104,7 +125,7 @@ fun AnnouncementBannerUI( } } -private fun bannerConstraints(expanded: Boolean): ConstraintSet { +private fun bannerConstraints(expanded: Boolean, showReadMore: Boolean): ConstraintSet { return ConstraintSet { val bannerIcon = createRefFor(bannerIconId) val bannerTitle = createRefFor(bannerTitleId) @@ -159,7 +180,7 @@ private fun bannerConstraints(expanded: Boolean): ConstraintSet { top.linkTo(buttonDismiss.top) end.linkTo(buttonDismiss.start) bottom.linkTo(buttonDismiss.bottom) - visibility = if (expanded) { + visibility = if (expanded && showReadMore) { Visibility.Visible } else { Visibility.Gone @@ -175,6 +196,7 @@ private fun AnnouncementBannerUIPreview() { AnnouncementBannerUI( title = stringResource(id = R.string.news_feed), description = stringResource(id = R.string.news_feed), + showReadMore = false, onDismissClick = {}, onLearnMoreClick = {} ) diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt index 21cb51385..308c7fdae 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/Button.kt @@ -150,7 +150,7 @@ fun HGTextButton( contentPadding = if (leadingIcon != null) { ButtonDefaults.ButtonWithIconContentPadding } else { - ButtonDefaults.ContentPadding + ButtonDefaults.TextButtonContentPadding } ) { HGButtonContent( @@ -187,7 +187,7 @@ fun HGTextButton( contentPadding = if (leadingIcon != null) { ButtonDefaults.ButtonWithIconContentPadding } else { - ButtonDefaults.ContentPadding + ButtonDefaults.TextButtonContentPadding } ) { HGButtonContent( @@ -203,7 +203,7 @@ fun HGTextButton( modifier: Modifier = Modifier, enabled: Boolean = true, defaultHeight: Dp = HGButtonDefaults.LargeButtonHeight, - contentPadding: PaddingValues = ButtonDefaults.ContentPadding, + contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, content: @Composable RowScope.() -> Unit ) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/HGProgressIndicator.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/HGProgressIndicator.kt new file mode 100644 index 000000000..f4277d1d3 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/HGProgressIndicator.kt @@ -0,0 +1,52 @@ +package ca.bc.gov.bchealth.compose.component + +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale +import androidx.compose.ui.res.painterResource +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme + +@Composable +fun HGProgressIndicator(modifier: Modifier = Modifier) { + + Box(modifier = modifier.fillMaxSize()) { + val infiniteTransition = rememberInfiniteTransition() + val heartbeatAnimation by infiniteTransition.animateFloat( + initialValue = 1f, + targetValue = 1.4f, + animationSpec = infiniteRepeatable( + animation = tween(1000), + repeatMode = RepeatMode.Reverse + ) + ) + + Icon( + modifier = Modifier.align(Alignment.Center) + .scale(heartbeatAnimation), + painter = painterResource(id = R.drawable.ic_heart), + contentDescription = null, + tint = MaterialTheme.colors.primary + ) + } +} + +@Composable +@BasePreview +private fun HGProgressIndicatorPreview() { + HealthGatewayTheme { + HGProgressIndicator() + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt index 7b6263763..426037c60 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/LoginInfoCardUI.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -29,19 +28,22 @@ fun LoginInfoCardUI( onClick: () -> Unit, modifier: Modifier = Modifier, title: String, - subTitle: String, - image: Painter = painterResource(id = R.drawable.img_un_authenticated_home_screen) + description: String, + buttonText: String, + image: Painter? = null ) { Card( modifier = modifier, backgroundColor = greyBg ) { Box(modifier = modifier) { - Image( - modifier = Modifier.align(Alignment.BottomEnd), - painter = image, - contentDescription = null - ) + image?.let { + Image( + modifier = Modifier.align(Alignment.BottomEnd), + painter = it, + contentDescription = null + ) + } Column( modifier = modifier @@ -57,13 +59,13 @@ fun LoginInfoCardUI( ) Spacer(modifier = Modifier.height(4.dp)) Text( - text = subTitle, + text = description, style = MaterialTheme.typography.body2 ) Spacer(modifier = Modifier.height(16.dp)) HGButton( onClick = onClick, - text = stringResource(id = R.string.get_started), + text = buttonText, defaultHeight = HGButtonDefaults.SmallButtonHeight ) } @@ -78,7 +80,8 @@ private fun LoginInfoCardUIPreview() { LoginInfoCardUI( onClick = { /*TODO*/ }, title = stringResource(id = R.string.log_in_with_bc_services_card), - subTitle = stringResource(id = R.string.login_to_view_hidden_records_msg) + description = stringResource(id = R.string.login_to_view_hidden_records_msg), + buttonText = stringResource(id = R.string.get_started) ) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt deleted file mode 100644 index 261b38f13..000000000 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Type.kt +++ /dev/null @@ -1,67 +0,0 @@ -package ca.bc.gov.bchealth.compose.theme - -import androidx.compose.material.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp -import ca.bc.gov.bchealth.R - -val hgFonts = FontFamily( - Font(R.font.bc_sans_regular, weight = FontWeight.Normal, style = FontStyle.Normal), - Font(R.font.bc_sans_bold, weight = FontWeight.Bold, style = FontStyle.Normal), - Font(R.font.bc_sans_italic, weight = FontWeight.Normal, style = FontStyle.Italic), - Font(R.font.bc_sans_bold_italic, weight = FontWeight.Bold, style = FontStyle.Italic), -) - -internal val HealthGatewayTypography = Typography( - defaultFontFamily = hgFonts, - h1 = TextStyle( - fontSize = 60.sp, - lineHeight = 72.sp, - letterSpacing = 0.sp, - ), - h2 = TextStyle( - fontSize = 50.sp, - lineHeight = 64.sp, - letterSpacing = 0.sp, - ), - h3 = TextStyle( - fontSize = 40.sp, - lineHeight = 56.sp, - letterSpacing = 0.sp, - ), - h4 = TextStyle( - fontSize = 33.sp, - lineHeight = 40.sp, - letterSpacing = 0.sp, - ), - h5 = TextStyle( - fontSize = 24.sp, - lineHeight = 32.sp, - letterSpacing = 0.sp, - ), - subtitle1 = TextStyle( - fontSize = 20.sp, - lineHeight = 38.sp, - letterSpacing = 0.sp, - ), - subtitle2 = TextStyle( - fontSize = 17.sp, - lineHeight = 34.sp, - letterSpacing = 0.sp, - ), - body1 = TextStyle( - fontSize = 15.sp, - lineHeight = 24.sp, - letterSpacing = 0.sp, - ), - body2 = TextStyle( - fontSize = 13.sp, - lineHeight = 22.sp, - fontStyle = FontStyle.Normal, - letterSpacing = 0.sp, - ) -) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt index e5eeea7eb..28d7f88b3 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt @@ -63,6 +63,12 @@ abstract class BaseFragment(@LayoutRes private val contentLayoutId: Int?) : Frag open fun setToolBar(appBarConfiguration: AppBarConfiguration) {} + @Deprecated( + "Should replace with extension mentioned in the FragmentExtensions" + + "as in compose we need to get rid of all the fragment", + replaceWith = ReplaceWith("launchAndRepeatWithLifecycle"), + level = DeprecationLevel.WARNING + ) fun StateFlow.collectOnStart(action: ((T) -> Unit)) { launchOnStart { this@collectOnStart.collect { state -> @@ -71,6 +77,12 @@ abstract class BaseFragment(@LayoutRes private val contentLayoutId: Int?) : Frag } } + @Deprecated( + "Should be added as a extension to the fragment" + + "as in compose we need to get rid of all the fragment", + replaceWith = ReplaceWith(""), + level = DeprecationLevel.WARNING + ) fun composeEmail(address: String = HEALTH_GATEWAY_EMAIL_ADDRESS, subject: String = "") { requireActivity().composeEmail(address, subject) } @@ -84,6 +96,12 @@ abstract class BaseFragment(@LayoutRes private val contentLayoutId: Int?) : Frag ) } + @Deprecated( + "remove usage of popup navigation and replace it with the a findNavController().popBackStack()" + + "as in compose we need to get rid of all the fragment", + replaceWith = ReplaceWith("findNavController().popBackStack()"), + level = DeprecationLevel.WARNING + ) fun popNavigation() { findNavController().popBackStack() } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt deleted file mode 100644 index 3deb163fb..000000000 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt +++ /dev/null @@ -1,248 +0,0 @@ -package ca.bc.gov.bchealth.ui.home - -import android.text.method.LinkMovementMethod -import android.util.TypedValue -import android.widget.TextView -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.viewinterop.AndroidView -import androidx.constraintlayout.compose.ConstraintLayout -import androidx.constraintlayout.compose.Dimension -import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.BasePreview -import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.bold -import ca.bc.gov.bchealth.compose.minButtonSize -import ca.bc.gov.bchealth.compose.theme.blue -import ca.bc.gov.bchealth.compose.theme.primaryBlue -import ca.bc.gov.bchealth.ui.custom.DecorativeImage -import ca.bc.gov.bchealth.utils.fromHtml - -@Composable -fun BannerUI( - uiState: BannerItem, - onClickToggle: () -> Unit, - onClickLearnMore: (BannerItem) -> Unit, - onClickDismiss: () -> Unit, -) { - - if (uiState.isHidden) return - - ConstraintLayout( - Modifier - .fillMaxWidth() - .padding(top = 16.dp, bottom = 8.dp, start = 32.dp, end = 32.dp) - .shadow(elevation = 8.dp, shape = RoundedCornerShape(10.dp)) - .clip(RoundedCornerShape(10.dp)) - .background(Color(0xFFD9EAF7)) - - ) { - - val (imgIcon, txtTitle, btToggle, txtBody, btLearnMore, btDismiss, spacer) = createRefs() - - DecorativeImage( - resourceId = R.drawable.ic_banner_icon, - modifier = Modifier - .constrainAs(imgIcon) { - start.linkTo(parent.start, margin = 16.dp) - linkTo(txtTitle.top, txtTitle.bottom, bias = 0.52f) - } - ) - - Text( - text = uiState.title, - modifier = Modifier - .constrainAs(txtTitle) { - start.linkTo(imgIcon.end, margin = 8.dp) - end.linkTo(btToggle.start) - top.linkTo(parent.top, margin = 16.dp) - width = Dimension.fillToConstraints - }, - color = blue, - style = MyHealthTypography.h3, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - - val toggleIcon = if (uiState.expanded) { - R.drawable.ic_content_short - } else { - R.drawable.ic_content_full - } - - Image( - painter = painterResource(id = toggleIcon), - contentScale = ContentScale.Inside, - modifier = Modifier - .constrainAs(btToggle) { - end.linkTo(parent.end) - top.linkTo(txtTitle.top) - bottom.linkTo(txtTitle.bottom) - } - .width(minButtonSize) - .height(minButtonSize) - .clickable { onClickToggle.invoke() }, - contentDescription = stringResource(id = R.string.expand_content) - ) - - if (uiState.expanded) { - Box( - modifier = Modifier - .constrainAs(txtBody) { - top.linkTo(txtTitle.bottom, margin = 8.dp) - start.linkTo(txtTitle.start) - end.linkTo(parent.end, margin = 24.dp) - width = Dimension.fillToConstraints - } - ) { - AndroidView( - modifier = Modifier, - factory = { context -> - TextView(context).apply { - setTextAppearance(R.style.HealthGateway_TextAppearance_MaterialComponents_Headline4) - setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f) - } - }, - update = { - it.text = uiState.body.fromHtml().trimEnd().take(120) - it.movementMethod = LinkMovementMethod.getInstance() - } - ) - } - - if (uiState.displayReadMore) { - Row( - modifier = Modifier - .clickable { onClickLearnMore.invoke(uiState) } - .constrainAs(btLearnMore) { - top.linkTo(btDismiss.top) - end.linkTo(btDismiss.start, margin = 24.dp) - }, - verticalAlignment = Alignment.CenterVertically - ) { - DecorativeImage(resourceId = R.drawable.ic_external_link) - Text( - modifier = Modifier.padding(start = 4.dp, top = 16.dp, bottom = 16.dp), - text = stringResource(id = R.string.learn_more).uppercase(), - style = MyHealthTypography.h4.bold(), - color = primaryBlue, - fontSize = 13.sp - ) - } - } - - Row( - modifier = Modifier - .clickable { onClickDismiss.invoke() } - .constrainAs(btDismiss) { - top.linkTo(txtBody.bottom) - end.linkTo(parent.end, margin = 24.dp) - }, - verticalAlignment = Alignment.CenterVertically - ) { - DecorativeImage(resourceId = R.drawable.ic_dismiss) - Text( - modifier = Modifier.padding(start = 4.dp, top = 16.dp, bottom = 16.dp), - text = stringResource(id = R.string.dismiss).uppercase(), - style = MyHealthTypography.h4.bold(), - color = primaryBlue, - fontSize = 13.sp - ) - } - } - Spacer( - modifier = Modifier - .constrainAs(spacer) { - top.linkTo(txtTitle.bottom, margin = 16.dp) - } - ) - } -} - -@BasePreview -@Composable -private fun PreviewBannerUI() { - BannerUI( - uiState = BannerItem( - title = "Great news! Really Big Announcement", - body = "View and manage all your available health records, including dispensed medications, health visits, COVID-19 test results, immunizations and more.", - date = "", - displayReadMore = true, - ), - onClickToggle = {}, - onClickLearnMore = {}, - onClickDismiss = {} - ) -} - -@BasePreview -@Composable -private fun PreviewBannerUICollapsed() { - BannerUI( - uiState = BannerItem( - title = "Great news! Really Big Announcement", - body = "View and manage all your available health records, including dispensed medications, health visits, COVID-19 test results, immunizations and more.", - date = "", - displayReadMore = true, - expanded = false - ), - onClickToggle = {}, - onClickLearnMore = {}, - onClickDismiss = {} - ) -} - -@BasePreview -@Composable -private fun PreviewBannerUIWithoutReadMore() { - BannerUI( - uiState = BannerItem( - title = "Great news! Really Big Announcement", - body = "View and manage all your available health records, including dispensed medications, health visits, COVID-19 test results, immunizations and more.", - date = "", - displayReadMore = false, - ), - onClickToggle = {}, - onClickLearnMore = {}, - onClickDismiss = {} - ) -} - -@BasePreview -@Composable -private fun PreviewBannerUIHidden() { - BannerUI( - uiState = BannerItem( - title = "Great news! Really Big Announcement", - body = "View and manage all your available health records, including dispensed medications, health visits, COVID-19 test results, immunizations and more.", - date = "", - displayReadMore = true, - isHidden = true, - ), - onClickToggle = {}, - onClickLearnMore = {}, - onClickDismiss = {} - ) -} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt deleted file mode 100644 index 5b86cf0ab..000000000 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt +++ /dev/null @@ -1,137 +0,0 @@ -package ca.bc.gov.bchealth.ui.home - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Button -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.BasePreview -import ca.bc.gov.bchealth.compose.MyHealthTheme -import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.bold -import ca.bc.gov.bchealth.compose.theme.primaryBlue -import ca.bc.gov.bchealth.compose.theme.white -import ca.bc.gov.bchealth.ui.custom.DecorativeImage - -@Composable -fun HomeCardUI(uiItem: HomeRecordItem, onClickItem: (HomeRecordItem) -> Unit) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp, bottom = 8.dp, start = 32.dp, end = 32.dp) - .shadow(elevation = 8.dp, shape = RoundedCornerShape(4.dp)) - .clip(RoundedCornerShape(4.dp)) - .clickable { onClickItem.invoke(uiItem) } - .background(white) - .padding(bottom = 32.dp) - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(start = 32.dp, top = 32.dp) - ) { - DecorativeImage(resourceId = uiItem.iconTitle) - - Text( - modifier = Modifier.padding(start = 8.dp, end = 32.dp), - text = stringResource(id = uiItem.title), - style = MyHealthTypography.h3 - ) - } - Text( - modifier = Modifier.padding(top = 8.dp, start = 32.dp, end = 32.dp), - text = stringResource(id = uiItem.description), - style = MyHealthTypography.h4, - fontSize = 13.sp, - ) - - CardButtonUI(uiItem, onClickItem) - } -} - -@Composable -private fun CardButtonUI(uiItem: HomeRecordItem, onClickItem: (HomeRecordItem) -> Unit) { - when (uiItem.recordType) { - HomeNavigationType.HEALTH_RECORD -> { - Button( - onClick = { onClickItem.invoke(uiItem) }, - modifier = Modifier.padding(top = 8.dp, start = 32.dp, end = 32.dp) - ) { - Text( - text = stringResource(id = uiItem.btnTitle), - style = MyHealthTypography.button.bold() - ) - } - } - - else -> { - Row( - modifier = Modifier.padding(top = 16.dp, start = 32.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = stringResource(id = uiItem.btnTitle), - style = MyHealthTypography.h4, - fontSize = 15.sp, - color = primaryBlue - ) - DecorativeImage( - resourceId = uiItem.icon, - modifier = Modifier.padding(start = 16.dp) - ) - } - } - } -} - -@BasePreview -@Composable -fun PreviewHomeCardUIHealthRecord() { - MyHealthTheme { - HomeCardUI( - uiItem = - HomeRecordItem( - iconTitle = R.drawable.ic_login_info, - title = R.string.health_records, - description = R.string.home_recommendations_body, - icon = R.drawable.ic_right_arrow, - btnTitle = R.string.get_started, - recordType = HomeNavigationType.HEALTH_RECORD - - ), - onClickItem = {}, - ) - } -} - -@BasePreview -@Composable -fun PreviewHomeCardUI() { - MyHealthTheme { - HomeCardUI( - uiItem = - HomeRecordItem( - iconTitle = R.drawable.ic_login_info, - title = R.string.recommendations_home_title, - description = R.string.home_recommendations_body, - icon = R.drawable.ic_right_arrow, - btnTitle = R.string.get_started, - recordType = HomeNavigationType.RECOMMENDATIONS - - ), - onClickItem = {}, - ) - } -} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt index cfadab7ac..e69de29bb 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt @@ -1,73 +0,0 @@ -package ca.bc.gov.bchealth.ui.home - -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels -import androidx.navigation.fragment.findNavController -import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.component.HGTopAppBar -import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem -import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme -import ca.bc.gov.bchealth.ui.BaseSecureFragment -import ca.bc.gov.bchealth.ui.filter.TimelineTypeFilter -import ca.bc.gov.bchealth.ui.healthrecord.filter.PatientFilterViewModel -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class HomeComposeFragment : BaseSecureFragment(null) { - private val viewModel: HomeComposeViewModel by viewModels() - private val filterSharedViewModel: PatientFilterViewModel by activityViewModels() - - @Composable - override fun GetComposableLayout() { - val menuItems = listOf( - TopAppBarActionItem.IconActionItem.AlwaysShown( - title = getString(R.string.settings), - onClick = { findNavController().navigate(R.id.settingsFragment) }, - icon = R.drawable.ic_menu_settings, - contentDescription = getString(R.string.settings), - ) - ) - HealthGatewayTheme { - Scaffold( - topBar = { - HGTopAppBar( - title = stringResource(id = R.string.home), - actionItems = menuItems - ) - }, - content = { - HomeScreen( - Modifier - .statusBarsPadding() - .navigationBarsPadding() - .padding(it), - viewModel, - onClickManage = ::onClickManage, - onQuickAccessTileClicked = ::onQuickAccessTileClicked - ) - } - ) - } - } - - private fun onClickManage() { - findNavController().navigate(R.id.quickAccessManagementFragment) - } - - private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { - if (quickAccessTileItem.destinationId == R.id.health_records && quickAccessTileItem.destinationParam != null) { - filterSharedViewModel.updateFilter( - listOf(TimelineTypeFilter.findByFilterValue(quickAccessTileItem.destinationParam!!).name), - ) - } - - findNavController().navigate(quickAccessTileItem.destinationId) - } -} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt index 346e87549..e69de29bb 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -1,86 +0,0 @@ -package ca.bc.gov.bchealth.ui.home - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ca.bc.gov.common.model.settings.AppFeatureDto -import ca.bc.gov.repository.settings.AppFeatureRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class HomeComposeViewModel @Inject constructor( - private val appFeatureRepository: AppFeatureRepository -) : ViewModel() { - private val _uiState = MutableStateFlow(HomeComposeUiState()) - val uiState: StateFlow = _uiState.asStateFlow() - - fun loadQuickAccessTiles() = viewModelScope.launch { - val quickAccessTileItems = appFeatureRepository.getQuickAccessTiles().map { - it.toUiItem() - } - - _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } - } -} - -data class HomeComposeUiState( - val quickAccessTileItems: List = emptyList() -) - -sealed class QuickAccessTileItem( - open val id: Long, - open val destinationId: Int, - open val destinationParam: String?, - open val categoryId: Int, - open var enabled: Boolean, -) { - data class PredefinedItem( - override val id: Long, - val icon: Int, - val nameId: Int, - override val destinationId: Int, - override val destinationParam: String?, - override val categoryId: Int, - override var enabled: Boolean, - ) : QuickAccessTileItem(id, destinationId, destinationParam, categoryId, enabled) - - data class DynamicItem( - override val id: Long, - val icon: Int, - val nameId: Int?, - val text: String, - override val destinationId: Int, - override val destinationParam: String?, - override val categoryId: Int, - override var enabled: Boolean, - ) : QuickAccessTileItem(id, destinationId, destinationParam, categoryId, enabled) -} - -fun AppFeatureDto.toUiItem(): QuickAccessTileItem = - if (this.featureName == null) { - QuickAccessTileItem.PredefinedItem( - id = this.id, - icon = this.featureIconId, - nameId = this.featureNameId ?: -1, - destinationId = this.destinationId, - destinationParam = this.destinationParam, - categoryId = this.categoryId, - enabled = this.isQuickAccessEnabled - ) - } else { - QuickAccessTileItem.DynamicItem( - id = this.id, - icon = this.featureIconId, - nameId = this.featureNameId, - text = this.featureName.orEmpty(), - destinationId = this.destinationId, - destinationParam = this.destinationParam, - categoryId = this.categoryId, - enabled = this.isQuickAccessEnabled - ) - } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt index 9f950a5b1..6f300dfa8 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt @@ -1,87 +1,51 @@ package ca.bc.gov.bchealth.ui.home -import android.app.Activity import android.os.Bundle import android.view.View -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold -import androidx.compose.material.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.core.os.bundleOf import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.BasePreview -import ca.bc.gov.bchealth.compose.MyHealthTheme +import ca.bc.gov.bchealth.compose.component.HGTopAppBar +import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import ca.bc.gov.bchealth.ui.BaseSecureFragment import ca.bc.gov.bchealth.ui.BcscAuthState +import ca.bc.gov.bchealth.ui.NavigationAction import ca.bc.gov.bchealth.ui.auth.BioMetricState import ca.bc.gov.bchealth.ui.auth.BiometricsAuthenticationFragment -import ca.bc.gov.bchealth.ui.custom.MyHealthToolBar import ca.bc.gov.bchealth.ui.login.BcscAuthViewModel import ca.bc.gov.bchealth.ui.login.LoginStatus -import ca.bc.gov.bchealth.utils.AlertDialogHelper -import ca.bc.gov.bchealth.utils.launchOnStart import ca.bc.gov.bchealth.utils.observeCurrentBackStackForAction -import ca.bc.gov.bchealth.utils.showServiceDownMessage import ca.bc.gov.bchealth.viewmodel.SharedViewModel import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch @AndroidEntryPoint class HomeFragment : BaseSecureFragment(null) { - private val viewModel: HomeViewModel by activityViewModels() + private val viewModel: HomeViewModel by viewModels() + private val authViewModel: BcscAuthViewModel by viewModels() private val sharedViewModel: SharedViewModel by activityViewModels() - private val bcscAuthViewModel: BcscAuthViewModel by viewModels() - private val homeViewModel: HomeComposeViewModel by viewModels() - - private var logoutResultLauncher = registerForActivityResult( - ActivityResultContracts.StartActivityForResult() - ) { activityResult -> - if (activityResult.resultCode == Activity.RESULT_OK) { - bcscAuthViewModel.processLogoutResponse(requireContext()).invokeOnCompletion { - updateHomeRecordsList() - } - } else { - AlertDialogHelper.showAlertDialog( - context = requireContext(), - title = getString(R.string.error), - msg = getString(R.string.error_message), - positiveBtnMsg = getString(R.string.dialog_button_ok) - ) - } - } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) observeCurrentBackStackForAction(BiometricsAuthenticationFragment.BIOMETRIC_STATE) { + viewModel.onBiometricAuthenticationCompleted() when (it) { BioMetricState.SUCCESS -> { + sharedViewModel.shouldFetchBanner = true findNavController().currentBackStackEntry?.savedStateHandle?.remove( BiometricsAuthenticationFragment.BIOMETRIC_STATE ) - viewModel.onAuthenticationRequired(false) - viewModel.launchCheck() - viewModel.executeOneTimeDataFetch() } else -> { @@ -89,198 +53,111 @@ class HomeFragment : BaseSecureFragment(null) { } } } - - viewLifecycleOwner.lifecycleScope.launch { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - onBoardingFlow() - } - } - } - - updateHomeRecordsList() - observeAuthStatus() - - bcscAuthViewModel.checkSession() - - viewModel.launchCheck() - viewModel.getAuthenticatedPatientName() } @Composable override fun GetComposableLayout() { - val homeUiState = viewModel.uiState.collectAsState().value - val bannerUiState = viewModel.bannerState.collectAsState().value - val homeItems = viewModel.homeList.collectAsState().value.orEmpty() - val authState = bcscAuthViewModel.authStatus.collectAsState().value + println("Home: ComposeCreated") + val authState = authViewModel.authStatus.collectAsState().value + val menuItems = mutableListOf( + TopAppBarActionItem.IconActionItem.AlwaysShown( + title = getString(R.string.settings), + onClick = { findNavController().navigate(R.id.settingsFragment) }, + icon = R.drawable.ic_menu_settings, + contentDescription = getString(R.string.settings), + ) + ) - MyHealthTheme { + authState.loginStatus?.let { + if (it == LoginStatus.ACTIVE) { + menuItems.add( + 0, + TopAppBarActionItem.IconActionItem.AlwaysShown( + title = getString(R.string.notifications), + onClick = { findNavController().navigate(R.id.notificationFragment) }, + icon = R.drawable.ic_notification, + contentDescription = getString(R.string.notifications), + ) + ) + } + } + HealthGatewayTheme { Scaffold( - topBar = { HomeToolbar(authState.loginStatus != LoginStatus.NOT_AUTHENTICATED) }, + topBar = { + HGTopAppBar( + title = authState.userName ?: stringResource(id = R.string.home), + actionItems = menuItems + ) + }, content = { - Column( - modifier = Modifier + HomeScreen( + Modifier .statusBarsPadding() .navigationBarsPadding() - .padding(it) - .verticalScroll(rememberScrollState()), - ) { - HomeScreen( - Modifier - .statusBarsPadding() - .navigationBarsPadding() - .padding(it), - homeViewModel, - onClickManage = {}, - onQuickAccessTileClicked = {} - ) - } - }, - contentColor = contentColorFor(backgroundColor = MaterialTheme.colors.background) - ) - } - } - - @Composable - private fun HomeToolbar(isAuthenticated: Boolean) { - MyHealthToolBar( - title = "", - actions = { - - if (isAuthenticated) { - IconButton( - onClick = { findNavController().navigate(R.id.notificationFragment) } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_notification), - contentDescription = stringResource(id = R.string.notifications), - tint = MaterialTheme.colors.primary - ) - } - } - IconButton( - onClick = { findNavController().navigate(R.id.settingsFragment) } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_settings), - contentDescription = stringResource( - id = R.string.settings - ), - tint = MaterialTheme.colors.primary + .padding(it), + authViewModel = authViewModel, + viewModel = viewModel, + sharedViewModel = sharedViewModel, + onLoginClick = ::onLoginClick, + onManageClick = ::onManageClicked, + onOnBoardingRequired = ::onOnBoardingRequired, + onBiometricAuthenticationRequired = ::onBiometricAuthenticationRequired, + onQuickAccessTileClicked = ::onQuickAccessTileClicked ) } - } - ) - } - - override fun handleBCSCAuthState(bcscAuthState: BcscAuthState) { - if (bcscAuthState == BcscAuthState.SUCCESS) { - if (sharedViewModel.destinationId > 0) { - findNavController().navigate(sharedViewModel.destinationId) - } + ) } } - private fun observeAuthStatus() { - viewLifecycleOwner.lifecycleScope.launch { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - bcscAuthViewModel.authStatus.collect { - - if (it.isError) { - bcscAuthViewModel.resetAuthStatus() - } - - if (it.endSessionIntent != null) { - logoutResultLauncher.launch(it.endSessionIntent) - bcscAuthViewModel.resetAuthStatus() - } + override fun handleBCSCAuthState(bcscAuthState: BcscAuthState) { + viewModel.resetUIState() + when (bcscAuthState) { + BcscAuthState.SUCCESS -> { + if (sharedViewModel.destinationId > 0) { + findNavController().navigate(sharedViewModel.destinationId) } } + BcscAuthState.NO_ACTION, + BcscAuthState.NOT_NOW -> {} } } - private fun updateHomeRecordsList() { - launchOnStart { viewModel.getHomeRecordsList() } - } - - private fun navigateToDestination(destinationType: HomeNavigationType) { - val destination = when (destinationType) { - HomeNavigationType.HEALTH_RECORD -> { - if (bcscAuthViewModel.authStatus.value.loginStatus == LoginStatus.ACTIVE) { - R.id.action_homeFragment_to_health_records - } else { - sharedViewModel.destinationId = R.id.health_records - R.id.bcscAuthInfoFragment - } + override fun handleNavigationAction(navigationAction: NavigationAction) { + when (navigationAction) { + NavigationAction.ACTION_BACK -> { + findNavController().popBackStack() } - HomeNavigationType.VACCINE_PROOF -> R.id.action_homeFragment_to_health_pass - - HomeNavigationType.RESOURCES -> R.id.action_homeFragment_to_resources - - HomeNavigationType.RECOMMENDATIONS -> R.id.action_homeFragment_to_recommendations + NavigationAction.ACTION_RE_CHECK -> { + authViewModel.checkSession() + } } - findNavController().navigate(destination) } - private suspend fun onBoardingFlow() { - viewModel.uiState.collect { uiState -> - if (uiState.isOnBoardingRequired || uiState.isReOnBoardingRequired) { - - findNavController().navigate( - R.id.onBoardingSliderFragment, - bundleOf("reOnBoardingRequired" to uiState.isReOnBoardingRequired) - ) - viewModel.onBoardingShown() - } - - if (uiState.isAuthenticationRequired && !sharedViewModel.isBiometricAuthShown) { - findNavController().navigate(R.id.biometricsAuthenticationFragment) - sharedViewModel.isBiometricAuthShown = true - viewModel.onAuthenticationRequired(false) - } - - if (uiState.isBcscLoginRequiredPostBiometrics) { - sharedViewModel.destinationId = 0 - findNavController().navigate(R.id.bcscAuthInfoFragment) - sharedViewModel.isBCSCAuthShown = true - viewModel.onBcscLoginRequired(false) - } - - if (uiState.isForceLogout) { - bcscAuthViewModel.getEndSessionIntent() - viewModel.onForceLogout(false) - } + private fun onLoginClick() { + sharedViewModel.destinationId = 0 + findNavController().navigate(R.id.bcscAuthInfoFragment) + } - if (uiState.displayServiceDownMessage) { - view?.showServiceDownMessage(requireContext()) - viewModel.resetUiState() - } + private fun onBiometricAuthenticationRequired() { + if (!sharedViewModel.isBiometricAuthShown) { + findNavController().navigate(R.id.biometricsAuthenticationFragment) + sharedViewModel.isBiometricAuthShown = true } + viewModel.onBiometricAuthenticationCompleted() } - private fun onClickLearnMore(banner: BannerItem) { - val action = HomeFragmentDirections.actionHomeFragmentToBannerDetail( - title = banner.title, - date = banner.date, - body = banner.body, + private fun onOnBoardingRequired(isReOnBoarding: Boolean) { + findNavController().navigate( + R.id.onBoardingSliderFragment, + bundleOf("reOnBoardingRequired" to isReOnBoarding) ) - findNavController().navigate(action) } - - @BasePreview - @Composable - private fun PreviewHomeToolbar() { - MyHealthTheme { - HomeToolbar(true) - } + private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { + findNavController().navigate(quickAccessTileItem.destinationId) } - @BasePreview - @Composable - private fun PreviewHomeToolbarNonAuthenticated() { - MyHealthTheme { - HomeToolbar(false) - } + private fun onManageClicked() { + findNavController().navigate(R.id.quickAccessManagementFragment) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 174d7a050..e7ddf2ad6 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -1,11 +1,17 @@ package ca.bc.gov.bchealth.ui.home -import androidx.compose.foundation.clickable +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid @@ -15,43 +21,134 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layoutId import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Popup +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintSet import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.component.AnnouncementBannerUI +import ca.bc.gov.bchealth.compose.component.HGProgressIndicator +import ca.bc.gov.bchealth.compose.component.HGTextButton import ca.bc.gov.bchealth.compose.component.LoginInfoCardUI import ca.bc.gov.bchealth.compose.component.QuickAccessTileItemUI import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.statusBlue +import ca.bc.gov.bchealth.compose.theme.white +import ca.bc.gov.bchealth.ui.login.BcscAuthViewModel +import ca.bc.gov.bchealth.ui.login.LoginStatus +import ca.bc.gov.bchealth.viewmodel.SharedViewModel @Composable fun HomeScreen( modifier: Modifier = Modifier, - viewModel: HomeComposeViewModel, + authViewModel: BcscAuthViewModel, + viewModel: HomeViewModel, + sharedViewModel: SharedViewModel, + onLoginClick: () -> Unit, + onManageClick: () -> Unit, + onOnBoardingRequired: (isReOnBoarding: Boolean) -> Unit, + onBiometricAuthenticationRequired: () -> Unit, onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit, - onClickManage: () -> Unit, ) { val uiState = viewModel.uiState.collectAsState().value + + val authState = authViewModel.authStatus.collectAsState().value + LaunchedEffect(key1 = Unit) { - viewModel.loadQuickAccessTiles() + viewModel.launchCheck() + } + + if (uiState.isLoading) { + HGProgressIndicator(modifier) + } else { + uiState.launchCheckStatus?.let { + when (it) { + LaunchCheckStatus.REQUIRE_ON_BOARDING -> { + LaunchedEffect(key1 = Unit) { + onOnBoardingRequired(false) + viewModel.resetUIState() + } + } + + LaunchCheckStatus.REQUIRE_RE_ON_BOARDING -> { + LaunchedEffect(key1 = Unit) { + onOnBoardingRequired(true) + viewModel.resetUIState() + } + } + + LaunchCheckStatus.REQUIRE_BIOMETRIC_AUTHENTICATION -> { + LaunchedEffect(key1 = Unit) { + onBiometricAuthenticationRequired() + } + } + + LaunchCheckStatus.SUCCESS -> { + LaunchedEffect(key1 = Unit) { + authViewModel.checkSession() + } + } + } + } + } + + if (authState.showLoading) { + HGProgressIndicator(modifier) + } else { + authState.loginStatus?.let { + LaunchedEffect(key1 = Unit) { + viewModel.loadQuickAccessTiles(it) + } + + if (sharedViewModel.shouldFetchBanner) { + LaunchedEffect(key1 = Unit) { + viewModel.fetchBanner() + } + } + + HomeScreenContent( + modifier, + onLoginClick, + onQuickAccessTileClicked, + onManageClick, + onDismissClick = { + sharedViewModel.shouldFetchBanner = false + viewModel.dismissBanner() + }, + onDismissTutorialClicked = { viewModel.tutorialDismissed() }, + it, + viewModel.getLoginInfoCardData(it), + uiState.bannerItem, + uiState.quickAccessTileItems, + uiState.isQuickAccessTileTutorialRequired + ) + } } - HomeScreenContent( - modifier, - onQuickAccessTileClicked, - onClickManage, - uiState.quickAccessTileItems - ) } @Composable private fun HomeScreenContent( modifier: Modifier = Modifier, + onLoginClick: () -> Unit, onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit, - onClickManage: () -> Unit, - quickAccessTileItems: List + onManageClick: () -> Unit, + onDismissClick: () -> Unit, + onDismissTutorialClicked: () -> Unit, + loginStatus: LoginStatus, + loginInfoCardData: LoginInfoCardData?, + bannerItem: HomeBannerItem?, + quickAccessTileItems: List, + isQuickAccessTileTutorialRequired: Boolean ) { LazyVerticalGrid( columns = GridCells.Fixed(2), @@ -61,122 +158,179 @@ private fun HomeScreenContent( horizontalArrangement = Arrangement.spacedBy(20.dp) ) { - item(span = { GridItemSpan(maxLineSpan) }) { - AnnouncementBannerUI( - title = stringResource(id = R.string.home_banner_toolbar_title), - description = stringResource(id = R.string.home_banner_toolbar_title), - onLearnMoreClick = { /*TODO*/ }, - onDismissClick = { /*TODO*/ } - ) + bannerItem?.let { banner -> + if (!banner.isDismissed) { + item(span = { GridItemSpan(maxLineSpan) }) { + AnnouncementBannerUI( + title = banner.title, + description = banner.body, + showReadMore = banner.showReadMore(), + onLearnMoreClick = { /*TODO*/ }, + onDismissClick = { onDismissClick() } + ) + } + } + } + + loginInfoCardData?.let { data -> + item(span = { GridItemSpan(maxLineSpan) }) { + LoginInfoCardUI( + onClick = { onLoginClick() }, + title = stringResource(id = data.title), + description = stringResource(id = data.description), + buttonText = stringResource(id = data.buttonText), + image = if (data.image > 0) { + painterResource(id = data.image) + } else { + null + } + ) + } } item(span = { GridItemSpan(maxLineSpan) }) { - LoginInfoCardUI( - onClick = { /*TODO*/ }, - title = stringResource(id = R.string.log_in_with_bc_services_card), - subTitle = stringResource(id = R.string.login_to_view_hidden_records_msg) + QuickAccessHeaderUI(onManageClick, onDismissTutorialClicked, loginStatus, isQuickAccessTileTutorialRequired) + } + + items(quickAccessTileItems) { + QuickAccessTileItemUI( + onClick = { onQuickAccessTileClicked(it) }, + icon = painterResource(id = it.icon), + title = it.name ) } + } +} - item(span = { GridItemSpan(maxLineSpan) }) { - Row(Modifier.fillMaxWidth()) { - Text( - modifier = Modifier.weight(1f), - text = stringResource(id = R.string.quick_access), - style = MaterialTheme.typography.subtitle2, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.primary - ) +@Composable +private fun QuickAccessHeaderUI(onManageClick: () -> Unit, onDismissTutorialClicked: () -> Unit, loginStatus: LoginStatus, isQuickAccessTileTutorialRequired: Boolean) { - // todo: Replace it: HAPP-1537 + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = stringResource(id = R.string.quick_access), + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary + ) + + AnimatedVisibility(visible = (LoginStatus.ACTIVE == loginStatus)) { + HGTextButton(onClick = onManageClick) { Text( - modifier = Modifier.clickable { onClickManage.invoke() }, - text = "Manage", - style = MaterialTheme.typography.subtitle2, + text = stringResource(id = R.string.manage), + style = MaterialTheme.typography.body2, fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.primary + color = blue, + textDecoration = TextDecoration.Underline ) } + if (isQuickAccessTileTutorialRequired) { + QuickAccessManagementTutorialUI(onDismissTutorialClicked) + } } + } +} - items(quickAccessTileItems) { - QuickAccessTileItemUi(it, onQuickAccessTileClicked) +private const val anchorIconId = "anchorIconId" +private const val bannerBodyId = "bannerBodyId" + +private fun quickAccessManagementTutorialConstraint(): ConstraintSet { + return ConstraintSet { + val anchorIcon = createRefFor(anchorIconId) + val body = createRefFor(bannerBodyId) + + constrain(anchorIcon) { + top.linkTo(parent.top, 16.dp) + end.linkTo(parent.end, 16.dp) + } + + constrain(body) { + start.linkTo(parent.start) + top.linkTo(anchorIcon.bottom) + end.linkTo(parent.end) + bottom.linkTo(parent.bottom) } } } @Composable -private fun QuickAccessTileItemUi( - item: QuickAccessTileItem, - onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit -) { - val title: String - val icon: Int - - when (item) { - is QuickAccessTileItem.PredefinedItem -> { - icon = item.icon - title = stringResource(id = item.nameId) - } +private fun QuickAccessManagementTutorialUI(onDismissTutorialClicked: () -> Unit) { + Popup( + alignment = Alignment.TopEnd, + offset = IntOffset(0, 100) + ) { + BoxWithConstraints(modifier = Modifier.wrapContentSize()) { - is QuickAccessTileItem.DynamicItem -> { - icon = item.icon - title = if (item.nameId == null) { - item.text - } else { - stringResource(item.nameId, item.text).replaceFirst("s’s", "s’") + ConstraintLayout(constraintSet = quickAccessManagementTutorialConstraint()) { + Image( + modifier = Modifier.layoutId(anchorIconId), + painter = painterResource(id = R.drawable.ic_anchor), + contentDescription = null + ) + Column( + modifier = Modifier + .layoutId(bannerBodyId) + .background(statusBlue) + .padding(start = 8.dp, top = 8.dp, end = 8.dp) + ) { + Text( + modifier = Modifier.padding(start = 8.dp, end = 8.dp), + text = stringResource(id = R.string.manage_hint), + style = MaterialTheme.typography.body2, + color = white + ) + HGTextButton(onClick = { onDismissTutorialClicked() }) { + Text( + text = "Got it", + style = MaterialTheme.typography.body2, + fontWeight = FontWeight.Bold, + color = white, + textDecoration = TextDecoration.Underline + ) + } + } } } } - - QuickAccessTileItemUI( - onClick = { onQuickAccessTileClicked(item) }, - icon = painterResource(id = icon), - title = title - ) } @Composable @BasePreview -private fun HomeScreenPreview() { - val dynamicItemSample = QuickAccessTileItem.DynamicItem( - id = 0, - icon = R.drawable.ic_health_record, - nameId = -1, - text = "", - destinationId = -1, - destinationParam = null, - categoryId = -1, - enabled = true, - ) +private fun HomeScreenNonAuthenticatedPreview() { + HealthGatewayTheme { + HomeScreenContent( + onLoginClick = {}, + onManageClick = {}, + onQuickAccessTileClicked = {}, + onDismissClick = {}, + onDismissTutorialClicked = {}, + loginStatus = LoginStatus.NOT_AUTHENTICATED, + loginInfoCardData = null, + bannerItem = null, + quickAccessTileItems = emptyList(), + isQuickAccessTileTutorialRequired = false + ) + } +} +@Composable +@BasePreview +private fun HomeScreenAuthenticatedPreview() { HealthGatewayTheme { HomeScreenContent( + onLoginClick = {}, + onManageClick = {}, onQuickAccessTileClicked = {}, - onClickManage = {}, - quickAccessTileItems = listOf( - dynamicItemSample.copy( - nameId = R.string.feature_quick_action_dependents, - text = "Jane", - ), - dynamicItemSample.copy( - nameId = R.string.feature_quick_action_dependents, - text = "James", - ), - dynamicItemSample.copy( - nameId = null, - text = "Dynamic text", - ), - QuickAccessTileItem.PredefinedItem( - id = 0, - icon = R.drawable.ic_health_record, - nameId = R.string.immnz_schedules_infant, - destinationId = -1, - destinationParam = null, - categoryId = -1, - enabled = true, - ), - ) + onDismissClick = {}, + onDismissTutorialClicked = {}, + loginStatus = LoginStatus.ACTIVE, + loginInfoCardData = null, + bannerItem = null, + quickAccessTileItems = emptyList(), + isQuickAccessTileTutorialRequired = false ) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt index f25faf3c9..57c73a1a6 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt @@ -1,304 +1,331 @@ package ca.bc.gov.bchealth.ui.home import androidx.annotation.DrawableRes +import androidx.annotation.IdRes import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.ui.login.LoginStatus import ca.bc.gov.bchealth.utils.COMMUNICATION_BANNER_MAX_LENGTH -import ca.bc.gov.bchealth.utils.INDEX_NOT_FOUND import ca.bc.gov.bchealth.utils.fromHtml -import ca.bc.gov.bchealth.workers.WorkerInvoker -import ca.bc.gov.common.exceptions.ServiceDownException -import ca.bc.gov.common.model.AuthenticationStatus -import ca.bc.gov.common.model.banner.BannerDto -import ca.bc.gov.common.utils.toDate -import ca.bc.gov.common.utils.yyyy_MM_dd +import ca.bc.gov.common.model.AppFeatureName +import ca.bc.gov.common.model.QuickAccessLinkName +import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.repository.BannerRepository import ca.bc.gov.repository.OnBoardingRepository -import ca.bc.gov.repository.bcsc.BcscAuthRepo -import ca.bc.gov.repository.bcsc.PostLoginCheck -import ca.bc.gov.repository.immunization.ImmunizationRecommendationRepository -import ca.bc.gov.repository.patient.PatientRepository +import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( + private val appFeatureWithQuickAccessTilesRepository: AppFeatureWithQuickAccessTilesRepository, private val onBoardingRepository: OnBoardingRepository, - private val patientRepository: PatientRepository, - private val bcscAuthRepo: BcscAuthRepo, - private val workerInvoker: WorkerInvoker, - recommendationRepository: ImmunizationRecommendationRepository, - private val bannerRepository: BannerRepository, + private val bannerRepository: BannerRepository ) : ViewModel() { + private val _uiState = + MutableStateFlow(HomeComposeUiState(isQuickAccessTileTutorialRequired = appFeatureWithQuickAccessTilesRepository.isQuickAccessTileTutorialRequired)) + val uiState: StateFlow = _uiState.asStateFlow() - private var bannerRequested = false - - private val _bannerState = MutableStateFlow(null) - val bannerState: StateFlow = _bannerState.asStateFlow() - - private val _uiState = MutableStateFlow(HomeUiState()) - val uiState: StateFlow = _uiState.asStateFlow() - var isAuthenticationRequired: Boolean = true - var isForceLogout: Boolean = false - - private val _homeList = MutableStateFlow?>(null) - val homeList: StateFlow?> = _homeList.asStateFlow() - - private val recommendationItem = HomeRecordItem( - R.drawable.ic_recommendation, - R.string.home_recommendations_title, - R.string.home_recommendations_body, - R.drawable.ic_right_arrow, - R.string.learn_more, - HomeNavigationType.RECOMMENDATIONS - ) - - private val displayRecommendations = - recommendationRepository.getAllRecommendations().map { list -> - val isLoggedIn: Boolean = try { - bcscAuthRepo.checkSession() - } catch (e: Exception) { - false - } - list.isNotEmpty() && isLoggedIn + private var isBiometricAuthenticationRequired: Boolean = true + + fun loadQuickAccessTiles(loginStatus: LoginStatus) = viewModelScope.launch { + var quickAccessTileItems = mutableListOf() + val data = appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() + + val appFeatures = + appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() + .filter { it.appFeatureDto.showAsQuickAccess } + .map { + QuickAccessTileItem.FeatureTileItem.from(it.appFeatureDto) + } + + quickAccessTileItems.addAll(appFeatures) + + data.filter { it.appFeatureDto.showAsQuickAccess }.forEach { + val quickLink = it.quickAccessTiles.filter { it.showAsQuickAccess } + .map { tile -> QuickAccessTileItem.QuickLinkTileItem.from(tile) } + quickAccessTileItems.addAll(quickLink) } - fun launchCheck() = viewModelScope.launch { - if (bcscAuthRepo.checkSession()) { - onBoardingRepository.onBCSCLoginRequiredPostBiometric = false + if (loginStatus != LoginStatus.ACTIVE) { + quickAccessTileItems = + quickAccessTileItems.filterIsInstance().toMutableList() } + _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } + } + + fun launchCheck() = viewModelScope.launch { when { onBoardingRepository.onBoardingRequired -> { _uiState.update { state -> - state.copy(isLoading = false, isOnBoardingRequired = true) + state.copy( + isLoading = false, + launchCheckStatus = LaunchCheckStatus.REQUIRE_ON_BOARDING + ) } } onBoardingRepository.isReOnBoardingRequired -> { - _uiState.update { state -> - state.copy(isLoading = false, isReOnBoardingRequired = true) + _uiState.update { + it.copy( + isLoading = false, + launchCheckStatus = LaunchCheckStatus.REQUIRE_RE_ON_BOARDING + ) } } - isAuthenticationRequired -> { - _uiState.update { state -> state.copy(isAuthenticationRequired = true) } - } - - onBoardingRepository.onBCSCLoginRequiredPostBiometric -> { - _uiState.update { state -> state.copy(isBcscLoginRequiredPostBiometrics = true) } + isBiometricAuthenticationRequired -> { + _uiState.update { + it.copy( + isLoading = false, + launchCheckStatus = LaunchCheckStatus.REQUIRE_BIOMETRIC_AUTHENTICATION + ) + } } - bcscAuthRepo.getPostLoginCheck() == PostLoginCheck.IN_PROGRESS.name -> { - _uiState.update { state -> state.copy(isForceLogout = true) } + else -> { + _uiState.update { + it.copy(isLoading = false, launchCheckStatus = LaunchCheckStatus.SUCCESS) + } } } } - fun onBoardingShown() { - _uiState.update { - it.copy(isOnBoardingRequired = false, isReOnBoardingRequired = false) - } - } - - fun onAuthenticationRequired(isRequired: Boolean) { - isAuthenticationRequired = isRequired - _uiState.update { state -> state.copy(isAuthenticationRequired = isRequired) } + fun onBiometricAuthenticationCompleted() { + isBiometricAuthenticationRequired = false + resetUIState() + launchCheck() } - fun onBcscLoginRequired(isRequired: Boolean) { - onBoardingRepository.onBCSCLoginRequiredPostBiometric = isRequired - _uiState.update { state -> state.copy(isBcscLoginRequiredPostBiometrics = isRequired) } - } - - fun onForceLogout(isRequired: Boolean) { - isForceLogout = isRequired - _uiState.update { state -> state.copy(isForceLogout = isRequired) } + fun resetUIState() { + _uiState.update { + it.copy(launchCheckStatus = null) + } } - fun getAuthenticatedPatientName() = viewModelScope.launch { + fun fetchBanner() = viewModelScope.launch { try { - val patient = - patientRepository.findPatientByAuthStatus(AuthenticationStatus.AUTHENTICATED) - val names = patient.fullName.split(" ") - val firstName = if (names.isNotEmpty()) names.first() else "" - _uiState.update { - it.copy(patientFirstName = firstName) + bannerRepository.getBanner()?.let { banner -> + _uiState.update { + it.copy( + isLoading = false, + bannerItem = HomeBannerItem( + banner.title, + body = banner.body + ) + ) + } } } catch (e: Exception) { - _uiState.update { - it.copy(patientFirstName = "") - } } } - suspend fun getHomeRecordsList() { - val isLoggedIn: Boolean = try { - bcscAuthRepo.checkSession() - } catch (e: Exception) { - false - } - - val list = mutableListOf( - HomeRecordItem( - R.drawable.ic_login_info, - R.string.health_records, - R.string.health_records_desc, - 0, - if (isLoggedIn) R.string.view_records else R.string.get_started, - HomeNavigationType.HEALTH_RECORD - ), - HomeRecordItem( - R.drawable.ic_resources, - R.string.health_resources, - R.string.resources_desc, - R.drawable.ic_right_arrow, - R.string.learn_more, - HomeNavigationType.RESOURCES - ), - HomeRecordItem( - R.drawable.ic_green_tick, - R.string.health_passes, - R.string.proof_of_vaccination_desc, - R.drawable.ic_right_arrow, - R.string.add_proofs, - HomeNavigationType.VACCINE_PROOF - ), - ) - - _homeList.update { list } - displayRecommendations.collect { - manageRecommendationCard(it) + fun dismissBanner() { + _uiState.update { + it.copy(isLoading = false, bannerItem = it.bannerItem?.copy(isDismissed = true)) } } - private fun manageRecommendationCard(displayCard: Boolean) { - _homeList.value?.let { list -> - val cardIndex = list.indexOfFirst { - it.recordType == HomeNavigationType.RECOMMENDATIONS + fun getLoginInfoCardData(loginStatus: LoginStatus): LoginInfoCardData? { + + return when (loginStatus) { + + LoginStatus.ACTIVE -> { + null } - if (displayCard) { - if (cardIndex == INDEX_NOT_FOUND) { - _homeList.update { - list.toMutableList().apply { add(1, recommendationItem) } - } - } - } else { - if (cardIndex > INDEX_NOT_FOUND) { - _homeList.update { - list.toMutableList().apply { removeAt(cardIndex) } - } - } + LoginStatus.EXPIRED -> { + LoginInfoCardData( + title = R.string.session_time_out, + description = R.string.login_to_view_hidden_records_msg, + buttonText = R.string.log_in + ) + } + + LoginStatus.NOT_AUTHENTICATED -> { + LoginInfoCardData( + title = R.string.log_in_with_bc_services_card, + description = R.string.log_in_description, + buttonText = R.string.get_started, + image = R.drawable.img_un_authenticated_home_screen + ) } } } - fun executeOneTimeDataFetch() { - fetchBanner() - workerInvoker.executeOneTimeDataFetch() + fun tutorialDismissed() { + appFeatureWithQuickAccessTilesRepository.isQuickAccessTileTutorialRequired = false + _uiState.update { + it.copy(isQuickAccessTileTutorialRequired = false) + } } +} + +data class HomeComposeUiState( + val isLoading: Boolean = false, + val launchCheckStatus: LaunchCheckStatus? = null, + val bannerItem: HomeBannerItem? = null, + val loginInfoCardData: LoginInfoCardData? = null, + val quickAccessTileItems: List = emptyList(), + val isQuickAccessTileTutorialRequired: Boolean = false +) - private fun fetchBanner() { - if (bannerRequested.not()) { - viewModelScope.launch { +enum class LaunchCheckStatus { + REQUIRE_ON_BOARDING, + REQUIRE_RE_ON_BOARDING, + REQUIRE_BIOMETRIC_AUTHENTICATION, + SUCCESS +} - try { - callBannerRepository() - } catch (e: Exception) { - when (e) { - is ServiceDownException -> displayServiceDownMessage() - else -> e.printStackTrace() +data class LoginInfoCardData( + @StringRes val title: Int, + @StringRes val description: Int, + @StringRes val buttonText: Int, + @DrawableRes val image: Int = 0 +) + +sealed class QuickAccessTileItem( + @DrawableRes open val icon: Int, + open val name: String, + open val payload: String? = null, + @IdRes open val destinationId: Int, + open val isEditable: Boolean = false +) { + data class FeatureTileItem( + val id: Long, + override val icon: Int, + override val name: String, + override val payload: String?, + override val destinationId: Int, + override val isEditable: Boolean + ) : QuickAccessTileItem( + icon, name, payload, destinationId, isEditable + ) { + companion object { + fun from(appFeatureDto: AppFeatureDto): FeatureTileItem { + val (tileIcon, endDestinationId) = when (appFeatureDto.name) { + AppFeatureName.HEALTH_RECORDS -> { + Pair(R.drawable.icon_tile_health_record, R.id.health_records) } - } - } - bannerRequested = true - } - } + AppFeatureName.IMMUNIZATION_SCHEDULES -> { - private fun displayServiceDownMessage() { - _uiState.update { state -> - state.copy(displayServiceDownMessage = true) - } - } + Pair( + R.drawable.ic_tile_immunization_schedules, + R.id.immunizationSchedulesFragment + ) + } - private suspend fun callBannerRepository() { - bannerRepository.getBanner()?.apply { - if (validateBannerDates(this)) { - _bannerState.update { - BannerItem( - title = title, - date = startDate.toDate(yyyy_MM_dd), - body = body, - displayReadMore = shouldDisplayReadMore(body), - ) + AppFeatureName.HEALTH_RESOURCES -> { + Pair( + R.drawable.ic_tile_healt_resources, + R.id.action_homeFragment_to_resources + ) + } + + AppFeatureName.PROOF_OF_VACCINE -> { + Pair( + R.drawable.ic_tile_proof_of_vaccine, + R.id.action_homeFragment_to_health_pass + ) + } + + AppFeatureName.SERVICES -> { + Pair(R.drawable.ic_organ_donor, R.id.services) + } } + return FeatureTileItem( + id = appFeatureDto.id, + name = appFeatureDto.name.value, + icon = tileIcon, + destinationId = endDestinationId, + payload = null, + isEditable = false + ) } } } - private fun validateBannerDates(bannerDto: BannerDto): Boolean { - val currentTime = System.currentTimeMillis() - return bannerDto.startDate.toEpochMilli() <= currentTime && - currentTime < bannerDto.endDate.toEpochMilli() - } + data class QuickLinkTileItem( + val id: Long, + val featureId: Long, + override val icon: Int, + override val name: String, + override val payload: String?, + override val destinationId: Int, + override val isEditable: Boolean + ) : QuickAccessTileItem( + icon, name, payload, destinationId, isEditable + ) { + companion object { + fun from(quickAccessTileDto: QuickAccessTileDto): QuickLinkTileItem { + val (tileIcon, endDestination) = when (quickAccessTileDto.tileName) { + QuickAccessLinkName.IMMUNIZATIONS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } - fun toggleBanner() { - _bannerState.update { it?.copy(expanded = it.expanded.not()) } - } + QuickAccessLinkName.MEDICATIONS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } - fun dismissBanner() { - _bannerState.update { it?.copy(isHidden = true) } - } + QuickAccessLinkName.LAB_RESULTS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } - private fun shouldDisplayReadMore(body: String): Boolean = - body.fromHtml().length > COMMUNICATION_BANNER_MAX_LENGTH + QuickAccessLinkName.COVID_19_TESTS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } + + QuickAccessLinkName.HEALTH_VISITS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } + + QuickAccessLinkName.MY_NOTES -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } + + QuickAccessLinkName.SPECIAL_AUTHORITY -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } + + QuickAccessLinkName.CLINICAL_DOCUMENTS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } - fun resetUiState() { - _uiState.tryEmit(HomeUiState()) + QuickAccessLinkName.HOSPITAL_VISITS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } + + QuickAccessLinkName.IMAGING_REPORTS -> { + Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + } + } + return QuickLinkTileItem( + id = quickAccessTileDto.id, + featureId = quickAccessTileDto.featureId, + name = quickAccessTileDto.tileName.value, + payload = quickAccessTileDto.tilePayload, + icon = tileIcon, + destinationId = endDestination, + isEditable = true + ) + } + } } } -data class BannerItem( +data class HomeBannerItem( val title: String, - val date: String, val body: String, - val displayReadMore: Boolean, - var expanded: Boolean = true, - var isHidden: Boolean = false -) - -data class HomeUiState( - val isLoading: Boolean = false, - val isOnBoardingRequired: Boolean = false, - val isReOnBoardingRequired: Boolean = false, - val isAuthenticationRequired: Boolean = false, - val isBcscLoginRequiredPostBiometrics: Boolean = false, - val patientFirstName: String? = null, - val isForceLogout: Boolean = false, - val displayServiceDownMessage: Boolean = false -) - -data class HomeRecordItem( - @DrawableRes val iconTitle: Int, - @StringRes val title: Int, - @StringRes val description: Int, - @DrawableRes val icon: Int, - @StringRes val btnTitle: Int, - val recordType: HomeNavigationType -) - -enum class HomeNavigationType { - HEALTH_RECORD, - RECOMMENDATIONS, - RESOURCES, - VACCINE_PROOF, + var isDismissed: Boolean = false +) { + fun showReadMore() = body.fromHtml().length > COMMUNICATION_BANNER_MAX_LENGTH } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt index 5f6fedc70..4fe6741b7 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt @@ -18,7 +18,6 @@ import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.ui.custom.AppBarDefaults import ca.bc.gov.bchealth.ui.custom.MyHealthBackButton import ca.bc.gov.bchealth.ui.custom.MyHealthToolBar -import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -60,7 +59,7 @@ class QuickAccessManagementFragment : BaseFragment(null) { } } - private fun onClickItem(item: QuickAccessTileItem) { + private fun onClickItem(item: QuickAccessManagementViewModel.QuickAccessItem) { viewModel.toggleItem(item) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt index 8a6345071..e7241c7cd 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementScreen.kt @@ -1,7 +1,6 @@ package ca.bc.gov.bchealth.ui.home.manage import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope @@ -14,7 +13,6 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Card import androidx.compose.material.Checkbox -import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -30,43 +28,39 @@ import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography import ca.bc.gov.bchealth.compose.bold +import ca.bc.gov.bchealth.compose.component.HGProgressIndicator import ca.bc.gov.bchealth.compose.theme.primaryBlue import ca.bc.gov.bchealth.compose.theme.statusBlue import ca.bc.gov.bchealth.compose.theme.white -import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem @Composable fun QuickAccessManagementScreen( viewModel: QuickAccessManagementViewModel, - onClickItem: (QuickAccessTileItem) -> Unit, + onClickItem: (QuickAccessManagementViewModel.QuickAccessItem) -> Unit, onUpdateCompleted: () -> Unit, modifier: Modifier = Modifier ) { val uiState = viewModel.uiState.collectAsState().value LaunchedEffect(Unit) { - viewModel.loadTilesUi() + viewModel.loadQuickAccessTileData() } - Box { - QuickAccessManagementContent(uiState.uiMap, onClickItem, modifier) - - if (uiState.isLoading) { - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center), - ) - } + if (uiState.isLoading) { + HGProgressIndicator(modifier) + } else { + QuickAccessManagementContent(uiState.featureWithQuickAccessItems, onClickItem = onClickItem) } if (uiState.isUpdateCompleted) { - onUpdateCompleted.invoke() + onUpdateCompleted() } } @Composable private fun QuickAccessManagementContent( - uiMap: Map>, - onClickItem: (QuickAccessTileItem) -> Unit, + featureWithQuickAccessItems: List, + onClickItem: (QuickAccessManagementViewModel.QuickAccessItem) -> Unit, modifier: Modifier = Modifier, ) { LazyColumn( @@ -83,22 +77,19 @@ private fun QuickAccessManagementContent( item { Spacer(modifier = Modifier.size(16.dp)) } - for ((category, tiles) in uiMap) { + featureWithQuickAccessItems.forEach { item { Text( - text = stringResource(id = category), + text = it.name, style = MyHealthTypography.body1.bold(), color = statusBlue ) } - item { Spacer(modifier = Modifier.size(12.dp)) } - - items(tiles) { tile -> + items(it.quickAccessItems) { tile -> TileItemUi(tile, onClickItem) Spacer(modifier = Modifier.size(10.dp)) } - item { Spacer(modifier = Modifier.size(6.dp)) } } } @@ -107,10 +98,10 @@ private fun QuickAccessManagementContent( @Composable private fun TileItemUi( - item: QuickAccessTileItem, - onClickItem: (QuickAccessTileItem) -> Unit, + item: QuickAccessManagementViewModel.QuickAccessItem, + onClickItem: (QuickAccessManagementViewModel.QuickAccessItem) -> Unit, ) { - val checkedState = remember { mutableStateOf(item.enabled) } + val checkedState = remember { mutableStateOf(item.isEnabled) } Card( modifier = Modifier.clickable { @@ -143,17 +134,12 @@ private fun TileItemUi( } @Composable -private fun RowScope.TileName(item: QuickAccessTileItem) { - val name = when (item) { - is QuickAccessTileItem.PredefinedItem -> stringResource(id = item.nameId) - is QuickAccessTileItem.DynamicItem -> item.text - } - +private fun RowScope.TileName(item: QuickAccessManagementViewModel.QuickAccessItem) { Text( modifier = Modifier .fillMaxWidth() .weight(1f), - text = name, + text = item.name, style = MyHealthTypography.body1.bold(), ) } @@ -161,27 +147,10 @@ private fun RowScope.TileName(item: QuickAccessTileItem) { @BasePreview @Composable private fun PreviewQuickAccessManagementContent() { - val sample = QuickAccessTileItem.PredefinedItem( - id = -1, - destinationId = -1, - destinationParam = null, - categoryId = -1, - enabled = false, - icon = -1, - nameId = -1 - ) MyHealthTheme { QuickAccessManagementContent( - mapOf( - R.string.health_records to listOf( - sample.copy(nameId = R.string.feature_medications, enabled = true), - sample.copy(nameId = R.string.feature_health_visit, enabled = false), - ), - R.string.services to listOf( - sample.copy(nameId = R.string.organ_donor, enabled = true), - ), - ), + emptyList(), {} ) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt index 9c884ff01..3e9829f9c 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt @@ -2,9 +2,7 @@ package ca.bc.gov.bchealth.ui.home.manage import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import ca.bc.gov.bchealth.ui.home.QuickAccessTileItem -import ca.bc.gov.bchealth.ui.home.toUiItem -import ca.bc.gov.repository.settings.AppFeatureRepository +import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -16,33 +14,44 @@ import javax.inject.Inject @HiltViewModel class QuickAccessManagementViewModel @Inject constructor( - private val appFeatureRepository: AppFeatureRepository + private val appFeatureRepository: AppFeatureWithQuickAccessTilesRepository ) : ViewModel() { private val _uiState = MutableStateFlow(QuickAccessManagementUiState()) val uiState: StateFlow = _uiState.asStateFlow() - fun loadTilesUi() = viewModelScope.launch { + fun loadQuickAccessTileData() = viewModelScope.launch { _uiState.update { it.copy(isLoading = true) } - val tiles = appFeatureRepository.getManageableTiles() - .map { it.toUiItem() } - .groupBy { it.categoryId } + val featureWithQuickAccessItems = appFeatureRepository.getAppFeaturesWithQuickAccessTiles().filter { it.appFeatureDto.hasManageableQuickAccessLinks } + .map { + FeatureWithQuickAccessItems( + id = it.appFeatureDto.id, + name = it.appFeatureDto.name.value, + quickAccessItems = it.quickAccessTiles.map { tile -> + QuickAccessItem( + tile.id, + tile.tileName.value, + tile.showAsQuickAccess + ) + } + ) + } - _uiState.update { it.copy(uiMap = tiles, isLoading = false) } + _uiState.update { it.copy(featureWithQuickAccessItems = featureWithQuickAccessItems, isLoading = false) } } - fun toggleItem(item: QuickAccessTileItem) { - item.enabled = item.enabled.not() + fun toggleItem(item: QuickAccessItem) { + item.isEnabled = item.isEnabled.not() } fun saveSelection() { viewModelScope.launch { _uiState.update { it.copy(isLoading = true) } - for ((_, features) in _uiState.value.uiMap) { - features.forEach { tileItem -> - appFeatureRepository.updateQuickAccessFlag(tileItem.id, tileItem.enabled) + _uiState.value.featureWithQuickAccessItems.forEach { + it.quickAccessItems.forEach { tile -> + // TODO : make call to the query to update the item } } @@ -53,8 +62,20 @@ class QuickAccessManagementViewModel @Inject constructor( } data class QuickAccessManagementUiState( - val uiMap: Map> = mapOf(), val isLoading: Boolean = false, val isUpdateCompleted: Boolean = false, + val featureWithQuickAccessItems: List = emptyList() + ) + + data class FeatureWithQuickAccessItems( + val id: Long = 0, + val name: String, + val quickAccessItems: List = emptyList() + ) + + data class QuickAccessItem( + val id: Long = 0, + val name: String, + var isEnabled: Boolean = false ) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt index d1b89fea1..051dec33b 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt @@ -227,8 +227,9 @@ class BcscAuthViewModel @Inject constructor( val isLoggedSuccess = bcscAuthRepo.checkSession() var userName: String? = null try { - userName = - patientRepository.findPatientByAuthStatus(AuthenticationStatus.AUTHENTICATED).fullName + val patient = + patientRepository.findPatientByAuthStatus(AuthenticationStatus.AUTHENTICATED) + userName = patient.fullName val loginSessionStatus = if (isLoggedSuccess) { LoginStatus.ACTIVE } else { @@ -238,7 +239,8 @@ class BcscAuthViewModel @Inject constructor( it.copy( showLoading = false, userName = userName, - loginStatus = loginSessionStatus + loginStatus = loginSessionStatus, + patient = patient ) } } catch (e: Exception) { @@ -457,11 +459,12 @@ data class AuthStatus( val userName: String? = null, val queItTokenUpdated: Boolean = false, val queItUrl: String? = null, - val loginStatus: LoginStatus = LoginStatus.NOT_AUTHENTICATED, + val loginStatus: LoginStatus? = null, val ageLimitCheck: AgeLimitCheck? = null, val canInitiateBcscLogin: Boolean? = null, val tosStatus: TOSStatus? = null, - val isConnected: Boolean = true + val isConnected: Boolean = true, + val patient: PatientDto? = null ) enum class LoginStatus { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesFragment.kt index 7ab3ac4fa..55c210fff 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesFragment.kt @@ -70,17 +70,19 @@ class ServicesFragment : BaseSecureFragment(null) { if (it.showLoading) { servicesViewModel.showProgressBar() } else { - when (it.loginStatus) { - LoginStatus.ACTIVE -> { - observeHealthRecordsSyncCompletion() - } + it.loginStatus?.let { status -> + when (status) { + LoginStatus.ACTIVE -> { + observeHealthRecordsSyncCompletion() + } - LoginStatus.EXPIRED -> { - findNavController().navigate(R.id.bcServiceCardSessionFragment) - } + LoginStatus.EXPIRED -> { + findNavController().navigate(R.id.bcServiceCardSessionFragment) + } - LoginStatus.NOT_AUTHENTICATED -> { - findNavController().navigate(R.id.bcServicesCardLoginFragment) + LoginStatus.NOT_AUTHENTICATED -> { + findNavController().navigate(R.id.bcServicesCardLoginFragment) + } } } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesScreen.kt index 119f87353..4de5692e3 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/services/ServicesScreen.kt @@ -2,7 +2,6 @@ package ca.bc.gov.bchealth.ui.services import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -36,6 +35,7 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.compose.MyHealthTypography +import ca.bc.gov.bchealth.compose.component.HGProgressIndicator import ca.bc.gov.common.model.services.OrganDonorStatusDto @Composable @@ -107,14 +107,7 @@ private fun ServiceScreenContent( Spacer(modifier = Modifier.height(16.dp)) if (onLoading || organDonorRegistrationDetail == null) { - Box( - modifier = modifier - .fillMaxSize() - ) { - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center), - ) - } + HGProgressIndicator(modifier) } else { OrganDonor(organDonorRegistrationDetail, organDonorFileStatus, onRegisterOnUpdateDecisionClicked = { url -> onRegisterOnUpdateDecisionClicked(url) diff --git a/app/src/main/java/ca/bc/gov/bchealth/utils/Extensions.kt b/app/src/main/java/ca/bc/gov/bchealth/utils/Extensions.kt index f6678174e..54ab84373 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/utils/Extensions.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/utils/Extensions.kt @@ -223,7 +223,7 @@ fun View.showNoInternetConnectionMessage(context: Context) { showErrorSnackbar(context.getString(R.string.no_internet_connection)) } -fun View?.showErrorSnackbar(message: String) { +fun View?.showErrorSnackbar(message: String, anchor: View? = null) { this ?: return val snackBar = Snackbar.make( @@ -232,6 +232,9 @@ fun View?.showErrorSnackbar(message: String) { snackBar.setAction(context.getString(R.string.dismiss)) { performClick() } + if (anchor != null) { + snackBar.anchorView = anchor + } snackBar.view.findViewById(com.google.android.material.R.id.snackbar_text).maxLines = 10 snackBar.show() diff --git a/app/src/main/java/ca/bc/gov/bchealth/viewmodel/SharedViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/viewmodel/SharedViewModel.kt index 764655e87..11e7822c2 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/viewmodel/SharedViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/viewmodel/SharedViewModel.kt @@ -25,4 +25,5 @@ class SharedViewModel @Inject constructor() : ViewModel() { var destinationId: Int = 0 var isBCSCAuthShown = false var isBiometricAuthShown = false + var shouldFetchBanner = true } diff --git a/app/src/main/res/drawable/ic_anchor.xml b/app/src/main/res/drawable/ic_anchor.xml new file mode 100644 index 000000000..932720169 --- /dev/null +++ b/app/src/main/res/drawable/ic_anchor.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_heart.xml b/app/src/main/res/drawable/ic_heart.xml new file mode 100644 index 000000000..26c374787 --- /dev/null +++ b/app/src/main/res/drawable/ic_heart.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/navigation/home.xml b/app/src/main/res/navigation/home.xml index 0a2cf9882..2cb7f1cb0 100644 --- a/app/src/main/res/navigation/home.xml +++ b/app/src/main/res/navigation/home.xml @@ -2,29 +2,8 @@ + app:startDestination="@id/homeFragment"> - - - - - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa3766c10..582ac5d46 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -500,7 +500,11 @@ What do you want to focus on today? New updates Quick access + Manage Immunization schedules + Log in + Gain full access to health records for you and your family. + Set a folder to display \non your home page \nfor quick access. Immunization Schedules diff --git a/common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt b/common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt new file mode 100644 index 000000000..4322d5700 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt @@ -0,0 +1,14 @@ +package ca.bc.gov.common.model + +enum class AppFeatureName(val value: String) { + HEALTH_RECORDS("Health records"), + IMMUNIZATION_SCHEDULES("Immunization schedules"), + HEALTH_RESOURCES("Health resources"), + PROOF_OF_VACCINE("Proof of vaccination"), + SERVICES("Services"); + + companion object { + private val map = AppFeatureName.values().associateBy(AppFeatureName::value) + operator fun get(value: String) = map[value] + } +} diff --git a/common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt b/common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt new file mode 100644 index 000000000..96b250af2 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt @@ -0,0 +1,19 @@ +package ca.bc.gov.common.model + +enum class QuickAccessLinkName(val value: String) { + IMMUNIZATIONS("Immunizations"), + MEDICATIONS("Medications"), + LAB_RESULTS("Lab Results"), + COVID_19_TESTS("COVID‑19 Tests"), + HEALTH_VISITS("Health Visits"), + MY_NOTES("My Notes"), + SPECIAL_AUTHORITY("Special Authority"), + CLINICAL_DOCUMENTS("Clinical Documents"), + HOSPITAL_VISITS("Hospital Visits"), + IMAGING_REPORTS("Imaging Reports"); + + companion object { + private val map = QuickAccessLinkName.values().associateBy(QuickAccessLinkName::value) + operator fun get(value: String) = map[value] + } +} diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt index 86657c082..18233edcb 100644 --- a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt +++ b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureDto.kt @@ -1,13 +1,10 @@ package ca.bc.gov.common.model.settings +import ca.bc.gov.common.model.AppFeatureName + data class AppFeatureDto( val id: Long = 0, - val featureName: String? = null, - val featureNameId: Int? = null, - val categoryId: Int, - val featureIconId: Int, - val destinationId: Int, - val destinationParam: String? = null, - val isManagementEnabled: Boolean = false, - val isQuickAccessEnabled: Boolean = false + val name: AppFeatureName, + val hasManageableQuickAccessLinks: Boolean = false, + val showAsQuickAccess: Boolean = false ) diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt new file mode 100644 index 000000000..8fddc3b36 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/settings/AppFeatureWithQuickAccessTilesDto.kt @@ -0,0 +1,6 @@ +package ca.bc.gov.common.model.settings + +data class AppFeatureWithQuickAccessTilesDto( + val appFeatureDto: AppFeatureDto, + val quickAccessTiles: List = emptyList() +) diff --git a/common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt b/common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt index d43409d47..a7d84b9ef 100644 --- a/common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt +++ b/common/src/main/java/ca/bc/gov/common/model/settings/QuickAccessTileDto.kt @@ -1,9 +1,11 @@ package ca.bc.gov.common.model.settings +import ca.bc.gov.common.model.QuickAccessLinkName + data class QuickAccessTileDto( val id: Long = 0, val featureId: Long = 0, - val titleNameId: Int, - val titleIconId: Int, - val isEnabled: Boolean = false + val tileName: QuickAccessLinkName, + val tilePayload: String? = null, + val showAsQuickAccess: Boolean = false ) diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json index 6ae4d3df8..ce7c843eb 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/13.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 13, - "identityHash": "cd63a7ddb38d8002c82af9bc1156c3dc", + "identityHash": "d250a01adf6d2d797cb46b6b9b0a2178", "entities": [ { "tableName": "patient", @@ -1998,132 +1998,12 @@ }, "indices": [], "foreignKeys": [] - }, - { - "tableName": "app_feature", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name_id` INTEGER NOT NULL, `feature_icon_id` INTEGER NOT NULL, `destination_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, `quick_access_enabled` INTEGER NOT NULL DEFAULT false)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "featureNameId", - "columnName": "feature_name_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "featureIconId", - "columnName": "feature_icon_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "destinationId", - "columnName": "destination_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isEnabled", - "columnName": "enabled", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "false" - }, - { - "fieldPath": "isQuickAccessEnabled", - "columnName": "quick_access_enabled", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "false" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_app_feature_feature_name_id_feature_icon_id", - "unique": true, - "columnNames": [ - "feature_name_id", - "feature_icon_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_app_feature_feature_name_id_feature_icon_id` ON `${TABLE_NAME}` (`feature_name_id`, `feature_icon_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "quick_access_tile", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_id` INTEGER NOT NULL, `tile_name_id` INTEGER NOT NULL, `tile_icon_id` INTEGER NOT NULL, `enabled` INTEGER NOT NULL DEFAULT false, FOREIGN KEY(`feature_id`) REFERENCES `app_feature`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "featureId", - "columnName": "feature_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tileNameId", - "columnName": "tile_name_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tileIconId", - "columnName": "tile_icon_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isEnabled", - "columnName": "enabled", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "false" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [ - { - "table": "app_feature", - "onDelete": "CASCADE", - "onUpdate": "CASCADE", - "columns": [ - "feature_id" - ], - "referencedColumns": [ - "id" - ] - } - ] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cd63a7ddb38d8002c82af9bc1156c3dc')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd250a01adf6d2d797cb46b6b9b0a2178')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt index 1c6533da8..b791475f3 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/AppFeatureLocalDataSource.kt @@ -1,6 +1,7 @@ package ca.bc.gov.data.datasource.local import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.AppFeatureWithQuickAccessTilesDto import ca.bc.gov.data.datasource.local.dao.AppFeatureDao import ca.bc.gov.data.model.mapper.toDto import ca.bc.gov.data.model.mapper.toEntity @@ -10,32 +11,11 @@ class AppFeatureLocalDataSource @Inject constructor( private val appFeatureDao: AppFeatureDao ) { - suspend fun insertIfEmpty(appFeatures: List) { - val count = appFeatureDao.countRegisters() - if (count == 0) { - appFeatures.forEach { dto -> - appFeatureDao.insert(dto.toEntity()) - } - } + suspend fun insert(appFeatureDto: AppFeatureDto): Long { + return appFeatureDao.insert(appFeatureDto.toEntity()) } - suspend fun updateQuickAccessFlag(id: Long, enabled: Boolean) { - appFeatureDao.updateQuickAccessFlag(id, enabled) - } - - suspend fun getQuickAccessTiles(): List { - return appFeatureDao.getQuickAccessTiles().map { - it.toDto() - } - } - - suspend fun getManageableTiles(): List { - return appFeatureDao.getManageableTiles().map { - it.toDto() - } - } - - suspend fun deleteAll() { - appFeatureDao.deleteAll() + suspend fun getAppFeaturesWithQuickAccessTiles(): List { + return appFeatureDao.getAllFeatureWithQuickAccessTiles().map { it.toDto() } } } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt index a76b72e7b..90e1e94a9 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt @@ -2,12 +2,17 @@ package ca.bc.gov.data.datasource.local import androidx.room.AutoMigration import androidx.room.Database +import androidx.room.DeleteColumn +import androidx.room.RenameColumn import androidx.room.RoomDatabase import androidx.room.TypeConverters +import androidx.room.migration.AutoMigrationSpec import ca.bc.gov.data.datasource.local.converter.AddressConverter +import ca.bc.gov.data.datasource.local.converter.AppFeatureNameConverter import ca.bc.gov.data.datasource.local.converter.AuthenticationStatusTypeConverter import ca.bc.gov.data.datasource.local.converter.DateTimeConverter import ca.bc.gov.data.datasource.local.converter.PatientNameConverter +import ca.bc.gov.data.datasource.local.converter.QuickAccessLinkNameConverter import ca.bc.gov.data.datasource.local.converter.SyncStatusConverter import ca.bc.gov.data.datasource.local.dao.AppFeatureDao import ca.bc.gov.data.datasource.local.dao.ClinicalDocumentDao @@ -30,6 +35,7 @@ import ca.bc.gov.data.datasource.local.dao.MedicationSummaryDao import ca.bc.gov.data.datasource.local.dao.NotificationDao import ca.bc.gov.data.datasource.local.dao.OrganDonorDao import ca.bc.gov.data.datasource.local.dao.PatientDao +import ca.bc.gov.data.datasource.local.dao.QuickAccessTileDao import ca.bc.gov.data.datasource.local.dao.SpecialAuthorityDao import ca.bc.gov.data.datasource.local.dao.UserProfileDao import ca.bc.gov.data.datasource.local.dao.VaccineRecordDao @@ -56,6 +62,7 @@ import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity @@ -63,7 +70,7 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity * @author Pinakin Kansara */ @Database( - version = 15, + version = 16, entities = [ PatientEntity::class, VaccineRecordEntity::class, @@ -90,6 +97,7 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity DiagnosticImagingDataEntity::class, NotificationEntity::class, AppFeatureEntity::class, + QuickAccessTileEntity::class ], autoMigrations = [ AutoMigration(from = 8, to = 9), @@ -98,7 +106,8 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity AutoMigration(from = 11, to = 12), AutoMigration(from = 12, to = 13), AutoMigration(from = 13, to = 14), - AutoMigration(from = 14, to = 15) + AutoMigration(from = 14, to = 15), + AutoMigration(from = 15, to = 16, spec = MyHealthDataBase.AppFeatureMigration::class) ], exportSchema = true ) @@ -107,10 +116,33 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity AuthenticationStatusTypeConverter::class, AddressConverter::class, SyncStatusConverter::class, - PatientNameConverter::class + PatientNameConverter::class, + AppFeatureNameConverter::class, + QuickAccessLinkNameConverter::class ) abstract class MyHealthDataBase : RoomDatabase() { + @DeleteColumn.Entries( + DeleteColumn(tableName = "app_feature", columnName = "feature_name_id"), + DeleteColumn(tableName = "app_feature", columnName = "feature_icon_id"), + DeleteColumn(tableName = "app_feature", columnName = "destination_id"), + DeleteColumn(tableName = "app_feature", columnName = "category_name_id"), + DeleteColumn(tableName = "app_feature", columnName = "destination_param") + ) + @RenameColumn.Entries( + RenameColumn( + tableName = "app_feature", + fromColumnName = "quick_access_enabled", + toColumnName = "show_as_quick_access" + ), + RenameColumn( + tableName = "app_feature", + fromColumnName = "is_management_enabled", + toColumnName = "has_manageable_quick_access_links" + ) + ) + class AppFeatureMigration : AutoMigrationSpec + abstract fun getPatientDao(): PatientDao abstract fun getVaccineRecordDao(): VaccineRecordDao @@ -158,4 +190,6 @@ abstract class MyHealthDataBase : RoomDatabase() { abstract fun getNotificationDao(): NotificationDao abstract fun getAppFeatureDao(): AppFeatureDao + + abstract fun getQuickAccessTileDao(): QuickAccessTileDao } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt new file mode 100644 index 000000000..506d63e4d --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt @@ -0,0 +1,15 @@ +package ca.bc.gov.data.datasource.local + +import ca.bc.gov.common.model.settings.QuickAccessTileDto +import ca.bc.gov.data.datasource.local.dao.QuickAccessTileDao +import ca.bc.gov.data.model.mapper.toEntity +import javax.inject.Inject + +class QuickActionTileLocalDataSource @Inject constructor( + private val quickAccessTileDao: QuickAccessTileDao +) { + + suspend fun insert(quickAccessTileDto: QuickAccessTileDto): Long { + return quickAccessTileDao.insert(quickAccessTileDto.toEntity()) + } +} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/converter/AppFeatureNameConverter.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/converter/AppFeatureNameConverter.kt new file mode 100644 index 000000000..acb94a522 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/converter/AppFeatureNameConverter.kt @@ -0,0 +1,13 @@ +package ca.bc.gov.data.datasource.local.converter + +import androidx.room.TypeConverter +import ca.bc.gov.common.model.AppFeatureName + +class AppFeatureNameConverter { + + @TypeConverter + fun stringToEnum(name: String) = AppFeatureName[name] + + @TypeConverter + fun enumToString(appFeatureName: AppFeatureName) = appFeatureName.value +} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/converter/QuickAccessLinkNameConverter.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/converter/QuickAccessLinkNameConverter.kt new file mode 100644 index 000000000..9851a7996 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/converter/QuickAccessLinkNameConverter.kt @@ -0,0 +1,13 @@ +package ca.bc.gov.data.datasource.local.converter + +import androidx.room.TypeConverter +import ca.bc.gov.common.model.QuickAccessLinkName + +class QuickAccessLinkNameConverter { + + @TypeConverter + fun stringToEnum(value: String) = QuickAccessLinkName[value] + + @TypeConverter + fun enumToString(linkName: QuickAccessLinkName) = linkName.value +} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt index 16093e335..800ff3ee2 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/AppFeatureDao.kt @@ -4,26 +4,17 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import ca.bc.gov.data.datasource.local.entity.relations.AppFeatureWithQuickAccessTiles import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity @Dao interface AppFeatureDao { - @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insert(appFeatureEntity: AppFeatureEntity): Long - @Query("UPDATE app_feature SET quick_access_enabled = :enabled WHERE ID = :appFeatureId") - suspend fun updateQuickAccessFlag(appFeatureId: Long, enabled: Boolean) - @Query("DELETE FROM app_feature") suspend fun deleteAll() - @Query("SELECT count(*) FROM app_feature") - suspend fun countRegisters(): Int - - @Query("SELECT * FROM app_feature WHERE quick_access_enabled = 1 ") - suspend fun getQuickAccessTiles(): List - - @Query("SELECT * FROM app_feature WHERE is_management_enabled = 1 ") - suspend fun getManageableTiles(): List + @Query("SELECT * FROM app_feature") + suspend fun getAllFeatureWithQuickAccessTiles(): List } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt new file mode 100644 index 000000000..42092ffc0 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt @@ -0,0 +1,7 @@ +package ca.bc.gov.data.datasource.local.dao + +import androidx.room.Dao +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity + +@Dao +interface QuickAccessTileDao : BaseDao diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt new file mode 100644 index 000000000..b27a645ac --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/AppFeatureWithQuickAccessTiles.kt @@ -0,0 +1,17 @@ +package ca.bc.gov.data.datasource.local.entity.relations + +import androidx.room.Embedded +import androidx.room.Relation +import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity + +data class AppFeatureWithQuickAccessTiles( + @Embedded + val appFeature: AppFeatureEntity, + + @Relation( + parentColumn = "id", + entityColumn = "feature_id" + ) + val quickAccessTiles: List = emptyList() +) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt index ad58cc283..038f97622 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/AppFeatureEntity.kt @@ -2,28 +2,21 @@ package ca.bc.gov.data.datasource.local.entity.settings import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.Index import androidx.room.PrimaryKey +import ca.bc.gov.common.model.AppFeatureName @Entity( tableName = "app_feature", + indices = [Index(value = ["feature_name"], unique = true)] ) data class AppFeatureEntity( @PrimaryKey(autoGenerate = true) val id: Long = 0, @ColumnInfo(name = "feature_name") - val featureName: String?, - @ColumnInfo(name = "feature_name_id") - val featureNameId: Int?, - @ColumnInfo(name = "category_name_id") - val categoryNameId: Int, - @ColumnInfo(name = "feature_icon_id") - val featureIconId: Int, - @ColumnInfo(name = "destination_id") - val destinationId: Int, - @ColumnInfo(name = "destination_param") - val destinationParam: String?, - @ColumnInfo(name = "is_management_enabled", defaultValue = "false") - val isManagementEnabled: Boolean = false, - @ColumnInfo(name = "quick_access_enabled", defaultValue = "false") - val isQuickAccessEnabled: Boolean = false + val name: AppFeatureName, + @ColumnInfo(name = "has_manageable_quick_access_links", defaultValue = "false") + val hasManageableQuickAccessLinks: Boolean = false, + @ColumnInfo(name = "show_as_quick_access", defaultValue = "false") + val showAsQuickAccess: Boolean = false ) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt new file mode 100644 index 000000000..14708894e --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileEntity.kt @@ -0,0 +1,33 @@ +package ca.bc.gov.data.datasource.local.entity.settings + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import ca.bc.gov.common.model.QuickAccessLinkName + +@Entity( + tableName = "quick_access_tile", + foreignKeys = [ + androidx.room.ForeignKey( + entity = AppFeatureEntity::class, + parentColumns = ["id"], + childColumns = ["feature_id"], + onDelete = androidx.room.ForeignKey.CASCADE, + onUpdate = androidx.room.ForeignKey.CASCADE + ) + ], + indices = [Index(value = ["tile_name"], unique = true)] +) +data class QuickAccessTileEntity( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + @ColumnInfo(name = "feature_id") + val featureId: Long, + @ColumnInfo(name = "tile_name") + val tileName: QuickAccessLinkName, + @ColumnInfo(name = "tile_payload", defaultValue = "null") + val tilePayload: String? = null, + @ColumnInfo(name = "show_as_quick_access", defaultValue = "false") + val showAsQuickAccess: Boolean = false +) diff --git a/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt b/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt index 5e11957b9..47d7ce943 100644 --- a/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt +++ b/data/src/main/java/ca/bc/gov/data/di/LocalDataSourceModule.kt @@ -20,6 +20,7 @@ import ca.bc.gov.data.datasource.local.MyHealthDataBase import ca.bc.gov.data.datasource.local.NotificationLocalDataSource import ca.bc.gov.data.datasource.local.OrganDonorLocalDataSource import ca.bc.gov.data.datasource.local.PatientLocalDataSource +import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource import ca.bc.gov.data.datasource.local.SpecialAuthorityLocalDataSource import ca.bc.gov.data.datasource.local.UserProfileLocalDataSource import ca.bc.gov.data.datasource.local.VaccineRecordLocalDataSource @@ -159,4 +160,9 @@ class LocalDataSourceModule { @Singleton fun provideAppFeatureLocalDataSource(db: MyHealthDataBase): AppFeatureLocalDataSource = AppFeatureLocalDataSource(db.getAppFeatureDao()) + + @Provides + @Singleton + fun provideQuickAccessLocalDataSource(db: MyHealthDataBase): QuickActionTileLocalDataSource = + QuickActionTileLocalDataSource(db.getQuickAccessTileDao()) } diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt index f4bf61c68..f5e9f05b0 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt @@ -23,6 +23,7 @@ import ca.bc.gov.common.model.patient.PatientNameDto import ca.bc.gov.common.model.services.DiagnosticImagingDataDto import ca.bc.gov.common.model.services.OrganDonorDto import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.common.model.specialauthority.SpecialAuthorityDto import ca.bc.gov.common.model.test.CovidOrderDto import ca.bc.gov.common.model.test.CovidTestDto @@ -52,6 +53,7 @@ import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity @@ -361,12 +363,15 @@ fun NotificationDto.toEntity() = NotificationEntity( fun AppFeatureDto.toEntity() = AppFeatureEntity( id, - featureName, - featureNameId, - categoryId, - featureIconId, - destinationId, - destinationParam, - isManagementEnabled, - isQuickAccessEnabled + name, + hasManageableQuickAccessLinks, + showAsQuickAccess +) + +fun QuickAccessTileDto.toEntity() = QuickAccessTileEntity( + id, + featureId, + tileName, + tilePayload, + showAsQuickAccess ) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt index 73872a6c7..bb8c58575 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt @@ -42,6 +42,8 @@ import ca.bc.gov.common.model.relation.VaccineWithDosesDto import ca.bc.gov.common.model.services.DiagnosticImagingDataDto import ca.bc.gov.common.model.services.OrganDonorDto import ca.bc.gov.common.model.settings.AppFeatureDto +import ca.bc.gov.common.model.settings.AppFeatureWithQuickAccessTilesDto +import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.common.model.specialauthority.SpecialAuthorityDto import ca.bc.gov.common.model.test.CovidOrderDto import ca.bc.gov.common.model.test.CovidOrderWithCovidTestAndPatientDto @@ -76,6 +78,7 @@ import ca.bc.gov.data.datasource.local.entity.medication.DispensingPharmacyEntit import ca.bc.gov.data.datasource.local.entity.medication.MedicationRecordEntity import ca.bc.gov.data.datasource.local.entity.medication.MedicationSummaryEntity import ca.bc.gov.data.datasource.local.entity.notification.NotificationEntity +import ca.bc.gov.data.datasource.local.entity.relations.AppFeatureWithQuickAccessTiles import ca.bc.gov.data.datasource.local.entity.relations.MedicationWithSummaryAndPharmacy import ca.bc.gov.data.datasource.local.entity.relations.PatientWithClinicalDocuments import ca.bc.gov.data.datasource.local.entity.relations.PatientWithCovidOrderAndCovidTest @@ -91,6 +94,7 @@ import ca.bc.gov.data.datasource.local.entity.relations.VaccineRecordWithDose import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEntity import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity import java.time.Instant @@ -479,13 +483,21 @@ fun NotificationEntity.toDto() = NotificationDto( ) fun AppFeatureEntity.toDto() = AppFeatureDto( - id = id, - featureName = featureName, - featureNameId = featureNameId, - categoryId = categoryNameId, - featureIconId = featureIconId, - destinationId = destinationId, - destinationParam = destinationParam, - isManagementEnabled = isManagementEnabled, - isQuickAccessEnabled = isQuickAccessEnabled + id, + name, + hasManageableQuickAccessLinks, + showAsQuickAccess +) + +fun QuickAccessTileEntity.toDto() = QuickAccessTileDto( + id, + featureId, + tileName, + tilePayload, + showAsQuickAccess +) + +fun AppFeatureWithQuickAccessTiles.toDto() = AppFeatureWithQuickAccessTilesDto( + appFeature.toDto(), + quickAccessTiles.map { it.toDto() } ) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/ResponseToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/ResponseToDtoMapper.kt index 8e107f7b6..c34ca3671 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/ResponseToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/ResponseToDtoMapper.kt @@ -402,12 +402,23 @@ fun Recommendation.toDto(): ImmunizationRecommendationsDto { ) } -fun BannerPayload.toDto() = BannerDto( - title = this.title, - body = this.body, - startDate = this.startDate.toDateTimeZ(), - endDate = this.endDate.toDateTimeZ() -) +fun BannerPayload.toDto(): BannerDto? { + val startDate = this.startDate.toDateTimeZ() + val endDate = this.endDate.toDateTimeZ() + val currentTime = System.currentTimeMillis() + return if (startDate.toEpochMilli() <= currentTime && + currentTime < endDate.toEpochMilli() + ) { + BannerDto( + title = this.title, + body = this.body, + startDate = this.startDate.toDateTimeZ(), + endDate = this.endDate.toDateTimeZ() + ) + } else { + null + } +} fun DependentPayload.toDto() = DependentDto( hdid = dependentInformation.hdid, diff --git a/preference/src/main/java/ca/bc/gov/preference/EncryptedPreferenceStorage.kt b/preference/src/main/java/ca/bc/gov/preference/EncryptedPreferenceStorage.kt index 643cea227..879ea07ff 100644 --- a/preference/src/main/java/ca/bc/gov/preference/EncryptedPreferenceStorage.kt +++ b/preference/src/main/java/ca/bc/gov/preference/EncryptedPreferenceStorage.kt @@ -40,6 +40,7 @@ class EncryptedPreferenceStorage @Inject constructor( private const val APP_VERSION_CODE = "APP_VERSION_CODE" private const val RE_ON_BOARDING_REQUIRED = "RE_ON_BOARDING_REQUIRED" private const val PREVIOUS_ON_BOARDING_SCREEN_NAME = "PREVIOUS_ON_BOARDING_SCREEN_NAME" + private const val QUICK_ACCESS_TILE_MANAGEMENT_TUTORIAL = "QUICK_ACCESS_TILE_MANAGEMENT_TUTORIAL" } var cookies: MutableSet? @@ -196,4 +197,12 @@ class EncryptedPreferenceStorage @Inject constructor( encryptedSharedPreferences.edit().putBoolean(RE_ON_BOARDING_REQUIRED, value) .apply() } + + var isQuickAccessTileTutorialRequired: Boolean + get() = encryptedSharedPreferences.getBoolean(QUICK_ACCESS_TILE_MANAGEMENT_TUTORIAL, true) + set(value) { + encryptedSharedPreferences.edit() + .putBoolean(QUICK_ACCESS_TILE_MANAGEMENT_TUTORIAL, value) + .apply() + } } diff --git a/repository/src/main/java/ca/bc/gov/repository/OnBoardingRepository.kt b/repository/src/main/java/ca/bc/gov/repository/OnBoardingRepository.kt index b27552bfe..cc938d9e7 100644 --- a/repository/src/main/java/ca/bc/gov/repository/OnBoardingRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/OnBoardingRepository.kt @@ -43,9 +43,16 @@ class OnBoardingRepository @Inject constructor( fun checkIfReOnBoardingRequired(currentAppVersionCode: Int) { if (!onBoardingRequired && currentAppVersionCode > previousVersionCode && - !BuildConfig.FLAG_NEW_ON_BOARDING_SCREEN.equals(previousOnBoardingScreenName, ignoreCase = true) + !BuildConfig.FLAG_NEW_ON_BOARDING_SCREEN.equals( + previousOnBoardingScreenName, + ignoreCase = true + ) ) { isReOnBoardingRequired = true } } + + fun checkIfAppUpdated(currentAppVersionCode: Int): Boolean { + return currentAppVersionCode > previousVersionCode + } } diff --git a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt index 844984117..070ef5ca6 100644 --- a/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt +++ b/repository/src/main/java/ca/bc/gov/repository/di/RepositoriesModule.kt @@ -16,6 +16,7 @@ import ca.bc.gov.data.datasource.local.MedicationRecordLocalDataSource import ca.bc.gov.data.datasource.local.NotificationLocalDataSource import ca.bc.gov.data.datasource.local.OrganDonorLocalDataSource import ca.bc.gov.data.datasource.local.PatientLocalDataSource +import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource import ca.bc.gov.data.datasource.local.VaccineRecordLocalDataSource import ca.bc.gov.data.datasource.remote.BannerRemoteDataSource import ca.bc.gov.data.datasource.remote.CommentRemoteDataSource @@ -58,6 +59,8 @@ import ca.bc.gov.repository.services.DiagnosticImagingRepository import ca.bc.gov.repository.services.OrganDonorRepository import ca.bc.gov.repository.services.PatientServicesRepository import ca.bc.gov.repository.settings.AppFeatureRepository +import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository +import ca.bc.gov.repository.settings.QuickAccessTileRepository import ca.bc.gov.repository.testrecord.CovidOrderRepository import ca.bc.gov.repository.testrecord.CovidTestRepository import ca.bc.gov.repository.utils.Base64ToInputImageConverter @@ -356,4 +359,18 @@ class RepositoriesModule { fun provideAppFeatureRepository( appFeatureLocalDataSource: AppFeatureLocalDataSource ): AppFeatureRepository = AppFeatureRepository(appFeatureLocalDataSource) + + @Provides + @Singleton + fun providesQuickAccessTileRepository( + quickActionTileLocalDataSource: QuickActionTileLocalDataSource + ): QuickAccessTileRepository = QuickAccessTileRepository(quickActionTileLocalDataSource) + + @Provides + @Singleton + fun providesAppFeatureWithQuickAccessTilesRepository( + appFeatureRepository: AppFeatureRepository, + preferenceStorage: EncryptedPreferenceStorage + ): AppFeatureWithQuickAccessTilesRepository = + AppFeatureWithQuickAccessTilesRepository(appFeatureRepository, preferenceStorage) } diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt index 666b12e21..5d3ea947a 100644 --- a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureRepository.kt @@ -7,15 +7,10 @@ import javax.inject.Inject class AppFeatureRepository @Inject constructor( private val appFeatureLocalDataSource: AppFeatureLocalDataSource ) { - suspend fun insert(appFeatures: List) { - appFeatureLocalDataSource.insertIfEmpty(appFeatures) - } - suspend fun updateQuickAccessFlag(id: Long, enabled: Boolean) { - appFeatureLocalDataSource.updateQuickAccessFlag(id, enabled) + suspend fun insert(appFeatureDto: AppFeatureDto): Long { + return appFeatureLocalDataSource.insert(appFeatureDto) } - suspend fun getQuickAccessTiles() = appFeatureLocalDataSource.getQuickAccessTiles() - - suspend fun getManageableTiles() = appFeatureLocalDataSource.getManageableTiles() + suspend fun getAppFeaturesWithQuickAccessTiles() = appFeatureLocalDataSource.getAppFeaturesWithQuickAccessTiles() } diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt new file mode 100644 index 000000000..ab92a10fd --- /dev/null +++ b/repository/src/main/java/ca/bc/gov/repository/settings/AppFeatureWithQuickAccessTilesRepository.kt @@ -0,0 +1,18 @@ +package ca.bc.gov.repository.settings + +import ca.bc.gov.preference.EncryptedPreferenceStorage +import javax.inject.Inject + +class AppFeatureWithQuickAccessTilesRepository @Inject constructor( + private val appFeatureRepository: AppFeatureRepository, + private val preferenceStorage: EncryptedPreferenceStorage +) { + + var isQuickAccessTileTutorialRequired: Boolean + get() = preferenceStorage.isQuickAccessTileTutorialRequired + set(value) { + preferenceStorage.isQuickAccessTileTutorialRequired = value + } + + suspend fun getAppFeaturesWithQuickAccessTiles() = appFeatureRepository.getAppFeaturesWithQuickAccessTiles() +} diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt new file mode 100644 index 000000000..cd610b729 --- /dev/null +++ b/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt @@ -0,0 +1,13 @@ +package ca.bc.gov.repository.settings + +import ca.bc.gov.common.model.settings.QuickAccessTileDto +import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource +import javax.inject.Inject + +class QuickAccessTileRepository @Inject constructor( + private val quickActionTileLocalDataSource: QuickActionTileLocalDataSource +) { + + suspend fun insert(quickAccessTileDto: QuickAccessTileDto) = + quickActionTileLocalDataSource.insert(quickAccessTileDto) +} diff --git a/scripts/versions.gradle b/scripts/versions.gradle index 27a50a3b6..d71b3e3d3 100644 --- a/scripts/versions.gradle +++ b/scripts/versions.gradle @@ -16,23 +16,23 @@ versions.gradle = '7.4.2' versions.annotation = '1.6.0' versions.appcompat = '1.6.1' -versions.core_ktx = "1.10.0" +versions.core_ktx = "1.10.1" versions.constraint_layout = "2.1.4" versions.constraint_compose = "1.0.1" versions.coroutines = "1.6.4" versions.swipe_refresh_layout = "1.1.0" versions.room = "2.5.1" -versions.material = "1.8.0" +versions.material = "1.9.0" versions.recycler = "1.3.0" versions.coordinator = "1.2.0" versions.constraint_compose = "1.0.1" -versions.navigation = "2.5.3" +versions.navigation = "2.6.0" versions.lifecycle = "2.6.1" versions.compose_compiler = "1.4.6" -versions.compose_bom = "2023.04.01" +versions.compose_bom = "2023.06.01" versions.hilt = "2.45" From 2b0039bb517e9ae0e95d93b80263269ebdd96e45 Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Mon, 26 Jun 2023 09:20:01 -0700 Subject: [PATCH 22/38] HAPP-1535 - added home screen redesign 2.0 for non authenticated users. --- .../java/ca/bc/gov/bchealth/MainActivity.kt | 1 + .../bc/gov/bchealth/ui/home/HomeBannerUI.kt | 0 .../ca/bc/gov/bchealth/ui/home/HomeCardUI.kt | 0 .../bchealth/ui/home/HomeComposeFragment.kt | 59 +++++++++++++++++++ .../bchealth/ui/home/HomeComposeViewModel.kt | 45 ++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt index 6f85ec7a0..951befdea 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt @@ -89,6 +89,7 @@ class MainActivity : AppCompatActivity() { R.id.vaccineRecordDetailFragment, R.id.addHealthRecordsFragment, R.id.homeFragment, + R.id.homeComposeFragment, R.id.bannerDetailFragment, R.id.newsfeedFragment, R.id.servicesFragment, diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeBannerUI.kt new file mode 100644 index 000000000..e69de29bb diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeCardUI.kt new file mode 100644 index 000000000..e69de29bb diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt index e69de29bb..02074f581 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt @@ -0,0 +1,59 @@ +package ca.bc.gov.bchealth.ui.home + +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.component.HGTopAppBar +import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.ui.BaseSecureFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class HomeComposeFragment : BaseSecureFragment(null) { + + private val viewModel: HomeComposeViewModel by viewModels() + + @Composable + override fun GetComposableLayout() { + val menuItems = listOf( + TopAppBarActionItem.IconActionItem.AlwaysShown( + title = getString(R.string.settings), + onClick = { findNavController().navigate(R.id.settingsFragment) }, + icon = R.drawable.ic_menu_settings, + contentDescription = getString(R.string.settings), + ) + ) + HealthGatewayTheme { + Scaffold( + topBar = { + HGTopAppBar( + title = stringResource(id = R.string.home), + actionItems = menuItems + ) + }, + content = { + HomeScreen( + Modifier + .statusBarsPadding() + .navigationBarsPadding() + .padding(it), + viewModel, + onQuickAccessTileClicked = ::onQuickAccessTileClicked + ) + } + ) + } + } + + private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { + findNavController().navigate(quickAccessTileItem.destinationId) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt index e69de29bb..728efae0e 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt @@ -0,0 +1,45 @@ +package ca.bc.gov.bchealth.ui.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class HomeComposeViewModel @Inject constructor( + private val appFeatureWithQuickAccessTilesRepository: AppFeatureWithQuickAccessTilesRepository +) : ViewModel() { + private val _uiState = MutableStateFlow(HomeComposeUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadQuickAccessTiles() = viewModelScope.launch { + val quickAccessTileItems = + appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() + .filter { it.appFeatureDto.isQuickAccessEnabled } + .map { + QuickAccessTileItem( + it.appFeatureDto.featureIconId, + it.appFeatureDto.featureNameId, + it.appFeatureDto.destinationId + ) + } + + _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } + } +} + +data class HomeComposeUiState( + val quickAccessTileItems: List = emptyList() +) + +data class QuickAccessTileItem( + val icon: Int, + val name: Int, + val destinationId: Int +) From edcf9de308d517cdf84bfe76a692b3cc217d7198 Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Wed, 26 Jul 2023 10:14:41 -0700 Subject: [PATCH 23/38] HAPP-1614 - fixed home screen recommended immunization icon based on user login status & response from API. --- app/build.gradle | 1 + .../java/ca/bc/gov/bchealth/MainActivity.kt | 1 - .../ca/bc/gov/bchealth/SplashViewModel.kt | 7 +++ .../bchealth/ui/home/HomeComposeFragment.kt | 59 ------------------- .../bchealth/ui/home/HomeComposeViewModel.kt | 45 -------------- .../bc/gov/bchealth/ui/home/HomeFragment.kt | 1 + .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 17 +++++- .../bc/gov/bchealth/ui/home/HomeViewModel.kt | 56 ++++++++++++++---- .../ic_recommendation_immunization.xml | 16 +++++ .../ca/bc/gov/common/model/AppFeatureName.kt | 1 + ...munizationRecommendationLocalDataSource.kt | 2 + .../dao/ImmunizationRecommendationDao.kt | 3 + .../ImmunizationRecommendationRepository.kt | 2 + 13 files changed, 94 insertions(+), 117 deletions(-) delete mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt delete mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt create mode 100644 app/src/main/res/drawable/ic_recommendation_immunization.xml diff --git a/app/build.gradle b/app/build.gradle index 921bb1ecf..70b02751e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,6 +113,7 @@ dependencies { implementation composeBom implementation 'androidx.compose.material:material' implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.runtime:runtime-livedata' implementation "androidx.constraintlayout:constraintlayout-compose:$versions.constraint_compose" debugImplementation 'androidx.compose.ui:ui-tooling' diff --git a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt index 951befdea..6f85ec7a0 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/MainActivity.kt @@ -89,7 +89,6 @@ class MainActivity : AppCompatActivity() { R.id.vaccineRecordDetailFragment, R.id.addHealthRecordsFragment, R.id.homeFragment, - R.id.homeComposeFragment, R.id.bannerDetailFragment, R.id.newsfeedFragment, R.id.servicesFragment, diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index 87b44b100..d65150e4a 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -84,6 +84,13 @@ class SplashViewModel @Inject constructor( ) appFeatureRepository.insert(immunizationSchedule) + val recommendations = AppFeatureDto( + name = AppFeatureName.RECOMMENDED_IMMUNIZATIONS, + hasManageableQuickAccessLinks = false, + showAsQuickAccess = true + ) + appFeatureRepository.insert(recommendations) + val healthResources = AppFeatureDto( name = AppFeatureName.HEALTH_RESOURCES, hasManageableQuickAccessLinks = false, diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt deleted file mode 100644 index 02074f581..000000000 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeFragment.kt +++ /dev/null @@ -1,59 +0,0 @@ -package ca.bc.gov.bchealth.ui.home - -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.fragment.app.viewModels -import androidx.navigation.fragment.findNavController -import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.component.HGTopAppBar -import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem -import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme -import ca.bc.gov.bchealth.ui.BaseSecureFragment -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class HomeComposeFragment : BaseSecureFragment(null) { - - private val viewModel: HomeComposeViewModel by viewModels() - - @Composable - override fun GetComposableLayout() { - val menuItems = listOf( - TopAppBarActionItem.IconActionItem.AlwaysShown( - title = getString(R.string.settings), - onClick = { findNavController().navigate(R.id.settingsFragment) }, - icon = R.drawable.ic_menu_settings, - contentDescription = getString(R.string.settings), - ) - ) - HealthGatewayTheme { - Scaffold( - topBar = { - HGTopAppBar( - title = stringResource(id = R.string.home), - actionItems = menuItems - ) - }, - content = { - HomeScreen( - Modifier - .statusBarsPadding() - .navigationBarsPadding() - .padding(it), - viewModel, - onQuickAccessTileClicked = ::onQuickAccessTileClicked - ) - } - ) - } - } - - private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { - findNavController().navigate(quickAccessTileItem.destinationId) - } -} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt deleted file mode 100644 index 728efae0e..000000000 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeComposeViewModel.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ca.bc.gov.bchealth.ui.home - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class HomeComposeViewModel @Inject constructor( - private val appFeatureWithQuickAccessTilesRepository: AppFeatureWithQuickAccessTilesRepository -) : ViewModel() { - private val _uiState = MutableStateFlow(HomeComposeUiState()) - val uiState: StateFlow = _uiState.asStateFlow() - - fun loadQuickAccessTiles() = viewModelScope.launch { - val quickAccessTileItems = - appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() - .filter { it.appFeatureDto.isQuickAccessEnabled } - .map { - QuickAccessTileItem( - it.appFeatureDto.featureIconId, - it.appFeatureDto.featureNameId, - it.appFeatureDto.destinationId - ) - } - - _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } - } -} - -data class HomeComposeUiState( - val quickAccessTileItems: List = emptyList() -) - -data class QuickAccessTileItem( - val icon: Int, - val name: Int, - val destinationId: Int -) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt index 6f300dfa8..443928e12 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt @@ -46,6 +46,7 @@ class HomeFragment : BaseSecureFragment(null) { findNavController().currentBackStackEntry?.savedStateHandle?.remove( BiometricsAuthenticationFragment.BIOMETRIC_STATE ) + viewModel.executeOneTimeDataFetch() } else -> { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index e7ddf2ad6..1f54416ff 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -21,9 +21,11 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -33,6 +35,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet +import androidx.work.WorkInfo +import androidx.work.WorkManager import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.component.AnnouncementBannerUI @@ -47,6 +51,7 @@ import ca.bc.gov.bchealth.compose.theme.white import ca.bc.gov.bchealth.ui.login.BcscAuthViewModel import ca.bc.gov.bchealth.ui.login.LoginStatus import ca.bc.gov.bchealth.viewmodel.SharedViewModel +import ca.bc.gov.repository.bcsc.BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME @Composable fun HomeScreen( @@ -106,8 +111,16 @@ fun HomeScreen( HGProgressIndicator(modifier) } else { authState.loginStatus?.let { - LaunchedEffect(key1 = Unit) { - viewModel.loadQuickAccessTiles(it) + + val context = LocalContext.current + val workRequest = WorkManager.getInstance(context) + .getWorkInfosForUniqueWorkLiveData(BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME) + .observeAsState() + if (workRequest.value?.firstOrNull()?.state == WorkInfo.State.RUNNING) { + } else { + LaunchedEffect(key1 = Unit) { + viewModel.loadQuickAccessTiles(it) + } } if (sharedViewModel.shouldFetchBanner) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt index 57c73a1a6..6cec1b93a 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt @@ -9,26 +9,32 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.ui.login.LoginStatus import ca.bc.gov.bchealth.utils.COMMUNICATION_BANNER_MAX_LENGTH import ca.bc.gov.bchealth.utils.fromHtml +import ca.bc.gov.bchealth.workers.WorkerInvoker import ca.bc.gov.common.model.AppFeatureName import ca.bc.gov.common.model.QuickAccessLinkName import ca.bc.gov.common.model.settings.AppFeatureDto import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.repository.BannerRepository import ca.bc.gov.repository.OnBoardingRepository +import ca.bc.gov.repository.immunization.ImmunizationRecommendationRepository import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( private val appFeatureWithQuickAccessTilesRepository: AppFeatureWithQuickAccessTilesRepository, private val onBoardingRepository: OnBoardingRepository, - private val bannerRepository: BannerRepository + private val bannerRepository: BannerRepository, + private val workerInvoker: WorkerInvoker, + private val recommendationRepository: ImmunizationRecommendationRepository, ) : ViewModel() { private val _uiState = MutableStateFlow(HomeComposeUiState(isQuickAccessTileTutorialRequired = appFeatureWithQuickAccessTilesRepository.isQuickAccessTileTutorialRequired)) @@ -37,7 +43,7 @@ class HomeViewModel @Inject constructor( private var isBiometricAuthenticationRequired: Boolean = true fun loadQuickAccessTiles(loginStatus: LoginStatus) = viewModelScope.launch { - var quickAccessTileItems = mutableListOf() + val quickAccessTileItems = mutableListOf() val data = appFeatureWithQuickAccessTilesRepository.getAppFeaturesWithQuickAccessTiles() val appFeatures = @@ -49,17 +55,41 @@ class HomeViewModel @Inject constructor( quickAccessTileItems.addAll(appFeatures) - data.filter { it.appFeatureDto.showAsQuickAccess }.forEach { - val quickLink = it.quickAccessTiles.filter { it.showAsQuickAccess } - .map { tile -> QuickAccessTileItem.QuickLinkTileItem.from(tile) } - quickAccessTileItems.addAll(quickLink) + if (loginStatus == LoginStatus.ACTIVE) { + data.filter { it.appFeatureDto.showAsQuickAccess }.forEach { + val quickLink = it.quickAccessTiles.filter { tile -> tile.showAsQuickAccess } + .map { tile -> QuickAccessTileItem.QuickLinkTileItem.from(tile) } + quickAccessTileItems.addAll(quickLink) + } + + quickAccessTileItems.removeIf { tile -> + (tile.name == AppFeatureName.IMMUNIZATION_SCHEDULES.value) + } + runBlocking(Dispatchers.IO) { + val hasRecommendations = recommendationRepository.hasRecommendations() + + if (!hasRecommendations) { + quickAccessTileItems.removeIf { + (it.name == AppFeatureName.RECOMMENDED_IMMUNIZATIONS.value) + } + } else { + quickAccessTileItems.find { it.name == AppFeatureName.RECOMMENDED_IMMUNIZATIONS.value } + ?.let { + val index = quickAccessTileItems.indexOf(it) + if (index != 1) { + quickAccessTileItems.removeAt(index) + quickAccessTileItems.add(1, it) + } + } + } + } } - if (loginStatus != LoginStatus.ACTIVE) { - quickAccessTileItems = - quickAccessTileItems.filterIsInstance().toMutableList() + quickAccessTileItems.removeIf { + (it.name == AppFeatureName.RECOMMENDED_IMMUNIZATIONS.value && (loginStatus != LoginStatus.ACTIVE)) } - _uiState.update { it.copy(quickAccessTileItems = quickAccessTileItems) } + + _uiState.update { it.copy(isLoading = false, quickAccessTileItems = quickAccessTileItems) } } fun launchCheck() = viewModelScope.launch { @@ -167,6 +197,8 @@ class HomeViewModel @Inject constructor( it.copy(isQuickAccessTileTutorialRequired = false) } } + + fun executeOneTimeDataFetch() = workerInvoker.executeOneTimeDataFetch() } data class HomeComposeUiState( @@ -241,6 +273,10 @@ sealed class QuickAccessTileItem( AppFeatureName.SERVICES -> { Pair(R.drawable.ic_organ_donor, R.id.services) } + + AppFeatureName.RECOMMENDED_IMMUNIZATIONS -> { + Pair(R.drawable.ic_recommendation_immunization, R.id.recommendations) + } } return FeatureTileItem( id = appFeatureDto.id, diff --git a/app/src/main/res/drawable/ic_recommendation_immunization.xml b/app/src/main/res/drawable/ic_recommendation_immunization.xml new file mode 100644 index 000000000..902e91898 --- /dev/null +++ b/app/src/main/res/drawable/ic_recommendation_immunization.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt b/common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt index 4322d5700..948fe1e24 100644 --- a/common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt +++ b/common/src/main/java/ca/bc/gov/common/model/AppFeatureName.kt @@ -3,6 +3,7 @@ package ca.bc.gov.common.model enum class AppFeatureName(val value: String) { HEALTH_RECORDS("Health records"), IMMUNIZATION_SCHEDULES("Immunization schedules"), + RECOMMENDED_IMMUNIZATIONS("Recommended immunizations"), HEALTH_RESOURCES("Health resources"), PROOF_OF_VACCINE("Proof of vaccination"), SERVICES("Services"); diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/ImmunizationRecommendationLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/ImmunizationRecommendationLocalDataSource.kt index 7b05a1a75..13e5d52c9 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/ImmunizationRecommendationLocalDataSource.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/ImmunizationRecommendationLocalDataSource.kt @@ -17,6 +17,8 @@ class ImmunizationRecommendationLocalDataSource @Inject constructor( list.map { it.toDto() } } + suspend fun hasRecommendations() = dao.hasRecommendations() + suspend fun insert(immunizationRecommendation: ImmunizationRecommendationsDto): Long = dao.insert(immunizationRecommendation.toEntity()) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/ImmunizationRecommendationDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/ImmunizationRecommendationDao.kt index ea6e58677..fa84d2f1e 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/ImmunizationRecommendationDao.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/ImmunizationRecommendationDao.kt @@ -15,6 +15,9 @@ interface ImmunizationRecommendationDao : BaseDao> + @Query("SELECT EXISTS(SELECT * ,(SELECT id FROM patient WHERE authentication_status = 'AUTHENTICATED') AS patientId FROM immunization_recommendation)") + fun hasRecommendations(): Boolean + @Query("DELETE FROM immunization_recommendation WHERE recommendation_set_id = :id") suspend fun delete(id: Long): Int } diff --git a/repository/src/main/java/ca/bc/gov/repository/immunization/ImmunizationRecommendationRepository.kt b/repository/src/main/java/ca/bc/gov/repository/immunization/ImmunizationRecommendationRepository.kt index ff04c4393..5c1523363 100644 --- a/repository/src/main/java/ca/bc/gov/repository/immunization/ImmunizationRecommendationRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/immunization/ImmunizationRecommendationRepository.kt @@ -12,6 +12,8 @@ class ImmunizationRecommendationRepository @Inject constructor( fun getAllRecommendations(): Flow> = localDataSource.getAllRecommendations() + suspend fun hasRecommendations() = localDataSource.hasRecommendations() + suspend fun insert(immunizationRecommendation: ImmunizationRecommendationsDto): Long = localDataSource.insert(immunizationRecommendation) From 0af34e516c79be06fc9e9bf4fd932dd23a74455b Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Wed, 9 Aug 2023 11:43:48 -0700 Subject: [PATCH 24/38] HAPP-1248 : Recommendation Immunization screen UI - updated recommendation screen UI --- .../recommendations/RecommendationScreen.kt | 293 ++++++++++++++++++ .../RecommendationsFragment.kt | 87 +++--- app/src/main/res/values/strings.xml | 2 + 3 files changed, 331 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt new file mode 100644 index 000000000..61e21312a --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt @@ -0,0 +1,293 @@ +package ca.bc.gov.bchealth.ui.recommendations + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Divider +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintSet +import androidx.constraintlayout.compose.Dimension +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue +import ca.bc.gov.bchealth.compose.theme.descriptionGrey +import ca.bc.gov.bchealth.compose.theme.grey +import ca.bc.gov.bchealth.compose.theme.statusBlue + +@Composable +fun RecommendationScreen(modifier: Modifier = Modifier) { + + RecommendationScreenContent(modifier) +} + +@Composable +private fun RecommendationScreenContent( + modifier: Modifier = Modifier +) { + LazyColumn( + modifier = modifier.fillMaxSize(), + contentPadding = PaddingValues(32.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + + item { + Text( + modifier = Modifier.fillMaxWidth(), + text = stringResource(id = R.string.recommendations_para1), + style = MaterialTheme.typography.body2, + color = grey + ) + } + item { + Text( + modifier = Modifier.fillMaxWidth(), + text = stringResource(id = R.string.recommendations_para2), + style = MaterialTheme.typography.body2, + color = grey + ) + } + + for (i in 0 until 5) { + item { + RecommendationItem() + } + } + } +} + +@Composable +private fun RecommendationItem(modifier: Modifier = Modifier) { + var expanded by remember { mutableStateOf(true) } + Card( + modifier = modifier.fillMaxWidth(), + elevation = 15.dp, + backgroundColor = MaterialTheme.colors.background + ) { + BoxWithConstraints( + modifier = Modifier.fillMaxWidth() + ) { + ConstraintLayout( + recommendationItemConstraints(), + modifier = Modifier.fillMaxWidth() + ) { + + Image( + modifier = Modifier + .layoutId(ITEM_ICON_ID) + .size(48.dp) + .clip(RoundedCornerShape(8.dp)) + .background( + bannerBackgroundBlue + ), + painter = painterResource(id = R.drawable.ic_banner_icon), + contentScale = ContentScale.None, + contentDescription = null + ) + + Text( + modifier = Modifier.layoutId(ITEM_PATIENT_NAME_ID), + text = stringResource(id = R.string.home), + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold + ) + + Text( + modifier = Modifier.layoutId(ITEM_RECORD_COUNT_ID), + text = "3", + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary + ) + + val toggleIcon = if (expanded) { + R.drawable.ic_content_short + } else { + R.drawable.ic_content_full + } + + IconButton( + onClick = { expanded = !expanded }, + modifier = Modifier + .layoutId(ITEM_ARROW_ID) + ) { + Image( + painter = painterResource(id = toggleIcon), + contentDescription = null + ) + } + + AnimatedVisibility(modifier = Modifier.layoutId(ITEM_DETAIL_ID), visible = expanded) { + + LazyColumn( + modifier = Modifier.heightIn(max = (5 * 80).dp), + userScrollEnabled = false + ) { + item { + Divider() + } + items(5) { + RecommendationDetailItem() + } + } + } + } + } + } +} + +private const val ITEM_ICON_ID = "item_icon_id" +private const val ITEM_ARROW_ID = "item_arrow_id" +private const val ITEM_DETAIL_ID = "item_detail_id" +private const val ITEM_PATIENT_NAME_ID = "item_patient_name_id" +private const val ITEM_RECORD_COUNT_ID = "item_record_count_id" +private fun recommendationItemConstraints() = ConstraintSet { + val itemIconId = createRefFor(ITEM_ICON_ID) + val itemArrow = createRefFor(ITEM_ARROW_ID) + val itemDetailId = createRefFor(ITEM_DETAIL_ID) + val itemPatientNameId = createRefFor(ITEM_PATIENT_NAME_ID) + val itemRecordCountId = createRefFor(ITEM_RECORD_COUNT_ID) + + constrain(itemIconId) { + start.linkTo(parent.start, 16.dp) + top.linkTo(parent.top, 16.dp) + } + + constrain(itemPatientNameId) { + start.linkTo(itemIconId.end, 16.dp) + top.linkTo(itemIconId.top) + bottom.linkTo(itemIconId.bottom) + end.linkTo(itemRecordCountId.start) + width = Dimension.fillToConstraints + } + + constrain(itemRecordCountId) { + top.linkTo(itemArrow.top) + end.linkTo(itemArrow.start) + bottom.linkTo(itemArrow.bottom) + } + + constrain(itemArrow) { + top.linkTo(parent.top, 16.dp) + end.linkTo(parent.end, 16.dp) + } + + constrain(itemDetailId) { + top.linkTo(itemIconId.bottom, 16.dp) + } +} + +@Composable +private fun RecommendationDetailItem(modifier: Modifier = Modifier) { + BoxWithConstraints(modifier = Modifier.background(MaterialTheme.colors.background)) { + ConstraintLayout( + modifier = Modifier.fillMaxWidth(), + constraintSet = recommendationDetailItemConstraint() + ) { + Text( + modifier = Modifier.layoutId(ITEM_VACCINE_NAME_ID), + text = "Yello Fever", + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + color = statusBlue + ) + Icon( + modifier = Modifier + .size(16.dp) + .layoutId(ITEM_ICON_ID), + painter = painterResource(id = R.drawable.ic_recommendation), + tint = MaterialTheme.colors.primary, + contentDescription = null + ) + Text( + modifier = Modifier.layoutId(ITEM_STATUS_ID), + text = "Yello Fever", + style = MaterialTheme.typography.body2, + color = descriptionGrey + ) + Text( + modifier = Modifier.layoutId(ITEM_DATE_ID), + text = "Yello Fever", + style = MaterialTheme.typography.body2, + color = descriptionGrey + ) + } + } +} + +private const val ITEM_VACCINE_NAME_ID = "item_vaccine_name_id" +private const val ITEM_STATUS_ID = "item_status_id" +private const val ITEM_DATE_ID = "item_date_id" +private fun recommendationDetailItemConstraint() = ConstraintSet { + val itemVaccineNameId = createRefFor(ITEM_VACCINE_NAME_ID) + val itemStatusId = createRefFor(ITEM_STATUS_ID) + val itemDateId = createRefFor(ITEM_DATE_ID) + val itemIconId = createRefFor(ITEM_ICON_ID) + + constrain(itemVaccineNameId) { + start.linkTo(parent.start, 16.dp) + top.linkTo(parent.top, 16.dp) + } + + constrain(itemIconId) { + start.linkTo(itemVaccineNameId.end, 16.dp) + top.linkTo(itemVaccineNameId.top) + bottom.linkTo(itemVaccineNameId.bottom) + } + + constrain(itemStatusId) { + start.linkTo(itemVaccineNameId.start) + top.linkTo(itemVaccineNameId.bottom) + } + + constrain(itemDateId) { + start.linkTo(itemStatusId.start) + top.linkTo(itemStatusId.bottom) + } +} + +@Composable +@Preview(showBackground = false) +private fun RecommendationItemPreview() { + + HealthGatewayTheme { + RecommendationItem() + } +} + +@BasePreview +@Composable +private fun RecommendationScreenPreview() { + + HealthGatewayTheme { + RecommendationScreen() + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt index b750572be..80edcbba5 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt @@ -1,71 +1,56 @@ package ca.bc.gov.bchealth.ui.recommendations -import android.os.Bundle -import android.view.View +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController -import androidx.navigation.ui.AppBarConfiguration import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.component.HGTopAppBar +import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import ca.bc.gov.bchealth.databinding.FragmentRecommendationsBinding import ca.bc.gov.bchealth.ui.BaseFragment -import ca.bc.gov.bchealth.utils.makeLinks -import ca.bc.gov.bchealth.utils.redirect import ca.bc.gov.bchealth.utils.viewBindings import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch @AndroidEntryPoint -class RecommendationsFragment : BaseFragment(R.layout.fragment_recommendations) { +class RecommendationsFragment : BaseFragment(null) { private val binding by viewBindings(FragmentRecommendationsBinding::bind) private val viewModel: RecommendationsViewModel by viewModels() - override fun setToolBar(appBarConfiguration: AppBarConfiguration) { - with(binding.layoutToolbar.appbar) { - stateListAnimator = null - elevation = 0f - } - with(binding.layoutToolbar.topAppBar) { - setNavigationIcon(R.drawable.ic_toolbar_back) - setNavigationOnClickListener { - findNavController().popBackStack() - } - title = getString(R.string.recommendations_home_title) - inflateMenu(R.menu.settings_menu) - setOnMenuItemClickListener { menu -> - when (menu.itemId) { - R.id.menu_settings -> { - findNavController().navigate(R.id.settingsFragment) - } - } - return@setOnMenuItemClickListener true - } - } - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - with(binding) { - super.onViewCreated(view, savedInstanceState) - tvDescription.makeLinks( - getString(R.string.recommendations_description_clickable) to - View.OnClickListener { - it.context.redirect(getString(R.string.url_immunize_bc)) - } + @Composable + override fun GetComposableLayout() { + val menuItems = mutableListOf( + TopAppBarActionItem.IconActionItem.AlwaysShown( + title = getString(R.string.settings), + onClick = { findNavController().navigate(R.id.settingsFragment) }, + icon = R.drawable.ic_menu_settings, + contentDescription = getString(R.string.settings), ) - val adapter = RecommendationAdapter() - rvRecommendations.adapter = adapter - - viewLifecycleOwner.lifecycleScope.launch { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - - viewModel.recommendationList.collect { list -> - adapter.submitList(list) - } + ) + HealthGatewayTheme { + Scaffold( + topBar = { + HGTopAppBar( + title = stringResource(id = R.string.recommendations_home_title), + actionItems = menuItems + ) + }, + content = { + RecommendationScreen( + Modifier + .statusBarsPadding() + .navigationBarsPadding() + .padding(it), + ) } - } + ) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 582ac5d46..8a185b45b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,6 +48,8 @@ Recommended immunizations For more information on vaccines recommendation and eligibility, please visit immunizeBC or speak to your health care provider. immunizeBC + Recommended immunization are suggestion for your health journey. This page will containt all of future immunization for you and your dependents. To add dependents to application, click on Dependent in the menu bar at the bottom. + For more information on vaccines recommendation and eligibility, please visit immunizeBC or speak to your health care provider. Dependent From caeaab2a8eec14fff4803d70050f012f5fb1138f Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Fri, 23 Jun 2023 09:45:14 -0700 Subject: [PATCH 25/38] HAPP-1534 - fixed bottom navigation bar icon, color & typography - moved colors to Colors.kt --- app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt index e9f28bb2b..47d94a1d9 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt @@ -20,5 +20,3 @@ val greyBg = Color(0xFFF2F2F2) val green = Color(0xFF2E8540) val red = Color(0xFFD8292F) - -val bannerInfoBg = Color(0xFFD9EAF7) From 9ad6595e49acebfc3e55e2be1f5c415119f7844d Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Wed, 26 Jul 2023 14:51:46 -0700 Subject: [PATCH 26/38] HAPP-1538: Remove Tile - upgraded gradle version to 8.1 - upgraded java version 17 - removed popBackstack from BaseFragment and also removed its reference usage. - removed composeEmail from BaseFragment to FragmentExtensions.kt - updated HomeScreen floe handling to lifecycle aware. - updated UI to handle remove action click - updated authentication status observation --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 3 +- .../java/ca/bc/gov/bchealth/compose/Styles.kt | 1 + .../component/QuickAccessTileItemUI.kt | 40 +++++++- .../bchealth/compose/component/TopAppBar.kt | 59 ++++++++++++ .../bc/gov/bchealth/compose/theme/Colors.kt | 1 + .../ca/bc/gov/bchealth/ui/BaseFragment.kt | 24 +---- .../bchealth/ui/comment/CommentsFragment.kt | 4 +- .../profile/DependentProfileFragment.kt | 2 +- .../registration/AddDependentFragment.kt | 1 + .../bchealth/ui/feeback/FeedbackFragment.kt | 3 +- .../ClinicalDocumentDetailFragment.kt | 2 +- .../healthvisits/HealthVisitDetailFragment.kt | 4 +- .../HospitalVisitDetailFragment.kt | 3 +- .../bc/gov/bchealth/ui/home/HomeFragment.kt | 45 ++++++--- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 91 ++++++++++-------- ...emoveQuickAccessTileBottomSheetFragment.kt | 33 +++++++ .../RemoveQuickAccessTileBottomSheetScreen.kt | 96 +++++++++++++++++++ .../ImmunizationSchedulesFragment.kt | 3 +- .../manage/QuickAccessManagementFragment.kt | 5 +- .../bchealth/ui/login/BcscAuthViewModel.kt | 18 +++- .../ui/login/error/BcscAuthErrorFragment.kt | 4 +- .../ui/notification/NotificationFragment.kt | 2 +- .../bchealth/ui/profile/ProfileFragment.kt | 5 +- .../ui/resources/ResourcesFragment.kt | 3 +- .../gov/bchealth/utils/FragmentExtensions.kt | 4 + app/src/main/res/navigation/home.xml | 15 +++ app/src/main/res/values/strings.xml | 5 + .../appauth/extension/AppAuthExtensions.kt | 1 + common/build.gradle | 1 + common/src/main/AndroidManifest.xml | 3 +- .../common/model/UserAuthenticationStatus.kt | 7 ++ data/build.gradle | 1 + data/src/main/AndroidManifest.xml | 3 +- gradle.properties | 5 +- gradle/wrapper/gradle-wrapper.properties | 2 +- repository/build.gradle | 1 + repository/src/main/AndroidManifest.xml | 2 +- .../ca/bc/gov/repository/bcsc/BcscAuthRepo.kt | 19 ++++ scripts/versions.gradle | 2 +- 40 files changed, 415 insertions(+), 110 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt create mode 100644 common/src/main/java/ca/bc/gov/common/model/UserAuthenticationStatus.kt diff --git a/app/build.gradle b/app/build.gradle index 70b02751e..9000bb85a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -96,6 +96,7 @@ android { task getApkVersion { println defaultConfig.versionName + "-" + defaultConfig.versionCode } + namespace 'ca.bc.gov.bchealth' } dependencies { @@ -125,6 +126,7 @@ dependencies { //JetPack LiveData ViewModel implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$versions.lifecycle" + implementation "androidx.lifecycle:lifecycle-runtime-compose:$versions.lifecycle" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.lifecycle" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$versions.lifecycle" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$versions.lifecycle" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dfea5071b..757b5d433 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt index 3c1d347df..4ab501a8a 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/Styles.kt @@ -116,6 +116,7 @@ fun TextStyle.bold() = this.copy(fontWeight = FontWeight.Bold) fun TextStyle.italic() = this.copy(fontStyle = FontStyle.Italic) +@Deprecated("Replace with HealthGatewayTheme", replaceWith = ReplaceWith("HealthGatewayTheme"), DeprecationLevel.WARNING) @Composable fun MyHealthTheme(content: @Composable () -> Unit) = MaterialTheme( colors = MaterialTheme.colors.copy( diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt index ba4a684bb..8ae70713f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/QuickAccessTileItemUI.kt @@ -8,8 +8,12 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.Card +import androidx.compose.material.Icon +import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -21,6 +25,7 @@ import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet import androidx.constraintlayout.compose.Dimension +import androidx.constraintlayout.compose.Visibility import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme @@ -28,13 +33,16 @@ import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme private const val tileIconId = "tileIconId" private const val tileTitleId = "tileTitleId" private const val tileArrowId = "tileArrowId" +private const val moreActionId = "moreActionId" @Composable fun QuickAccessTileItemUI( onClick: () -> Unit, + onMoreActionClick: () -> Unit, modifier: Modifier = Modifier, icon: Painter, - title: String + title: String, + hasMoreOptions: Boolean = false ) { Card( modifier = modifier @@ -49,7 +57,7 @@ fun QuickAccessTileItemUI( .fillMaxSize() ) { ConstraintLayout( - tileConstraints(), + tileConstraints(hasMoreOptions), modifier = modifier .fillMaxSize() ) { @@ -78,17 +86,29 @@ fun QuickAccessTileItemUI( painter = painterResource(id = R.drawable.ic_right_arrow), contentDescription = null ) + + IconButton( + onClick = { + if (hasMoreOptions) { + onMoreActionClick() + } + }, + modifier = Modifier.layoutId(moreActionId) + ) { + Icon(imageVector = Icons.Filled.MoreVert, contentDescription = null) + } } } } } -private fun tileConstraints(): ConstraintSet { +private fun tileConstraints(showMoreAction: Boolean = false): ConstraintSet { return ConstraintSet { val tileIcon = createRefFor(tileIconId) val tileTitle = createRefFor(tileTitleId) val tileArrow = createRefFor(tileArrowId) + val moreAction = createRefFor(moreActionId) constrain(tileIcon) { start.linkTo(parent.start, 16.dp) @@ -106,6 +126,16 @@ private fun tileConstraints(): ConstraintSet { bottom.linkTo(tileTitle.bottom) end.linkTo(parent.end, 16.dp) } + + constrain(moreAction) { + top.linkTo(parent.top) + end.linkTo(parent.end) + visibility = if (showMoreAction) { + Visibility.Visible + } else { + Visibility.Gone + } + } } } @@ -117,7 +147,9 @@ private fun QuickAccessTileItemUIPreview() { QuickAccessTileItemUI( onClick = { /*TODO*/ }, icon = painterResource(id = R.drawable.ic_tile_healt_resources), - title = stringResource(id = R.string.health_resources) + title = stringResource(id = R.string.health_resources), + hasMoreOptions = true, + onMoreActionClick = {} ) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt index fea1aead3..aafb7a52b 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt @@ -1,5 +1,9 @@ package ca.bc.gov.bchealth.compose.component +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material.Icon +import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TopAppBar @@ -8,7 +12,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import ca.bc.gov.bchealth.R @@ -16,6 +22,7 @@ import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.component.menu.ActionMenu import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.primaryBlue @Composable fun HGTopAppBar( @@ -49,6 +56,50 @@ fun HGTopAppBar( ) } +@Composable +fun HGCenterAlignedTopAppBar( + modifier: Modifier = Modifier, + onNavigationAction: () -> Unit, + title: String, + actionItems: List = emptyList() +) { + var menuOpen by remember { + mutableStateOf(false) + } + TopAppBar( + modifier = modifier, + navigationIcon = { + IconButton(onClick = { onNavigationAction() }) { + Icon( + painter = painterResource(id = R.drawable.ic_toolbar_back), + contentDescription = stringResource(id = R.string.back), + tint = primaryBlue + ) + } + }, + title = { + Text( + modifier = Modifier + .fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally), + text = title, + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold, + ) + }, + actions = { + ActionMenu( + items = actionItems, + isOpen = menuOpen, + onToggleOverflow = { menuOpen = !menuOpen }, + maxVisibleItems = 3 + ) + }, + backgroundColor = MaterialTheme.colors.surface, + contentColor = MaterialTheme.colors.primary + ) +} + @Composable @BasePreview private fun HGTopAppBarPreview() { @@ -56,3 +107,11 @@ private fun HGTopAppBarPreview() { HGTopAppBar(title = stringResource(id = R.string.home)) } } + +@Composable +@BasePreview +private fun HGCenterAlignedTopAppBarPreview() { + HealthGatewayTheme { + HGCenterAlignedTopAppBar(onNavigationAction = {}, title = stringResource(id = R.string.home)) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt index 47d94a1d9..7203c2ba7 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt @@ -17,6 +17,7 @@ val bannerBackgroundBlue = Color(0xFFD9EAF7) val descriptionGrey = Color(0xFF6D757D) val grey = Color(0xFF606060) val greyBg = Color(0xFFF2F2F2) +val dividerGrey = Color(0x14212121) val green = Color(0xFF2E8540) val red = Color(0xFFD8292F) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt index 28d7f88b3..6ffae4c1a 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt @@ -15,8 +15,6 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.ui.custom.MyHealthToolbar import ca.bc.gov.bchealth.utils.AlertDialogHelper -import ca.bc.gov.bchealth.utils.HEALTH_GATEWAY_EMAIL_ADDRESS -import ca.bc.gov.bchealth.utils.composeEmail import ca.bc.gov.bchealth.utils.launchOnStart import ca.bc.gov.bchealth.utils.showNoInternetConnectionMessage import ca.bc.gov.bchealth.utils.showServiceDownMessage @@ -77,16 +75,6 @@ abstract class BaseFragment(@LayoutRes private val contentLayoutId: Int?) : Frag } } - @Deprecated( - "Should be added as a extension to the fragment" + - "as in compose we need to get rid of all the fragment", - replaceWith = ReplaceWith(""), - level = DeprecationLevel.WARNING - ) - fun composeEmail(address: String = HEALTH_GATEWAY_EMAIL_ADDRESS, subject: String = "") { - requireActivity().composeEmail(address, subject) - } - fun showGenericError() { AlertDialogHelper.showAlertDialog( context = requireContext(), @@ -96,23 +84,13 @@ abstract class BaseFragment(@LayoutRes private val contentLayoutId: Int?) : Frag ) } - @Deprecated( - "remove usage of popup navigation and replace it with the a findNavController().popBackStack()" + - "as in compose we need to get rid of all the fragment", - replaceWith = ReplaceWith("findNavController().popBackStack()"), - level = DeprecationLevel.WARNING - ) - fun popNavigation() { - findNavController().popBackStack() - } - fun setupComposeToolbar(composeView: ComposeView, title: String? = null) { composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { MyHealthTheme { MyHealthToolbar(title) { - popNavigation() + findNavController().popBackStack() } } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsFragment.kt index 5f9d76eba..047054b69 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/comment/CommentsFragment.kt @@ -34,7 +34,7 @@ class CommentsFragment : BaseFragment(null) { MyHealthScaffold( title = stringResource(id = R.string.comments), isLoading = uiState.onLoading, - navigationAction = ::popNavigation, + navigationAction = { findNavController().popBackStack() }, ) { CommentsScreen( uiState, @@ -103,7 +103,7 @@ class CommentsFragment : BaseFragment(null) { showError() viewModel.resetUiState() } - state.commentsList != null && state.commentsList.isEmpty() -> popNavigation() + state.commentsList != null && state.commentsList.isEmpty() -> findNavController().popBackStack() } } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileFragment.kt index ba34a9356..1d318501c 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/profile/DependentProfileFragment.kt @@ -19,7 +19,7 @@ class DependentProfileFragment : BaseDependentFragment(null) { @Composable override fun GetComposableLayout() { - DependentProfileUI(viewModel, ::popNavigation) { dto -> + DependentProfileUI(viewModel, { findNavController().popBackStack() }) { dto -> dto?.let { confirmDeletion(dto.patientId, dto.firstname) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/registration/AddDependentFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/registration/AddDependentFragment.kt index 1227ffc67..667b46f31 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/registration/AddDependentFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/dependents/registration/AddDependentFragment.kt @@ -12,6 +12,7 @@ import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.utils.AlertDialogHelper import ca.bc.gov.bchealth.utils.DatePickerHelper import ca.bc.gov.bchealth.utils.PhnHelper +import ca.bc.gov.bchealth.utils.composeEmail import ca.bc.gov.bchealth.utils.hideKeyboard import ca.bc.gov.bchealth.utils.launchOnStart import ca.bc.gov.bchealth.utils.showNoInternetConnectionMessage diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackFragment.kt index 5efc4b32e..0e01323f8 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/feeback/FeedbackFragment.kt @@ -2,6 +2,7 @@ package ca.bc.gov.bchealth.ui.feeback import androidx.compose.runtime.Composable import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.utils.showErrorSnackbar @@ -18,7 +19,7 @@ class FeedbackFragment : BaseFragment(null) { override fun GetComposableLayout() { FeedbackUI( uiStateFlow = feedbackViewModel.uiState, - navigationAction = ::popNavigation, + navigationAction = { findNavController().popBackStack() }, sendAction = ::onClickSend, onMessageSent = ::onMessageSent, onError = ::displayError, diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/clinicaldocument/ClinicalDocumentDetailFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/clinicaldocument/ClinicalDocumentDetailFragment.kt index ddb783c9b..869fb1fc5 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/clinicaldocument/ClinicalDocumentDetailFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/clinicaldocument/ClinicalDocumentDetailFragment.kt @@ -29,7 +29,7 @@ class ClinicalDocumentDetailFragment : BaseFragment(null) { @Composable override fun GetComposableLayout() { - ClinicalDocumentDetailUI(viewModel, ::popNavigation) + ClinicalDocumentDetailUI(viewModel, { findNavController().popBackStack() }) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/healthvisits/HealthVisitDetailFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/healthvisits/HealthVisitDetailFragment.kt index 9bb109fa3..5af2b5d72 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/healthvisits/HealthVisitDetailFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/healthvisits/HealthVisitDetailFragment.kt @@ -41,7 +41,7 @@ class HealthVisitDetailFragment : BaseFragment(null) { MyHealthScaffold( title = uiState.title, isLoading = uiState.onLoading, - navigationAction = ::popNavigation + navigationAction = { findNavController().popBackStack() } ) { HealthVisitDetailScreen( uiState = uiState, @@ -56,7 +56,7 @@ class HealthVisitDetailFragment : BaseFragment(null) { showGenericError() viewModel.resetUiState() } - if (commentState?.isBcscSessionActive == false) popNavigation() + if (commentState?.isBcscSessionActive == false) findNavController().popBackStack() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/hospitalvisits/HospitalVisitDetailFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/hospitalvisits/HospitalVisitDetailFragment.kt index 817035e38..85a0731f5 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/hospitalvisits/HospitalVisitDetailFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/hospitalvisits/HospitalVisitDetailFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.View import androidx.compose.runtime.Composable import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import ca.bc.gov.bchealth.ui.BaseFragment import dagger.hilt.android.AndroidEntryPoint @@ -15,7 +16,7 @@ class HospitalVisitDetailFragment : BaseFragment(null) { @Composable override fun GetComposableLayout() { - HospitalVisitDetailUI(viewModel, ::popNavigation) + HospitalVisitDetailUI(viewModel, { findNavController().popBackStack() }) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt index 443928e12..350b14469 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt @@ -5,6 +5,7 @@ import android.view.View import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -13,7 +14,10 @@ import androidx.compose.ui.res.stringResource import androidx.core.os.bundleOf import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.fragment.findNavController +import ca.bc.gov.bchealth.HomeDirections import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.component.HGTopAppBar import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem @@ -24,11 +28,12 @@ import ca.bc.gov.bchealth.ui.NavigationAction import ca.bc.gov.bchealth.ui.auth.BioMetricState import ca.bc.gov.bchealth.ui.auth.BiometricsAuthenticationFragment import ca.bc.gov.bchealth.ui.login.BcscAuthViewModel -import ca.bc.gov.bchealth.ui.login.LoginStatus import ca.bc.gov.bchealth.utils.observeCurrentBackStackForAction import ca.bc.gov.bchealth.viewmodel.SharedViewModel +import ca.bc.gov.common.model.UserAuthenticationStatus import dagger.hilt.android.AndroidEntryPoint +@OptIn(ExperimentalMaterialApi::class) @AndroidEntryPoint class HomeFragment : BaseSecureFragment(null) { @@ -58,7 +63,8 @@ class HomeFragment : BaseSecureFragment(null) { @Composable override fun GetComposableLayout() { - println("Home: ComposeCreated") + val userAuthState = authViewModel.userAuthenticationState.collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value + val authState = authViewModel.authStatus.collectAsState().value val menuItems = mutableListOf( TopAppBarActionItem.IconActionItem.AlwaysShown( @@ -69,19 +75,18 @@ class HomeFragment : BaseSecureFragment(null) { ) ) - authState.loginStatus?.let { - if (it == LoginStatus.ACTIVE) { - menuItems.add( - 0, - TopAppBarActionItem.IconActionItem.AlwaysShown( - title = getString(R.string.notifications), - onClick = { findNavController().navigate(R.id.notificationFragment) }, - icon = R.drawable.ic_notification, - contentDescription = getString(R.string.notifications), - ) + if (userAuthState == UserAuthenticationStatus.AUTHENTICATED) { + menuItems.add( + 0, + TopAppBarActionItem.IconActionItem.AlwaysShown( + title = getString(R.string.notifications), + onClick = { findNavController().navigate(R.id.notificationFragment) }, + icon = R.drawable.ic_notification, + contentDescription = getString(R.string.notifications), ) - } + ) } + HealthGatewayTheme { Scaffold( topBar = { @@ -103,7 +108,8 @@ class HomeFragment : BaseSecureFragment(null) { onManageClick = ::onManageClicked, onOnBoardingRequired = ::onOnBoardingRequired, onBiometricAuthenticationRequired = ::onBiometricAuthenticationRequired, - onQuickAccessTileClicked = ::onQuickAccessTileClicked + onQuickAccessTileClicked = ::onQuickAccessTileClicked, + onMoreActionClick = ::onMoreActionClicked ) } ) @@ -118,8 +124,10 @@ class HomeFragment : BaseSecureFragment(null) { findNavController().navigate(sharedViewModel.destinationId) } } + BcscAuthState.NO_ACTION, - BcscAuthState.NOT_NOW -> {} + BcscAuthState.NOT_NOW -> { + } } } @@ -154,6 +162,7 @@ class HomeFragment : BaseSecureFragment(null) { bundleOf("reOnBoardingRequired" to isReOnBoarding) ) } + private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { findNavController().navigate(quickAccessTileItem.destinationId) } @@ -161,4 +170,10 @@ class HomeFragment : BaseSecureFragment(null) { private fun onManageClicked() { findNavController().navigate(R.id.quickAccessManagementFragment) } + + private fun onMoreActionClicked(id: Long, name: String) { + val action = + HomeDirections.actionGlobalRemoveQuickAccessTileBottomSheetFragment(id, name) + findNavController().navigate(action) + } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 1f54416ff..41e3613c7 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -20,7 +20,6 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -35,6 +34,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.work.WorkInfo import androidx.work.WorkManager import ca.bc.gov.bchealth.R @@ -64,10 +64,11 @@ fun HomeScreen( onOnBoardingRequired: (isReOnBoarding: Boolean) -> Unit, onBiometricAuthenticationRequired: () -> Unit, onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit, + onMoreActionClick: (id: Long, name: String) -> Unit ) { - val uiState = viewModel.uiState.collectAsState().value + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value - val authState = authViewModel.authStatus.collectAsState().value + val authState = authViewModel.authStatus.collectAsStateWithLifecycle().value LaunchedEffect(key1 = Unit) { viewModel.launchCheck() @@ -107,45 +108,44 @@ fun HomeScreen( } } - if (authState.showLoading) { - HGProgressIndicator(modifier) - } else { - authState.loginStatus?.let { - - val context = LocalContext.current - val workRequest = WorkManager.getInstance(context) - .getWorkInfosForUniqueWorkLiveData(BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME) - .observeAsState() - if (workRequest.value?.firstOrNull()?.state == WorkInfo.State.RUNNING) { - } else { - LaunchedEffect(key1 = Unit) { - viewModel.loadQuickAccessTiles(it) - } + authState.loginStatus?.let { + LaunchedEffect(key1 = it) { + viewModel.loadQuickAccessTiles(it) + } + val context = LocalContext.current + val workRequest = WorkManager.getInstance(context) + .getWorkInfosForUniqueWorkLiveData(BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME) + .observeAsState() + if (workRequest.value?.firstOrNull()?.state == WorkInfo.State.RUNNING) { + } else { + LaunchedEffect(key1 = Unit) { + viewModel.loadQuickAccessTiles(it) } + } - if (sharedViewModel.shouldFetchBanner) { - LaunchedEffect(key1 = Unit) { - viewModel.fetchBanner() - } + if (sharedViewModel.shouldFetchBanner) { + LaunchedEffect(key1 = Unit) { + viewModel.fetchBanner() } - - HomeScreenContent( - modifier, - onLoginClick, - onQuickAccessTileClicked, - onManageClick, - onDismissClick = { - sharedViewModel.shouldFetchBanner = false - viewModel.dismissBanner() - }, - onDismissTutorialClicked = { viewModel.tutorialDismissed() }, - it, - viewModel.getLoginInfoCardData(it), - uiState.bannerItem, - uiState.quickAccessTileItems, - uiState.isQuickAccessTileTutorialRequired - ) } + + HomeScreenContent( + modifier, + onLoginClick, + onQuickAccessTileClicked, + onManageClick, + onDismissClick = { + sharedViewModel.shouldFetchBanner = false + viewModel.dismissBanner() + }, + onDismissTutorialClicked = { viewModel.tutorialDismissed() }, + onMoreActionClick = onMoreActionClick, + it, + viewModel.getLoginInfoCardData(it), + uiState.bannerItem, + uiState.quickAccessTileItems, + uiState.isQuickAccessTileTutorialRequired + ) } } @@ -157,6 +157,7 @@ private fun HomeScreenContent( onManageClick: () -> Unit, onDismissClick: () -> Unit, onDismissTutorialClicked: () -> Unit, + onMoreActionClick: (id: Long, name: String) -> Unit, loginStatus: LoginStatus, loginInfoCardData: LoginInfoCardData?, bannerItem: HomeBannerItem?, @@ -209,7 +210,13 @@ private fun HomeScreenContent( QuickAccessTileItemUI( onClick = { onQuickAccessTileClicked(it) }, icon = painterResource(id = it.icon), - title = it.name + title = it.name, + hasMoreOptions = it is QuickAccessTileItem.QuickLinkTileItem, + onMoreActionClick = { + if (it is QuickAccessTileItem.QuickLinkTileItem) { + onMoreActionClick(it.id, it.name) + } + } ) } } @@ -324,7 +331,8 @@ private fun HomeScreenNonAuthenticatedPreview() { loginInfoCardData = null, bannerItem = null, quickAccessTileItems = emptyList(), - isQuickAccessTileTutorialRequired = false + isQuickAccessTileTutorialRequired = false, + onMoreActionClick = { id, name -> } ) } } @@ -343,7 +351,8 @@ private fun HomeScreenAuthenticatedPreview() { loginInfoCardData = null, bannerItem = null, quickAccessTileItems = emptyList(), - isQuickAccessTileTutorialRequired = false + isQuickAccessTileTutorialRequired = false, + onMoreActionClick = { id, name -> } ) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt new file mode 100644 index 000000000..1821b83ea --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt @@ -0,0 +1,33 @@ +package ca.bc.gov.bchealth.ui.home + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.navigation.fragment.navArgs +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class RemoveQuickAccessTileBottomSheetFragment : BottomSheetDialogFragment() { + + val args: RemoveQuickAccessTileBottomSheetFragmentArgs by navArgs() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + HealthGatewayTheme { + RemoveQuickAccessTileBottomSheetScreen(name = args.name) + } + } + } + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt new file mode 100644 index 000000000..b1e5962ed --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt @@ -0,0 +1,96 @@ +package ca.bc.gov.bchealth.ui.home + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Divider +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.dp +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.component.HGButton +import ca.bc.gov.bchealth.compose.component.HGButtonDefaults +import ca.bc.gov.bchealth.compose.component.HGTextButton +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.descriptionGrey +import ca.bc.gov.bchealth.compose.theme.dividerGrey + +@Composable +fun RemoveQuickAccessTileBottomSheetScreen( + modifier: Modifier = Modifier, + name: String +) { + RemoveQuickAccessTileBottomSheetContent( + modifier = modifier, + name = name + ) +} + +@Composable +private fun RemoveQuickAccessTileBottomSheetContent( + modifier: Modifier = Modifier, + name: String +) { + + Column( + modifier = modifier + .padding(16.dp) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + + Text( + text = stringResource(id = R.string.remove_tile_confirmation_title, name), + style = MaterialTheme.typography.subtitle2, + fontWeight = FontWeight.Bold + ) + Text( + text = stringResource(id = R.string.remove_tile_confirmation_description), + style = MaterialTheme.typography.body2, + color = descriptionGrey + ) + Spacer(modifier = Modifier.height(16.dp)) + Divider( + modifier = Modifier.fillMaxWidth(), + color = dividerGrey, + thickness = 1.dp + ) + Spacer(modifier = Modifier.height(16.dp)) + HGButton( + modifier = Modifier.fillMaxWidth(), + onClick = { }, + text = stringResource(id = R.string.remove), + defaultHeight = HGButtonDefaults.SmallButtonHeight + ) + Spacer(modifier = Modifier.height(16.dp)) + HGTextButton(onClick = {}) { + Text( + text = stringResource(id = R.string.dismiss), + style = MaterialTheme.typography.body2, + fontWeight = FontWeight.Bold, + color = blue, + textDecoration = TextDecoration.Underline + ) + } + } +} + +@Composable +@BasePreview +private fun RemoveQuickAccessTileBottomSheetScreenPreview() { + HealthGatewayTheme { + RemoveQuickAccessTileBottomSheetContent(name = "Test") + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt index 3be913a13..f12af0566 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesFragment.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.ui.BaseFragment @@ -25,7 +26,7 @@ class ImmunizationSchedulesFragment : BaseFragment(null) { topBar = { MyHealthToolbar( title = stringResource(id = R.string.immnz_schedules_title), - navigationAction = ::popNavigation + navigationAction = { findNavController().popBackStack() } ) }, content = { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt index 4fe6741b7..c2b51a009 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementFragment.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.MyHealthTheme import ca.bc.gov.bchealth.ui.BaseFragment @@ -31,7 +32,7 @@ class QuickAccessManagementFragment : BaseFragment(null) { topBar = { MyHealthToolBar( title = stringResource(id = R.string.quick_access_management_title), - navigationIcon = { MyHealthBackButton(::popNavigation) }, + navigationIcon = { MyHealthBackButton({ findNavController().popBackStack() }) }, actions = { IconButton(onClick = viewModel::saveSelection) { Icon( @@ -48,7 +49,7 @@ class QuickAccessManagementFragment : BaseFragment(null) { QuickAccessManagementScreen( viewModel = viewModel, onClickItem = ::onClickItem, - onUpdateCompleted = ::popNavigation, + onUpdateCompleted = { findNavController().popBackStack() }, modifier = Modifier .statusBarsPadding() .navigationBarsPadding() diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt index 051dec33b..90dc61576 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt @@ -12,6 +12,7 @@ import ca.bc.gov.common.exceptions.NetworkConnectionException import ca.bc.gov.common.exceptions.ServiceDownException import ca.bc.gov.common.model.AuthParametersDto import ca.bc.gov.common.model.AuthenticationStatus +import ca.bc.gov.common.model.UserAuthenticationStatus import ca.bc.gov.common.model.patient.PatientDto import ca.bc.gov.common.utils.toUniquePatientName import ca.bc.gov.repository.CacheRepository @@ -25,8 +26,11 @@ import ca.bc.gov.repository.patient.PatientRepository import ca.bc.gov.repository.worker.MobileConfigRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -49,6 +53,13 @@ class BcscAuthViewModel @Inject constructor( private val _authStatus = MutableStateFlow(AuthStatus()) val authStatus: StateFlow = _authStatus.asStateFlow() + val userAuthenticationState = + bcscAuthRepo.userAuthenticationStatus.catch { excepton -> emit(UserAuthenticationStatus.UN_AUTHENTICATED) }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(500), + initialValue = UserAuthenticationStatus.UN_AUTHENTICATED + ) + /* * Throttle calls to BCSC login * */ @@ -73,12 +84,14 @@ class BcscAuthViewModel @Inject constructor( ) } } + is ServiceDownException -> _authStatus.update { it.copy( showLoading = true, canInitiateBcscLogin = false ) } + else -> { _authStatus.update { it.copy( @@ -265,7 +278,7 @@ class BcscAuthViewModel @Inject constructor( isError = false, userName = null, queItTokenUpdated = false, - loginStatus = LoginStatus.NOT_AUTHENTICATED, + loginStatus = null, ageLimitCheck = null, canInitiateBcscLogin = null, tosStatus = null, @@ -304,6 +317,7 @@ class BcscAuthViewModel @Inject constructor( ) } } + else -> { _authStatus.update { it.copy( @@ -353,6 +367,7 @@ class BcscAuthViewModel @Inject constructor( ) } } + else -> { _authStatus.update { it.copy( @@ -432,6 +447,7 @@ class BcscAuthViewModel @Inject constructor( ) } } + else -> { _authStatus.update { it.copy( diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/login/error/BcscAuthErrorFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/login/error/BcscAuthErrorFragment.kt index 9a6a5103b..faf2064bf 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/login/error/BcscAuthErrorFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/login/error/BcscAuthErrorFragment.kt @@ -1,8 +1,10 @@ package ca.bc.gov.bchealth.ui.login.error import androidx.compose.runtime.Composable +import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.ui.BaseFragment +import ca.bc.gov.bchealth.utils.composeEmail import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -10,7 +12,7 @@ class BcscAuthErrorFragment : BaseFragment(null) { @Composable override fun GetComposableLayout() { - BcscAuthErrorUI(::popNavigation, ::onClickEmail) + BcscAuthErrorUI({ findNavController().popBackStack() }, ::onClickEmail) } private fun onClickEmail() { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt index 15e263efd..98dc22548 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/notification/NotificationFragment.kt @@ -105,7 +105,7 @@ class NotificationFragment : BaseFragment(null) { private fun NotificationToolbar(uiState: NotificationViewModel.NotificationsUIState) { MyHealthToolBar( title = if (uiState.sessionExpired) "" else stringResource(id = R.string.notifications), - navigationIcon = { MyHealthBackButton(::popNavigation) }, + navigationIcon = { MyHealthBackButton({ findNavController().popBackStack() }) }, actions = { IconButton(onClick = { if (isDeleteIconEnabled(uiState)) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileFragment.kt index 0e282682e..9839d42cf 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/profile/ProfileFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.View import androidx.compose.runtime.Composable import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.utils.URL_ADDRESS_CHANGE import ca.bc.gov.bchealth.utils.URL_COMMUNICATION_PREFS @@ -19,7 +20,7 @@ class ProfileFragment : BaseFragment(null) { override fun GetComposableLayout() { ProfileUI( viewModel = viewModel, - navigationAction = ::popNavigation, + navigationAction = { findNavController().popBackStack() }, onClickAddress = ::onClickAddressChange, onClickPrefs = ::onClickCommunicationPrefs, ) @@ -28,7 +29,7 @@ class ProfileFragment : BaseFragment(null) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.uiState.collectOnStart { - if (it.error != null) popNavigation() + if (it.error != null) findNavController().popBackStack() } viewModel.load() } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesFragment.kt index 5b9117a4a..8a6188a0f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/resources/ResourcesFragment.kt @@ -2,6 +2,7 @@ package ca.bc.gov.bchealth.ui.resources import androidx.compose.runtime.Composable import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.utils.redirect import ca.bc.gov.bchealth.viewmodel.AnalyticsFeatureViewModel @@ -18,7 +19,7 @@ class ResourcesFragment : BaseFragment(null) { override fun GetComposableLayout() { ResourcesUI( uiList = resourcesViewModel.getResourcesList(), - navigationAction = ::popNavigation, + navigationAction = { findNavController().popBackStack() }, onClickResource = ::onClickResource ) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/utils/FragmentExtensions.kt b/app/src/main/java/ca/bc/gov/bchealth/utils/FragmentExtensions.kt index 0a0a36dab..21011c25c 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/utils/FragmentExtensions.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/utils/FragmentExtensions.kt @@ -66,3 +66,7 @@ inline fun NavController.removeActionFromPreviousBackStackEntry(key: inline fun NavController.setActionToPreviousBackStackEntry(key: String, value: T) { previousBackStackEntry?.savedStateHandle?.set(key, value) } + +fun Fragment.composeEmail(address: String = HEALTH_GATEWAY_EMAIL_ADDRESS, subject: String = "") { + requireActivity().composeEmail(address, subject) +} diff --git a/app/src/main/res/navigation/home.xml b/app/src/main/res/navigation/home.xml index 2cb7f1cb0..3c6340d4f 100644 --- a/app/src/main/res/navigation/home.xml +++ b/app/src/main/res/navigation/home.xml @@ -60,4 +60,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8a185b45b..00e3a6a40 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -624,4 +624,9 @@ More Options + + + Remove %s + You can easily restore it by managing the settings. + Remove \ No newline at end of file diff --git a/appauth/src/main/java/net/openid/appauth/extension/AppAuthExtensions.kt b/appauth/src/main/java/net/openid/appauth/extension/AppAuthExtensions.kt index 2e4302db5..ed7143f4a 100644 --- a/appauth/src/main/java/net/openid/appauth/extension/AppAuthExtensions.kt +++ b/appauth/src/main/java/net/openid/appauth/extension/AppAuthExtensions.kt @@ -72,6 +72,7 @@ suspend fun getBCSCAuthData(applicationContext: Context, authState: AuthState): val accessToken = awaitPerformActionWithFreshTokens(applicationContext, authState) val json = decodeAccessToken(accessToken) val hdId = json.get(HDID).toString() + println("USERNAME = ${json.get("name")} PREFEREDNAME = ${json.get("preferred_username")}") if (hdId.isEmpty()) throw MyHealthAuthException("Invalid access token!") else diff --git a/common/build.gradle b/common/build.gradle index 4abede1d1..d3cc79950 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -102,6 +102,7 @@ android { kotlinOptions { jvmTarget = '17' } + namespace 'ca.bc.gov.common' } dependencies { diff --git a/common/src/main/AndroidManifest.xml b/common/src/main/AndroidManifest.xml index 39a61523b..a5918e68a 100644 --- a/common/src/main/AndroidManifest.xml +++ b/common/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + \ No newline at end of file diff --git a/common/src/main/java/ca/bc/gov/common/model/UserAuthenticationStatus.kt b/common/src/main/java/ca/bc/gov/common/model/UserAuthenticationStatus.kt new file mode 100644 index 000000000..d26f0c650 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/UserAuthenticationStatus.kt @@ -0,0 +1,7 @@ +package ca.bc.gov.common.model + +enum class UserAuthenticationStatus { + AUTHENTICATED, + SESSION_TIME_OUT, + UN_AUTHENTICATED +} diff --git a/data/build.gradle b/data/build.gradle index 333de1533..02f3920e6 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -63,6 +63,7 @@ android { // Adds exported schema location as test app assets. androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } + namespace 'ca.bc.gov.data' } dependencies { diff --git a/data/src/main/AndroidManifest.xml b/data/src/main/AndroidManifest.xml index 2761d1372..8c4c98268 100644 --- a/data/src/main/AndroidManifest.xml +++ b/data/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index dbc950620..b74d1f3be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,7 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=false # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 668ed1765..1b9c27278 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Oct 20 12:09:37 PST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/repository/build.gradle b/repository/build.gradle index 413ce1962..8521db9a3 100644 --- a/repository/build.gradle +++ b/repository/build.gradle @@ -67,6 +67,7 @@ android { kotlinOptions { jvmTarget = '17' } + namespace 'ca.bc.gov.repository' } dependencies { diff --git a/repository/src/main/AndroidManifest.xml b/repository/src/main/AndroidManifest.xml index cc1e52ac2..568741e54 100644 --- a/repository/src/main/AndroidManifest.xml +++ b/repository/src/main/AndroidManifest.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/repository/src/main/java/ca/bc/gov/repository/bcsc/BcscAuthRepo.kt b/repository/src/main/java/ca/bc/gov/repository/bcsc/BcscAuthRepo.kt index 6c16047c9..c0f85d380 100644 --- a/repository/src/main/java/ca/bc/gov/repository/bcsc/BcscAuthRepo.kt +++ b/repository/src/main/java/ca/bc/gov/repository/bcsc/BcscAuthRepo.kt @@ -9,8 +9,11 @@ import ca.bc.gov.common.const.AUTH_ERROR_DO_LOGIN import ca.bc.gov.common.const.MUST_CALL_MOBILE_CONFIG import ca.bc.gov.common.exceptions.MyHealthException import ca.bc.gov.common.model.AuthParametersDto +import ca.bc.gov.common.model.UserAuthenticationStatus import ca.bc.gov.data.datasource.local.PatientLocalDataSource import ca.bc.gov.preference.EncryptedPreferenceStorage +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationException import net.openid.appauth.AuthorizationRequest @@ -39,6 +42,22 @@ class BcscAuthRepo( private lateinit var authService: AuthorizationService private lateinit var authServiceConfiguration: AuthorizationServiceConfiguration + val userAuthenticationStatus: Flow = flow { + val authString = encryptedPreferenceStorage.authState + authString?.let { + val authState = AuthState.jsonDeserialize(it) + if (authState.isAuthorized) { + if (authState.needsTokenRefresh) { + emit(UserAuthenticationStatus.SESSION_TIME_OUT) + } else { + emit(UserAuthenticationStatus.AUTHENTICATED) + } + } else { + emit(UserAuthenticationStatus.UN_AUTHENTICATED) + } + } ?: emit(UserAuthenticationStatus.UN_AUTHENTICATED) + } + private fun setAuthState(authState: AuthState?) { if (authState != null) { authState.lastTokenResponse?.additionalParameters?.get("refresh_expires_in")?.toLong() diff --git a/scripts/versions.gradle b/scripts/versions.gradle index d71b3e3d3..663ec8b8f 100644 --- a/scripts/versions.gradle +++ b/scripts/versions.gradle @@ -12,7 +12,7 @@ versions.localApiVersion = 2 //Tools & Libs versions.kotlin_gradle_plugin = "1.8.20" -versions.gradle = '7.4.2' +versions.gradle = '8.1.0' versions.annotation = '1.6.0' versions.appcompat = '1.6.1' From 4c259152c01ba09bfff9d3046d8336ba43d96555 Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Tue, 15 Aug 2023 14:29:37 -0700 Subject: [PATCH 27/38] HAPP-1250: Display recommendation for primary & dependent user - added ability to see recommendations for primary and dependent user. --- .../bchealth/compose/component/TopAppBar.kt | 7 +- .../bc/gov/bchealth/compose/theme/Colors.kt | 1 + .../bc/gov/bchealth/ui/home/HomeViewModel.kt | 26 +- .../ImmunizationSchedulesScreen.kt | 4 +- .../recommendations/RecommendationAdapter.kt | 83 ----- .../RecommendationDetailItemUI.kt | 115 +++++++ .../recommendations/RecommendationItemUI.kt | 198 ++++++++++++ .../recommendations/RecommendationScreen.kt | 305 ++++-------------- .../RecommendationsFragment.kt | 28 +- .../RecommendationsViewModel.kt | 85 ++++- .../main/res/layout/item_recommendation.xml | 84 ----- app/src/main/res/values/strings.xml | 3 +- .../PatientWithDependentAndListOrderDto.kt | 8 + ...tientWithImmunizationRecommendationsDto.kt | 11 + .../local/PatientLocalDataSource.kt | 6 + .../data/datasource/local/dao/PatientDao.kt | 10 + .../PatientWithDependentAndListOder.kt | 17 + .../PatientWithImmunizationRecommendations.kt | 17 + .../data/model/mapper/EntityToDtoMapper.kt | 14 + .../bc/gov/repository/DependentsRepository.kt | 5 + .../repository/patient/PatientRepository.kt | 8 + 21 files changed, 579 insertions(+), 456 deletions(-) delete mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationAdapter.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt delete mode 100644 app/src/main/res/layout/item_recommendation.xml create mode 100644 common/src/main/java/ca/bc/gov/common/model/patient/PatientWithDependentAndListOrderDto.kt create mode 100644 common/src/main/java/ca/bc/gov/common/model/patient/PatientWithImmunizationRecommendationsDto.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithDependentAndListOder.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithImmunizationRecommendations.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt index aafb7a52b..1f6ca8a4e 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/component/TopAppBar.kt @@ -1,7 +1,6 @@ package ca.bc.gov.bchealth.compose.component import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme @@ -12,11 +11,11 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.component.menu.ActionMenu @@ -80,11 +79,11 @@ fun HGCenterAlignedTopAppBar( title = { Text( modifier = Modifier - .fillMaxWidth() - .wrapContentWidth(align = Alignment.CenterHorizontally), + .fillMaxWidth(), text = title, style = MaterialTheme.typography.subtitle2, fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center ) }, actions = { diff --git a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt index 7203c2ba7..76ee2d048 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/compose/theme/Colors.kt @@ -18,6 +18,7 @@ val descriptionGrey = Color(0xFF6D757D) val grey = Color(0xFF606060) val greyBg = Color(0xFFF2F2F2) val dividerGrey = Color(0x14212121) +val disableBackground = Color(0xFFCFCFCF) val green = Color(0xFF2E8540) val red = Color(0xFFD8292F) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt index 6cec1b93a..a3c20c7d0 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt @@ -16,16 +16,13 @@ import ca.bc.gov.common.model.settings.AppFeatureDto import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.repository.BannerRepository import ca.bc.gov.repository.OnBoardingRepository -import ca.bc.gov.repository.immunization.ImmunizationRecommendationRepository import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import javax.inject.Inject @HiltViewModel @@ -34,7 +31,6 @@ class HomeViewModel @Inject constructor( private val onBoardingRepository: OnBoardingRepository, private val bannerRepository: BannerRepository, private val workerInvoker: WorkerInvoker, - private val recommendationRepository: ImmunizationRecommendationRepository, ) : ViewModel() { private val _uiState = MutableStateFlow(HomeComposeUiState(isQuickAccessTileTutorialRequired = appFeatureWithQuickAccessTilesRepository.isQuickAccessTileTutorialRequired)) @@ -65,24 +61,14 @@ class HomeViewModel @Inject constructor( quickAccessTileItems.removeIf { tile -> (tile.name == AppFeatureName.IMMUNIZATION_SCHEDULES.value) } - runBlocking(Dispatchers.IO) { - val hasRecommendations = recommendationRepository.hasRecommendations() - - if (!hasRecommendations) { - quickAccessTileItems.removeIf { - (it.name == AppFeatureName.RECOMMENDED_IMMUNIZATIONS.value) + quickAccessTileItems.find { it.name == AppFeatureName.RECOMMENDED_IMMUNIZATIONS.value } + ?.let { + val index = quickAccessTileItems.indexOf(it) + if (index != 1) { + quickAccessTileItems.removeAt(index) + quickAccessTileItems.add(1, it) } - } else { - quickAccessTileItems.find { it.name == AppFeatureName.RECOMMENDED_IMMUNIZATIONS.value } - ?.let { - val index = quickAccessTileItems.indexOf(it) - if (index != 1) { - quickAccessTileItems.removeAt(index) - quickAccessTileItems.add(1, it) - } - } } - } } quickAccessTileItems.removeIf { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt index 095ec6570..fd62c57d1 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/immunizationschedules/ImmunizationSchedulesScreen.kt @@ -26,7 +26,7 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.MyHealthTypography import ca.bc.gov.bchealth.compose.bold -import ca.bc.gov.bchealth.compose.theme.bannerInfoBg +import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue import ca.bc.gov.bchealth.compose.theme.white import ca.bc.gov.bchealth.ui.custom.DecorativeImage import ca.bc.gov.bchealth.ui.home.immunizationschedules.ImmunizationSchedulesViewModel.ImmunizationSchedulesItem @@ -92,7 +92,7 @@ private fun ImmunizationScheduleUI(item: ImmunizationSchedulesItem, onClickItem: .padding(top = 16.dp, bottom = 10.dp) .size(48.dp) .clip(RoundedCornerShape(4.dp)) - .background(bannerInfoBg), + .background(bannerBackgroundBlue), contentAlignment = Alignment.Center ) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationAdapter.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationAdapter.kt deleted file mode 100644 index ad1c4955d..000000000 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationAdapter.kt +++ /dev/null @@ -1,83 +0,0 @@ -package ca.bc.gov.bchealth.ui.recommendations - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.appcompat.content.res.AppCompatResources -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.databinding.ItemRecommendationBinding -import ca.bc.gov.bchealth.utils.orPlaceholder -import ca.bc.gov.bchealth.utils.setColorSpannable -import ca.bc.gov.bchealth.utils.toggleVisibility -import ca.bc.gov.common.model.immunization.ForecastStatus - -class RecommendationAdapter : ListAdapter( - RecommendationDiffCallBacks() -) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( - ItemRecommendationBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - ) - - override fun onBindViewHolder(holder: ViewHolder, position: Int) = with(holder.binding) { - val recommendation = getItem(position) - - tvTitle.text = recommendation.title - - val icon = if (recommendation.status is ForecastStatus.Completed) { - R.drawable.ic_recommendation_checked - } else { - R.drawable.ic_recommendation - } - ivIcon.setImageDrawable( - AppCompatResources.getDrawable(this.root.context, icon) - ) - - val fullStatus: String - val statusOrPlaceholder: String = recommendation.status?.text.orPlaceholder() - val date: String - this.root.context.apply { - fullStatus = getString(R.string.immnz_forecast_status, statusOrPlaceholder) - date = getString(R.string.immnz_forecast_due_date, recommendation.date) - } - - val colorId: Int = when (recommendation.status) { - is ForecastStatus.Eligible -> R.color.status_green - is ForecastStatus.Overdue -> R.color.status_red - else -> R.color.status_grey - } - - tvStatus.setColorSpannable( - fullStatus, - statusOrPlaceholder, - this.root.context.getColor(colorId), - true - ) - tvDueDate.text = date - - renderContentState(recommendation.fullContent) - - holder.itemView.setOnClickListener { - recommendation.fullContent = recommendation.fullContent.not() - renderContentState(recommendation.fullContent) - } - } - - private fun ItemRecommendationBinding.renderContentState(displayFullContent: Boolean) { - groupFullContent.toggleVisibility(displayFullContent) - ivContentState.isSelected = displayFullContent - } - - class ViewHolder(val binding: ItemRecommendationBinding) : RecyclerView.ViewHolder(binding.root) -} - -class RecommendationDiffCallBacks : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: RecommendationDetailItem, newItem: RecommendationDetailItem) = - oldItem == newItem - - override fun areContentsTheSame(oldItem: RecommendationDetailItem, newItem: RecommendationDetailItem) = - oldItem.title == newItem.title -} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt new file mode 100644 index 000000000..6d94eff3a --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt @@ -0,0 +1,115 @@ +package ca.bc.gov.bchealth.ui.recommendations + +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintSet +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.descriptionGrey +import ca.bc.gov.bchealth.compose.theme.statusBlue +import ca.bc.gov.common.model.immunization.ForecastStatus + +private const val ITEM_VACCINE_NAME_ID = "item_vaccine_name_id" +private const val ITEM_STATUS_ID = "item_status_id" +private const val ITEM_DATE_ID = "item_date_id" +private const val ITEM_ICON_ID = "item_icon_id" + +@Composable +fun RecommendationDetailItem( + modifier: Modifier = Modifier, + recommendationDetailItem: RecommendationDetailItem +) { + BoxWithConstraints(modifier) { + ConstraintLayout( + modifier = Modifier.fillMaxWidth(), + constraintSet = recommendationDetailItemConstraint() + ) { + Text( + modifier = Modifier + .layoutId(ITEM_VACCINE_NAME_ID) + .widthIn(min = 100.dp, max = 250.dp), + text = recommendationDetailItem.title, + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + maxLines = 2, + color = statusBlue + ) + Icon( + modifier = Modifier + .size(16.dp) + .layoutId(ITEM_ICON_ID), + painter = painterResource(id = R.drawable.ic_recommendation), + tint = MaterialTheme.colors.primary, + contentDescription = null + ) + Text( + modifier = Modifier.layoutId(ITEM_STATUS_ID), + text = recommendationDetailItem.status?.text ?: "", + style = MaterialTheme.typography.body2, + color = descriptionGrey + ) + Text( + modifier = Modifier.layoutId(ITEM_DATE_ID), + text = recommendationDetailItem.date, + style = MaterialTheme.typography.body2, + color = descriptionGrey + ) + } + } +} + +private fun recommendationDetailItemConstraint() = ConstraintSet { + val itemVaccineNameId = createRefFor(ITEM_VACCINE_NAME_ID) + val itemStatusId = createRefFor(ITEM_STATUS_ID) + val itemDateId = createRefFor(ITEM_DATE_ID) + val itemIconId = createRefFor(ITEM_ICON_ID) + + constrain(itemVaccineNameId) { + start.linkTo(parent.start, 16.dp) + top.linkTo(parent.top, 16.dp) + } + + constrain(itemIconId) { + start.linkTo(itemVaccineNameId.end, 16.dp) + top.linkTo(itemVaccineNameId.top) + bottom.linkTo(itemVaccineNameId.bottom) + } + + constrain(itemStatusId) { + start.linkTo(itemVaccineNameId.start) + top.linkTo(itemVaccineNameId.bottom) + } + + constrain(itemDateId) { + start.linkTo(itemStatusId.start) + top.linkTo(itemStatusId.bottom) + bottom.linkTo(parent.bottom, 16.dp) + } +} + +@Composable +@BasePreview +private fun RecommendationDetailItemPreview() { + HealthGatewayTheme { + RecommendationDetailItem( + recommendationDetailItem = RecommendationDetailItem( + title = "test", + date = "date", + status = ForecastStatus.getByText("Test") + ) + ) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt new file mode 100644 index 000000000..b0a3cd549 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt @@ -0,0 +1,198 @@ +package ca.bc.gov.bchealth.ui.recommendations + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Divider +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintSet +import androidx.constraintlayout.compose.Dimension +import androidx.constraintlayout.compose.Visibility +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue +import ca.bc.gov.bchealth.compose.theme.black +import ca.bc.gov.bchealth.compose.theme.descriptionGrey +import ca.bc.gov.bchealth.compose.theme.disableBackground +import ca.bc.gov.bchealth.compose.theme.grey + +private const val ITEM_ICON_ID = "item_icon_id" +private const val ITEM_ARROW_ID = "item_arrow_id" +private const val ITEM_DETAIL_ID = "item_detail_id" +private const val ITEM_PATIENT_NAME_ID = "item_patient_name_id" +private const val ITEM_RECORD_COUNT_ID = "item_record_count_id" + +@Composable +fun RecommendationItem( + modifier: Modifier = Modifier, + patientWithRecommendations: PatientWithRecommendations, + expanded: Boolean, + onArrowClick: () -> Unit +) { + val hasRecommendations = patientWithRecommendations.recommendations.isNotEmpty() + + Card( + modifier = modifier.fillMaxWidth(), + elevation = 15.dp, + backgroundColor = MaterialTheme.colors.background + ) { + BoxWithConstraints( + modifier = Modifier.fillMaxWidth() + ) { + ConstraintLayout( + recommendationItemConstraints(hasRecommendations), + modifier = Modifier.fillMaxWidth() + ) { + + Image( + modifier = Modifier + .layoutId(ITEM_ICON_ID) + .size(48.dp) + .clip(RoundedCornerShape(8.dp)) + .background( + if (hasRecommendations) { + bannerBackgroundBlue + } else { + disableBackground + } + ), + painter = painterResource( + id = if (patientWithRecommendations.isDependent) { + R.drawable.ic_manage_dependent + } else { + R.drawable.ic_profile + } + ), + contentScale = ContentScale.None, + colorFilter = ColorFilter.tint( + if (hasRecommendations) { + MaterialTheme.colors.primary + } else { + grey + } + ), + contentDescription = null + ) + + Text( + modifier = Modifier.layoutId(ITEM_PATIENT_NAME_ID), + text = patientWithRecommendations.name ?: "", + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + color = if (!hasRecommendations) { + descriptionGrey + } else { + black + } + ) + + Text( + modifier = Modifier.layoutId(ITEM_RECORD_COUNT_ID), + text = "${patientWithRecommendations.recommendations.size}", + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary + ) + + val toggleIcon = if (expanded) { + R.drawable.ic_content_short + } else { + R.drawable.ic_content_full + } + + IconButton( + onClick = { + if (hasRecommendations) { + onArrowClick() + } + }, + modifier = Modifier + .layoutId(ITEM_ARROW_ID) + ) { + Image( + painter = painterResource(id = toggleIcon), + contentDescription = null + ) + } + + AnimatedVisibility( + modifier = Modifier.layoutId(ITEM_DETAIL_ID), + visible = expanded + ) { + + LazyColumn( + modifier = Modifier.heightIn(max = (patientWithRecommendations.recommendations.size * 112).dp), + userScrollEnabled = false + ) { + item { + Divider() + } + items(patientWithRecommendations.recommendations) { + RecommendationDetailItem(recommendationDetailItem = it) + } + } + } + } + } + } +} + +private fun recommendationItemConstraints(hasRecommendations: Boolean) = ConstraintSet { + val itemIconId = createRefFor(ITEM_ICON_ID) + val itemArrow = createRefFor(ITEM_ARROW_ID) + val itemDetailId = createRefFor(ITEM_DETAIL_ID) + val itemPatientNameId = createRefFor(ITEM_PATIENT_NAME_ID) + val itemRecordCountId = createRefFor(ITEM_RECORD_COUNT_ID) + + constrain(itemIconId) { + start.linkTo(parent.start, 16.dp) + top.linkTo(parent.top, 16.dp) + } + + constrain(itemPatientNameId) { + start.linkTo(itemIconId.end, 16.dp) + top.linkTo(itemIconId.top) + bottom.linkTo(itemIconId.bottom) + end.linkTo(itemRecordCountId.start) + width = Dimension.fillToConstraints + } + + constrain(itemRecordCountId) { + top.linkTo(itemArrow.top) + end.linkTo(itemArrow.start) + bottom.linkTo(itemArrow.bottom) + } + + constrain(itemArrow) { + top.linkTo(parent.top, 16.dp) + end.linkTo(parent.end, 16.dp) + visibility = if (hasRecommendations) { + Visibility.Visible + } else { + Visibility.Invisible + } + } + + constrain(itemDetailId) { + top.linkTo(itemIconId.bottom, 16.dp) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt index 61e21312a..dad0746da 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationScreen.kt @@ -1,57 +1,77 @@ package ca.bc.gov.bchealth.ui.recommendations -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Card -import androidx.compose.material.Divider -import androidx.compose.material.Icon -import androidx.compose.material.IconButton +import androidx.compose.foundation.lazy.items import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.layout.layoutId -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.constraintlayout.compose.ConstraintLayout -import androidx.constraintlayout.compose.ConstraintSet -import androidx.constraintlayout.compose.Dimension +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.work.WorkManager import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.component.HGProgressIndicator import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme -import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue -import ca.bc.gov.bchealth.compose.theme.descriptionGrey import ca.bc.gov.bchealth.compose.theme.grey -import ca.bc.gov.bchealth.compose.theme.statusBlue +import ca.bc.gov.bchealth.ui.custom.MyHealthClickableText +import ca.bc.gov.repository.bcsc.BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME @Composable -fun RecommendationScreen(modifier: Modifier = Modifier) { +fun RecommendationScreen( + modifier: Modifier = Modifier, + viewModel: RecommendationsViewModel, + onLinkClicked: () -> Unit +) { + + val uiState = + viewModel.uiState.collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value + val context = LocalContext.current + val workRequest = WorkManager.getInstance(context) + .getWorkInfosForUniqueWorkLiveData(BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME) + .observeAsState() + val workState = workRequest.value?.firstOrNull()?.state + if (workState != null && workState.isFinished) { + LaunchedEffect(key1 = Unit) { + viewModel.loadRecommendations() + } + } else { + LaunchedEffect(Unit) { + viewModel.showProgress() + } + } - RecommendationScreenContent(modifier) + if (uiState.isLoading) { + HGProgressIndicator(modifier) + } else { + RecommendationScreenContent( + modifier, + uiState.patientWithRecommendations, + expandedIds = uiState.expandedCardIds, + onArrowClick = { + viewModel.expandedCard(it) + }, + onLinkClicked = onLinkClicked + ) + } } @Composable private fun RecommendationScreenContent( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + recommendations: List, + expandedIds: Set, + onArrowClick: (Long) -> Unit, + onLinkClicked: () -> Unit ) { LazyColumn( modifier = modifier.fillMaxSize(), @@ -68,226 +88,39 @@ private fun RecommendationScreenContent( ) } item { - Text( + + MyHealthClickableText( + style = MaterialTheme.typography.body2.copy(color = grey), modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.recommendations_para2), - style = MaterialTheme.typography.body2, - color = grey + fullText = stringResource(id = R.string.recommendations_para2), + clickableText = stringResource(id = R.string.immunizeBC), + action = onLinkClicked ) } - for (i in 0 until 5) { - item { - RecommendationItem() - } - } - } -} - -@Composable -private fun RecommendationItem(modifier: Modifier = Modifier) { - var expanded by remember { mutableStateOf(true) } - Card( - modifier = modifier.fillMaxWidth(), - elevation = 15.dp, - backgroundColor = MaterialTheme.colors.background - ) { - BoxWithConstraints( - modifier = Modifier.fillMaxWidth() + items( + recommendations, + key = { item -> item.patientId } ) { - ConstraintLayout( - recommendationItemConstraints(), - modifier = Modifier.fillMaxWidth() - ) { - - Image( - modifier = Modifier - .layoutId(ITEM_ICON_ID) - .size(48.dp) - .clip(RoundedCornerShape(8.dp)) - .background( - bannerBackgroundBlue - ), - painter = painterResource(id = R.drawable.ic_banner_icon), - contentScale = ContentScale.None, - contentDescription = null - ) - - Text( - modifier = Modifier.layoutId(ITEM_PATIENT_NAME_ID), - text = stringResource(id = R.string.home), - style = MaterialTheme.typography.body1, - fontWeight = FontWeight.Bold - ) - - Text( - modifier = Modifier.layoutId(ITEM_RECORD_COUNT_ID), - text = "3", - style = MaterialTheme.typography.body1, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.primary - ) - - val toggleIcon = if (expanded) { - R.drawable.ic_content_short - } else { - R.drawable.ic_content_full - } - - IconButton( - onClick = { expanded = !expanded }, - modifier = Modifier - .layoutId(ITEM_ARROW_ID) - ) { - Image( - painter = painterResource(id = toggleIcon), - contentDescription = null - ) - } - - AnimatedVisibility(modifier = Modifier.layoutId(ITEM_DETAIL_ID), visible = expanded) { - - LazyColumn( - modifier = Modifier.heightIn(max = (5 * 80).dp), - userScrollEnabled = false - ) { - item { - Divider() - } - items(5) { - RecommendationDetailItem() - } - } - } - } - } - } -} - -private const val ITEM_ICON_ID = "item_icon_id" -private const val ITEM_ARROW_ID = "item_arrow_id" -private const val ITEM_DETAIL_ID = "item_detail_id" -private const val ITEM_PATIENT_NAME_ID = "item_patient_name_id" -private const val ITEM_RECORD_COUNT_ID = "item_record_count_id" -private fun recommendationItemConstraints() = ConstraintSet { - val itemIconId = createRefFor(ITEM_ICON_ID) - val itemArrow = createRefFor(ITEM_ARROW_ID) - val itemDetailId = createRefFor(ITEM_DETAIL_ID) - val itemPatientNameId = createRefFor(ITEM_PATIENT_NAME_ID) - val itemRecordCountId = createRefFor(ITEM_RECORD_COUNT_ID) - - constrain(itemIconId) { - start.linkTo(parent.start, 16.dp) - top.linkTo(parent.top, 16.dp) - } - - constrain(itemPatientNameId) { - start.linkTo(itemIconId.end, 16.dp) - top.linkTo(itemIconId.top) - bottom.linkTo(itemIconId.bottom) - end.linkTo(itemRecordCountId.start) - width = Dimension.fillToConstraints - } - - constrain(itemRecordCountId) { - top.linkTo(itemArrow.top) - end.linkTo(itemArrow.start) - bottom.linkTo(itemArrow.bottom) - } - - constrain(itemArrow) { - top.linkTo(parent.top, 16.dp) - end.linkTo(parent.end, 16.dp) - } - - constrain(itemDetailId) { - top.linkTo(itemIconId.bottom, 16.dp) - } -} - -@Composable -private fun RecommendationDetailItem(modifier: Modifier = Modifier) { - BoxWithConstraints(modifier = Modifier.background(MaterialTheme.colors.background)) { - ConstraintLayout( - modifier = Modifier.fillMaxWidth(), - constraintSet = recommendationDetailItemConstraint() - ) { - Text( - modifier = Modifier.layoutId(ITEM_VACCINE_NAME_ID), - text = "Yello Fever", - style = MaterialTheme.typography.body1, - fontWeight = FontWeight.Bold, - color = statusBlue - ) - Icon( - modifier = Modifier - .size(16.dp) - .layoutId(ITEM_ICON_ID), - painter = painterResource(id = R.drawable.ic_recommendation), - tint = MaterialTheme.colors.primary, - contentDescription = null - ) - Text( - modifier = Modifier.layoutId(ITEM_STATUS_ID), - text = "Yello Fever", - style = MaterialTheme.typography.body2, - color = descriptionGrey - ) - Text( - modifier = Modifier.layoutId(ITEM_DATE_ID), - text = "Yello Fever", - style = MaterialTheme.typography.body2, - color = descriptionGrey + RecommendationItem( + patientWithRecommendations = it, + expanded = expandedIds.contains(it.patientId), + onArrowClick = { onArrowClick(it.patientId) } ) } } } -private const val ITEM_VACCINE_NAME_ID = "item_vaccine_name_id" -private const val ITEM_STATUS_ID = "item_status_id" -private const val ITEM_DATE_ID = "item_date_id" -private fun recommendationDetailItemConstraint() = ConstraintSet { - val itemVaccineNameId = createRefFor(ITEM_VACCINE_NAME_ID) - val itemStatusId = createRefFor(ITEM_STATUS_ID) - val itemDateId = createRefFor(ITEM_DATE_ID) - val itemIconId = createRefFor(ITEM_ICON_ID) - - constrain(itemVaccineNameId) { - start.linkTo(parent.start, 16.dp) - top.linkTo(parent.top, 16.dp) - } - - constrain(itemIconId) { - start.linkTo(itemVaccineNameId.end, 16.dp) - top.linkTo(itemVaccineNameId.top) - bottom.linkTo(itemVaccineNameId.bottom) - } - - constrain(itemStatusId) { - start.linkTo(itemVaccineNameId.start) - top.linkTo(itemVaccineNameId.bottom) - } - - constrain(itemDateId) { - start.linkTo(itemStatusId.start) - top.linkTo(itemStatusId.bottom) - } -} - -@Composable -@Preview(showBackground = false) -private fun RecommendationItemPreview() { - - HealthGatewayTheme { - RecommendationItem() - } -} - @BasePreview @Composable private fun RecommendationScreenPreview() { HealthGatewayTheme { - RecommendationScreen() + RecommendationScreenContent( + recommendations = emptyList(), + expandedIds = emptySet(), + onArrowClick = {}, + onLinkClicked = {} + ) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt index 80edcbba5..7433b9a16 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt @@ -10,36 +10,24 @@ import androidx.compose.ui.res.stringResource import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.component.HGTopAppBar -import ca.bc.gov.bchealth.compose.component.menu.TopAppBarActionItem +import ca.bc.gov.bchealth.compose.component.HGCenterAlignedTopAppBar import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme -import ca.bc.gov.bchealth.databinding.FragmentRecommendationsBinding import ca.bc.gov.bchealth.ui.BaseFragment -import ca.bc.gov.bchealth.utils.viewBindings +import ca.bc.gov.bchealth.utils.redirect import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class RecommendationsFragment : BaseFragment(null) { - private val binding by viewBindings(FragmentRecommendationsBinding::bind) - private val viewModel: RecommendationsViewModel by viewModels() @Composable override fun GetComposableLayout() { - val menuItems = mutableListOf( - TopAppBarActionItem.IconActionItem.AlwaysShown( - title = getString(R.string.settings), - onClick = { findNavController().navigate(R.id.settingsFragment) }, - icon = R.drawable.ic_menu_settings, - contentDescription = getString(R.string.settings), - ) - ) HealthGatewayTheme { Scaffold( topBar = { - HGTopAppBar( - title = stringResource(id = R.string.recommendations_home_title), - actionItems = menuItems + HGCenterAlignedTopAppBar( + onNavigationAction = { findNavController().popBackStack() }, + title = stringResource(id = R.string.recommendations_home_title) ) }, content = { @@ -48,9 +36,15 @@ class RecommendationsFragment : BaseFragment(null) { .statusBarsPadding() .navigationBarsPadding() .padding(it), + viewModel = viewModel, + onLinkClicked = ::onLinkClicked ) } ) } } + + private fun onLinkClicked() { + requireContext().redirect("https://immunizebc.ca/") + } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt index 2e37cb528..e78b7e86f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt @@ -1,31 +1,98 @@ package ca.bc.gov.bchealth.ui.recommendations import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import ca.bc.gov.bchealth.model.mapper.toUiModel +import ca.bc.gov.common.model.AuthenticationStatus import ca.bc.gov.common.model.immunization.ForecastStatus -import ca.bc.gov.repository.immunization.ImmunizationRecommendationRepository +import ca.bc.gov.repository.DependentsRepository +import ca.bc.gov.repository.patient.PatientRepository import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class RecommendationsViewModel @Inject constructor( - recommendationRepository: ImmunizationRecommendationRepository, + private val patientRepository: PatientRepository, + private val dependentsRepository: DependentsRepository ) : ViewModel() { - val recommendationList = recommendationRepository.getAllRecommendations().map { list -> - list.mapIndexed { index, dto -> - dto.toUiModel().apply { - expandFirstItem(index) + private val _uiState = + MutableStateFlow(RecommendationUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + fun showProgress() { + _uiState.update { it.copy(isLoading = true) } + } + + fun loadRecommendations() = viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + try { + val patient = + patientRepository.findPatientByAuthStatus(AuthenticationStatus.AUTHENTICATED) + val patientData = patientRepository.getPatientWithImmunizationRecommendations(patient.id) + + val records = mutableListOf() + records.add( + PatientWithRecommendations( + patient.id, + patient.fullName, + patientData.recommendations.map { recommendation -> recommendation.toUiModel() } + ) + ) + + val patientWithDependents = patientRepository.getPatientWithDependents(patient.id) + patientWithDependents.dependents.forEach { + dependentsRepository.requestRecordsIfNeeded(it.patientId, it.hdid) + val dependentData = + dependentsRepository.getPatientWithImmunizationRecommendations(it.patientId) + + records.add( + PatientWithRecommendations( + it.patientId, it.firstname, + dependentData.recommendations.map { recommendation -> recommendation.toUiModel() }, + isDependent = true + ) + ) } + + _uiState.update { it.copy(isLoading = false, patientWithRecommendations = records) } + } catch (e: Exception) { + _uiState.update { it.copy(isLoading = false) } } } - private fun RecommendationDetailItem.expandFirstItem(index: Int) { - if (index == 0) this.fullContent = true + fun expandedCard(id: Long) { + if (_uiState.value.expandedCardIds.contains(id)) { + val ids = _uiState.value.expandedCardIds.toMutableSet() + ids.remove(id) + _uiState.update { it.copy(expandedCardIds = ids) } + } else { + val ids = _uiState.value.expandedCardIds.toMutableSet() + ids.add(id) + _uiState.update { it.copy(expandedCardIds = ids) } + } } } +data class RecommendationUiState( + val isLoading: Boolean = true, + val patientWithRecommendations: List = emptyList(), + val expandedCardIds: Set = emptySet() +) + +data class PatientWithRecommendations( + val patientId: Long = 0, + val name: String? = null, + val recommendations: List, + var expanded: Boolean = false, + var isDependent: Boolean = false +) + data class RecommendationDetailItem( val title: String, val status: ForecastStatus?, diff --git a/app/src/main/res/layout/item_recommendation.xml b/app/src/main/res/layout/item_recommendation.xml deleted file mode 100644 index 45e528daf..000000000 --- a/app/src/main/res/layout/item_recommendation.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00e3a6a40..03e31aa01 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,8 +48,9 @@ Recommended immunizations For more information on vaccines recommendation and eligibility, please visit immunizeBC or speak to your health care provider. immunizeBC - Recommended immunization are suggestion for your health journey. This page will containt all of future immunization for you and your dependents. To add dependents to application, click on Dependent in the menu bar at the bottom. + Recommended immunization are suggestion for your health journey. This page will contains all of future immunization for you and your dependents. To add dependents to application, click on Dependent in the menu bar at the bottom. For more information on vaccines recommendation and eligibility, please visit immunizeBC or speak to your health care provider. + immunizeBC Dependent diff --git a/common/src/main/java/ca/bc/gov/common/model/patient/PatientWithDependentAndListOrderDto.kt b/common/src/main/java/ca/bc/gov/common/model/patient/PatientWithDependentAndListOrderDto.kt new file mode 100644 index 000000000..689d90187 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/patient/PatientWithDependentAndListOrderDto.kt @@ -0,0 +1,8 @@ +package ca.bc.gov.common.model.patient + +import ca.bc.gov.common.model.dependents.DependentDto + +data class PatientWithDependentAndListOrderDto( + val patient: PatientDto, + val dependents: List +) diff --git a/common/src/main/java/ca/bc/gov/common/model/patient/PatientWithImmunizationRecommendationsDto.kt b/common/src/main/java/ca/bc/gov/common/model/patient/PatientWithImmunizationRecommendationsDto.kt new file mode 100644 index 000000000..021f74c85 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/patient/PatientWithImmunizationRecommendationsDto.kt @@ -0,0 +1,11 @@ +package ca.bc.gov.common.model.patient + +import ca.bc.gov.common.model.immunization.ImmunizationRecommendationsDto + +/** + * @author Pinakin Kansara + */ +data class PatientWithImmunizationRecommendationsDto( + val patient: PatientDto, + val recommendations: List +) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/PatientLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/PatientLocalDataSource.kt index c2c8b66e1..9fe9c7130 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/PatientLocalDataSource.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/PatientLocalDataSource.kt @@ -131,6 +131,12 @@ class PatientLocalDataSource @Inject constructor( suspend fun getPatientWithData(patientId: Long): PatientWithDataDto? = patientDao.getPatientWithData(patientId)?.toDto() + suspend fun getPatientWithImmunizationRecommendations(patientId: Long) = + patientDao.getPatientWithImmunizationRecommendations(patientId)?.toDto() + + suspend fun getPatientWithDependents(patientId: Long) = + patientDao.getPatientWithDependents(patientId)?.toDto() + suspend fun getAuthenticatedPatientId() = patientDao.getAuthenticatedPatientId() ?: -1 suspend fun deleteDependentPatients() { diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/PatientDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/PatientDao.kt index 274b76264..7a4ede314 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/PatientDao.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/PatientDao.kt @@ -12,8 +12,10 @@ import ca.bc.gov.data.datasource.local.entity.PatientOrderUpdate import ca.bc.gov.data.datasource.local.entity.relations.PatientWithClinicalDocuments import ca.bc.gov.data.datasource.local.entity.relations.PatientWithCovidOrderAndCovidTest import ca.bc.gov.data.datasource.local.entity.relations.PatientWithData +import ca.bc.gov.data.datasource.local.entity.relations.PatientWithDependentAndListOder import ca.bc.gov.data.datasource.local.entity.relations.PatientWithHealthVisits import ca.bc.gov.data.datasource.local.entity.relations.PatientWithHospitalVisits +import ca.bc.gov.data.datasource.local.entity.relations.PatientWithImmunizationRecommendations import ca.bc.gov.data.datasource.local.entity.relations.PatientWithImmunizationRecordAndForecast import ca.bc.gov.data.datasource.local.entity.relations.PatientWithLabOrdersAndLabTests import ca.bc.gov.data.datasource.local.entity.relations.PatientWithMedicationRecords @@ -98,6 +100,14 @@ interface PatientDao { @Query("SELECT * FROM patient WHERE id = :patientId") suspend fun getPatientWithData(patientId: Long): PatientWithData? + @Transaction + @Query("SELECT * FROM patient WHERE id = :patientId") + suspend fun getPatientWithImmunizationRecommendations(patientId: Long): PatientWithImmunizationRecommendations? + + @Transaction + @Query("SELECT * FROM patient WHERE id = :patientId") + suspend fun getPatientWithDependents(patientId: Long): PatientWithDependentAndListOder? + @Query("SELECT id FROM patient WHERE authentication_status = 'AUTHENTICATED'") suspend fun getAuthenticatedPatientId(): Long? diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithDependentAndListOder.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithDependentAndListOder.kt new file mode 100644 index 000000000..4a8d07c1c --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithDependentAndListOder.kt @@ -0,0 +1,17 @@ +package ca.bc.gov.data.datasource.local.entity.relations + +import androidx.room.Embedded +import androidx.room.Relation +import ca.bc.gov.data.datasource.local.entity.PatientEntity +import ca.bc.gov.data.datasource.local.entity.dependent.DependentEntity + +data class PatientWithDependentAndListOder( + @Embedded + val patient: PatientEntity, + + @Relation( + parentColumn = "id", + entityColumn = "guardian_id" + ) + val dependentAndListOrder: List = emptyList() +) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithImmunizationRecommendations.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithImmunizationRecommendations.kt new file mode 100644 index 000000000..e95f0de0e --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/relations/PatientWithImmunizationRecommendations.kt @@ -0,0 +1,17 @@ +package ca.bc.gov.data.datasource.local.entity.relations + +import androidx.room.Embedded +import androidx.room.Relation +import ca.bc.gov.data.datasource.local.entity.PatientEntity +import ca.bc.gov.data.datasource.local.entity.immunization.ImmunizationRecommendationEntity + +data class PatientWithImmunizationRecommendations( + @Embedded + val patient: PatientEntity, + + @Relation( + parentColumn = "id", + entityColumn = "patient_id" + ) + val recommendations: List = emptyList() +) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt index bb8c58575..04872441e 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt @@ -30,8 +30,10 @@ import ca.bc.gov.common.model.patient.PatientNameDto import ca.bc.gov.common.model.patient.PatientWithClinicalDocumentsDto import ca.bc.gov.common.model.patient.PatientWithCovidOrderAndTestDto import ca.bc.gov.common.model.patient.PatientWithDataDto +import ca.bc.gov.common.model.patient.PatientWithDependentAndListOrderDto import ca.bc.gov.common.model.patient.PatientWithHealthVisitsDto import ca.bc.gov.common.model.patient.PatientWithHospitalVisitsDto +import ca.bc.gov.common.model.patient.PatientWithImmunizationRecommendationsDto import ca.bc.gov.common.model.patient.PatientWithImmunizationRecordAndForecastDto import ca.bc.gov.common.model.patient.PatientWithLabOrderAndLatTestsDto import ca.bc.gov.common.model.patient.PatientWithSpecialAuthorityDto @@ -83,8 +85,10 @@ import ca.bc.gov.data.datasource.local.entity.relations.MedicationWithSummaryAnd import ca.bc.gov.data.datasource.local.entity.relations.PatientWithClinicalDocuments import ca.bc.gov.data.datasource.local.entity.relations.PatientWithCovidOrderAndCovidTest import ca.bc.gov.data.datasource.local.entity.relations.PatientWithData +import ca.bc.gov.data.datasource.local.entity.relations.PatientWithDependentAndListOder import ca.bc.gov.data.datasource.local.entity.relations.PatientWithHealthVisits import ca.bc.gov.data.datasource.local.entity.relations.PatientWithHospitalVisits +import ca.bc.gov.data.datasource.local.entity.relations.PatientWithImmunizationRecommendations import ca.bc.gov.data.datasource.local.entity.relations.PatientWithImmunizationRecordAndForecast import ca.bc.gov.data.datasource.local.entity.relations.PatientWithLabOrdersAndLabTests import ca.bc.gov.data.datasource.local.entity.relations.PatientWithMedicationRecords @@ -501,3 +505,13 @@ fun AppFeatureWithQuickAccessTiles.toDto() = AppFeatureWithQuickAccessTilesDto( appFeature.toDto(), quickAccessTiles.map { it.toDto() } ) + +fun PatientWithImmunizationRecommendations.toDto() = PatientWithImmunizationRecommendationsDto( + patient = patient.toDto(), + recommendations = recommendations.map { it.toDto() } +) + +fun PatientWithDependentAndListOder.toDto() = PatientWithDependentAndListOrderDto( + patient = patient.toDto(), + dependents = dependentAndListOrder.map { it.toDto() } +) diff --git a/repository/src/main/java/ca/bc/gov/repository/DependentsRepository.kt b/repository/src/main/java/ca/bc/gov/repository/DependentsRepository.kt index 416e809df..639866d7c 100644 --- a/repository/src/main/java/ca/bc/gov/repository/DependentsRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/DependentsRepository.kt @@ -11,6 +11,7 @@ import ca.bc.gov.common.model.immunization.ImmunizationDto import ca.bc.gov.common.model.labtest.LabOrderWithLabTestDto import ca.bc.gov.common.model.patient.PatientWithClinicalDocumentsDto import ca.bc.gov.common.model.patient.PatientWithCovidOrderAndTestDto +import ca.bc.gov.common.model.patient.PatientWithImmunizationRecommendationsDto import ca.bc.gov.common.model.patient.PatientWithImmunizationRecordAndForecastDto import ca.bc.gov.common.model.patient.PatientWithLabOrderAndLatTestsDto import ca.bc.gov.common.model.test.CovidOrderWithCovidTestDto @@ -219,6 +220,10 @@ class DependentsRepository @Inject constructor( patientLocalDataSource.getPatientWithClinicalDocuments(patientId) ?: throw getDatabaseException(patientId) + suspend fun getPatientWithImmunizationRecommendations(patientId: Long): PatientWithImmunizationRecommendationsDto = + patientLocalDataSource.getPatientWithImmunizationRecommendations(patientId) + ?: throw getDatabaseException(patientId) + suspend fun updateDependentListOrder(list: List) { localDataSource.deleteAllDependentListOrders() diff --git a/repository/src/main/java/ca/bc/gov/repository/patient/PatientRepository.kt b/repository/src/main/java/ca/bc/gov/repository/patient/PatientRepository.kt index 73e058349..f7939a423 100644 --- a/repository/src/main/java/ca/bc/gov/repository/patient/PatientRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/patient/PatientRepository.kt @@ -116,6 +116,14 @@ class PatientRepository @Inject constructor( patientLocalDataSource.getPatientWithData(patientId) ?: throw getNoRecordFoundException(patientId) + suspend fun getPatientWithImmunizationRecommendations(patientId: Long) = + patientLocalDataSource.getPatientWithImmunizationRecommendations(patientId) + ?: throw getNoRecordFoundException(patientId) + + suspend fun getPatientWithDependents(patientId: Long) = + patientLocalDataSource.getPatientWithDependents(patientId) + ?: throw getNoRecordFoundException(patientId) + private fun getNoRecordFoundException(patientId: Long) = MyHealthException( DATABASE_ERROR, "No record found for patient id= $patientId" ) From 43da9e685f907dbb01e549fe024309f44d8f5de0 Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Wed, 16 Aug 2023 12:10:14 -0700 Subject: [PATCH 28/38] HAPP-1645,1646,1647: Recommendations Fixes - fixed click to expand issue - fixed color of the count issue - fixed ordering issue - fixed title center issue on tool bar. --- .../ui/recommendations/RecommendationItemUI.kt | 13 +++++++++++-- .../ui/recommendations/RecommendationsFragment.kt | 6 +++--- .../ui/recommendations/RecommendationsViewModel.kt | 8 ++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt index b0a3cd549..85749e2eb 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt @@ -3,6 +3,7 @@ package ca.bc.gov.bchealth.ui.recommendations import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn @@ -94,7 +95,11 @@ fun RecommendationItem( ) Text( - modifier = Modifier.layoutId(ITEM_PATIENT_NAME_ID), + modifier = Modifier.layoutId(ITEM_PATIENT_NAME_ID).clickable { + if (hasRecommendations) { + onArrowClick() + } + }, text = patientWithRecommendations.name ?: "", style = MaterialTheme.typography.body1, fontWeight = FontWeight.Bold, @@ -110,7 +115,11 @@ fun RecommendationItem( text = "${patientWithRecommendations.recommendations.size}", style = MaterialTheme.typography.body1, fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.primary + color = if (!hasRecommendations) { + descriptionGrey + } else { + MaterialTheme.colors.primary + } ) val toggleIcon = if (expanded) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt index 7433b9a16..576f40a6d 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsFragment.kt @@ -10,9 +10,9 @@ import androidx.compose.ui.res.stringResource import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.compose.component.HGCenterAlignedTopAppBar import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import ca.bc.gov.bchealth.ui.BaseFragment +import ca.bc.gov.bchealth.ui.custom.MyHealthToolbar import ca.bc.gov.bchealth.utils.redirect import dagger.hilt.android.AndroidEntryPoint @@ -25,8 +25,8 @@ class RecommendationsFragment : BaseFragment(null) { HealthGatewayTheme { Scaffold( topBar = { - HGCenterAlignedTopAppBar( - onNavigationAction = { findNavController().popBackStack() }, + MyHealthToolbar( + navigationAction = { findNavController().popBackStack() }, title = stringResource(id = R.string.recommendations_home_title) ) }, diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt index e78b7e86f..2eadd2843 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt @@ -34,7 +34,8 @@ class RecommendationsViewModel @Inject constructor( try { val patient = patientRepository.findPatientByAuthStatus(AuthenticationStatus.AUTHENTICATED) - val patientData = patientRepository.getPatientWithImmunizationRecommendations(patient.id) + val patientData = + patientRepository.getPatientWithImmunizationRecommendations(patient.id) val records = mutableListOf() records.add( @@ -54,7 +55,10 @@ class RecommendationsViewModel @Inject constructor( records.add( PatientWithRecommendations( it.patientId, it.firstname, - dependentData.recommendations.map { recommendation -> recommendation.toUiModel() }, + dependentData.recommendations.map { recommendation -> recommendation.toUiModel() } + .sortedByDescending { item -> + item.date + }, isDependent = true ) ) From de55f6eb632a8609aa1e9e3eddc317d7dd72a097 Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Thu, 17 Aug 2023 10:23:34 -0700 Subject: [PATCH 29/38] HAPP-1647: ui issues fixes - removed icon from the title - fixed issue with order by date --- .../RecommendationDetailItemUI.kt | 28 +++++-------------- .../recommendations/RecommendationItemUI.kt | 5 +++- .../RecommendationsViewModel.kt | 5 +--- .../data/model/mapper/EntityToDtoMapper.kt | 2 +- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt index 6d94eff3a..2ac8e97db 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationDetailItemUI.kt @@ -2,20 +2,17 @@ package ca.bc.gov.bchealth.ui.recommendations import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.widthIn -import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layoutId -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet -import ca.bc.gov.bchealth.R +import androidx.constraintlayout.compose.Dimension import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import ca.bc.gov.bchealth.compose.theme.descriptionGrey @@ -39,22 +36,15 @@ fun RecommendationDetailItem( ) { Text( modifier = Modifier - .layoutId(ITEM_VACCINE_NAME_ID) - .widthIn(min = 100.dp, max = 250.dp), + .layoutId(ITEM_VACCINE_NAME_ID), text = recommendationDetailItem.title, style = MaterialTheme.typography.body1, fontWeight = FontWeight.Bold, maxLines = 2, + overflow = TextOverflow.Ellipsis, color = statusBlue ) - Icon( - modifier = Modifier - .size(16.dp) - .layoutId(ITEM_ICON_ID), - painter = painterResource(id = R.drawable.ic_recommendation), - tint = MaterialTheme.colors.primary, - contentDescription = null - ) + Text( modifier = Modifier.layoutId(ITEM_STATUS_ID), text = recommendationDetailItem.status?.text ?: "", @@ -79,13 +69,9 @@ private fun recommendationDetailItemConstraint() = ConstraintSet { constrain(itemVaccineNameId) { start.linkTo(parent.start, 16.dp) + end.linkTo(parent.end, 16.dp) top.linkTo(parent.top, 16.dp) - } - - constrain(itemIconId) { - start.linkTo(itemVaccineNameId.end, 16.dp) - top.linkTo(itemVaccineNameId.top) - bottom.linkTo(itemVaccineNameId.bottom) + width = Dimension.fillToConstraints } constrain(itemStatusId) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt index 85749e2eb..5943a78c6 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationItemUI.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.layoutId import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet @@ -103,6 +104,8 @@ fun RecommendationItem( text = patientWithRecommendations.name ?: "", style = MaterialTheme.typography.body1, fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis, color = if (!hasRecommendations) { descriptionGrey } else { @@ -149,7 +152,7 @@ fun RecommendationItem( ) { LazyColumn( - modifier = Modifier.heightIn(max = (patientWithRecommendations.recommendations.size * 112).dp), + modifier = Modifier.heightIn(max = (patientWithRecommendations.recommendations.size * 136).dp), userScrollEnabled = false ) { item { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt index 2eadd2843..c08e677c7 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/recommendations/RecommendationsViewModel.kt @@ -55,10 +55,7 @@ class RecommendationsViewModel @Inject constructor( records.add( PatientWithRecommendations( it.patientId, it.firstname, - dependentData.recommendations.map { recommendation -> recommendation.toUiModel() } - .sortedByDescending { item -> - item.date - }, + dependentData.recommendations.map { recommendation -> recommendation.toUiModel() }, isDependent = true ) ) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt index 04872441e..3da2c497f 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/EntityToDtoMapper.kt @@ -508,7 +508,7 @@ fun AppFeatureWithQuickAccessTiles.toDto() = AppFeatureWithQuickAccessTilesDto( fun PatientWithImmunizationRecommendations.toDto() = PatientWithImmunizationRecommendationsDto( patient = patient.toDto(), - recommendations = recommendations.map { it.toDto() } + recommendations = recommendations.map { it.toDto() }.sortedByDescending { record -> record.agentDueDate } ) fun PatientWithDependentAndListOder.toDto() = PatientWithDependentAndListOrderDto( From 58164cd44e520ad5dd71c179bc1fe5c6f1c2b2d6 Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Wed, 23 Aug 2023 09:24:10 -0700 Subject: [PATCH 30/38] HAPP-1644,1538 - added feature to remove tile from the home screen - added ability to manage tiles from manage tile option - added local tile management feature --- .../ca/bc/gov/bchealth/SplashViewModel.kt | 91 +++++++++++++++++-- .../bc/gov/bchealth/ui/home/HomeFragment.kt | 22 ++++- .../ca/bc/gov/bchealth/ui/home/HomeScreen.kt | 24 ++--- .../bc/gov/bchealth/ui/home/HomeViewModel.kt | 22 +++-- ...emoveQuickAccessTileBottomSheetFragment.kt | 22 ++++- .../RemoveQuickAccessTileBottomSheetScreen.kt | 27 ++++-- .../ui/home/RemoveQuickAccessTileViewModel.kt | 19 ++++ .../manage/QuickAccessManagementViewModel.kt | 13 ++- .../bchealth/ui/login/BcscAuthViewModel.kt | 6 +- app/src/main/res/navigation/home.xml | 6 ++ .../gov/common/model/QuickAccessLinkName.kt | 3 +- .../QuickAccessTileShowAsQuickLinkDto.kt | 6 ++ .../local/QuickActionTileLocalDataSource.kt | 16 ++++ .../local/dao/QuickAccessTileDao.kt | 15 ++- .../QuickAccessTileShowAsQuickLinkEntity.kt | 9 ++ .../data/model/mapper/DtoToEntityMapper.kt | 6 ++ .../settings/QuickAccessTileRepository.kt | 13 +++ 17 files changed, 269 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileViewModel.kt create mode 100644 common/src/main/java/ca/bc/gov/common/model/QuickAccessTileShowAsQuickLinkDto.kt create mode 100644 data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileShowAsQuickLinkEntity.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt index d65150e4a..9c545a0ce 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/SplashViewModel.kt @@ -68,13 +68,9 @@ class SplashViewModel @Inject constructor( val id = appFeatureRepository.insert(healthRecord) if (id > 0) { - val immunization = QuickAccessTileDto( - featureId = id, - tileName = QuickAccessLinkName.IMMUNIZATIONS, - tilePayload = "Immunization", - showAsQuickAccess = true - ) - quickAccessTileRepository.insert(immunization) + + val tiles = timelineQuickLinkTiles(id) + quickAccessTileRepository.insertAll(tiles) } val immunizationSchedule = AppFeatureDto( @@ -104,6 +100,87 @@ class SplashViewModel @Inject constructor( showAsQuickAccess = true ) appFeatureRepository.insert(proofOfVaccine) + + val services = AppFeatureDto( + name = AppFeatureName.SERVICES, + hasManageableQuickAccessLinks = true, + showAsQuickAccess = false + ) + + val serviceId = appFeatureRepository.insert(services) + if (serviceId > 0) { + quickAccessTileRepository.insertAll(serviceQuickLinkTilesItem(serviceId)) + } + } + + private fun serviceQuickLinkTilesItem(id: Long): List { + return listOf( + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.ORGAN_DONOR, + tilePayload = "Organ Donor", + showAsQuickAccess = false + ) + ) + } + + private fun timelineQuickLinkTiles(id: Long): List { + return listOf( + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.IMMUNIZATIONS, + tilePayload = "Immunization", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.MEDICATIONS, + tilePayload = "Medications", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.LAB_RESULTS, + tilePayload = "Laboratory", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.COVID_19_TESTS, + tilePayload = "COVID19Laboratory", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.HEALTH_VISITS, + tilePayload = "HealthVisit", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.SPECIAL_AUTHORITY, + tilePayload = "SpecialAuthority", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.HOSPITAL_VISITS, + tilePayload = "HospitalVisit", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.CLINICAL_DOCUMENTS, + tilePayload = "ClinicalDocument", + showAsQuickAccess = false + ), + QuickAccessTileDto( + featureId = id, + tileName = QuickAccessLinkName.IMAGING_REPORTS, + tilePayload = "ImagingReports", + showAsQuickAccess = false + ) + ) } enum class UpdateType { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt index 350b14469..c8ab183a4 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeFragment.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.core.os.bundleOf @@ -27,6 +26,8 @@ import ca.bc.gov.bchealth.ui.BcscAuthState import ca.bc.gov.bchealth.ui.NavigationAction import ca.bc.gov.bchealth.ui.auth.BioMetricState import ca.bc.gov.bchealth.ui.auth.BiometricsAuthenticationFragment +import ca.bc.gov.bchealth.ui.filter.TimelineTypeFilter +import ca.bc.gov.bchealth.ui.healthrecord.filter.PatientFilterViewModel import ca.bc.gov.bchealth.ui.login.BcscAuthViewModel import ca.bc.gov.bchealth.utils.observeCurrentBackStackForAction import ca.bc.gov.bchealth.viewmodel.SharedViewModel @@ -40,6 +41,7 @@ class HomeFragment : BaseSecureFragment(null) { private val viewModel: HomeViewModel by viewModels() private val authViewModel: BcscAuthViewModel by viewModels() private val sharedViewModel: SharedViewModel by activityViewModels() + private val filterSharedViewModel: PatientFilterViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -63,9 +65,10 @@ class HomeFragment : BaseSecureFragment(null) { @Composable override fun GetComposableLayout() { - val userAuthState = authViewModel.userAuthenticationState.collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value + val userAuthState = + authViewModel.userAuthenticationState.collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value - val authState = authViewModel.authStatus.collectAsState().value + val authState = authViewModel.authStatus.collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value val menuItems = mutableListOf( TopAppBarActionItem.IconActionItem.AlwaysShown( title = getString(R.string.settings), @@ -164,6 +167,19 @@ class HomeFragment : BaseSecureFragment(null) { } private fun onQuickAccessTileClicked(quickAccessTileItem: QuickAccessTileItem) { + when (quickAccessTileItem) { + is QuickAccessTileItem.QuickLinkTileItem -> { + quickAccessTileItem.payload?.let { + filterSharedViewModel.updateFilter( + listOf(TimelineTypeFilter.findByFilterValue(it).name) + ) + } + } + + else -> { + filterSharedViewModel.clearFilter() + } + } findNavController().navigate(quickAccessTileItem.destinationId) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt index 41e3613c7..f78b468c5 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeScreen.kt @@ -20,11 +20,9 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layoutId -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -34,9 +32,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet +import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.work.WorkInfo -import androidx.work.WorkManager import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.component.AnnouncementBannerUI @@ -51,7 +48,6 @@ import ca.bc.gov.bchealth.compose.theme.white import ca.bc.gov.bchealth.ui.login.BcscAuthViewModel import ca.bc.gov.bchealth.ui.login.LoginStatus import ca.bc.gov.bchealth.viewmodel.SharedViewModel -import ca.bc.gov.repository.bcsc.BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME @Composable fun HomeScreen( @@ -66,9 +62,13 @@ fun HomeScreen( onQuickAccessTileClicked: (QuickAccessTileItem) -> Unit, onMoreActionClick: (id: Long, name: String) -> Unit ) { - val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val uiState = viewModel.uiState.collectAsStateWithLifecycle( + minActiveState = Lifecycle.State.RESUMED + ).value - val authState = authViewModel.authStatus.collectAsStateWithLifecycle().value + val authState = authViewModel.authStatus.collectAsStateWithLifecycle( + minActiveState = Lifecycle.State.RESUMED + ).value LaunchedEffect(key1 = Unit) { viewModel.launchCheck() @@ -112,16 +112,6 @@ fun HomeScreen( LaunchedEffect(key1 = it) { viewModel.loadQuickAccessTiles(it) } - val context = LocalContext.current - val workRequest = WorkManager.getInstance(context) - .getWorkInfosForUniqueWorkLiveData(BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME) - .observeAsState() - if (workRequest.value?.firstOrNull()?.state == WorkInfo.State.RUNNING) { - } else { - LaunchedEffect(key1 = Unit) { - viewModel.loadQuickAccessTiles(it) - } - } if (sharedViewModel.shouldFetchBanner) { LaunchedEffect(key1 = Unit) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt index a3c20c7d0..e05acbf16 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/HomeViewModel.kt @@ -52,7 +52,7 @@ class HomeViewModel @Inject constructor( quickAccessTileItems.addAll(appFeatures) if (loginStatus == LoginStatus.ACTIVE) { - data.filter { it.appFeatureDto.showAsQuickAccess }.forEach { + data.filter { it.appFeatureDto.hasManageableQuickAccessLinks }.forEach { val quickLink = it.quickAccessTiles.filter { tile -> tile.showAsQuickAccess } .map { tile -> QuickAccessTileItem.QuickLinkTileItem.from(tile) } quickAccessTileItems.addAll(quickLink) @@ -295,19 +295,19 @@ sealed class QuickAccessTileItem( } QuickAccessLinkName.MEDICATIONS -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_health_record_medication, R.id.health_records) } QuickAccessLinkName.LAB_RESULTS -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_lab_test, R.id.health_records) } QuickAccessLinkName.COVID_19_TESTS -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_health_record_covid_test, R.id.health_records) } QuickAccessLinkName.HEALTH_VISITS -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_health_record_health_visit, R.id.health_records) } QuickAccessLinkName.MY_NOTES -> { @@ -315,19 +315,23 @@ sealed class QuickAccessTileItem( } QuickAccessLinkName.SPECIAL_AUTHORITY -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_health_record_special_authority, R.id.health_records) } QuickAccessLinkName.CLINICAL_DOCUMENTS -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_health_record_clinical_document, R.id.health_records) } QuickAccessLinkName.HOSPITAL_VISITS -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_health_record_hospital_visit, R.id.health_records) } QuickAccessLinkName.IMAGING_REPORTS -> { - Pair(R.drawable.ic_health_record_vaccine, R.id.health_records) + Pair(R.drawable.ic_health_record_diagnostic_imaging, R.id.health_records) + } + + QuickAccessLinkName.ORGAN_DONOR -> { + Pair(R.drawable.ic_organ_donor, R.id.services) } } return QuickLinkTileItem( diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt index 1821b83ea..3c73ad307 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetFragment.kt @@ -6,7 +6,10 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint @@ -14,7 +17,8 @@ import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class RemoveQuickAccessTileBottomSheetFragment : BottomSheetDialogFragment() { - val args: RemoveQuickAccessTileBottomSheetFragmentArgs by navArgs() + private val removeQuickAccessTileViewModel: RemoveQuickAccessTileViewModel by viewModels() + private val args: RemoveQuickAccessTileBottomSheetFragmentArgs by navArgs() override fun onCreateView( inflater: LayoutInflater, @@ -25,9 +29,23 @@ class RemoveQuickAccessTileBottomSheetFragment : BottomSheetDialogFragment() { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { HealthGatewayTheme { - RemoveQuickAccessTileBottomSheetScreen(name = args.name) + RemoveQuickAccessTileBottomSheetScreen( + viewModel = removeQuickAccessTileViewModel, + id = args.id, + name = args.name, + onRemoveClicked = ::onRemoveClicked, + ondDismissClicked = ::ondDismissClicked + ) } } } } + + private fun onRemoveClicked() { + dismiss() + findNavController().navigate(R.id.action_home_self) + } + private fun ondDismissClicked() { + dismiss() + } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt index b1e5962ed..64c064bdb 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileBottomSheetScreen.kt @@ -29,18 +29,29 @@ import ca.bc.gov.bchealth.compose.theme.dividerGrey @Composable fun RemoveQuickAccessTileBottomSheetScreen( modifier: Modifier = Modifier, - name: String + viewModel: RemoveQuickAccessTileViewModel, + id: Long = 0, + name: String, + onRemoveClicked: () -> Unit, + ondDismissClicked: () -> Unit ) { RemoveQuickAccessTileBottomSheetContent( modifier = modifier, - name = name + name = name, + onRemoveClicked = { + viewModel.updateTile(id) + onRemoveClicked() + }, + ondDismissClicked ) } @Composable private fun RemoveQuickAccessTileBottomSheetContent( modifier: Modifier = Modifier, - name: String + name: String, + onRemoveClicked: () -> Unit, + ondDismissClicked: () -> Unit ) { Column( @@ -70,12 +81,12 @@ private fun RemoveQuickAccessTileBottomSheetContent( Spacer(modifier = Modifier.height(16.dp)) HGButton( modifier = Modifier.fillMaxWidth(), - onClick = { }, + onClick = onRemoveClicked, text = stringResource(id = R.string.remove), defaultHeight = HGButtonDefaults.SmallButtonHeight ) Spacer(modifier = Modifier.height(16.dp)) - HGTextButton(onClick = {}) { + HGTextButton(onClick = ondDismissClicked) { Text( text = stringResource(id = R.string.dismiss), style = MaterialTheme.typography.body2, @@ -91,6 +102,10 @@ private fun RemoveQuickAccessTileBottomSheetContent( @BasePreview private fun RemoveQuickAccessTileBottomSheetScreenPreview() { HealthGatewayTheme { - RemoveQuickAccessTileBottomSheetContent(name = "Test") + RemoveQuickAccessTileBottomSheetContent( + name = "Test", + onRemoveClicked = {}, + ondDismissClicked = {} + ) } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileViewModel.kt new file mode 100644 index 000000000..d54dd75a1 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/RemoveQuickAccessTileViewModel.kt @@ -0,0 +1,19 @@ +package ca.bc.gov.bchealth.ui.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import ca.bc.gov.common.model.QuickAccessTileShowAsQuickLinkDto +import ca.bc.gov.repository.settings.QuickAccessTileRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class RemoveQuickAccessTileViewModel @Inject constructor( + private val quickAccessTileRepository: QuickAccessTileRepository +) : ViewModel() { + + fun updateTile(id: Long) = viewModelScope.launch { + quickAccessTileRepository.update(QuickAccessTileShowAsQuickLinkDto(id, showAsQuickAccess = false)) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt index 3e9829f9c..dbfd22a12 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/home/manage/QuickAccessManagementViewModel.kt @@ -2,7 +2,9 @@ package ca.bc.gov.bchealth.ui.home.manage import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import ca.bc.gov.common.model.QuickAccessTileShowAsQuickLinkDto import ca.bc.gov.repository.settings.AppFeatureWithQuickAccessTilesRepository +import ca.bc.gov.repository.settings.QuickAccessTileRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -14,7 +16,8 @@ import javax.inject.Inject @HiltViewModel class QuickAccessManagementViewModel @Inject constructor( - private val appFeatureRepository: AppFeatureWithQuickAccessTilesRepository + private val appFeatureRepository: AppFeatureWithQuickAccessTilesRepository, + private val quickAccessTileRepository: QuickAccessTileRepository ) : ViewModel() { private val _uiState = MutableStateFlow(QuickAccessManagementUiState()) @@ -50,9 +53,11 @@ class QuickAccessManagementViewModel @Inject constructor( _uiState.update { it.copy(isLoading = true) } _uiState.value.featureWithQuickAccessItems.forEach { - it.quickAccessItems.forEach { tile -> - // TODO : make call to the query to update the item - } + quickAccessTileRepository.updateAll( + it.quickAccessItems.map { tile -> + QuickAccessTileShowAsQuickLinkDto(tile.id, tile.isEnabled) + } + ) } delay(300L) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt index 90dc61576..ab51f2d84 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/login/BcscAuthViewModel.kt @@ -23,6 +23,7 @@ import ca.bc.gov.repository.bcsc.BACKGROUND_AUTH_RECORD_FETCH_WORK_NAME import ca.bc.gov.repository.bcsc.BcscAuthRepo import ca.bc.gov.repository.bcsc.PostLoginCheck import ca.bc.gov.repository.patient.PatientRepository +import ca.bc.gov.repository.settings.QuickAccessTileRepository import ca.bc.gov.repository.worker.MobileConfigRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -47,7 +48,8 @@ class BcscAuthViewModel @Inject constructor( private val patientRepository: PatientRepository, private val patientWithBCSCLoginRepository: PatientWithBCSCLoginRepository, private val mobileConfigRepository: MobileConfigRepository, - private val cacheRepository: CacheRepository + private val cacheRepository: CacheRepository, + private val quickAccessTileRepository: QuickAccessTileRepository ) : ViewModel() { private val _authStatus = MutableStateFlow(AuthStatus()) @@ -401,6 +403,7 @@ class BcscAuthViewModel @Inject constructor( ) { patientRepository.deleteByPatientId(patientFromLocalSource.id) patientRepository.insertAuthenticatedPatient(patientFromRemoteSource) + quickAccessTileRepository.update(showAsQuickAccess = false) } } catch (e: java.lang.Exception) { /* @@ -408,6 +411,7 @@ class BcscAuthViewModel @Inject constructor( * to show patient name soon after login * */ patientFromRemoteSource?.let { patientRepository.insertAuthenticatedPatient(it) } + quickAccessTileRepository.update(showAsQuickAccess = false) } } diff --git a/app/src/main/res/navigation/home.xml b/app/src/main/res/navigation/home.xml index 3c6340d4f..acd14c625 100644 --- a/app/src/main/res/navigation/home.xml +++ b/app/src/main/res/navigation/home.xml @@ -74,5 +74,11 @@ + \ No newline at end of file diff --git a/common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt b/common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt index 96b250af2..0cd65cb21 100644 --- a/common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt +++ b/common/src/main/java/ca/bc/gov/common/model/QuickAccessLinkName.kt @@ -10,7 +10,8 @@ enum class QuickAccessLinkName(val value: String) { SPECIAL_AUTHORITY("Special Authority"), CLINICAL_DOCUMENTS("Clinical Documents"), HOSPITAL_VISITS("Hospital Visits"), - IMAGING_REPORTS("Imaging Reports"); + IMAGING_REPORTS("Imaging Reports"), + ORGAN_DONOR("Organ Donor"); companion object { private val map = QuickAccessLinkName.values().associateBy(QuickAccessLinkName::value) diff --git a/common/src/main/java/ca/bc/gov/common/model/QuickAccessTileShowAsQuickLinkDto.kt b/common/src/main/java/ca/bc/gov/common/model/QuickAccessTileShowAsQuickLinkDto.kt new file mode 100644 index 000000000..941604e41 --- /dev/null +++ b/common/src/main/java/ca/bc/gov/common/model/QuickAccessTileShowAsQuickLinkDto.kt @@ -0,0 +1,6 @@ +package ca.bc.gov.common.model + +data class QuickAccessTileShowAsQuickLinkDto( + val id: Long = 0, + val showAsQuickAccess: Boolean = false +) diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt index 506d63e4d..bd5a3a42b 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/QuickActionTileLocalDataSource.kt @@ -1,5 +1,6 @@ package ca.bc.gov.data.datasource.local +import ca.bc.gov.common.model.QuickAccessTileShowAsQuickLinkDto import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.data.datasource.local.dao.QuickAccessTileDao import ca.bc.gov.data.model.mapper.toEntity @@ -12,4 +13,19 @@ class QuickActionTileLocalDataSource @Inject constructor( suspend fun insert(quickAccessTileDto: QuickAccessTileDto): Long { return quickAccessTileDao.insert(quickAccessTileDto.toEntity()) } + + suspend fun insertAll(tiles: List): List { + return quickAccessTileDao.insert(tiles.map { it.toEntity() }) + } + + suspend fun updateAll(tiles: List): Int { + return quickAccessTileDao.updateAll(tiles.map { it.toEntity() }) + } + suspend fun update(tile: QuickAccessTileShowAsQuickLinkDto) { + quickAccessTileDao.update(tile.toEntity()) + } + + suspend fun update(showAsQuickAction: Boolean) { + quickAccessTileDao.update(showAsQuickAction) + } } diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt index 42092ffc0..1113b51aa 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/dao/QuickAccessTileDao.kt @@ -1,7 +1,20 @@ package ca.bc.gov.data.datasource.local.dao import androidx.room.Dao +import androidx.room.Query +import androidx.room.Update import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileShowAsQuickLinkEntity @Dao -interface QuickAccessTileDao : BaseDao +interface QuickAccessTileDao : BaseDao { + + @Update(entity = QuickAccessTileEntity::class) + suspend fun updateAll(tiles: List): Int + + @Update(entity = QuickAccessTileEntity::class) + suspend fun update(tile: QuickAccessTileShowAsQuickLinkEntity) + + @Query("UPDATE quick_access_tile SET show_as_quick_access = :showAsQuickAccess") + suspend fun update(showAsQuickAccess: Boolean) +} diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileShowAsQuickLinkEntity.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileShowAsQuickLinkEntity.kt new file mode 100644 index 000000000..ad293d889 --- /dev/null +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/entity/settings/QuickAccessTileShowAsQuickLinkEntity.kt @@ -0,0 +1,9 @@ +package ca.bc.gov.data.datasource.local.entity.settings + +import androidx.room.ColumnInfo + +data class QuickAccessTileShowAsQuickLinkEntity( + val id: Long = 0, + @ColumnInfo(name = "show_as_quick_access", defaultValue = "false") + val showAsQuickAccess: Boolean = false +) diff --git a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt index f5e9f05b0..7ce119eac 100644 --- a/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt +++ b/data/src/main/java/ca/bc/gov/data/model/mapper/DtoToEntityMapper.kt @@ -5,6 +5,7 @@ import ca.bc.gov.common.model.DispensingPharmacyDto import ca.bc.gov.common.model.MedicationRecordDto import ca.bc.gov.common.model.MedicationSummaryDto import ca.bc.gov.common.model.PatientAddressDto +import ca.bc.gov.common.model.QuickAccessTileShowAsQuickLinkDto import ca.bc.gov.common.model.VaccineDoseDto import ca.bc.gov.common.model.VaccineRecordDto import ca.bc.gov.common.model.clinicaldocument.ClinicalDocumentDto @@ -54,6 +55,7 @@ import ca.bc.gov.data.datasource.local.entity.services.DiagnosticImagingDataEnti import ca.bc.gov.data.datasource.local.entity.services.OrganDonorEntity import ca.bc.gov.data.datasource.local.entity.settings.AppFeatureEntity import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileEntity +import ca.bc.gov.data.datasource.local.entity.settings.QuickAccessTileShowAsQuickLinkEntity import ca.bc.gov.data.datasource.local.entity.specialauthority.SpecialAuthorityEntity import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity @@ -375,3 +377,7 @@ fun QuickAccessTileDto.toEntity() = QuickAccessTileEntity( tilePayload, showAsQuickAccess ) + +fun QuickAccessTileShowAsQuickLinkDto.toEntity() = QuickAccessTileShowAsQuickLinkEntity( + id, showAsQuickAccess +) diff --git a/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt b/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt index cd610b729..9a72c23a4 100644 --- a/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt +++ b/repository/src/main/java/ca/bc/gov/repository/settings/QuickAccessTileRepository.kt @@ -1,5 +1,6 @@ package ca.bc.gov.repository.settings +import ca.bc.gov.common.model.QuickAccessTileShowAsQuickLinkDto import ca.bc.gov.common.model.settings.QuickAccessTileDto import ca.bc.gov.data.datasource.local.QuickActionTileLocalDataSource import javax.inject.Inject @@ -10,4 +11,16 @@ class QuickAccessTileRepository @Inject constructor( suspend fun insert(quickAccessTileDto: QuickAccessTileDto) = quickActionTileLocalDataSource.insert(quickAccessTileDto) + + suspend fun insertAll(tiles: List) = + quickActionTileLocalDataSource.insertAll(tiles) + + suspend fun updateAll(tiles: List) = + quickActionTileLocalDataSource.updateAll(tiles) + + suspend fun update(tile: QuickAccessTileShowAsQuickLinkDto) = + quickActionTileLocalDataSource.update(tile) + + suspend fun update(showAsQuickAccess: Boolean) = + quickActionTileLocalDataSource.update(showAsQuickAccess) } From d4cf4f936bacbebbd42cb5acbcc6a533b0fe918f Mon Sep 17 00:00:00 2001 From: PINAKIN-KANSARA-EY Date: Tue, 5 Sep 2023 15:06:30 -0700 Subject: [PATCH 31/38] Merge conflict issue --- .../15.json | 122 +++++++++++++++++- .../data/datasource/local/MyHealthDataBase.kt | 5 +- 2 files changed, 122 insertions(+), 5 deletions(-) diff --git a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/15.json b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/15.json index 807ef0b05..796a6db76 100644 --- a/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/15.json +++ b/data/schemas/ca.bc.gov.data.datasource.local.MyHealthDataBase/15.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 15, - "identityHash": "1cd6080273feb77af674dc6bd9751e38", + "identityHash": "bfa234b2e6680ceb5454934b27b3bba8", "entities": [ { "tableName": "patient", @@ -2004,12 +2004,130 @@ }, "indices": [], "foreignKeys": [] + }, + { + "tableName": "app_feature", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_name` TEXT NOT NULL, `has_manageable_quick_access_links` INTEGER NOT NULL DEFAULT false, `show_as_quick_access` INTEGER NOT NULL DEFAULT false)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "feature_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasManageableQuickAccessLinks", + "columnName": "has_manageable_quick_access_links", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "showAsQuickAccess", + "columnName": "show_as_quick_access", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_app_feature_feature_name", + "unique": true, + "columnNames": [ + "feature_name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_app_feature_feature_name` ON `${TABLE_NAME}` (`feature_name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "quick_access_tile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `feature_id` INTEGER NOT NULL, `tile_name` TEXT NOT NULL, `tile_payload` TEXT DEFAULT null, `show_as_quick_access` INTEGER NOT NULL DEFAULT false, FOREIGN KEY(`feature_id`) REFERENCES `app_feature`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "featureId", + "columnName": "feature_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tileName", + "columnName": "tile_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tilePayload", + "columnName": "tile_payload", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "null" + }, + { + "fieldPath": "showAsQuickAccess", + "columnName": "show_as_quick_access", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_quick_access_tile_tile_name", + "unique": true, + "columnNames": [ + "tile_name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_quick_access_tile_tile_name` ON `${TABLE_NAME}` (`tile_name`)" + } + ], + "foreignKeys": [ + { + "table": "app_feature", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "feature_id" + ], + "referencedColumns": [ + "id" + ] + } + ] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1cd6080273feb77af674dc6bd9751e38')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bfa234b2e6680ceb5454934b27b3bba8')" ] } } \ No newline at end of file diff --git a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt index 90e1e94a9..58d94eee2 100644 --- a/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt +++ b/data/src/main/java/ca/bc/gov/data/datasource/local/MyHealthDataBase.kt @@ -70,7 +70,7 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity * @author Pinakin Kansara */ @Database( - version = 16, + version = 15, entities = [ PatientEntity::class, VaccineRecordEntity::class, @@ -106,8 +106,7 @@ import ca.bc.gov.data.datasource.local.entity.userprofile.UserProfileEntity AutoMigration(from = 11, to = 12), AutoMigration(from = 12, to = 13), AutoMigration(from = 13, to = 14), - AutoMigration(from = 14, to = 15), - AutoMigration(from = 15, to = 16, spec = MyHealthDataBase.AppFeatureMigration::class) + AutoMigration(from = 14, to = 15, spec = MyHealthDataBase.AppFeatureMigration::class) ], exportSchema = true ) From e482bb15fcf5e15b2b14e1f23057167b4b0b2888 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Tue, 12 Sep 2023 15:14:20 -0700 Subject: [PATCH 32/38] HAPP-1597: Display LabResults in Compose --- .../ca/bc/gov/bchealth/ui/BaseFragment.kt | 4 +- .../labtest/LabTestDetailAdapter.kt | 213 ---------- .../labtest/LabTestDetailFragment.kt | 127 +++--- .../ui/healthrecord/labtest/LabTestScreen.kt | 394 ++++++++++++++++++ .../bc/gov/bchealth/utils/StringExtensions.kt | 3 + .../res/layout/fragment_lab_test_detail.xml | 47 --- .../main/res/layout/item_lab_test_detail.xml | 108 ----- .../layout/item_lab_test_detail_banner.xml | 52 --- app/src/main/res/layout/item_view_pdf.xml | 23 - 9 files changed, 479 insertions(+), 492 deletions(-) delete mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailAdapter.kt create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt delete mode 100644 app/src/main/res/layout/fragment_lab_test_detail.xml delete mode 100644 app/src/main/res/layout/item_lab_test_detail.xml delete mode 100644 app/src/main/res/layout/item_lab_test_detail_banner.xml delete mode 100644 app/src/main/res/layout/item_view_pdf.xml diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt index 6ffae4c1a..a679f496c 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/BaseFragment.kt @@ -99,13 +99,13 @@ abstract class BaseFragment(@LayoutRes private val contentLayoutId: Int?) : Frag private fun resetBaseUiState() = getBaseViewModel()?.resetBaseUiState() - private fun showServiceDownMessage() { + fun showServiceDownMessage() { view?.let { it.showServiceDownMessage(it.context) } } - private fun showNoInternetConnectionMessage() { + fun showNoInternetConnectionMessage() { view?.let { it.showNoInternetConnectionMessage(it.context) } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailAdapter.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailAdapter.kt deleted file mode 100644 index 3439d5941..000000000 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailAdapter.kt +++ /dev/null @@ -1,213 +0,0 @@ -package ca.bc.gov.bchealth.ui.healthrecord.labtest - -import android.content.Context -import android.graphics.Typeface -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.res.ResourcesCompat -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.databinding.ItemLabTestDetailBannerBinding -import ca.bc.gov.bchealth.databinding.ItemLabTestDetailBinding -import ca.bc.gov.bchealth.databinding.ItemViewPdfBinding -import ca.bc.gov.bchealth.utils.makeLinks -import ca.bc.gov.bchealth.utils.redirect -import ca.bc.gov.bchealth.utils.showIfNullOrBlank - -/** - * @author: Created by Rashmi Bambhania on 02,March,2022 - */ -class LabTestDetailAdapter(private val viewPdfClickListener: ViewPdfClickListener) : - ListAdapter(LabTestRecordsDiffCallBacks()) { - - class LabTestBannerViewHolder(val binding: ItemLabTestDetailBannerBinding) : - RecyclerView.ViewHolder(binding.root) - - class LabOrderViewHolder(val binding: ItemLabTestDetailBinding) : - RecyclerView.ViewHolder(binding.root) - - class LabTestViewHolder(val binding: ItemLabTestDetailBinding) : - RecyclerView.ViewHolder(binding.root) - - class LabTestViewPdfViewHolder(val binding: ItemViewPdfBinding) : - RecyclerView.ViewHolder(binding.root) - - fun interface ViewPdfClickListener { - fun onClick() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return when (viewType) { - LabTestDetailViewModel.ITEM_VIEW_TYPE_LAB_TEST_BANNER -> { - val binding = ItemLabTestDetailBannerBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - LabTestBannerViewHolder(binding) - } - LabTestDetailViewModel.ITEM_VIEW_TYPE_LAB_ORDER -> { - val binding = ItemLabTestDetailBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - LabOrderViewHolder(binding) - } - LabTestDetailViewModel.ITEM_VIEW_TYPE_LAB_TEST -> { - val binding = ItemLabTestDetailBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - LabTestViewHolder(binding) - } - LabTestDetailViewModel.ITEM_VIEW_PDF -> { - val binding = ItemViewPdfBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - LabTestViewPdfViewHolder(binding) - } - else -> throw ClassCastException("Unknown viewType $viewType") - } - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - val labTestDetail = getItem(position) - when (holder) { - is LabTestBannerViewHolder -> displayBanner(holder, labTestDetail) - is LabOrderViewHolder -> displayLabOrder(holder, labTestDetail) - is LabTestViewHolder -> displayLabTest(holder, labTestDetail) - is LabTestViewPdfViewHolder -> { - holder.binding.btnViewPdf.setOnClickListener { viewPdfClickListener.onClick() } - } - } - } - - private fun displayBanner(holder: LabTestBannerViewHolder, labTestDetail: LabTestDetail) { - holder.binding.apply { - holder.itemView.context.apply { - tvTitle.text = labTestDetail.bannerHeader?.let { getString(it) } - tvInfo.text = labTestDetail.bannerText?.let { getString(it) } - labTestDetail.bannerClickableText?.let { - tvInfo.makeLinks( - getString(it) to View.OnClickListener { - redirect(getString(R.string.faq_link)) - }, - ) - } - } - } - } - - private fun displayLabOrder(holder: LabOrderViewHolder, labTestDetail: LabTestDetail) { - holder.binding.apply { - tvHeader.visibility = View.GONE - tvSummary.visibility = View.GONE - tvDesc1.text = - labTestDetail.timelineDateTime.showIfNullOrBlank(holder.itemView.context) - tvDesc2.text = - labTestDetail.orderingProvider.showIfNullOrBlank(holder.itemView.context) - tvDesc3.text = - labTestDetail.reportingSource.showIfNullOrBlank(holder.itemView.context) - holder.itemView.context.apply { - tvTitle1.text = labTestDetail.title1?.let { getString(it) } - tvTitle2.text = labTestDetail.title2?.let { getString(it) } - tvTitle3.text = labTestDetail.title3?.let { getString(it) } - } - } - } - - private fun displayLabTest(holder: LabTestViewHolder, labTestDetail: LabTestDetail) { - holder.binding.apply { - labTestDetail.header?.let { - tvHeader.text = holder.itemView.context.getString(it) - tvHeader.visibility = View.VISIBLE - } ?: run { - tvHeader.visibility = View.GONE - } - labTestDetail.summary?.let { - holder.itemView.context.apply { - tvSummary.text = getString(it) - tvSummary.makeLinks( - Pair( - getString(R.string.learn_more), - View.OnClickListener { - redirect(getString(R.string.faq_link)) - } - ) - ) - } - tvSummary.visibility = View.VISIBLE - } ?: run { - tvSummary.visibility = View.GONE - } - holder.itemView.context.apply { - tvTitle1.text = labTestDetail.title1?.let { getString(it) } - tvTitle2.text = labTestDetail.title2?.let { getString(it) } - tvTitle3.text = labTestDetail.title3?.let { getString(it) } - } - - tvDesc1.text = labTestDetail.testName.showIfNullOrBlank(holder.itemView.context) - - val pair = getTestResult( - labTestDetail.isOutOfRange, - labTestDetail.testStatus, - holder.itemView.context - ) - tvDesc2.text = pair.first.showIfNullOrBlank(holder.itemView.context) - tvDesc2.setTextColor(pair.second) - val typeface = - ResourcesCompat.getFont(holder.itemView.context, R.font.bc_sans_bold) - tvDesc2.setTypeface(typeface, Typeface.BOLD) - - tvDesc3.text = labTestDetail.testStatus?.let { - holder.itemView.context.getString( - it - ) - } - } - } - - private fun getTestResult( - outOfRange: Boolean?, - testStatus: Int?, - context: Context - ): Pair { - outOfRange?.let { - return if (outOfRange) { - Pair( - context.resources.getString(R.string.out_of_range), - context.resources.getColor( - R.color.error, null - ) - ) - } else { - Pair( - context.resources.getString(R.string.in_range), - context.resources.getColor( - R.color.status_green, null - ) - ) - } - } ?: run { - return Pair( - testStatus?.let { context.resources.getString(it) } ?: run { "" }, - context.resources.getColor( - R.color.text_black, null - ) - ) - } - } - - override fun getItemViewType(position: Int): Int { - return getItem(position).viewType - } -} - -class LabTestRecordsDiffCallBacks : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: LabTestDetail, newItem: LabTestDetail): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame(oldItem: LabTestDetail, newItem: LabTestDetail): Boolean { - return oldItem == newItem - } -} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt index 70c061f45..966acb533 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt @@ -3,38 +3,39 @@ package ca.bc.gov.bchealth.ui.healthrecord.labtest import android.os.Bundle import android.view.View import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.core.os.bundleOf -import androidx.core.view.isVisible import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import androidx.recyclerview.widget.ConcatAdapter +import androidx.work.WorkInfo import ca.bc.gov.bchealth.R -import ca.bc.gov.bchealth.databinding.FragmentLabTestDetailBinding +import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.ui.comment.CommentEntryTypeCode -import ca.bc.gov.bchealth.ui.healthrecord.BaseRecordDetailFragment +import ca.bc.gov.bchealth.ui.comment.CommentsSummary +import ca.bc.gov.bchealth.ui.comment.CommentsUiState +import ca.bc.gov.bchealth.ui.comment.CommentsViewModel +import ca.bc.gov.bchealth.ui.custom.MyHealthScaffold import ca.bc.gov.bchealth.utils.AlertDialogHelper import ca.bc.gov.bchealth.utils.PdfHelper -import ca.bc.gov.bchealth.utils.showNoInternetConnectionMessage -import ca.bc.gov.bchealth.utils.showServiceDownMessage -import ca.bc.gov.bchealth.utils.viewBindings +import ca.bc.gov.bchealth.utils.observeWork +import ca.bc.gov.bchealth.utils.redirect import ca.bc.gov.bchealth.viewmodel.PdfDecoderViewModel -import ca.bc.gov.bchealth.widget.AddCommentLayout +import ca.bc.gov.common.BuildConfig +import ca.bc.gov.repository.SYNC_COMMENTS import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import java.io.File @AndroidEntryPoint -class LabTestDetailFragment : BaseRecordDetailFragment(R.layout.fragment_lab_test_detail) { - - private val binding by viewBindings(FragmentLabTestDetailBinding::bind) +class LabTestDetailFragment : BaseFragment(null) { private val viewModel: LabTestDetailViewModel by viewModels() + private val commentsViewModel: CommentsViewModel by viewModels() - private lateinit var labTestDetailAdapter: LabTestDetailAdapter - private lateinit var concatAdapter: ConcatAdapter private val args: LabTestDetailFragmentArgs by navArgs() private val pdfDecoderViewModel: PdfDecoderViewModel by viewModels() private var fileInMemory: File? = null @@ -44,67 +45,99 @@ class LabTestDetailFragment : BaseRecordDetailFragment(R.layout.fragment_lab_tes override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupComposeToolbar(binding.composeToolbar.root) - setUpRecyclerView(args.hdid) - viewModel.getLabTestDetails(args.labOrderId) - observeUiState() observePdfData() - initComments() + observeCommentsSyncCompletion() } - override fun getCommentView(): AddCommentLayout = binding.comment + override fun onResume() { + super.onResume() + viewModel.getLabTestDetails(args.labOrderId) + } - override fun getScrollableView() = binding.rvLabTestDetailList + @Composable + override fun GetComposableLayout() { - override fun getCommentEntryTypeCode() = CommentEntryTypeCode.LAB_RESULTS + val uiState = viewModel.uiState.collectAsState().value - override fun getParentEntryId(): String? = viewModel.uiState.value.parentEntryId + var commentState: CommentsUiState? = null - override fun getProgressBar(): View = binding.progressBar + if (BuildConfig.FLAG_ADD_COMMENTS) { + uiState.parentEntryId?.let { commentsViewModel.getComments(it) } + commentState = commentsViewModel.uiState.collectAsState().value + } - private fun setUpRecyclerView(hdid: String?) { - labTestDetailAdapter = LabTestDetailAdapter( - viewPdfClickListener = { viewModel.getLabTestPdf(hdid) } - ) + MyHealthScaffold( + title = uiState.toolbarTitle, + isLoading = uiState.onLoading, + navigationAction = { findNavController().popBackStack() } + ) { + LabTestScreen( + uiState = uiState, + onClickViewPdf = { viewModel.getLabTestPdf(args.hdid) }, + onClickFaq = { context?.redirect(getString(R.string.faq_link)) }, + onClickComments = ::navigateToComments, + commentsSummary = commentState?.commentsSummary, + onSubmitComment = ::onSubmitComment, + ) + } + handledServiceDown(uiState) - concatAdapter = ConcatAdapter(labTestDetailAdapter, getRecordCommentsAdapter()) + if (uiState.onError) { + showError() + viewModel.resetUiState() + } - binding.rvLabTestDetailList.adapter = concatAdapter - } + handlePdfDownload(uiState) - private fun observeUiState() { - viewModel.uiState.collectOnStart { state -> - binding.progressBar.isVisible = state.onLoading + handleNoInternetConnection(uiState) - handledServiceDown(state) + if (commentState?.isBcscSessionActive == false) findNavController().popBackStack() + } - if (state.labTestDetails?.isNotEmpty() == true) { - labTestDetailAdapter.submitList(state.labTestDetails) - setupComposeToolbar(binding.composeToolbar.root, state.toolbarTitle) - } + private fun observeCommentsSyncCompletion() { + if (BuildConfig.FLAG_ADD_COMMENTS.not()) return - if (state.onError) { - showError() - viewModel.resetUiState() + observeWork(SYNC_COMMENTS) { + if (it == WorkInfo.State.SUCCEEDED) { + getParentEntryId()?.let { parentId -> + commentsViewModel.getComments(parentId) + } } + } + } - handlePdfDownload(state) - - handleNoInternetConnection(state) - getComments(state.parentEntryId) + private fun onSubmitComment(content: String) { + getParentEntryId()?.let { parentEntryId -> + commentsViewModel.addComment( + parentEntryId, + content, + CommentEntryTypeCode.LAB_RESULTS.value, + ) } } + private fun navigateToComments(commentsSummary: CommentsSummary) { + findNavController().navigate( + R.id.commentsFragment, + bundleOf( + "parentEntryId" to commentsSummary.parentEntryId, + "recordType" to commentsSummary.entryTypeCode, + ) + ) + } + + private fun getParentEntryId(): String? = viewModel.uiState.value.parentEntryId + private fun handledServiceDown(state: LabTestDetailUiState) { if (!state.isHgServicesUp) { - binding.root.showServiceDownMessage(requireContext()) + showServiceDownMessage() viewModel.resetUiState() } } private fun handleNoInternetConnection(uiState: LabTestDetailUiState) { if (!uiState.isConnected) { - binding.root.showNoInternetConnectionMessage(requireContext()) + showNoInternetConnectionMessage() viewModel.resetUiState() } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt new file mode 100644 index 000000000..77f876d6d --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt @@ -0,0 +1,394 @@ +package ca.bc.gov.bchealth.ui.healthrecord.labtest + +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.Divider +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.dp +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.MyHealthTypography +import ca.bc.gov.bchealth.compose.bold +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.darkText +import ca.bc.gov.bchealth.compose.theme.dividerGrey +import ca.bc.gov.bchealth.compose.theme.green +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.compose.theme.red +import ca.bc.gov.bchealth.ui.comment.CommentsSummary +import ca.bc.gov.bchealth.ui.comment.CommentsSummaryUI +import ca.bc.gov.bchealth.ui.component.HGSmallOutlinedButton +import ca.bc.gov.bchealth.ui.custom.DecorativeImage +import ca.bc.gov.bchealth.ui.custom.MyHealthClickableText +import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_PDF +import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_ORDER +import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST +import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST_BANNER +import ca.bc.gov.bchealth.utils.orPlaceholderIfNullOrBlank +import ca.bc.gov.bchealth.widget.CommentInputUI +import ca.bc.gov.common.BuildConfig + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun LabTestScreen( + uiState: LabTestDetailUiState, + onClickViewPdf: () -> Unit, + onClickFaq: () -> Unit, + onClickComments: (CommentsSummary) -> Unit, + commentsSummary: CommentsSummary?, + onSubmitComment: (String) -> Unit, +) { + val keyboardController = LocalSoftwareKeyboardController.current + + Column( + modifier = Modifier + .fillMaxSize() + .pointerInput(Unit) { + detectTapGestures(onPress = { keyboardController?.hide() }) + }, + ) { + LabTestUi(uiState, onClickViewPdf, onClickFaq, onClickComments, commentsSummary) + + if (BuildConfig.FLAG_ADD_COMMENTS) { + CommentInputUI(onSubmitComment = onSubmitComment) + } + } +} + +@Composable +private fun ColumnScope.LabTestUi( + uiState: LabTestDetailUiState, + onClickViewPdf: () -> Unit, + onClickFaq: () -> Unit, + onClickComments: (CommentsSummary) -> Unit, + commentsSummary: CommentsSummary?, +) { + if (uiState.labTestDetails.isNullOrEmpty()) return + + LazyColumn( + Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp) + .weight(1f) + ) { + items(uiState.labTestDetails) { sample -> + when (sample.viewType) { + ITEM_VIEW_TYPE_LAB_ORDER -> LabOrderUi(sample) + ITEM_VIEW_TYPE_LAB_TEST -> LabTestUi(sample, onClickFaq) + ITEM_VIEW_TYPE_LAB_TEST_BANNER -> LabTestBannerUi(sample, onClickFaq) + ITEM_VIEW_PDF -> LabTestPdfButton(onClickViewPdf) + } + } + + if (BuildConfig.FLAG_ADD_COMMENTS) { + item { Spacer(modifier = Modifier.weight(1f)) } + item { + CommentsSummaryUI( + commentsSummary = commentsSummary, + onClickComments = onClickComments + ) + } + } + } +} + +@Composable +private fun LabTestPdfButton(onClickViewPdf: () -> Unit) { + HGSmallOutlinedButton( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + onClick = onClickViewPdf, + text = "View PDF" + ) + + LabTestDivider() +} + +@Composable +private fun LabTestBannerUi(labTestDetail: LabTestDetail, onClickFaq: () -> Unit) { + + val title = labTestDetail.bannerHeader?.let { stringResource(it) } ?: return + val body = labTestDetail.bannerText?.let { stringResource(it) } ?: return + val clickableText = labTestDetail.bannerClickableText?.let { stringResource(it) } + + Row( + Modifier + .fillMaxWidth() + .padding(vertical = 16.dp) + .background(color = bannerBackgroundBlue) + .padding(16.dp) + ) { + DecorativeImage(resourceId = R.drawable.ic_info) + Spacer(modifier = Modifier.size(8.dp)) + Column { + Text( + text = title, + color = blue, + style = MyHealthTypography.body2.bold() + ) + + clickableText?.let { + MyHealthClickableText( + style = MyHealthTypography.body2.copy(color = blue), + fullText = body, + clickableText = it, + action = onClickFaq, + clickableStyle = SpanStyle( + color = primaryBlue, + textDecoration = TextDecoration.Underline, + fontWeight = FontWeight.Bold + ) + ) + } ?: run { + Text( + style = MyHealthTypography.body2.copy(color = blue), + text = body, + ) + } + } + } +} + +@Composable +private fun LabTestUi(labTestDetail: LabTestDetail, onClickFaq: () -> Unit) { + LabTestHeaderUi( + labTestDetail.header, + labTestDetail.summary?.let { stringResource(id = it) }, + onClickFaq + ) + + LabTestItemUi( + labTestDetail.title1, + labTestDetail.testName.orPlaceholderIfNullOrBlank(), + ) + + LabTestResultItemUi(labTestDetail) + + LabTestItemUi( + labTestDetail.title3, + labTestDetail.testStatus?.let { stringResource(id = it) }, + ) + + LabTestDivider() +} + +@Composable +private fun LabOrderUi(labTestDetail: LabTestDetail) { + LabTestItemUi( + labTestDetail.title1, + labTestDetail.timelineDateTime.orPlaceholderIfNullOrBlank(), + ) + + LabTestItemUi( + labTestDetail.title2, + labTestDetail.orderingProvider.orPlaceholderIfNullOrBlank(), + ) + + LabTestItemUi( + labTestDetail.title3, + labTestDetail.reportingSource.orPlaceholderIfNullOrBlank(), + ) + + LabTestDivider() +} + +@Composable +private fun LabTestHeaderUi(title: Int?, body: String?, onClickFaq: () -> Unit) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + title?.let { + Text( + text = stringResource(id = it), + style = MyHealthTypography.body2.bold() + ) + } + + body?.let { + MyHealthClickableText( + style = MyHealthTypography.body2, + fullText = it, + clickableText = stringResource(R.string.learn_more), + action = onClickFaq, + clickableStyle = SpanStyle( + color = primaryBlue, + textDecoration = TextDecoration.Underline, + fontWeight = FontWeight.Bold + ) + ) + } + } +} + +@Composable +private fun LabTestItemUi(title: Int?, body: String?) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + title?.let { + Text( + text = stringResource(id = it), + style = MyHealthTypography.body2.bold() + ) + } + + body?.let { + Text( + text = it, + style = MyHealthTypography.body2 + ) + } + } +} + +@Composable +private fun LabTestResultItemUi(labTestDetail: LabTestDetail) { + + val (result, color) = getTestResult( + labTestDetail.isOutOfRange, + labTestDetail.testStatus, + ) + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + labTestDetail.title2?.let { + Text( + text = stringResource(id = it), + style = MyHealthTypography.body2.bold() + ) + } + + Text( + text = result?.let { stringResource(id = it) }.orPlaceholderIfNullOrBlank(), + color = color, + style = MyHealthTypography.body2.bold() + ) + } +} + +private fun getTestResult( + outOfRange: Boolean?, + testStatus: Int?, +): Pair { + outOfRange?.let { + return if (outOfRange) { + Pair( + R.string.out_of_range, + red + ) + } else { + Pair( + R.string.in_range, + green + ) + } + } ?: run { + return Pair( + testStatus, + darkText + ) + } +} + +@Composable +private fun LabTestDivider() { + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + color = dividerGrey, + thickness = 1.dp + ) +} + +@Composable +@BasePreview +fun LabTestScreenPreview() { + val sample = listOf( + LabTestDetail( + bannerHeader = R.string.lab_test_banner_pending_title, + bannerText = R.string.lab_test_banner_pending_message_1, + bannerClickableText = R.string.lab_test_banner_pending_clickable_text, + viewType = ITEM_VIEW_TYPE_LAB_TEST_BANNER + ), + + LabTestDetail( + viewType = ITEM_VIEW_PDF + ), + + LabTestDetail( + title1 = R.string.collection_date, + collectionDateTime = "08/11/2022", + timelineDateTime = "09/11/2022", + title2 = R.string.ordering_provider, + orderingProvider = "provider", + title3 = R.string.reporting_lab, + reportingSource = "source" + ), + + LabTestDetail( + header = R.string.test_summary, + summary = R.string.summary_desc, + title1 = R.string.test_name, + testName = "the test name", + title2 = R.string.result, + isOutOfRange = false, + title3 = R.string.lab_test_status, + testStatus = R.string.corrected, + viewType = ITEM_VIEW_TYPE_LAB_TEST + ), + + LabTestDetail( + header = R.string.test_summary, + summary = R.string.summary_desc, + title1 = R.string.test_name, + testName = "the test name", + title2 = R.string.result, + isOutOfRange = false, + title3 = R.string.lab_test_status, + testStatus = R.string.corrected, + viewType = ITEM_VIEW_TYPE_LAB_TEST + ), + + ) + HealthGatewayTheme { + LabTestScreen( + LabTestDetailUiState(labTestDetails = sample, toolbarTitle = "Lab Results"), + {}, + {}, + {}, + null, + {} + ) + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/utils/StringExtensions.kt b/app/src/main/java/ca/bc/gov/bchealth/utils/StringExtensions.kt index a3d794d7d..4c1cd514f 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/utils/StringExtensions.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/utils/StringExtensions.kt @@ -7,5 +7,8 @@ import androidx.core.text.HtmlCompat fun String?.orPlaceholder(placeholder: String = "--"): String = this ?: placeholder +fun String?.orPlaceholderIfNullOrBlank(placeholder: String = "--"): String = + if (this.isNullOrBlank()) placeholder else this + fun String.fromHtml(): Spanned = Html.fromHtml(this, HtmlCompat.FROM_HTML_MODE_COMPACT) diff --git a/app/src/main/res/layout/fragment_lab_test_detail.xml b/app/src/main/res/layout/fragment_lab_test_detail.xml deleted file mode 100644 index 963bb63da..000000000 --- a/app/src/main/res/layout/fragment_lab_test_detail.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_lab_test_detail.xml b/app/src/main/res/layout/item_lab_test_detail.xml deleted file mode 100644 index c48f3cefe..000000000 --- a/app/src/main/res/layout/item_lab_test_detail.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_lab_test_detail_banner.xml b/app/src/main/res/layout/item_lab_test_detail_banner.xml deleted file mode 100644 index 784728cbe..000000000 --- a/app/src/main/res/layout/item_lab_test_detail_banner.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_view_pdf.xml b/app/src/main/res/layout/item_view_pdf.xml deleted file mode 100644 index ee16b4f8a..000000000 --- a/app/src/main/res/layout/item_view_pdf.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - \ No newline at end of file From 72bd7afad8b6d9406626e32daec2fcf68e7152eb Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Wed, 13 Sep 2023 08:05:04 -0700 Subject: [PATCH 33/38] Version 2.0.0-210 --- scripts/versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/versions.gradle b/scripts/versions.gradle index 663ec8b8f..b00f9cbfd 100644 --- a/scripts/versions.gradle +++ b/scripts/versions.gradle @@ -7,7 +7,7 @@ versions.compileSdkVersion = 33 //App versions.versionName = '2.0.0' -versions.versionCode = 209 +versions.versionCode = 210 versions.localApiVersion = 2 //Tools & Libs From de403ca7d38d710c11be701f60d8692d988b644d Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Thu, 14 Sep 2023 16:38:33 -0700 Subject: [PATCH 34/38] PR review: Create LabTestUI file --- .../labtest/LabTestDetailViewModel.kt | 19 +- .../ui/healthrecord/labtest/LabTestScreen.kt | 244 +-------------- .../ui/healthrecord/labtest/LabTestUI.kt | 284 ++++++++++++++++++ 3 files changed, 304 insertions(+), 243 deletions(-) create mode 100644 app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestUI.kt diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt index 22fd99385..42cf3fc66 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt @@ -1,9 +1,13 @@ package ca.bc.gov.bchealth.ui.healthrecord.labtest import androidx.annotation.StringRes +import androidx.compose.ui.graphics.Color import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.theme.darkText +import ca.bc.gov.bchealth.compose.theme.green +import ca.bc.gov.bchealth.compose.theme.red import ca.bc.gov.common.exceptions.NetworkConnectionException import ca.bc.gov.common.exceptions.ServiceDownException import ca.bc.gov.common.model.labtest.LabOrderWithLabTestDto @@ -90,6 +94,7 @@ class LabTestDetailViewModel @Inject constructor( testStatus = R.string.corrected result = labTest.outOfRange } + else -> { testStatus = R.string.completed result = labTest.outOfRange @@ -243,4 +248,16 @@ data class LabTestDetail( val bannerText: Int? = null, val bannerClickableText: Int? = null, val viewType: Int = LabTestDetailViewModel.ITEM_VIEW_TYPE_LAB_ORDER -) +) { + fun getResultColor(): Color = when (isOutOfRange) { + null -> darkText + true -> red + false -> green + } + + fun getResultText(): Int? = when (isOutOfRange) { + null -> testStatus + true -> R.string.out_of_range + false -> R.string.in_range + } +} diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt index 77f876d6d..b16fb85d3 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt @@ -1,52 +1,29 @@ package ca.bc.gov.bchealth.ui.healthrecord.labtest -import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material.Divider -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview -import ca.bc.gov.bchealth.compose.MyHealthTypography -import ca.bc.gov.bchealth.compose.bold import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme -import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue -import ca.bc.gov.bchealth.compose.theme.blue -import ca.bc.gov.bchealth.compose.theme.darkText -import ca.bc.gov.bchealth.compose.theme.dividerGrey -import ca.bc.gov.bchealth.compose.theme.green -import ca.bc.gov.bchealth.compose.theme.primaryBlue -import ca.bc.gov.bchealth.compose.theme.red import ca.bc.gov.bchealth.ui.comment.CommentsSummary import ca.bc.gov.bchealth.ui.comment.CommentsSummaryUI -import ca.bc.gov.bchealth.ui.component.HGSmallOutlinedButton -import ca.bc.gov.bchealth.ui.custom.DecorativeImage -import ca.bc.gov.bchealth.ui.custom.MyHealthClickableText import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_PDF import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_ORDER import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST_BANNER -import ca.bc.gov.bchealth.utils.orPlaceholderIfNullOrBlank import ca.bc.gov.bchealth.widget.CommentInputUI import ca.bc.gov.common.BuildConfig @@ -69,7 +46,7 @@ fun LabTestScreen( detectTapGestures(onPress = { keyboardController?.hide() }) }, ) { - LabTestUi(uiState, onClickViewPdf, onClickFaq, onClickComments, commentsSummary) + LabTestContent(uiState, onClickViewPdf, onClickFaq, onClickComments, commentsSummary) if (BuildConfig.FLAG_ADD_COMMENTS) { CommentInputUI(onSubmitComment = onSubmitComment) @@ -78,7 +55,7 @@ fun LabTestScreen( } @Composable -private fun ColumnScope.LabTestUi( +private fun ColumnScope.LabTestContent( uiState: LabTestDetailUiState, onClickViewPdf: () -> Unit, onClickFaq: () -> Unit, @@ -114,223 +91,6 @@ private fun ColumnScope.LabTestUi( } } -@Composable -private fun LabTestPdfButton(onClickViewPdf: () -> Unit) { - HGSmallOutlinedButton( - modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp), - onClick = onClickViewPdf, - text = "View PDF" - ) - - LabTestDivider() -} - -@Composable -private fun LabTestBannerUi(labTestDetail: LabTestDetail, onClickFaq: () -> Unit) { - - val title = labTestDetail.bannerHeader?.let { stringResource(it) } ?: return - val body = labTestDetail.bannerText?.let { stringResource(it) } ?: return - val clickableText = labTestDetail.bannerClickableText?.let { stringResource(it) } - - Row( - Modifier - .fillMaxWidth() - .padding(vertical = 16.dp) - .background(color = bannerBackgroundBlue) - .padding(16.dp) - ) { - DecorativeImage(resourceId = R.drawable.ic_info) - Spacer(modifier = Modifier.size(8.dp)) - Column { - Text( - text = title, - color = blue, - style = MyHealthTypography.body2.bold() - ) - - clickableText?.let { - MyHealthClickableText( - style = MyHealthTypography.body2.copy(color = blue), - fullText = body, - clickableText = it, - action = onClickFaq, - clickableStyle = SpanStyle( - color = primaryBlue, - textDecoration = TextDecoration.Underline, - fontWeight = FontWeight.Bold - ) - ) - } ?: run { - Text( - style = MyHealthTypography.body2.copy(color = blue), - text = body, - ) - } - } - } -} - -@Composable -private fun LabTestUi(labTestDetail: LabTestDetail, onClickFaq: () -> Unit) { - LabTestHeaderUi( - labTestDetail.header, - labTestDetail.summary?.let { stringResource(id = it) }, - onClickFaq - ) - - LabTestItemUi( - labTestDetail.title1, - labTestDetail.testName.orPlaceholderIfNullOrBlank(), - ) - - LabTestResultItemUi(labTestDetail) - - LabTestItemUi( - labTestDetail.title3, - labTestDetail.testStatus?.let { stringResource(id = it) }, - ) - - LabTestDivider() -} - -@Composable -private fun LabOrderUi(labTestDetail: LabTestDetail) { - LabTestItemUi( - labTestDetail.title1, - labTestDetail.timelineDateTime.orPlaceholderIfNullOrBlank(), - ) - - LabTestItemUi( - labTestDetail.title2, - labTestDetail.orderingProvider.orPlaceholderIfNullOrBlank(), - ) - - LabTestItemUi( - labTestDetail.title3, - labTestDetail.reportingSource.orPlaceholderIfNullOrBlank(), - ) - - LabTestDivider() -} - -@Composable -private fun LabTestHeaderUi(title: Int?, body: String?, onClickFaq: () -> Unit) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp) - ) { - title?.let { - Text( - text = stringResource(id = it), - style = MyHealthTypography.body2.bold() - ) - } - - body?.let { - MyHealthClickableText( - style = MyHealthTypography.body2, - fullText = it, - clickableText = stringResource(R.string.learn_more), - action = onClickFaq, - clickableStyle = SpanStyle( - color = primaryBlue, - textDecoration = TextDecoration.Underline, - fontWeight = FontWeight.Bold - ) - ) - } - } -} - -@Composable -private fun LabTestItemUi(title: Int?, body: String?) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp) - ) { - title?.let { - Text( - text = stringResource(id = it), - style = MyHealthTypography.body2.bold() - ) - } - - body?.let { - Text( - text = it, - style = MyHealthTypography.body2 - ) - } - } -} - -@Composable -private fun LabTestResultItemUi(labTestDetail: LabTestDetail) { - - val (result, color) = getTestResult( - labTestDetail.isOutOfRange, - labTestDetail.testStatus, - ) - - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp) - ) { - labTestDetail.title2?.let { - Text( - text = stringResource(id = it), - style = MyHealthTypography.body2.bold() - ) - } - - Text( - text = result?.let { stringResource(id = it) }.orPlaceholderIfNullOrBlank(), - color = color, - style = MyHealthTypography.body2.bold() - ) - } -} - -private fun getTestResult( - outOfRange: Boolean?, - testStatus: Int?, -): Pair { - outOfRange?.let { - return if (outOfRange) { - Pair( - R.string.out_of_range, - red - ) - } else { - Pair( - R.string.in_range, - green - ) - } - } ?: run { - return Pair( - testStatus, - darkText - ) - } -} - -@Composable -private fun LabTestDivider() { - Divider( - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp), - color = dividerGrey, - thickness = 1.dp - ) -} - @Composable @BasePreview fun LabTestScreenPreview() { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestUI.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestUI.kt new file mode 100644 index 000000000..5976feec1 --- /dev/null +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestUI.kt @@ -0,0 +1,284 @@ +package ca.bc.gov.bchealth.ui.healthrecord.labtest + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Divider +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.dp +import ca.bc.gov.bchealth.R +import ca.bc.gov.bchealth.compose.BasePreview +import ca.bc.gov.bchealth.compose.MyHealthTypography +import ca.bc.gov.bchealth.compose.bold +import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.compose.theme.bannerBackgroundBlue +import ca.bc.gov.bchealth.compose.theme.blue +import ca.bc.gov.bchealth.compose.theme.dividerGrey +import ca.bc.gov.bchealth.compose.theme.primaryBlue +import ca.bc.gov.bchealth.ui.component.HGSmallOutlinedButton +import ca.bc.gov.bchealth.ui.custom.DecorativeImage +import ca.bc.gov.bchealth.ui.custom.MyHealthClickableText +import ca.bc.gov.bchealth.utils.orPlaceholderIfNullOrBlank + +@Composable +fun LabTestPdfButton(onClickViewPdf: () -> Unit) { + HGSmallOutlinedButton( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + onClick = onClickViewPdf, + text = "View PDF" + ) + + LabTestDivider() +} + +@Composable +fun LabTestBannerUi(labTestDetail: LabTestDetail, onClickFaq: () -> Unit) { + + val title = labTestDetail.bannerHeader?.let { stringResource(it) } ?: return + val body = labTestDetail.bannerText?.let { stringResource(it) } ?: return + val clickableText = labTestDetail.bannerClickableText?.let { stringResource(it) } + + Row( + Modifier + .fillMaxWidth() + .padding(vertical = 16.dp) + .background(color = bannerBackgroundBlue) + .padding(16.dp) + ) { + DecorativeImage(resourceId = R.drawable.ic_info) + Spacer(modifier = Modifier.size(8.dp)) + Column { + Text( + text = title, + color = blue, + style = MyHealthTypography.body2.bold() + ) + + clickableText?.let { + MyHealthClickableText( + style = MyHealthTypography.body2.copy(color = blue), + fullText = body, + clickableText = it, + action = onClickFaq, + clickableStyle = SpanStyle( + color = primaryBlue, + textDecoration = TextDecoration.Underline, + fontWeight = FontWeight.Bold + ) + ) + } ?: run { + Text( + style = MyHealthTypography.body2.copy(color = blue), + text = body, + ) + } + } + } +} + +@Composable +fun LabTestUi(labTestDetail: LabTestDetail, onClickFaq: () -> Unit) { + Column { + LabTestHeaderUi( + labTestDetail.header, + labTestDetail.summary?.let { stringResource(id = it) }, + onClickFaq + ) + + LabTestItemUi( + labTestDetail.title1, + labTestDetail.testName.orPlaceholderIfNullOrBlank(), + ) + + LabTestResultItemUi(labTestDetail) + + LabTestItemUi( + labTestDetail.title3, + labTestDetail.testStatus?.let { stringResource(id = it) }, + ) + + LabTestDivider() + } +} + +@Composable +fun LabOrderUi(labTestDetail: LabTestDetail) { + Column { + LabTestItemUi( + labTestDetail.title1, + labTestDetail.timelineDateTime.orPlaceholderIfNullOrBlank(), + ) + + LabTestItemUi( + labTestDetail.title2, + labTestDetail.orderingProvider.orPlaceholderIfNullOrBlank(), + ) + + LabTestItemUi( + labTestDetail.title3, + labTestDetail.reportingSource.orPlaceholderIfNullOrBlank(), + ) + + LabTestDivider() + } +} + +@Composable +private fun LabTestHeaderUi(title: Int?, body: String?, onClickFaq: () -> Unit) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + title?.let { + Text( + text = stringResource(id = it), + style = MyHealthTypography.body2.bold() + ) + } + + body?.let { + MyHealthClickableText( + style = MyHealthTypography.body2, + fullText = it, + clickableText = stringResource(R.string.learn_more), + action = onClickFaq, + clickableStyle = SpanStyle( + color = primaryBlue, + textDecoration = TextDecoration.Underline, + fontWeight = FontWeight.Bold + ) + ) + } + } +} + +@Composable +private fun LabTestItemUi(title: Int?, body: String?) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + title?.let { + Text( + text = stringResource(id = it), + style = MyHealthTypography.body2.bold() + ) + } + + body?.let { + Text( + text = it, + style = MyHealthTypography.body2 + ) + } + } +} + +@Composable +private fun LabTestResultItemUi(labTestDetail: LabTestDetail) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + labTestDetail.title2?.let { + Text( + text = stringResource(id = it), + style = MyHealthTypography.body2.bold() + ) + } + + Text( + text = labTestDetail.getResultText()?.let { stringResource(id = it) } + .orPlaceholderIfNullOrBlank(), + color = labTestDetail.getResultColor(), + style = MyHealthTypography.body2.bold() + ) + } +} + +@Composable +private fun LabTestDivider() { + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + color = dividerGrey, + thickness = 1.dp + ) +} + +@BasePreview +@Composable +private fun LabTestPdfButtonPreview() { + HealthGatewayTheme { + LabTestPdfButton {} + } +} + +@BasePreview +@Composable +private fun LabTestBannerUiPreview() { + HealthGatewayTheme { + LabTestBannerUi( + LabTestDetail( + bannerHeader = R.string.lab_test_banner_pending_title, + bannerText = R.string.lab_test_banner_pending_message_1, + bannerClickableText = R.string.lab_test_banner_pending_clickable_text, + viewType = LabTestDetailViewModel.ITEM_VIEW_TYPE_LAB_TEST_BANNER + ), + ) {} + } +} + +@BasePreview +@Composable +private fun LabTestUiPreview() { + HealthGatewayTheme { + LabTestUi( + LabTestDetail( + header = R.string.test_summary, + summary = R.string.summary_desc, + title1 = R.string.test_name, + testName = "the test name", + title2 = R.string.result, + isOutOfRange = false, + title3 = R.string.lab_test_status, + testStatus = R.string.corrected, + viewType = LabTestDetailViewModel.ITEM_VIEW_TYPE_LAB_TEST + ), + ) {} + } +} + +@BasePreview +@Composable +private fun LabOrderUiPreview() { + HealthGatewayTheme { + LabOrderUi( + LabTestDetail( + title1 = R.string.collection_date, + collectionDateTime = "08/11/2022", + timelineDateTime = "09/11/2022", + title2 = R.string.ordering_provider, + orderingProvider = "provider", + title3 = R.string.reporting_lab, + reportingSource = "source" + ), + ) + } +} From dfa56bd53c15b53655c419afea6678ec5035f7b5 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Fri, 15 Sep 2023 09:19:35 -0700 Subject: [PATCH 35/38] PR review: Move uiState to Composable file --- .../labtest/LabTestDetailFragment.kt | 80 ++----------- .../ui/healthrecord/labtest/LabTestScreen.kt | 106 +++++++++++++++++- 2 files changed, 112 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt index 966acb533..b2b538b59 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.core.os.bundleOf import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -17,10 +16,7 @@ import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.ui.BaseFragment import ca.bc.gov.bchealth.ui.comment.CommentEntryTypeCode import ca.bc.gov.bchealth.ui.comment.CommentsSummary -import ca.bc.gov.bchealth.ui.comment.CommentsUiState import ca.bc.gov.bchealth.ui.comment.CommentsViewModel -import ca.bc.gov.bchealth.ui.custom.MyHealthScaffold -import ca.bc.gov.bchealth.utils.AlertDialogHelper import ca.bc.gov.bchealth.utils.PdfHelper import ca.bc.gov.bchealth.utils.observeWork import ca.bc.gov.bchealth.utils.redirect @@ -56,42 +52,16 @@ class LabTestDetailFragment : BaseFragment(null) { @Composable override fun GetComposableLayout() { - - val uiState = viewModel.uiState.collectAsState().value - - var commentState: CommentsUiState? = null - - if (BuildConfig.FLAG_ADD_COMMENTS) { - uiState.parentEntryId?.let { commentsViewModel.getComments(it) } - commentState = commentsViewModel.uiState.collectAsState().value - } - - MyHealthScaffold( - title = uiState.toolbarTitle, - isLoading = uiState.onLoading, - navigationAction = { findNavController().popBackStack() } - ) { - LabTestScreen( - uiState = uiState, - onClickViewPdf = { viewModel.getLabTestPdf(args.hdid) }, - onClickFaq = { context?.redirect(getString(R.string.faq_link)) }, - onClickComments = ::navigateToComments, - commentsSummary = commentState?.commentsSummary, - onSubmitComment = ::onSubmitComment, - ) - } - handledServiceDown(uiState) - - if (uiState.onError) { - showError() - viewModel.resetUiState() - } - - handlePdfDownload(uiState) - - handleNoInternetConnection(uiState) - - if (commentState?.isBcscSessionActive == false) findNavController().popBackStack() + LabTestScreen( + hdid = args.hdid, + viewModel = viewModel, + commentsViewModel = commentsViewModel, + pdfDecoderViewModel = pdfDecoderViewModel, + onClickFaq = { context?.redirect(getString(R.string.faq_link)) }, + onPopNavigation = findNavController()::popBackStack, + showServiceDownMessage = ::showServiceDownMessage, + showNoInternetConnectionMessage = ::showNoInternetConnectionMessage, + ) } private fun observeCommentsSyncCompletion() { @@ -128,36 +98,6 @@ class LabTestDetailFragment : BaseFragment(null) { private fun getParentEntryId(): String? = viewModel.uiState.value.parentEntryId - private fun handledServiceDown(state: LabTestDetailUiState) { - if (!state.isHgServicesUp) { - showServiceDownMessage() - viewModel.resetUiState() - } - } - - private fun handleNoInternetConnection(uiState: LabTestDetailUiState) { - if (!uiState.isConnected) { - showNoInternetConnectionMessage() - viewModel.resetUiState() - } - } - - private fun handlePdfDownload(state: LabTestDetailUiState) { - if (state.pdfData?.isNotEmpty() == true) { - pdfDecoderViewModel.base64ToPDFFile(state.pdfData) - viewModel.resetUiState() - } - } - - private fun showError() { - AlertDialogHelper.showAlertDialog( - context = requireContext(), - title = getString(R.string.error), - msg = getString(R.string.error_message), - positiveBtnMsg = getString(R.string.dialog_button_ok) - ) - } - private fun observePdfData() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt index b16fb85d3..746cbd1db 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt @@ -10,26 +10,125 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme import ca.bc.gov.bchealth.ui.comment.CommentsSummary import ca.bc.gov.bchealth.ui.comment.CommentsSummaryUI +import ca.bc.gov.bchealth.ui.comment.CommentsUiState +import ca.bc.gov.bchealth.ui.comment.CommentsViewModel +import ca.bc.gov.bchealth.ui.custom.MyHealthScaffold import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_PDF import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_ORDER import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST_BANNER +import ca.bc.gov.bchealth.utils.AlertDialogHelper +import ca.bc.gov.bchealth.viewmodel.PdfDecoderViewModel import ca.bc.gov.bchealth.widget.CommentInputUI import ca.bc.gov.common.BuildConfig -@OptIn(ExperimentalComposeUiApi::class) @Composable fun LabTestScreen( + hdid: String?, + viewModel: LabTestDetailViewModel, + commentsViewModel: CommentsViewModel, + pdfDecoderViewModel: PdfDecoderViewModel, + onClickFaq: () -> Unit, + onPopNavigation: () -> Unit, + showServiceDownMessage: () -> Unit, + showNoInternetConnectionMessage: () -> Unit, +) { + val uiState = viewModel.uiState.collectAsState().value + + var commentState: CommentsUiState? = null + + if (BuildConfig.FLAG_ADD_COMMENTS) { + uiState.parentEntryId?.let { commentsViewModel.getComments(it) } + commentState = commentsViewModel.uiState.collectAsState().value + } + + MyHealthScaffold( + title = uiState.toolbarTitle, + isLoading = uiState.onLoading, + navigationAction = onPopNavigation + ) { + LabTestContent( + uiState = uiState, + onClickViewPdf = { viewModel.getLabTestPdf(hdid) }, + onClickFaq = onClickFaq, + onClickComments = {}, // ::navigateToComments, + commentsSummary = commentState?.commentsSummary, + onSubmitComment = {} + ) // ::onSubmitComment) + } + handledServiceDown(uiState, viewModel, showServiceDownMessage) + + if (uiState.onError) { + ErrorDialog() + viewModel.resetUiState() + } + + handlePdfDownload(uiState, viewModel, pdfDecoderViewModel) + + handleNoInternetConnection(uiState, viewModel, showNoInternetConnectionMessage) + + if (commentState?.isBcscSessionActive == false) onPopNavigation.invoke() +} + +private fun handledServiceDown( + state: LabTestDetailUiState, + viewModel: LabTestDetailViewModel, + showServiceDownMessage: () -> Unit, +) { + if (!state.isHgServicesUp) { + showServiceDownMessage() + viewModel.resetUiState() + } +} + +private fun handleNoInternetConnection( + uiState: LabTestDetailUiState, + viewModel: LabTestDetailViewModel, + showNoInternetConnectionMessage: () -> Unit, +) { + if (!uiState.isConnected) { + showNoInternetConnectionMessage() + viewModel.resetUiState() + } +} + +private fun handlePdfDownload( + state: LabTestDetailUiState, + viewModel: LabTestDetailViewModel, + pdfDecoderViewModel: PdfDecoderViewModel, +) { + if (state.pdfData?.isNotEmpty() == true) { + pdfDecoderViewModel.base64ToPDFFile(state.pdfData) + viewModel.resetUiState() + } +} + +@Composable +private fun ErrorDialog() { + AlertDialogHelper.showAlertDialog( + context = LocalContext.current, + title = stringResource(R.string.error), + msg = stringResource(R.string.error_message), + positiveBtnMsg = stringResource(R.string.dialog_button_ok) + ) +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +private fun LabTestContent( uiState: LabTestDetailUiState, onClickViewPdf: () -> Unit, onClickFaq: () -> Unit, @@ -93,7 +192,7 @@ private fun ColumnScope.LabTestContent( @Composable @BasePreview -fun LabTestScreenPreview() { +fun LabTestContentPreview() { val sample = listOf( LabTestDetail( bannerHeader = R.string.lab_test_banner_pending_title, @@ -139,10 +238,9 @@ fun LabTestScreenPreview() { testStatus = R.string.corrected, viewType = ITEM_VIEW_TYPE_LAB_TEST ), - ) HealthGatewayTheme { - LabTestScreen( + LabTestContent( LabTestDetailUiState(labTestDetails = sample, toolbarTitle = "Lab Results"), {}, {}, From 06f992c491d8307cdb00f93cf2c486d22eb6745b Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Fri, 15 Sep 2023 09:23:50 -0700 Subject: [PATCH 36/38] PR review: Implement collectAsStateWithLifecycle --- .../bchealth/ui/healthrecord/labtest/LabTestScreen.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt index 746cbd1db..377393970 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput @@ -18,6 +17,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.collectAsStateWithLifecycle import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme @@ -46,13 +47,15 @@ fun LabTestScreen( showServiceDownMessage: () -> Unit, showNoInternetConnectionMessage: () -> Unit, ) { - val uiState = viewModel.uiState.collectAsState().value + val uiState = viewModel.uiState + .collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value var commentState: CommentsUiState? = null if (BuildConfig.FLAG_ADD_COMMENTS) { uiState.parentEntryId?.let { commentsViewModel.getComments(it) } - commentState = commentsViewModel.uiState.collectAsState().value + commentState = commentsViewModel.uiState + .collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value } MyHealthScaffold( From b0a95e01e4659c25b4baa7d776ead1dcf4afee2d Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Fri, 15 Sep 2023 15:20:43 -0700 Subject: [PATCH 37/38] PR review: Move PDF state to Composable file --- .../labtest/LabTestDetailFragment.kt | 43 +++++++------------ .../labtest/LabTestDetailViewModel.kt | 9 ++++ .../ui/healthrecord/labtest/LabTestScreen.kt | 24 ++++++++--- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt index b2b538b59..a705a8e2c 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt @@ -6,9 +6,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.core.os.bundleOf import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.work.WorkInfo @@ -20,11 +17,11 @@ import ca.bc.gov.bchealth.ui.comment.CommentsViewModel import ca.bc.gov.bchealth.utils.PdfHelper import ca.bc.gov.bchealth.utils.observeWork import ca.bc.gov.bchealth.utils.redirect +import ca.bc.gov.bchealth.viewmodel.PdfDecoderUiState import ca.bc.gov.bchealth.viewmodel.PdfDecoderViewModel import ca.bc.gov.common.BuildConfig import ca.bc.gov.repository.SYNC_COMMENTS import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch import java.io.File @AndroidEntryPoint @@ -41,19 +38,14 @@ class LabTestDetailFragment : BaseFragment(null) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - observePdfData() observeCommentsSyncCompletion() } - override fun onResume() { - super.onResume() - viewModel.getLabTestDetails(args.labOrderId) - } - @Composable override fun GetComposableLayout() { LabTestScreen( hdid = args.hdid, + labOrderId = args.labOrderId, viewModel = viewModel, commentsViewModel = commentsViewModel, pdfDecoderViewModel = pdfDecoderViewModel, @@ -61,6 +53,7 @@ class LabTestDetailFragment : BaseFragment(null) { onPopNavigation = findNavController()::popBackStack, showServiceDownMessage = ::showServiceDownMessage, showNoInternetConnectionMessage = ::showNoInternetConnectionMessage, + onPdfStateChanged = ::onPdfStateChanged, ) } @@ -98,26 +91,20 @@ class LabTestDetailFragment : BaseFragment(null) { private fun getParentEntryId(): String? = viewModel.uiState.value.parentEntryId - private fun observePdfData() { - viewLifecycleOwner.lifecycleScope.launch { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - pdfDecoderViewModel.uiState.collect { uiState -> - uiState.pdf?.let { - val (base64Pdf, file) = it - if (file != null) { - try { - fileInMemory = file - PdfHelper().showPDF(file, requireActivity(), resultListener) - } catch (e: Exception) { - fallBackToPdfRenderer(base64Pdf) - } - } else { - fallBackToPdfRenderer(base64Pdf) - } - pdfDecoderViewModel.resetUiState() - } + private fun onPdfStateChanged(uiState: PdfDecoderUiState) { + uiState.pdf?.let { + val (base64Pdf, file) = it + if (file != null) { + try { + fileInMemory = file + PdfHelper().showPDF(file, requireActivity(), resultListener) + } catch (e: Exception) { + fallBackToPdfRenderer(base64Pdf) } + } else { + fallBackToPdfRenderer(base64Pdf) } + pdfDecoderViewModel.resetUiState() } } diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt index 42cf3fc66..3e3c85eba 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailViewModel.kt @@ -216,6 +216,15 @@ class LabTestDetailViewModel @Inject constructor( ) } } + + fun resetPdfUiState() { + _uiState.update { + it.copy( + onLoading = false, + pdfData = null, + ) + } + } } data class LabTestDetailUiState( diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt index 377393970..98921728d 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput @@ -32,20 +33,23 @@ import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Compani import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST import ca.bc.gov.bchealth.ui.healthrecord.labtest.LabTestDetailViewModel.Companion.ITEM_VIEW_TYPE_LAB_TEST_BANNER import ca.bc.gov.bchealth.utils.AlertDialogHelper +import ca.bc.gov.bchealth.viewmodel.PdfDecoderUiState import ca.bc.gov.bchealth.viewmodel.PdfDecoderViewModel import ca.bc.gov.bchealth.widget.CommentInputUI import ca.bc.gov.common.BuildConfig @Composable fun LabTestScreen( - hdid: String?, - viewModel: LabTestDetailViewModel, - commentsViewModel: CommentsViewModel, - pdfDecoderViewModel: PdfDecoderViewModel, + onPdfStateChanged: (PdfDecoderUiState) -> Unit, onClickFaq: () -> Unit, onPopNavigation: () -> Unit, showServiceDownMessage: () -> Unit, showNoInternetConnectionMessage: () -> Unit, + hdid: String?, + labOrderId: Long, + viewModel: LabTestDetailViewModel, + commentsViewModel: CommentsViewModel, + pdfDecoderViewModel: PdfDecoderViewModel, ) { val uiState = viewModel.uiState .collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value @@ -58,6 +62,13 @@ fun LabTestScreen( .collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value } + val pdfUiState = pdfDecoderViewModel.uiState + .collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value + + LaunchedEffect(Unit) { + viewModel.getLabTestDetails(labOrderId) + } + MyHealthScaffold( title = uiState.toolbarTitle, isLoading = uiState.onLoading, @@ -72,6 +83,9 @@ fun LabTestScreen( onSubmitComment = {} ) // ::onSubmitComment) } + + onPdfStateChanged(pdfUiState) + handledServiceDown(uiState, viewModel, showServiceDownMessage) if (uiState.onError) { @@ -115,7 +129,7 @@ private fun handlePdfDownload( ) { if (state.pdfData?.isNotEmpty() == true) { pdfDecoderViewModel.base64ToPDFFile(state.pdfData) - viewModel.resetUiState() + viewModel.resetPdfUiState() } } From 9762b3ef603a287e8765b456c09dee9b6b0f9f22 Mon Sep 17 00:00:00 2001 From: bsavoini-fw Date: Mon, 18 Sep 2023 11:58:10 -0700 Subject: [PATCH 38/38] PR review: Move Comments handling state to Composable file --- .../labtest/LabTestDetailFragment.kt | 37 +------------- .../ui/healthrecord/labtest/LabTestScreen.kt | 49 +++++++++++++++++-- 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt index a705a8e2c..1be94d0ce 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestDetailFragment.kt @@ -1,26 +1,19 @@ package ca.bc.gov.bchealth.ui.healthrecord.labtest -import android.os.Bundle -import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.core.os.bundleOf import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import androidx.work.WorkInfo import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.ui.BaseFragment -import ca.bc.gov.bchealth.ui.comment.CommentEntryTypeCode import ca.bc.gov.bchealth.ui.comment.CommentsSummary import ca.bc.gov.bchealth.ui.comment.CommentsViewModel import ca.bc.gov.bchealth.utils.PdfHelper -import ca.bc.gov.bchealth.utils.observeWork import ca.bc.gov.bchealth.utils.redirect import ca.bc.gov.bchealth.viewmodel.PdfDecoderUiState import ca.bc.gov.bchealth.viewmodel.PdfDecoderViewModel -import ca.bc.gov.common.BuildConfig -import ca.bc.gov.repository.SYNC_COMMENTS import dagger.hilt.android.AndroidEntryPoint import java.io.File @@ -36,11 +29,6 @@ class LabTestDetailFragment : BaseFragment(null) { ActivityResultContracts.StartActivityForResult() ) { fileInMemory?.delete() } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - observeCommentsSyncCompletion() - } - @Composable override fun GetComposableLayout() { LabTestScreen( @@ -54,31 +42,10 @@ class LabTestDetailFragment : BaseFragment(null) { showServiceDownMessage = ::showServiceDownMessage, showNoInternetConnectionMessage = ::showNoInternetConnectionMessage, onPdfStateChanged = ::onPdfStateChanged, + navigateToComments = ::navigateToComments ) } - private fun observeCommentsSyncCompletion() { - if (BuildConfig.FLAG_ADD_COMMENTS.not()) return - - observeWork(SYNC_COMMENTS) { - if (it == WorkInfo.State.SUCCEEDED) { - getParentEntryId()?.let { parentId -> - commentsViewModel.getComments(parentId) - } - } - } - } - - private fun onSubmitComment(content: String) { - getParentEntryId()?.let { parentEntryId -> - commentsViewModel.addComment( - parentEntryId, - content, - CommentEntryTypeCode.LAB_RESULTS.value, - ) - } - } - private fun navigateToComments(commentsSummary: CommentsSummary) { findNavController().navigate( R.id.commentsFragment, @@ -89,8 +56,6 @@ class LabTestDetailFragment : BaseFragment(null) { ) } - private fun getParentEntryId(): String? = viewModel.uiState.value.parentEntryId - private fun onPdfStateChanged(uiState: PdfDecoderUiState) { uiState.pdf?.let { val (base64Pdf, file) = it diff --git a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt index 98921728d..4333aa477 100644 --- a/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt +++ b/app/src/main/java/ca/bc/gov/bchealth/ui/healthrecord/labtest/LabTestScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput @@ -20,9 +21,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.work.WorkInfo +import androidx.work.WorkManager import ca.bc.gov.bchealth.R import ca.bc.gov.bchealth.compose.BasePreview import ca.bc.gov.bchealth.compose.theme.HealthGatewayTheme +import ca.bc.gov.bchealth.ui.comment.CommentEntryTypeCode import ca.bc.gov.bchealth.ui.comment.CommentsSummary import ca.bc.gov.bchealth.ui.comment.CommentsSummaryUI import ca.bc.gov.bchealth.ui.comment.CommentsUiState @@ -37,6 +41,7 @@ import ca.bc.gov.bchealth.viewmodel.PdfDecoderUiState import ca.bc.gov.bchealth.viewmodel.PdfDecoderViewModel import ca.bc.gov.bchealth.widget.CommentInputUI import ca.bc.gov.common.BuildConfig +import ca.bc.gov.repository.SYNC_COMMENTS @Composable fun LabTestScreen( @@ -45,6 +50,7 @@ fun LabTestScreen( onPopNavigation: () -> Unit, showServiceDownMessage: () -> Unit, showNoInternetConnectionMessage: () -> Unit, + navigateToComments: (CommentsSummary) -> Unit, hdid: String?, labOrderId: Long, viewModel: LabTestDetailViewModel, @@ -62,6 +68,8 @@ fun LabTestScreen( .collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value } + ObserveCommentsWorker(viewModel, commentsViewModel) + val pdfUiState = pdfDecoderViewModel.uiState .collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED).value @@ -78,10 +86,10 @@ fun LabTestScreen( uiState = uiState, onClickViewPdf = { viewModel.getLabTestPdf(hdid) }, onClickFaq = onClickFaq, - onClickComments = {}, // ::navigateToComments, + onClickComments = navigateToComments, commentsSummary = commentState?.commentsSummary, - onSubmitComment = {} - ) // ::onSubmitComment) + onSubmitComment = { onSubmitComment(viewModel, commentsViewModel, it) } + ) } onPdfStateChanged(pdfUiState) @@ -100,6 +108,41 @@ fun LabTestScreen( if (commentState?.isBcscSessionActive == false) onPopNavigation.invoke() } +private fun onSubmitComment( + viewModel: LabTestDetailViewModel, + commentsViewModel: CommentsViewModel, + content: String +) { + viewModel.uiState.value.parentEntryId?.let { parentEntryId -> + commentsViewModel.addComment( + parentEntryId, + content, + CommentEntryTypeCode.LAB_RESULTS.value, + ) + } +} + +@Composable +private fun ObserveCommentsWorker( + viewModel: LabTestDetailViewModel, + commentsViewModel: CommentsViewModel +) { + if (BuildConfig.FLAG_ADD_COMMENTS.not()) return + + val workRequest = WorkManager + .getInstance(LocalContext.current) + .getWorkInfosForUniqueWorkLiveData(SYNC_COMMENTS) + .observeAsState() + val workState = workRequest.value?.firstOrNull()?.state + if (workState == WorkInfo.State.SUCCEEDED && workState.isFinished) { + LaunchedEffect(key1 = Unit) { + viewModel.uiState.value.parentEntryId?.let { parentId -> + commentsViewModel.getComments(parentId) + } + } + } +} + private fun handledServiceDown( state: LabTestDetailUiState, viewModel: LabTestDetailViewModel,