From 118ea11003dd08d9d7c768aec6f434f78ead35be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:04:22 +0300 Subject: [PATCH 01/73] Add adaptive width breakpoint helper for home navigation drawer --- .../app/feature/home/HomeWidthBreakpoint.kt | 28 +++++++++++++++ .../feature/home/HomeWidthBreakpointTest.kt | 36 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt new file mode 100644 index 00000000..e00e3b71 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.home + +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +/** Material 3's "expanded" window size class breakpoint. */ +internal val HOME_EXPANDED_WIDTH_BREAKPOINT: Dp = 840.dp + +/** + * True once the available width reaches [HOME_EXPANDED_WIDTH_BREAKPOINT], where [HomeScreen] + * switches from a hamburger-triggered modal drawer to an always-visible permanent drawer. + */ +internal fun isHomeDrawerExpandedWidth(width: Dp): Boolean = width >= HOME_EXPANDED_WIDTH_BREAKPOINT diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt new file mode 100644 index 00000000..39175cf7 --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.home + +import androidx.compose.ui.unit.dp +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class HomeWidthBreakpointTest { + + @Test + fun isHomeDrawerExpandedWidth_belowBreakpoint_isFalse() { + assertFalse(isHomeDrawerExpandedWidth(600.dp)) + assertFalse(isHomeDrawerExpandedWidth(839.dp)) + } + + @Test + fun isHomeDrawerExpandedWidth_atOrAboveBreakpoint_isTrue() { + assertTrue(isHomeDrawerExpandedWidth(840.dp)) + assertTrue(isHomeDrawerExpandedWidth(1200.dp)) + } +} From 8a31da10eb9c879396c238c90f06354241f23f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:32:15 +0300 Subject: [PATCH 02/73] Add HomeScreen with adaptive navigation drawer around GroupListScreen --- .../app/feature/home/HomeDestination.kt | 28 +++++ .../reference/app/feature/home/HomeScreen.kt | 103 ++++++++++++++++++ .../app/feature/home/HomeScreenTest.kt | 69 ++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt new file mode 100644 index 00000000..498fec44 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.home + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home +import androidx.compose.ui.graphics.vector.ImageVector + +/** + * A top-level destination reachable from [HomeScreen]'s navigation drawer. `Households` is the only + * entry today; adding a second destination later is a new enum entry, not a rewrite. + */ +enum class HomeDestination(val label: String, val icon: ImageVector) { + Households(label = "Households", icon = Icons.Filled.Home) +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt new file mode 100644 index 00000000..6a0612d0 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.home + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ModalDrawerSheet +import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.NavigationDrawerItem +import androidx.compose.material3.PermanentDrawerSheet +import androidx.compose.material3.PermanentNavigationDrawer +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberDrawerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import dev.ohs.player.reference.app.feature.group.list.GroupListScreen +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit) { + var selectedDestination by remember { mutableStateOf(HomeDestination.Households) } + val drawerState = rememberDrawerState(DrawerValue.Closed) + val scope = rememberCoroutineScope() + + BoxWithConstraints { + val isExpandedWidth = isHomeDrawerExpandedWidth(maxWidth) + + val drawerItems: @Composable () -> Unit = { + HomeDestination.entries.forEach { destination -> + NavigationDrawerItem( + label = { Text(destination.label) }, + icon = { Icon(destination.icon, contentDescription = null) }, + selected = destination == selectedDestination, + onClick = { + selectedDestination = destination + if (!isExpandedWidth) scope.launch { drawerState.close() } + }, + ) + } + } + + val content: @Composable () -> Unit = { + when (selectedDestination) { + HomeDestination.Households -> + GroupListScreen(onGroupClick = onGroupClick, onDataCaptureClick = onDataCaptureClick) + } + } + + if (isExpandedWidth) { + PermanentNavigationDrawer(drawerContent = { PermanentDrawerSheet { drawerItems() } }) { + content() + } + } else { + ModalNavigationDrawer( + drawerState = drawerState, + drawerContent = { ModalDrawerSheet { drawerItems() } }, + ) { + Scaffold( + topBar = { + TopAppBar( + title = { Text(selectedDestination.label) }, + navigationIcon = { + IconButton(onClick = { scope.launch { drawerState.open() } }) { + Icon(Icons.Filled.Menu, contentDescription = "Open navigation menu") + } + }, + ) + } + ) { padding -> + Box(modifier = Modifier.padding(padding)) { content() } + } + } + } + } +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt new file mode 100644 index 00000000..7397e5fe --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.home + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.runComposeUiTest +import dev.ohs.player.library.registry.LocalViewRegistry +import dev.ohs.player.reference.app.buildAppViewRegistry +import dev.ohs.player.reference.app.data.di.repositoryModule +import dev.ohs.player.reference.app.data.di.viewModelModule +import dev.ohs.player.reference.app.data.repository.FhirRepository +import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertTrue +import org.koin.core.context.startKoin +import org.koin.core.context.stopKoin +import org.koin.dsl.module + +@OptIn(ExperimentalTestApi::class) +class HomeScreenTest { + + @BeforeTest + fun setUp() { + startKoin { + modules( + module { single { InMemorySampleFhirRepository() } }, + repositoryModule, + viewModelModule, + ) + } + } + + @AfterTest fun tearDown() = stopKoin() + + @Test + fun homeScreen_defaultsToHouseholdsContentWithDrawerItemVisible() = runComposeUiTest { + val registry = buildAppViewRegistry() + setContent { + CompositionLocalProvider(LocalViewRegistry provides registry) { + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } + } + } + + // GroupListScreen's own empty state, proving it's the active content by default. + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("No households").fetchSemanticsNodes().isNotEmpty() + } + // The drawer's "Households" item (and/or GroupListScreen's own title) should also be present. + assertTrue(onAllNodesWithText("Households").fetchSemanticsNodes().isNotEmpty()) + } +} From 24848d10d2830e1fd1d5e892e4cb9fcc43d7ab32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:37:09 +0300 Subject: [PATCH 03/73] Make HomeScreen the app's start destination --- .../kotlin/dev/ohs/player/reference/app/App.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index cd57fc18..74eeaa9c 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -25,8 +25,8 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import androidx.savedstate.read import dev.ohs.player.library.registry.LocalViewRegistry -import dev.ohs.player.reference.app.feature.group.list.GroupListScreen import dev.ohs.player.reference.app.feature.group.profile.GroupProfileScreen +import dev.ohs.player.reference.app.feature.home.HomeScreen import dev.ohs.player.reference.app.feature.patient.profile.PatientProfileScreen import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireHostScreen import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireIds @@ -40,11 +40,11 @@ fun App() { CompositionLocalProvider(LocalViewRegistry provides registry) { OhsPlayerTheme { val navController = rememberNavController() - NavHost(navController = navController, startDestination = "groupList") { + NavHost(navController = navController, startDestination = "home") { - // Screen 1: Household list - composable("groupList") { - GroupListScreen( + // Screen 1: Home (adaptive navigation drawer shell around the household list) + composable("home") { + HomeScreen( onGroupClick = { id -> navController.navigate("groupProfile/$id") }, onDataCaptureClick = { navController.navigate( From f59e680b5d1fec3c03b94ac322ca972bb46621b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 15 Jul 2026 13:27:50 +0300 Subject: [PATCH 04/73] Split home drawer into Registers and bottom Sync now sections --- .../dev/ohs/player/reference/app/App.kt | 2 + .../reference/app/feature/home/HomeScreen.kt | 59 +++++++++++-- .../app/feature/home/HomeScreenTest.kt | 84 ++++++++++++++++++- 3 files changed, 135 insertions(+), 10 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index 74eeaa9c..ecb46dc0 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -51,6 +51,8 @@ fun App() { questionnaireHostRoute(questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION) ) }, + onSyncNowClick = {}, + lastSyncedAt = null, ) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index 6a0612d0..95572f70 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -17,13 +17,19 @@ package dev.ohs.player.reference.app.feature.home import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.NavigationDrawerItem @@ -40,12 +46,18 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import dev.ohs.player.reference.app.feature.group.list.GroupListScreen import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun HomeScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit) { +fun HomeScreen( + onGroupClick: (String) -> Unit, + onDataCaptureClick: () -> Unit, + onSyncNowClick: () -> Unit, + lastSyncedAt: String?, +) { var selectedDestination by remember { mutableStateOf(HomeDestination.Households) } val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope() @@ -53,15 +65,48 @@ fun HomeScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit) { BoxWithConstraints { val isExpandedWidth = isHomeDrawerExpandedWidth(maxWidth) + fun closeDrawerIfCompact() { + if (!isExpandedWidth) scope.launch { drawerState.close() } + } + val drawerItems: @Composable () -> Unit = { - HomeDestination.entries.forEach { destination -> + Column(modifier = Modifier.fillMaxHeight()) { + Text( + text = "Registers", + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(start = 28.dp, top = 16.dp, bottom = 8.dp), + ) + HomeDestination.entries.forEach { destination -> + NavigationDrawerItem( + label = { Text(destination.label) }, + icon = { Icon(destination.icon, contentDescription = null) }, + selected = destination == selectedDestination, + onClick = { + selectedDestination = destination + closeDrawerIfCompact() + }, + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + HorizontalDivider() + if (lastSyncedAt != null) { + Text( + text = "Last synced: $lastSyncedAt", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(start = 28.dp, top = 8.dp), + ) + } NavigationDrawerItem( - label = { Text(destination.label) }, - icon = { Icon(destination.icon, contentDescription = null) }, - selected = destination == selectedDestination, + label = { Text("Sync now") }, + icon = { Icon(Icons.Filled.Refresh, contentDescription = null) }, + selected = false, onClick = { - selectedDestination = destination - if (!isExpandedWidth) scope.launch { drawerState.close() } + onSyncNowClick() + closeDrawerIfCompact() }, ) } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index 7397e5fe..f86c1d75 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -19,6 +19,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick import androidx.compose.ui.test.runComposeUiTest import dev.ohs.player.library.registry.LocalViewRegistry import dev.ohs.player.reference.app.buildAppViewRegistry @@ -29,6 +31,7 @@ import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertTrue import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -51,11 +54,18 @@ class HomeScreenTest { @AfterTest fun tearDown() = stopKoin() @Test - fun homeScreen_defaultsToHouseholdsContentWithDrawerItemVisible() = runComposeUiTest { + fun homeScreen_defaultsToHouseholdsContentWithDrawerSectionsVisible() = runComposeUiTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onSyncNowClick = {}, + lastSyncedAt = null, + ) + } } } @@ -63,7 +73,75 @@ class HomeScreenTest { waitUntil(timeoutMillis = 5_000L) { onAllNodesWithText("No households").fetchSemanticsNodes().isNotEmpty() } - // The drawer's "Households" item (and/or GroupListScreen's own title) should also be present. + assertTrue(onAllNodesWithText("Registers").fetchSemanticsNodes().isNotEmpty()) assertTrue(onAllNodesWithText("Households").fetchSemanticsNodes().isNotEmpty()) + assertTrue(onAllNodesWithText("Sync now").fetchSemanticsNodes().isNotEmpty()) + } + + @Test + fun homeScreen_tappingSyncNow_invokesCallback() = runComposeUiTest { + val registry = buildAppViewRegistry() + var syncClicked = false + setContent { + CompositionLocalProvider(LocalViewRegistry provides registry) { + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onSyncNowClick = { syncClicked = true }, + lastSyncedAt = null, + ) + } + } + } + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sync now").fetchSemanticsNodes().isNotEmpty() + } + onNodeWithText("Sync now").performClick() + assertTrue(syncClicked) + } + + @Test + fun homeScreen_lastSyncedAtNull_hidesLastSyncedText() = runComposeUiTest { + val registry = buildAppViewRegistry() + setContent { + CompositionLocalProvider(LocalViewRegistry provides registry) { + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onSyncNowClick = {}, + lastSyncedAt = null, + ) + } + } + } + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sync now").fetchSemanticsNodes().isNotEmpty() + } + assertEquals(0, onAllNodesWithText("Last synced", substring = true).fetchSemanticsNodes().size) + } + + @Test + fun homeScreen_lastSyncedAtProvided_showsLastSyncedText() = runComposeUiTest { + val registry = buildAppViewRegistry() + setContent { + CompositionLocalProvider(LocalViewRegistry provides registry) { + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onSyncNowClick = {}, + lastSyncedAt = "2026-07-15 10:00", + ) + } + } + } + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Last synced: 2026-07-15 10:00").fetchSemanticsNodes().isNotEmpty() + } } } From 14907d988167d9fa87228afd53e9f45318b9817b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 22 Jul 2026 04:13:42 +0300 Subject: [PATCH 05/73] Configure remote FHIR server on all platform entry points --- .../dev/ohs/player/reference/app/OhsPlayerApplication.kt | 8 +++++++- .../dev/ohs/player/reference/app/MainViewController.kt | 7 ++++++- .../jvmMain/kotlin/dev/ohs/player/reference/app/main.kt | 8 +++++++- .../webMain/kotlin/dev/ohs/player/reference/app/main.kt | 7 ++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt index d99eb15e..68a12780 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt @@ -19,6 +19,7 @@ import android.app.Application import dev.ohs.fhir.FhirEngine import dev.ohs.fhir.FhirEngineConfiguration import dev.ohs.fhir.FhirEngineProvider +import dev.ohs.fhir.ServerConfiguration import dev.ohs.fhir.datacapture.DataCapture import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module @@ -26,7 +27,12 @@ import org.koin.dsl.module class OhsPlayerApplication : Application() { override fun onCreate() { super.onCreate() - FhirEngineProvider.init(FhirEngineConfiguration(), applicationContext) + FhirEngineProvider.init( + FhirEngineConfiguration( + serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4") + ), + applicationContext, + ) initKoin(module { single { FhirEngineProvider.getInstance(applicationContext) } }) DataCapture.initialize(applicationContext) } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt index b0a9609a..23006c48 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt @@ -19,11 +19,16 @@ import androidx.compose.ui.window.ComposeUIViewController import dev.ohs.fhir.FhirEngine import dev.ohs.fhir.FhirEngineConfiguration import dev.ohs.fhir.FhirEngineProvider +import dev.ohs.fhir.ServerConfiguration import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module fun MainViewController() = run { - FhirEngineProvider.init(FhirEngineConfiguration()) + FhirEngineProvider.init( + FhirEngineConfiguration( + serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4") + ) + ) initKoin(module { single { FhirEngineProvider.getInstance() } }) ComposeUIViewController { App() } } diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index 288b297e..5cc08926 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.window.application import dev.ohs.fhir.FhirEngine import dev.ohs.fhir.FhirEngineConfiguration import dev.ohs.fhir.FhirEngineProvider +import dev.ohs.fhir.ServerConfiguration import dev.ohs.player.reference.app.data.di.initKoin import java.io.File import org.koin.dsl.module @@ -27,7 +28,12 @@ import org.koin.dsl.module fun main() = application { val userHome = System.getProperty("user.home").orEmpty().ifBlank { "." } val storageDirectory = File(userHome, ".ohs-player-reference-app").absolutePath - FhirEngineProvider.init(FhirEngineConfiguration(storageDirectory = storageDirectory)) + FhirEngineProvider.init( + FhirEngineConfiguration( + storageDirectory = storageDirectory, + serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4"), + ) + ) initKoin(module { single { FhirEngineProvider.getInstance() } }) Window(onCloseRequest = ::exitApplication, title = "OHS Player Reference App") { App() } } diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt index 23398291..47af9c42 100644 --- a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -20,12 +20,17 @@ import androidx.compose.ui.window.ComposeViewport import dev.ohs.fhir.FhirEngine import dev.ohs.fhir.FhirEngineConfiguration import dev.ohs.fhir.FhirEngineProvider +import dev.ohs.fhir.ServerConfiguration import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module @OptIn(ExperimentalComposeUiApi::class) fun main() { - FhirEngineProvider.init(FhirEngineConfiguration()) + FhirEngineProvider.init( + FhirEngineConfiguration( + serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4") + ) + ) initKoin(module { single { FhirEngineProvider.getInstance() } }) ComposeViewport { App() } } From a8dbee6c3111af5acee3c511a2ed73443131fdf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 22 Jul 2026 04:15:21 +0300 Subject: [PATCH 06/73] Add AppFhirSyncTask and SyncNowUseCase for one-time FHIR sync --- .../app/data/sync/AppFhirSyncTask.kt | 58 +++++++++++++++++++ .../app/data/sync/InMemoryTimestampContext.kt | 39 +++++++++++++ .../reference/app/data/sync/SyncNowUseCase.kt | 35 +++++++++++ .../data/sync/InMemoryTimestampContextTest.kt | 53 +++++++++++++++++ 4 files changed, 185 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt new file mode 100644 index 00000000..7e20dcc1 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.FhirEngine +import dev.ohs.fhir.model.r4.terminologies.ResourceType +import dev.ohs.fhir.sync.AcceptLocalConflictResolver +import dev.ohs.fhir.sync.ConflictResolver +import dev.ohs.fhir.sync.DownloadWorkManager +import dev.ohs.fhir.sync.FhirSyncTask +import dev.ohs.fhir.sync.download.ResourceParamsBasedDownloadWorkManager +import dev.ohs.fhir.sync.download.ResourceSearchParams +import dev.ohs.fhir.sync.upload.HttpCreateMethod +import dev.ohs.fhir.sync.upload.HttpUpdateMethod +import dev.ohs.fhir.sync.upload.UploadStrategy + +/** + * Resource types downloaded on every sync, with their search parameters. Empty parameters mean + * "everything of this type, since the last sync". Adding a resource type later is a new map entry. + */ +private val SYNC_RESOURCE_PARAMS: ResourceSearchParams = + mapOf(ResourceType.Patient to emptyMap(), ResourceType.Group to emptyMap()) + +/** + * This app's [FhirSyncTask]: downloads [SYNC_RESOURCE_PARAMS], resolves conflicts in favor of the + * local change, and uploads pending local changes as a single bundle request. + */ +class AppFhirSyncTask(private val fhirEngine: FhirEngine) : FhirSyncTask { + private val timestampContext = InMemoryTimestampContext() + + override fun getFhirEngine(): FhirEngine = fhirEngine + + override fun getDownloadWorkManager(): DownloadWorkManager = + ResourceParamsBasedDownloadWorkManager(SYNC_RESOURCE_PARAMS, timestampContext) + + override fun getConflictResolver(): ConflictResolver = AcceptLocalConflictResolver + + override fun getUploadStrategy(): UploadStrategy = + UploadStrategy.forBundleRequest( + methodForCreate = HttpCreateMethod.PUT, + methodForUpdate = HttpUpdateMethod.PATCH, + squash = true, + bundleSize = 500, + ) +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt new file mode 100644 index 00000000..3b0d42d3 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.model.r4.terminologies.ResourceType +import dev.ohs.fhir.sync.download.ResourceParamsBasedDownloadWorkManager + +/** + * In-memory implementation of [ResourceParamsBasedDownloadWorkManager.TimestampContext]. Tracks + * per-[ResourceType] download cursors only for the lifetime of the process — a fresh app launch + * re-downloads all resources for each configured type rather than only what changed since the last + * session. The user-visible last-synced timestamp is unaffected: it is tracked separately by + * [dev.ohs.fhir.sync.FhirDataStore]. + */ +class InMemoryTimestampContext : ResourceParamsBasedDownloadWorkManager.TimestampContext { + private val lastUpdatedByResourceType = mutableMapOf() + + override suspend fun saveLastUpdatedTimestamp(resourceType: ResourceType, timestamp: String?) { + if (timestamp != null) { + lastUpdatedByResourceType[resourceType] = timestamp + } + } + + override suspend fun getLasUpdateTimestamp(resourceType: ResourceType): String? = + lastUpdatedByResourceType[resourceType] +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt new file mode 100644 index 00000000..372c6682 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.sync.FhirSyncTask +import dev.ohs.fhir.sync.SyncJobStatus +import dev.ohs.fhir.sync.runSync + +/** + * Triggers a one-time sync and returns its terminal result. A dedicated interface (rather than + * calling [runSync] directly) so [dev.ohs.player.reference.app.feature.home.HomeViewModel] can be + * unit-tested against a fake — [runSync] is a top-level extension function that reaches into the + * library's global `FhirEngineProvider` singleton, so no [FhirSyncTask] instance, real or fake, + * makes it independently testable. + */ +fun interface SyncNowUseCase { + suspend fun invoke(): SyncJobStatus +} + +class RunSyncNowUseCase(private val fhirSyncTask: FhirSyncTask) : SyncNowUseCase { + override suspend fun invoke(): SyncJobStatus = fhirSyncTask.runSync(taskName = null) {} +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt new file mode 100644 index 00000000..602048a7 --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.model.r4.terminologies.ResourceType +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlinx.coroutines.test.runTest + +class InMemoryTimestampContextTest { + + @Test + fun getLasUpdateTimestamp_beforeAnySave_isNull() = runTest { + val context = InMemoryTimestampContext() + + assertNull(context.getLasUpdateTimestamp(ResourceType.Patient)) + } + + @Test + fun saveThenGet_roundTripsPerResourceType() = runTest { + val context = InMemoryTimestampContext() + + context.saveLastUpdatedTimestamp(ResourceType.Patient, "2026-07-15T10:00:00Z") + context.saveLastUpdatedTimestamp(ResourceType.Group, "2026-07-14T09:00:00Z") + + assertEquals("2026-07-15T10:00:00Z", context.getLasUpdateTimestamp(ResourceType.Patient)) + assertEquals("2026-07-14T09:00:00Z", context.getLasUpdateTimestamp(ResourceType.Group)) + } + + @Test + fun saveWithNullTimestamp_doesNotOverwriteExistingValue() = runTest { + val context = InMemoryTimestampContext() + context.saveLastUpdatedTimestamp(ResourceType.Patient, "2026-07-15T10:00:00Z") + + context.saveLastUpdatedTimestamp(ResourceType.Patient, null) + + assertEquals("2026-07-15T10:00:00Z", context.getLasUpdateTimestamp(ResourceType.Patient)) + } +} From 13ce3646d3d54f83a8a5dc46cb41f5bbc6bf7554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 22 Jul 2026 04:16:39 +0300 Subject: [PATCH 07/73] Add HomeViewModel driving sync state --- .../app/feature/home/HomeViewModel.kt | 92 ++++++++++++ .../app/feature/home/HomeViewModelTest.kt | 132 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt new file mode 100644 index 00000000..75922982 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dev.ohs.fhir.sync.FhirDataStore +import dev.ohs.fhir.sync.SyncJobStatus +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import kotlin.time.Instant +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Job +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.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime + +data class HomeUiState( + val isSyncing: Boolean = false, + val lastSyncedAt: String? = null, + val syncError: String? = null, +) + +class HomeViewModel( + private val syncNowUseCase: SyncNowUseCase, + private val fhirDataStore: FhirDataStore, +) : ViewModel() { + private val _uiState = MutableStateFlow(HomeUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + viewModelScope.launch { + val lastSyncedAt = fhirDataStore.readLastSyncTimestamp()?.toDisplayString() + _uiState.update { it.copy(lastSyncedAt = lastSyncedAt) } + } + } + + /** + * Triggers a one-time sync. Returns `null` without starting a new sync if one is already in + * progress; otherwise returns the launched [Job] (primarily so tests can `join()` it). + */ + fun syncNow(): Job? { + if (_uiState.value.isSyncing) return null + _uiState.update { it.copy(isSyncing = true, syncError = null) } + return viewModelScope.launch { + val result = + try { + syncNowUseCase.invoke() + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + SyncJobStatus.Failed() + } + when (result) { + is SyncJobStatus.Succeeded -> { + val lastSyncedAt = fhirDataStore.readLastSyncTimestamp()?.toDisplayString() + _uiState.update { it.copy(isSyncing = false, lastSyncedAt = lastSyncedAt) } + } + else -> + _uiState.update { + it.copy(isSyncing = false, syncError = "Sync failed. Please try again.") + } + } + } + } + + fun clearSyncError() { + _uiState.update { it.copy(syncError = null) } + } +} + +private fun Instant.toDisplayString(): String { + val local = toLocalDateTime(TimeZone.currentSystemDefault()) + return "${local.date} ${local.hour.toString().padStart(2, '0')}:" + + local.minute.toString().padStart(2, '0') +} diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt new file mode 100644 index 00000000..b52b8e5f --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.home + +import dev.ohs.fhir.sync.FhirDataStore +import dev.ohs.fhir.sync.SyncJobStatus +import dev.ohs.fhir.sync.createDataStore +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import java.nio.file.Files +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlinx.coroutines.test.runTest + +private class FakeSyncNowUseCase( + private val fhirDataStore: FhirDataStore, + private val result: suspend () -> SyncJobStatus, +) : SyncNowUseCase { + var invocationCount = 0 + private set + + override suspend fun invoke(): SyncJobStatus { + invocationCount++ + val status = result() + // Mirrors FhirSynchronizer's real behavior of persisting the timestamp on every terminal + // outcome, so the ViewModel's re-read-after-sync behavior is exercised realistically. + fhirDataStore.writeLastSyncTimestamp(status.timestamp) + return status + } +} + +class HomeViewModelTest { + + private fun newFhirDataStore(): FhirDataStore { + val path = Files.createTempFile("home-viewmodel-test", ".preferences_pb").toString() + return FhirDataStore(createDataStore { path }) + } + + @Test + fun initialState_withNoPriorSync_hasNoLastSyncedAt() = runTest { + val viewModel = + HomeViewModel( + FakeSyncNowUseCase(newFhirDataStore()) { SyncJobStatus.Succeeded() }, + newFhirDataStore(), + ) + + assertNull(viewModel.uiState.value.lastSyncedAt) + } + + @Test + fun syncNow_onSuccess_clearsIsSyncingAndPopulatesLastSyncedAt() = runTest { + val fhirDataStore = newFhirDataStore() + val viewModel = + HomeViewModel(FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Succeeded() }, fhirDataStore) + + viewModel.syncNow()?.join() + + val state = viewModel.uiState.value + assertEquals(false, state.isSyncing) + assertNull(state.syncError) + assertNotNull(state.lastSyncedAt) + } + + @Test + fun syncNow_onFailure_clearsIsSyncingAndSetsSyncError() = runTest { + val fhirDataStore = newFhirDataStore() + val viewModel = + HomeViewModel(FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Failed() }, fhirDataStore) + + viewModel.syncNow()?.join() + + val state = viewModel.uiState.value + assertEquals(false, state.isSyncing) + assertEquals("Sync failed. Please try again.", state.syncError) + } + + @Test + fun syncNow_whenUseCaseThrows_setsSyncError() = runTest { + val fhirDataStore = newFhirDataStore() + val viewModel = + HomeViewModel( + FakeSyncNowUseCase(fhirDataStore) { throw RuntimeException("network down") }, + fhirDataStore, + ) + + viewModel.syncNow()?.join() + + val state = viewModel.uiState.value + assertEquals(false, state.isSyncing) + assertEquals("Sync failed. Please try again.", state.syncError) + } + + @Test + fun syncNow_whileAlreadySyncing_doesNotStartASecondSync() = runTest { + val fhirDataStore = newFhirDataStore() + val fake = FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Succeeded() } + val viewModel = HomeViewModel(fake, fhirDataStore) + + val first = viewModel.syncNow() + val second = viewModel.syncNow() + first?.join() + + assertNull(second) + assertEquals(1, fake.invocationCount) + } + + @Test + fun clearSyncError_removesTheErrorMessage() = runTest { + val fhirDataStore = newFhirDataStore() + val viewModel = + HomeViewModel(FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Failed() }, fhirDataStore) + viewModel.syncNow()?.join() + + viewModel.clearSyncError() + + assertNull(viewModel.uiState.value.syncError) + } +} From 5b0a2c52ad7c218a9f3e2f4a8b19323bd7146dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 22 Jul 2026 04:20:19 +0300 Subject: [PATCH 08/73] Wire Sync now to HomeViewModel and real one-time FHIR sync --- .../app/feature/home/HomeScreenTest.kt | 102 +++++++++--------- 1 file changed, 50 insertions(+), 52 deletions(-) rename ohs-player-reference-app/src/{commonTest => jvmTest}/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt (57%) diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt similarity index 57% rename from ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt rename to ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index f86c1d75..0cead0e2 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -18,33 +18,51 @@ package dev.ohs.player.reference.app.feature.home import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onAllNodesWithContentDescription import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.runComposeUiTest +import dev.ohs.fhir.sync.FhirDataStore +import dev.ohs.fhir.sync.SyncJobStatus +import dev.ohs.fhir.sync.createDataStore import dev.ohs.player.library.registry.LocalViewRegistry import dev.ohs.player.reference.app.buildAppViewRegistry import dev.ohs.player.reference.app.data.di.repositoryModule import dev.ohs.player.reference.app.data.di.viewModelModule import dev.ohs.player.reference.app.data.repository.FhirRepository import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import java.nio.file.Files import kotlin.test.AfterTest -import kotlin.test.BeforeTest import kotlin.test.Test -import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlinx.coroutines.CompletableDeferred import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.dsl.module +private class FakeHomeScreenSyncNowUseCase(private val result: suspend () -> SyncJobStatus) : + SyncNowUseCase { + override suspend fun invoke(): SyncJobStatus = result() +} + @OptIn(ExperimentalTestApi::class) class HomeScreenTest { - @BeforeTest - fun setUp() { + private fun newFhirDataStore(): FhirDataStore { + val path = Files.createTempFile("home-screen-test", ".preferences_pb").toString() + return FhirDataStore(createDataStore { path }) + } + + private fun startTestKoin(syncNowUseCase: SyncNowUseCase) { startKoin { modules( - module { single { InMemorySampleFhirRepository() } }, + module { + single { InMemorySampleFhirRepository() } + single { newFhirDataStore() } + single { syncNowUseCase } + }, repositoryModule, viewModelModule, ) @@ -55,21 +73,14 @@ class HomeScreenTest { @Test fun homeScreen_defaultsToHouseholdsContentWithDrawerSectionsVisible() = runComposeUiTest { + startTestKoin(FakeHomeScreenSyncNowUseCase { SyncJobStatus.Succeeded() }) val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { - HomeScreen( - onGroupClick = {}, - onDataCaptureClick = {}, - onSyncNowClick = {}, - lastSyncedAt = null, - ) - } + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } } } - // GroupListScreen's own empty state, proving it's the active content by default. waitUntil(timeoutMillis = 5_000L) { onAllNodesWithText("No households").fetchSemanticsNodes().isNotEmpty() } @@ -79,19 +90,19 @@ class HomeScreenTest { } @Test - fun homeScreen_tappingSyncNow_invokesCallback() = runComposeUiTest { + fun tappingSyncNow_showsProgressIndicatorWhileSyncPending() = runComposeUiTest { + val syncStarted = CompletableDeferred() + val releaseSyncResult = CompletableDeferred() + startTestKoin( + FakeHomeScreenSyncNowUseCase { + syncStarted.complete(Unit) + releaseSyncResult.await() + } + ) val registry = buildAppViewRegistry() - var syncClicked = false setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { - HomeScreen( - onGroupClick = {}, - onDataCaptureClick = {}, - onSyncNowClick = { syncClicked = true }, - lastSyncedAt = null, - ) - } + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } } } @@ -99,49 +110,36 @@ class HomeScreenTest { onAllNodesWithText("Sync now").fetchSemanticsNodes().isNotEmpty() } onNodeWithText("Sync now").performClick() - assertTrue(syncClicked) - } - @Test - fun homeScreen_lastSyncedAtNull_hidesLastSyncedText() = runComposeUiTest { - val registry = buildAppViewRegistry() - setContent { - CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { - HomeScreen( - onGroupClick = {}, - onDataCaptureClick = {}, - onSyncNowClick = {}, - lastSyncedAt = null, - ) - } - } + waitUntil(timeoutMillis = 5_000L) { syncStarted.isCompleted } + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithContentDescription("Sync in progress").fetchSemanticsNodes().isNotEmpty() } + releaseSyncResult.complete(SyncJobStatus.Succeeded()) + waitUntil(timeoutMillis = 5_000L) { - onAllNodesWithText("Sync now").fetchSemanticsNodes().isNotEmpty() + onAllNodesWithContentDescription("Sync in progress").fetchSemanticsNodes().isEmpty() } - assertEquals(0, onAllNodesWithText("Last synced", substring = true).fetchSemanticsNodes().size) } @Test - fun homeScreen_lastSyncedAtProvided_showsLastSyncedText() = runComposeUiTest { + fun tappingSyncNow_onFailure_showsSnackbarMessage() = runComposeUiTest { + startTestKoin(FakeHomeScreenSyncNowUseCase { SyncJobStatus.Failed() }) val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { - HomeScreen( - onGroupClick = {}, - onDataCaptureClick = {}, - onSyncNowClick = {}, - lastSyncedAt = "2026-07-15 10:00", - ) - } + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } } } waitUntil(timeoutMillis = 5_000L) { - onAllNodesWithText("Last synced: 2026-07-15 10:00").fetchSemanticsNodes().isNotEmpty() + onAllNodesWithText("Sync now").fetchSemanticsNodes().isNotEmpty() + } + onNodeWithText("Sync now").performClick() + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sync failed. Please try again.").fetchSemanticsNodes().isNotEmpty() } } } From 089d757a6282dde825a9115de877f28f69120dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 22 Jul 2026 04:20:46 +0300 Subject: [PATCH 09/73] Wire HomeScreen and DI to HomeViewModel --- .../dev/ohs/player/reference/app/App.kt | 2 - .../player/reference/app/data/di/AppModule.kt | 13 +++++ .../player/reference/app/data/di/InitKoin.kt | 1 + .../reference/app/feature/home/HomeScreen.kt | 51 +++++++++++++++---- 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index ecb46dc0..74eeaa9c 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -51,8 +51,6 @@ fun App() { questionnaireHostRoute(questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION) ) }, - onSyncNowClick = {}, - lastSyncedAt = null, ) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt index bf47ce14..a59bc9af 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt @@ -15,12 +15,18 @@ */ package dev.ohs.player.reference.app.data.di +import dev.ohs.fhir.FhirEngineProvider +import dev.ohs.fhir.sync.FhirSyncTask import dev.ohs.player.reference.app.data.repository.FhirEngineRepository import dev.ohs.player.reference.app.data.repository.FhirRepository import dev.ohs.player.reference.app.data.repository.GroupRepository import dev.ohs.player.reference.app.data.repository.PatientRepository +import dev.ohs.player.reference.app.data.sync.AppFhirSyncTask +import dev.ohs.player.reference.app.data.sync.RunSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import dev.ohs.player.reference.app.feature.group.list.GroupListViewModel import dev.ohs.player.reference.app.feature.group.profile.GroupProfileViewModel +import dev.ohs.player.reference.app.feature.home.HomeViewModel import dev.ohs.player.reference.app.feature.patient.list.PatientListViewModel import dev.ohs.player.reference.app.feature.patient.profile.PatientProfileViewModel import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireHostViewModel @@ -49,6 +55,12 @@ internal val repositoryModule = module { internal val serviceModule = module { factory { QuestionnaireService(get()) } } +internal val syncModule = module { + single { FhirEngineProvider.getFhirDataStore() } + single { AppFhirSyncTask(get()) } + single { RunSyncNowUseCase(get()) } +} + internal val viewModelModule = module { viewModel { PatientListViewModel(get()) } viewModel { (patientId: String) -> PatientProfileViewModel(patientId, get()) } @@ -57,4 +69,5 @@ internal val viewModelModule = module { viewModel { (questionnaireId: String, launchContext: QuestionnaireLaunchContext) -> QuestionnaireHostViewModel(questionnaireId, launchContext, get()) } + viewModel { HomeViewModel(get(), get()) } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt index 7f4aef16..aa2d7576 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt @@ -29,6 +29,7 @@ fun initKoin(platformModule: Module) { fhirEngineRepositoryModule, repositoryModule, serviceModule, + syncModule, viewModelModule, ) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index 95572f70..44606650 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -21,9 +21,11 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider @@ -36,31 +38,44 @@ import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.PermanentDrawerSheet import androidx.compose.material3.PermanentNavigationDrawer import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.ohs.player.reference.app.feature.group.list.GroupListScreen import kotlinx.coroutines.launch +import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun HomeScreen( - onGroupClick: (String) -> Unit, - onDataCaptureClick: () -> Unit, - onSyncNowClick: () -> Unit, - lastSyncedAt: String?, -) { +fun HomeScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit) { + val homeViewModel: HomeViewModel = koinViewModel() + val uiState by homeViewModel.uiState.collectAsStateWithLifecycle() + var selectedDestination by remember { mutableStateOf(HomeDestination.Households) } val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope() + val snackbarHostState = remember { SnackbarHostState() } + + LaunchedEffect(uiState.syncError) { + uiState.syncError?.let { message -> + snackbarHostState.showSnackbar(message) + homeViewModel.clearSyncError() + } + } BoxWithConstraints { val isExpandedWidth = isHomeDrawerExpandedWidth(maxWidth) @@ -92,7 +107,7 @@ fun HomeScreen( Spacer(modifier = Modifier.weight(1f)) HorizontalDivider() - if (lastSyncedAt != null) { + uiState.lastSyncedAt?.let { lastSyncedAt -> Text( text = "Last synced: $lastSyncedAt", style = MaterialTheme.typography.bodySmall, @@ -103,10 +118,21 @@ fun HomeScreen( NavigationDrawerItem( label = { Text("Sync now") }, icon = { Icon(Icons.Filled.Refresh, contentDescription = null) }, + badge = { + if (uiState.isSyncing) { + CircularProgressIndicator( + modifier = + Modifier.size(16.dp).semantics { contentDescription = "Sync in progress" }, + strokeWidth = 2.dp, + ) + } + }, selected = false, onClick = { - onSyncNowClick() - closeDrawerIfCompact() + if (!uiState.isSyncing) { + homeViewModel.syncNow() + closeDrawerIfCompact() + } }, ) } @@ -121,7 +147,9 @@ fun HomeScreen( if (isExpandedWidth) { PermanentNavigationDrawer(drawerContent = { PermanentDrawerSheet { drawerItems() } }) { - content() + Scaffold(snackbarHost = { SnackbarHost(snackbarHostState) }) { padding -> + Box(modifier = Modifier.padding(padding)) { content() } + } } } else { ModalNavigationDrawer( @@ -138,7 +166,8 @@ fun HomeScreen( } }, ) - } + }, + snackbarHost = { SnackbarHost(snackbarHostState) }, ) { padding -> Box(modifier = Modifier.padding(padding)) { content() } } From 476cb45fc392c085248b17089c6e96db469b20cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:10:35 +0300 Subject: [PATCH 10/73] Add auth dependencies and generateAuthConfig codegen task --- gradle/libs.versions.toml | 21 +++- local.properties.sample | 33 ++++++ ohs-player-reference-app/build.gradle.kts | 123 ++++++++++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 local.properties.sample diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 587ee760..c94b66f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,6 +16,11 @@ fhirDataCapture = "2.0.0-alpha02" junit = "4.13.2" kermit = "2.1.0" koinBom = "4.1.1" +ktor = "3.2.3" +ksafe = "2.1.3" +kotlincryptoHash = "0.8.0" +androidxBrowser = "1.8.0" +kotlinxBrowser = "0.3" kotlin = "2.3.21" kotlinx-coroutines = "1.11.0" kotlinpoet = "2.3.0" @@ -26,7 +31,7 @@ navigation-compose = "2.9.2" material3 = "1.10.0-alpha05" kotlinxSerializationJson = "1.11.0" spotless = "8.6.0" -ohsFhirEngine = "2.0.0-alpha01" +ohsFhirEngine = "2.0.0-alpha01-local" ohsFhirModel = "1.0.0-beta05" ohsFhirPath = "1.0.0-beta03" kotlinxDatetime = "0.8.0" @@ -70,6 +75,20 @@ ohs-fhir-model = { module = "dev.ohs.fhir:fhir-model", version.ref = "ohsFhirMod ohs-fhir-path = { module = "dev.ohs.fhir:fhir-path", version.ref = "ohsFhirPath" } ionspin-bignum = { module = "com.ionspin.kotlin:bignum", version.ref = "ionspin-bignum" } spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } +ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } +ktor-client-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" } +ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } +ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } +ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } +ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } +ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } +ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" } +ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } +ksafe = { module = "eu.anifantakis:ksafe", version.ref = "ksafe" } +androidx-browser = { module = "androidx.browser:browser", version.ref = "androidxBrowser" } +kotlincrypto-hash-bom = { module = "org.kotlincrypto.hash:bom", version.ref = "kotlincryptoHash" } +kotlincrypto-hash-sha2 = { module = "org.kotlincrypto.hash:sha2" } +kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinxBrowser" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } diff --git a/local.properties.sample b/local.properties.sample new file mode 100644 index 00000000..21418fd5 --- /dev/null +++ b/local.properties.sample @@ -0,0 +1,33 @@ +# --------------------------------------------------------------------------- +# local.properties.sample — copy to `local.properties` and fill in your values. +# `local.properties` is git-ignored. These are read at build time by the +# :ohs-player-reference-app `generateAuthConfig` task. CI may override any of +# these with environment variables of the same name. +# +# PKCE uses a PUBLIC client, so there is NO client secret here. +# --------------------------------------------------------------------------- + +# --- OAuth / OIDC provider --------------------------------------------------- +# Provider-agnostic: the app resolves all endpoints via OIDC discovery at +# {OAUTH_ISSUER}/.well-known/openid-configuration. +# Keycloak: https://host/realms/ (the realm IS part of the issuer) +OAUTH_ISSUER=https://keycloak.example.org/realms/ohs-player +# Public client id (PKCE / public client — no secret). +OAUTH_CLIENT_ID=ohs-player-reference-app +# OAuth scopes (space separated). offline_access yields a refresh token. +OAUTH_SCOPES=openid profile email offline_access + +# --- Redirect URIs (must be registered as Valid Redirect URIs at the provider) --- +# Android + iOS use a custom URI scheme deep link: {SCHEME}://{HOST} +OAUTH_REDIRECT_SCHEME=dev.ohs.player.reference.app +OAUTH_REDIRECT_HOST=auth +# Desktop (JVM) uses a localhost loopback redirect on this port. +# Register as: http://127.0.0.1:8765/callback (and http://localhost:8765/callback) +OAUTH_DESKTOP_REDIRECT_PORT=8765 +# Web (JS/Wasm) uses a full-page redirect back to the app origin. +OAUTH_WEB_REDIRECT_URL=http://localhost:8080/callback + +# --- FHIR server -------------------------------------------------------------- +# Base URL of the remote FHIR server. Requests are authenticated with the +# signed-in session's Bearer token (see FhirBearerAuthenticator). +FHIR_BASE_URL=https://hapi.fhir.org/baseR4 diff --git a/ohs-player-reference-app/build.gradle.kts b/ohs-player-reference-app/build.gradle.kts index 470f3c57..ba2ffd93 100644 --- a/ohs-player-reference-app/build.gradle.kts +++ b/ohs-player-reference-app/build.gradle.kts @@ -52,10 +52,15 @@ kotlin { binaries.executable() } + // expect/actual classes (AuthorizationLauncher) are stable enough for our use. + compilerOptions { freeCompilerArgs.add("-Xexpect-actual-classes") } + sourceSets { androidMain.dependencies { implementation(libs.compose.uiToolingPreview) implementation(libs.androidx.activity.compose) + implementation(libs.ktor.client.okhttp) + implementation(libs.androidx.browser) } commonMain.dependencies { implementation(project(":ohs-player-library")) @@ -81,15 +86,42 @@ kotlin { implementation(libs.ohs.fhir.model) implementation(libs.ohs.fhir.path) implementation(libs.fhir.data.capture) + // Auth: shared OAuth2/PKCE client, secure session storage, SHA-256 for PKCE. + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.auth) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.ksafe) + implementation(project.dependencies.platform(libs.kotlincrypto.hash.bom)) + implementation(libs.kotlincrypto.hash.sha2) } commonTest.dependencies { implementation(libs.kotlin.test) implementation(libs.compose.uiTest) implementation(libs.kotlinx.coroutines.test) + implementation(libs.ktor.client.mock) + } + iosMain.dependencies { implementation(libs.ktor.client.darwin) } + webMain.dependencies { + // :engine's WebWorkerSQLiteDriver worker (androidx.sqlite:sqlite-web) is loaded via + // `new Worker(new URL("sqlite-wasm-worker/worker.js", import.meta.url), { type: "module" })` + // — a bare npm specifier that :engine's own local file: npm dependency can't propagate to + // consumers. Vendoring a matching "sqlite-wasm-worker" npm module here (copied from + // kotlin-fhir-engine's engine/src/webMain/npm/sqlite-wasm-worker/) makes that specifier + // resolve in this app's own build too. + implementation( + npm( + "sqlite-wasm-worker", + layout.projectDirectory.dir("src/webMain/npm/sqlite-wasm-worker").asFile, + ) + ) + implementation(libs.ktor.client.js) + implementation(libs.kotlinx.browser) } jvmMain.dependencies { implementation(compose.desktop.currentOs) implementation(libs.kotlinx.coroutinesSwing) + implementation(libs.ktor.client.cio) } jvmTest.dependencies { implementation(compose.desktop.currentOs) } } @@ -142,6 +174,97 @@ val hasReleaseSigning: Boolean = !keystoreKeyPassword.isNullOrBlank() && !keystoreStorePassword.isNullOrBlank() +// --- OAuth / OIDC config ------------------------------------------------------ +// Provider-agnostic: the app resolves endpoints via OIDC discovery +// ({issuer}/.well-known/openid-configuration). +fun authProp(key: String, default: String): String = + nonBlankEnv(key).orNull ?: localProperties[key]?.takeIf { it.isNotBlank() } ?: default + +// Auth/deployment settings live in local.properties (git-ignored), with env-var overrides for CI. +val localProperties: Map = + providers + .fileContents(rootProject.layout.projectDirectory.file("local.properties")) + .asText + .map { text -> + val props = Properties().apply { load(text.reader()) } + props.stringPropertyNames().associateWith(props::getProperty) + } + .getOrElse(emptyMap()) + +val authConfigOutputDir = layout.buildDirectory.dir("generated/authconfig/commonMain/kotlin") + +val generateAuthConfig = + tasks.register("generateAuthConfig") { + val issuer = authProp("OAUTH_ISSUER", "https://keycloak.example.org/realms/ohs-player") + val clientId = authProp("OAUTH_CLIENT_ID", "ohs-player-reference-app") + val redirectScheme = authProp("OAUTH_REDIRECT_SCHEME", "dev.ohs.player.reference.app") + val redirectHost = authProp("OAUTH_REDIRECT_HOST", "auth") + val webRedirectUrl = authProp("OAUTH_WEB_REDIRECT_URL", "http://localhost:8080/callback") + val desktopPort = authProp("OAUTH_DESKTOP_REDIRECT_PORT", "8765") + val scopes = authProp("OAUTH_SCOPES", "openid profile email offline_access") + val fhirBaseUrl = authProp("FHIR_BASE_URL", "https://hapi.fhir.org/baseR4") + val versionName = releaseVersionName + val versionCode = releaseVersionCode + val outDir = authConfigOutputDir + inputs.property("issuer", issuer) + inputs.property("clientId", clientId) + inputs.property("redirectScheme", redirectScheme) + inputs.property("redirectHost", redirectHost) + inputs.property("webRedirectUrl", webRedirectUrl) + inputs.property("desktopPort", desktopPort) + inputs.property("scopes", scopes) + inputs.property("fhirBaseUrl", fhirBaseUrl) + inputs.property("versionName", versionName) + inputs.property("versionCode", versionCode) + outputs.dir(outDir) + doLast { + val pkgDir = outDir.get().asFile.resolve("dev/ohs/player/reference/app/auth").apply { mkdirs() } + pkgDir + .resolve("GeneratedAuthConfig.kt") + .writeText( + """ + |// Generated by the :ohs-player-reference-app generateAuthConfig task. Do not edit. + |// Values come from local.properties / env vars; see local.properties.sample. + |package dev.ohs.player.reference.app.auth + | + |internal object GeneratedAuthConfig { + | const val ISSUER: String = "$issuer" + | const val CLIENT_ID: String = "$clientId" + | const val REDIRECT_SCHEME: String = "$redirectScheme" + | const val REDIRECT_HOST: String = "$redirectHost" + | const val WEB_REDIRECT_URL: String = "$webRedirectUrl" + | const val DESKTOP_REDIRECT_PORT: Int = $desktopPort + | const val SCOPES: String = "$scopes" + | const val FHIR_BASE_URL: String = "$fhirBaseUrl" + |} + | + """ + .trimMargin() + ) + pkgDir + .resolve("GeneratedAppInfo.kt") + .writeText( + """ + |// Generated by the :ohs-player-reference-app generateAuthConfig task. Do not edit. + |package dev.ohs.player.reference.app.auth + | + |internal object GeneratedAppInfo { + | const val VERSION_NAME: String = "$versionName" + | const val VERSION_CODE: Int = $versionCode + |} + | + """ + .trimMargin() + ) + } + } + +kotlin.sourceSets.named("commonMain") { kotlin.srcDir(authConfigOutputDir) } + +tasks.withType>().configureEach { + dependsOn(generateAuthConfig) +} + android { namespace = "dev.ohs.player.reference.app" compileSdk = libs.versions.android.compileSdk.get().toInt() From 753da4db49f3ecea1dbee8ce4334dfa9d937f829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:20:16 +0300 Subject: [PATCH 11/73] Add auth data models (Session, AuthState, LoginOutcome, ...) Also fixes pre-existing dev.ohs.fhir.* imports (should be dev.ohs.fhir.engine.*) in jvmMain/main.kt and several jvmTest/commonTest files that were blocking the jvmTest source set from compiling at all, unrelated to this feature but needed to unblock test verification for it. --- .../player/reference/app/auth/AuthModels.kt | 121 ++++++++++++++++++ .../reference/app/auth/AuthModelsTest.kt | 80 ++++++++++++ .../InMemorySampleFhirRepository.kt | 2 +- .../dev/ohs/player/reference/app/main.kt | 8 +- .../repository/FhirEngineRepositoryTest.kt | 6 +- .../app/feature/home/HomeScreenTest.kt | 6 +- .../app/feature/home/HomeViewModelTest.kt | 6 +- 7 files changed, 215 insertions(+), 14 deletions(-) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthModels.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthModelsTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthModels.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthModels.kt new file mode 100644 index 00000000..ff651187 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthModels.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Subset of the OIDC discovery document ({issuer}/.well-known/openid-configuration) we use. Lets + * the app stay provider-agnostic — endpoints are read from here rather than hardcoded per provider. + */ +@Serializable +internal data class OpenIdConfiguration( + @SerialName("authorization_endpoint") val authorizationEndpoint: String, + @SerialName("token_endpoint") val tokenEndpoint: String, + @SerialName("userinfo_endpoint") val userInfoEndpoint: String = "", + @SerialName("end_session_endpoint") val endSessionEndpoint: String = "", +) + +/** Raw token endpoint response from the OIDC provider. */ +@Serializable +internal data class TokenResponse( + @SerialName("access_token") val accessToken: String, + @SerialName("refresh_token") val refreshToken: String? = null, + @SerialName("id_token") val idToken: String? = null, + @SerialName("token_type") val tokenType: String = "Bearer", + @SerialName("expires_in") val expiresIn: Long = 0, + @SerialName("refresh_expires_in") val refreshExpiresIn: Long = 0, + @SerialName("scope") val scope: String? = null, +) + +/** Subset of the provider's `userinfo` response we care about. */ +@Serializable +data class UserInfo( + @SerialName("sub") val subject: String = "", + @SerialName("preferred_username") val username: String = "", + @SerialName("name") val fullName: String = "", + @SerialName("email") val email: String = "", +) + +/** + * The persisted session — tokens plus user identity. Stored encrypted in KSafe. + * [obtainedAtEpochSeconds] + [expiresInSeconds] let us know when to refresh. + */ +@Serializable +data class Session( + val accessToken: String, + val refreshToken: String?, + val idToken: String?, + val expiresInSeconds: Long, + val obtainedAtEpochSeconds: Long, + val user: UserInfo, +) { + /** True when the access token has expired (with a small safety skew). */ + fun isAccessTokenExpired(nowEpochSeconds: Long, skewSeconds: Long = 30): Boolean = + nowEpochSeconds >= (obtainedAtEpochSeconds + expiresInSeconds - skewSeconds) +} + +/** + * Short-lived state we must remember across the authorization round-trip (especially on web, where + * the page reloads). Stored encrypted in KSafe. + */ +@Serializable internal data class PendingAuth(val codeVerifier: String, val state: String) + +/** Result of an [AuthorizationLauncher.authorize] call. */ +sealed interface AuthResult { + /** Web only: the browser is navigating away; the result arrives on reload. */ + data object Redirecting : AuthResult + + /** The full callback URL (contains `code` + `state`, or `error`). */ + data class Success(val callbackUrl: String) : AuthResult + + /** The user dismissed the browser/auth sheet. */ + data object Canceled : AuthResult + + data class Failure(val message: String) : AuthResult +} + +/** High-level outcome of a login attempt, surfaced to the UI. */ +sealed interface LoginOutcome { + data class Authenticated(val session: Session) : LoginOutcome + + /** Web: page is unloading to the provider; nothing more to do this load. */ + data object Redirecting : LoginOutcome + + data object Canceled : LoginOutcome + + data class Error(val message: String) : LoginOutcome +} + +/** + * Server-side liveness of a session. [Unknown] means the provider could not be reached (offline); + * callers MUST treat it as "keep the session" so connectivity loss never logs a field user out. + */ +enum class SessionStatus { + Active, + Revoked, + Unknown, +} + +/** Auth state the UI renders against. */ +sealed interface AuthState { + data object Loading : AuthState + + data object Unauthenticated : AuthState + + data class Authenticated(val session: Session) : AuthState +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthModelsTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthModelsTest.kt new file mode 100644 index 00000000..809416af --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthModelsTest.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.serialization.json.Json + +class AuthModelsTest { + + private val json = Json { ignoreUnknownKeys = true } + + @Test + fun discoveryDocument_parsesProviderEndpoints() { + val body = + """ + { + "issuer": "https://idp.example.org", + "authorization_endpoint": "https://idp.example.org/oauth/v2/authorize", + "token_endpoint": "https://idp.example.org/oauth/v2/token", + "userinfo_endpoint": "https://idp.example.org/oidc/v1/userinfo", + "end_session_endpoint": "https://idp.example.org/oidc/v1/end_session" + } + """ + .trimIndent() + + val doc = json.decodeFromString(body) + + assertEquals("https://idp.example.org/oauth/v2/authorize", doc.authorizationEndpoint) + assertEquals("https://idp.example.org/oauth/v2/token", doc.tokenEndpoint) + assertEquals("https://idp.example.org/oidc/v1/userinfo", doc.userInfoEndpoint) + assertEquals("https://idp.example.org/oidc/v1/end_session", doc.endSessionEndpoint) + } + + @Test + fun discoveryDocument_toleratesMissingOptionalEndpoints() { + val body = + """ + { + "authorization_endpoint": "https://idp.example.org/authorize", + "token_endpoint": "https://idp.example.org/token" + } + """ + .trimIndent() + + val doc = json.decodeFromString(body) + + assertEquals("", doc.userInfoEndpoint) + assertEquals("", doc.endSessionEndpoint) + } + + @Test + fun session_isAccessTokenExpired_trueOncePastExpiryMinusSkew() { + val session = + Session( + accessToken = "a", + refreshToken = "r", + idToken = null, + expiresInSeconds = 300, + obtainedAtEpochSeconds = 1_000, + user = UserInfo(), + ) + + assertEquals(false, session.isAccessTokenExpired(nowEpochSeconds = 1_269)) // 1000+300-30=1270 + assertEquals(true, session.isAccessTokenExpired(nowEpochSeconds = 1_270)) + } +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt index 2021fa46..54ff3c41 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt @@ -15,9 +15,9 @@ */ package dev.ohs.player.reference.app.data.repository +import dev.ohs.fhir.engine.resourceType import dev.ohs.fhir.model.r4.Bundle import dev.ohs.fhir.model.r4.Resource -import dev.ohs.fhir.resourceType import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index 5cc08926..39a0e175 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -17,10 +17,10 @@ package dev.ohs.player.reference.app import androidx.compose.ui.window.Window import androidx.compose.ui.window.application -import dev.ohs.fhir.FhirEngine -import dev.ohs.fhir.FhirEngineConfiguration -import dev.ohs.fhir.FhirEngineProvider -import dev.ohs.fhir.ServerConfiguration +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.FhirEngineConfiguration +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.ServerConfiguration import dev.ohs.player.reference.app.data.di.initKoin import java.io.File import org.koin.dsl.module diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt index ed9ce518..eb4c4fa2 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt @@ -15,9 +15,9 @@ */ package dev.ohs.player.reference.app.data.repository -import dev.ohs.fhir.FhirEngine -import dev.ohs.fhir.FhirEngineConfiguration -import dev.ohs.fhir.FhirEngineProvider +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.FhirEngineConfiguration +import dev.ohs.fhir.engine.FhirEngineProvider import dev.ohs.fhir.model.r4.Bundle import dev.ohs.fhir.model.r4.Group import dev.ohs.fhir.model.r4.Patient diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index 0cead0e2..2ba831c9 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -23,9 +23,9 @@ import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.runComposeUiTest -import dev.ohs.fhir.sync.FhirDataStore -import dev.ohs.fhir.sync.SyncJobStatus -import dev.ohs.fhir.sync.createDataStore +import dev.ohs.fhir.engine.sync.FhirDataStore +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.createDataStore import dev.ohs.player.library.registry.LocalViewRegistry import dev.ohs.player.reference.app.buildAppViewRegistry import dev.ohs.player.reference.app.data.di.repositoryModule diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt index b52b8e5f..eae5eb24 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt @@ -15,9 +15,9 @@ */ package dev.ohs.player.reference.app.feature.home -import dev.ohs.fhir.sync.FhirDataStore -import dev.ohs.fhir.sync.SyncJobStatus -import dev.ohs.fhir.sync.createDataStore +import dev.ohs.fhir.engine.sync.FhirDataStore +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.createDataStore import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import java.nio.file.Files import kotlin.test.Test From 62e0e6b763cb524d0115691213564ae5b846f58b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:22:02 +0300 Subject: [PATCH 12/73] Add OAuthConfig with OIDC discovery URL --- .../player/reference/app/auth/OAuthConfig.kt | 55 +++++++++++++++++++ .../reference/app/auth/OAuthConfigTest.kt | 42 ++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OAuthConfigTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt new file mode 100644 index 00000000..6c79ffcd --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +/** + * Provider-agnostic OAuth2/OIDC settings. + * + * Only the [issuer] (plus client id and scopes) is configured; the concrete endpoints are resolved + * at runtime via OIDC discovery ([OidcEndpoints]) from `{issuer}/.well-known/openid-configuration`. + * The redirect URI is intentionally NOT part of this object — it is platform specific and provided + * by the [AuthorizationLauncher]. + */ +data class OAuthConfig( + /** OIDC issuer, e.g. a Keycloak realm `https://host/realms/ohs-player` or `https://x.zitadel.cloud`. */ + val issuer: String, + val clientId: String, + /** Space-separated OAuth scopes, e.g. "openid profile email offline_access". */ + val scopes: String, +) { + /** Standard OIDC discovery document URL for this issuer. */ + val discoveryUrl: String + get() = "${issuer.trimEnd('/')}/.well-known/openid-configuration" + + companion object { + /** The single config baked in from `local.properties`. */ + val Default: OAuthConfig = + OAuthConfig( + issuer = GeneratedAuthConfig.ISSUER, + clientId = GeneratedAuthConfig.CLIENT_ID, + scopes = GeneratedAuthConfig.SCOPES, + ) + } +} + +/** OAuth/OIDC endpoints resolved from the provider's discovery document. */ +data class OidcEndpoints( + val authorizationEndpoint: String, + val tokenEndpoint: String, + val userInfoEndpoint: String, + /** May be blank: not every provider advertises an end-session endpoint. */ + val endSessionEndpoint: String, +) diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OAuthConfigTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OAuthConfigTest.kt new file mode 100644 index 00000000..97c805b3 --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OAuthConfigTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import kotlin.test.Test +import kotlin.test.assertEquals + +class OAuthConfigTest { + + @Test + fun discoveryUrl_appendsWellKnown_andTrimsTrailingSlash() { + val keycloak = + OAuthConfig( + issuer = "https://keycloak.example.org/realms/ohs-player/", + clientId = "ohs-player-reference-app", + scopes = "openid profile", + ) + assertEquals( + "https://keycloak.example.org/realms/ohs-player/.well-known/openid-configuration", + keycloak.discoveryUrl, + ) + + val zitadel = keycloak.copy(issuer = "https://my-instance.zitadel.cloud") + assertEquals( + "https://my-instance.zitadel.cloud/.well-known/openid-configuration", + zitadel.discoveryUrl, + ) + } +} From 5ea673e4184434f2bd985596a6670d88c18ab5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:27:13 +0300 Subject: [PATCH 13/73] Add PKCE helpers with per-platform secure random bytes Also fixes the same pre-existing dev.ohs.fhir.* import issue in OhsPlayerApplication.kt (Android) and MainViewController.kt (iOS), which was blocking those two targets from compiling at all. --- .../reference/app/OhsPlayerApplication.kt | 8 +-- .../reference/app/auth/Platform.android.kt | 21 +++++++ .../dev/ohs/player/reference/app/auth/Pkce.kt | 56 +++++++++++++++++++ .../ohs/player/reference/app/auth/PkceTest.kt | 52 +++++++++++++++++ .../reference/app/MainViewController.kt | 8 +-- .../player/reference/app/auth/Platform.ios.kt | 32 +++++++++++ .../reference/app/auth/SecureRandom.js.kt | 26 +++++++++ .../player/reference/app/auth/Platform.jvm.kt | 21 +++++++ .../reference/app/auth/SecureRandom.wasmJs.kt | 30 ++++++++++ 9 files changed, 246 insertions(+), 8 deletions(-) create mode 100644 ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/Pkce.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/PkceTest.kt create mode 100644 ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt create mode 100644 ohs-player-reference-app/src/jsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.js.kt create mode 100644 ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt create mode 100644 ohs-player-reference-app/src/wasmJsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.wasmJs.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt index 68a12780..35f4892a 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt @@ -16,11 +16,11 @@ package dev.ohs.player.reference.app import android.app.Application -import dev.ohs.fhir.FhirEngine -import dev.ohs.fhir.FhirEngineConfiguration -import dev.ohs.fhir.FhirEngineProvider -import dev.ohs.fhir.ServerConfiguration import dev.ohs.fhir.datacapture.DataCapture +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.FhirEngineConfiguration +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.ServerConfiguration import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt new file mode 100644 index 00000000..aa743160 --- /dev/null +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import java.security.SecureRandom + +internal actual fun secureRandomBytes(size: Int): ByteArray = + ByteArray(size).also { SecureRandom().nextBytes(it) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/Pkce.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/Pkce.kt new file mode 100644 index 00000000..e82784d1 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/Pkce.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi +import org.kotlincrypto.hash.sha2.SHA256 + +/** + * RFC 7636 (PKCE) helpers. + * + * The verifier is high-entropy random bytes, base64url-encoded. The challenge is + * `BASE64URL(SHA-256(ASCII(verifier)))` (method `S256`). SHA-256 comes from KotlinCrypto so it runs + * synchronously on every target — including Wasm/JS, where the browser's SubtleCrypto digest is + * async-only. + */ +internal data class PkcePair(val verifier: String, val challenge: String) { + val method: String = "S256" +} + +internal object Pkce { + + /** 32 random bytes → 43-char verifier, comfortably inside the 43–128 range. */ + @OptIn(ExperimentalEncodingApi::class) + fun generate(): PkcePair { + val verifier = base64Url(secureRandomBytes(32)) + val challenge = base64Url(SHA256().digest(verifier.encodeToByteArray())) + return PkcePair(verifier = verifier, challenge = challenge) + } + + /** A random, URL-safe `state` value to defend against CSRF on the callback. */ + fun randomState(): String = base64Url(secureRandomBytes(16)) + + @OptIn(ExperimentalEncodingApi::class) + private fun base64Url(bytes: ByteArray): String = + Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT).encode(bytes) +} + +/** + * Cryptographically secure random bytes. Implemented per platform with the OS CSPRNG (SecureRandom + * / SecRandomCopyBytes / Web Crypto getRandomValues). + */ +internal expect fun secureRandomBytes(size: Int): ByteArray diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/PkceTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/PkceTest.kt new file mode 100644 index 00000000..7b01f06d --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/PkceTest.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue +import org.kotlincrypto.hash.sha2.SHA256 + +class PkceTest { + + @OptIn(ExperimentalEncodingApi::class) + @Test + fun pkceChallenge_isBase64UrlSha256OfVerifier() { + val pair = Pkce.generate() + + assertEquals("S256", pair.method) + assertTrue(pair.verifier.length in 43..128, "verifier length ${pair.verifier.length}") + + val expected = + Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT) + .encode(SHA256().digest(pair.verifier.encodeToByteArray())) + assertEquals(expected, pair.challenge, "challenge must be S256 of verifier") + + listOf(pair.verifier, pair.challenge).forEach { + assertFalse(it.contains('+') || it.contains('/') || it.contains('='), "not url-safe: $it") + } + } + + @Test + fun verifiersAndStates_areUnique() { + assertNotEquals(Pkce.generate().verifier, Pkce.generate().verifier) + assertNotEquals(Pkce.randomState(), Pkce.randomState()) + } +} diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt index 23006c48..0d2addee 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt @@ -16,10 +16,10 @@ package dev.ohs.player.reference.app import androidx.compose.ui.window.ComposeUIViewController -import dev.ohs.fhir.FhirEngine -import dev.ohs.fhir.FhirEngineConfiguration -import dev.ohs.fhir.FhirEngineProvider -import dev.ohs.fhir.ServerConfiguration +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.FhirEngineConfiguration +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.ServerConfiguration import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt new file mode 100644 index 00000000..d229660b --- /dev/null +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:OptIn(ExperimentalForeignApi::class) + +package dev.ohs.player.reference.app.auth + +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.usePinned +import platform.Security.SecRandomCopyBytes +import platform.Security.kSecRandomDefault + +internal actual fun secureRandomBytes(size: Int): ByteArray { + val bytes = ByteArray(size) + bytes.usePinned { pinned -> + SecRandomCopyBytes(kSecRandomDefault, size.toULong(), pinned.addressOf(0)) + } + return bytes +} diff --git a/ohs-player-reference-app/src/jsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.js.kt b/ohs-player-reference-app/src/jsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.js.kt new file mode 100644 index 00000000..dbd2bba8 --- /dev/null +++ b/ohs-player-reference-app/src/jsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.js.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import org.khronos.webgl.Int8Array +import org.khronos.webgl.get + +/** Browser CSPRNG via Web Crypto `crypto.getRandomValues`. */ +internal actual fun secureRandomBytes(size: Int): ByteArray { + val array = Int8Array(size) + js("crypto.getRandomValues(array)") + return ByteArray(size) { array[it] } +} diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt new file mode 100644 index 00000000..aa743160 --- /dev/null +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import java.security.SecureRandom + +internal actual fun secureRandomBytes(size: Int): ByteArray = + ByteArray(size).also { SecureRandom().nextBytes(it) } diff --git a/ohs-player-reference-app/src/wasmJsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.wasmJs.kt b/ohs-player-reference-app/src/wasmJsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.wasmJs.kt new file mode 100644 index 00000000..97e23f7c --- /dev/null +++ b/ohs-player-reference-app/src/wasmJsMain/kotlin/dev/ohs/player/reference/app/auth/SecureRandom.wasmJs.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:OptIn(kotlin.js.ExperimentalWasmJsInterop::class) + +package dev.ohs.player.reference.app.auth + +import org.khronos.webgl.Int8Array +import org.khronos.webgl.get + +private fun fillRandom(array: Int8Array): Unit = js("crypto.getRandomValues(array)") + +/** Browser CSPRNG via Web Crypto `crypto.getRandomValues`. */ +internal actual fun secureRandomBytes(size: Int): ByteArray { + val array = Int8Array(size) + fillRandom(array) + return ByteArray(size) { array[it] } +} From 809cd71cff187428476a46b0c4d1de07666f5d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:28:46 +0300 Subject: [PATCH 14/73] Add OidcAuthApi (discovery, token exchange, refresh, userinfo, logout) --- .../player/reference/app/auth/OidcAuthApi.kt | 183 ++++++++++++++++++ .../reference/app/auth/OidcAuthApiTest.kt | 136 +++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt new file mode 100644 index 00000000..2c83c202 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt @@ -0,0 +1,183 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.forms.submitForm +import io.ktor.client.request.get +import io.ktor.client.request.header +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.bodyAsText +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.parameters +import io.ktor.serialization.kotlinx.json.json +import kotlin.coroutines.cancellation.CancellationException +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive + +/** + * Provider-agnostic OpenID Connect client (discovery, token exchange, refresh, userinfo, logout). + * Endpoints are resolved from the provider's discovery document rather than hardcoded, so any + * standards-compliant OIDC provider works by changing only [OAuthConfig.issuer]. The Ktor engine is + * selected automatically from each platform's classpath (OkHttp / CIO / Darwin / JS). + */ +internal class OidcAuthApi( + private val config: OAuthConfig, + private val httpClient: HttpClient = defaultHttpClient(), +) { + + private val endpointsMutex = Mutex() + private var cachedEndpoints: OidcEndpoints? = null + + /** Resolves and caches the provider endpoints via OIDC discovery. */ + suspend fun endpoints(): OidcEndpoints = + cachedEndpoints + ?: endpointsMutex.withLock { cachedEndpoints ?: discover().also { cachedEndpoints = it } } + + private suspend fun discover(): OidcEndpoints { + val response = httpClient.get(config.discoveryUrl) + check(response.status.isSuccess()) { + "Could not reach the sign-in provider (HTTP ${response.status.value} from ${config.discoveryUrl})" + } + val doc: OpenIdConfiguration = response.body() + return OidcEndpoints( + authorizationEndpoint = doc.authorizationEndpoint, + tokenEndpoint = doc.tokenEndpoint, + userInfoEndpoint = doc.userInfoEndpoint, + endSessionEndpoint = doc.endSessionEndpoint, + ) + } + + /** Exchanges an authorization `code` (+ PKCE verifier) for tokens. */ + suspend fun exchangeCode(code: String, codeVerifier: String, redirectUri: String): TokenResponse = + httpClient + .submitForm( + url = endpoints().tokenEndpoint, + formParameters = + parameters { + append("grant_type", "authorization_code") + append("client_id", config.clientId) + append("code", code) + append("redirect_uri", redirectUri) + append("code_verifier", codeVerifier) + }, + ) + .requireTokens() + + /** Refreshes an expired access token using the refresh token. */ + suspend fun refresh(refreshToken: String): TokenResponse = + httpClient + .submitForm( + url = endpoints().tokenEndpoint, + formParameters = + parameters { + append("grant_type", "refresh_token") + append("client_id", config.clientId) + append("refresh_token", refreshToken) + }, + ) + .requireTokens() + + private suspend fun HttpResponse.requireTokens(): TokenResponse { + if (!status.isSuccess()) { + val raw = runCatching { bodyAsText() }.getOrNull().orEmpty() + throw AuthException(oauthErrorMessage(raw, status.value)) + } + return body() + } + + suspend fun fetchUserInfo(accessToken: String): UserInfo = + httpClient + .get(endpoints().userInfoEndpoint) { header(HttpHeaders.Authorization, "Bearer $accessToken") } + .body() + + /** + * Liveness check via the userinfo endpoint. Fails OPEN: unreachable provider / timeout / + * discovery failure returns [SessionStatus.Unknown] so the caller keeps the local session + * (offline must never force a logout). + */ + suspend fun sessionStatus(accessToken: String): SessionStatus = + try { + val response = + httpClient.get(endpoints().userInfoEndpoint) { + header(HttpHeaders.Authorization, "Bearer $accessToken") + } + when { + response.status.isSuccess() -> SessionStatus.Active + response.status == HttpStatusCode.Unauthorized -> SessionStatus.Revoked + else -> SessionStatus.Unknown + } + } catch (cancellation: CancellationException) { + throw cancellation + } catch (unreachable: Throwable) { + SessionStatus.Unknown + } + + /** Best-effort server-side logout; failures are ignored (local clear still wins). */ + suspend fun logout(refreshToken: String) { + runCatching { + val endSession = endpoints().endSessionEndpoint + if (endSession.isBlank()) return@runCatching + val response = + httpClient.submitForm( + url = endSession, + formParameters = + parameters { + append("client_id", config.clientId) + append("refresh_token", refreshToken) + }, + ) + if (response.status != HttpStatusCode.NoContent && !response.status.isSuccess()) { + response.bodyAsText() + } + } + } + + companion object { + fun defaultHttpClient(): HttpClient = HttpClient { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + install(HttpTimeout) { + requestTimeoutMillis = 15_000 + connectTimeoutMillis = 10_000 + } + } + } +} + +private fun HttpStatusCode.isSuccess(): Boolean = value in 200..299 + +/** A failed auth/token request, carrying a human-readable reason for the UI. */ +internal class AuthException(message: String) : Exception(message) + +private fun oauthErrorMessage(body: String, statusCode: Int): String { + val parsed = + runCatching { + val obj = Json.parseToJsonElement(body).jsonObject + val error = obj["error"]?.jsonPrimitive?.contentOrNull + val description = obj["error_description"]?.jsonPrimitive?.contentOrNull + listOfNotNull(error, description).joinToString(": ") + } + .getOrNull() + return parsed?.takeIf { it.isNotBlank() } ?: "Authentication request failed (HTTP $statusCode)" +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt new file mode 100644 index 00000000..7a9574ca --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt @@ -0,0 +1,136 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.MockRequestHandleScope +import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.HttpResponseData +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headersOf +import io.ktor.serialization.kotlinx.json.json +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.Json + +class OidcAuthApiTest { + + private val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") + private val discoveryBody = + """ + { + "authorization_endpoint": "https://idp.example.org/authorize", + "token_endpoint": "https://idp.example.org/token", + "userinfo_endpoint": "https://idp.example.org/userinfo", + "end_session_endpoint": "https://idp.example.org/logout" + } + """ + .trimIndent() + + private fun apiWith( + onToken: MockRequestHandleScope.() -> HttpResponseData = { + respond( + """{"access_token":"a","refresh_token":"r","expires_in":300,"token_type":"Bearer"}""", + HttpStatusCode.OK, + jsonHeaders, + ) + }, + onUserInfo: MockRequestHandleScope.() -> HttpResponseData = { + respond("{}", HttpStatusCode.OK, jsonHeaders) + }, + ): OidcAuthApi { + val engine = MockEngine { request -> + val path = request.url.encodedPath + when { + path.endsWith("openid-configuration") -> + respond(discoveryBody, HttpStatusCode.OK, jsonHeaders) + path.endsWith("/token") -> onToken() + path.endsWith("/userinfo") -> onUserInfo() + else -> respond("not found", HttpStatusCode.NotFound) + } + } + val client = + HttpClient(engine) { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + } + return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) + } + + @Test + fun exchangeCode_returnsTokensFromMockedEndpoint() = runTest { + val tokens = apiWith().exchangeCode("code123", "verifier", "app://callback") + assertEquals("a", tokens.accessToken) + assertEquals("r", tokens.refreshToken) + } + + @Test + fun exchangeCode_onProviderError_throwsAuthExceptionWithMessage() = runTest { + val api = + apiWith( + onToken = { + respond( + """{"error":"invalid_grant","error_description":"Token is not active"}""", + HttpStatusCode.BadRequest, + jsonHeaders, + ) + } + ) + val error = assertFailsWith { api.exchangeCode("bad", "v", "app://callback") } + assertEquals("invalid_grant: Token is not active", error.message) + } + + @Test + fun sessionStatus_mapsHttpResultsToSessionStatus() = runTest { + assertEquals( + SessionStatus.Active, + apiWith(onUserInfo = { respond("{}", HttpStatusCode.OK, jsonHeaders) }).sessionStatus("t"), + ) + assertEquals( + SessionStatus.Revoked, + apiWith(onUserInfo = { respond("", HttpStatusCode.Unauthorized) }).sessionStatus("t"), + ) + assertEquals( + SessionStatus.Unknown, + apiWith(onUserInfo = { respond("", HttpStatusCode.InternalServerError) }).sessionStatus("t"), + ) + assertEquals( + SessionStatus.Unknown, + apiWith(onUserInfo = { throw RuntimeException("offline") }).sessionStatus("t"), + ) + } + + @Test + fun fetchUserInfo_parsesUserFields() = runTest { + val api = + apiWith( + onUserInfo = { + respond( + """{"sub":"u1","preferred_username":"jdoe","name":"Jane Doe","email":"j@example.org"}""", + HttpStatusCode.OK, + jsonHeaders, + ) + } + ) + val user = api.fetchUserInfo("token") + assertEquals("jdoe", user.username) + assertEquals("j@example.org", user.email) + } +} From 9dc02076ed0b3f9ed26dda4c7a54559d24c0f6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:31:40 +0300 Subject: [PATCH 15/73] Add KSafe-backed SessionRepository with per-platform storage --- .../reference/app/auth/Platform.android.kt | 14 +++ .../reference/app/auth/SessionRepository.kt | 100 ++++++++++++++++++ .../player/reference/app/auth/Platform.ios.kt | 3 + .../player/reference/app/auth/Platform.jvm.kt | 5 + .../app/auth/SessionRepositoryTest.kt | 69 ++++++++++++ .../player/reference/app/auth/Platform.web.kt | 24 +++++ 6 files changed, 215 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/SessionRepository.kt create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt create mode 100644 ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt index aa743160..f7c3b67f 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt @@ -15,7 +15,21 @@ */ package dev.ohs.player.reference.app.auth +import android.app.Application +import eu.anifantakis.lib.ksafe.KSafe import java.security.SecureRandom +/** Holds the Application context so KSafe (and others) can be created from common code. */ +internal object AndroidAppContext { + lateinit var application: Application + private set + + fun init(app: Application) { + application = app + } +} + +internal actual fun createKSafe(): KSafe = KSafe(AndroidAppContext.application) + internal actual fun secureRandomBytes(size: Int): ByteArray = ByteArray(size).also { SecureRandom().nextBytes(it) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/SessionRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/SessionRepository.kt new file mode 100644 index 00000000..14db5a4f --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/SessionRepository.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import eu.anifantakis.lib.ksafe.KSafe +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Storage seam for the session + pending-auth state. Lets tests substitute an in-memory fake. */ +internal interface SessionStore { + val session: StateFlow + + suspend fun load(): Session? + + suspend fun save(session: Session) + + suspend fun clear() + + suspend fun savePending(pending: PendingAuth) + + suspend fun takePending(): PendingAuth? +} + +/** + * Single source of truth for the signed-in [Session] and the transient [PendingAuth] used during + * the authorization round-trip. Everything is persisted through [KSafe], which encrypts at rest + * using each platform's hardware-backed keystore (Android Keystore, Apple Keychain, OS vaults, + * non-extractable WebCrypto keys on web). + * + * KSafe's `get` needs a default, so we use blank sentinels and treat a blank token / verifier as + * "absent". + * + * Deliberately a plain `object`, not Koin-constructed: [FhirBearerAuthenticator] must read it from + * each platform's `main()` / `Application.onCreate()`, which runs before `initKoin()`. Koin binds + * this same singleton via `single { SessionRepository }` for everything else. + */ +internal object SessionRepository : SessionStore { + + private val ksafe: KSafe by lazy { createKSafe() } + + private val _session = MutableStateFlow(null) + /** Emits the current session, or null when signed out. Hydrate via [load]. */ + override val session: StateFlow = _session.asStateFlow() + + /** Reads any persisted session into [session]. Call once at startup. */ + override suspend fun load(): Session? = readSession().also { _session.value = it } + + override suspend fun save(session: Session) { + ksafe.put(KEY_SESSION, session) + _session.value = session + } + + override suspend fun clear() { + ksafe.put(KEY_SESSION, EMPTY_SESSION) + _session.value = null + } + + override suspend fun savePending(pending: PendingAuth) = ksafe.put(KEY_PENDING, pending) + + /** Returns the pending auth (if any) and clears it — single use. */ + override suspend fun takePending(): PendingAuth? { + val pending = ksafe.get(KEY_PENDING, EMPTY_PENDING) + ksafe.put(KEY_PENDING, EMPTY_PENDING) + return pending.takeIf { it.codeVerifier.isNotBlank() } + } + + private suspend fun readSession(): Session? = + ksafe.get(KEY_SESSION, EMPTY_SESSION).takeIf { it.accessToken.isNotBlank() } + + private const val KEY_SESSION = "auth.session" + private const val KEY_PENDING = "auth.pending" + + private val EMPTY_SESSION = + Session( + accessToken = "", + refreshToken = null, + idToken = null, + expiresInSeconds = 0, + obtainedAtEpochSeconds = 0, + user = UserInfo(), + ) + private val EMPTY_PENDING = PendingAuth(codeVerifier = "", state = "") +} + +/** Creates the platform [KSafe] (Android needs the app Context; others don't). */ +internal expect fun createKSafe(): KSafe diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt index d229660b..c2f14e1b 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt @@ -17,12 +17,15 @@ package dev.ohs.player.reference.app.auth +import eu.anifantakis.lib.ksafe.KSafe import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.addressOf import kotlinx.cinterop.usePinned import platform.Security.SecRandomCopyBytes import platform.Security.kSecRandomDefault +internal actual fun createKSafe(): KSafe = KSafe() + internal actual fun secureRandomBytes(size: Int): ByteArray { val bytes = ByteArray(size) bytes.usePinned { pinned -> diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt index aa743160..e0f1e65e 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt @@ -15,7 +15,12 @@ */ package dev.ohs.player.reference.app.auth +import eu.anifantakis.lib.ksafe.KSafe +import java.io.File import java.security.SecureRandom +internal actual fun createKSafe(): KSafe = + KSafe(baseDir = File(System.getProperty("user.home"), ".ohs-player-reference-app")) + internal actual fun secureRandomBytes(size: Int): ByteArray = ByteArray(size).also { SecureRandom().nextBytes(it) } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt new file mode 100644 index 00000000..7762214e --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlinx.coroutines.test.runTest + +class SessionRepositoryTest { + + @AfterTest + fun tearDown() = runTest { SessionRepository.clear() } + + private fun testSession() = + Session( + accessToken = "access-1", + refreshToken = "refresh-1", + idToken = "id-1", + expiresInSeconds = 300, + obtainedAtEpochSeconds = 1_000, + user = UserInfo(subject = "u1", username = "jdoe"), + ) + + @Test + fun saveThenLoad_roundTripsTheSession() = runTest { + SessionRepository.save(testSession()) + + val loaded = SessionRepository.load() + + assertEquals(testSession(), loaded) + assertEquals(testSession(), SessionRepository.session.value) + } + + @Test + fun clear_removesTheSession() = runTest { + SessionRepository.save(testSession()) + + SessionRepository.clear() + + assertNull(SessionRepository.load()) + assertNull(SessionRepository.session.value) + } + + @Test + fun takePending_returnsAndClearsPendingAuth_onlyOnce() = runTest { + SessionRepository.savePending(PendingAuth(codeVerifier = "verifier-1", state = "state-1")) + + val first = SessionRepository.takePending() + val second = SessionRepository.takePending() + + assertEquals(PendingAuth("verifier-1", "state-1"), first) + assertNull(second, "pending auth must be single-use") + } +} diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt new file mode 100644 index 00000000..e86ccd88 --- /dev/null +++ b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import eu.anifantakis.lib.ksafe.KSafe + +internal actual fun createKSafe(): KSafe = KSafe() + +// secureRandomBytes lives in jsMain / wasmJsMain — Web Crypto interop differs +// between the two web targets, so it can't be shared from webMain. +// AuthorizationLauncher is added here in a later task. From ebc3cd38942316dabc217c47137068c4ee7db14f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:33:03 +0300 Subject: [PATCH 16/73] Add AuthorizationLauncher with JVM desktop loopback-redirect actual --- .../app/auth/AuthorizationLauncher.kt | 49 +++++++++++++++ .../player/reference/app/auth/Platform.jvm.kt | 61 +++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt new file mode 100644 index 00000000..f92f0328 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import androidx.compose.runtime.Composable + +/** + * Opens the system browser to the identity provider's authorization page and returns the redirect + * callback. Each platform uses its idiomatic mechanism: + * - Android: Chrome Custom Tabs + a custom-scheme deep link. + * - iOS: ASWebAuthenticationSession (returns the callback URL directly). + * - Desktop: a localhost loopback HTTP server + the system browser. + * - Web: a full-page redirect; the callback is read on the next page load via + * [consumeRedirectCallback]. + * + * [redirectUri] is the platform-correct value and must be registered at the identity provider. It + * is used in BOTH the authorize request and the token exchange, so the launcher owns it. + */ +expect class AuthorizationLauncher { + val redirectUri: String + + /** + * Launches the auth flow. Suspends until the callback is received ([AuthResult.Success]) on + * platforms that can await it; returns [AuthResult.Redirecting] on web, where the page unloads. + */ + suspend fun authorize(authUrl: String): AuthResult + + /** + * Web only: if this page load is a redirect back from the provider, returns the callback URL + * (and clears it from the address bar). Returns null otherwise and on every non-web platform. + */ + fun consumeRedirectCallback(): String? +} + +/** Obtains a launcher, wiring in any platform context (e.g. the Android Activity). */ +@Composable expect fun rememberAuthorizationLauncher(): AuthorizationLauncher diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt index e0f1e65e..a2079fd8 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt @@ -15,12 +15,73 @@ */ package dev.ohs.player.reference.app.auth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import com.sun.net.httpserver.HttpServer import eu.anifantakis.lib.ksafe.KSafe +import java.awt.Desktop import java.io.File +import java.net.InetSocketAddress +import java.net.URI import java.security.SecureRandom +import kotlinx.coroutines.suspendCancellableCoroutine internal actual fun createKSafe(): KSafe = KSafe(baseDir = File(System.getProperty("user.home"), ".ohs-player-reference-app")) internal actual fun secureRandomBytes(size: Int): ByteArray = ByteArray(size).also { SecureRandom().nextBytes(it) } + +/** + * Desktop login: open the system browser to the identity provider and capture the redirect on a + * short-lived localhost loopback server (the OAuth 2.0 native-app recommendation, RFC 8252). + */ +actual class AuthorizationLauncher(private val port: Int) { + + actual val redirectUri: String = "http://127.0.0.1:$port/callback" + + actual suspend fun authorize(authUrl: String): AuthResult = + suspendCancellableCoroutine { continuation -> + val server = HttpServer.create(InetSocketAddress("127.0.0.1", port), 0) + server.createContext("/callback") { exchange -> + val callbackUrl = "$redirectUri?${exchange.requestURI.rawQuery.orEmpty()}" + val html = + "" + + "

You can return to the app

You may close this window.

" + + "" + val bytes = html.encodeToByteArray() + exchange.responseHeaders.add("Content-Type", "text/html; charset=utf-8") + exchange.sendResponseHeaders(200, bytes.size.toLong()) + exchange.responseBody.use { it.write(bytes) } + Thread { server.stop(0) }.start() + if (continuation.isActive) + continuation.resumeWith(Result.success(AuthResult.Success(callbackUrl))) + } + server.start() + continuation.invokeOnCancellation { server.stop(0) } + + runCatching { + val desktop = Desktop.getDesktop() + if (Desktop.isDesktopSupported() && desktop.isSupported(Desktop.Action.BROWSE)) { + desktop.browse(URI(authUrl)) + } else { + error("Opening a browser is not supported on this desktop environment") + } + } + .onFailure { + server.stop(0) + if (continuation.isActive) { + continuation.resumeWith( + Result.success(AuthResult.Failure(it.message ?: "Failed to open browser")) + ) + } + } + } + + actual fun consumeRedirectCallback(): String? = null +} + +@Composable +actual fun rememberAuthorizationLauncher(): AuthorizationLauncher = remember { + AuthorizationLauncher(GeneratedAuthConfig.DESKTOP_REDIRECT_PORT) +} From 60f39f7fa9f67369bf2872e424945175cd4aacf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:35:26 +0300 Subject: [PATCH 17/73] Add Android AuthorizationLauncher (Custom Tabs + deep link redirect) --- .../src/androidMain/AndroidManifest.xml | 21 ++++ .../ohs/player/reference/app/MainActivity.kt | 2 + .../reference/app/auth/Platform.android.kt | 97 +++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/ohs-player-reference-app/src/androidMain/AndroidManifest.xml b/ohs-player-reference-app/src/androidMain/AndroidManifest.xml index a36bd34e..f30fef9e 100644 --- a/ohs-player-reference-app/src/androidMain/AndroidManifest.xml +++ b/ohs-player-reference-app/src/androidMain/AndroidManifest.xml @@ -18,6 +18,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt index d7e79cb1..fe87774a 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt @@ -19,9 +19,11 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import dev.ohs.player.reference.app.auth.AndroidAppContext class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { + AndroidAppContext.init(application) enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { App() } diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt index f7c3b67f..7da25859 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt @@ -15,9 +15,27 @@ */ package dev.ohs.player.reference.app.auth +import android.app.Activity import android.app.Application +import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.browser.customtabs.CustomTabsIntent +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import dev.ohs.player.reference.app.MainActivity import eu.anifantakis.lib.ksafe.KSafe import java.security.SecureRandom +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.withContext /** Holds the Application context so KSafe (and others) can be created from common code. */ internal object AndroidAppContext { @@ -33,3 +51,82 @@ internal actual fun createKSafe(): KSafe = KSafe(AndroidAppContext.application) internal actual fun secureRandomBytes(size: Int): ByteArray = ByteArray(size).also { SecureRandom().nextBytes(it) } + +/** + * Bridges the deep-link redirect (a separate Activity) back to the awaiting coroutine. A null + * completion means the user dismissed the browser without finishing. + */ +internal object AuthRedirectBus { + @Volatile var pending: CompletableDeferred? = null +} + +actual class AuthorizationLauncher(private val activity: Activity, actual val redirectUri: String) { + actual suspend fun authorize(authUrl: String): AuthResult { + val deferred = CompletableDeferred() + AuthRedirectBus.pending = deferred + val lifecycle = (activity as? LifecycleOwner)?.lifecycle + val cancelOnReturn = + object : DefaultLifecycleObserver { + private var leftForBrowser = false + + override fun onStop(owner: LifecycleOwner) { + leftForBrowser = true + } + + override fun onResume(owner: LifecycleOwner) { + if (leftForBrowser && !deferred.isCompleted) deferred.complete(null) + } + } + return try { + lifecycle?.let { withContext(Dispatchers.Main) { it.addObserver(cancelOnReturn) } } + CustomTabsIntent.Builder().build().launchUrl(activity, Uri.parse(authUrl)) + val callbackUrl = deferred.await() + if (callbackUrl == null) AuthResult.Canceled else AuthResult.Success(callbackUrl) + } catch (t: Throwable) { + AuthResult.Failure(t.message ?: "Authorization failed") + } finally { + AuthRedirectBus.pending = null + lifecycle?.let { + withContext(NonCancellable + Dispatchers.Main) { it.removeObserver(cancelOnReturn) } + } + } + } + + actual fun consumeRedirectCallback(): String? = null +} + +@Composable +actual fun rememberAuthorizationLauncher(): AuthorizationLauncher { + val activity = LocalContext.current.findActivity() + return remember(activity) { + AuthorizationLauncher( + activity = activity, + redirectUri = "${GeneratedAuthConfig.REDIRECT_SCHEME}://${GeneratedAuthConfig.REDIRECT_HOST}", + ) + } +} + +private tailrec fun Context.findActivity(): Activity = + when (this) { + is Activity -> this + is ContextWrapper -> baseContext.findActivity() + else -> error("rememberAuthorizationLauncher must be called from an Activity context") + } + +/** + * Receives the redirect (custom scheme deep link) and hands the callback URL to the awaiting + * [AuthorizationLauncher]. Registered in the manifest with an intent-filter for + * `${SCHEME}://${HOST}`. + */ +class LoginRedirectActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + intent?.data?.let { AuthRedirectBus.pending?.complete(it.toString()) } + AuthRedirectBus.pending = null + startActivity( + Intent(this, MainActivity::class.java) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + ) + finish() + } +} From 456cb53acc0e7893bc3b4eee38fda4e30935ce99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:53:22 +0300 Subject: [PATCH 18/73] Add iOS AuthorizationLauncher (ASWebAuthenticationSession) --- .../player/reference/app/auth/Platform.ios.kt | 74 +++++++++ .../npm/sqlite-wasm-worker/package.json | 8 + .../webMain/npm/sqlite-wasm-worker/worker.js | 157 ++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/package.json create mode 100644 ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt index c2f14e1b..ed12be7d 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt @@ -17,12 +17,26 @@ package dev.ohs.player.reference.app.auth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import eu.anifantakis.lib.ksafe.KSafe +import kotlin.coroutines.resume import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.addressOf import kotlinx.cinterop.usePinned +import kotlinx.coroutines.suspendCancellableCoroutine +import platform.AuthenticationServices.ASPresentationAnchor +import platform.AuthenticationServices.ASWebAuthenticationPresentationContextProvidingProtocol +import platform.AuthenticationServices.ASWebAuthenticationSession +import platform.Foundation.NSError +import platform.Foundation.NSURL import platform.Security.SecRandomCopyBytes import platform.Security.kSecRandomDefault +import platform.UIKit.UIApplication +import platform.UIKit.UISceneActivationStateForegroundActive +import platform.UIKit.UIWindow +import platform.UIKit.UIWindowScene +import platform.darwin.NSObject internal actual fun createKSafe(): KSafe = KSafe() @@ -33,3 +47,63 @@ internal actual fun secureRandomBytes(size: Int): ByteArray { } return bytes } + +/** + * iOS login via ASWebAuthenticationSession — the platform-blessed flow that shares cookies with + * Safari and returns the callback URL directly to the app (no AppDelegate plumbing needed). + */ +actual class AuthorizationLauncher( + actual val redirectUri: String, + private val callbackScheme: String, +) { + private val contextProvider = PresentationContextProvider() + + actual suspend fun authorize(authUrl: String): AuthResult = + suspendCancellableCoroutine { continuation -> + val session = + ASWebAuthenticationSession( + uRL = NSURL(string = authUrl), + callbackURLScheme = callbackScheme, + ) { callbackURL: NSURL?, error: NSError? -> + val result = + when { + callbackURL != null -> AuthResult.Success(callbackURL.absoluteString ?: "") + // ASWebAuthenticationSessionErrorCodeCanceledLogin == 1 + error != null && error.code.toInt() == 1 -> AuthResult.Canceled + error != null -> AuthResult.Failure(error.localizedDescription) + else -> AuthResult.Failure("Unknown authentication error") + } + if (continuation.isActive) continuation.resume(result) + } + session.presentationContextProvider = contextProvider + session.prefersEphemeralWebBrowserSession = false + continuation.invokeOnCancellation { session.cancel() } + session.start() + } + + actual fun consumeRedirectCallback(): String? = null +} + +private class PresentationContextProvider : + NSObject(), ASWebAuthenticationPresentationContextProvidingProtocol { + @Suppress("DEPRECATION") + override fun presentationAnchorForWebAuthenticationSession( + session: ASWebAuthenticationSession + ): ASPresentationAnchor { + val app = UIApplication.sharedApplication + val scenes = app.connectedScenes.filterIsInstance() + val scene = + scenes.firstOrNull { it.activationState == UISceneActivationStateForegroundActive } + ?: scenes.firstOrNull() + val window = scene?.windows?.filterIsInstance()?.firstOrNull() + return window ?: app.keyWindow ?: UIWindow() + } +} + +@Composable +actual fun rememberAuthorizationLauncher(): AuthorizationLauncher = remember { + AuthorizationLauncher( + redirectUri = "${GeneratedAuthConfig.REDIRECT_SCHEME}://${GeneratedAuthConfig.REDIRECT_HOST}", + callbackScheme = GeneratedAuthConfig.REDIRECT_SCHEME, + ) +} diff --git a/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/package.json b/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/package.json new file mode 100644 index 00000000..a7639eb9 --- /dev/null +++ b/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/package.json @@ -0,0 +1,8 @@ +{ + "name": "sqlite-wasm-worker", + "version": "0.0.0", + "license": "Apache-2.0", + "dependencies": { + "@sqlite.org/sqlite-wasm": "^3.50.1-build1" + } +} diff --git a/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js b/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js new file mode 100644 index 00000000..d8c51325 --- /dev/null +++ b/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js @@ -0,0 +1,157 @@ +import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; + +let sqlite3 = null; + +// Maps to track of active database connections and prepared statements by their unique IDs. +const databases = new Map(); // stores databaseId -> SQLiteDbObject +const statements = new Map(); // stores statementId -> SQLiteStatementObject + +// Counters to generate unique IDs for new database connections and statements. +let nextDatabaseId = 0; +let nextStatementId = 0; + +function openRequest(id, requestData) { + try { + const newDatabaseId = nextDatabaseId++; + const newDatabase = new sqlite3.oo1.OpfsDb(requestData.fileName); + databases.set(newDatabaseId, newDatabase); + postMessage({'id': id, data: {'databaseId': newDatabaseId}}); + } catch (error) { + postMessage({'id': id, error: error.message}); + } +} + +function prepareRequest(id, requestData) { + try { + const newStatementId = nextStatementId++; + const resultData = { + 'statementId': newStatementId, + 'parameterCount': 0, + 'columnNames': [] + }; + const database = databases.get(requestData.databaseId); + if (!database) { + postMessage({'id': id, error: "Invalid database ID: " + requestData.databaseId}); + return; + } + const statement = database.prepare(requestData.sql); + statements.set(newStatementId, statement); + resultData.parameterCount = sqlite3.capi.sqlite3_bind_parameter_count(statement); + for (let i = 0; i < statement.columnCount; i++) { + resultData.columnNames.push(sqlite3.capi.sqlite3_column_name(statement, i)); + } + postMessage({'id': id, data: resultData}); + } catch (error) { + postMessage({'id': id, error: error.message}); + } +} + +function stepRequest(id, requestData) { + const statement = statements.get(requestData.statementId); + if (!statement) { + postMessage({'id': id, error: "Invalid statement ID: " + requestData.statementId}); + return; + } + try { + const resultData = { + 'rows': [], + 'columnTypes': [] + }; + statement.reset() + statement.clearBindings() + for (let i = 0; i < requestData.bindings.length; i++) { + statement.bind(i + 1, requestData.bindings[i]); + } + while (statement.step()) { + if (!resultData.columnTypes.length) { + for (let i = 0; i < statement.columnCount; i++) { + resultData.columnTypes.push(sqlite3.capi.sqlite3_column_type(statement, i)); + } + } + resultData.rows.push(statement.get([])); + } + postMessage({'id': id, data: resultData}); + } catch (error) { + postMessage({'id': id, error: error.message}); + } +} + +function closeRequest(id, requestData) { + if (requestData.statementId) { + const statement = statements.get(requestData.statementId); + if (!statement) { + postMessage({'id': id, error: "Invalid statement ID: " + requestData.statementId}); + return; + } + try { + statement.finalize(); + statements.delete(requestData.statementId); + } catch (error) { + postMessage({'id': id, error: error.message}); + } + } + + if (requestData.databaseId) { + const database = databases.get(requestData.databaseId); + if (!database) { + postMessage({'id': id, error: "Invalid database ID: " + requestData.databaseId}); + return; + } + try { + database.close(); + databases.delete(requestData.databaseId); + } catch (error) { + postMessage({'id': id, error: error.message}); + } + } +} + +// A map that links command names (strings) to their respective handler functions. +const commandMap = { + 'open': openRequest, + 'prepare': prepareRequest, + 'step': stepRequest, + 'close': closeRequest, +}; + +function handleMessage(e) { + const requestMsg = e.data; + console.log("handleMessage: " + JSON.stringify(requestMsg)); + if (!Object.hasOwn(requestMsg, 'data') && requestMsg.data == null) { + postMessage( + {'id': requestMsg.id, 'error': "Invalid request, missing 'data'."} + ); + return; + } + if (!Object.hasOwn(requestMsg.data, 'cmd') && requestMsg.data.cmd == null) { + postMessage( + {'id': requestMsg.id, 'error': "Invalid request, missing 'cmd'."} + ); + return; + } + const command = requestMsg.data.cmd; + const requestHandler = commandMap[command]; + if (requestHandler) { + requestHandler(requestMsg.id, requestMsg.data); + } else { + postMessage( + {'id': requestMsg.id, 'error': "Invalid request, unknown command: '" + command + "'."} + ); + } +} + +const messageQueue = []; +onmessage = (e) => { + if (!sqlite3) { + messageQueue.push(e); + } else { + handleMessage(e); + } +}; + +sqlite3InitModule().then(instance => { + sqlite3 = instance; + while (messageQueue.length > 0) { + handleMessage(messageQueue.shift()); + } +}); From 34f172e1619f937994a9495bde173f6ae9e42a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:56:27 +0300 Subject: [PATCH 19/73] Add Web AuthorizationLauncher (full-page redirect) --- .../player/reference/app/auth/Platform.web.kt | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt index e86ccd88..95275d62 100644 --- a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt +++ b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt @@ -15,10 +15,40 @@ */ package dev.ohs.player.reference.app.auth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import eu.anifantakis.lib.ksafe.KSafe +import kotlinx.browser.window internal actual fun createKSafe(): KSafe = KSafe() // secureRandomBytes lives in jsMain / wasmJsMain — Web Crypto interop differs // between the two web targets, so it can't be shared from webMain. -// AuthorizationLauncher is added here in a later task. + +/** + * Web login uses a full-page redirect to the identity provider (the recommended SPA flow). + * [authorize] navigates away and the page unloads, so it returns [AuthResult.Redirecting]; the + * [PendingAuth] saved in KSafe survives the round-trip. On the next load [consumeRedirectCallback] + * picks up the result. + */ +actual class AuthorizationLauncher(actual val redirectUri: String) { + + actual suspend fun authorize(authUrl: String): AuthResult { + window.location.href = authUrl + return AuthResult.Redirecting + } + + actual fun consumeRedirectCallback(): String? { + val search = window.location.search + if (!search.contains("code=") && !search.contains("error=")) return null + val href = window.location.href + // Strip the query so a refresh doesn't replay the (single-use) code. + window.history.replaceState(null, "", window.location.pathname) + return href + } +} + +@Composable +actual fun rememberAuthorizationLauncher(): AuthorizationLauncher = remember { + AuthorizationLauncher(GeneratedAuthConfig.WEB_REDIRECT_URL) +} From f95ef91446b36e2b9969fd824f0368d156b43673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:00:46 +0300 Subject: [PATCH 20/73] Add AuthService (PKCE flow, session refresh/revalidation) and FhirBearerAuthenticator --- .../reference/app/auth/Platform.android.kt | 7 +- .../player/reference/app/auth/AuthService.kt | 176 +++++++++++++ .../app/auth/AuthorizationLauncher.kt | 11 +- .../app/auth/FhirBearerAuthenticator.kt | 32 +++ .../reference/app/auth/AuthServiceTest.kt | 244 ++++++++++++++++++ .../player/reference/app/auth/Platform.ios.kt | 6 +- .../player/reference/app/auth/Platform.jvm.kt | 6 +- .../player/reference/app/auth/Platform.web.kt | 4 +- 8 files changed, 474 insertions(+), 12 deletions(-) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/FhirBearerAuthenticator.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt index 7da25859..3b61c06d 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt @@ -60,8 +60,11 @@ internal object AuthRedirectBus { @Volatile var pending: CompletableDeferred? = null } -actual class AuthorizationLauncher(private val activity: Activity, actual val redirectUri: String) { - actual suspend fun authorize(authUrl: String): AuthResult { +actual class AuthorizationLauncher( + private val activity: Activity, + actual override val redirectUri: String, +) : AuthorizationLauncherApi { + actual override suspend fun authorize(authUrl: String): AuthResult { val deferred = CompletableDeferred() AuthRedirectBus.pending = deferred val lifecycle = (activity as? LifecycleOwner)?.lifecycle diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt new file mode 100644 index 00000000..cb71a6e6 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt @@ -0,0 +1,176 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import io.ktor.http.URLBuilder +import io.ktor.http.Url +import kotlin.coroutines.cancellation.CancellationException +import kotlin.time.Clock +import kotlin.time.ExperimentalTime + +/** + * Drives the Authorization-Code-with-PKCE flow against the configured OIDC provider and keeps the + * [SessionStore] up to date. UI-agnostic; [AuthViewModel] adapts it to Compose state. + * + * Unlike opensrp's version, this constructor has NO default parameter values — Koin always supplies + * [config], [repository], and [api] (see `authModule` in `AppModule.kt`), so self-constructing + * defaults would be dead code here. + */ +internal class AuthService( + private val config: OAuthConfig, + private val repository: SessionStore, + private val api: OidcAuthApi, +) { + + /** Starts an interactive login. */ + suspend fun login(launcher: AuthorizationLauncherApi): LoginOutcome { + val pkce = Pkce.generate() + val state = Pkce.randomState() + repository.savePending(PendingAuth(codeVerifier = pkce.verifier, state = state)) + + val authUrl = + runCatching { buildAuthorizationUrl(launcher.redirectUri, pkce, state) } + .getOrElse { + return LoginOutcome.Error(it.message ?: "Could not reach the sign-in provider") + } + return when (val result = launcher.authorize(authUrl)) { + is AuthResult.Redirecting -> LoginOutcome.Redirecting + is AuthResult.Success -> completeLogin(result.callbackUrl, launcher.redirectUri) + is AuthResult.Canceled -> LoginOutcome.Canceled + is AuthResult.Failure -> LoginOutcome.Error(result.message) + } + } + + /** + * Web: on app start, finishes a login if this page load is a redirect back from the provider. + * Returns null when there is nothing to complete. + */ + suspend fun completeRedirectLoginIfPresent(launcher: AuthorizationLauncher): LoginOutcome? { + val callbackUrl = launcher.consumeRedirectCallback() ?: return null + return completeLogin(callbackUrl, launcher.redirectUri) + } + + /** Exchanges the callback's `code` for tokens and persists the session. */ + private suspend fun completeLogin(callbackUrl: String, redirectUri: String): LoginOutcome { + val params = Url(callbackUrl).parameters + params["error"]?.let { error -> return LoginOutcome.Error(params["error_description"] ?: error) } + val code = params["code"] ?: return LoginOutcome.Error("Missing authorization code") + val returnedState = params["state"] + + val pending = repository.takePending() + if (pending == null || pending.state != returnedState) { + return LoginOutcome.Error("Invalid state — possible CSRF, please try again") + } + + return runCatching { + val tokens = api.exchangeCode(code, pending.codeVerifier, redirectUri) + val session = tokens.toSession(api.fetchUserInfo(tokens.accessToken)) + repository.save(session) + LoginOutcome.Authenticated(session) + } + .getOrElse { LoginOutcome.Error(it.message ?: "Sign-in failed") } + } + + /** + * Returns a usable session at startup, refreshing an expired access token when possible. + * + * Offline-safe: the session is cleared (→ login) ONLY on a definitive provider rejection + * ([AuthException]). A transient/offline failure keeps the stored session. Server-side + * revocation while the access token is still locally valid is caught separately (and + * non-blockingly) by [revalidateSession]. + */ + suspend fun ensureFreshSession(): Session? { + val session = repository.load() ?: return null + if (!session.isAccessTokenExpired(now())) return session + + val refreshed = tryRefresh(session) + return refreshed ?: repository.session.value + } + + /** + * Confirms a locally-valid session is still alive server-side via the userinfo probe. Clears it + * only on a definitive revoke; offline is a no-op. Meant to run in the background after startup + * so it never blocks an offline launch. + */ + suspend fun revalidateSession(): Boolean { + val session = repository.session.value ?: repository.load() ?: return false + return when (api.sessionStatus(session.accessToken)) { + SessionStatus.Revoked -> { + repository.clear() + false + } + SessionStatus.Active, + SessionStatus.Unknown -> true + } + } + + /** Refresh hook for [FhirBearerAuthenticator]'s eventual retry path, and internal use. */ + internal suspend fun refreshTokensForRequest(): Session? { + val session = repository.session.value ?: repository.load() ?: return null + return tryRefresh(session) + } + + private suspend fun tryRefresh(session: Session): Session? { + val refreshToken = + session.refreshToken + ?: run { + repository.clear() + return null + } + return try { + val tokens = api.refresh(refreshToken) + tokens.toSession(session.user).also { repository.save(it) } + } catch (cancellation: CancellationException) { + throw cancellation + } catch (rejected: AuthException) { + repository.clear() + null + } catch (offline: Throwable) { + null + } + } + + suspend fun logout() { + repository.session.value?.refreshToken?.let { api.logout(it) } + repository.clear() + } + + private suspend fun buildAuthorizationUrl(redirectUri: String, pkce: PkcePair, state: String): String = + URLBuilder(api.endpoints().authorizationEndpoint) + .apply { + parameters.append("client_id", config.clientId) + parameters.append("response_type", "code") + parameters.append("scope", config.scopes) + parameters.append("redirect_uri", redirectUri) + parameters.append("code_challenge", pkce.challenge) + parameters.append("code_challenge_method", pkce.method) + parameters.append("state", state) + } + .buildString() + + private fun TokenResponse.toSession(user: UserInfo): Session = + Session( + accessToken = accessToken, + refreshToken = refreshToken, + idToken = idToken, + expiresInSeconds = expiresIn, + obtainedAtEpochSeconds = now(), + user = user, + ) + + @OptIn(ExperimentalTime::class) + private fun now(): Long = Clock.System.now().toEpochMilliseconds() / 1000 +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt index f92f0328..8ff82eaf 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt @@ -29,14 +29,21 @@ import androidx.compose.runtime.Composable * [redirectUri] is the platform-correct value and must be registered at the identity provider. It * is used in BOTH the authorize request and the token exchange, so the launcher owns it. */ -expect class AuthorizationLauncher { +/** The subset of [AuthorizationLauncher] that [AuthService] depends on — lets tests fake it. */ +internal interface AuthorizationLauncherApi { val redirectUri: String + suspend fun authorize(authUrl: String): AuthResult +} + +expect class AuthorizationLauncher : AuthorizationLauncherApi { + override val redirectUri: String + /** * Launches the auth flow. Suspends until the callback is received ([AuthResult.Success]) on * platforms that can await it; returns [AuthResult.Redirecting] on web, where the page unloads. */ - suspend fun authorize(authUrl: String): AuthResult + override suspend fun authorize(authUrl: String): AuthResult /** * Web only: if this page load is a redirect back from the provider, returns the callback URL diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/FhirBearerAuthenticator.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/FhirBearerAuthenticator.kt new file mode 100644 index 00000000..3ce74e4a --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/FhirBearerAuthenticator.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import dev.ohs.fhir.engine.sync.HttpAuthenticationMethod +import dev.ohs.fhir.engine.sync.HttpAuthenticator + +/** + * Feeds the signed-in session's access token to `ohs-fhir-engine`'s HTTP layer. Reads + * [SessionRepository] directly (not through Koin) — this is constructed at each platform's + * `FhirEngineProvider.init()` call site, which runs before `initKoin()`. + * + * [getAuthenticationMethod] is called by the engine on every request, so this always reads the + * *current* token — there's no ordering dependency on when a session becomes available. + */ +internal object FhirBearerAuthenticator : HttpAuthenticator { + override fun getAuthenticationMethod(): HttpAuthenticationMethod = + HttpAuthenticationMethod.Bearer(SessionRepository.session.value?.accessToken.orEmpty()) +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt new file mode 100644 index 00000000..070f400d --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt @@ -0,0 +1,244 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.MockRequestHandleScope +import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.HttpResponseData +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.Url +import io.ktor.http.headersOf +import io.ktor.serialization.kotlinx.json.json +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.Json + +/** + * Verifies the offline-safe session logic: a logout happens ONLY on a definitive provider + * rejection (4xx), never on a network/offline failure, which must keep the local session. + */ +class AuthServiceTest { + + private class FakeSessionStore(initial: Session?) : SessionStore { + private val _session = MutableStateFlow(initial) + override val session: StateFlow = _session.asStateFlow() + var cleared = false + private set + private var pending: PendingAuth? = null + + override suspend fun load(): Session? = _session.value + + override suspend fun save(session: Session) { + _session.value = session + } + + override suspend fun clear() { + _session.value = null + cleared = true + } + + override suspend fun savePending(pending: PendingAuth) { + this.pending = pending + } + + override suspend fun takePending(): PendingAuth? = pending.also { pending = null } + } + + private class FakeLauncher( + override val redirectUri: String, + private val onAuthorize: suspend (String) -> AuthResult, + ) : AuthorizationLauncherApi { + override suspend fun authorize(authUrl: String): AuthResult = onAuthorize(authUrl) + } + + private val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") + private val discoveryBody = + """ + { + "authorization_endpoint": "https://idp.example.org/authorize", + "token_endpoint": "https://idp.example.org/token", + "userinfo_endpoint": "https://idp.example.org/userinfo", + "end_session_endpoint": "https://idp.example.org/logout" + } + """ + .trimIndent() + private val tokenBody = + """{"access_token":"new-access","refresh_token":"new-refresh","expires_in":300,"token_type":"Bearer"}""" + private val invalidGrantBody = """{"error":"invalid_grant","error_description":"Token is not active"}""" + + private fun session(expired: Boolean, refreshToken: String? = "refresh-token") = + Session( + accessToken = "old-access", + refreshToken = refreshToken, + idToken = null, + expiresInSeconds = if (expired) 0L else 10_000_000_000L, + obtainedAtEpochSeconds = 0L, + user = UserInfo(), + ) + + private fun apiWith( + onToken: MockRequestHandleScope.() -> HttpResponseData = { + respond(tokenBody, HttpStatusCode.OK, jsonHeaders) + }, + onUserInfo: MockRequestHandleScope.() -> HttpResponseData = { + respond("{}", HttpStatusCode.OK, jsonHeaders) + }, + ): OidcAuthApi { + val engine = MockEngine { request -> + val path = request.url.encodedPath + when { + path.endsWith("openid-configuration") -> respond(discoveryBody, HttpStatusCode.OK, jsonHeaders) + path.endsWith("/token") -> onToken() + path.endsWith("/userinfo") -> onUserInfo() + else -> respond("not found", HttpStatusCode.NotFound) + } + } + val client = + HttpClient(engine) { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + } + return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) + } + + private fun service(store: SessionStore, api: OidcAuthApi) = + AuthService(OAuthConfig("https://idp.example.org", "client", "openid"), store, api) + + // ---- ensureFreshSession ---------------------------------------------------- + + @Test + fun refreshSuccess_updatesSession() = runTest { + val store = FakeSessionStore(session(expired = true)) + val result = service(store, apiWith()).ensureFreshSession() + + assertNotNull(result) + assertEquals("new-access", result.accessToken) + assertEquals("new-access", store.session.value?.accessToken) + assertFalse(store.cleared) + } + + @Test + fun refreshRejectedByProvider_logsOut() = runTest { + val store = FakeSessionStore(session(expired = true)) + val api = apiWith(onToken = { respond(invalidGrantBody, HttpStatusCode.BadRequest, jsonHeaders) }) + + val result = service(store, api).ensureFreshSession() + + assertNull(result) + assertTrue(store.cleared, "a definitive provider rejection must log the user out") + assertNull(store.session.value) + } + + @Test + fun refreshOffline_keepsSession() = runTest { + val original = session(expired = true) + val store = FakeSessionStore(original) + val api = apiWith(onToken = { throw RuntimeException("offline") }) + + val result = service(store, api).ensureFreshSession() + + assertEquals(original, result, "a network failure must NOT log the user out") + assertFalse(store.cleared) + } + + @Test + fun validToken_returnsWithoutContactingProvider() = runTest { + val original = session(expired = false) + val store = FakeSessionStore(original) + val api = + apiWith( + onToken = { throw RuntimeException("must not be called") }, + onUserInfo = { throw RuntimeException("must not be called") }, + ) + + assertEquals(original, service(store, api).ensureFreshSession()) + assertFalse(store.cleared) + } + + // ---- revalidateSession ------------------------------------------------------ + + @Test + fun revalidateRevoked_logsOut() = runTest { + val store = FakeSessionStore(session(expired = false)) + val api = apiWith(onUserInfo = { respond("", HttpStatusCode.Unauthorized) }) + + assertFalse(service(store, api).revalidateSession()) + assertTrue(store.cleared) + } + + @Test + fun revalidateOffline_keepsSession() = runTest { + val store = FakeSessionStore(session(expired = false)) + val api = apiWith(onUserInfo = { throw RuntimeException("offline") }) + + assertTrue(service(store, api).revalidateSession(), "offline probe must not log out") + assertFalse(store.cleared) + } + + // ---- login / buildAuthorizationUrl ----------------------------------------- + + @Test + fun login_buildsAuthorizationUrlWithRequiredPkceParamsAndCompletesOnMatchingState() = runTest { + val store = FakeSessionStore(null) + var capturedAuthUrl: String? = null + val fakeLauncher = + FakeLauncher( + redirectUri = "app://callback", + onAuthorize = { authUrl -> + capturedAuthUrl = authUrl + val state = Url(authUrl).parameters["state"].orEmpty() + AuthResult.Success("app://callback?code=abc123&state=$state") + }, + ) + + val outcome = service(store, apiWith()).login(fakeLauncher) + + assertTrue(outcome is LoginOutcome.Authenticated) + val builtUrl = capturedAuthUrl + assertNotNull(builtUrl) + val params = Url(builtUrl).parameters + assertEquals("code", params["response_type"]) + assertEquals("S256", params["code_challenge_method"]) + assertEquals("app://callback", params["redirect_uri"]) + assertNotNull(params["state"]) + assertNotNull(params["code_challenge"]) + } + + @Test + fun login_withMismatchedState_returnsError() = runTest { + val store = FakeSessionStore(null) + val fakeLauncher = + FakeLauncher( + redirectUri = "app://callback", + onAuthorize = { AuthResult.Success("app://callback?code=abc123&state=wrong-state") }, + ) + + val outcome = service(store, apiWith()).login(fakeLauncher) + + assertTrue(outcome is LoginOutcome.Error) + } +} diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt index ed12be7d..e66e519f 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt @@ -53,12 +53,12 @@ internal actual fun secureRandomBytes(size: Int): ByteArray { * Safari and returns the callback URL directly to the app (no AppDelegate plumbing needed). */ actual class AuthorizationLauncher( - actual val redirectUri: String, + actual override val redirectUri: String, private val callbackScheme: String, -) { +) : AuthorizationLauncherApi { private val contextProvider = PresentationContextProvider() - actual suspend fun authorize(authUrl: String): AuthResult = + actual override suspend fun authorize(authUrl: String): AuthResult = suspendCancellableCoroutine { continuation -> val session = ASWebAuthenticationSession( diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt index a2079fd8..74524488 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt @@ -36,11 +36,11 @@ internal actual fun secureRandomBytes(size: Int): ByteArray = * Desktop login: open the system browser to the identity provider and capture the redirect on a * short-lived localhost loopback server (the OAuth 2.0 native-app recommendation, RFC 8252). */ -actual class AuthorizationLauncher(private val port: Int) { +actual class AuthorizationLauncher(private val port: Int) : AuthorizationLauncherApi { - actual val redirectUri: String = "http://127.0.0.1:$port/callback" + actual override val redirectUri: String = "http://127.0.0.1:$port/callback" - actual suspend fun authorize(authUrl: String): AuthResult = + actual override suspend fun authorize(authUrl: String): AuthResult = suspendCancellableCoroutine { continuation -> val server = HttpServer.create(InetSocketAddress("127.0.0.1", port), 0) server.createContext("/callback") { exchange -> diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt index 95275d62..602d42d7 100644 --- a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt +++ b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt @@ -31,9 +31,9 @@ internal actual fun createKSafe(): KSafe = KSafe() * [PendingAuth] saved in KSafe survives the round-trip. On the next load [consumeRedirectCallback] * picks up the result. */ -actual class AuthorizationLauncher(actual val redirectUri: String) { +actual class AuthorizationLauncher(actual override val redirectUri: String) : AuthorizationLauncherApi { - actual suspend fun authorize(authUrl: String): AuthResult { + actual override suspend fun authorize(authUrl: String): AuthResult { window.location.href = authUrl return AuthResult.Redirecting } From 74de75518de3a7d0f04684420b08521646c76c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:03:56 +0300 Subject: [PATCH 21/73] Add AuthViewModel adapting AuthService to Compose state --- .../reference/app/auth/AuthViewModel.kt | 108 ++++++++++ .../reference/app/auth/AuthViewModelTest.kt | 185 ++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt new file mode 100644 index 00000000..3bfb6883 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +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.launch + +/** + * Adapts [AuthService] to Compose. Deviates from opensrp: [service] is a constructor param + * (Koin-injected via `viewModel { AuthViewModel(get()) }`) instead of self-constructed. + * [AuthorizationLauncher] is platform UI, so it's passed in from the composable rather than + * constructed here. + */ +internal class AuthViewModel(private val service: AuthService) : ViewModel() { + + private val _state = MutableStateFlow(AuthState.Loading) + val state: StateFlow = _state.asStateFlow() + + private val _signingIn = MutableStateFlow(false) + val signingIn: StateFlow = _signingIn.asStateFlow() + + private val _error = MutableStateFlow(null) + val error: StateFlow = _error.asStateFlow() + + private var bootstrapped = false + + /** Restores any saved session and completes a web redirect login. Runs once. */ + fun bootstrap(launcher: AuthorizationLauncher) { + if (bootstrapped) return + bootstrapped = true + viewModelScope.launch { + when (val redirect = service.completeRedirectLoginIfPresent(launcher)) { + is LoginOutcome.Authenticated -> { + _state.value = AuthState.Authenticated(redirect.session) + return@launch + } + is LoginOutcome.Error -> _error.value = redirect.message + else -> Unit + } + val session = service.ensureFreshSession() + if (session != null) { + _state.value = AuthState.Authenticated(session) + revalidateInBackground() + } else { + _state.value = AuthState.Unauthenticated + } + } + } + + /** Test seam: same bootstrap logic, but against [AuthorizationLauncherApi] so tests can fake it. */ + internal suspend fun bootstrapForTest(launcher: AuthorizationLauncherApi) { + val session = service.ensureFreshSession() + _state.value = if (session != null) AuthState.Authenticated(session) else AuthState.Unauthenticated + } + + private fun revalidateInBackground() { + viewModelScope.launch { + if (!service.revalidateSession()) _state.value = AuthState.Unauthenticated + } + } + + fun login(launcher: AuthorizationLauncher) { + viewModelScope.launch { runLogin(launcher) } + } + + /** Test seam: same login logic, but against [AuthorizationLauncherApi] so tests can fake it. */ + internal suspend fun loginForTest(launcher: AuthorizationLauncherApi) = runLogin(launcher) + + private suspend fun runLogin(launcher: AuthorizationLauncherApi) { + _signingIn.value = true + _error.value = null + when (val outcome = service.login(launcher)) { + is LoginOutcome.Authenticated -> _state.value = AuthState.Authenticated(outcome.session) + is LoginOutcome.Redirecting -> Unit // web: page is unloading to the provider + is LoginOutcome.Canceled -> Unit + is LoginOutcome.Error -> _error.value = outcome.message + } + _signingIn.value = false + } + + fun clearError() { + _error.value = null + } + + fun logout() { + viewModelScope.launch { + service.logout() + _state.value = AuthState.Unauthenticated + } + } +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt new file mode 100644 index 00000000..1dd666e5 --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt @@ -0,0 +1,185 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headersOf +import io.ktor.serialization.kotlinx.json.json +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNull +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.Json + +/** + * Drives the real [AuthService] against a [FakeSessionStore] + mocked [OidcAuthApi] (same pattern + * as [AuthServiceTest], one layer up), so [AuthViewModel]'s state transitions are exercised through + * real logic, not a hand-rolled fake of [AuthService] itself. + */ +class AuthViewModelTest { + + private class FakeSessionStore(initial: Session?) : SessionStore { + private val _session = MutableStateFlow(initial) + override val session: StateFlow = _session.asStateFlow() + + override suspend fun load(): Session? = _session.value + + override suspend fun save(session: Session) { + _session.value = session + } + + override suspend fun clear() { + _session.value = null + } + + override suspend fun savePending(pending: PendingAuth) = Unit + + override suspend fun takePending(): PendingAuth? = null + } + + private class FakeLauncher( + override val redirectUri: String, + private val onAuthorize: suspend (String) -> AuthResult, + ) : AuthorizationLauncherApi { + override suspend fun authorize(authUrl: String): AuthResult = onAuthorize(authUrl) + } + + private val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") + + private fun apiThatNeverGetsCalled(): OidcAuthApi { + val engine = MockEngine { respond("must not be called", HttpStatusCode.InternalServerError) } + val client = + HttpClient(engine) { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } + } + return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) + } + + /** + * [AuthService.login] always resolves discovery (to build the authorization URL) before + * invoking the launcher, so a login-path test needs discovery to succeed — only the launcher's + * own result determines the outcome under test. + */ + private fun apiWithWorkingDiscovery(): OidcAuthApi { + val discoveryBody = + """ + { + "authorization_endpoint": "https://idp.example.org/authorize", + "token_endpoint": "https://idp.example.org/token", + "userinfo_endpoint": "https://idp.example.org/userinfo", + "end_session_endpoint": "https://idp.example.org/logout" + } + """ + .trimIndent() + val engine = MockEngine { request -> + if (request.url.encodedPath.endsWith("openid-configuration")) { + respond(discoveryBody, HttpStatusCode.OK, jsonHeaders) + } else { + respond("must not be called", HttpStatusCode.InternalServerError) + } + } + val client = + HttpClient(engine) { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + } + return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) + } + + private fun session() = + Session( + accessToken = "a", + refreshToken = "r", + idToken = null, + expiresInSeconds = 10_000_000_000L, + obtainedAtEpochSeconds = 0, + user = UserInfo(), + ) + + @Test + fun bootstrap_withNoStoredSession_goesUnauthenticated() = runTest { + val service = + AuthService( + OAuthConfig("https://idp.example.org", "c", "openid"), + FakeSessionStore(null), + apiThatNeverGetsCalled(), + ) + val viewModel = AuthViewModel(service) + val launcher = FakeLauncher("app://callback") { AuthResult.Canceled } + + viewModel.bootstrapForTest(launcher) + + assertEquals(AuthState.Unauthenticated, viewModel.state.value) + } + + @Test + fun bootstrap_withValidStoredSession_goesAuthenticatedWithoutContactingProvider() = runTest { + val service = + AuthService( + OAuthConfig("https://idp.example.org", "c", "openid"), + FakeSessionStore(session()), + apiThatNeverGetsCalled(), + ) + val viewModel = AuthViewModel(service) + val launcher = FakeLauncher("app://callback") { AuthResult.Canceled } + + viewModel.bootstrapForTest(launcher) + + assertIs(viewModel.state.value) + } + + @Test + fun login_onError_setsErrorAndClearsSigningIn() = runTest { + val service = + AuthService( + OAuthConfig("https://idp.example.org", "c", "openid"), + FakeSessionStore(null), + apiWithWorkingDiscovery(), + ) + val viewModel = AuthViewModel(service) + val launcher = FakeLauncher("app://callback") { AuthResult.Failure("network down") } + + viewModel.loginForTest(launcher) + + assertEquals("network down", viewModel.error.value) + assertEquals(false, viewModel.signingIn.value) + } + + @Test + fun clearError_removesTheErrorMessage() = runTest { + val service = + AuthService( + OAuthConfig("https://idp.example.org", "c", "openid"), + FakeSessionStore(null), + apiWithWorkingDiscovery(), + ) + val viewModel = AuthViewModel(service) + val launcher = FakeLauncher("app://callback") { AuthResult.Failure("network down") } + viewModel.loginForTest(launcher) + + viewModel.clearError() + + assertNull(viewModel.error.value) + } +} From 2dad93d4849758f37df97718edf3a3779bf728f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:07:35 +0300 Subject: [PATCH 22/73] Wire auth classes through Koin (authModule, AuthViewModel) --- gradle/libs.versions.toml | 1 + ohs-player-reference-app/build.gradle.kts | 5 ++- .../player/reference/app/data/di/AppModule.kt | 22 +++++++++- .../player/reference/app/data/di/InitKoin.kt | 1 + .../reference/app/data/di/AuthModuleTest.kt | 40 +++++++++++++++++++ 5 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c94b66f0..a210fd80 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,6 +41,7 @@ ionspin-bignum = "0.3.10" fhir-data-capture = { module = "dev.ohs.fhir:fhir-data-capture", version.ref = "fhirDataCapture" } kermit = { module = "co.touchlab:kermit", version.ref = "kermit" } koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koinBom" } +koin-test = { module = "io.insert-koin:koin-test" } koin-core = { module = "io.insert-koin:koin-core" } koin-compose = { module = "io.insert-koin:koin-compose" } koin-composeViewmodel = { module = "io.insert-koin:koin-compose-viewmodel" } diff --git a/ohs-player-reference-app/build.gradle.kts b/ohs-player-reference-app/build.gradle.kts index ba2ffd93..d5279eb0 100644 --- a/ohs-player-reference-app/build.gradle.kts +++ b/ohs-player-reference-app/build.gradle.kts @@ -123,7 +123,10 @@ kotlin { implementation(libs.kotlinx.coroutinesSwing) implementation(libs.ktor.client.cio) } - jvmTest.dependencies { implementation(compose.desktop.currentOs) } + jvmTest.dependencies { + implementation(compose.desktop.currentOs) + implementation(libs.koin.test) + } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt index a59bc9af..489725d7 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt @@ -15,8 +15,14 @@ */ package dev.ohs.player.reference.app.data.di -import dev.ohs.fhir.FhirEngineProvider -import dev.ohs.fhir.sync.FhirSyncTask +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.sync.FhirSyncTask +import dev.ohs.player.reference.app.auth.AuthService +import dev.ohs.player.reference.app.auth.AuthViewModel +import dev.ohs.player.reference.app.auth.OAuthConfig +import dev.ohs.player.reference.app.auth.OidcAuthApi +import dev.ohs.player.reference.app.auth.SessionRepository +import dev.ohs.player.reference.app.auth.SessionStore import dev.ohs.player.reference.app.data.repository.FhirEngineRepository import dev.ohs.player.reference.app.data.repository.FhirRepository import dev.ohs.player.reference.app.data.repository.GroupRepository @@ -61,6 +67,17 @@ internal val syncModule = module { single { RunSyncNowUseCase(get()) } } +/** + * `SessionStore`/`SessionRepository`/`AuthService` — everything downstream of the plain + * `SessionRepository` object (kept outside Koin; see its kdoc) is Koin-injected here. + */ +internal val authModule = module { + single { OAuthConfig.Default } + single { SessionRepository } + single { OidcAuthApi(get()) } + single { AuthService(get(), get(), get()) } +} + internal val viewModelModule = module { viewModel { PatientListViewModel(get()) } viewModel { (patientId: String) -> PatientProfileViewModel(patientId, get()) } @@ -70,4 +87,5 @@ internal val viewModelModule = module { QuestionnaireHostViewModel(questionnaireId, launchContext, get()) } viewModel { HomeViewModel(get(), get()) } + viewModel { AuthViewModel(get()) } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt index aa2d7576..ed4a89fc 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/InitKoin.kt @@ -30,6 +30,7 @@ fun initKoin(platformModule: Module) { repositoryModule, serviceModule, syncModule, + authModule, viewModelModule, ) } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt new file mode 100644 index 00000000..0d3beb8b --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.di + +import dev.ohs.player.reference.app.auth.AuthService +import dev.ohs.player.reference.app.auth.AuthViewModel +import kotlin.test.AfterTest +import kotlin.test.Test +import org.koin.core.context.startKoin +import org.koin.core.context.stopKoin +import org.koin.test.KoinTest +import org.koin.test.inject + +class AuthModuleTest : KoinTest { + + private val authService by inject() + + @AfterTest fun tearDown() = stopKoin() + + @Test + fun authModule_resolvesAuthServiceAndAuthViewModel() { + startKoin { modules(authModule, viewModelModule) } + + authService // resolves without throwing + getKoin().get() + } +} From 86206f56195ba7a7ac275e7758fbee8c1de4ae38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:09:36 +0300 Subject: [PATCH 23/73] Authenticate FHIR sync with the session's Bearer token on all platforms --- .../player/reference/app/OhsPlayerApplication.kt | 8 +++++++- .../player/reference/app/MainViewController.kt | 8 +++++++- .../kotlin/dev/ohs/player/reference/app/main.kt | 8 +++++++- .../kotlin/dev/ohs/player/reference/app/main.kt | 16 +++++++++++----- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt index 35f4892a..313ab030 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt @@ -21,6 +21,8 @@ import dev.ohs.fhir.engine.FhirEngine import dev.ohs.fhir.engine.FhirEngineConfiguration import dev.ohs.fhir.engine.FhirEngineProvider import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator +import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module @@ -29,7 +31,11 @@ class OhsPlayerApplication : Application() { super.onCreate() FhirEngineProvider.init( FhirEngineConfiguration( - serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4") + serverConfiguration = + ServerConfiguration( + baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + authenticator = FhirBearerAuthenticator, + ) ), applicationContext, ) diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt index 0d2addee..95ee7121 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt @@ -20,13 +20,19 @@ import dev.ohs.fhir.engine.FhirEngine import dev.ohs.fhir.engine.FhirEngineConfiguration import dev.ohs.fhir.engine.FhirEngineProvider import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator +import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module fun MainViewController() = run { FhirEngineProvider.init( FhirEngineConfiguration( - serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4") + serverConfiguration = + ServerConfiguration( + baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + authenticator = FhirBearerAuthenticator, + ) ) ) initKoin(module { single { FhirEngineProvider.getInstance() } }) diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index 39a0e175..38f6109e 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -21,6 +21,8 @@ import dev.ohs.fhir.engine.FhirEngine import dev.ohs.fhir.engine.FhirEngineConfiguration import dev.ohs.fhir.engine.FhirEngineProvider import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator +import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin import java.io.File import org.koin.dsl.module @@ -31,7 +33,11 @@ fun main() = application { FhirEngineProvider.init( FhirEngineConfiguration( storageDirectory = storageDirectory, - serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4"), + serverConfiguration = + ServerConfiguration( + baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + authenticator = FhirBearerAuthenticator, + ), ) ) initKoin(module { single { FhirEngineProvider.getInstance() } }) diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt index 47af9c42..21b8ec02 100644 --- a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -17,10 +17,12 @@ package dev.ohs.player.reference.app import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.window.ComposeViewport -import dev.ohs.fhir.FhirEngine -import dev.ohs.fhir.FhirEngineConfiguration -import dev.ohs.fhir.FhirEngineProvider -import dev.ohs.fhir.ServerConfiguration +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.FhirEngineConfiguration +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator +import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin import org.koin.dsl.module @@ -28,7 +30,11 @@ import org.koin.dsl.module fun main() { FhirEngineProvider.init( FhirEngineConfiguration( - serverConfiguration = ServerConfiguration(baseUrl = "https://hapi.fhir.org/baseR4") + serverConfiguration = + ServerConfiguration( + baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + authenticator = FhirBearerAuthenticator, + ) ) ) initKoin(module { single { FhirEngineProvider.getInstance() } }) From 8ab372732ea8bb031ae72d269fc1a5b128076784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:11:51 +0300 Subject: [PATCH 24/73] Add adaptive LoginScreen --- .../composeResources/values/strings.xml | 10 + .../app/feature/login/LoginScreen.kt | 277 ++++++++++++++++++ .../app/feature/login/LoginScreenTest.kt | 63 ++++ 3 files changed, 350 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index a8868bea..8714d34f 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -6,4 +6,14 @@ Guardian Other relative Non-relative + + + OHS Player + Care coordination, offline-first + Sign in to sync households and patient records with your organization\'s FHIR server. + Sign in to OHS Player + You\'ll be securely redirected to your organization\'s sign-in page to continue. + Sign in + Sign-in failed + Dismiss \ No newline at end of file diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt new file mode 100644 index 00000000..4b544e6a --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt @@ -0,0 +1,277 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.login + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_brand +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_card_title +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_error_dismiss +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_error_title +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_redirect_hint +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_sign_in +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_subtitle +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_tagline +import org.jetbrains.compose.resources.stringResource + +/** Material 3's "expanded" window size class breakpoint (matches HomeWidthBreakpoint.kt). */ +private val LOGIN_EXPANDED_WIDTH_BREAKPOINT = 840.dp + +/** + * PKCE redirect login — no password form; the primary action hands off to the identity provider. + * Branches on width (never on platform), matching this app's existing manual-breakpoint convention + * (`HomeWidthBreakpoint.kt`): Expanded (≥840dp) shows a brand panel beside a centered sign-in card; + * Compact/Medium show a brand band over a sign-in sheet. + */ +@Composable +fun LoginScreen( + signingIn: Boolean, + error: String?, + onSignIn: () -> Unit, + onErrorDismiss: () -> Unit, +) { + BoxWithConstraints { + if (maxWidth >= LOGIN_EXPANDED_WIDTH_BREAKPOINT) { + ExpandedLogin(signingIn, onSignIn) + } else { + CompactLogin(signingIn, onSignIn) + } + } + + if (error != null) { + LoginErrorDialog(message = error, onDismiss = onErrorDismiss) + } +} + +/** Expanded: brand panel (left) + centered sign-in card on a grey field (right). */ +@Composable +private fun ExpandedLogin(signingIn: Boolean, onSignIn: () -> Unit) { + Row(Modifier.fillMaxSize()) { + Box(Modifier.weight(1f).fillMaxHeight().background(MaterialTheme.colorScheme.primary)) { + Column( + modifier = Modifier.fillMaxSize().safeDrawingPadding().padding(48.dp), + verticalArrangement = Arrangement.SpaceBetween, + ) { + Wordmark(MaterialTheme.colorScheme.onPrimary) + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + Text( + text = stringResource(Res.string.login_tagline), + style = MaterialTheme.typography.displaySmall, + color = MaterialTheme.colorScheme.onPrimary, + ) + Box( + Modifier.width(56.dp) + .height(4.dp) + .background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(2.dp)) + ) + Text( + text = stringResource(Res.string.login_subtitle), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.9f), + ) + } + } + } + + Box( + modifier = + Modifier.weight(1f) + .fillMaxHeight() + .background(MaterialTheme.colorScheme.background) + .safeDrawingPadding() + .padding(32.dp), + contentAlignment = Alignment.Center, + ) { + Surface( + modifier = Modifier.widthIn(max = 424.dp).fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + color = MaterialTheme.colorScheme.surface, + shadowElevation = 8.dp, + ) { + Column( + modifier = Modifier.padding(40.dp), + verticalArrangement = Arrangement.spacedBy(20.dp), + ) { + Text( + text = stringResource(Res.string.login_brand), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.primary, + fontWeight = FontWeight.Bold, + ) + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text(stringResource(Res.string.login_card_title), style = MaterialTheme.typography.headlineSmall) + Text( + text = stringResource(Res.string.login_redirect_hint), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + SignInButton(signingIn, onSignIn) + } + } + } + } +} + +/** Compact/Medium: brand band over a rounded sign-in sheet; button pinned low. */ +@Composable +private fun CompactLogin(signingIn: Boolean, onSignIn: () -> Unit) { + Column(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) { + Column( + modifier = + Modifier.fillMaxWidth() + .background(MaterialTheme.colorScheme.primary) + .statusBarsPadding() + .padding(horizontal = 32.dp, vertical = 32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Wordmark(MaterialTheme.colorScheme.onPrimary) + Text( + text = stringResource(Res.string.login_tagline), + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onPrimary, + textAlign = TextAlign.Center, + ) + Text( + text = stringResource(Res.string.login_subtitle), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.9f), + textAlign = TextAlign.Center, + ) + } + + Column( + modifier = + Modifier.fillMaxWidth() + .weight(1f) + .background( + MaterialTheme.colorScheme.surface, + RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp), + ) + .navigationBarsPadding() + .padding(horizontal = 24.dp) + .padding(top = 28.dp, bottom = 20.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(stringResource(Res.string.login_card_title), style = MaterialTheme.typography.headlineSmall) + Text( + text = stringResource(Res.string.login_redirect_hint), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(Modifier.weight(1f)) + SignInButton(signingIn, onSignIn) + Spacer(Modifier.height(8.dp)) + } + } +} + +@Composable +private fun Wordmark(color: androidx.compose.ui.graphics.Color) { + Text( + text = stringResource(Res.string.login_brand), + style = MaterialTheme.typography.titleLarge, + color = color, + fontWeight = FontWeight.Bold, + ) +} + +@Composable +private fun SignInButton(signingIn: Boolean, onSignIn: () -> Unit) { + Button( + onClick = onSignIn, + enabled = !signingIn, + shape = RoundedCornerShape(8.dp), + contentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp), + modifier = Modifier.fillMaxWidth(), + ) { + if (signingIn) { + CircularProgressIndicator( + modifier = Modifier.size(18.dp), + strokeWidth = 2.dp, + color = MaterialTheme.colorScheme.onPrimary, + ) + } else { + Text(stringResource(Res.string.login_sign_in).uppercase()) + } + } +} + +@Composable +private fun LoginErrorDialog(message: String, onDismiss: () -> Unit) { + AlertDialog( + onDismissRequest = onDismiss, + icon = { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = null, + tint = MaterialTheme.colorScheme.error, + modifier = Modifier.size(48.dp), + ) + }, + title = { + Text( + text = stringResource(Res.string.login_error_title), + color = MaterialTheme.colorScheme.error, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth(), + ) + }, + text = { Text(text = message, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()) }, + confirmButton = { + TextButton(onClick = onDismiss) { Text(stringResource(Res.string.login_error_dismiss)) } + }, + iconContentColor = MaterialTheme.colorScheme.error, + titleContentColor = MaterialTheme.colorScheme.error, + ) +} diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt new file mode 100644 index 00000000..67b64ff9 --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.login + +import androidx.compose.material3.MaterialTheme +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.runComposeUiTest +import kotlin.test.Test +import kotlin.test.assertTrue + +@OptIn(ExperimentalTestApi::class) +class LoginScreenTest { + + @Test + fun tappingSignIn_invokesCallback() = runComposeUiTest { + var clicked = false + setContent { + MaterialTheme { + LoginScreen(signingIn = false, error = null, onSignIn = { clicked = true }, onErrorDismiss = {}) + } + } + + onNodeWithText("Sign in", ignoreCase = true).performClick() + + assertTrue(clicked) + } + + @Test + fun error_showsDialogWithMessage_andDismissClearsIt() = runComposeUiTest { + var dismissed = false + setContent { + MaterialTheme { + LoginScreen( + signingIn = false, + error = "Sign-in failed: invalid_grant", + onSignIn = {}, + onErrorDismiss = { dismissed = true }, + ) + } + } + + assertTrue(onAllNodesWithText("Sign-in failed: invalid_grant").fetchSemanticsNodes().isNotEmpty()) + onNodeWithText("Dismiss", ignoreCase = true).performClick() + + assertTrue(dismissed) + } +} From a0f990d3f4e85a3f2805abfb188b5664ca3ec789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:14:35 +0300 Subject: [PATCH 25/73] Gate App() NavHost behind AuthViewModel's auth state --- .../dev/ohs/player/reference/app/App.kt | 188 +++++++++++------- .../player/reference/app/AppAuthGatingTest.kt | 120 +++++++++++ 2 files changed, 232 insertions(+), 76 deletions(-) create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index 74eeaa9c..c79321dc 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -15,9 +15,17 @@ */ package dev.ohs.player.reference.app +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -25,13 +33,18 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import androidx.savedstate.read import dev.ohs.player.library.registry.LocalViewRegistry +import dev.ohs.player.reference.app.auth.AuthState +import dev.ohs.player.reference.app.auth.AuthViewModel +import dev.ohs.player.reference.app.auth.rememberAuthorizationLauncher import dev.ohs.player.reference.app.feature.group.profile.GroupProfileScreen import dev.ohs.player.reference.app.feature.home.HomeScreen +import dev.ohs.player.reference.app.feature.login.LoginScreen import dev.ohs.player.reference.app.feature.patient.profile.PatientProfileScreen import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireHostScreen import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireIds import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid +import org.koin.compose.viewmodel.koinViewModel @Composable fun App() { @@ -39,89 +52,112 @@ fun App() { CompositionLocalProvider(LocalViewRegistry provides registry) { OhsPlayerTheme { - val navController = rememberNavController() - NavHost(navController = navController, startDestination = "home") { + val authViewModel: AuthViewModel = koinViewModel() + val launcher = rememberAuthorizationLauncher() + val authState by authViewModel.state.collectAsStateWithLifecycle() + val signingIn by authViewModel.signingIn.collectAsStateWithLifecycle() + val authError by authViewModel.error.collectAsStateWithLifecycle() - // Screen 1: Home (adaptive navigation drawer shell around the household list) - composable("home") { - HomeScreen( - onGroupClick = { id -> navController.navigate("groupProfile/$id") }, - onDataCaptureClick = { - navController.navigate( - questionnaireHostRoute(questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION) - ) - }, - ) - } + LaunchedEffect(launcher) { authViewModel.bootstrap(launcher) } - composable( - route = "questionnaireHost/{questionnaireId}?patientId={patientId}&groupId={groupId}", - arguments = - listOf( - navArgument("questionnaireId") { type = NavType.StringType }, - navArgument("patientId") { - type = NavType.StringType - nullable = true - defaultValue = null - }, - navArgument("groupId") { - type = NavType.StringType - nullable = true - defaultValue = null - }, - ), - ) { back -> - val questionnaireId = - back.arguments?.read { getStringOrNull("questionnaireId") }.orEmpty() - val patientId = back.arguments?.read { getStringOrNull("patientId") } - val groupId = back.arguments?.read { getStringOrNull("groupId") } - QuestionnaireHostScreen( - questionnaireId = questionnaireId, - patientId = patientId, - groupId = groupId, - onBack = { navController.popBackStack() }, + when (authState) { + is AuthState.Loading -> + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + is AuthState.Unauthenticated -> + LoginScreen( + signingIn = signingIn, + error = authError, + onSignIn = { authViewModel.login(launcher) }, + onErrorDismiss = { authViewModel.clearError() }, ) - } + is AuthState.Authenticated -> { + val navController = rememberNavController() + NavHost(navController = navController, startDestination = "home") { - // Screen 2: Household profile (head + members) - composable( - route = "groupProfile/{groupId}", - arguments = listOf(navArgument("groupId") { type = NavType.StringType }), - ) { back -> - val groupId = back.arguments?.read { getStringOrNull("groupId") }.orEmpty() - GroupProfileScreen( - groupId = groupId, - onBack = { navController.popBackStack() }, - onMemberClick = { id -> navController.navigate("patientProfile/$id") }, - onAddMembers = { - navController.navigate( - questionnaireHostRoute( - questionnaireId = QuestionnaireIds.HOUSEHOLD_MEMBERS, - groupId = groupId, - ) + // Screen 1: Home (adaptive navigation drawer shell around the household list) + composable("home") { + HomeScreen( + onGroupClick = { id -> navController.navigate("groupProfile/$id") }, + onDataCaptureClick = { + navController.navigate( + questionnaireHostRoute(questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION) + ) + }, ) - }, - ) - } + } - // Screen 3: Patient IPS summary - composable( - route = "patientProfile/{patientId}", - arguments = listOf(navArgument("patientId") { type = NavType.StringType }), - ) { back -> - val patientId = back.arguments?.read { getStringOrNull("patientId") }.orEmpty() - PatientProfileScreen( - patientId = patientId, - onBack = { navController.popBackStack() }, - onAddClinicalData = { - navController.navigate( - questionnaireHostRoute( - questionnaireId = QuestionnaireIds.PATIENT_CLINICAL_DATA, - patientId = patientId, - ) + composable( + route = "questionnaireHost/{questionnaireId}?patientId={patientId}&groupId={groupId}", + arguments = + listOf( + navArgument("questionnaireId") { type = NavType.StringType }, + navArgument("patientId") { + type = NavType.StringType + nullable = true + defaultValue = null + }, + navArgument("groupId") { + type = NavType.StringType + nullable = true + defaultValue = null + }, + ), + ) { back -> + val questionnaireId = + back.arguments?.read { getStringOrNull("questionnaireId") }.orEmpty() + val patientId = back.arguments?.read { getStringOrNull("patientId") } + val groupId = back.arguments?.read { getStringOrNull("groupId") } + QuestionnaireHostScreen( + questionnaireId = questionnaireId, + patientId = patientId, + groupId = groupId, + onBack = { navController.popBackStack() }, ) - }, - ) + } + + // Screen 2: Household profile (head + members) + composable( + route = "groupProfile/{groupId}", + arguments = listOf(navArgument("groupId") { type = NavType.StringType }), + ) { back -> + val groupId = back.arguments?.read { getStringOrNull("groupId") }.orEmpty() + GroupProfileScreen( + groupId = groupId, + onBack = { navController.popBackStack() }, + onMemberClick = { id -> navController.navigate("patientProfile/$id") }, + onAddMembers = { + navController.navigate( + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.HOUSEHOLD_MEMBERS, + groupId = groupId, + ) + ) + }, + ) + } + + // Screen 3: Patient IPS summary + composable( + route = "patientProfile/{patientId}", + arguments = listOf(navArgument("patientId") { type = NavType.StringType }), + ) { back -> + val patientId = back.arguments?.read { getStringOrNull("patientId") }.orEmpty() + PatientProfileScreen( + patientId = patientId, + onBack = { navController.popBackStack() }, + onAddClinicalData = { + navController.navigate( + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.PATIENT_CLINICAL_DATA, + patientId = patientId, + ) + ) + }, + ) + } + } } } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt new file mode 100644 index 00000000..08001b77 --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.runComposeUiTest +import dev.ohs.fhir.engine.sync.FhirDataStore +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.createDataStore +import dev.ohs.player.reference.app.auth.AuthService +import dev.ohs.player.reference.app.auth.OAuthConfig +import dev.ohs.player.reference.app.auth.OidcAuthApi +import dev.ohs.player.reference.app.auth.PendingAuth +import dev.ohs.player.reference.app.auth.Session +import dev.ohs.player.reference.app.auth.SessionStore +import dev.ohs.player.reference.app.data.di.repositoryModule +import dev.ohs.player.reference.app.data.di.viewModelModule +import dev.ohs.player.reference.app.data.repository.FhirRepository +import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.http.HttpStatusCode +import io.ktor.serialization.kotlinx.json.json +import java.nio.file.Files +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertTrue +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.serialization.json.Json +import org.koin.core.context.startKoin +import org.koin.core.context.stopKoin +import org.koin.dsl.module + +private class FakeAppSyncNowUseCase : SyncNowUseCase { + override suspend fun invoke(): SyncJobStatus = SyncJobStatus.Succeeded() +} + +private class InMemorySessionStore(initial: Session? = null) : SessionStore { + private val _session = MutableStateFlow(initial) + override val session: StateFlow = _session.asStateFlow() + + override suspend fun load(): Session? = _session.value + + override suspend fun save(session: Session) { + _session.value = session + } + + override suspend fun clear() { + _session.value = null + } + + override suspend fun savePending(pending: PendingAuth) = Unit + + override suspend fun takePending(): PendingAuth? = null +} + +@OptIn(ExperimentalTestApi::class) +class AppAuthGatingTest { + + private fun newFhirDataStore(): FhirDataStore { + val path = Files.createTempFile("app-auth-gating-test", ".preferences_pb").toString() + return FhirDataStore(createDataStore { path }) + } + + private fun startTestKoin() { + val engine = MockEngine { respond("not found", HttpStatusCode.NotFound) } + val client = + HttpClient(engine) { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } + } + startKoin { + modules( + module { + single { InMemorySampleFhirRepository() } + single { newFhirDataStore() } + single { FakeAppSyncNowUseCase() } + single { OAuthConfig("https://idp.example.org", "client", "openid") } + single { InMemorySessionStore(initial = null) } + single { OidcAuthApi(get(), client) } + single { AuthService(get(), get(), get()) } + }, + repositoryModule, + viewModelModule, + ) + } + } + + @AfterTest fun tearDown() = stopKoin() + + @Test + fun signedOut_showsLoginScreen() = runComposeUiTest { + startTestKoin() + + setContent { App() } + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sign in", ignoreCase = true).fetchSemanticsNodes().isNotEmpty() + } + assertTrue(onAllNodesWithText("Sign in", ignoreCase = true).fetchSemanticsNodes().isNotEmpty()) + } +} From 9d8645c83ba03a53e824193d6f6a896ee6ffe3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:17:14 +0300 Subject: [PATCH 26/73] Add Sign out entry to the home navigation drawer Also fixes the pre-existing HomeScreenTest.kt/HomeViewModelTest.kt dev.ohs.fhir.sync.* import issue (should be dev.ohs.fhir.engine.sync.*) from the earlier jvmTest-unblock, so the full jvmTest suite is green. --- .../kotlin/dev/ohs/player/reference/app/App.kt | 1 + .../reference/app/feature/home/HomeScreen.kt | 16 +++++++++++++++- .../reference/app/feature/home/HomeScreenTest.kt | 6 +++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index c79321dc..43a0bb23 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -85,6 +85,7 @@ fun App() { questionnaireHostRoute(questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION) ) }, + onSignOut = { authViewModel.logout() }, ) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index 44606650..72ccccac 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ExitToApp import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.CircularProgressIndicator @@ -61,7 +62,11 @@ import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun HomeScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit) { +fun HomeScreen( + onGroupClick: (String) -> Unit, + onDataCaptureClick: () -> Unit, + onSignOut: () -> Unit, +) { val homeViewModel: HomeViewModel = koinViewModel() val uiState by homeViewModel.uiState.collectAsStateWithLifecycle() @@ -135,6 +140,15 @@ fun HomeScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit) { } }, ) + NavigationDrawerItem( + label = { Text("Sign out") }, + icon = { Icon(Icons.AutoMirrored.Filled.ExitToApp, contentDescription = null) }, + selected = false, + onClick = { + onSignOut() + closeDrawerIfCompact() + }, + ) } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index 2ba831c9..97a031c2 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -77,7 +77,7 @@ class HomeScreenTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } } } @@ -102,7 +102,7 @@ class HomeScreenTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } } } @@ -129,7 +129,7 @@ class HomeScreenTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}) } + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } } } From 06ed1b07621929a172c145b9e73224aacfd25601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:33:07 +0300 Subject: [PATCH 27/73] Apply formatting pass and bump ohs-fhir-engine to 2.0.0-alpha02 Reformats the auth module and sync/home files to project style, and updates the FHIR engine dependency off the local snapshot version. --- gradle.properties | 3 +++ gradle/libs.versions.toml | 2 +- ohs-player-reference-app/build.gradle.kts | 3 ++- .../dev/ohs/player/reference/app/App.kt | 4 +++- .../player/reference/app/auth/AuthService.kt | 16 +++++++++---- .../reference/app/auth/AuthViewModel.kt | 7 ++++-- .../app/auth/AuthorizationLauncher.kt | 4 ++-- .../player/reference/app/auth/OAuthConfig.kt | 5 +++- .../player/reference/app/auth/OidcAuthApi.kt | 13 +++++++++-- .../data/repository/FhirEngineRepository.kt | 8 +++---- .../app/data/sync/AppFhirSyncTask.kt | 20 ++++++++-------- .../app/data/sync/InMemoryTimestampContext.kt | 2 +- .../reference/app/data/sync/SyncNowUseCase.kt | 6 ++--- .../app/feature/home/HomeViewModel.kt | 4 ++-- .../app/feature/login/LoginScreen.kt | 14 ++++++++--- .../reference/app/auth/AuthServiceTest.kt | 23 ++++++++++++++----- .../reference/app/auth/AuthViewModelTest.kt | 19 +++++++++------ .../reference/app/auth/OidcAuthApiTest.kt | 9 +++++++- .../player/reference/app/AppAuthGatingTest.kt | 4 +--- .../app/auth/SessionRepositoryTest.kt | 3 +-- .../app/feature/login/LoginScreenTest.kt | 11 +++++++-- .../player/reference/app/auth/Platform.web.kt | 3 ++- 22 files changed, 123 insertions(+), 60 deletions(-) diff --git a/gradle.properties b/gradle.properties index 8ce6b587..f735ca87 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,3 +15,6 @@ android.useAndroidX=true #MPP kotlin.mpp.enableCInteropCommonization=true + +# Enabled parallel sync for Gradle 9.4+ +org.gradle.tooling.parallel=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a210fd80..1553e58c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ navigation-compose = "2.9.2" material3 = "1.10.0-alpha05" kotlinxSerializationJson = "1.11.0" spotless = "8.6.0" -ohsFhirEngine = "2.0.0-alpha01-local" +ohsFhirEngine = "2.0.0-alpha02" ohsFhirModel = "1.0.0-beta05" ohsFhirPath = "1.0.0-beta03" kotlinxDatetime = "0.8.0" diff --git a/ohs-player-reference-app/build.gradle.kts b/ohs-player-reference-app/build.gradle.kts index d5279eb0..741b25c8 100644 --- a/ohs-player-reference-app/build.gradle.kts +++ b/ohs-player-reference-app/build.gradle.kts @@ -221,7 +221,8 @@ val generateAuthConfig = inputs.property("versionCode", versionCode) outputs.dir(outDir) doLast { - val pkgDir = outDir.get().asFile.resolve("dev/ohs/player/reference/app/auth").apply { mkdirs() } + val pkgDir = + outDir.get().asFile.resolve("dev/ohs/player/reference/app/auth").apply { mkdirs() } pkgDir .resolve("GeneratedAuthConfig.kt") .writeText( diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index 43a0bb23..aaf3b377 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -82,7 +82,9 @@ fun App() { onGroupClick = { id -> navController.navigate("groupProfile/$id") }, onDataCaptureClick = { navController.navigate( - questionnaireHostRoute(questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION) + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION + ) ) }, onSignOut = { authViewModel.logout() }, diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt index cb71a6e6..3864c99c 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt @@ -66,7 +66,9 @@ internal class AuthService( /** Exchanges the callback's `code` for tokens and persists the session. */ private suspend fun completeLogin(callbackUrl: String, redirectUri: String): LoginOutcome { val params = Url(callbackUrl).parameters - params["error"]?.let { error -> return LoginOutcome.Error(params["error_description"] ?: error) } + params["error"]?.let { error -> + return LoginOutcome.Error(params["error_description"] ?: error) + } val code = params["code"] ?: return LoginOutcome.Error("Missing authorization code") val returnedState = params["state"] @@ -88,9 +90,9 @@ internal class AuthService( * Returns a usable session at startup, refreshing an expired access token when possible. * * Offline-safe: the session is cleared (→ login) ONLY on a definitive provider rejection - * ([AuthException]). A transient/offline failure keeps the stored session. Server-side - * revocation while the access token is still locally valid is caught separately (and - * non-blockingly) by [revalidateSession]. + * ([AuthException]). A transient/offline failure keeps the stored session. Server-side revocation + * while the access token is still locally valid is caught separately (and non-blockingly) by + * [revalidateSession]. */ suspend fun ensureFreshSession(): Session? { val session = repository.load() ?: return null @@ -148,7 +150,11 @@ internal class AuthService( repository.clear() } - private suspend fun buildAuthorizationUrl(redirectUri: String, pkce: PkcePair, state: String): String = + private suspend fun buildAuthorizationUrl( + redirectUri: String, + pkce: PkcePair, + state: String, + ): String = URLBuilder(api.endpoints().authorizationEndpoint) .apply { parameters.append("client_id", config.clientId) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt index 3bfb6883..16c0b9b2 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt @@ -64,10 +64,13 @@ internal class AuthViewModel(private val service: AuthService) : ViewModel() { } } - /** Test seam: same bootstrap logic, but against [AuthorizationLauncherApi] so tests can fake it. */ + /** + * Test seam: same bootstrap logic, but against [AuthorizationLauncherApi] so tests can fake it. + */ internal suspend fun bootstrapForTest(launcher: AuthorizationLauncherApi) { val session = service.ensureFreshSession() - _state.value = if (session != null) AuthState.Authenticated(session) else AuthState.Unauthenticated + _state.value = + if (session != null) AuthState.Authenticated(session) else AuthState.Unauthenticated } private fun revalidateInBackground() { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt index 8ff82eaf..258a3358 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt @@ -46,8 +46,8 @@ expect class AuthorizationLauncher : AuthorizationLauncherApi { override suspend fun authorize(authUrl: String): AuthResult /** - * Web only: if this page load is a redirect back from the provider, returns the callback URL - * (and clears it from the address bar). Returns null otherwise and on every non-web platform. + * Web only: if this page load is a redirect back from the provider, returns the callback URL (and + * clears it from the address bar). Returns null otherwise and on every non-web platform. */ fun consumeRedirectCallback(): String? } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt index 6c79ffcd..50390491 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt @@ -24,7 +24,10 @@ package dev.ohs.player.reference.app.auth * by the [AuthorizationLauncher]. */ data class OAuthConfig( - /** OIDC issuer, e.g. a Keycloak realm `https://host/realms/ohs-player` or `https://x.zitadel.cloud`. */ + /** + * OIDC issuer, e.g. a Keycloak realm `https://host/realms/ohs-player` or + * `https://x.zitadel.cloud`. + */ val issuer: String, val clientId: String, /** Space-separated OAuth scopes, e.g. "openid profile email offline_access". */ diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt index 2c83c202..8dae6397 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt @@ -109,7 +109,9 @@ internal class OidcAuthApi( suspend fun fetchUserInfo(accessToken: String): UserInfo = httpClient - .get(endpoints().userInfoEndpoint) { header(HttpHeaders.Authorization, "Bearer $accessToken") } + .get(endpoints().userInfoEndpoint) { + header(HttpHeaders.Authorization, "Bearer $accessToken") + } .body() /** @@ -156,7 +158,14 @@ internal class OidcAuthApi( companion object { fun defaultHttpClient(): HttpClient = HttpClient { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + install(ContentNegotiation) { + json( + Json { + ignoreUnknownKeys = true + isLenient = true + } + ) + } install(HttpTimeout) { requestTimeoutMillis = 15_000 connectTimeoutMillis = 10_000 diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt index 60ea2820..eeb91aa6 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt @@ -15,13 +15,13 @@ */ package dev.ohs.player.reference.app.data.repository -import dev.ohs.fhir.FhirEngine -import dev.ohs.fhir.db.ResourceNotFoundException +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.db.ResourceNotFoundException +import dev.ohs.fhir.engine.resourceType +import dev.ohs.fhir.engine.search.Search import dev.ohs.fhir.model.r4.Bundle import dev.ohs.fhir.model.r4.Resource import dev.ohs.fhir.model.r4.terminologies.ResourceType -import dev.ohs.fhir.resourceType -import dev.ohs.fhir.search.Search import dev.ohs.player.reference.app.generateId import dev.ohs.player.reference.app.util.FhirJson import kotlinx.coroutines.flow.MutableStateFlow diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt index 7e20dcc1..39aeab94 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt @@ -15,17 +15,17 @@ */ package dev.ohs.player.reference.app.data.sync -import dev.ohs.fhir.FhirEngine +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.sync.AcceptLocalConflictResolver +import dev.ohs.fhir.engine.sync.ConflictResolver +import dev.ohs.fhir.engine.sync.DownloadWorkManager +import dev.ohs.fhir.engine.sync.FhirSyncTask +import dev.ohs.fhir.engine.sync.download.ResourceParamsBasedDownloadWorkManager +import dev.ohs.fhir.engine.sync.download.ResourceSearchParams +import dev.ohs.fhir.engine.sync.upload.HttpCreateMethod +import dev.ohs.fhir.engine.sync.upload.HttpUpdateMethod +import dev.ohs.fhir.engine.sync.upload.UploadStrategy import dev.ohs.fhir.model.r4.terminologies.ResourceType -import dev.ohs.fhir.sync.AcceptLocalConflictResolver -import dev.ohs.fhir.sync.ConflictResolver -import dev.ohs.fhir.sync.DownloadWorkManager -import dev.ohs.fhir.sync.FhirSyncTask -import dev.ohs.fhir.sync.download.ResourceParamsBasedDownloadWorkManager -import dev.ohs.fhir.sync.download.ResourceSearchParams -import dev.ohs.fhir.sync.upload.HttpCreateMethod -import dev.ohs.fhir.sync.upload.HttpUpdateMethod -import dev.ohs.fhir.sync.upload.UploadStrategy /** * Resource types downloaded on every sync, with their search parameters. Empty parameters mean diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt index 3b0d42d3..af935e38 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt @@ -15,8 +15,8 @@ */ package dev.ohs.player.reference.app.data.sync +import dev.ohs.fhir.engine.sync.download.ResourceParamsBasedDownloadWorkManager import dev.ohs.fhir.model.r4.terminologies.ResourceType -import dev.ohs.fhir.sync.download.ResourceParamsBasedDownloadWorkManager /** * In-memory implementation of [ResourceParamsBasedDownloadWorkManager.TimestampContext]. Tracks diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt index 372c6682..2281389e 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt @@ -15,9 +15,9 @@ */ package dev.ohs.player.reference.app.data.sync -import dev.ohs.fhir.sync.FhirSyncTask -import dev.ohs.fhir.sync.SyncJobStatus -import dev.ohs.fhir.sync.runSync +import dev.ohs.fhir.engine.sync.FhirSyncTask +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.runSync /** * Triggers a one-time sync and returns its terminal result. A dedicated interface (rather than diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt index 75922982..2f2c2b13 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt @@ -17,8 +17,8 @@ package dev.ohs.player.reference.app.feature.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import dev.ohs.fhir.sync.FhirDataStore -import dev.ohs.fhir.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.FhirDataStore +import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import kotlin.time.Instant import kotlinx.coroutines.CancellationException diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt index 4b544e6a..e7fc2b35 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt @@ -147,7 +147,10 @@ private fun ExpandedLogin(signingIn: Boolean, onSignIn: () -> Unit) { fontWeight = FontWeight.Bold, ) Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - Text(stringResource(Res.string.login_card_title), style = MaterialTheme.typography.headlineSmall) + Text( + stringResource(Res.string.login_card_title), + style = MaterialTheme.typography.headlineSmall, + ) Text( text = stringResource(Res.string.login_redirect_hint), style = MaterialTheme.typography.bodyMedium, @@ -202,7 +205,10 @@ private fun CompactLogin(signingIn: Boolean, onSignIn: () -> Unit) { .padding(top = 28.dp, bottom = 20.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - Text(stringResource(Res.string.login_card_title), style = MaterialTheme.typography.headlineSmall) + Text( + stringResource(Res.string.login_card_title), + style = MaterialTheme.typography.headlineSmall, + ) Text( text = stringResource(Res.string.login_redirect_hint), style = MaterialTheme.typography.bodyMedium, @@ -267,7 +273,9 @@ private fun LoginErrorDialog(message: String, onDismiss: () -> Unit) { modifier = Modifier.fillMaxWidth(), ) }, - text = { Text(text = message, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()) }, + text = { + Text(text = message, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()) + }, confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.login_error_dismiss)) } }, diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt index 070f400d..9681abf1 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt @@ -39,8 +39,8 @@ import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json /** - * Verifies the offline-safe session logic: a logout happens ONLY on a definitive provider - * rejection (4xx), never on a network/offline failure, which must keep the local session. + * Verifies the offline-safe session logic: a logout happens ONLY on a definitive provider rejection + * (4xx), never on a network/offline failure, which must keep the local session. */ class AuthServiceTest { @@ -49,6 +49,7 @@ class AuthServiceTest { override val session: StateFlow = _session.asStateFlow() var cleared = false private set + private var pending: PendingAuth? = null override suspend fun load(): Session? = _session.value @@ -89,7 +90,8 @@ class AuthServiceTest { .trimIndent() private val tokenBody = """{"access_token":"new-access","refresh_token":"new-refresh","expires_in":300,"token_type":"Bearer"}""" - private val invalidGrantBody = """{"error":"invalid_grant","error_description":"Token is not active"}""" + private val invalidGrantBody = + """{"error":"invalid_grant","error_description":"Token is not active"}""" private fun session(expired: Boolean, refreshToken: String? = "refresh-token") = Session( @@ -112,7 +114,8 @@ class AuthServiceTest { val engine = MockEngine { request -> val path = request.url.encodedPath when { - path.endsWith("openid-configuration") -> respond(discoveryBody, HttpStatusCode.OK, jsonHeaders) + path.endsWith("openid-configuration") -> + respond(discoveryBody, HttpStatusCode.OK, jsonHeaders) path.endsWith("/token") -> onToken() path.endsWith("/userinfo") -> onUserInfo() else -> respond("not found", HttpStatusCode.NotFound) @@ -120,7 +123,14 @@ class AuthServiceTest { } val client = HttpClient(engine) { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + install(ContentNegotiation) { + json( + Json { + ignoreUnknownKeys = true + isLenient = true + } + ) + } } return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) } @@ -144,7 +154,8 @@ class AuthServiceTest { @Test fun refreshRejectedByProvider_logsOut() = runTest { val store = FakeSessionStore(session(expired = true)) - val api = apiWith(onToken = { respond(invalidGrantBody, HttpStatusCode.BadRequest, jsonHeaders) }) + val api = + apiWith(onToken = { respond(invalidGrantBody, HttpStatusCode.BadRequest, jsonHeaders) }) val result = service(store, api).ensureFreshSession() diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt index 1dd666e5..fa408dbf 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt @@ -71,16 +71,14 @@ class AuthViewModelTest { private fun apiThatNeverGetsCalled(): OidcAuthApi { val engine = MockEngine { respond("must not be called", HttpStatusCode.InternalServerError) } val client = - HttpClient(engine) { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } - } + HttpClient(engine) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) } /** - * [AuthService.login] always resolves discovery (to build the authorization URL) before - * invoking the launcher, so a login-path test needs discovery to succeed — only the launcher's - * own result determines the outcome under test. + * [AuthService.login] always resolves discovery (to build the authorization URL) before invoking + * the launcher, so a login-path test needs discovery to succeed — only the launcher's own result + * determines the outcome under test. */ private fun apiWithWorkingDiscovery(): OidcAuthApi { val discoveryBody = @@ -102,7 +100,14 @@ class AuthViewModelTest { } val client = HttpClient(engine) { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + install(ContentNegotiation) { + json( + Json { + ignoreUnknownKeys = true + isLenient = true + } + ) + } } return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt index 7a9574ca..d937c6a0 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApiTest.kt @@ -69,7 +69,14 @@ class OidcAuthApiTest { } val client = HttpClient(engine) { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; isLenient = true }) } + install(ContentNegotiation) { + json( + Json { + ignoreUnknownKeys = true + isLenient = true + } + ) + } } return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt index 08001b77..cf5807a5 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt @@ -84,9 +84,7 @@ class AppAuthGatingTest { private fun startTestKoin() { val engine = MockEngine { respond("not found", HttpStatusCode.NotFound) } val client = - HttpClient(engine) { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } - } + HttpClient(engine) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } startKoin { modules( module { diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt index 7762214e..46a89507 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/SessionRepositoryTest.kt @@ -23,8 +23,7 @@ import kotlinx.coroutines.test.runTest class SessionRepositoryTest { - @AfterTest - fun tearDown() = runTest { SessionRepository.clear() } + @AfterTest fun tearDown() = runTest { SessionRepository.clear() } private fun testSession() = Session( diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt index 67b64ff9..fd2cf2e8 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt @@ -32,7 +32,12 @@ class LoginScreenTest { var clicked = false setContent { MaterialTheme { - LoginScreen(signingIn = false, error = null, onSignIn = { clicked = true }, onErrorDismiss = {}) + LoginScreen( + signingIn = false, + error = null, + onSignIn = { clicked = true }, + onErrorDismiss = {}, + ) } } @@ -55,7 +60,9 @@ class LoginScreenTest { } } - assertTrue(onAllNodesWithText("Sign-in failed: invalid_grant").fetchSemanticsNodes().isNotEmpty()) + assertTrue( + onAllNodesWithText("Sign-in failed: invalid_grant").fetchSemanticsNodes().isNotEmpty() + ) onNodeWithText("Dismiss", ignoreCase = true).performClick() assertTrue(dismissed) diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt index 602d42d7..e3049cd7 100644 --- a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt +++ b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt @@ -31,7 +31,8 @@ internal actual fun createKSafe(): KSafe = KSafe() * [PendingAuth] saved in KSafe survives the round-trip. On the next load [consumeRedirectCallback] * picks up the result. */ -actual class AuthorizationLauncher(actual override val redirectUri: String) : AuthorizationLauncherApi { +actual class AuthorizationLauncher(actual override val redirectUri: String) : + AuthorizationLauncherApi { actual override suspend fun authorize(authUrl: String): AuthResult { window.location.href = authUrl From 8bfa93b2cb61ce2a56db04a52a98b4f895203ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:47:39 +0300 Subject: [PATCH 28/73] Fix Android Internet permission --- ohs-player-reference-app/src/androidMain/AndroidManifest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ohs-player-reference-app/src/androidMain/AndroidManifest.xml b/ohs-player-reference-app/src/androidMain/AndroidManifest.xml index f30fef9e..4f9a0f2c 100644 --- a/ohs-player-reference-app/src/androidMain/AndroidManifest.xml +++ b/ohs-player-reference-app/src/androidMain/AndroidManifest.xml @@ -1,6 +1,9 @@ + + + Date: Tue, 28 Jul 2026 18:58:47 +0300 Subject: [PATCH 29/73] Add FhirRepository.hasAnyData(), backed by FhirEngine.count() --- .../data/repository/FhirEngineRepository.kt | 4 ++++ .../app/data/repository/FhirRepository.kt | 3 +++ .../InMemorySampleFhirRepository.kt | 2 ++ .../repository/FhirEngineRepositoryTest.kt | 21 +++++++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt index eeb91aa6..62b6410c 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt @@ -69,6 +69,10 @@ class FhirEngineRepository(private val fhirEngine: FhirEngine) : FhirRepository } } + override suspend fun hasAnyData(): Boolean = + fhirEngine.count(Search(ResourceType.Patient)) > 0 || + fhirEngine.count(Search(ResourceType.Group)) > 0 + private suspend fun upsertResource(resource: Resource) { val withId = if (resource.id == null) resource.withId(generateId()) else resource val type = ResourceType.valueOf(withId.resourceType) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt index 6557a11e..75695bce 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt @@ -37,4 +37,7 @@ interface FhirRepository { suspend fun get(resourceType: String, id: String): Resource? suspend fun all(resourceType: String): List + + /** True once any `Patient` or `Group` has been persisted locally. Drives the initial sync gate. */ + suspend fun hasAnyData(): Boolean } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt index 54ff3c41..70dafb7c 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt @@ -48,6 +48,8 @@ class InMemorySampleFhirRepository : FhirRepository { return resourcesByType[resourceType]?.values?.toList().orEmpty() } + override suspend fun hasAnyData(): Boolean = resourcesByType.values.any { it.isNotEmpty() } + private fun store(resource: Resource) { val id = resource.id ?: return resourcesByType.getOrPut(resource.resourceType) { mutableMapOf() }[id] = resource diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt index eb4c4fa2..a7af2b02 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt @@ -224,4 +224,25 @@ class FhirEngineRepositoryTest { assertEquals(1, groups.size) assertEquals("Patient/patient-abs-1", groups.first().member.first().entity.reference?.value) } + + @Test + fun hasAnyData_onEmptyDatabase_isFalse() = runTest { + val repository = FhirEngineRepository(fhirEngine) + + assertEquals(false, repository.hasAnyData()) + } + + @Test + fun hasAnyData_afterUpsertingPatient_isTrue() = runTest { + val repository = FhirEngineRepository(fhirEngine) + val patient = + json.decodeFromString( + Patient.serializer(), + """{"resourceType": "Patient", "id": "patient-has-data"}""", + ) + + repository.upsert(patient) + + assertEquals(true, repository.hasAnyData()) + } } From c9b100266ec5ed59bfb14e32427d45a0fd1c5dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:01:24 +0300 Subject: [PATCH 30/73] Clear local FHIR data on logout (fhirEngine.clearDatabase()) --- .../reference/app/auth/AuthViewModel.kt | 20 ++- .../player/reference/app/data/di/AppModule.kt | 2 +- .../reference/app/auth/AuthViewModelTest.kt | 65 +++++++- .../app/auth/AuthViewModelLogoutTest.kt | 142 ++++++++++++++++++ 4 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt index 16c0b9b2..3a79bbc7 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt @@ -17,6 +17,7 @@ package dev.ohs.player.reference.app.auth import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import dev.ohs.fhir.engine.FhirEngine import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -28,7 +29,10 @@ import kotlinx.coroutines.launch * [AuthorizationLauncher] is platform UI, so it's passed in from the composable rather than * constructed here. */ -internal class AuthViewModel(private val service: AuthService) : ViewModel() { +internal class AuthViewModel( + private val service: AuthService, + private val fhirEngine: FhirEngine, +) : ViewModel() { private val _state = MutableStateFlow(AuthState.Loading) val state: StateFlow = _state.asStateFlow() @@ -103,9 +107,15 @@ internal class AuthViewModel(private val service: AuthService) : ViewModel() { } fun logout() { - viewModelScope.launch { - service.logout() - _state.value = AuthState.Unauthenticated - } + viewModelScope.launch { runLogout() } + } + + /** Test seam: same logout logic, runnable directly without `viewModelScope.launch`. */ + internal suspend fun logoutForTest() = runLogout() + + private suspend fun runLogout() { + service.logout() + fhirEngine.clearDatabase() + _state.value = AuthState.Unauthenticated } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt index 489725d7..80bfda18 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt @@ -87,5 +87,5 @@ internal val viewModelModule = module { QuestionnaireHostViewModel(questionnaireId, launchContext, get()) } viewModel { HomeViewModel(get(), get()) } - viewModel { AuthViewModel(get()) } + viewModel { AuthViewModel(get(), get()) } } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt index fa408dbf..cfd68428 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt @@ -15,6 +15,18 @@ */ package dev.ohs.player.reference.app.auth +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.LocalChange +import dev.ohs.fhir.engine.OffsetDateTime +import dev.ohs.fhir.engine.SearchResult +import dev.ohs.fhir.engine.db.LocalChangeResourceReference +import dev.ohs.fhir.engine.search.Search +import dev.ohs.fhir.engine.sync.ConflictResolver +import dev.ohs.fhir.engine.sync.upload.SyncUploadProgress +import dev.ohs.fhir.engine.sync.upload.UploadRequestResult +import dev.ohs.fhir.engine.sync.upload.UploadStrategy +import dev.ohs.fhir.model.r4.Resource +import dev.ohs.fhir.model.r4.terminologies.ResourceType import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond @@ -27,6 +39,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertNull +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -66,6 +79,50 @@ class AuthViewModelTest { override suspend fun authorize(authUrl: String): AuthResult = onAuthorize(authUrl) } + /** Every `AuthViewModel` in this file gets one of these — none of these tests call logout(). */ + private class NoOpFhirEngine : FhirEngine { + override suspend fun create(vararg resource: Resource): List = + error("not used in this test") + + override suspend fun get(type: ResourceType, id: String): Resource = error("not used in this test") + + override suspend fun update(vararg resource: Resource) = error("not used in this test") + + override suspend fun delete(type: ResourceType, id: String) = error("not used in this test") + + override suspend fun search(search: Search): List> = + error("not used in this test") + + override suspend fun syncUpload( + uploadStrategy: UploadStrategy, + upload: + suspend (List, List) -> Flow, + ): Flow = error("not used in this test") + + override suspend fun syncDownload( + conflictResolver: ConflictResolver, + download: suspend () -> Flow>, + ) = error("not used in this test") + + override suspend fun count(search: Search): Long = error("not used in this test") + + override suspend fun getLastSyncTimeStamp(): OffsetDateTime? = error("not used in this test") + + override suspend fun clearDatabase() = error("not used in this test") + + override suspend fun getLocalChanges(type: ResourceType, id: String): List = + error("not used in this test") + + override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) = + error("not used in this test") + + override suspend fun purge(type: ResourceType, ids: Set, forcePurge: Boolean) = + error("not used in this test") + + override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) = + error("not used in this test") + } + private val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") private fun apiThatNeverGetsCalled(): OidcAuthApi { @@ -130,7 +187,7 @@ class AuthViewModelTest { FakeSessionStore(null), apiThatNeverGetsCalled(), ) - val viewModel = AuthViewModel(service) + val viewModel = AuthViewModel(service, NoOpFhirEngine()) val launcher = FakeLauncher("app://callback") { AuthResult.Canceled } viewModel.bootstrapForTest(launcher) @@ -146,7 +203,7 @@ class AuthViewModelTest { FakeSessionStore(session()), apiThatNeverGetsCalled(), ) - val viewModel = AuthViewModel(service) + val viewModel = AuthViewModel(service, NoOpFhirEngine()) val launcher = FakeLauncher("app://callback") { AuthResult.Canceled } viewModel.bootstrapForTest(launcher) @@ -162,7 +219,7 @@ class AuthViewModelTest { FakeSessionStore(null), apiWithWorkingDiscovery(), ) - val viewModel = AuthViewModel(service) + val viewModel = AuthViewModel(service, NoOpFhirEngine()) val launcher = FakeLauncher("app://callback") { AuthResult.Failure("network down") } viewModel.loginForTest(launcher) @@ -179,7 +236,7 @@ class AuthViewModelTest { FakeSessionStore(null), apiWithWorkingDiscovery(), ) - val viewModel = AuthViewModel(service) + val viewModel = AuthViewModel(service, NoOpFhirEngine()) val launcher = FakeLauncher("app://callback") { AuthResult.Failure("network down") } viewModel.loginForTest(launcher) diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt new file mode 100644 index 00000000..07904232 --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.FhirEngineConfiguration +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.search.Search +import dev.ohs.fhir.model.r4.Patient +import dev.ohs.fhir.model.r4.terminologies.ResourceType +import dev.ohs.player.reference.app.data.repository.FhirEngineRepository +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headersOf +import io.ktor.serialization.kotlinx.json.json +import java.nio.file.Files +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.Json + +/** Verifies AuthViewModel.logout() actually wipes local FHIR data, using a real FhirEngine. */ +class AuthViewModelLogoutTest { + + private lateinit var fhirEngine: FhirEngine + + private class FakeSessionStore(initial: Session?) : SessionStore { + private val _session = MutableStateFlow(initial) + override val session: StateFlow = _session.asStateFlow() + + override suspend fun load(): Session? = _session.value + + override suspend fun save(session: Session) { + _session.value = session + } + + override suspend fun clear() { + _session.value = null + } + + override suspend fun savePending(pending: PendingAuth) = Unit + + override suspend fun takePending(): PendingAuth? = null + } + + @BeforeTest + fun setUp() = runTest { + if (FhirEngineProvider.isNotInitialized()) { + FhirEngineProvider.init( + FhirEngineConfiguration( + storageDirectory = Files.createTempDirectory("auth-viewmodel-logout-test").toString() + ) + ) + } + fhirEngine = FhirEngineProvider.getInstance() + fhirEngine.clearDatabase() + } + + private fun apiWithEndSessionEndpoint(): OidcAuthApi { + val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") + val discoveryBody = + """ + { + "authorization_endpoint": "https://idp.example.org/authorize", + "token_endpoint": "https://idp.example.org/token", + "userinfo_endpoint": "https://idp.example.org/userinfo", + "end_session_endpoint": "https://idp.example.org/logout" + } + """ + .trimIndent() + val engine = MockEngine { request -> + when { + request.url.encodedPath.endsWith("openid-configuration") -> + respond(discoveryBody, HttpStatusCode.OK, jsonHeaders) + request.url.encodedPath.endsWith("/logout") -> respond("", HttpStatusCode.NoContent) + else -> respond("not found", HttpStatusCode.NotFound) + } + } + val client = + HttpClient(engine) { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } + } + return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) + } + + private fun session() = + Session( + accessToken = "a", + refreshToken = "r", + idToken = null, + expiresInSeconds = 10_000_000_000L, + obtainedAtEpochSeconds = 0, + user = UserInfo(), + ) + + @Test + fun logout_clearsLocalFhirData() = runTest { + val json = Json { ignoreUnknownKeys = true } + val repository = FhirEngineRepository(fhirEngine) + val patient = + json.decodeFromString( + Patient.serializer(), + """{"resourceType": "Patient", "id": "patient-to-be-cleared"}""", + ) + repository.upsert(patient) + assertEquals(1L, fhirEngine.count(Search(ResourceType.Patient))) + + val service = + AuthService( + OAuthConfig("https://idp.example.org", "client", "openid"), + FakeSessionStore(session()), + apiWithEndSessionEndpoint(), + ) + val viewModel = AuthViewModel(service, fhirEngine) + + viewModel.logoutForTest() + + assertEquals(0L, fhirEngine.count(Search(ResourceType.Patient))) + assertEquals(AuthState.Unauthenticated, viewModel.state.value) + } +} From 7f41eaf8988fc14e8dd88dc453bed6ae33d65f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:02:41 +0300 Subject: [PATCH 31/73] Add InitialSyncViewModel gating a session on local data presence --- .../app/feature/sync/InitialSyncViewModel.kt | 91 +++++++++++++++ .../feature/sync/InitialSyncViewModelTest.kt | 110 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt new file mode 100644 index 00000000..8704ce05 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.sync + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.player.reference.app.data.repository.FhirRepository +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import kotlin.coroutines.cancellation.CancellationException +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +/** State the blocking initial-sync gate renders against. */ +sealed interface InitialSyncGateState { + data object Checking : InitialSyncGateState + + data object Syncing : InitialSyncGateState + + data class Failed(val message: String) : InitialSyncGateState + + data object Passed : InitialSyncGateState +} + +/** + * Gates a freshly-authenticated session behind a one-time blocking sync when the local FHIR + * database is empty. [start] deliberately has no "already ran" guard — it must fully re-run its + * check every time it's called, because Koin can hand back the same ViewModel instance across a + * logout → login round-trip (the top-level `App()` composables aren't scoped per auth session), and + * a fresh login must always be re-evaluated (e.g. after + * [dev.ohs.player.reference.app.auth.AuthViewModel.logout] wipes local data). + */ +class InitialSyncViewModel( + private val fhirRepository: FhirRepository, + private val syncNowUseCase: SyncNowUseCase, +) : ViewModel() { + + private val _state = MutableStateFlow(InitialSyncGateState.Checking) + val state: StateFlow = _state.asStateFlow() + + fun start(): Job = viewModelScope.launch { runCheck() } + + fun retry(): Job = viewModelScope.launch { runSync() } + + fun continueAnyway() { + _state.value = InitialSyncGateState.Passed + } + + private suspend fun runCheck() { + _state.value = InitialSyncGateState.Checking + if (fhirRepository.hasAnyData()) { + _state.value = InitialSyncGateState.Passed + } else { + runSync() + } + } + + private suspend fun runSync() { + _state.value = InitialSyncGateState.Syncing + val result = + try { + syncNowUseCase.invoke() + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + SyncJobStatus.Failed() + } + _state.value = + when (result) { + is SyncJobStatus.Succeeded -> InitialSyncGateState.Passed + else -> + InitialSyncGateState.Failed("Sync failed. Please check your connection and try again.") + } + } +} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt new file mode 100644 index 00000000..e8f52ddd --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.sync + +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.model.r4.Patient +import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.Json + +class InitialSyncViewModelTest { + + private class FakeSyncNowUseCase(private val result: suspend () -> SyncJobStatus) : SyncNowUseCase { + var invocationCount = 0 + private set + + override suspend fun invoke(): SyncJobStatus { + invocationCount++ + return result() + } + } + + private val json = Json { ignoreUnknownKeys = true } + + private fun testPatient() = + json.decodeFromString(Patient.serializer(), """{"resourceType": "Patient", "id": "p1"}""") + + @Test + fun start_withExistingData_goesPassedWithoutSyncing() = runTest { + val repository = InMemorySampleFhirRepository() + repository.upsert(testPatient()) + val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Succeeded() } + val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + + viewModel.start().join() + + assertEquals(InitialSyncGateState.Passed, viewModel.state.value) + assertEquals(0, syncNowUseCase.invocationCount) + } + + @Test + fun start_withNoData_syncsThenPasses() = runTest { + val repository = InMemorySampleFhirRepository() + val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Succeeded() } + val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + + viewModel.start().join() + + assertEquals(InitialSyncGateState.Passed, viewModel.state.value) + assertEquals(1, syncNowUseCase.invocationCount) + } + + @Test + fun start_withNoDataAndSyncFails_goesFailed() = runTest { + val repository = InMemorySampleFhirRepository() + val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Failed() } + val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + + viewModel.start().join() + + assertIs(viewModel.state.value) + } + + @Test + fun retry_afterFailure_syncsAgain() = runTest { + val repository = InMemorySampleFhirRepository() + var shouldFail = true + val syncNowUseCase = + FakeSyncNowUseCase { if (shouldFail) SyncJobStatus.Failed() else SyncJobStatus.Succeeded() } + val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + viewModel.start().join() + assertIs(viewModel.state.value) + + shouldFail = false + viewModel.retry().join() + + assertEquals(InitialSyncGateState.Passed, viewModel.state.value) + assertEquals(2, syncNowUseCase.invocationCount) + } + + @Test + fun continueAnyway_movesToPassed() = runTest { + val repository = InMemorySampleFhirRepository() + val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Failed() } + val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + viewModel.start().join() + assertIs(viewModel.state.value) + + viewModel.continueAnyway() + + assertEquals(InitialSyncGateState.Passed, viewModel.state.value) + } +} From 2712a01bfe0901bdd92d39356b33d58511fd2855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:03:53 +0300 Subject: [PATCH 32/73] Add InitialSyncScreen (blocking progress + retry/continue UI) --- .../app/feature/sync/InitialSyncScreen.kt | 105 ++++++++++++++++++ .../app/feature/sync/InitialSyncScreenTest.kt | 80 +++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt new file mode 100644 index 00000000..65083329 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.sync + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.material.icons.Icons +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp + +/** + * Full-screen blocking gate shown between login and Home: a progress state while checking/syncing, + * and a failure state offering Retry or Continue without syncing. Never shown for [Passed] — the + * caller ([dev.ohs.player.reference.app.App]) switches to the real content on that state instead. + */ +@Composable +fun InitialSyncScreen( + state: InitialSyncGateState, + onRetry: () -> Unit, + onContinueAnyway: () -> Unit, +) { + Box(Modifier.fillMaxSize().padding(32.dp), contentAlignment = Alignment.Center) { + when (state) { + InitialSyncGateState.Checking, + InitialSyncGateState.Syncing -> SyncingContent() + is InitialSyncGateState.Failed -> FailedContent(state.message, onRetry, onContinueAnyway) + InitialSyncGateState.Passed -> Unit + } + } +} + +@Composable +private fun SyncingContent() { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + CircularProgressIndicator() + Text( + text = "Setting up your data…", + style = MaterialTheme.typography.titleMedium, + textAlign = TextAlign.Center, + ) + Text( + text = "This may take a moment the first time.", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) + } +} + +@Composable +private fun FailedContent(message: String, onRetry: () -> Unit, onContinueAnyway: () -> Unit) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = null, + tint = MaterialTheme.colorScheme.error, + modifier = Modifier.size(48.dp), + ) + Text( + text = "Couldn't sync your data", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.error, + textAlign = TextAlign.Center, + ) + Text(text = message, style = MaterialTheme.typography.bodyMedium, textAlign = TextAlign.Center) + Button(onClick = onRetry, modifier = Modifier.fillMaxWidth()) { Text("Retry") } + TextButton(onClick = onContinueAnyway, modifier = Modifier.fillMaxWidth()) { + Text("Continue without syncing") + } + } +} diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt new file mode 100644 index 00000000..e406828e --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.feature.sync + +import androidx.compose.material3.MaterialTheme +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.runComposeUiTest +import kotlin.test.Test +import kotlin.test.assertTrue + +@OptIn(ExperimentalTestApi::class) +class InitialSyncScreenTest { + + @Test + fun syncingState_showsBlockingProgressText() = runComposeUiTest { + setContent { + MaterialTheme { + InitialSyncScreen(state = InitialSyncGateState.Syncing, onRetry = {}, onContinueAnyway = {}) + } + } + + assertTrue( + onAllNodesWithText("Setting up your data", substring = true).fetchSemanticsNodes().isNotEmpty() + ) + } + + @Test + fun failedState_tappingRetry_invokesCallback() = runComposeUiTest { + var retried = false + setContent { + MaterialTheme { + InitialSyncScreen( + state = + InitialSyncGateState.Failed("Sync failed. Please check your connection and try again."), + onRetry = { retried = true }, + onContinueAnyway = {}, + ) + } + } + + onNodeWithText("Retry", ignoreCase = true).performClick() + + assertTrue(retried) + } + + @Test + fun failedState_tappingContinueAnyway_invokesCallback() = runComposeUiTest { + var continued = false + setContent { + MaterialTheme { + InitialSyncScreen( + state = + InitialSyncGateState.Failed("Sync failed. Please check your connection and try again."), + onRetry = {}, + onContinueAnyway = { continued = true }, + ) + } + } + + onNodeWithText("Continue without syncing", ignoreCase = true).performClick() + + assertTrue(continued) + } +} From ac7e4644ba2624423a46b3c7dbe358b2ed639592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:06:07 +0300 Subject: [PATCH 33/73] Gate Authenticated sessions behind InitialSyncViewModel before showing Home --- .../dev/ohs/player/reference/app/App.kt | 178 ++++++++++-------- .../player/reference/app/data/di/AppModule.kt | 2 + .../player/reference/app/AppAuthGatingTest.kt | 123 +++++++++++- 3 files changed, 219 insertions(+), 84 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index aaf3b377..14062d11 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -42,6 +42,9 @@ import dev.ohs.player.reference.app.feature.login.LoginScreen import dev.ohs.player.reference.app.feature.patient.profile.PatientProfileScreen import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireHostScreen import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireIds +import dev.ohs.player.reference.app.feature.sync.InitialSyncGateState +import dev.ohs.player.reference.app.feature.sync.InitialSyncScreen +import dev.ohs.player.reference.app.feature.sync.InitialSyncViewModel import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid import org.koin.compose.viewmodel.koinViewModel @@ -73,92 +76,109 @@ fun App() { onErrorDismiss = { authViewModel.clearError() }, ) is AuthState.Authenticated -> { - val navController = rememberNavController() - NavHost(navController = navController, startDestination = "home") { + val initialSyncViewModel: InitialSyncViewModel = koinViewModel() + val gateState by initialSyncViewModel.state.collectAsStateWithLifecycle() + LaunchedEffect(Unit) { initialSyncViewModel.start() } - // Screen 1: Home (adaptive navigation drawer shell around the household list) - composable("home") { - HomeScreen( - onGroupClick = { id -> navController.navigate("groupProfile/$id") }, - onDataCaptureClick = { - navController.navigate( - questionnaireHostRoute( - questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION - ) - ) - }, - onSignOut = { authViewModel.logout() }, + when (gateState) { + InitialSyncGateState.Checking, + InitialSyncGateState.Syncing, + is InitialSyncGateState.Failed -> + InitialSyncScreen( + state = gateState, + onRetry = { initialSyncViewModel.retry() }, + onContinueAnyway = { initialSyncViewModel.continueAnyway() }, ) - } + InitialSyncGateState.Passed -> { + val navController = rememberNavController() + NavHost(navController = navController, startDestination = "home") { - composable( - route = "questionnaireHost/{questionnaireId}?patientId={patientId}&groupId={groupId}", - arguments = - listOf( - navArgument("questionnaireId") { type = NavType.StringType }, - navArgument("patientId") { - type = NavType.StringType - nullable = true - defaultValue = null - }, - navArgument("groupId") { - type = NavType.StringType - nullable = true - defaultValue = null - }, - ), - ) { back -> - val questionnaireId = - back.arguments?.read { getStringOrNull("questionnaireId") }.orEmpty() - val patientId = back.arguments?.read { getStringOrNull("patientId") } - val groupId = back.arguments?.read { getStringOrNull("groupId") } - QuestionnaireHostScreen( - questionnaireId = questionnaireId, - patientId = patientId, - groupId = groupId, - onBack = { navController.popBackStack() }, - ) - } + // Screen 1: Home (adaptive navigation drawer shell around the household list) + composable("home") { + HomeScreen( + onGroupClick = { id -> navController.navigate("groupProfile/$id") }, + onDataCaptureClick = { + navController.navigate( + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.HOUSEHOLD_REGISTRATION + ) + ) + }, + onSignOut = { authViewModel.logout() }, + ) + } - // Screen 2: Household profile (head + members) - composable( - route = "groupProfile/{groupId}", - arguments = listOf(navArgument("groupId") { type = NavType.StringType }), - ) { back -> - val groupId = back.arguments?.read { getStringOrNull("groupId") }.orEmpty() - GroupProfileScreen( - groupId = groupId, - onBack = { navController.popBackStack() }, - onMemberClick = { id -> navController.navigate("patientProfile/$id") }, - onAddMembers = { - navController.navigate( - questionnaireHostRoute( - questionnaireId = QuestionnaireIds.HOUSEHOLD_MEMBERS, - groupId = groupId, - ) + composable( + route = + "questionnaireHost/{questionnaireId}?patientId={patientId}&groupId={groupId}", + arguments = + listOf( + navArgument("questionnaireId") { type = NavType.StringType }, + navArgument("patientId") { + type = NavType.StringType + nullable = true + defaultValue = null + }, + navArgument("groupId") { + type = NavType.StringType + nullable = true + defaultValue = null + }, + ), + ) { back -> + val questionnaireId = + back.arguments?.read { getStringOrNull("questionnaireId") }.orEmpty() + val patientId = back.arguments?.read { getStringOrNull("patientId") } + val groupId = back.arguments?.read { getStringOrNull("groupId") } + QuestionnaireHostScreen( + questionnaireId = questionnaireId, + patientId = patientId, + groupId = groupId, + onBack = { navController.popBackStack() }, ) - }, - ) - } + } - // Screen 3: Patient IPS summary - composable( - route = "patientProfile/{patientId}", - arguments = listOf(navArgument("patientId") { type = NavType.StringType }), - ) { back -> - val patientId = back.arguments?.read { getStringOrNull("patientId") }.orEmpty() - PatientProfileScreen( - patientId = patientId, - onBack = { navController.popBackStack() }, - onAddClinicalData = { - navController.navigate( - questionnaireHostRoute( - questionnaireId = QuestionnaireIds.PATIENT_CLINICAL_DATA, - patientId = patientId, - ) + // Screen 2: Household profile (head + members) + composable( + route = "groupProfile/{groupId}", + arguments = listOf(navArgument("groupId") { type = NavType.StringType }), + ) { back -> + val groupId = back.arguments?.read { getStringOrNull("groupId") }.orEmpty() + GroupProfileScreen( + groupId = groupId, + onBack = { navController.popBackStack() }, + onMemberClick = { id -> navController.navigate("patientProfile/$id") }, + onAddMembers = { + navController.navigate( + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.HOUSEHOLD_MEMBERS, + groupId = groupId, + ) + ) + }, ) - }, - ) + } + + // Screen 3: Patient IPS summary + composable( + route = "patientProfile/{patientId}", + arguments = listOf(navArgument("patientId") { type = NavType.StringType }), + ) { back -> + val patientId = back.arguments?.read { getStringOrNull("patientId") }.orEmpty() + PatientProfileScreen( + patientId = patientId, + onBack = { navController.popBackStack() }, + onAddClinicalData = { + navController.navigate( + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.PATIENT_CLINICAL_DATA, + patientId = patientId, + ) + ) + }, + ) + } + } } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt index 80bfda18..b468ec37 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt @@ -38,6 +38,7 @@ import dev.ohs.player.reference.app.feature.patient.profile.PatientProfileViewMo import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireHostViewModel import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireLaunchContext import dev.ohs.player.reference.app.feature.questionnaire.QuestionnaireService +import dev.ohs.player.reference.app.feature.sync.InitialSyncViewModel import org.koin.core.module.dsl.viewModel import org.koin.dsl.module @@ -88,4 +89,5 @@ internal val viewModelModule = module { } viewModel { HomeViewModel(get(), get()) } viewModel { AuthViewModel(get(), get()) } + viewModel { InitialSyncViewModel(get(), get()) } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt index cf5807a5..074c9704 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt @@ -18,15 +18,29 @@ package dev.ohs.player.reference.app import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.runComposeUiTest +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.LocalChange +import dev.ohs.fhir.engine.OffsetDateTime +import dev.ohs.fhir.engine.SearchResult +import dev.ohs.fhir.engine.db.LocalChangeResourceReference +import dev.ohs.fhir.engine.search.Search +import dev.ohs.fhir.engine.sync.ConflictResolver import dev.ohs.fhir.engine.sync.FhirDataStore import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.engine.sync.createDataStore +import dev.ohs.fhir.engine.sync.upload.SyncUploadProgress +import dev.ohs.fhir.engine.sync.upload.UploadRequestResult +import dev.ohs.fhir.engine.sync.upload.UploadStrategy +import dev.ohs.fhir.model.r4.Patient +import dev.ohs.fhir.model.r4.Resource +import dev.ohs.fhir.model.r4.terminologies.ResourceType import dev.ohs.player.reference.app.auth.AuthService import dev.ohs.player.reference.app.auth.OAuthConfig import dev.ohs.player.reference.app.auth.OidcAuthApi import dev.ohs.player.reference.app.auth.PendingAuth import dev.ohs.player.reference.app.auth.Session import dev.ohs.player.reference.app.auth.SessionStore +import dev.ohs.player.reference.app.auth.UserInfo import dev.ohs.player.reference.app.data.di.repositoryModule import dev.ohs.player.reference.app.data.di.viewModelModule import dev.ohs.player.reference.app.data.repository.FhirRepository @@ -41,17 +55,26 @@ import io.ktor.serialization.kotlinx.json.json import java.nio.file.Files import kotlin.test.AfterTest import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.runBlocking import kotlinx.serialization.json.Json import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.dsl.module private class FakeAppSyncNowUseCase : SyncNowUseCase { - override suspend fun invoke(): SyncJobStatus = SyncJobStatus.Succeeded() + var invocationCount = 0 + private set + + override suspend fun invoke(): SyncJobStatus { + invocationCount++ + return SyncJobStatus.Succeeded() + } } private class InMemorySessionStore(initial: Session? = null) : SessionStore { @@ -73,6 +96,49 @@ private class InMemorySessionStore(initial: Session? = null) : SessionStore { override suspend fun takePending(): PendingAuth? = null } +/** Satisfies AuthViewModel's constructor; no test in this file calls logout(). */ +private class NoOpFhirEngine : FhirEngine { + override suspend fun create(vararg resource: Resource): List = error("not used in this test") + + override suspend fun get(type: ResourceType, id: String): Resource = error("not used in this test") + + override suspend fun update(vararg resource: Resource) = error("not used in this test") + + override suspend fun delete(type: ResourceType, id: String) = error("not used in this test") + + override suspend fun search(search: Search): List> = + error("not used in this test") + + override suspend fun syncUpload( + uploadStrategy: UploadStrategy, + upload: + suspend (List, List) -> Flow, + ): Flow = error("not used in this test") + + override suspend fun syncDownload( + conflictResolver: ConflictResolver, + download: suspend () -> Flow>, + ) = error("not used in this test") + + override suspend fun count(search: Search): Long = error("not used in this test") + + override suspend fun getLastSyncTimeStamp(): OffsetDateTime? = error("not used in this test") + + override suspend fun clearDatabase() = error("not used in this test") + + override suspend fun getLocalChanges(type: ResourceType, id: String): List = + error("not used in this test") + + override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) = + error("not used in this test") + + override suspend fun purge(type: ResourceType, ids: Set, forcePurge: Boolean) = + error("not used in this test") + + override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) = + error("not used in this test") +} + @OptIn(ExperimentalTestApi::class) class AppAuthGatingTest { @@ -81,20 +147,39 @@ class AppAuthGatingTest { return FhirDataStore(createDataStore { path }) } - private fun startTestKoin() { + private fun testSession() = + Session( + accessToken = "a", + refreshToken = "r", + idToken = null, + expiresInSeconds = 10_000_000_000L, + obtainedAtEpochSeconds = 0, + user = UserInfo(), + ) + + private fun testPatient(): Patient = + Json { ignoreUnknownKeys = true } + .decodeFromString(Patient.serializer(), """{"resourceType": "Patient", "id": "p1"}""") + + private fun startTestKoin( + session: Session? = null, + fhirRepository: FhirRepository = InMemorySampleFhirRepository(), + syncNowUseCase: SyncNowUseCase = FakeAppSyncNowUseCase(), + ) { val engine = MockEngine { respond("not found", HttpStatusCode.NotFound) } val client = HttpClient(engine) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } startKoin { modules( module { - single { InMemorySampleFhirRepository() } + single { fhirRepository } single { newFhirDataStore() } - single { FakeAppSyncNowUseCase() } + single { syncNowUseCase } single { OAuthConfig("https://idp.example.org", "client", "openid") } - single { InMemorySessionStore(initial = null) } + single { InMemorySessionStore(initial = session) } single { OidcAuthApi(get(), client) } single { AuthService(get(), get(), get()) } + single { NoOpFhirEngine() } }, repositoryModule, viewModelModule, @@ -115,4 +200,32 @@ class AppAuthGatingTest { } assertTrue(onAllNodesWithText("Sign in", ignoreCase = true).fetchSemanticsNodes().isNotEmpty()) } + + @Test + fun authenticatedWithNoLocalData_runsSyncThenShowsHome() = runComposeUiTest { + val syncNowUseCase = FakeAppSyncNowUseCase() + startTestKoin(session = testSession(), syncNowUseCase = syncNowUseCase) + + setContent { App() } + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sync now", ignoreCase = true).fetchSemanticsNodes().isNotEmpty() + } + assertEquals(1, syncNowUseCase.invocationCount) + } + + @Test + fun authenticatedWithExistingLocalData_skipsSyncAndShowsHomeImmediately() = runComposeUiTest { + val repository = InMemorySampleFhirRepository() + runBlocking { repository.upsert(testPatient()) } + val syncNowUseCase = FakeAppSyncNowUseCase() + startTestKoin(session = testSession(), fhirRepository = repository, syncNowUseCase = syncNowUseCase) + + setContent { App() } + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sync now", ignoreCase = true).fetchSemanticsNodes().isNotEmpty() + } + assertEquals(0, syncNowUseCase.invocationCount) + } } From 42d25bfb69bb530d737bc832f14e3e5b53c7798d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:08:01 +0300 Subject: [PATCH 34/73] Fix AuthModuleTest for AuthViewModel's new FhirEngine dependency The full jvmTest run caught this: AuthViewModel now requires a FhirEngine binding (added when logout() started clearing local data), which this test's minimal Koin module didn't provide. --- .../reference/app/data/di/AuthModuleTest.kt | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt index 0d3beb8b..5a1f1762 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt @@ -15,15 +15,72 @@ */ package dev.ohs.player.reference.app.data.di +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.LocalChange +import dev.ohs.fhir.engine.OffsetDateTime +import dev.ohs.fhir.engine.SearchResult +import dev.ohs.fhir.engine.db.LocalChangeResourceReference +import dev.ohs.fhir.engine.search.Search +import dev.ohs.fhir.engine.sync.ConflictResolver +import dev.ohs.fhir.engine.sync.upload.SyncUploadProgress +import dev.ohs.fhir.engine.sync.upload.UploadRequestResult +import dev.ohs.fhir.engine.sync.upload.UploadStrategy +import dev.ohs.fhir.model.r4.Resource +import dev.ohs.fhir.model.r4.terminologies.ResourceType import dev.ohs.player.reference.app.auth.AuthService import dev.ohs.player.reference.app.auth.AuthViewModel import kotlin.test.AfterTest import kotlin.test.Test +import kotlinx.coroutines.flow.Flow import org.koin.core.context.startKoin import org.koin.core.context.stopKoin +import org.koin.dsl.module import org.koin.test.KoinTest import org.koin.test.inject +/** Satisfies AuthViewModel's constructor; this test never calls logout(). */ +private class NoOpFhirEngine : FhirEngine { + override suspend fun create(vararg resource: Resource): List = error("not used in this test") + + override suspend fun get(type: ResourceType, id: String): Resource = error("not used in this test") + + override suspend fun update(vararg resource: Resource) = error("not used in this test") + + override suspend fun delete(type: ResourceType, id: String) = error("not used in this test") + + override suspend fun search(search: Search): List> = + error("not used in this test") + + override suspend fun syncUpload( + uploadStrategy: UploadStrategy, + upload: + suspend (List, List) -> Flow, + ): Flow = error("not used in this test") + + override suspend fun syncDownload( + conflictResolver: ConflictResolver, + download: suspend () -> Flow>, + ) = error("not used in this test") + + override suspend fun count(search: Search): Long = error("not used in this test") + + override suspend fun getLastSyncTimeStamp(): OffsetDateTime? = error("not used in this test") + + override suspend fun clearDatabase() = error("not used in this test") + + override suspend fun getLocalChanges(type: ResourceType, id: String): List = + error("not used in this test") + + override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) = + error("not used in this test") + + override suspend fun purge(type: ResourceType, ids: Set, forcePurge: Boolean) = + error("not used in this test") + + override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) = + error("not used in this test") +} + class AuthModuleTest : KoinTest { private val authService by inject() @@ -32,7 +89,9 @@ class AuthModuleTest : KoinTest { @Test fun authModule_resolvesAuthServiceAndAuthViewModel() { - startKoin { modules(authModule, viewModelModule) } + startKoin { + modules(module { single { NoOpFhirEngine() } }, authModule, viewModelModule) + } authService // resolves without throwing getKoin().get() From 50d16e6c2dcbdafad880c73202e491a7d1d4be51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:09:49 +0300 Subject: [PATCH 35/73] Remove redundant TopAppBar from the Households screen --- .../app/feature/group/list/GroupListScreen.kt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt index f797dd17..b28dd692 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt @@ -24,11 +24,8 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -54,16 +51,6 @@ fun GroupListScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Un } Scaffold( - topBar = { - TopAppBar( - title = { Text("Households") }, - colors = - TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.primary, - titleContentColor = MaterialTheme.colorScheme.onPrimary, - ), - ) - }, floatingActionButton = { FloatingActionButton(onClick = onDataCaptureClick) { Icon(Icons.Filled.Add, contentDescription = "Register household") From 1efaa7c5e12b6402d7641fe6c72918f13d109326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:02:23 +0300 Subject: [PATCH 36/73] Replace RunSyncNowUseCase with per-platform sync implementations Android now runs one-time sync through WorkManager (AppFhirSyncWorker + WorkManagerSyncNowUseCase) instead of calling runSync() in-process, so sync survives the triggering screen backgrounding or the process dying mid-sync. JVM and web share a hand-rolled foreground scheduler (Sync.kt, mirroring kotlin-fhir-engine's engine-app) with dedup and retry-with-backoff. iOS gets its own foreground/background-aware job (IosSyncNowUseCase), cancelled on backgrounding and relaunched on returning to the foreground since iOS suspends networking for backgrounded apps. Also replaces InMemoryTimestampContext (process-lifetime only) with DataStoreTimestampContext, persisting each resource type's download cursor to a Preferences DataStore per platform, so incremental sync survives app restarts instead of re-downloading everything on every launch. --- gradle/libs.versions.toml | 2 + ohs-player-reference-app/build.gradle.kts | 29 ++- .../reference/app/OhsPlayerApplication.kt | 18 +- .../app/data/sync/AppFhirSyncWorker.kt | 42 ++++ .../sync/SyncTimestampDataStore.android.kt | 29 +++ .../data/sync/WorkManagerSyncNowUseCase.kt | 42 ++++ .../player/reference/app/data/di/AppModule.kt | 16 +- .../app/data/sync/AppFhirSyncTask.kt | 4 +- .../data/sync/DataStoreTimestampContext.kt | 50 +++++ .../app/data/sync/InMemoryTimestampContext.kt | 39 ---- .../reference/app/data/sync/SyncNowUseCase.kt | 11 +- .../app/data/sync/ForegroundSyncNowUseCase.kt | 38 ++++ .../player/reference/app/data/sync/Sync.kt | 188 ++++++++++++++++++ .../data/sync/SyncTimestampDataStore.web.kt | 33 +++ .../dev/ohs/player/reference/app/main.kt | 18 +- .../reference/app/MainViewController.kt | 18 +- .../app/data/sync/IosSyncNowUseCase.kt | 101 ++++++++++ .../data/sync/SyncTimestampDataStore.ios.kt | 41 ++++ .../data/sync/SyncTimestampDataStore.jvm.kt | 29 +++ .../dev/ohs/player/reference/app/main.kt | 18 +- .../sync/DataStoreTimestampContextTest.kt} | 17 +- 21 files changed, 716 insertions(+), 67 deletions(-) create mode 100644 ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt create mode 100644 ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.android.kt create mode 100644 ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContext.kt delete mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt create mode 100644 ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt create mode 100644 ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt create mode 100644 ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.web.kt rename ohs-player-reference-app/src/{webMain => foregroundSyncWebMain}/kotlin/dev/ohs/player/reference/app/main.kt (66%) create mode 100644 ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt create mode 100644 ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.ios.kt create mode 100644 ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt rename ohs-player-reference-app/src/{commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt => jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContextTest.kt} (75%) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1553e58c..a3ebdb95 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ androidx-core = "1.18.0" androidx-espresso = "3.7.0" androidx-lifecycle = "2.10.0" androidx-testExt = "1.3.0" +androidx-work = "2.8.1" composeHotReload = "1.1.1" composeMultiplatform = "1.11.0" composeMaterialIcons = "1.7.3" @@ -87,6 +88,7 @@ ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" } ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } ksafe = { module = "eu.anifantakis:ksafe", version.ref = "ksafe" } androidx-browser = { module = "androidx.browser:browser", version.ref = "androidxBrowser" } +androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" } kotlincrypto-hash-bom = { module = "org.kotlincrypto.hash:bom", version.ref = "kotlincryptoHash" } kotlincrypto-hash-sha2 = { module = "org.kotlincrypto.hash:sha2" } kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinxBrowser" } diff --git a/ohs-player-reference-app/build.gradle.kts b/ohs-player-reference-app/build.gradle.kts index 741b25c8..0d5db7f1 100644 --- a/ohs-player-reference-app/build.gradle.kts +++ b/ohs-player-reference-app/build.gradle.kts @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(ExperimentalKotlinGradlePluginApi::class) + import java.util.Properties import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget @@ -30,7 +33,26 @@ plugins { } kotlin { - androidTarget { compilerOptions { jvmTarget.set(JvmTarget.JVM_11) } } + // Desktop, js and wasmJs lack a native OS background scheduler, so they share a "foregroundSync" + // source set (see `foregroundSyncMain/.../data/sync/Sync.kt`) letting one coroutine-based + // scheduler serve all three instead of a separate implementation per platform. js and wasmJs + // further share a nested "foregroundSyncWeb", kept apart from the default `webMain` group, which + // holds web code unrelated to sync. + applyDefaultHierarchyTemplate { + common { + group("foregroundSync") { + withJvm() + group("foregroundSyncWeb") { + withJs() + withWasmJs() + } + } + } + } + + // fhir-engine's Android artifact ships inline functions (e.g. Sync.oneTimeSync) compiled at JVM + // target 21 — inlining them requires this target to be at least as high. + androidTarget { compilerOptions { jvmTarget.set(JvmTarget.JVM_21) } } listOf(iosArm64(), iosSimulatorArm64()).forEach { iosTarget -> iosTarget.binaries.framework { @@ -61,6 +83,7 @@ kotlin { implementation(libs.androidx.activity.compose) implementation(libs.ktor.client.okhttp) implementation(libs.androidx.browser) + implementation(libs.androidx.work.runtime) } commonMain.dependencies { implementation(project(":ohs-player-library")) @@ -305,8 +328,8 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } } diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt index 313ab030..e8ee448c 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt @@ -20,10 +20,15 @@ import dev.ohs.fhir.datacapture.DataCapture import dev.ohs.fhir.engine.FhirEngine import dev.ohs.fhir.engine.FhirEngineConfiguration import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.NetworkConfiguration import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.WorkManagerSyncNowUseCase import org.koin.dsl.module class OhsPlayerApplication : Application() { @@ -34,12 +39,23 @@ class OhsPlayerApplication : Application() { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + networkConfiguration = NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), + httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ) ), applicationContext, ) - initKoin(module { single { FhirEngineProvider.getInstance(applicationContext) } }) + initKoin( + module { + single { FhirEngineProvider.getInstance(applicationContext) } + single { WorkManagerSyncNowUseCase(applicationContext) } + } + ) DataCapture.initialize(applicationContext) } } diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt new file mode 100644 index 00000000..efeae188 --- /dev/null +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import android.content.Context +import androidx.work.WorkerParameters +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.sync.ConflictResolver +import dev.ohs.fhir.engine.sync.DownloadWorkManager +import dev.ohs.fhir.engine.sync.FhirSyncWorker +import dev.ohs.fhir.engine.sync.upload.UploadStrategy + +/** + * WorkManager entry point for this app's sync, enqueued via [dev.ohs.fhir.engine.sync.Sync]. Built + * by WorkManager's default factory (reflection on the `(Context, WorkerParameters)` constructor), + * so it constructs its own [AppFhirSyncTask] rather than resolving one through Koin. + */ +class AppFhirSyncWorker(appContext: Context, workerParams: WorkerParameters) : + FhirSyncWorker(appContext, workerParams) { + private val syncTask = AppFhirSyncTask(FhirEngineProvider.getInstance(appContext)) + + override fun getFhirEngine() = syncTask.getFhirEngine() + + override fun getDownloadWorkManager(): DownloadWorkManager = syncTask.getDownloadWorkManager() + + override fun getConflictResolver(): ConflictResolver = syncTask.getConflictResolver() + + override fun getUploadStrategy(): UploadStrategy = syncTask.getUploadStrategy() +} diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.android.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.android.kt new file mode 100644 index 00000000..90a1c995 --- /dev/null +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.android.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import dev.ohs.fhir.engine.sync.createDataStore +import dev.ohs.player.reference.app.auth.AndroidAppContext + +private val dataStore: DataStore by lazy { + createDataStore { + AndroidAppContext.application.filesDir.resolve(SYNC_TIMESTAMP_DATASTORE_FILE_NAME).absolutePath + } +} + +internal actual fun createSyncTimestampDataStore(): DataStore = dataStore diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt new file mode 100644 index 00000000..c7ea2dfd --- /dev/null +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import android.content.Context +import dev.ohs.fhir.engine.sync.CurrentSyncJobStatus +import dev.ohs.fhir.engine.sync.Sync +import dev.ohs.fhir.engine.sync.SyncJobStatus +import kotlinx.coroutines.flow.first + +/** + * Runs a one-time sync through WorkManager (via [AppFhirSyncWorker]) rather than calling + * [dev.ohs.fhir.engine.sync.runSync] in-process, so the sync survives the triggering screen being + * backgrounded or the process dying mid-sync. + */ +class WorkManagerSyncNowUseCase(private val context: Context) : SyncNowUseCase { + override suspend fun invoke(): SyncJobStatus { + val terminalStatus = + Sync.oneTimeSync(context).first { + it is CurrentSyncJobStatus.Succeeded || + it is CurrentSyncJobStatus.Failed || + it is CurrentSyncJobStatus.Cancelled + } + return when (terminalStatus) { + is CurrentSyncJobStatus.Succeeded -> SyncJobStatus.Succeeded() + else -> SyncJobStatus.Failed() + } + } +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt index b468ec37..d2317331 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt @@ -16,7 +16,6 @@ package dev.ohs.player.reference.app.data.di import dev.ohs.fhir.engine.FhirEngineProvider -import dev.ohs.fhir.engine.sync.FhirSyncTask import dev.ohs.player.reference.app.auth.AuthService import dev.ohs.player.reference.app.auth.AuthViewModel import dev.ohs.player.reference.app.auth.OAuthConfig @@ -27,9 +26,6 @@ import dev.ohs.player.reference.app.data.repository.FhirEngineRepository import dev.ohs.player.reference.app.data.repository.FhirRepository import dev.ohs.player.reference.app.data.repository.GroupRepository import dev.ohs.player.reference.app.data.repository.PatientRepository -import dev.ohs.player.reference.app.data.sync.AppFhirSyncTask -import dev.ohs.player.reference.app.data.sync.RunSyncNowUseCase -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import dev.ohs.player.reference.app.feature.group.list.GroupListViewModel import dev.ohs.player.reference.app.feature.group.profile.GroupProfileViewModel import dev.ohs.player.reference.app.feature.home.HomeViewModel @@ -62,11 +58,13 @@ internal val repositoryModule = module { internal val serviceModule = module { factory { QuestionnaireService(get()) } } -internal val syncModule = module { - single { FhirEngineProvider.getFhirDataStore() } - single { AppFhirSyncTask(get()) } - single { RunSyncNowUseCase(get()) } -} +/** + * [SyncNowUseCase] isn't bound here — each platform's `initKoin` caller supplies its own + * implementation, constructing `AppFhirSyncTask` directly rather than through Koin (see + * `WorkManagerSyncNowUseCase` on Android, `ForegroundSyncNowUseCase` on JVM/web, and + * `IosSyncNowUseCase` on iOS). + */ +internal val syncModule = module { single { FhirEngineProvider.getFhirDataStore() } } /** * `SessionStore`/`SessionRepository`/`AuthService` — everything downstream of the plain diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt index 39aeab94..13cffd84 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncTask.kt @@ -34,12 +34,14 @@ import dev.ohs.fhir.model.r4.terminologies.ResourceType private val SYNC_RESOURCE_PARAMS: ResourceSearchParams = mapOf(ResourceType.Patient to emptyMap(), ResourceType.Group to emptyMap()) +const val SYNC_TIMEOUT_DURATION = 120L + /** * This app's [FhirSyncTask]: downloads [SYNC_RESOURCE_PARAMS], resolves conflicts in favor of the * local change, and uploads pending local changes as a single bundle request. */ class AppFhirSyncTask(private val fhirEngine: FhirEngine) : FhirSyncTask { - private val timestampContext = InMemoryTimestampContext() + private val timestampContext = DataStoreTimestampContext(createSyncTimestampDataStore()) override fun getFhirEngine(): FhirEngine = fhirEngine diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContext.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContext.kt new file mode 100644 index 00000000..9a77a283 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContext.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import dev.ohs.fhir.engine.sync.download.ResourceParamsBasedDownloadWorkManager +import dev.ohs.fhir.model.r4.terminologies.ResourceType +import kotlinx.coroutines.flow.first + +/** + * File name for the [DataStore] backing [DataStoreTimestampContext] — see + * [createSyncTimestampDataStore]. + */ +internal const val SYNC_TIMESTAMP_DATASTORE_FILE_NAME = "sync_timestamps.preferences_pb" + +/** Supplies the platform [DataStore] backing [DataStoreTimestampContext]. */ +internal expect fun createSyncTimestampDataStore(): DataStore + +/** + * Persists each [ResourceType]'s download cursor (`_lastUpdated`) in a Preferences [DataStore], so + * a fresh app launch resumes incremental sync instead of re-downloading every configured resource + * type from scratch. Mirrors kotlin-fhir-engine's engine-app `DemoDataStore`. + */ +class DataStoreTimestampContext(private val dataStore: DataStore) : + ResourceParamsBasedDownloadWorkManager.TimestampContext { + + override suspend fun saveLastUpdatedTimestamp(resourceType: ResourceType, timestamp: String?) { + if (timestamp == null) return + dataStore.edit { prefs -> prefs[stringPreferencesKey(resourceType.name)] = timestamp } + } + + override suspend fun getLasUpdateTimestamp(resourceType: ResourceType): String? = + dataStore.data.first()[stringPreferencesKey(resourceType.name)] +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt deleted file mode 100644 index af935e38..00000000 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContext.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.data.sync - -import dev.ohs.fhir.engine.sync.download.ResourceParamsBasedDownloadWorkManager -import dev.ohs.fhir.model.r4.terminologies.ResourceType - -/** - * In-memory implementation of [ResourceParamsBasedDownloadWorkManager.TimestampContext]. Tracks - * per-[ResourceType] download cursors only for the lifetime of the process — a fresh app launch - * re-downloads all resources for each configured type rather than only what changed since the last - * session. The user-visible last-synced timestamp is unaffected: it is tracked separately by - * [dev.ohs.fhir.sync.FhirDataStore]. - */ -class InMemoryTimestampContext : ResourceParamsBasedDownloadWorkManager.TimestampContext { - private val lastUpdatedByResourceType = mutableMapOf() - - override suspend fun saveLastUpdatedTimestamp(resourceType: ResourceType, timestamp: String?) { - if (timestamp != null) { - lastUpdatedByResourceType[resourceType] = timestamp - } - } - - override suspend fun getLasUpdateTimestamp(resourceType: ResourceType): String? = - lastUpdatedByResourceType[resourceType] -} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt index 2281389e..ae714e58 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt @@ -15,7 +15,6 @@ */ package dev.ohs.player.reference.app.data.sync -import dev.ohs.fhir.engine.sync.FhirSyncTask import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.engine.sync.runSync @@ -23,13 +22,11 @@ import dev.ohs.fhir.engine.sync.runSync * Triggers a one-time sync and returns its terminal result. A dedicated interface (rather than * calling [runSync] directly) so [dev.ohs.player.reference.app.feature.home.HomeViewModel] can be * unit-tested against a fake — [runSync] is a top-level extension function that reaches into the - * library's global `FhirEngineProvider` singleton, so no [FhirSyncTask] instance, real or fake, - * makes it independently testable. + * library's global `FhirEngineProvider` singleton, so no `FhirSyncTask` instance, real or fake, + * makes it independently testable. Each platform supplies its own implementation — see + * `AppFhirSyncWorker`/`WorkManagerSyncNowUseCase` (Android), `ForegroundSyncNowUseCase` (JVM/web), + * and `IosSyncNowUseCase` (iOS). */ fun interface SyncNowUseCase { suspend fun invoke(): SyncJobStatus } - -class RunSyncNowUseCase(private val fhirSyncTask: FhirSyncTask) : SyncNowUseCase { - override suspend fun invoke(): SyncJobStatus = fhirSyncTask.runSync(taskName = null) {} -} diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt new file mode 100644 index 00000000..a23e781e --- /dev/null +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.sync.CurrentSyncJobStatus +import dev.ohs.fhir.engine.sync.SyncJobStatus +import kotlinx.coroutines.flow.first + +/** + * Runs a one-time sync through the shared foreground [Sync] scheduler — Desktop and web only sync + * while their host process is alive (see [Sync]'s docs for why). + */ +class ForegroundSyncNowUseCase : SyncNowUseCase { + override suspend fun invoke(): SyncJobStatus { + val terminalStatus = + Sync.oneTimeSync(taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }).first { + it is CurrentSyncJobStatus.Succeeded || it is CurrentSyncJobStatus.Failed + } + return when (terminalStatus) { + is CurrentSyncJobStatus.Succeeded -> SyncJobStatus.Succeeded() + else -> SyncJobStatus.Failed() + } + } +} diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt new file mode 100644 index 00000000..da839db5 --- /dev/null +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt @@ -0,0 +1,188 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import co.touchlab.kermit.Logger +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.sync.BackoffPolicy +import dev.ohs.fhir.engine.sync.CurrentSyncJobStatus +import dev.ohs.fhir.engine.sync.FhirDataStore +import dev.ohs.fhir.engine.sync.FhirSyncTask +import dev.ohs.fhir.engine.sync.RetryConfiguration +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.defaultRetryConfiguration +import dev.ohs.fhir.engine.sync.runSync +import dev.ohs.fhir.engine.sync.syncDispatcher +import kotlin.coroutines.cancellation.CancellationException +import kotlin.time.Clock +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withTimeoutOrNull + +/** + * Foreground-only one-time sync scheduler shared by Desktop (JVM) and web (js, wasmJs) — neither + * platform has a native OS background scheduler (no WorkManager, no BGTaskScheduler), so sync only + * runs while the host process (JVM process / browser tab) stays alive. Mirrors + * kotlin-fhir-engine's engine-app `Sync` object, trimmed to the one-time-sync surface this app + * uses (no periodic sync). + */ +internal object Sync { + private val scope = CoroutineScope(SupervisorJob() + syncDispatcher) + private val mutex = Mutex() + private val activeSyncs = mutableMapOf() + private val fhirDataStore: FhirDataStore by lazy { FhirEngineProvider.getFhirDataStore() } + + /** + * Executes a one-time sync using [FhirSyncTask] instances created by [taskFactory]. + * + * If a one-time sync for [T] is already active, the existing [Flow] is returned immediately + * without starting a new job. + * + * @param taskFactory Creates a fresh [FhirSyncTask] for each attempt (including retries). + * @param retryConfiguration Retry policy on failure, or null to disable retries. + * @param syncTimeout Maximum duration for a single sync attempt. If exceeded, the attempt is + * treated as failed and subject to retry. `null` means no timeout. + * @return A [Flow] of [CurrentSyncJobStatus] tracking the full sync lifecycle. + */ + suspend inline fun oneTimeSync( + noinline taskFactory: () -> T, + retryConfiguration: RetryConfiguration? = defaultRetryConfiguration, + syncTimeout: Duration? = null, + ): Flow { + val uniqueWorkName = "${T::class.simpleName}-oneTimeSync" + return runOneTimeSync(uniqueWorkName, taskFactory, retryConfiguration, syncTimeout) + } + + suspend fun runOneTimeSync( + uniqueWorkName: String, + taskFactory: () -> FhirSyncTask, + retryConfiguration: RetryConfiguration?, + syncTimeout: Duration? = null, + ): Flow { + mutex + .withLock { activeSyncs[uniqueWorkName] } + ?.takeIf { it.job.isActive } + ?.let { + return it.progressChannel + } + + val statusFlow = MutableSharedFlow(replay = 1) + storeUniqueWorkNameInDataStore(fhirDataStore, uniqueWorkName) + + statusFlow.emit(CurrentSyncJobStatus.Enqueued) + + val job = + scope.launch { + val maxRetries = retryConfiguration?.maxRetries ?: 0 + var attempt = 0 + var lastResult: SyncJobStatus = SyncJobStatus.Failed() + + while (attempt <= maxRetries) { + if (attempt > 0) { + delay(computeBackoffDelayMillis(retryConfiguration!!, attempt - 1).milliseconds) + } + statusFlow.emit(CurrentSyncJobStatus.Running(SyncJobStatus.Started())) + lastResult = + try { + runSyncWithTimeout(taskFactory(), uniqueWorkName, syncTimeout) { syncJobStatus -> + statusFlow.emit(CurrentSyncJobStatus.Running(syncJobStatus)) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + Logger.e(e) { "One-time sync failed: ${e.message}" } + SyncJobStatus.Failed() + } + if (lastResult is SyncJobStatus.Succeeded) break + attempt++ + } + + when (lastResult) { + is SyncJobStatus.Succeeded -> + statusFlow.emit(CurrentSyncJobStatus.Succeeded(lastResult.timestamp)) + else -> + statusFlow.emit( + CurrentSyncJobStatus.Failed( + (lastResult as? SyncJobStatus.Failed)?.timestamp ?: Clock.System.now() + ) + ) + } + removeUniqueWorkNameInDataStore(fhirDataStore, uniqueWorkName) + mutex.withLock { activeSyncs.remove(uniqueWorkName) } + } + + mutex.withLock { activeSyncs[uniqueWorkName] = SyncHandle(job, statusFlow) } + return statusFlow + } + + private suspend fun runSyncWithTimeout( + task: FhirSyncTask, + taskName: String?, + syncTimeout: Duration?, + onProgress: suspend (SyncJobStatus) -> Unit, + ): SyncJobStatus { + val call = suspend { task.runSync(taskName = taskName, onProgress = onProgress) } + return if (syncTimeout != null) { + withTimeoutOrNull(syncTimeout) { call() } + ?: run { + Logger.w { "Sync timed out after $syncTimeout" } + SyncJobStatus.Failed() + } + } else { + call() + } + } + + private suspend fun storeUniqueWorkNameInDataStore( + fhirDataStore: FhirDataStore, + uniqueWorkName: String, + ) { + if (fhirDataStore.fetchUniqueWorkName(uniqueWorkName) == null) { + fhirDataStore.storeUniqueWorkName(key = uniqueWorkName, value = uniqueWorkName) + } + } + + private suspend fun removeUniqueWorkNameInDataStore( + fhirDataStore: FhirDataStore, + uniqueWorkName: String, + ) { + if (fhirDataStore.fetchUniqueWorkName(uniqueWorkName) != null) { + fhirDataStore.removeUniqueWorkName(key = uniqueWorkName) + } + } + + private fun computeBackoffDelayMillis(config: RetryConfiguration, attempt: Int): Long { + val baseDelayMs = config.backoffCriteria.backoffDelay.inWholeMilliseconds + return when (config.backoffCriteria.backoffPolicy) { + BackoffPolicy.EXPONENTIAL -> baseDelayMs * (1L shl attempt) + BackoffPolicy.LINEAR -> baseDelayMs + } + } +} + +private data class SyncHandle( + val job: Job, + val progressChannel: MutableSharedFlow, +) diff --git a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.web.kt b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.web.kt new file mode 100644 index 00000000..a5d0c77e --- /dev/null +++ b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.web.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import androidx.datastore.core.DataStore +import androidx.datastore.core.okio.WebLocalStorage +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.PreferencesSerializer + +// The web has no native filesystem for a DataStore file, so this persists in browser localStorage +// (survives page reloads, shared across tabs). +private val dataStore: DataStore by lazy { + PreferenceDataStoreFactory.create( + storage = + WebLocalStorage(serializer = PreferencesSerializer, name = SYNC_TIMESTAMP_DATASTORE_FILE_NAME) + ) +} + +internal actual fun createSyncTimestampDataStore(): DataStore = dataStore diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt similarity index 66% rename from ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt rename to ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt index 21b8ec02..21ab69ed 100644 --- a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -20,10 +20,15 @@ import androidx.compose.ui.window.ComposeViewport import dev.ohs.fhir.engine.FhirEngine import dev.ohs.fhir.engine.FhirEngineConfiguration import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.NetworkConfiguration import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.ForegroundSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import org.koin.dsl.module @OptIn(ExperimentalComposeUiApi::class) @@ -33,10 +38,21 @@ fun main() { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + networkConfiguration = NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), + httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ) ) ) - initKoin(module { single { FhirEngineProvider.getInstance() } }) + initKoin( + module { + single { FhirEngineProvider.getInstance() } + single { ForegroundSyncNowUseCase() } + } + ) ComposeViewport { App() } } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt index 95ee7121..7ef6816b 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt @@ -19,10 +19,15 @@ import androidx.compose.ui.window.ComposeUIViewController import dev.ohs.fhir.engine.FhirEngine import dev.ohs.fhir.engine.FhirEngineConfiguration import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.NetworkConfiguration import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.IosSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import org.koin.dsl.module fun MainViewController() = run { @@ -31,10 +36,21 @@ fun MainViewController() = run { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + networkConfiguration = NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), + httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ) ) ) - initKoin(module { single { FhirEngineProvider.getInstance() } }) + initKoin( + module { + single { FhirEngineProvider.getInstance() } + single { IosSyncNowUseCase() } + } + ) ComposeUIViewController { App() } } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt new file mode 100644 index 00000000..c8a54008 --- /dev/null +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import co.touchlab.kermit.Logger +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.runSync +import kotlin.coroutines.cancellation.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import platform.Foundation.NSNotificationCenter +import platform.UIKit.UIApplicationDidEnterBackgroundNotification +import platform.UIKit.UIApplicationWillEnterForegroundNotification + +/** + * Runs a one-time sync on a dedicated [Dispatchers.IO]-backed scope decoupled from the caller, so + * backgrounding the triggering screen doesn't cancel an in-flight network sync. Mirrors + * kotlin-fhir-engine's engine-app `FhirSyncController.ios.kt`: iOS suspends networking for + * backgrounded apps with no active background task, so an in-progress sync job is cancelled on + * [UIApplicationDidEnterBackgroundNotification] and relaunched from scratch on + * [UIApplicationWillEnterForegroundNotification], with [invoke] suspending across the relaunch + * until a terminal result arrives. + */ +class IosSyncNowUseCase : SyncNowUseCase { + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + + private var currentJob: Job? = null + private var currentStatusFlow: MutableSharedFlow? = null + private var syncWasRunning = false + + init { + NSNotificationCenter.defaultCenter.addObserverForName( + UIApplicationDidEnterBackgroundNotification, + null, + null, + ) { _ -> + if (currentJob?.isActive == true) { + syncWasRunning = true + currentJob?.cancel() + Logger.d { "IosSyncNowUseCase: sync suspended on background" } + } + } + + NSNotificationCenter.defaultCenter.addObserverForName( + UIApplicationWillEnterForegroundNotification, + null, + null, + ) { _ -> + if (syncWasRunning) { + syncWasRunning = false + launchSyncJob() + Logger.d { "IosSyncNowUseCase: sync restarted on foreground" } + } + } + } + + override suspend fun invoke(): SyncJobStatus { + val statusFlow = MutableSharedFlow(replay = 1) + currentStatusFlow = statusFlow + launchSyncJob() + return statusFlow.first { it is SyncJobStatus.Succeeded || it is SyncJobStatus.Failed } + } + + private fun launchSyncJob() { + val statusFlow = currentStatusFlow ?: return + currentJob?.cancel() + currentJob = + scope.launch { + val result = + try { + AppFhirSyncTask(FhirEngineProvider.getInstance()).runSync(taskName = null) {} + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + Logger.e(e) { "IosSyncNowUseCase: one-time sync failed" } + SyncJobStatus.Failed() + } + statusFlow.emit(result) + } + } +} diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.ios.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.ios.kt new file mode 100644 index 00000000..7272f7bb --- /dev/null +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.ios.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import dev.ohs.fhir.engine.sync.createDataStore +import kotlinx.cinterop.ExperimentalForeignApi +import platform.Foundation.NSApplicationSupportDirectory +import platform.Foundation.NSFileManager +import platform.Foundation.NSSearchPathForDirectoriesInDomains +import platform.Foundation.NSUserDomainMask + +@OptIn(ExperimentalForeignApi::class) +private val dataStore: DataStore by lazy { + val appSupportDir = + NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true) + .first() as String + NSFileManager.defaultManager.createDirectoryAtPath( + appSupportDir, + withIntermediateDirectories = true, + attributes = null, + error = null, + ) + createDataStore { "$appSupportDir/$SYNC_TIMESTAMP_DATASTORE_FILE_NAME" } +} + +internal actual fun createSyncTimestampDataStore(): DataStore = dataStore diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt new file mode 100644 index 00000000..2d8f9e60 --- /dev/null +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import dev.ohs.fhir.engine.sync.createDataStore +import java.io.File + +private val dataStore: DataStore by lazy { + val userHome = System.getProperty("user.home").orEmpty().ifBlank { "." } + val storageDirectory = File(userHome, ".ohs-player-reference-app") + createDataStore { File(storageDirectory, SYNC_TIMESTAMP_DATASTORE_FILE_NAME).absolutePath } +} + +internal actual fun createSyncTimestampDataStore(): DataStore = dataStore diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index 38f6109e..35c3ed24 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -20,10 +20,15 @@ import androidx.compose.ui.window.application import dev.ohs.fhir.engine.FhirEngine import dev.ohs.fhir.engine.FhirEngineConfiguration import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.NetworkConfiguration import dev.ohs.fhir.engine.ServerConfiguration +import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.ForegroundSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION +import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import java.io.File import org.koin.dsl.module @@ -36,10 +41,21 @@ fun main() = application { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, + networkConfiguration = NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), + httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ), ) ) - initKoin(module { single { FhirEngineProvider.getInstance() } }) + initKoin( + module { + single { FhirEngineProvider.getInstance() } + single { ForegroundSyncNowUseCase() } + } + ) Window(onCloseRequest = ::exitApplication, title = "OHS Player Reference App") { App() } } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContextTest.kt similarity index 75% rename from ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt rename to ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContextTest.kt index 602048a7..fe5bedb2 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/InMemoryTimestampContextTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/DataStoreTimestampContextTest.kt @@ -15,24 +15,33 @@ */ package dev.ohs.player.reference.app.data.sync +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import dev.ohs.fhir.engine.sync.createDataStore import dev.ohs.fhir.model.r4.terminologies.ResourceType import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull import kotlinx.coroutines.test.runTest -class InMemoryTimestampContextTest { +class DataStoreTimestampContextTest { + + private fun testDataStore(): DataStore { + val file = kotlin.io.path.createTempFile(suffix = ".preferences_pb").toFile() + file.deleteOnExit() + return createDataStore { file.absolutePath } + } @Test fun getLasUpdateTimestamp_beforeAnySave_isNull() = runTest { - val context = InMemoryTimestampContext() + val context = DataStoreTimestampContext(testDataStore()) assertNull(context.getLasUpdateTimestamp(ResourceType.Patient)) } @Test fun saveThenGet_roundTripsPerResourceType() = runTest { - val context = InMemoryTimestampContext() + val context = DataStoreTimestampContext(testDataStore()) context.saveLastUpdatedTimestamp(ResourceType.Patient, "2026-07-15T10:00:00Z") context.saveLastUpdatedTimestamp(ResourceType.Group, "2026-07-14T09:00:00Z") @@ -43,7 +52,7 @@ class InMemoryTimestampContextTest { @Test fun saveWithNullTimestamp_doesNotOverwriteExistingValue() = runTest { - val context = InMemoryTimestampContext() + val context = DataStoreTimestampContext(testDataStore()) context.saveLastUpdatedTimestamp(ResourceType.Patient, "2026-07-15T10:00:00Z") context.saveLastUpdatedTimestamp(ResourceType.Patient, null) From da4a6c7c635f7148bd98af3ed3e22793a073c605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:06:59 +0300 Subject: [PATCH 37/73] Add Sync.cancelOneTimeSync to the JVM/web foreground scheduler --- .../player/reference/app/data/sync/Sync.kt | 17 +++ .../reference/app/data/sync/SyncTest.kt | 111 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt index da839db5..0625c080 100644 --- a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt @@ -75,6 +75,11 @@ internal object Sync { return runOneTimeSync(uniqueWorkName, taskFactory, retryConfiguration, syncTimeout) } + /** Cancels an active one-time sync for [T]. No-op if none is active. */ + suspend inline fun cancelOneTimeSync() { + cancelSync("${T::class.simpleName}-oneTimeSync") + } + suspend fun runOneTimeSync( uniqueWorkName: String, taskFactory: () -> FhirSyncTask, @@ -137,6 +142,18 @@ internal object Sync { return statusFlow } + suspend fun cancelSync(uniqueWorkName: String) { + val handle = mutex.withLock { activeSyncs[uniqueWorkName] } + if (handle == null || !handle.job.isActive) { + Logger.w { "No active sync found for: $uniqueWorkName" } + return + } + handle.progressChannel.emit(CurrentSyncJobStatus.Cancelled) + handle.job.cancel() + mutex.withLock { activeSyncs.remove(uniqueWorkName) } + removeUniqueWorkNameInDataStore(fhirDataStore, uniqueWorkName) + } + private suspend fun runSyncWithTimeout( task: FhirSyncTask, taskName: String?, diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt new file mode 100644 index 00000000..6b5a3db7 --- /dev/null +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.fhir.engine.FhirEngineConfiguration +import dev.ohs.fhir.engine.FhirEngineProvider +import dev.ohs.fhir.engine.sync.AcceptLocalConflictResolver +import dev.ohs.fhir.engine.sync.ConflictResolver +import dev.ohs.fhir.engine.sync.CurrentSyncJobStatus +import dev.ohs.fhir.engine.sync.DownloadWorkManager +import dev.ohs.fhir.engine.sync.FhirSyncTask +import dev.ohs.fhir.engine.sync.download.DownloadRequest +import dev.ohs.fhir.engine.sync.upload.HttpCreateMethod +import dev.ohs.fhir.engine.sync.upload.HttpUpdateMethod +import dev.ohs.fhir.engine.sync.upload.UploadStrategy +import dev.ohs.fhir.model.r4.Resource +import dev.ohs.fhir.model.r4.terminologies.ResourceType +import java.nio.file.Files +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertIs +import kotlin.test.assertTrue +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest + +/** + * A no-op [FhirSyncTask] whose download step optionally blocks on [gate] before completing empty + * (no requests, no local changes) — real enough for [dev.ohs.fhir.engine.sync.runSync] to reach + * [dev.ohs.fhir.engine.sync.SyncJobStatus.Succeeded] without ever touching the network. Matches + * the shape kotlin-fhir-engine's own `DownloaderImplTest.TestDownloadWorkManager` uses. + */ +private class TestFhirSyncTask(private val gate: CompletableDeferred? = null) : FhirSyncTask { + override fun getFhirEngine(): FhirEngine = FhirEngineProvider.getInstance() + + override fun getDownloadWorkManager(): DownloadWorkManager = + object : DownloadWorkManager { + private var requested = false + + override suspend fun getNextRequest(): DownloadRequest? { + if (requested) return null + requested = true + gate?.await() + return null + } + + override suspend fun getSummaryRequestUrls(): Map = emptyMap() + + override suspend fun processResponse(response: Resource): Collection = emptyList() + } + + override fun getConflictResolver(): ConflictResolver = AcceptLocalConflictResolver + + override fun getUploadStrategy(): UploadStrategy = + UploadStrategy.forBundleRequest( + methodForCreate = HttpCreateMethod.PUT, + methodForUpdate = HttpUpdateMethod.PATCH, + squash = true, + bundleSize = 500, + ) +} + +class SyncTest { + + @BeforeTest + fun setUp() = runTest { + // Matches the established pattern in FhirEngineRepositoryTest: FhirEngineProvider is a + // process-wide singleton that throws if initialized twice, so guard it and reset state + // between tests instead of re-initializing. + if (FhirEngineProvider.isNotInitialized()) { + FhirEngineProvider.init( + FhirEngineConfiguration(storageDirectory = Files.createTempDirectory("sync-test").toString()) + ) + } + FhirEngineProvider.getInstance().clearDatabase() + } + + @Test + fun cancelOneTimeSync_whileRunning_emitsCancelled() = runTest { + val gate = CompletableDeferred() + val statusFlow = Sync.oneTimeSync(taskFactory = { TestFhirSyncTask(gate) }) + + // Let the sync actually start (past Enqueued) before cancelling. + statusFlow.first { it is CurrentSyncJobStatus.Running } + + Sync.cancelOneTimeSync() + + val terminal = statusFlow.first { it !is CurrentSyncJobStatus.Running } + assertIs(terminal) + } + + @Test + fun cancelOneTimeSync_withNoActiveSync_doesNotThrow() = runTest { + Sync.cancelOneTimeSync() + assertTrue(true) // reaching here means no-op didn't throw + } +} From 2d066869c8d33b74647eaf14ffa678bf58646723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:18:00 +0300 Subject: [PATCH 38/73] Add Sync.periodicSync with a per-platform connectivity gate --- ohs-player-reference-app/build.gradle.kts | 1 + .../player/reference/app/data/sync/Sync.kt | 123 ++++++++++++++---- .../app/data/sync/NetworkConnectivity.web.kt | 25 ++++ .../app/data/sync/NetworkConnectivity.jvm.kt | 32 +++++ .../reference/app/data/sync/SyncTest.kt | 47 +++++++ 5 files changed, 200 insertions(+), 28 deletions(-) create mode 100644 ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt create mode 100644 ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.jvm.kt diff --git a/ohs-player-reference-app/build.gradle.kts b/ohs-player-reference-app/build.gradle.kts index 0d5db7f1..24d4a796 100644 --- a/ohs-player-reference-app/build.gradle.kts +++ b/ohs-player-reference-app/build.gradle.kts @@ -125,6 +125,7 @@ kotlin { implementation(libs.ktor.client.mock) } iosMain.dependencies { implementation(libs.ktor.client.darwin) } + getByName("foregroundSyncWebMain").dependencies { implementation(libs.kotlinx.browser) } webMain.dependencies { // :engine's WebWorkerSQLiteDriver worker (androidx.sqlite:sqlite-web) is loaded via // `new Worker(new URL("sqlite-wasm-worker/worker.js", import.meta.url), { type: "module" })` diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt index 0625c080..fb054515 100644 --- a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt @@ -30,6 +30,7 @@ import kotlin.coroutines.cancellation.CancellationException import kotlin.time.Clock import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob @@ -41,17 +42,20 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withTimeoutOrNull +/** Supplies each platform's best-effort "is the device online" check for [Sync.periodicSync]. */ +internal expect fun isNetworkConnected(): Boolean + /** - * Foreground-only one-time sync scheduler shared by Desktop (JVM) and web (js, wasmJs) — neither - * platform has a native OS background scheduler (no WorkManager, no BGTaskScheduler), so sync only - * runs while the host process (JVM process / browser tab) stays alive. Mirrors - * kotlin-fhir-engine's engine-app `Sync` object, trimmed to the one-time-sync surface this app - * uses (no periodic sync). + * Foreground-only sync scheduler shared by Desktop (JVM) and web (js, wasmJs) — neither platform + * has a native OS background scheduler (no WorkManager, no BGTaskScheduler), so sync only runs + * while the host process (JVM process / browser tab) stays alive. Mirrors kotlin-fhir-engine's + * engine-app `Sync` object. */ internal object Sync { private val scope = CoroutineScope(SupervisorJob() + syncDispatcher) private val mutex = Mutex() private val activeSyncs = mutableMapOf() + private val activePeriodicJobs = mutableMapOf() private val fhirDataStore: FhirDataStore by lazy { FhirEngineProvider.getFhirDataStore() } /** @@ -80,6 +84,33 @@ internal object Sync { cancelSync("${T::class.simpleName}-oneTimeSync") } + /** + * Schedules a recurring foreground sync using [FhirSyncTask] instances created by [taskFactory]. + * Safe to call repeatedly — a cycle already running for [T] is left alone, never duplicated. + * Skips (not fails/retries) any cycle where [isNetworkConnected] returns false. + * + * @param repeatInterval Delay between the end of one cycle and the start of the next. + * @param retryConfiguration Retry policy applied within each cycle, or null to disable retries. + */ + suspend inline fun periodicSync( + noinline taskFactory: () -> T, + repeatInterval: Duration = 15.minutes, + retryConfiguration: RetryConfiguration? = defaultRetryConfiguration, + ) { + runPeriodicSync( + "${T::class.simpleName}-periodicSync", + taskFactory, + repeatInterval, + retryConfiguration, + ) + } + + /** Cancels the recurring periodic sync for [T]. No-op if none is active. */ + suspend inline fun cancelPeriodicSync() { + val job = mutex.withLock { activePeriodicJobs.remove("${T::class.simpleName}-periodicSync") } + job?.cancel() + } + suspend fun runOneTimeSync( uniqueWorkName: String, taskFactory: () -> FhirSyncTask, @@ -100,30 +131,10 @@ internal object Sync { val job = scope.launch { - val maxRetries = retryConfiguration?.maxRetries ?: 0 - var attempt = 0 - var lastResult: SyncJobStatus = SyncJobStatus.Failed() - - while (attempt <= maxRetries) { - if (attempt > 0) { - delay(computeBackoffDelayMillis(retryConfiguration!!, attempt - 1).milliseconds) + val lastResult = + runAttemptsWithRetry(taskFactory, uniqueWorkName, retryConfiguration, syncTimeout) { + statusFlow.emit(it) } - statusFlow.emit(CurrentSyncJobStatus.Running(SyncJobStatus.Started())) - lastResult = - try { - runSyncWithTimeout(taskFactory(), uniqueWorkName, syncTimeout) { syncJobStatus -> - statusFlow.emit(CurrentSyncJobStatus.Running(syncJobStatus)) - } - } catch (e: CancellationException) { - throw e - } catch (e: Exception) { - Logger.e(e) { "One-time sync failed: ${e.message}" } - SyncJobStatus.Failed() - } - if (lastResult is SyncJobStatus.Succeeded) break - attempt++ - } - when (lastResult) { is SyncJobStatus.Succeeded -> statusFlow.emit(CurrentSyncJobStatus.Succeeded(lastResult.timestamp)) @@ -142,6 +153,28 @@ internal object Sync { return statusFlow } + suspend fun runPeriodicSync( + uniqueWorkName: String, + taskFactory: () -> FhirSyncTask, + repeatInterval: Duration, + retryConfiguration: RetryConfiguration?, + ) { + mutex.withLock { activePeriodicJobs[uniqueWorkName] }?.takeIf { it.isActive }?.let { return } + + val job = + scope.launch { + while (true) { + if (isNetworkConnected()) { + runAttemptsWithRetry(taskFactory, uniqueWorkName, retryConfiguration, syncTimeout = null) {} + } else { + Logger.d { "Periodic sync cycle skipped for $uniqueWorkName — offline" } + } + delay(repeatInterval) + } + } + mutex.withLock { activePeriodicJobs[uniqueWorkName] = job } + } + suspend fun cancelSync(uniqueWorkName: String) { val handle = mutex.withLock { activeSyncs[uniqueWorkName] } if (handle == null || !handle.job.isActive) { @@ -154,6 +187,40 @@ internal object Sync { removeUniqueWorkNameInDataStore(fhirDataStore, uniqueWorkName) } + /** Runs [taskFactory]'s sync with retry, reporting each intermediate status via [onStatus]. */ + private suspend fun runAttemptsWithRetry( + taskFactory: () -> FhirSyncTask, + uniqueWorkName: String, + retryConfiguration: RetryConfiguration?, + syncTimeout: Duration?, + onStatus: suspend (CurrentSyncJobStatus) -> Unit, + ): SyncJobStatus { + val maxRetries = retryConfiguration?.maxRetries ?: 0 + var attempt = 0 + var lastResult: SyncJobStatus = SyncJobStatus.Failed() + + while (attempt <= maxRetries) { + if (attempt > 0) { + delay(computeBackoffDelayMillis(retryConfiguration!!, attempt - 1).milliseconds) + } + onStatus(CurrentSyncJobStatus.Running(SyncJobStatus.Started())) + lastResult = + try { + runSyncWithTimeout(taskFactory(), uniqueWorkName, syncTimeout) { syncJobStatus -> + onStatus(CurrentSyncJobStatus.Running(syncJobStatus)) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + Logger.e(e) { "Sync failed: ${e.message}" } + SyncJobStatus.Failed() + } + if (lastResult is SyncJobStatus.Succeeded) break + attempt++ + } + return lastResult + } + private suspend fun runSyncWithTimeout( task: FhirSyncTask, taskName: String?, diff --git a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt new file mode 100644 index 00000000..7ea030c5 --- /dev/null +++ b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import kotlinx.browser.window + +/** + * `navigator.onLine` is a best-effort browser signal (reliably `false` when definitely offline, + * but can still report `true` without real connectivity) — good enough to skip an obviously + * doomed periodic sync cycle. + */ +internal actual fun isNetworkConnected(): Boolean = window.navigator.onLine diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.jvm.kt new file mode 100644 index 00000000..6e01283f --- /dev/null +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.jvm.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import java.net.NetworkInterface +import java.util.Collections + +/** + * Desktop has no OS-level sync scheduler to enforce a network constraint (unlike Android/iOS), so + * [dev.ohs.player.reference.app.data.sync.Sync.periodicSync] checks this before every cycle. Fails + * open (returns true) on enumeration errors — an unnecessary sync attempt that fails is preferable + * to silently never syncing again because this heuristic broke. + */ +internal actual fun isNetworkConnected(): Boolean = + try { + Collections.list(NetworkInterface.getNetworkInterfaces()).any { it.isUp && !it.isLoopback } + } catch (e: Exception) { + true + } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt index 6b5a3db7..df1b2d25 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt @@ -32,8 +32,10 @@ import dev.ohs.fhir.model.r4.terminologies.ResourceType import java.nio.file.Files import kotlin.test.BeforeTest import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.minutes import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest @@ -108,4 +110,49 @@ class SyncTest { Sync.cancelOneTimeSync() assertTrue(true) // reaching here means no-op didn't throw } + + @Test + fun periodicSync_start_runsFirstCycleImmediately() = runTest { + val firstAttempt = CompletableDeferred() + + Sync.periodicSync( + taskFactory = { + firstAttempt.complete(Unit) + TestFhirSyncTask() + }, + // Long enough that only one cycle should fire during this test's lifetime. + repeatInterval = 1.minutes, + ) + + // No withTimeout wrapper here on purpose: withTimeout schedules its cancellation on the + // *test* dispatcher's virtual-time scheduler, which auto-advances past it near-instantly + // once this coroutine looks "idle" (runTest doesn't know about the real work happening on + // Sync's separate syncDispatcher) — a virtual/real time mismatch that made this test flake. + // A plain await() is a real suspension resolved only when the background coroutine actually + // completes it; runTest's own real-time dispatch timeout is the safety net if it never does. + firstAttempt.await() + + Sync.cancelPeriodicSync() + } + + @Test + fun periodicSync_calledTwice_doesNotStartASecondCompetingLoop() = runTest { + var startCount = 0 + val firstAttemptStarted = CompletableDeferred() + val factory: () -> TestFhirSyncTask = { + startCount++ + firstAttemptStarted.complete(Unit) + TestFhirSyncTask() + } + + Sync.periodicSync(taskFactory = factory, repeatInterval = 1.minutes) + firstAttemptStarted.await() + // The first cycle's loop is now sleeping the 1-minute interval, so it's still "active" — + // this second call must see that and no-op rather than starting a competing loop. + Sync.periodicSync(taskFactory = factory, repeatInterval = 1.minutes) + + assertEquals(1, startCount) + + Sync.cancelPeriodicSync() + } } From 25d2761f1da939cc695d48c6c1c2331d2b9723f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:23:25 +0300 Subject: [PATCH 39/73] Add SyncNowUseCase.cancel() and implement it on every platform --- .../reference/app/data/sync/WorkManagerSyncNowUseCase.kt | 4 ++++ .../ohs/player/reference/app/data/sync/SyncNowUseCase.kt | 5 ++++- .../app/feature/sync/InitialSyncViewModelTest.kt | 2 ++ .../reference/app/data/sync/ForegroundSyncNowUseCase.kt | 8 +++++++- .../player/reference/app/data/sync/IosSyncNowUseCase.kt | 6 ++++++ .../dev/ohs/player/reference/app/AppAuthGatingTest.kt | 2 ++ .../player/reference/app/feature/home/HomeScreenTest.kt | 7 +++++++ .../reference/app/feature/home/HomeViewModelTest.kt | 6 ++++++ 8 files changed, 38 insertions(+), 2 deletions(-) diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt index c7ea2dfd..d9c945fa 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt @@ -39,4 +39,8 @@ class WorkManagerSyncNowUseCase(private val context: Context) : SyncNowUseCase { else -> SyncJobStatus.Failed() } } + + override suspend fun cancel() { + Sync.cancelOneTimeSync(context) + } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt index ae714e58..2919b9a6 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt @@ -27,6 +27,9 @@ import dev.ohs.fhir.engine.sync.runSync * `AppFhirSyncWorker`/`WorkManagerSyncNowUseCase` (Android), `ForegroundSyncNowUseCase` (JVM/web), * and `IosSyncNowUseCase` (iOS). */ -fun interface SyncNowUseCase { +interface SyncNowUseCase { suspend fun invoke(): SyncJobStatus + + /** Cancels an in-flight [invoke] call. No-op if no sync is currently running. */ + suspend fun cancel() } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt index e8f52ddd..b05df6da 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt @@ -35,6 +35,8 @@ class InitialSyncViewModelTest { invocationCount++ return result() } + + override suspend fun cancel() {} } private val json = Json { ignoreUnknownKeys = true } diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt index a23e781e..d2e71127 100644 --- a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt @@ -28,11 +28,17 @@ class ForegroundSyncNowUseCase : SyncNowUseCase { override suspend fun invoke(): SyncJobStatus { val terminalStatus = Sync.oneTimeSync(taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }).first { - it is CurrentSyncJobStatus.Succeeded || it is CurrentSyncJobStatus.Failed + it is CurrentSyncJobStatus.Succeeded || + it is CurrentSyncJobStatus.Failed || + it is CurrentSyncJobStatus.Cancelled } return when (terminalStatus) { is CurrentSyncJobStatus.Succeeded -> SyncJobStatus.Succeeded() else -> SyncJobStatus.Failed() } } + + override suspend fun cancel() { + Sync.cancelOneTimeSync() + } } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt index c8a54008..e72e37a0 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt @@ -81,6 +81,12 @@ class IosSyncNowUseCase : SyncNowUseCase { return statusFlow.first { it is SyncJobStatus.Succeeded || it is SyncJobStatus.Failed } } + override suspend fun cancel() { + syncWasRunning = false + currentJob?.cancel() + currentStatusFlow?.emit(SyncJobStatus.Failed()) + } + private fun launchSyncJob() { val statusFlow = currentStatusFlow ?: return currentJob?.cancel() diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt index 074c9704..49b06b44 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt @@ -75,6 +75,8 @@ private class FakeAppSyncNowUseCase : SyncNowUseCase { invocationCount++ return SyncJobStatus.Succeeded() } + + override suspend fun cancel() {} } private class InMemorySessionStore(initial: Session? = null) : SessionStore { diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index 97a031c2..768dc9c2 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -44,7 +44,14 @@ import org.koin.dsl.module private class FakeHomeScreenSyncNowUseCase(private val result: suspend () -> SyncJobStatus) : SyncNowUseCase { + var cancelCalled = false + private set + override suspend fun invoke(): SyncJobStatus = result() + + override suspend fun cancel() { + cancelCalled = true + } } @OptIn(ExperimentalTestApi::class) diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt index eae5eb24..33a3eb36 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt @@ -32,6 +32,8 @@ private class FakeSyncNowUseCase( ) : SyncNowUseCase { var invocationCount = 0 private set + var cancelCount = 0 + private set override suspend fun invoke(): SyncJobStatus { invocationCount++ @@ -41,6 +43,10 @@ private class FakeSyncNowUseCase( fhirDataStore.writeLastSyncTimestamp(status.timestamp) return status } + + override suspend fun cancel() { + cancelCount++ + } } class HomeViewModelTest { From c1bbadb0d904c9400ae4f7f797ecb45db640c767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:27:03 +0300 Subject: [PATCH 40/73] Add PeriodicSyncUseCase with a WorkManager-backed Android implementation --- .../reference/app/OhsPlayerApplication.kt | 3 ++ .../sync/WorkManagerPeriodicSyncUseCase.kt | 42 +++++++++++++++++++ .../app/data/sync/PeriodicSyncUseCase.kt | 35 ++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt index e8ee448c..4028a488 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt @@ -26,8 +26,10 @@ import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.WorkManagerPeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.WorkManagerSyncNowUseCase import org.koin.dsl.module @@ -54,6 +56,7 @@ class OhsPlayerApplication : Application() { module { single { FhirEngineProvider.getInstance(applicationContext) } single { WorkManagerSyncNowUseCase(applicationContext) } + single { WorkManagerPeriodicSyncUseCase(applicationContext) } } ) DataCapture.initialize(applicationContext) diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt new file mode 100644 index 00000000..8eece71c --- /dev/null +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import android.content.Context +import dev.ohs.fhir.engine.sync.PeriodicSyncConfiguration +import dev.ohs.fhir.engine.sync.RepeatInterval +import dev.ohs.fhir.engine.sync.Sync +import kotlin.time.Duration.Companion.minutes + +/** + * Schedules this app's sync as periodic WorkManager work, reusing [AppFhirSyncWorker] (the same + * Worker class [WorkManagerSyncNowUseCase] uses for one-time sync). [PeriodicSyncConfiguration]'s + * default `SyncConstraints` already requires `NetworkType.CONNECTED`, so WorkManager itself defers + * runs until the device is online — no separate connectivity check needed here. + */ +class WorkManagerPeriodicSyncUseCase(private val context: Context) : PeriodicSyncUseCase { + override suspend fun start() { + Sync.periodicSync( + context, + periodicSyncConfiguration = + PeriodicSyncConfiguration(repeat = RepeatInterval(interval = 15.minutes)), + ) + } + + override suspend fun cancel() { + Sync.cancelPeriodicSync(context) + } +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt new file mode 100644 index 00000000..39c50cf4 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +/** + * Schedules this app's sync to run every 15 minutes while the device is online. Each platform + * supplies its own implementation — see `WorkManagerPeriodicSyncUseCase` (Android), + * `ForegroundPeriodicSyncUseCase` (JVM/web), and `IosPeriodicSyncUseCase` (iOS). + */ +interface PeriodicSyncUseCase { + /** + * Safe to call repeatedly — never schedules a duplicate/competing cycle. What "safe to call + * repeatedly" means differs per platform: Android's `ExistingPeriodicWorkPolicy.KEEP` (default) + * literally no-ops if already scheduled; iOS's scheduler cancels any pending request before + * submitting a fresh one (a refresh, not a no-op); JVM/web's own implementation checks an + * "already running" job and no-ops, for consistency with the other two. + */ + suspend fun start() + + /** Cancels the periodic sync. No-op if none is scheduled. */ + suspend fun cancel() +} From 3d13245161fa608ecda97b61467588491d4ac9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:29:48 +0300 Subject: [PATCH 41/73] Add ForegroundPeriodicSyncUseCase for JVM and web --- .../sync/ForegroundPeriodicSyncUseCase.kt | 37 +++++++++++++++++++ .../dev/ohs/player/reference/app/main.kt | 3 ++ .../dev/ohs/player/reference/app/main.kt | 3 ++ 3 files changed, 43 insertions(+) create mode 100644 ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt new file mode 100644 index 00000000..26ed9bda --- /dev/null +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.engine.FhirEngineProvider +import kotlin.time.Duration.Companion.minutes + +/** + * Schedules this app's sync through the shared foreground [Sync] scheduler — Desktop and web only + * sync while their host process is alive (see [Sync]'s docs for why), gated each cycle on + * [isNetworkConnected]. + */ +class ForegroundPeriodicSyncUseCase : PeriodicSyncUseCase { + override suspend fun start() { + Sync.periodicSync( + taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }, + repeatInterval = 15.minutes, + ) + } + + override suspend fun cancel() { + Sync.cancelPeriodicSync() + } +} diff --git a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt index 21ab69ed..d0316035 100644 --- a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -26,7 +26,9 @@ import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.ForegroundPeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.ForegroundSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import org.koin.dsl.module @@ -52,6 +54,7 @@ fun main() { module { single { FhirEngineProvider.getInstance() } single { ForegroundSyncNowUseCase() } + single { ForegroundPeriodicSyncUseCase() } } ) ComposeViewport { App() } diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index 35c3ed24..6a553a42 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -26,7 +26,9 @@ import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.ForegroundPeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.ForegroundSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import java.io.File @@ -55,6 +57,7 @@ fun main() = application { module { single { FhirEngineProvider.getInstance() } single { ForegroundSyncNowUseCase() } + single { ForegroundPeriodicSyncUseCase() } } ) Window(onCloseRequest = ::exitApplication, title = "OHS Player Reference App") { App() } From 6c7a7c2a13bdf34daca5537d6459769bbbdd8728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:19:51 +0300 Subject: [PATCH 42/73] Add IosPeriodicSyncUseCase backed by BGTaskScheduler --- iosApp/iosApp/Info.plist | 8 ++ .../reference/app/MainViewController.kt | 6 + .../app/data/sync/IosBgSyncScheduler.kt | 132 ++++++++++++++++++ .../app/data/sync/IosPeriodicSyncUseCase.kt | 46 ++++++ 4 files changed, 192 insertions(+) create mode 100644 ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt create mode 100644 ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 11845e1d..4ed514f7 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -4,5 +4,13 @@ CADisableMinimumFrameDurationOnPhone + BGTaskSchedulerPermittedIdentifiers + + dev.ohs.player.reference.app.sync.periodic + + UIBackgroundModes + + processing + diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt index 7ef6816b..000d1691 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt @@ -25,7 +25,9 @@ import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin +import dev.ohs.player.reference.app.data.sync.IosPeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.IosSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import org.koin.dsl.module @@ -46,10 +48,14 @@ fun MainViewController() = run { ) ) ) + // Constructed eagerly (not inside the Koin lambda, which is lazy) so BGTaskScheduler + // registration happens now, during app launch — see IosBgSyncScheduler's docs. + val periodicSyncUseCase = IosPeriodicSyncUseCase() initKoin( module { single { FhirEngineProvider.getInstance() } single { IosSyncNowUseCase() } + single { periodicSyncUseCase } } ) ComposeUIViewController { App() } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt new file mode 100644 index 00000000..2e726910 --- /dev/null +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import co.touchlab.kermit.Logger +import dev.ohs.fhir.engine.sync.FhirSyncTask +import dev.ohs.fhir.engine.sync.SyncJobStatus +import dev.ohs.fhir.engine.sync.runSync +import kotlin.concurrent.AtomicInt +import kotlin.coroutines.cancellation.CancellationException +import kotlinx.cinterop.BetaInteropApi +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.ObjCObjectVar +import kotlinx.cinterop.alloc +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.ptr +import kotlinx.cinterop.value +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import platform.BackgroundTasks.BGProcessingTask +import platform.BackgroundTasks.BGProcessingTaskRequest +import platform.BackgroundTasks.BGTaskScheduler +import platform.Foundation.NSError + +/** + * Registers and schedules this app's sync as a `BGProcessingTask`. Mirrors kotlin-fhir-engine's + * engine-app `IosBgSyncScheduler`. [register] must be called during app launch, before + * `applicationDidFinishLaunching` returns per Apple's `BGTaskScheduler` docs — see + * [dev.ohs.player.reference.app.MainViewController], which constructs [IosPeriodicSyncUseCase] + * eagerly (not lazily through Koin) for exactly this reason. Requires + * `BGTaskSchedulerPermittedIdentifiers`/`UIBackgroundModes` in `iosApp/iosApp/Info.plist`. + */ +internal class IosBgSyncScheduler( + private val taskIdentifier: String, + private val taskFactory: () -> FhirSyncTask, +) { + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + private val registered = AtomicInt(0) + + fun register() { + if (!registered.compareAndSet(0, 1)) return + BGTaskScheduler.sharedScheduler.registerForTaskWithIdentifier( + taskIdentifier, + usingQueue = null, + launchHandler = { task -> handleTask(task as BGProcessingTask) }, + ) + Logger.d { "IosBgSyncScheduler: registered handler for $taskIdentifier" } + } + + fun schedule() { + BGTaskScheduler.sharedScheduler.cancelTaskRequestWithIdentifier(taskIdentifier) + val request = + BGProcessingTaskRequest(taskIdentifier).apply { + requiresNetworkConnectivity = true + requiresExternalPower = false + } + submitRequest(request) + } + + fun cancel() { + BGTaskScheduler.sharedScheduler.cancelTaskRequestWithIdentifier(taskIdentifier) + } + + private fun handleTask(task: BGProcessingTask) { + val mutex = Mutex() + var completed = false + + val completeOnce: (Boolean) -> Unit = { success -> + scope.launch { + mutex.withLock { + if (!completed) { + completed = true + task.setTaskCompletedWithSuccess(success) + if (success) schedule() + } + } + } + } + + task.expirationHandler = { + Logger.w { "IosBgSyncScheduler: task expired for $taskIdentifier" } + completeOnce(false) + } + + scope.launch { + try { + val status = taskFactory().runSync(taskName = taskIdentifier, onProgress = {}) + Logger.d { "IosBgSyncScheduler: sync completed with $status" } + completeOnce(status is SyncJobStatus.Succeeded) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + Logger.e(e) { "IosBgSyncScheduler: sync failed" } + completeOnce(false) + } + } + } + + @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) + private fun submitRequest(request: BGProcessingTaskRequest) { + try { + memScoped { + val error = alloc>() + val success = BGTaskScheduler.sharedScheduler.submitTaskRequest(request, error.ptr) + if (!success) { + val msg = error.value?.localizedDescription ?: "no error details" + Logger.e { "IosBgSyncScheduler: submit failed ($msg)" } + } + } + } catch (e: Exception) { + Logger.e(e) { "IosBgSyncScheduler: exception submitting request" } + } + } +} diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt new file mode 100644 index 00000000..d1b554aa --- /dev/null +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.engine.FhirEngineProvider + +/** Must match `BGTaskSchedulerPermittedIdentifiers` in `iosApp/iosApp/Info.plist`. */ +internal const val PERIODIC_SYNC_TASK_IDENTIFIER = "dev.ohs.player.reference.app.sync.periodic" + +/** + * Schedules this app's sync as a `BGProcessingTask` via [IosBgSyncScheduler]. Registration happens + * in the constructor (not lazily) — see [IosBgSyncScheduler]'s docs on why that matters, and + * [dev.ohs.player.reference.app.MainViewController], which constructs this eagerly. + */ +class IosPeriodicSyncUseCase : PeriodicSyncUseCase { + private val scheduler = + IosBgSyncScheduler( + taskIdentifier = PERIODIC_SYNC_TASK_IDENTIFIER, + taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }, + ) + + init { + scheduler.register() + } + + override suspend fun start() { + scheduler.schedule() + } + + override suspend fun cancel() { + scheduler.cancel() + } +} From fdd6d7cd74c9f6db0686180ba6c4bde3d06f60c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:51:50 +0300 Subject: [PATCH 43/73] Start periodic sync once InitialSyncViewModel passes its gate --- .../player/reference/app/data/di/AppModule.kt | 2 +- .../app/feature/sync/InitialSyncViewModel.kt | 23 +++++++----- .../feature/sync/InitialSyncViewModelTest.kt | 36 +++++++++++++++---- .../player/reference/app/AppAuthGatingTest.kt | 8 +++++ 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt index d2317331..9b12b8a9 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt @@ -87,5 +87,5 @@ internal val viewModelModule = module { } viewModel { HomeViewModel(get(), get()) } viewModel { AuthViewModel(get(), get()) } - viewModel { InitialSyncViewModel(get(), get()) } + viewModel { InitialSyncViewModel(get(), get(), get()) } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt index 8704ce05..0d8fc908 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt @@ -19,6 +19,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.player.reference.app.data.repository.FhirRepository +import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import kotlin.coroutines.cancellation.CancellationException import kotlinx.coroutines.Job @@ -49,6 +50,7 @@ sealed interface InitialSyncGateState { class InitialSyncViewModel( private val fhirRepository: FhirRepository, private val syncNowUseCase: SyncNowUseCase, + private val periodicSyncUseCase: PeriodicSyncUseCase, ) : ViewModel() { private val _state = MutableStateFlow(InitialSyncGateState.Checking) @@ -58,14 +60,12 @@ class InitialSyncViewModel( fun retry(): Job = viewModelScope.launch { runSync() } - fun continueAnyway() { - _state.value = InitialSyncGateState.Passed - } + fun continueAnyway(): Job = viewModelScope.launch { passGate() } private suspend fun runCheck() { _state.value = InitialSyncGateState.Checking if (fhirRepository.hasAnyData()) { - _state.value = InitialSyncGateState.Passed + passGate() } else { runSync() } @@ -81,11 +81,16 @@ class InitialSyncViewModel( } catch (e: Exception) { SyncJobStatus.Failed() } - _state.value = - when (result) { - is SyncJobStatus.Succeeded -> InitialSyncGateState.Passed - else -> + when (result) { + is SyncJobStatus.Succeeded -> passGate() + else -> + _state.value = InitialSyncGateState.Failed("Sync failed. Please check your connection and try again.") - } + } + } + + private suspend fun passGate() { + periodicSyncUseCase.start() + _state.value = InitialSyncGateState.Passed } } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt index b05df6da..1977acab 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt @@ -18,6 +18,7 @@ package dev.ohs.player.reference.app.feature.sync import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.model.r4.Patient import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository +import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import kotlin.test.Test import kotlin.test.assertEquals @@ -39,6 +40,17 @@ class InitialSyncViewModelTest { override suspend fun cancel() {} } + private class FakePeriodicSyncUseCase : PeriodicSyncUseCase { + var startCount = 0 + private set + + override suspend fun start() { + startCount++ + } + + override suspend fun cancel() {} + } + private val json = Json { ignoreUnknownKeys = true } private fun testPatient() = @@ -49,35 +61,41 @@ class InitialSyncViewModelTest { val repository = InMemorySampleFhirRepository() repository.upsert(testPatient()) val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Succeeded() } - val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + val periodicSyncUseCase = FakePeriodicSyncUseCase() + val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) viewModel.start().join() assertEquals(InitialSyncGateState.Passed, viewModel.state.value) assertEquals(0, syncNowUseCase.invocationCount) + assertEquals(1, periodicSyncUseCase.startCount) } @Test fun start_withNoData_syncsThenPasses() = runTest { val repository = InMemorySampleFhirRepository() val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Succeeded() } - val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + val periodicSyncUseCase = FakePeriodicSyncUseCase() + val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) viewModel.start().join() assertEquals(InitialSyncGateState.Passed, viewModel.state.value) assertEquals(1, syncNowUseCase.invocationCount) + assertEquals(1, periodicSyncUseCase.startCount) } @Test fun start_withNoDataAndSyncFails_goesFailed() = runTest { val repository = InMemorySampleFhirRepository() val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Failed() } - val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + val periodicSyncUseCase = FakePeriodicSyncUseCase() + val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) viewModel.start().join() assertIs(viewModel.state.value) + assertEquals(0, periodicSyncUseCase.startCount) } @Test @@ -86,7 +104,8 @@ class InitialSyncViewModelTest { var shouldFail = true val syncNowUseCase = FakeSyncNowUseCase { if (shouldFail) SyncJobStatus.Failed() else SyncJobStatus.Succeeded() } - val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + val periodicSyncUseCase = FakePeriodicSyncUseCase() + val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) viewModel.start().join() assertIs(viewModel.state.value) @@ -95,18 +114,21 @@ class InitialSyncViewModelTest { assertEquals(InitialSyncGateState.Passed, viewModel.state.value) assertEquals(2, syncNowUseCase.invocationCount) + assertEquals(1, periodicSyncUseCase.startCount) } @Test - fun continueAnyway_movesToPassed() = runTest { + fun continueAnyway_movesToPassedAndStartsPeriodicSync() = runTest { val repository = InMemorySampleFhirRepository() val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Failed() } - val viewModel = InitialSyncViewModel(repository, syncNowUseCase) + val periodicSyncUseCase = FakePeriodicSyncUseCase() + val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) viewModel.start().join() assertIs(viewModel.state.value) - viewModel.continueAnyway() + viewModel.continueAnyway().join() assertEquals(InitialSyncGateState.Passed, viewModel.state.value) + assertEquals(1, periodicSyncUseCase.startCount) } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt index 49b06b44..d3cb1a46 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt @@ -45,6 +45,7 @@ import dev.ohs.player.reference.app.data.di.repositoryModule import dev.ohs.player.reference.app.data.di.viewModelModule import dev.ohs.player.reference.app.data.repository.FhirRepository import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository +import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SyncNowUseCase import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine @@ -79,6 +80,12 @@ private class FakeAppSyncNowUseCase : SyncNowUseCase { override suspend fun cancel() {} } +private class FakeAppPeriodicSyncUseCase : PeriodicSyncUseCase { + override suspend fun start() {} + + override suspend fun cancel() {} +} + private class InMemorySessionStore(initial: Session? = null) : SessionStore { private val _session = MutableStateFlow(initial) override val session: StateFlow = _session.asStateFlow() @@ -177,6 +184,7 @@ class AppAuthGatingTest { single { fhirRepository } single { newFhirDataStore() } single { syncNowUseCase } + single { FakeAppPeriodicSyncUseCase() } single { OAuthConfig("https://idp.example.org", "client", "openid") } single { InMemorySessionStore(initial = session) } single { OidcAuthApi(get(), client) } From 95afd7ba2ccf38dcc6cb9598d039b6c3a71fa5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:58:51 +0300 Subject: [PATCH 44/73] Turn the drawer's Sync now item into a start/cancel toggle --- .../reference/app/feature/home/HomeScreen.kt | 16 ++++++--- .../app/feature/home/HomeViewModel.kt | 18 +++++++--- .../app/feature/home/HomeScreenTest.kt | 35 +++++++++++++++++++ .../app/feature/home/HomeViewModelTest.kt | 30 ++++++++++++++++ 4 files changed, 91 insertions(+), 8 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index 72ccccac..b4a12327 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ExitToApp +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.CircularProgressIndicator @@ -121,8 +122,13 @@ fun HomeScreen( ) } NavigationDrawerItem( - label = { Text("Sync now") }, - icon = { Icon(Icons.Filled.Refresh, contentDescription = null) }, + label = { Text(if (uiState.isSyncing) "Cancel sync" else "Sync now") }, + icon = { + Icon( + if (uiState.isSyncing) Icons.Filled.Close else Icons.Filled.Refresh, + contentDescription = null, + ) + }, badge = { if (uiState.isSyncing) { CircularProgressIndicator( @@ -134,10 +140,12 @@ fun HomeScreen( }, selected = false, onClick = { - if (!uiState.isSyncing) { + if (uiState.isSyncing) { + homeViewModel.cancelSync() + } else { homeViewModel.syncNow() - closeDrawerIfCompact() } + closeDrawerIfCompact() }, ) NavigationDrawerItem( diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt index 2f2c2b13..6f505dc1 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt @@ -44,6 +44,8 @@ class HomeViewModel( private val _uiState = MutableStateFlow(HomeUiState()) val uiState: StateFlow = _uiState.asStateFlow() + private var cancelRequested = false + init { viewModelScope.launch { val lastSyncedAt = fhirDataStore.readLastSyncTimestamp()?.toDisplayString() @@ -57,6 +59,7 @@ class HomeViewModel( */ fun syncNow(): Job? { if (_uiState.value.isSyncing) return null + cancelRequested = false _uiState.update { it.copy(isSyncing = true, syncError = null) } return viewModelScope.launch { val result = @@ -72,14 +75,21 @@ class HomeViewModel( val lastSyncedAt = fhirDataStore.readLastSyncTimestamp()?.toDisplayString() _uiState.update { it.copy(isSyncing = false, lastSyncedAt = lastSyncedAt) } } - else -> - _uiState.update { - it.copy(isSyncing = false, syncError = "Sync failed. Please try again.") - } + else -> { + val message = if (cancelRequested) "Sync cancelled." else "Sync failed. Please try again." + _uiState.update { it.copy(isSyncing = false, syncError = message) } + } } } } + /** Cancels an in-flight [syncNow]. No-op if no sync is currently running. */ + fun cancelSync() { + if (!_uiState.value.isSyncing) return + cancelRequested = true + viewModelScope.launch { syncNowUseCase.cancel() } + } + fun clearSyncError() { _uiState.update { it.copy(syncError = null) } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index 768dc9c2..cf56f322 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -130,6 +130,41 @@ class HomeScreenTest { } } + @Test + fun tappingCancelSync_whileSyncing_callsCancelAndShowsCancelledMessage() = runComposeUiTest { + val syncStarted = CompletableDeferred() + val releaseSyncResult = CompletableDeferred() + val fake = + FakeHomeScreenSyncNowUseCase { + syncStarted.complete(Unit) + releaseSyncResult.await() + } + startTestKoin(fake) + val registry = buildAppViewRegistry() + setContent { + CompositionLocalProvider(LocalViewRegistry provides registry) { + MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } + } + } + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sync now").fetchSemanticsNodes().isNotEmpty() + } + onNodeWithText("Sync now").performClick() + waitUntil(timeoutMillis = 5_000L) { syncStarted.isCompleted } + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Cancel sync").fetchSemanticsNodes().isNotEmpty() + } + + onNodeWithText("Cancel sync").performClick() + waitUntil(timeoutMillis = 5_000L) { fake.cancelCalled } + releaseSyncResult.complete(SyncJobStatus.Failed()) + + waitUntil(timeoutMillis = 5_000L) { + onAllNodesWithText("Sync cancelled.").fetchSemanticsNodes().isNotEmpty() + } + } + @Test fun tappingSyncNow_onFailure_showsSnackbarMessage() = runComposeUiTest { startTestKoin(FakeHomeScreenSyncNowUseCase { SyncJobStatus.Failed() }) diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt index 33a3eb36..c4b66273 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt @@ -24,6 +24,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest private class FakeSyncNowUseCase( @@ -124,6 +125,35 @@ class HomeViewModelTest { assertEquals(1, fake.invocationCount) } + @Test + fun cancelSync_whileSyncing_callsUseCaseCancelAndSetsCancelledMessage() = runTest { + val fhirDataStore = newFhirDataStore() + val releaseSyncResult = CompletableDeferred() + val fake = FakeSyncNowUseCase(fhirDataStore) { releaseSyncResult.await() } + val viewModel = HomeViewModel(fake, fhirDataStore) + + val job = viewModel.syncNow() + viewModel.cancelSync() + releaseSyncResult.complete(SyncJobStatus.Failed()) + job?.join() + + assertEquals(1, fake.cancelCount) + val state = viewModel.uiState.value + assertEquals(false, state.isSyncing) + assertEquals("Sync cancelled.", state.syncError) + } + + @Test + fun cancelSync_whileNotSyncing_isNoOp() = runTest { + val fhirDataStore = newFhirDataStore() + val fake = FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Succeeded() } + val viewModel = HomeViewModel(fake, fhirDataStore) + + viewModel.cancelSync() + + assertEquals(0, fake.cancelCount) + } + @Test fun clearSyncError_removesTheErrorMessage() = runTest { val fhirDataStore = newFhirDataStore() From 3576cf995208d19635e5b1ca91b161780c12f59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:08:54 +0300 Subject: [PATCH 45/73] Apply spotless formatting --- .../player/reference/app/OhsPlayerApplication.kt | 11 ++++++----- .../ohs/player/reference/app/auth/AuthViewModel.kt | 6 ++---- .../app/data/repository/FhirRepository.kt | 4 +++- .../app/feature/group/list/GroupListScreen.kt | 2 +- .../player/reference/app/auth/AuthViewModelTest.kt | 3 ++- .../app/feature/sync/InitialSyncViewModelTest.kt | 8 +++++--- .../dev/ohs/player/reference/app/data/sync/Sync.kt | 14 ++++++++++++-- .../app/data/sync/NetworkConnectivity.web.kt | 6 +++--- .../kotlin/dev/ohs/player/reference/app/main.kt | 11 ++++++----- .../ohs/player/reference/app/MainViewController.kt | 11 ++++++----- .../kotlin/dev/ohs/player/reference/app/main.kt | 11 ++++++----- .../ohs/player/reference/app/AppAuthGatingTest.kt | 12 +++++++++--- .../reference/app/auth/AuthViewModelLogoutTest.kt | 4 +--- .../player/reference/app/data/di/AuthModuleTest.kt | 6 ++++-- .../ohs/player/reference/app/data/sync/SyncTest.kt | 8 +++++--- .../reference/app/feature/home/HomeScreenTest.kt | 9 ++++----- .../app/feature/home/HomeViewModelTest.kt | 1 + .../app/feature/sync/InitialSyncScreenTest.kt | 4 +++- 18 files changed, 79 insertions(+), 52 deletions(-) diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt index 4028a488..d9f33420 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt @@ -41,11 +41,12 @@ class OhsPlayerApplication : Application() { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, - networkConfiguration = NetworkConfiguration( - connectionTimeOut = SYNC_TIMEOUT_DURATION, - readTimeOut = SYNC_TIMEOUT_DURATION, - writeTimeOut = SYNC_TIMEOUT_DURATION, - ), + networkConfiguration = + NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt index 3a79bbc7..c257d595 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt @@ -29,10 +29,8 @@ import kotlinx.coroutines.launch * [AuthorizationLauncher] is platform UI, so it's passed in from the composable rather than * constructed here. */ -internal class AuthViewModel( - private val service: AuthService, - private val fhirEngine: FhirEngine, -) : ViewModel() { +internal class AuthViewModel(private val service: AuthService, private val fhirEngine: FhirEngine) : + ViewModel() { private val _state = MutableStateFlow(AuthState.Loading) val state: StateFlow = _state.asStateFlow() diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt index 75695bce..55f691da 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt @@ -38,6 +38,8 @@ interface FhirRepository { suspend fun all(resourceType: String): List - /** True once any `Patient` or `Group` has been persisted locally. Drives the initial sync gate. */ + /** + * True once any `Patient` or `Group` has been persisted locally. Drives the initial sync gate. + */ suspend fun hasAnyData(): Boolean } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt index b28dd692..9eccda43 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt @@ -55,7 +55,7 @@ fun GroupListScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Un FloatingActionButton(onClick = onDataCaptureClick) { Icon(Icons.Filled.Add, contentDescription = "Register household") } - }, + } ) { padding -> Box(modifier = Modifier.fillMaxSize().padding(padding)) { ListScaffold( diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt index cfd68428..05a8bea6 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt @@ -84,7 +84,8 @@ class AuthViewModelTest { override suspend fun create(vararg resource: Resource): List = error("not used in this test") - override suspend fun get(type: ResourceType, id: String): Resource = error("not used in this test") + override suspend fun get(type: ResourceType, id: String): Resource = + error("not used in this test") override suspend fun update(vararg resource: Resource) = error("not used in this test") diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt index 1977acab..1106a23f 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt @@ -28,7 +28,8 @@ import kotlinx.serialization.json.Json class InitialSyncViewModelTest { - private class FakeSyncNowUseCase(private val result: suspend () -> SyncJobStatus) : SyncNowUseCase { + private class FakeSyncNowUseCase(private val result: suspend () -> SyncJobStatus) : + SyncNowUseCase { var invocationCount = 0 private set @@ -102,8 +103,9 @@ class InitialSyncViewModelTest { fun retry_afterFailure_syncsAgain() = runTest { val repository = InMemorySampleFhirRepository() var shouldFail = true - val syncNowUseCase = - FakeSyncNowUseCase { if (shouldFail) SyncJobStatus.Failed() else SyncJobStatus.Succeeded() } + val syncNowUseCase = FakeSyncNowUseCase { + if (shouldFail) SyncJobStatus.Failed() else SyncJobStatus.Succeeded() + } val periodicSyncUseCase = FakePeriodicSyncUseCase() val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) viewModel.start().join() diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt index fb054515..10269c1e 100644 --- a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt @@ -159,13 +159,23 @@ internal object Sync { repeatInterval: Duration, retryConfiguration: RetryConfiguration?, ) { - mutex.withLock { activePeriodicJobs[uniqueWorkName] }?.takeIf { it.isActive }?.let { return } + mutex + .withLock { activePeriodicJobs[uniqueWorkName] } + ?.takeIf { it.isActive } + ?.let { + return + } val job = scope.launch { while (true) { if (isNetworkConnected()) { - runAttemptsWithRetry(taskFactory, uniqueWorkName, retryConfiguration, syncTimeout = null) {} + runAttemptsWithRetry( + taskFactory, + uniqueWorkName, + retryConfiguration, + syncTimeout = null, + ) {} } else { Logger.d { "Periodic sync cycle skipped for $uniqueWorkName — offline" } } diff --git a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt index 7ea030c5..bdb14115 100644 --- a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt +++ b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/data/sync/NetworkConnectivity.web.kt @@ -18,8 +18,8 @@ package dev.ohs.player.reference.app.data.sync import kotlinx.browser.window /** - * `navigator.onLine` is a best-effort browser signal (reliably `false` when definitely offline, - * but can still report `true` without real connectivity) — good enough to skip an obviously - * doomed periodic sync cycle. + * `navigator.onLine` is a best-effort browser signal (reliably `false` when definitely offline, but + * can still report `true` without real connectivity) — good enough to skip an obviously doomed + * periodic sync cycle. */ internal actual fun isNetworkConnected(): Boolean = window.navigator.onLine diff --git a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt index d0316035..d86ff8c6 100644 --- a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -40,11 +40,12 @@ fun main() { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, - networkConfiguration = NetworkConfiguration( - connectionTimeOut = SYNC_TIMEOUT_DURATION, - readTimeOut = SYNC_TIMEOUT_DURATION, - writeTimeOut = SYNC_TIMEOUT_DURATION, - ), + networkConfiguration = + NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ) diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt index 000d1691..b7e05de8 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt @@ -38,11 +38,12 @@ fun MainViewController() = run { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, - networkConfiguration = NetworkConfiguration( - connectionTimeOut = SYNC_TIMEOUT_DURATION, - readTimeOut = SYNC_TIMEOUT_DURATION, - writeTimeOut = SYNC_TIMEOUT_DURATION, - ), + networkConfiguration = + NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ) diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index 6a553a42..e5910aef 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -43,11 +43,12 @@ fun main() = application { serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, - networkConfiguration = NetworkConfiguration( - connectionTimeOut = SYNC_TIMEOUT_DURATION, - readTimeOut = SYNC_TIMEOUT_DURATION, - writeTimeOut = SYNC_TIMEOUT_DURATION, - ), + networkConfiguration = + NetworkConfiguration( + connectionTimeOut = SYNC_TIMEOUT_DURATION, + readTimeOut = SYNC_TIMEOUT_DURATION, + writeTimeOut = SYNC_TIMEOUT_DURATION, + ), httpLogger = HttpLogger(level = HttpLogger.Level.HEADERS), authenticator = FhirBearerAuthenticator, ), diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt index d3cb1a46..b6981961 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt @@ -107,9 +107,11 @@ private class InMemorySessionStore(initial: Session? = null) : SessionStore { /** Satisfies AuthViewModel's constructor; no test in this file calls logout(). */ private class NoOpFhirEngine : FhirEngine { - override suspend fun create(vararg resource: Resource): List = error("not used in this test") + override suspend fun create(vararg resource: Resource): List = + error("not used in this test") - override suspend fun get(type: ResourceType, id: String): Resource = error("not used in this test") + override suspend fun get(type: ResourceType, id: String): Resource = + error("not used in this test") override suspend fun update(vararg resource: Resource) = error("not used in this test") @@ -229,7 +231,11 @@ class AppAuthGatingTest { val repository = InMemorySampleFhirRepository() runBlocking { repository.upsert(testPatient()) } val syncNowUseCase = FakeAppSyncNowUseCase() - startTestKoin(session = testSession(), fhirRepository = repository, syncNowUseCase = syncNowUseCase) + startTestKoin( + session = testSession(), + fhirRepository = repository, + syncNowUseCase = syncNowUseCase, + ) setContent { App() } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt index 07904232..d0bc2ea7 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt @@ -98,9 +98,7 @@ class AuthViewModelLogoutTest { } } val client = - HttpClient(engine) { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } - } + HttpClient(engine) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt index 5a1f1762..6ff84661 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt @@ -40,9 +40,11 @@ import org.koin.test.inject /** Satisfies AuthViewModel's constructor; this test never calls logout(). */ private class NoOpFhirEngine : FhirEngine { - override suspend fun create(vararg resource: Resource): List = error("not used in this test") + override suspend fun create(vararg resource: Resource): List = + error("not used in this test") - override suspend fun get(type: ResourceType, id: String): Resource = error("not used in this test") + override suspend fun get(type: ResourceType, id: String): Resource = + error("not used in this test") override suspend fun update(vararg resource: Resource) = error("not used in this test") diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt index df1b2d25..b114ebf0 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/sync/SyncTest.kt @@ -43,8 +43,8 @@ import kotlinx.coroutines.test.runTest /** * A no-op [FhirSyncTask] whose download step optionally blocks on [gate] before completing empty * (no requests, no local changes) — real enough for [dev.ohs.fhir.engine.sync.runSync] to reach - * [dev.ohs.fhir.engine.sync.SyncJobStatus.Succeeded] without ever touching the network. Matches - * the shape kotlin-fhir-engine's own `DownloaderImplTest.TestDownloadWorkManager` uses. + * [dev.ohs.fhir.engine.sync.SyncJobStatus.Succeeded] without ever touching the network. Matches the + * shape kotlin-fhir-engine's own `DownloaderImplTest.TestDownloadWorkManager` uses. */ private class TestFhirSyncTask(private val gate: CompletableDeferred? = null) : FhirSyncTask { override fun getFhirEngine(): FhirEngine = FhirEngineProvider.getInstance() @@ -85,7 +85,9 @@ class SyncTest { // between tests instead of re-initializing. if (FhirEngineProvider.isNotInitialized()) { FhirEngineProvider.init( - FhirEngineConfiguration(storageDirectory = Files.createTempDirectory("sync-test").toString()) + FhirEngineConfiguration( + storageDirectory = Files.createTempDirectory("sync-test").toString() + ) ) } FhirEngineProvider.getInstance().clearDatabase() diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index cf56f322..bcf248d3 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -134,11 +134,10 @@ class HomeScreenTest { fun tappingCancelSync_whileSyncing_callsCancelAndShowsCancelledMessage() = runComposeUiTest { val syncStarted = CompletableDeferred() val releaseSyncResult = CompletableDeferred() - val fake = - FakeHomeScreenSyncNowUseCase { - syncStarted.complete(Unit) - releaseSyncResult.await() - } + val fake = FakeHomeScreenSyncNowUseCase { + syncStarted.complete(Unit) + releaseSyncResult.await() + } startTestKoin(fake) val registry = buildAppViewRegistry() setContent { diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt index c4b66273..975f91df 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt @@ -33,6 +33,7 @@ private class FakeSyncNowUseCase( ) : SyncNowUseCase { var invocationCount = 0 private set + var cancelCount = 0 private set diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt index e406828e..c8bb8032 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt @@ -36,7 +36,9 @@ class InitialSyncScreenTest { } assertTrue( - onAllNodesWithText("Setting up your data", substring = true).fetchSemanticsNodes().isNotEmpty() + onAllNodesWithText("Setting up your data", substring = true) + .fetchSemanticsNodes() + .isNotEmpty() ) } From f6503cfb62d6ddb31a9fed94371e1a9e03329be4 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:16:08 +0300 Subject: [PATCH 46/73] Fix sqlite-wasm worker request validation The two request guards used && where || is required, so a message with a null `data` bypassed validation and then dereferenced null. Correct both guards, and document the worker's origin (the @sqlite.org/sqlite-wasm package) and why it must run off the main thread for OPFS. --- .../src/webMain/npm/sqlite-wasm-worker/worker.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js b/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js index d8c51325..55edfd68 100644 --- a/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js +++ b/ohs-player-reference-app/src/webMain/npm/sqlite-wasm-worker/worker.js @@ -1,3 +1,11 @@ +// Web Worker backing the wasmJs SQLite driver for the browser build. It wraps the official +// `@sqlite.org/sqlite-wasm` package (see package.json) and exposes an open/prepare/step/close +// message protocol that the Kotlin/Wasm side drives over `postMessage`. +// +// This must run in a dedicated Worker, not the main thread: persistence uses SQLite's OPFS VFS +// (`sqlite3.oo1.OpfsDb`), which relies on synchronous OPFS access handles that the browser only +// exposes off the main thread. The protocol below is adapted from the worker examples shipped +// with `@sqlite.org/sqlite-wasm`. import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; let sqlite3 = null; @@ -117,13 +125,13 @@ const commandMap = { function handleMessage(e) { const requestMsg = e.data; console.log("handleMessage: " + JSON.stringify(requestMsg)); - if (!Object.hasOwn(requestMsg, 'data') && requestMsg.data == null) { + if (!Object.hasOwn(requestMsg, 'data') || requestMsg.data == null) { postMessage( {'id': requestMsg.id, 'error': "Invalid request, missing 'data'."} ); return; } - if (!Object.hasOwn(requestMsg.data, 'cmd') && requestMsg.data.cmd == null) { + if (!Object.hasOwn(requestMsg.data, 'cmd') || requestMsg.data.cmd == null) { postMessage( {'id': requestMsg.id, 'error': "Invalid request, missing 'cmd'."} ); From cc42874a1403b5c42d81c1529cd6b4a44bf12558 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:16:22 +0300 Subject: [PATCH 47/73] Document identity-provider configuration in the README Explain that the client is OIDC-discovery based and therefore provider agnostic: switching providers is a local.properties change, not a code change. Add a config-key reference table, issuer examples for Keycloak, Okta, and Zitadel, and the per-platform redirect URIs to register. --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index 6464c889..35b37343 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,51 @@ Code generation is part of compilation. The `ig-codegen` Gradle plugin runs its For iOS, open [`iosApp/`](./iosApp) in Xcode and run, or use the run-configuration widget in a Kotlin Multiplatform IDE. +## Identity provider (OIDC) configuration + +Sign-in is provider-agnostic. The client speaks standard OpenID Connect and resolves every endpoint — authorization, token, userinfo, and end-session — at runtime from the provider's discovery document at `{issuer}/.well-known/openid-configuration`. Nothing is hardcoded per provider, so switching from one OAuth2/OIDC provider to another is a configuration change, not a code change: point `OAUTH_ISSUER` at the new provider (and update the client id, scopes, and redirect registration to match). See [`OidcAuthApi`](./ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OidcAuthApi.kt) and [`OAuthConfig`](./ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt). + +The flow is Authorization Code with PKCE and a `state` check. The provider must therefore expose the client as a **public client** (no client secret) that permits PKCE, and the app's redirect URIs must be registered as allowed redirects. Any standards-compliant OIDC provider — Keycloak, Okta, Zitadel, and others — works under these constraints. + +### Configure a provider + +1. Register a public (PKCE) client at your provider and note its client id. +2. Register the per-platform redirect URIs below as allowed redirect URIs. +3. Copy `local.properties.sample` to `local.properties` and fill in the keys below. The file is git-ignored; CI may override any key with an environment variable of the same name. + +Configuration is read at build time by the `generateAuthConfig` task and baked into `GeneratedAuthConfig`, which backs `OAuthConfig.Default`. + +| Key | Meaning | Example | +| --- | --- | --- | +| `OAUTH_ISSUER` | OIDC issuer; discovery is performed against `{issuer}/.well-known/openid-configuration` | `https://keycloak.example.org/realms/ohs-player` | +| `OAUTH_CLIENT_ID` | Public client id (PKCE, no secret) | `ohs-player-reference-app` | +| `OAUTH_SCOPES` | Space-separated scopes; `offline_access` yields a refresh token | `openid profile email offline_access` | +| `OAUTH_REDIRECT_SCHEME` | Custom URI scheme for the mobile deep-link redirect | `dev.ohs.player.reference.app` | +| `OAUTH_REDIRECT_HOST` | Host component of the mobile redirect | `auth` | +| `OAUTH_DESKTOP_REDIRECT_PORT` | Localhost loopback port for the desktop (JVM) redirect | `8765` | +| `OAUTH_WEB_REDIRECT_URL` | Full-page redirect URL for the web (JS/Wasm) build | `http://localhost:8080/callback` | +| `FHIR_BASE_URL` | Base URL of the FHIR server; requests carry the session Bearer token | `https://hapi.fhir.org/baseR4` | + +### Issuer examples + +The issuer is the only value that identifies the provider. Note that for Keycloak the realm is part of the issuer. + +| Provider | `OAUTH_ISSUER` | +| --- | --- | +| Keycloak | `https://host/realms/` | +| Okta | `https://.okta.com` (or a custom authorization server, `https://.okta.com/oauth2/`) | +| Zitadel | `https://.zitadel.cloud` | + +### Redirect URIs to register + +Each platform completes the authorization redirect differently, so register all of the ones you build for: + +| Platform | Redirect URI | Derived from | +| --- | --- | --- | +| Android / iOS | `{OAUTH_REDIRECT_SCHEME}://{OAUTH_REDIRECT_HOST}` | e.g. `dev.ohs.player.reference.app://auth` | +| Desktop (JVM) | `http://127.0.0.1:{OAUTH_DESKTOP_REDIRECT_PORT}/callback` (also register the `http://localhost:...` form) | loopback port | +| Web (JS/Wasm) | `OAUTH_WEB_REDIRECT_URL` | e.g. `http://localhost:8080/callback` | + ## From FHIR data to view state A screen never consumes a raw FHIR resource. It consumes a typed *view-state* — a flat, serializable data class containing exactly the fields the screen needs. View-state is produced by a configuration-driven pipeline: From 9d7ecd47438b7f0277985d8d730fb7412291942f Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:16:22 +0300 Subject: [PATCH 48/73] Move the OAuthConfig default note into its class KDoc Per review, fold the property comment on `Default` into the class-level documentation instead of leaving it inline. --- .../kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt index 50390491..12386b43 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/OAuthConfig.kt @@ -21,7 +21,8 @@ package dev.ohs.player.reference.app.auth * Only the [issuer] (plus client id and scopes) is configured; the concrete endpoints are resolved * at runtime via OIDC discovery ([OidcEndpoints]) from `{issuer}/.well-known/openid-configuration`. * The redirect URI is intentionally NOT part of this object — it is platform specific and provided - * by the [AuthorizationLauncher]. + * by the [AuthorizationLauncher]. [Default] is the single instance, baked in from + * `local.properties` via [GeneratedAuthConfig]. */ data class OAuthConfig( /** @@ -38,7 +39,6 @@ data class OAuthConfig( get() = "${issuer.trimEnd('/')}/.well-known/openid-configuration" companion object { - /** The single config baked in from `local.properties`. */ val Default: OAuthConfig = OAuthConfig( issuer = GeneratedAuthConfig.ISSUER, From 244cef4585fa10bfe02c784130f3888e69187fc5 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:18:02 +0300 Subject: [PATCH 49/73] Unify desktop storage under ~/.player-reference The FHIR database, KSafe, and the sync-timestamp DataStore each derived their own path under ~/.ohs-player-reference-app. Route all three through a single desktopStorageDirectory rooted at ~/.player-reference, per review. --- .../player/reference/app/DesktopStorage.kt | 25 +++++++++++++++++++ .../player/reference/app/auth/Platform.jvm.kt | 5 ++-- .../data/sync/SyncTimestampDataStore.jvm.kt | 5 ++-- 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/DesktopStorage.kt diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/DesktopStorage.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/DesktopStorage.kt new file mode 100644 index 00000000..7e874ef8 --- /dev/null +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/DesktopStorage.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app + +import java.io.File + +/** + * One desktop storage root: the FHIR database, KSafe, and the sync-timestamp DataStore all live + * here. + */ +internal val desktopStorageDirectory: File = + File(System.getProperty("user.home").orEmpty().ifBlank { "." }, ".player-reference") diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt index 74524488..29d577b6 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt @@ -18,16 +18,15 @@ package dev.ohs.player.reference.app.auth import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import com.sun.net.httpserver.HttpServer +import dev.ohs.player.reference.app.desktopStorageDirectory import eu.anifantakis.lib.ksafe.KSafe import java.awt.Desktop -import java.io.File import java.net.InetSocketAddress import java.net.URI import java.security.SecureRandom import kotlinx.coroutines.suspendCancellableCoroutine -internal actual fun createKSafe(): KSafe = - KSafe(baseDir = File(System.getProperty("user.home"), ".ohs-player-reference-app")) +internal actual fun createKSafe(): KSafe = KSafe(baseDir = desktopStorageDirectory) internal actual fun secureRandomBytes(size: Int): ByteArray = ByteArray(size).also { SecureRandom().nextBytes(it) } diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt index 2d8f9e60..2d8e1767 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncTimestampDataStore.jvm.kt @@ -18,12 +18,11 @@ package dev.ohs.player.reference.app.data.sync import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import dev.ohs.fhir.engine.sync.createDataStore +import dev.ohs.player.reference.app.desktopStorageDirectory import java.io.File private val dataStore: DataStore by lazy { - val userHome = System.getProperty("user.home").orEmpty().ifBlank { "." } - val storageDirectory = File(userHome, ".ohs-player-reference-app") - createDataStore { File(storageDirectory, SYNC_TIMESTAMP_DATASTORE_FILE_NAME).absolutePath } + createDataStore { File(desktopStorageDirectory, SYNC_TIMESTAMP_DATASTORE_FILE_NAME).absolutePath } } internal actual fun createSyncTimestampDataStore(): DataStore = dataStore From 4166fe5fc2352506127b4feaa7621fc473408ac2 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:19:21 +0300 Subject: [PATCH 50/73] Replace the sync use cases with a single SyncManager Collapse the per-verb SyncNowUseCase and PeriodicSyncUseCase interfaces and their six platform implementations into one injected SyncManager (syncNow / cancelSyncNow / startPeriodicSync / cancelPeriodicSync) with one implementation per platform. This keeps the ViewModels unit-testable against a fake while dropping the domain-use-case ceremony flagged in review. Drop two low-value tests that only exercised the old wiring. --- .../sync/WorkManagerPeriodicSyncUseCase.kt | 42 --- ...owUseCase.kt => WorkManagerSyncManager.kt} | 29 +- .../app/data/sync/PeriodicSyncUseCase.kt | 35 --- .../reference/app/data/sync/SyncManager.kt | 46 ++++ .../reference/app/data/sync/SyncNowUseCase.kt | 35 --- .../app/data/sync/FakeSyncManager.kt | 52 ++++ .../sync/ForegroundPeriodicSyncUseCase.kt | 37 --- ...NowUseCase.kt => ForegroundSyncManager.kt} | 23 +- .../dev/ohs/player/reference/app/main.kt | 9 +- .../reference/app/MainViewController.kt | 11 +- .../app/data/sync/IosPeriodicSyncUseCase.kt | 46 ---- ...IosSyncNowUseCase.kt => IosSyncManager.kt} | 42 ++- .../dev/ohs/player/reference/app/main.kt | 14 +- .../player/reference/app/AppAuthGatingTest.kt | 247 ------------------ .../reference/app/data/di/AuthModuleTest.kt | 101 ------- .../app/feature/home/HomeScreenTest.kt | 29 +- 16 files changed, 192 insertions(+), 606 deletions(-) delete mode 100644 ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt rename ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/{WorkManagerSyncNowUseCase.kt => WorkManagerSyncManager.kt} (55%) delete mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncManager.kt delete mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt create mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/FakeSyncManager.kt delete mode 100644 ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt rename ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/{ForegroundSyncNowUseCase.kt => ForegroundSyncManager.kt} (65%) delete mode 100644 ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt rename ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/{IosSyncNowUseCase.kt => IosSyncManager.kt} (69%) delete mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt delete mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt deleted file mode 100644 index 8eece71c..00000000 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerPeriodicSyncUseCase.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.data.sync - -import android.content.Context -import dev.ohs.fhir.engine.sync.PeriodicSyncConfiguration -import dev.ohs.fhir.engine.sync.RepeatInterval -import dev.ohs.fhir.engine.sync.Sync -import kotlin.time.Duration.Companion.minutes - -/** - * Schedules this app's sync as periodic WorkManager work, reusing [AppFhirSyncWorker] (the same - * Worker class [WorkManagerSyncNowUseCase] uses for one-time sync). [PeriodicSyncConfiguration]'s - * default `SyncConstraints` already requires `NetworkType.CONNECTED`, so WorkManager itself defers - * runs until the device is online — no separate connectivity check needed here. - */ -class WorkManagerPeriodicSyncUseCase(private val context: Context) : PeriodicSyncUseCase { - override suspend fun start() { - Sync.periodicSync( - context, - periodicSyncConfiguration = - PeriodicSyncConfiguration(repeat = RepeatInterval(interval = 15.minutes)), - ) - } - - override suspend fun cancel() { - Sync.cancelPeriodicSync(context) - } -} diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncManager.kt similarity index 55% rename from ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt rename to ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncManager.kt index d9c945fa..63a8934e 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncNowUseCase.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/WorkManagerSyncManager.kt @@ -17,17 +17,22 @@ package dev.ohs.player.reference.app.data.sync import android.content.Context import dev.ohs.fhir.engine.sync.CurrentSyncJobStatus +import dev.ohs.fhir.engine.sync.PeriodicSyncConfiguration +import dev.ohs.fhir.engine.sync.RepeatInterval import dev.ohs.fhir.engine.sync.Sync import dev.ohs.fhir.engine.sync.SyncJobStatus +import kotlin.time.Duration.Companion.minutes import kotlinx.coroutines.flow.first /** - * Runs a one-time sync through WorkManager (via [AppFhirSyncWorker]) rather than calling - * [dev.ohs.fhir.engine.sync.runSync] in-process, so the sync survives the triggering screen being - * backgrounded or the process dying mid-sync. + * Android [SyncManager]: runs sync through WorkManager (via [AppFhirSyncWorker]) rather than in + * process, so both one-time and periodic sync survive the triggering screen being backgrounded or + * the process dying mid-sync. [PeriodicSyncConfiguration]'s default `SyncConstraints` already + * requires `NetworkType.CONNECTED`, so WorkManager itself defers periodic runs until the device is + * online. */ -class WorkManagerSyncNowUseCase(private val context: Context) : SyncNowUseCase { - override suspend fun invoke(): SyncJobStatus { +class WorkManagerSyncManager(private val context: Context) : SyncManager { + override suspend fun syncNow(): SyncJobStatus { val terminalStatus = Sync.oneTimeSync(context).first { it is CurrentSyncJobStatus.Succeeded || @@ -40,7 +45,19 @@ class WorkManagerSyncNowUseCase(private val context: Context) : SyncNowUseCase { } } - override suspend fun cancel() { + override suspend fun cancelSyncNow() { Sync.cancelOneTimeSync(context) } + + override suspend fun startPeriodicSync() { + Sync.periodicSync( + context, + periodicSyncConfiguration = + PeriodicSyncConfiguration(repeat = RepeatInterval(interval = 15.minutes)), + ) + } + + override suspend fun cancelPeriodicSync() { + Sync.cancelPeriodicSync(context) + } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt deleted file mode 100644 index 39c50cf4..00000000 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/PeriodicSyncUseCase.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.data.sync - -/** - * Schedules this app's sync to run every 15 minutes while the device is online. Each platform - * supplies its own implementation — see `WorkManagerPeriodicSyncUseCase` (Android), - * `ForegroundPeriodicSyncUseCase` (JVM/web), and `IosPeriodicSyncUseCase` (iOS). - */ -interface PeriodicSyncUseCase { - /** - * Safe to call repeatedly — never schedules a duplicate/competing cycle. What "safe to call - * repeatedly" means differs per platform: Android's `ExistingPeriodicWorkPolicy.KEEP` (default) - * literally no-ops if already scheduled; iOS's scheduler cancels any pending request before - * submitting a fresh one (a refresh, not a no-op); JVM/web's own implementation checks an - * "already running" job and no-ops, for consistency with the other two. - */ - suspend fun start() - - /** Cancels the periodic sync. No-op if none is scheduled. */ - suspend fun cancel() -} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncManager.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncManager.kt new file mode 100644 index 00000000..68f9d7ad --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncManager.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.engine.sync.SyncJobStatus + +/** + * This app's single sync seam. Scheduling is inherently platform-specific — WorkManager on Android, + * `BGTaskScheduler` on iOS, an in-process foreground loop on JVM/web — so each platform supplies + * its own implementation (`WorkManagerSyncManager`, `ForegroundSyncManager`, `IosSyncManager`). + * Routing every sync operation through one interface also keeps the ViewModels unit-testable + * against a fake: the engine's `runSync` is a top-level extension over a global singleton, so there + * is no seam to fake without it. + */ +interface SyncManager { + /** Runs a one-time sync and returns its terminal result. */ + suspend fun syncNow(): SyncJobStatus + + /** Cancels an in-flight [syncNow]. No-op if no one-time sync is running. */ + suspend fun cancelSyncNow() + + /** + * Schedules the recurring background sync (every 15 minutes while online). Safe to call + * repeatedly — never schedules a duplicate/competing cycle. What that means differs per platform: + * Android's `ExistingPeriodicWorkPolicy.KEEP` no-ops if already scheduled; iOS cancels any + * pending request before submitting a fresh one; JVM/web checks an already-running job and + * no-ops. + */ + suspend fun startPeriodicSync() + + /** Cancels the periodic sync. No-op if none is scheduled. */ + suspend fun cancelPeriodicSync() +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt deleted file mode 100644 index 2919b9a6..00000000 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/SyncNowUseCase.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.data.sync - -import dev.ohs.fhir.engine.sync.SyncJobStatus -import dev.ohs.fhir.engine.sync.runSync - -/** - * Triggers a one-time sync and returns its terminal result. A dedicated interface (rather than - * calling [runSync] directly) so [dev.ohs.player.reference.app.feature.home.HomeViewModel] can be - * unit-tested against a fake — [runSync] is a top-level extension function that reaches into the - * library's global `FhirEngineProvider` singleton, so no `FhirSyncTask` instance, real or fake, - * makes it independently testable. Each platform supplies its own implementation — see - * `AppFhirSyncWorker`/`WorkManagerSyncNowUseCase` (Android), `ForegroundSyncNowUseCase` (JVM/web), - * and `IosSyncNowUseCase` (iOS). - */ -interface SyncNowUseCase { - suspend fun invoke(): SyncJobStatus - - /** Cancels an in-flight [invoke] call. No-op if no sync is currently running. */ - suspend fun cancel() -} diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/FakeSyncManager.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/FakeSyncManager.kt new file mode 100644 index 00000000..5c777ad2 --- /dev/null +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/sync/FakeSyncManager.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import dev.ohs.fhir.engine.sync.SyncJobStatus + +/** In-memory [SyncManager] that counts calls, so ViewModel tests can assert sync behavior. */ +internal class FakeSyncManager( + private val syncResult: suspend () -> SyncJobStatus = { SyncJobStatus.Succeeded() } +) : SyncManager { + var syncNowCount = 0 + private set + + var cancelSyncNowCount = 0 + private set + + var startPeriodicCount = 0 + private set + + var cancelPeriodicCount = 0 + private set + + override suspend fun syncNow(): SyncJobStatus { + syncNowCount++ + return syncResult() + } + + override suspend fun cancelSyncNow() { + cancelSyncNowCount++ + } + + override suspend fun startPeriodicSync() { + startPeriodicCount++ + } + + override suspend fun cancelPeriodicSync() { + cancelPeriodicCount++ + } +} diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt deleted file mode 100644 index 26ed9bda..00000000 --- a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundPeriodicSyncUseCase.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.data.sync - -import dev.ohs.fhir.engine.FhirEngineProvider -import kotlin.time.Duration.Companion.minutes - -/** - * Schedules this app's sync through the shared foreground [Sync] scheduler — Desktop and web only - * sync while their host process is alive (see [Sync]'s docs for why), gated each cycle on - * [isNetworkConnected]. - */ -class ForegroundPeriodicSyncUseCase : PeriodicSyncUseCase { - override suspend fun start() { - Sync.periodicSync( - taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }, - repeatInterval = 15.minutes, - ) - } - - override suspend fun cancel() { - Sync.cancelPeriodicSync() - } -} diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncManager.kt similarity index 65% rename from ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt rename to ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncManager.kt index d2e71127..aaf69b27 100644 --- a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncNowUseCase.kt +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/ForegroundSyncManager.kt @@ -18,14 +18,16 @@ package dev.ohs.player.reference.app.data.sync import dev.ohs.fhir.engine.FhirEngineProvider import dev.ohs.fhir.engine.sync.CurrentSyncJobStatus import dev.ohs.fhir.engine.sync.SyncJobStatus +import kotlin.time.Duration.Companion.minutes import kotlinx.coroutines.flow.first /** - * Runs a one-time sync through the shared foreground [Sync] scheduler — Desktop and web only sync - * while their host process is alive (see [Sync]'s docs for why). + * JVM/web [SyncManager]: schedules sync through the shared foreground [Sync] scheduler. Desktop and + * web only sync while their host process is alive (see [Sync]'s docs for why), with each periodic + * cycle gated on [isNetworkConnected]. */ -class ForegroundSyncNowUseCase : SyncNowUseCase { - override suspend fun invoke(): SyncJobStatus { +class ForegroundSyncManager : SyncManager { + override suspend fun syncNow(): SyncJobStatus { val terminalStatus = Sync.oneTimeSync(taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }).first { it is CurrentSyncJobStatus.Succeeded || @@ -38,7 +40,18 @@ class ForegroundSyncNowUseCase : SyncNowUseCase { } } - override suspend fun cancel() { + override suspend fun cancelSyncNow() { Sync.cancelOneTimeSync() } + + override suspend fun startPeriodicSync() { + Sync.periodicSync( + taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }, + repeatInterval = 15.minutes, + ) + } + + override suspend fun cancelPeriodicSync() { + Sync.cancelPeriodicSync() + } } diff --git a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt index d86ff8c6..b773edaf 100644 --- a/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/foregroundSyncWebMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -26,11 +26,9 @@ import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin -import dev.ohs.player.reference.app.data.sync.ForegroundPeriodicSyncUseCase -import dev.ohs.player.reference.app.data.sync.ForegroundSyncNowUseCase -import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase +import dev.ohs.player.reference.app.data.sync.ForegroundSyncManager import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SyncManager import org.koin.dsl.module @OptIn(ExperimentalComposeUiApi::class) @@ -54,8 +52,7 @@ fun main() { initKoin( module { single { FhirEngineProvider.getInstance() } - single { ForegroundSyncNowUseCase() } - single { ForegroundPeriodicSyncUseCase() } + single { ForegroundSyncManager() } } ) ComposeViewport { App() } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt index b7e05de8..52d1cf9f 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/MainViewController.kt @@ -25,11 +25,9 @@ import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin -import dev.ohs.player.reference.app.data.sync.IosPeriodicSyncUseCase -import dev.ohs.player.reference.app.data.sync.IosSyncNowUseCase -import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase +import dev.ohs.player.reference.app.data.sync.IosSyncManager import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SyncManager import org.koin.dsl.module fun MainViewController() = run { @@ -51,12 +49,11 @@ fun MainViewController() = run { ) // Constructed eagerly (not inside the Koin lambda, which is lazy) so BGTaskScheduler // registration happens now, during app launch — see IosBgSyncScheduler's docs. - val periodicSyncUseCase = IosPeriodicSyncUseCase() + val syncManager = IosSyncManager() initKoin( module { single { FhirEngineProvider.getInstance() } - single { IosSyncNowUseCase() } - single { periodicSyncUseCase } + single { syncManager } } ) ComposeUIViewController { App() } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt deleted file mode 100644 index d1b554aa..00000000 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosPeriodicSyncUseCase.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.data.sync - -import dev.ohs.fhir.engine.FhirEngineProvider - -/** Must match `BGTaskSchedulerPermittedIdentifiers` in `iosApp/iosApp/Info.plist`. */ -internal const val PERIODIC_SYNC_TASK_IDENTIFIER = "dev.ohs.player.reference.app.sync.periodic" - -/** - * Schedules this app's sync as a `BGProcessingTask` via [IosBgSyncScheduler]. Registration happens - * in the constructor (not lazily) — see [IosBgSyncScheduler]'s docs on why that matters, and - * [dev.ohs.player.reference.app.MainViewController], which constructs this eagerly. - */ -class IosPeriodicSyncUseCase : PeriodicSyncUseCase { - private val scheduler = - IosBgSyncScheduler( - taskIdentifier = PERIODIC_SYNC_TASK_IDENTIFIER, - taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }, - ) - - init { - scheduler.register() - } - - override suspend fun start() { - scheduler.schedule() - } - - override suspend fun cancel() { - scheduler.cancel() - } -} diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncManager.kt similarity index 69% rename from ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt rename to ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncManager.kt index e72e37a0..89bf1df1 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncNowUseCase.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncManager.kt @@ -32,16 +32,30 @@ import platform.Foundation.NSNotificationCenter import platform.UIKit.UIApplicationDidEnterBackgroundNotification import platform.UIKit.UIApplicationWillEnterForegroundNotification +/** Must match `BGTaskSchedulerPermittedIdentifiers` in `iosApp/iosApp/Info.plist`. */ +internal const val PERIODIC_SYNC_TASK_IDENTIFIER = "dev.ohs.player.reference.app.sync.periodic" + /** - * Runs a one-time sync on a dedicated [Dispatchers.IO]-backed scope decoupled from the caller, so + * iOS [SyncManager]. Periodic sync runs as a `BGProcessingTask` via [IosBgSyncScheduler]; + * registration happens in the constructor (not lazily) because `BGTaskScheduler` requires it before + * `applicationDidFinishLaunching` returns — see [IosBgSyncScheduler]'s docs and + * [dev.ohs.player.reference.app.MainViewController], which constructs this eagerly. + * + * One-time sync runs on a dedicated [Dispatchers.IO]-backed scope decoupled from the caller, so * backgrounding the triggering screen doesn't cancel an in-flight network sync. Mirrors * kotlin-fhir-engine's engine-app `FhirSyncController.ios.kt`: iOS suspends networking for - * backgrounded apps with no active background task, so an in-progress sync job is cancelled on + * backgrounded apps with no active background task, so an in-progress one-time sync is cancelled on * [UIApplicationDidEnterBackgroundNotification] and relaunched from scratch on - * [UIApplicationWillEnterForegroundNotification], with [invoke] suspending across the relaunch + * [UIApplicationWillEnterForegroundNotification], with [syncNow] suspending across the relaunch * until a terminal result arrives. */ -class IosSyncNowUseCase : SyncNowUseCase { +class IosSyncManager : SyncManager { + private val scheduler = + IosBgSyncScheduler( + taskIdentifier = PERIODIC_SYNC_TASK_IDENTIFIER, + taskFactory = { AppFhirSyncTask(FhirEngineProvider.getInstance()) }, + ) + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private var currentJob: Job? = null @@ -49,6 +63,8 @@ class IosSyncNowUseCase : SyncNowUseCase { private var syncWasRunning = false init { + scheduler.register() + NSNotificationCenter.defaultCenter.addObserverForName( UIApplicationDidEnterBackgroundNotification, null, @@ -57,7 +73,7 @@ class IosSyncNowUseCase : SyncNowUseCase { if (currentJob?.isActive == true) { syncWasRunning = true currentJob?.cancel() - Logger.d { "IosSyncNowUseCase: sync suspended on background" } + Logger.d { "IosSyncManager: sync suspended on background" } } } @@ -69,24 +85,32 @@ class IosSyncNowUseCase : SyncNowUseCase { if (syncWasRunning) { syncWasRunning = false launchSyncJob() - Logger.d { "IosSyncNowUseCase: sync restarted on foreground" } + Logger.d { "IosSyncManager: sync restarted on foreground" } } } } - override suspend fun invoke(): SyncJobStatus { + override suspend fun syncNow(): SyncJobStatus { val statusFlow = MutableSharedFlow(replay = 1) currentStatusFlow = statusFlow launchSyncJob() return statusFlow.first { it is SyncJobStatus.Succeeded || it is SyncJobStatus.Failed } } - override suspend fun cancel() { + override suspend fun cancelSyncNow() { syncWasRunning = false currentJob?.cancel() currentStatusFlow?.emit(SyncJobStatus.Failed()) } + override suspend fun startPeriodicSync() { + scheduler.schedule() + } + + override suspend fun cancelPeriodicSync() { + scheduler.cancel() + } + private fun launchSyncJob() { val statusFlow = currentStatusFlow ?: return currentJob?.cancel() @@ -98,7 +122,7 @@ class IosSyncNowUseCase : SyncNowUseCase { } catch (e: CancellationException) { throw e } catch (e: Exception) { - Logger.e(e) { "IosSyncNowUseCase: one-time sync failed" } + Logger.e(e) { "IosSyncManager: one-time sync failed" } SyncJobStatus.Failed() } statusFlow.emit(result) diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index e5910aef..c3ede0b6 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -26,20 +26,15 @@ import dev.ohs.fhir.engine.sync.remote.HttpLogger import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin -import dev.ohs.player.reference.app.data.sync.ForegroundPeriodicSyncUseCase -import dev.ohs.player.reference.app.data.sync.ForegroundSyncNowUseCase -import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase +import dev.ohs.player.reference.app.data.sync.ForegroundSyncManager import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase -import java.io.File +import dev.ohs.player.reference.app.data.sync.SyncManager import org.koin.dsl.module fun main() = application { - val userHome = System.getProperty("user.home").orEmpty().ifBlank { "." } - val storageDirectory = File(userHome, ".ohs-player-reference-app").absolutePath FhirEngineProvider.init( FhirEngineConfiguration( - storageDirectory = storageDirectory, + storageDirectory = desktopStorageDirectory.absolutePath, serverConfiguration = ServerConfiguration( baseUrl = GeneratedAuthConfig.FHIR_BASE_URL, @@ -57,8 +52,7 @@ fun main() = application { initKoin( module { single { FhirEngineProvider.getInstance() } - single { ForegroundSyncNowUseCase() } - single { ForegroundPeriodicSyncUseCase() } + single { ForegroundSyncManager() } } ) Window(onCloseRequest = ::exitApplication, title = "OHS Player Reference App") { App() } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt deleted file mode 100644 index b6981961..00000000 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/AppAuthGatingTest.kt +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app - -import androidx.compose.ui.test.ExperimentalTestApi -import androidx.compose.ui.test.onAllNodesWithText -import androidx.compose.ui.test.runComposeUiTest -import dev.ohs.fhir.engine.FhirEngine -import dev.ohs.fhir.engine.LocalChange -import dev.ohs.fhir.engine.OffsetDateTime -import dev.ohs.fhir.engine.SearchResult -import dev.ohs.fhir.engine.db.LocalChangeResourceReference -import dev.ohs.fhir.engine.search.Search -import dev.ohs.fhir.engine.sync.ConflictResolver -import dev.ohs.fhir.engine.sync.FhirDataStore -import dev.ohs.fhir.engine.sync.SyncJobStatus -import dev.ohs.fhir.engine.sync.createDataStore -import dev.ohs.fhir.engine.sync.upload.SyncUploadProgress -import dev.ohs.fhir.engine.sync.upload.UploadRequestResult -import dev.ohs.fhir.engine.sync.upload.UploadStrategy -import dev.ohs.fhir.model.r4.Patient -import dev.ohs.fhir.model.r4.Resource -import dev.ohs.fhir.model.r4.terminologies.ResourceType -import dev.ohs.player.reference.app.auth.AuthService -import dev.ohs.player.reference.app.auth.OAuthConfig -import dev.ohs.player.reference.app.auth.OidcAuthApi -import dev.ohs.player.reference.app.auth.PendingAuth -import dev.ohs.player.reference.app.auth.Session -import dev.ohs.player.reference.app.auth.SessionStore -import dev.ohs.player.reference.app.auth.UserInfo -import dev.ohs.player.reference.app.data.di.repositoryModule -import dev.ohs.player.reference.app.data.di.viewModelModule -import dev.ohs.player.reference.app.data.repository.FhirRepository -import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository -import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase -import io.ktor.client.HttpClient -import io.ktor.client.engine.mock.MockEngine -import io.ktor.client.engine.mock.respond -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.http.HttpStatusCode -import io.ktor.serialization.kotlinx.json.json -import java.nio.file.Files -import kotlin.test.AfterTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.runBlocking -import kotlinx.serialization.json.Json -import org.koin.core.context.startKoin -import org.koin.core.context.stopKoin -import org.koin.dsl.module - -private class FakeAppSyncNowUseCase : SyncNowUseCase { - var invocationCount = 0 - private set - - override suspend fun invoke(): SyncJobStatus { - invocationCount++ - return SyncJobStatus.Succeeded() - } - - override suspend fun cancel() {} -} - -private class FakeAppPeriodicSyncUseCase : PeriodicSyncUseCase { - override suspend fun start() {} - - override suspend fun cancel() {} -} - -private class InMemorySessionStore(initial: Session? = null) : SessionStore { - private val _session = MutableStateFlow(initial) - override val session: StateFlow = _session.asStateFlow() - - override suspend fun load(): Session? = _session.value - - override suspend fun save(session: Session) { - _session.value = session - } - - override suspend fun clear() { - _session.value = null - } - - override suspend fun savePending(pending: PendingAuth) = Unit - - override suspend fun takePending(): PendingAuth? = null -} - -/** Satisfies AuthViewModel's constructor; no test in this file calls logout(). */ -private class NoOpFhirEngine : FhirEngine { - override suspend fun create(vararg resource: Resource): List = - error("not used in this test") - - override suspend fun get(type: ResourceType, id: String): Resource = - error("not used in this test") - - override suspend fun update(vararg resource: Resource) = error("not used in this test") - - override suspend fun delete(type: ResourceType, id: String) = error("not used in this test") - - override suspend fun search(search: Search): List> = - error("not used in this test") - - override suspend fun syncUpload( - uploadStrategy: UploadStrategy, - upload: - suspend (List, List) -> Flow, - ): Flow = error("not used in this test") - - override suspend fun syncDownload( - conflictResolver: ConflictResolver, - download: suspend () -> Flow>, - ) = error("not used in this test") - - override suspend fun count(search: Search): Long = error("not used in this test") - - override suspend fun getLastSyncTimeStamp(): OffsetDateTime? = error("not used in this test") - - override suspend fun clearDatabase() = error("not used in this test") - - override suspend fun getLocalChanges(type: ResourceType, id: String): List = - error("not used in this test") - - override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) = - error("not used in this test") - - override suspend fun purge(type: ResourceType, ids: Set, forcePurge: Boolean) = - error("not used in this test") - - override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) = - error("not used in this test") -} - -@OptIn(ExperimentalTestApi::class) -class AppAuthGatingTest { - - private fun newFhirDataStore(): FhirDataStore { - val path = Files.createTempFile("app-auth-gating-test", ".preferences_pb").toString() - return FhirDataStore(createDataStore { path }) - } - - private fun testSession() = - Session( - accessToken = "a", - refreshToken = "r", - idToken = null, - expiresInSeconds = 10_000_000_000L, - obtainedAtEpochSeconds = 0, - user = UserInfo(), - ) - - private fun testPatient(): Patient = - Json { ignoreUnknownKeys = true } - .decodeFromString(Patient.serializer(), """{"resourceType": "Patient", "id": "p1"}""") - - private fun startTestKoin( - session: Session? = null, - fhirRepository: FhirRepository = InMemorySampleFhirRepository(), - syncNowUseCase: SyncNowUseCase = FakeAppSyncNowUseCase(), - ) { - val engine = MockEngine { respond("not found", HttpStatusCode.NotFound) } - val client = - HttpClient(engine) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } - startKoin { - modules( - module { - single { fhirRepository } - single { newFhirDataStore() } - single { syncNowUseCase } - single { FakeAppPeriodicSyncUseCase() } - single { OAuthConfig("https://idp.example.org", "client", "openid") } - single { InMemorySessionStore(initial = session) } - single { OidcAuthApi(get(), client) } - single { AuthService(get(), get(), get()) } - single { NoOpFhirEngine() } - }, - repositoryModule, - viewModelModule, - ) - } - } - - @AfterTest fun tearDown() = stopKoin() - - @Test - fun signedOut_showsLoginScreen() = runComposeUiTest { - startTestKoin() - - setContent { App() } - - waitUntil(timeoutMillis = 5_000L) { - onAllNodesWithText("Sign in", ignoreCase = true).fetchSemanticsNodes().isNotEmpty() - } - assertTrue(onAllNodesWithText("Sign in", ignoreCase = true).fetchSemanticsNodes().isNotEmpty()) - } - - @Test - fun authenticatedWithNoLocalData_runsSyncThenShowsHome() = runComposeUiTest { - val syncNowUseCase = FakeAppSyncNowUseCase() - startTestKoin(session = testSession(), syncNowUseCase = syncNowUseCase) - - setContent { App() } - - waitUntil(timeoutMillis = 5_000L) { - onAllNodesWithText("Sync now", ignoreCase = true).fetchSemanticsNodes().isNotEmpty() - } - assertEquals(1, syncNowUseCase.invocationCount) - } - - @Test - fun authenticatedWithExistingLocalData_skipsSyncAndShowsHomeImmediately() = runComposeUiTest { - val repository = InMemorySampleFhirRepository() - runBlocking { repository.upsert(testPatient()) } - val syncNowUseCase = FakeAppSyncNowUseCase() - startTestKoin( - session = testSession(), - fhirRepository = repository, - syncNowUseCase = syncNowUseCase, - ) - - setContent { App() } - - waitUntil(timeoutMillis = 5_000L) { - onAllNodesWithText("Sync now", ignoreCase = true).fetchSemanticsNodes().isNotEmpty() - } - assertEquals(0, syncNowUseCase.invocationCount) - } -} diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt deleted file mode 100644 index 6ff84661..00000000 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/di/AuthModuleTest.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.data.di - -import dev.ohs.fhir.engine.FhirEngine -import dev.ohs.fhir.engine.LocalChange -import dev.ohs.fhir.engine.OffsetDateTime -import dev.ohs.fhir.engine.SearchResult -import dev.ohs.fhir.engine.db.LocalChangeResourceReference -import dev.ohs.fhir.engine.search.Search -import dev.ohs.fhir.engine.sync.ConflictResolver -import dev.ohs.fhir.engine.sync.upload.SyncUploadProgress -import dev.ohs.fhir.engine.sync.upload.UploadRequestResult -import dev.ohs.fhir.engine.sync.upload.UploadStrategy -import dev.ohs.fhir.model.r4.Resource -import dev.ohs.fhir.model.r4.terminologies.ResourceType -import dev.ohs.player.reference.app.auth.AuthService -import dev.ohs.player.reference.app.auth.AuthViewModel -import kotlin.test.AfterTest -import kotlin.test.Test -import kotlinx.coroutines.flow.Flow -import org.koin.core.context.startKoin -import org.koin.core.context.stopKoin -import org.koin.dsl.module -import org.koin.test.KoinTest -import org.koin.test.inject - -/** Satisfies AuthViewModel's constructor; this test never calls logout(). */ -private class NoOpFhirEngine : FhirEngine { - override suspend fun create(vararg resource: Resource): List = - error("not used in this test") - - override suspend fun get(type: ResourceType, id: String): Resource = - error("not used in this test") - - override suspend fun update(vararg resource: Resource) = error("not used in this test") - - override suspend fun delete(type: ResourceType, id: String) = error("not used in this test") - - override suspend fun search(search: Search): List> = - error("not used in this test") - - override suspend fun syncUpload( - uploadStrategy: UploadStrategy, - upload: - suspend (List, List) -> Flow, - ): Flow = error("not used in this test") - - override suspend fun syncDownload( - conflictResolver: ConflictResolver, - download: suspend () -> Flow>, - ) = error("not used in this test") - - override suspend fun count(search: Search): Long = error("not used in this test") - - override suspend fun getLastSyncTimeStamp(): OffsetDateTime? = error("not used in this test") - - override suspend fun clearDatabase() = error("not used in this test") - - override suspend fun getLocalChanges(type: ResourceType, id: String): List = - error("not used in this test") - - override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) = - error("not used in this test") - - override suspend fun purge(type: ResourceType, ids: Set, forcePurge: Boolean) = - error("not used in this test") - - override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) = - error("not used in this test") -} - -class AuthModuleTest : KoinTest { - - private val authService by inject() - - @AfterTest fun tearDown() = stopKoin() - - @Test - fun authModule_resolvesAuthServiceAndAuthViewModel() { - startKoin { - modules(module { single { NoOpFhirEngine() } }, authModule, viewModelModule) - } - - authService // resolves without throwing - getKoin().get() - } -} diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index bcf248d3..ffa3ac67 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -32,7 +32,8 @@ import dev.ohs.player.reference.app.data.di.repositoryModule import dev.ohs.player.reference.app.data.di.viewModelModule import dev.ohs.player.reference.app.data.repository.FhirRepository import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.FakeSyncManager +import dev.ohs.player.reference.app.data.sync.SyncManager import java.nio.file.Files import kotlin.test.AfterTest import kotlin.test.Test @@ -42,18 +43,6 @@ import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.dsl.module -private class FakeHomeScreenSyncNowUseCase(private val result: suspend () -> SyncJobStatus) : - SyncNowUseCase { - var cancelCalled = false - private set - - override suspend fun invoke(): SyncJobStatus = result() - - override suspend fun cancel() { - cancelCalled = true - } -} - @OptIn(ExperimentalTestApi::class) class HomeScreenTest { @@ -62,13 +51,13 @@ class HomeScreenTest { return FhirDataStore(createDataStore { path }) } - private fun startTestKoin(syncNowUseCase: SyncNowUseCase) { + private fun startTestKoin(syncManager: SyncManager) { startKoin { modules( module { single { InMemorySampleFhirRepository() } single { newFhirDataStore() } - single { syncNowUseCase } + single { syncManager } }, repositoryModule, viewModelModule, @@ -80,7 +69,7 @@ class HomeScreenTest { @Test fun homeScreen_defaultsToHouseholdsContentWithDrawerSectionsVisible() = runComposeUiTest { - startTestKoin(FakeHomeScreenSyncNowUseCase { SyncJobStatus.Succeeded() }) + startTestKoin(FakeSyncManager { SyncJobStatus.Succeeded() }) val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { @@ -101,7 +90,7 @@ class HomeScreenTest { val syncStarted = CompletableDeferred() val releaseSyncResult = CompletableDeferred() startTestKoin( - FakeHomeScreenSyncNowUseCase { + FakeSyncManager { syncStarted.complete(Unit) releaseSyncResult.await() } @@ -134,7 +123,7 @@ class HomeScreenTest { fun tappingCancelSync_whileSyncing_callsCancelAndShowsCancelledMessage() = runComposeUiTest { val syncStarted = CompletableDeferred() val releaseSyncResult = CompletableDeferred() - val fake = FakeHomeScreenSyncNowUseCase { + val fake = FakeSyncManager { syncStarted.complete(Unit) releaseSyncResult.await() } @@ -156,7 +145,7 @@ class HomeScreenTest { } onNodeWithText("Cancel sync").performClick() - waitUntil(timeoutMillis = 5_000L) { fake.cancelCalled } + waitUntil(timeoutMillis = 5_000L) { fake.cancelSyncNowCount > 0 } releaseSyncResult.complete(SyncJobStatus.Failed()) waitUntil(timeoutMillis = 5_000L) { @@ -166,7 +155,7 @@ class HomeScreenTest { @Test fun tappingSyncNow_onFailure_showsSnackbarMessage() = runComposeUiTest { - startTestKoin(FakeHomeScreenSyncNowUseCase { SyncJobStatus.Failed() }) + startTestKoin(FakeSyncManager { SyncJobStatus.Failed() }) val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { From 2029bd4f5a66fd80e9e8d6051d5690e50f48dd97 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:19:39 +0300 Subject: [PATCH 51/73] Refresh the session before headless background sync A WorkManager relaunch after the process died, or an iOS BGProcessingTask, never runs the UI bootstrap that hydrates SessionRepository, so the sync was sending an empty Bearer token. Load and refresh the session first via a Koin-free ensureFreshSessionForSync(), called from AppFhirSyncWorker and the iOS scheduler. Move AndroidAppContext.init to Application.onCreate so KSafe exists in a headless process that never touches MainActivity. --- .../ohs/player/reference/app/MainActivity.kt | 2 - .../reference/app/OhsPlayerApplication.kt | 13 ++++--- .../app/data/sync/AppFhirSyncWorker.kt | 11 ++++++ .../app/auth/HeadlessSessionRefresh.kt | 39 +++++++++++++++++++ .../app/data/sync/IosBgSyncScheduler.kt | 4 ++ 5 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/HeadlessSessionRefresh.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt index fe87774a..d7e79cb1 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/MainActivity.kt @@ -19,11 +19,9 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import dev.ohs.player.reference.app.auth.AndroidAppContext class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { - AndroidAppContext.init(application) enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { App() } diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt index d9f33420..e171d3d8 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/OhsPlayerApplication.kt @@ -23,19 +23,21 @@ import dev.ohs.fhir.engine.FhirEngineProvider import dev.ohs.fhir.engine.NetworkConfiguration import dev.ohs.fhir.engine.ServerConfiguration import dev.ohs.fhir.engine.sync.remote.HttpLogger +import dev.ohs.player.reference.app.auth.AndroidAppContext import dev.ohs.player.reference.app.auth.FhirBearerAuthenticator import dev.ohs.player.reference.app.auth.GeneratedAuthConfig import dev.ohs.player.reference.app.data.di.initKoin -import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase -import dev.ohs.player.reference.app.data.sync.WorkManagerPeriodicSyncUseCase -import dev.ohs.player.reference.app.data.sync.WorkManagerSyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SyncManager +import dev.ohs.player.reference.app.data.sync.WorkManagerSyncManager import org.koin.dsl.module class OhsPlayerApplication : Application() { override fun onCreate() { super.onCreate() + // Before FhirEngineProvider.init: creating KSafe / the sync-timestamp DataStore reaches for + // this, and a headless WorkManager launch never goes through MainActivity. + AndroidAppContext.init(this) FhirEngineProvider.init( FhirEngineConfiguration( serverConfiguration = @@ -56,8 +58,7 @@ class OhsPlayerApplication : Application() { initKoin( module { single { FhirEngineProvider.getInstance(applicationContext) } - single { WorkManagerSyncNowUseCase(applicationContext) } - single { WorkManagerPeriodicSyncUseCase(applicationContext) } + single { WorkManagerSyncManager(applicationContext) } } ) DataCapture.initialize(applicationContext) diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt index efeae188..6634be83 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt @@ -22,6 +22,7 @@ import dev.ohs.fhir.engine.sync.ConflictResolver import dev.ohs.fhir.engine.sync.DownloadWorkManager import dev.ohs.fhir.engine.sync.FhirSyncWorker import dev.ohs.fhir.engine.sync.upload.UploadStrategy +import dev.ohs.player.reference.app.auth.ensureFreshSessionForSync /** * WorkManager entry point for this app's sync, enqueued via [dev.ohs.fhir.engine.sync.Sync]. Built @@ -32,6 +33,16 @@ class AppFhirSyncWorker(appContext: Context, workerParams: WorkerParameters) : FhirSyncWorker(appContext, workerParams) { private val syncTask = AppFhirSyncTask(FhirEngineProvider.getInstance(appContext)) + /** + * WorkManager can relaunch this worker in a fresh process after the app was killed, where the UI + * bootstrap never ran and [dev.ohs.player.reference.app.auth.SessionRepository] is empty. Hydrate + * and refresh the session first so the sync's requests carry a valid Bearer token. + */ + override suspend fun doWork(): Result { + ensureFreshSessionForSync() + return super.doWork() + } + override fun getFhirEngine() = syncTask.getFhirEngine() override fun getDownloadWorkManager(): DownloadWorkManager = syncTask.getDownloadWorkManager() diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/HeadlessSessionRefresh.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/HeadlessSessionRefresh.kt new file mode 100644 index 00000000..702cc849 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/HeadlessSessionRefresh.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.auth + +/** + * Loads the persisted session and refreshes an expired access token, so a headless/background sync + * (Android WorkManager, iOS `BGProcessingTask`) hands [FhirBearerAuthenticator] a valid Bearer + * token even when the UI bootstrap that normally hydrates [SessionRepository] never ran. + * + * Builds its own [AuthService] over the baked [OAuthConfig.Default] and the singleton + * [SessionRepository] rather than resolving one from Koin: iOS background launches don't call + * `initKoin`, so nothing is registered there. Offline-safe — a transient/offline failure keeps the + * stored session (see [AuthService.ensureFreshSession]); it only returns null when there is no + * usable session at all, in which case the sync will simply 401 and be retried later. + */ +internal suspend fun ensureFreshSessionForSync() { + headlessAuthService.ensureFreshSession() +} + +/** + * One instance per process (like the foreground Koin singleton) so repeated syncs don't leak an + * [OidcAuthApi]'s underlying HTTP client. + */ +private val headlessAuthService: AuthService by lazy { + AuthService(OAuthConfig.Default, SessionRepository, OidcAuthApi(OAuthConfig.Default)) +} diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt index 2e726910..2cdf7255 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt @@ -19,6 +19,7 @@ import co.touchlab.kermit.Logger import dev.ohs.fhir.engine.sync.FhirSyncTask import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.engine.sync.runSync +import dev.ohs.player.reference.app.auth.ensureFreshSessionForSync import kotlin.concurrent.AtomicInt import kotlin.coroutines.cancellation.CancellationException import kotlinx.cinterop.BetaInteropApi @@ -102,6 +103,9 @@ internal class IosBgSyncScheduler( scope.launch { try { + // A background launch never runs the UI bootstrap, so hydrate + refresh the session first + // to hand the sync's requests a valid Bearer token. + ensureFreshSessionForSync() val status = taskFactory().runSync(taskName = taskIdentifier, onProgress = {}) Logger.d { "IosBgSyncScheduler: sync completed with $status" } completeOnce(status is SyncJobStatus.Succeeded) From 95183164a4bb737b470cbe5f1605188e3bc7c029 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:20:13 +0300 Subject: [PATCH 52/73] Cancel sync and keep local data on logout, cover redirect bootstrap Inject SyncManager into AuthViewModel and cancel both the in-flight and periodic sync on logout and on a server-side revoke, so neither keeps firing with a stale token. Logout now clears the session but leaves local FHIR data on disk. Lift consumeRedirectCallback into AuthorizationLauncherApi so the bootstrap test seam runs the real redirect-completion branch instead of diverging from production, and add coverage for it. --- .../reference/app/auth/Platform.android.kt | 2 +- .../player/reference/app/auth/AuthService.kt | 2 +- .../reference/app/auth/AuthViewModel.kt | 63 ++++---- .../app/auth/AuthorizationLauncher.kt | 12 +- .../reference/app/auth/AuthServiceTest.kt | 8 +- .../reference/app/auth/AuthViewModelTest.kt | 109 ++++++-------- .../player/reference/app/auth/Platform.ios.kt | 2 +- .../player/reference/app/auth/Platform.jvm.kt | 2 +- .../app/auth/AuthViewModelLogoutTest.kt | 140 ------------------ .../player/reference/app/auth/Platform.web.kt | 2 +- 10 files changed, 98 insertions(+), 244 deletions(-) delete mode 100644 ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt index 3b61c06d..a7a3a2f3 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/auth/Platform.android.kt @@ -95,7 +95,7 @@ actual class AuthorizationLauncher( } } - actual fun consumeRedirectCallback(): String? = null + actual override fun consumeRedirectCallback(): String? = null } @Composable diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt index 3864c99c..dcf95a34 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthService.kt @@ -58,7 +58,7 @@ internal class AuthService( * Web: on app start, finishes a login if this page load is a redirect back from the provider. * Returns null when there is nothing to complete. */ - suspend fun completeRedirectLoginIfPresent(launcher: AuthorizationLauncher): LoginOutcome? { + suspend fun completeRedirectLoginIfPresent(launcher: AuthorizationLauncherApi): LoginOutcome? { val callbackUrl = launcher.consumeRedirectCallback() ?: return null return completeLogin(callbackUrl, launcher.redirectUri) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt index c257d595..d152e2d5 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthViewModel.kt @@ -17,7 +17,7 @@ package dev.ohs.player.reference.app.auth import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import dev.ohs.fhir.engine.FhirEngine +import dev.ohs.player.reference.app.data.sync.SyncManager import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -29,8 +29,10 @@ import kotlinx.coroutines.launch * [AuthorizationLauncher] is platform UI, so it's passed in from the composable rather than * constructed here. */ -internal class AuthViewModel(private val service: AuthService, private val fhirEngine: FhirEngine) : - ViewModel() { +internal class AuthViewModel( + private val service: AuthService, + private val syncManager: SyncManager, +) : ViewModel() { private val _state = MutableStateFlow(AuthState.Loading) val state: StateFlow = _state.asStateFlow() @@ -47,38 +49,34 @@ internal class AuthViewModel(private val service: AuthService, private val fhirE fun bootstrap(launcher: AuthorizationLauncher) { if (bootstrapped) return bootstrapped = true - viewModelScope.launch { - when (val redirect = service.completeRedirectLoginIfPresent(launcher)) { - is LoginOutcome.Authenticated -> { - _state.value = AuthState.Authenticated(redirect.session) - return@launch - } - is LoginOutcome.Error -> _error.value = redirect.message - else -> Unit - } - val session = service.ensureFreshSession() - if (session != null) { - _state.value = AuthState.Authenticated(session) - revalidateInBackground() - } else { - _state.value = AuthState.Unauthenticated - } - } + viewModelScope.launch { runBootstrap(launcher) } } /** * Test seam: same bootstrap logic, but against [AuthorizationLauncherApi] so tests can fake it. */ - internal suspend fun bootstrapForTest(launcher: AuthorizationLauncherApi) { + internal suspend fun bootstrapForTest(launcher: AuthorizationLauncherApi) = runBootstrap(launcher) + + private suspend fun runBootstrap(launcher: AuthorizationLauncherApi) { + when (val redirect = service.completeRedirectLoginIfPresent(launcher)) { + is LoginOutcome.Authenticated -> { + _state.value = AuthState.Authenticated(redirect.session) + return + } + is LoginOutcome.Error -> _error.value = redirect.message + else -> Unit + } val session = service.ensureFreshSession() - _state.value = - if (session != null) AuthState.Authenticated(session) else AuthState.Unauthenticated + if (session != null) { + _state.value = AuthState.Authenticated(session) + revalidateInBackground() + } else { + _state.value = AuthState.Unauthenticated + } } private fun revalidateInBackground() { - viewModelScope.launch { - if (!service.revalidateSession()) _state.value = AuthState.Unauthenticated - } + viewModelScope.launch { if (!service.revalidateSession()) signOut() } } fun login(launcher: AuthorizationLauncher) { @@ -113,7 +111,18 @@ internal class AuthViewModel(private val service: AuthService, private val fhirE private suspend fun runLogout() { service.logout() - fhirEngine.clearDatabase() + signOut() + } + + /** + * Ends the local session: stops any in-flight and recurring sync before dropping to the login + * screen, so neither can keep firing with a stale/absent token after a logout or a server-side + * revoke. Local FHIR data is intentionally left on disk — logout clears the session, not the + * data. + */ + private suspend fun signOut() { + syncManager.cancelSyncNow() + syncManager.cancelPeriodicSync() _state.value = AuthState.Unauthenticated } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt index 258a3358..f4b98f68 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/auth/AuthorizationLauncher.kt @@ -34,6 +34,12 @@ internal interface AuthorizationLauncherApi { val redirectUri: String suspend fun authorize(authUrl: String): AuthResult + + /** + * Web only: if this page load is a redirect back from the provider, returns the callback URL (and + * clears it from the address bar). Returns null otherwise and on every non-web platform. + */ + fun consumeRedirectCallback(): String? } expect class AuthorizationLauncher : AuthorizationLauncherApi { @@ -45,11 +51,7 @@ expect class AuthorizationLauncher : AuthorizationLauncherApi { */ override suspend fun authorize(authUrl: String): AuthResult - /** - * Web only: if this page load is a redirect back from the provider, returns the callback URL (and - * clears it from the address bar). Returns null otherwise and on every non-web platform. - */ - fun consumeRedirectCallback(): String? + override fun consumeRedirectCallback(): String? } /** Obtains a launcher, wiring in any platform context (e.g. the Android Activity). */ diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt index 9681abf1..9a0296b8 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthServiceTest.kt @@ -75,6 +75,8 @@ class AuthServiceTest { private val onAuthorize: suspend (String) -> AuthResult, ) : AuthorizationLauncherApi { override suspend fun authorize(authUrl: String): AuthResult = onAuthorize(authUrl) + + override fun consumeRedirectCallback(): String? = null } private val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") @@ -138,8 +140,6 @@ class AuthServiceTest { private fun service(store: SessionStore, api: OidcAuthApi) = AuthService(OAuthConfig("https://idp.example.org", "client", "openid"), store, api) - // ---- ensureFreshSession ---------------------------------------------------- - @Test fun refreshSuccess_updatesSession() = runTest { val store = FakeSessionStore(session(expired = true)) @@ -190,8 +190,6 @@ class AuthServiceTest { assertFalse(store.cleared) } - // ---- revalidateSession ------------------------------------------------------ - @Test fun revalidateRevoked_logsOut() = runTest { val store = FakeSessionStore(session(expired = false)) @@ -210,8 +208,6 @@ class AuthServiceTest { assertFalse(store.cleared) } - // ---- login / buildAuthorizationUrl ----------------------------------------- - @Test fun login_buildsAuthorizationUrlWithRequiredPkceParamsAndCompletesOnMatchingState() = runTest { val store = FakeSessionStore(null) diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt index 05a8bea6..388ce63a 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelTest.kt @@ -15,18 +15,7 @@ */ package dev.ohs.player.reference.app.auth -import dev.ohs.fhir.engine.FhirEngine -import dev.ohs.fhir.engine.LocalChange -import dev.ohs.fhir.engine.OffsetDateTime -import dev.ohs.fhir.engine.SearchResult -import dev.ohs.fhir.engine.db.LocalChangeResourceReference -import dev.ohs.fhir.engine.search.Search -import dev.ohs.fhir.engine.sync.ConflictResolver -import dev.ohs.fhir.engine.sync.upload.SyncUploadProgress -import dev.ohs.fhir.engine.sync.upload.UploadRequestResult -import dev.ohs.fhir.engine.sync.upload.UploadStrategy -import dev.ohs.fhir.model.r4.Resource -import dev.ohs.fhir.model.r4.terminologies.ResourceType +import dev.ohs.player.reference.app.data.sync.FakeSyncManager import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond @@ -39,7 +28,6 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertNull -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -74,54 +62,12 @@ class AuthViewModelTest { private class FakeLauncher( override val redirectUri: String, + private val redirectCallback: String? = null, private val onAuthorize: suspend (String) -> AuthResult, ) : AuthorizationLauncherApi { override suspend fun authorize(authUrl: String): AuthResult = onAuthorize(authUrl) - } - - /** Every `AuthViewModel` in this file gets one of these — none of these tests call logout(). */ - private class NoOpFhirEngine : FhirEngine { - override suspend fun create(vararg resource: Resource): List = - error("not used in this test") - - override suspend fun get(type: ResourceType, id: String): Resource = - error("not used in this test") - - override suspend fun update(vararg resource: Resource) = error("not used in this test") - - override suspend fun delete(type: ResourceType, id: String) = error("not used in this test") - - override suspend fun search(search: Search): List> = - error("not used in this test") - - override suspend fun syncUpload( - uploadStrategy: UploadStrategy, - upload: - suspend (List, List) -> Flow, - ): Flow = error("not used in this test") - - override suspend fun syncDownload( - conflictResolver: ConflictResolver, - download: suspend () -> Flow>, - ) = error("not used in this test") - - override suspend fun count(search: Search): Long = error("not used in this test") - - override suspend fun getLastSyncTimeStamp(): OffsetDateTime? = error("not used in this test") - override suspend fun clearDatabase() = error("not used in this test") - - override suspend fun getLocalChanges(type: ResourceType, id: String): List = - error("not used in this test") - - override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) = - error("not used in this test") - - override suspend fun purge(type: ResourceType, ids: Set, forcePurge: Boolean) = - error("not used in this test") - - override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) = - error("not used in this test") + override fun consumeRedirectCallback(): String? = redirectCallback } private val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") @@ -188,7 +134,7 @@ class AuthViewModelTest { FakeSessionStore(null), apiThatNeverGetsCalled(), ) - val viewModel = AuthViewModel(service, NoOpFhirEngine()) + val viewModel = AuthViewModel(service, FakeSyncManager()) val launcher = FakeLauncher("app://callback") { AuthResult.Canceled } viewModel.bootstrapForTest(launcher) @@ -204,7 +150,7 @@ class AuthViewModelTest { FakeSessionStore(session()), apiThatNeverGetsCalled(), ) - val viewModel = AuthViewModel(service, NoOpFhirEngine()) + val viewModel = AuthViewModel(service, FakeSyncManager()) val launcher = FakeLauncher("app://callback") { AuthResult.Canceled } viewModel.bootstrapForTest(launcher) @@ -212,6 +158,27 @@ class AuthViewModelTest { assertIs(viewModel.state.value) } + @Test + fun bootstrap_withRedirectCallback_completesRedirectLoginBranch() = runTest { + val service = + AuthService( + OAuthConfig("https://idp.example.org", "c", "openid"), + FakeSessionStore(null), + apiThatNeverGetsCalled(), + ) + val viewModel = AuthViewModel(service, FakeSyncManager()) + // A callback with no matching pending auth is rejected as a CSRF mismatch — proving bootstrap + // ran the redirect-completion branch rather than only ensureFreshSession(). + val launcher = + FakeLauncher("app://callback", redirectCallback = "app://callback?code=abc&state=xyz") { + AuthResult.Canceled + } + + viewModel.bootstrapForTest(launcher) + + assertEquals("Invalid state — possible CSRF, please try again", viewModel.error.value) + } + @Test fun login_onError_setsErrorAndClearsSigningIn() = runTest { val service = @@ -220,7 +187,7 @@ class AuthViewModelTest { FakeSessionStore(null), apiWithWorkingDiscovery(), ) - val viewModel = AuthViewModel(service, NoOpFhirEngine()) + val viewModel = AuthViewModel(service, FakeSyncManager()) val launcher = FakeLauncher("app://callback") { AuthResult.Failure("network down") } viewModel.loginForTest(launcher) @@ -237,7 +204,7 @@ class AuthViewModelTest { FakeSessionStore(null), apiWithWorkingDiscovery(), ) - val viewModel = AuthViewModel(service, NoOpFhirEngine()) + val viewModel = AuthViewModel(service, FakeSyncManager()) val launcher = FakeLauncher("app://callback") { AuthResult.Failure("network down") } viewModel.loginForTest(launcher) @@ -245,4 +212,24 @@ class AuthViewModelTest { assertNull(viewModel.error.value) } + + @Test + fun logout_clearsSessionAndStopsSync() = runTest { + val store = FakeSessionStore(session()) + val service = + AuthService( + OAuthConfig("https://idp.example.org", "c", "openid"), + store, + apiThatNeverGetsCalled(), + ) + val syncManager = FakeSyncManager() + val viewModel = AuthViewModel(service, syncManager) + + viewModel.logoutForTest() + + assertEquals(AuthState.Unauthenticated, viewModel.state.value) + assertNull(store.session.value) + assertEquals(1, syncManager.cancelSyncNowCount) + assertEquals(1, syncManager.cancelPeriodicCount) + } } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt index e66e519f..03d43f23 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt @@ -81,7 +81,7 @@ actual class AuthorizationLauncher( session.start() } - actual fun consumeRedirectCallback(): String? = null + actual override fun consumeRedirectCallback(): String? = null } private class PresentationContextProvider : diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt index 29d577b6..4f2cdbdb 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/auth/Platform.jvm.kt @@ -77,7 +77,7 @@ actual class AuthorizationLauncher(private val port: Int) : AuthorizationLaunche } } - actual fun consumeRedirectCallback(): String? = null + actual override fun consumeRedirectCallback(): String? = null } @Composable diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt deleted file mode 100644 index d0bc2ea7..00000000 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/auth/AuthViewModelLogoutTest.kt +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.auth - -import dev.ohs.fhir.engine.FhirEngine -import dev.ohs.fhir.engine.FhirEngineConfiguration -import dev.ohs.fhir.engine.FhirEngineProvider -import dev.ohs.fhir.engine.search.Search -import dev.ohs.fhir.model.r4.Patient -import dev.ohs.fhir.model.r4.terminologies.ResourceType -import dev.ohs.player.reference.app.data.repository.FhirEngineRepository -import io.ktor.client.HttpClient -import io.ktor.client.engine.mock.MockEngine -import io.ktor.client.engine.mock.respond -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpStatusCode -import io.ktor.http.headersOf -import io.ktor.serialization.kotlinx.json.json -import java.nio.file.Files -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json - -/** Verifies AuthViewModel.logout() actually wipes local FHIR data, using a real FhirEngine. */ -class AuthViewModelLogoutTest { - - private lateinit var fhirEngine: FhirEngine - - private class FakeSessionStore(initial: Session?) : SessionStore { - private val _session = MutableStateFlow(initial) - override val session: StateFlow = _session.asStateFlow() - - override suspend fun load(): Session? = _session.value - - override suspend fun save(session: Session) { - _session.value = session - } - - override suspend fun clear() { - _session.value = null - } - - override suspend fun savePending(pending: PendingAuth) = Unit - - override suspend fun takePending(): PendingAuth? = null - } - - @BeforeTest - fun setUp() = runTest { - if (FhirEngineProvider.isNotInitialized()) { - FhirEngineProvider.init( - FhirEngineConfiguration( - storageDirectory = Files.createTempDirectory("auth-viewmodel-logout-test").toString() - ) - ) - } - fhirEngine = FhirEngineProvider.getInstance() - fhirEngine.clearDatabase() - } - - private fun apiWithEndSessionEndpoint(): OidcAuthApi { - val jsonHeaders = headersOf(HttpHeaders.ContentType, "application/json") - val discoveryBody = - """ - { - "authorization_endpoint": "https://idp.example.org/authorize", - "token_endpoint": "https://idp.example.org/token", - "userinfo_endpoint": "https://idp.example.org/userinfo", - "end_session_endpoint": "https://idp.example.org/logout" - } - """ - .trimIndent() - val engine = MockEngine { request -> - when { - request.url.encodedPath.endsWith("openid-configuration") -> - respond(discoveryBody, HttpStatusCode.OK, jsonHeaders) - request.url.encodedPath.endsWith("/logout") -> respond("", HttpStatusCode.NoContent) - else -> respond("not found", HttpStatusCode.NotFound) - } - } - val client = - HttpClient(engine) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } - return OidcAuthApi(OAuthConfig("https://idp.example.org", "client", "openid"), client) - } - - private fun session() = - Session( - accessToken = "a", - refreshToken = "r", - idToken = null, - expiresInSeconds = 10_000_000_000L, - obtainedAtEpochSeconds = 0, - user = UserInfo(), - ) - - @Test - fun logout_clearsLocalFhirData() = runTest { - val json = Json { ignoreUnknownKeys = true } - val repository = FhirEngineRepository(fhirEngine) - val patient = - json.decodeFromString( - Patient.serializer(), - """{"resourceType": "Patient", "id": "patient-to-be-cleared"}""", - ) - repository.upsert(patient) - assertEquals(1L, fhirEngine.count(Search(ResourceType.Patient))) - - val service = - AuthService( - OAuthConfig("https://idp.example.org", "client", "openid"), - FakeSessionStore(session()), - apiWithEndSessionEndpoint(), - ) - val viewModel = AuthViewModel(service, fhirEngine) - - viewModel.logoutForTest() - - assertEquals(0L, fhirEngine.count(Search(ResourceType.Patient))) - assertEquals(AuthState.Unauthenticated, viewModel.state.value) - } -} diff --git a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt index e3049cd7..0e0f2335 100644 --- a/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt +++ b/ohs-player-reference-app/src/webMain/kotlin/dev/ohs/player/reference/app/auth/Platform.web.kt @@ -39,7 +39,7 @@ actual class AuthorizationLauncher(actual override val redirectUri: String) : return AuthResult.Redirecting } - actual fun consumeRedirectCallback(): String? { + actual override fun consumeRedirectCallback(): String? { val search = window.location.search if (!search.contains("code=") && !search.contains("error=")) return null val href = window.location.href From b20e3414d7723454dc923090a7035c4267bd7148 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:20:28 +0300 Subject: [PATCH 53/73] Harden iOS secure random and Keychain accessibility secureRandomBytes ignored SecRandomCopyBytes' status and would return an all-zero buffer on failure, yielding a predictable PKCE verifier and state; check the status and fail loudly instead. Pin the KSafe key to kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly (the default) explicitly, so the background BGProcessingTask sync can read the session while the device is locked. --- .../player/reference/app/auth/Platform.ios.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt index 03d43f23..a2326055 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/auth/Platform.ios.kt @@ -20,6 +20,7 @@ package dev.ohs.player.reference.app.auth import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import eu.anifantakis.lib.ksafe.KSafe +import eu.anifantakis.lib.ksafe.KSafeConfig import kotlin.coroutines.resume import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.addressOf @@ -31,6 +32,7 @@ import platform.AuthenticationServices.ASWebAuthenticationSession import platform.Foundation.NSError import platform.Foundation.NSURL import platform.Security.SecRandomCopyBytes +import platform.Security.errSecSuccess import platform.Security.kSecRandomDefault import platform.UIKit.UIApplication import platform.UIKit.UISceneActivationStateForegroundActive @@ -38,13 +40,22 @@ import platform.UIKit.UIWindow import platform.UIKit.UIWindowScene import platform.darwin.NSObject -internal actual fun createKSafe(): KSafe = KSafe() +/** + * `requireUnlockedDevice = false` keeps the Keychain key at + * `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`, so the background `BGProcessingTask` sync can + * read the session while the device is locked (once it has been unlocked at least once since boot). + * Setting it `true` would break headless sync on a locked device. + */ +internal actual fun createKSafe(): KSafe = + KSafe(config = KSafeConfig(requireUnlockedDevice = false)) internal actual fun secureRandomBytes(size: Int): ByteArray { val bytes = ByteArray(size) - bytes.usePinned { pinned -> - SecRandomCopyBytes(kSecRandomDefault, size.toULong(), pinned.addressOf(0)) - } + val status = + bytes.usePinned { pinned -> + SecRandomCopyBytes(kSecRandomDefault, size.toULong(), pinned.addressOf(0)) + } + check(status == errSecSuccess) { "SecRandomCopyBytes failed (OSStatus $status)" } return bytes } From 084ee715347edde49cb5a2f7f1e33679b65a2132 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:22:19 +0300 Subject: [PATCH 54/73] Extract user-facing strings to composeResources Move hardcoded UI strings across the screens into composeResources so the app can be localized. Locale resolution stays out of the ViewModels: HomeViewModel exposes a semantic SyncError the screen maps to a string, and the initial-sync gate's failure message moves to the screen. Also inline the Home drawer width breakpoint into its only caller and drop its trivial boundary test. --- .../composeResources/values/strings.xml | 45 +++++++++++++++ .../app/feature/group/list/GroupListScreen.kt | 11 +++- .../group/profile/GroupProfileScreen.kt | 20 +++++-- .../app/feature/home/HomeDestination.kt | 7 ++- .../reference/app/feature/home/HomeScreen.kt | 55 +++++++++++++++---- .../app/feature/home/HomeViewModel.kt | 20 ++++--- .../app/feature/home/HomeWidthBreakpoint.kt | 28 ---------- .../feature/patient/list/PatientListScreen.kt | 8 ++- .../patient/profile/PatientProfileScreen.kt | 22 ++++++-- .../questionnaire/QuestionnaireHostScreen.kt | 13 ++++- .../app/feature/sync/InitialSyncScreen.kt | 30 +++++++--- .../feature/home/HomeWidthBreakpointTest.kt | 36 ------------ .../app/feature/home/HomeViewModelTest.kt | 39 +++++++------ 13 files changed, 210 insertions(+), 124 deletions(-) delete mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt delete mode 100644 ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index 8714d34f..220e1ebe 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -16,4 +16,49 @@ Sign in Sign-in failed Dismiss + + + Households + Registers + Last synced: %1$s + Sync now + Cancel sync + Sync in progress + Sign out + Open navigation menu + Sync failed. Please try again. + Sync cancelled. + + + Setting up your data… + This may take a moment the first time. + Couldn\'t sync your data + Sync failed. Please check your connection and try again. + Retry + Continue without syncing + + + Register household + No households + + + Household + Back + Add members + Add household members + + + Patients + No patients + + + Patient + Back + Add clinical data + Patient not found + + + Questionnaire + Back + Retry \ No newline at end of file diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt index 9eccda43..d16db610 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt @@ -35,6 +35,10 @@ import dev.ohs.player.generated.state.GroupListState import dev.ohs.player.generated.viewtype.ViewTypeCS import dev.ohs.player.library.layout.VerticalListRenderer import dev.ohs.player.library.scaffold.ListScaffold +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_list_empty +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_list_register_household +import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -53,7 +57,10 @@ fun GroupListScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Un Scaffold( floatingActionButton = { FloatingActionButton(onClick = onDataCaptureClick) { - Icon(Icons.Filled.Add, contentDescription = "Register household") + Icon( + Icons.Filled.Add, + contentDescription = stringResource(Res.string.group_list_register_household), + ) } } ) { padding -> @@ -65,7 +72,7 @@ fun GroupListScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Un ) { component(ViewTypeCS.GroupCard) layout(VerticalListRenderer.VIEW_TYPE) - emptyState { Text("No households") } + emptyState { Text(stringResource(Res.string.group_list_empty)) } } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupProfileScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupProfileScreen.kt index f80bae1a..42c940e7 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupProfileScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupProfileScreen.kt @@ -48,6 +48,12 @@ import dev.ohs.player.library.registry.LocalViewRegistry import dev.ohs.player.library.registry.componentRenderer import dev.ohs.player.library.registry.layoutRenderer import dev.ohs.player.library.renderer.RenderOptions +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_profile_add_members +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_profile_add_members_description +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_profile_back +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_profile_default_name +import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf @@ -70,7 +76,8 @@ fun GroupProfileScreen( val memberSectionLayout = remember(registry) { registry.layoutRenderer(ViewTypeCS.SectionCard) } - val groupName = state?.groupHeader?.groupName ?: "Household" + val groupName = + state?.groupHeader?.groupName ?: stringResource(Res.string.group_profile_default_name) Scaffold( topBar = { @@ -80,7 +87,7 @@ fun GroupProfileScreen( IconButton(onClick = onBack) { Icon( Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back", + contentDescription = stringResource(Res.string.group_profile_back), tint = MaterialTheme.colorScheme.onPrimary, ) } @@ -95,8 +102,13 @@ fun GroupProfileScreen( floatingActionButton = { ExtendedFloatingActionButton( onClick = onAddMembers, - text = { Text("Add members") }, - icon = { Icon(Icons.Filled.Add, contentDescription = "Add household members") }, + text = { Text(stringResource(Res.string.group_profile_add_members)) }, + icon = { + Icon( + Icons.Filled.Add, + contentDescription = stringResource(Res.string.group_profile_add_members_description), + ) + }, ) }, ) { padding -> diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt index 498fec44..ad45a329 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeDestination.kt @@ -18,11 +18,14 @@ package dev.ohs.player.reference.app.feature.home import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Home import androidx.compose.ui.graphics.vector.ImageVector +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_destination_households +import org.jetbrains.compose.resources.StringResource /** * A top-level destination reachable from [HomeScreen]'s navigation drawer. `Households` is the only * entry today; adding a second destination later is a new enum entry, not a rewrite. */ -enum class HomeDestination(val label: String, val icon: ImageVector) { - Households(label = "Households", icon = Icons.Filled.Home) +enum class HomeDestination(val label: StringResource, val icon: ImageVector) { + Households(label = Res.string.home_destination_households, icon = Icons.Filled.Home) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index b4a12327..3381d31b 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -55,12 +55,29 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.ohs.player.reference.app.feature.group.list.GroupListScreen import kotlinx.coroutines.launch +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_cancel_sync +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_last_synced +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_open_navigation_menu +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_registers +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sign_out +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_cancelled +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_failed +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_in_progress +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_now +import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel +/** + * Material 3's "expanded" window size class breakpoint: at or above it the drawer stays permanent. + */ +private val HOME_EXPANDED_WIDTH_BREAKPOINT: Dp = 840.dp + @OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeScreen( @@ -76,31 +93,38 @@ fun HomeScreen( val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } + val syncErrorMessage = + when (uiState.syncError) { + SyncError.Failed -> stringResource(Res.string.home_sync_failed) + SyncError.Cancelled -> stringResource(Res.string.home_sync_cancelled) + null -> null + } LaunchedEffect(uiState.syncError) { - uiState.syncError?.let { message -> - snackbarHostState.showSnackbar(message) + if (syncErrorMessage != null) { + snackbarHostState.showSnackbar(syncErrorMessage) homeViewModel.clearSyncError() } } BoxWithConstraints { - val isExpandedWidth = isHomeDrawerExpandedWidth(maxWidth) + val isExpandedWidth = maxWidth >= HOME_EXPANDED_WIDTH_BREAKPOINT fun closeDrawerIfCompact() { if (!isExpandedWidth) scope.launch { drawerState.close() } } val drawerItems: @Composable () -> Unit = { + val syncInProgressDescription = stringResource(Res.string.home_sync_in_progress) Column(modifier = Modifier.fillMaxHeight()) { Text( - text = "Registers", + text = stringResource(Res.string.home_registers), style = MaterialTheme.typography.titleSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(start = 28.dp, top = 16.dp, bottom = 8.dp), ) HomeDestination.entries.forEach { destination -> NavigationDrawerItem( - label = { Text(destination.label) }, + label = { Text(stringResource(destination.label)) }, icon = { Icon(destination.icon, contentDescription = null) }, selected = destination == selectedDestination, onClick = { @@ -115,14 +139,20 @@ fun HomeScreen( HorizontalDivider() uiState.lastSyncedAt?.let { lastSyncedAt -> Text( - text = "Last synced: $lastSyncedAt", + text = stringResource(Res.string.home_last_synced, lastSyncedAt), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(start = 28.dp, top = 8.dp), ) } NavigationDrawerItem( - label = { Text(if (uiState.isSyncing) "Cancel sync" else "Sync now") }, + label = { + Text( + stringResource( + if (uiState.isSyncing) Res.string.home_cancel_sync else Res.string.home_sync_now + ) + ) + }, icon = { Icon( if (uiState.isSyncing) Icons.Filled.Close else Icons.Filled.Refresh, @@ -133,7 +163,7 @@ fun HomeScreen( if (uiState.isSyncing) { CircularProgressIndicator( modifier = - Modifier.size(16.dp).semantics { contentDescription = "Sync in progress" }, + Modifier.size(16.dp).semantics { contentDescription = syncInProgressDescription }, strokeWidth = 2.dp, ) } @@ -149,7 +179,7 @@ fun HomeScreen( }, ) NavigationDrawerItem( - label = { Text("Sign out") }, + label = { Text(stringResource(Res.string.home_sign_out)) }, icon = { Icon(Icons.AutoMirrored.Filled.ExitToApp, contentDescription = null) }, selected = false, onClick = { @@ -181,10 +211,13 @@ fun HomeScreen( Scaffold( topBar = { TopAppBar( - title = { Text(selectedDestination.label) }, + title = { Text(stringResource(selectedDestination.label)) }, navigationIcon = { IconButton(onClick = { scope.launch { drawerState.open() } }) { - Icon(Icons.Filled.Menu, contentDescription = "Open navigation menu") + Icon( + Icons.Filled.Menu, + contentDescription = stringResource(Res.string.home_open_navigation_menu), + ) } }, ) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt index 6f505dc1..47150fb6 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModel.kt @@ -19,7 +19,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dev.ohs.fhir.engine.sync.FhirDataStore import dev.ohs.fhir.engine.sync.SyncJobStatus -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SyncManager import kotlin.time.Instant import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job @@ -31,14 +31,20 @@ import kotlinx.coroutines.launch import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime +/** Why the last sync ended without success — the screen maps each to a localized message. */ +enum class SyncError { + Failed, + Cancelled, +} + data class HomeUiState( val isSyncing: Boolean = false, val lastSyncedAt: String? = null, - val syncError: String? = null, + val syncError: SyncError? = null, ) class HomeViewModel( - private val syncNowUseCase: SyncNowUseCase, + private val syncManager: SyncManager, private val fhirDataStore: FhirDataStore, ) : ViewModel() { private val _uiState = MutableStateFlow(HomeUiState()) @@ -64,7 +70,7 @@ class HomeViewModel( return viewModelScope.launch { val result = try { - syncNowUseCase.invoke() + syncManager.syncNow() } catch (e: CancellationException) { throw e } catch (e: Exception) { @@ -76,8 +82,8 @@ class HomeViewModel( _uiState.update { it.copy(isSyncing = false, lastSyncedAt = lastSyncedAt) } } else -> { - val message = if (cancelRequested) "Sync cancelled." else "Sync failed. Please try again." - _uiState.update { it.copy(isSyncing = false, syncError = message) } + val error = if (cancelRequested) SyncError.Cancelled else SyncError.Failed + _uiState.update { it.copy(isSyncing = false, syncError = error) } } } } @@ -87,7 +93,7 @@ class HomeViewModel( fun cancelSync() { if (!_uiState.value.isSyncing) return cancelRequested = true - viewModelScope.launch { syncNowUseCase.cancel() } + viewModelScope.launch { syncManager.cancelSyncNow() } } fun clearSyncError() { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt deleted file mode 100644 index e00e3b71..00000000 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpoint.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.feature.home - -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -/** Material 3's "expanded" window size class breakpoint. */ -internal val HOME_EXPANDED_WIDTH_BREAKPOINT: Dp = 840.dp - -/** - * True once the available width reaches [HOME_EXPANDED_WIDTH_BREAKPOINT], where [HomeScreen] - * switches from a hamburger-triggered modal drawer to an always-visible permanent drawer. - */ -internal fun isHomeDrawerExpandedWidth(width: Dp): Boolean = width >= HOME_EXPANDED_WIDTH_BREAKPOINT diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListScreen.kt index 108f636f..7b9ca930 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListScreen.kt @@ -32,6 +32,10 @@ import dev.ohs.player.generated.state.PatientSummaryState import dev.ohs.player.generated.viewtype.ViewTypeCS import dev.ohs.player.library.layout.VerticalListRenderer import dev.ohs.player.library.scaffold.ListScaffold +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.patient_list_empty +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.patient_list_title +import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -56,7 +60,7 @@ fun PatientListScreen(onPatientClick: (String) -> Unit) { layout(VerticalListRenderer.VIEW_TYPE) topBar { TopAppBar( - title = { Text("Patients") }, + title = { Text(stringResource(Res.string.patient_list_title)) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, @@ -64,6 +68,6 @@ fun PatientListScreen(onPatientClick: (String) -> Unit) { ), ) } - emptyState { Text("No patients") } + emptyState { Text(stringResource(Res.string.patient_list_empty)) } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt index 1407ea95..fea27d1b 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt @@ -57,6 +57,12 @@ import dev.ohs.player.library.registry.LocalViewRegistry import dev.ohs.player.library.registry.componentRenderer import dev.ohs.player.library.registry.layoutRenderer import dev.ohs.player.library.renderer.RenderOptions +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.patient_profile_add_clinical_data +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.patient_profile_back +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.patient_profile_default_name +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.patient_profile_not_found +import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf @@ -108,9 +114,12 @@ fun PatientProfileScreen(patientId: String, onBack: () -> Unit, onAddClinicalDat remember(registry) { registry.componentRenderer(ViewTypeCS.TelecomItem) } val patient = state?.patient + val defaultPatientName = stringResource(Res.string.patient_profile_default_name) val patientName = - remember(patient) { - listOfNotNull(patient?.givenName, patient?.familyName).joinToString(" ").ifBlank { "Patient" } + remember(patient, defaultPatientName) { + listOfNotNull(patient?.givenName, patient?.familyName).joinToString(" ").ifBlank { + defaultPatientName + } } Scaffold( @@ -121,7 +130,7 @@ fun PatientProfileScreen(patientId: String, onBack: () -> Unit, onAddClinicalDat IconButton(onClick = onBack) { Icon( Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back", + contentDescription = stringResource(Res.string.patient_profile_back), tint = MaterialTheme.colorScheme.onPrimary, ) } @@ -135,7 +144,10 @@ fun PatientProfileScreen(patientId: String, onBack: () -> Unit, onAddClinicalDat }, floatingActionButton = { FloatingActionButton(onClick = onAddClinicalData) { - Icon(Icons.Filled.Add, contentDescription = "Add clinical data") + Icon( + Icons.Filled.Add, + contentDescription = stringResource(Res.string.patient_profile_add_clinical_data), + ) } }, ) { padding -> @@ -148,7 +160,7 @@ fun PatientProfileScreen(patientId: String, onBack: () -> Unit, onAddClinicalDat } if (s.patient == null) { Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) { - Text("Patient not found") + Text(stringResource(Res.string.patient_profile_not_found)) } return@Scaffold } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt index d895a71c..89b242b9 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt @@ -52,6 +52,11 @@ import dev.ohs.fhir.datacapture.QuestionnaireItemViewFactoryMatchersProvider import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_back +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_retry +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_title +import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf @@ -95,12 +100,12 @@ fun QuestionnaireHostScreen( Scaffold( topBar = { TopAppBar( - title = { Text(title ?: "Questionnaire") }, + title = { Text(title ?: stringResource(Res.string.questionnaire_title)) }, navigationIcon = { IconButton(onClick = onBack) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back", + contentDescription = stringResource(Res.string.questionnaire_back), tint = MaterialTheme.colorScheme.onPrimary, ) } @@ -140,7 +145,9 @@ fun QuestionnaireHostScreen( style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(16.dp), ) - TextButton(onClick = viewModel::load) { Text("Retry") } + TextButton(onClick = viewModel::load) { + Text(stringResource(Res.string.questionnaire_retry)) + } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt index 65083329..55dba6f3 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreen.kt @@ -35,6 +35,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.initial_sync_continue +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.initial_sync_failed_body +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.initial_sync_failed_title +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.initial_sync_retry +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.initial_sync_subtitle +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.initial_sync_title +import org.jetbrains.compose.resources.stringResource /** * Full-screen blocking gate shown between login and Home: a progress state while checking/syncing, @@ -51,7 +59,7 @@ fun InitialSyncScreen( when (state) { InitialSyncGateState.Checking, InitialSyncGateState.Syncing -> SyncingContent() - is InitialSyncGateState.Failed -> FailedContent(state.message, onRetry, onContinueAnyway) + InitialSyncGateState.Failed -> FailedContent(onRetry, onContinueAnyway) InitialSyncGateState.Passed -> Unit } } @@ -65,12 +73,12 @@ private fun SyncingContent() { ) { CircularProgressIndicator() Text( - text = "Setting up your data…", + text = stringResource(Res.string.initial_sync_title), style = MaterialTheme.typography.titleMedium, textAlign = TextAlign.Center, ) Text( - text = "This may take a moment the first time.", + text = stringResource(Res.string.initial_sync_subtitle), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, textAlign = TextAlign.Center, @@ -79,7 +87,7 @@ private fun SyncingContent() { } @Composable -private fun FailedContent(message: String, onRetry: () -> Unit, onContinueAnyway: () -> Unit) { +private fun FailedContent(onRetry: () -> Unit, onContinueAnyway: () -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp), @@ -91,15 +99,21 @@ private fun FailedContent(message: String, onRetry: () -> Unit, onContinueAnyway modifier = Modifier.size(48.dp), ) Text( - text = "Couldn't sync your data", + text = stringResource(Res.string.initial_sync_failed_title), style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.error, textAlign = TextAlign.Center, ) - Text(text = message, style = MaterialTheme.typography.bodyMedium, textAlign = TextAlign.Center) - Button(onClick = onRetry, modifier = Modifier.fillMaxWidth()) { Text("Retry") } + Text( + text = stringResource(Res.string.initial_sync_failed_body), + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + ) + Button(onClick = onRetry, modifier = Modifier.fillMaxWidth()) { + Text(stringResource(Res.string.initial_sync_retry)) + } TextButton(onClick = onContinueAnyway, modifier = Modifier.fillMaxWidth()) { - Text("Continue without syncing") + Text(stringResource(Res.string.initial_sync_continue)) } } } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt deleted file mode 100644 index 39175cf7..00000000 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeWidthBreakpointTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.feature.home - -import androidx.compose.ui.unit.dp -import kotlin.test.Test -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -class HomeWidthBreakpointTest { - - @Test - fun isHomeDrawerExpandedWidth_belowBreakpoint_isFalse() { - assertFalse(isHomeDrawerExpandedWidth(600.dp)) - assertFalse(isHomeDrawerExpandedWidth(839.dp)) - } - - @Test - fun isHomeDrawerExpandedWidth_atOrAboveBreakpoint_isTrue() { - assertTrue(isHomeDrawerExpandedWidth(840.dp)) - assertTrue(isHomeDrawerExpandedWidth(1200.dp)) - } -} diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt index 975f91df..92c964ee 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeViewModelTest.kt @@ -18,7 +18,7 @@ package dev.ohs.player.reference.app.feature.home import dev.ohs.fhir.engine.sync.FhirDataStore import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.engine.sync.createDataStore -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.SyncManager import java.nio.file.Files import kotlin.test.Test import kotlin.test.assertEquals @@ -27,17 +27,17 @@ import kotlin.test.assertNull import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest -private class FakeSyncNowUseCase( +private class RecordingSyncManager( private val fhirDataStore: FhirDataStore, private val result: suspend () -> SyncJobStatus, -) : SyncNowUseCase { +) : SyncManager { var invocationCount = 0 private set var cancelCount = 0 private set - override suspend fun invoke(): SyncJobStatus { + override suspend fun syncNow(): SyncJobStatus { invocationCount++ val status = result() // Mirrors FhirSynchronizer's real behavior of persisting the timestamp on every terminal @@ -46,9 +46,13 @@ private class FakeSyncNowUseCase( return status } - override suspend fun cancel() { + override suspend fun cancelSyncNow() { cancelCount++ } + + override suspend fun startPeriodicSync() {} + + override suspend fun cancelPeriodicSync() {} } class HomeViewModelTest { @@ -62,7 +66,7 @@ class HomeViewModelTest { fun initialState_withNoPriorSync_hasNoLastSyncedAt() = runTest { val viewModel = HomeViewModel( - FakeSyncNowUseCase(newFhirDataStore()) { SyncJobStatus.Succeeded() }, + RecordingSyncManager(newFhirDataStore()) { SyncJobStatus.Succeeded() }, newFhirDataStore(), ) @@ -73,7 +77,10 @@ class HomeViewModelTest { fun syncNow_onSuccess_clearsIsSyncingAndPopulatesLastSyncedAt() = runTest { val fhirDataStore = newFhirDataStore() val viewModel = - HomeViewModel(FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Succeeded() }, fhirDataStore) + HomeViewModel( + RecordingSyncManager(fhirDataStore) { SyncJobStatus.Succeeded() }, + fhirDataStore, + ) viewModel.syncNow()?.join() @@ -87,13 +94,13 @@ class HomeViewModelTest { fun syncNow_onFailure_clearsIsSyncingAndSetsSyncError() = runTest { val fhirDataStore = newFhirDataStore() val viewModel = - HomeViewModel(FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Failed() }, fhirDataStore) + HomeViewModel(RecordingSyncManager(fhirDataStore) { SyncJobStatus.Failed() }, fhirDataStore) viewModel.syncNow()?.join() val state = viewModel.uiState.value assertEquals(false, state.isSyncing) - assertEquals("Sync failed. Please try again.", state.syncError) + assertEquals(SyncError.Failed, state.syncError) } @Test @@ -101,7 +108,7 @@ class HomeViewModelTest { val fhirDataStore = newFhirDataStore() val viewModel = HomeViewModel( - FakeSyncNowUseCase(fhirDataStore) { throw RuntimeException("network down") }, + RecordingSyncManager(fhirDataStore) { throw RuntimeException("network down") }, fhirDataStore, ) @@ -109,13 +116,13 @@ class HomeViewModelTest { val state = viewModel.uiState.value assertEquals(false, state.isSyncing) - assertEquals("Sync failed. Please try again.", state.syncError) + assertEquals(SyncError.Failed, state.syncError) } @Test fun syncNow_whileAlreadySyncing_doesNotStartASecondSync() = runTest { val fhirDataStore = newFhirDataStore() - val fake = FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Succeeded() } + val fake = RecordingSyncManager(fhirDataStore) { SyncJobStatus.Succeeded() } val viewModel = HomeViewModel(fake, fhirDataStore) val first = viewModel.syncNow() @@ -130,7 +137,7 @@ class HomeViewModelTest { fun cancelSync_whileSyncing_callsUseCaseCancelAndSetsCancelledMessage() = runTest { val fhirDataStore = newFhirDataStore() val releaseSyncResult = CompletableDeferred() - val fake = FakeSyncNowUseCase(fhirDataStore) { releaseSyncResult.await() } + val fake = RecordingSyncManager(fhirDataStore) { releaseSyncResult.await() } val viewModel = HomeViewModel(fake, fhirDataStore) val job = viewModel.syncNow() @@ -141,13 +148,13 @@ class HomeViewModelTest { assertEquals(1, fake.cancelCount) val state = viewModel.uiState.value assertEquals(false, state.isSyncing) - assertEquals("Sync cancelled.", state.syncError) + assertEquals(SyncError.Cancelled, state.syncError) } @Test fun cancelSync_whileNotSyncing_isNoOp() = runTest { val fhirDataStore = newFhirDataStore() - val fake = FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Succeeded() } + val fake = RecordingSyncManager(fhirDataStore) { SyncJobStatus.Succeeded() } val viewModel = HomeViewModel(fake, fhirDataStore) viewModel.cancelSync() @@ -159,7 +166,7 @@ class HomeViewModelTest { fun clearSyncError_removesTheErrorMessage() = runTest { val fhirDataStore = newFhirDataStore() val viewModel = - HomeViewModel(FakeSyncNowUseCase(fhirDataStore) { SyncJobStatus.Failed() }, fhirDataStore) + HomeViewModel(RecordingSyncManager(fhirDataStore) { SyncJobStatus.Failed() }, fhirDataStore) viewModel.syncNow()?.join() viewModel.clearSyncError() From 4b1a6803ac29211454f7f9a214139daaef409dc4 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 16:22:35 +0300 Subject: [PATCH 55/73] Gate initial sync on a persisted success flag The blocking initial-sync gate keyed off hasAnyData(), so an account that synced successfully but held no resources would look un-synced forever. Persist an "initial sync completed" flag set only on a successful sync and gate on that instead; a failed first sync still shows the gate. Remove the now-unused hasAnyData() and its tests. --- .../player/reference/app/data/di/AppModule.kt | 16 ++- .../data/repository/FhirEngineRepository.kt | 4 - .../app/data/repository/FhirRepository.kt | 5 - .../app/data/sync/InitialSyncStore.kt | 48 ++++++++ .../app/feature/sync/InitialSyncViewModel.kt | 39 ++++--- .../InMemorySampleFhirRepository.kt | 2 - .../feature/sync/InitialSyncViewModelTest.kt | 107 +++++++----------- .../repository/FhirEngineRepositoryTest.kt | 21 ---- .../app/feature/sync/InitialSyncScreenTest.kt | 6 +- 9 files changed, 121 insertions(+), 127 deletions(-) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InitialSyncStore.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt index 9b12b8a9..20f6cbee 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/di/AppModule.kt @@ -26,6 +26,9 @@ import dev.ohs.player.reference.app.data.repository.FhirEngineRepository import dev.ohs.player.reference.app.data.repository.FhirRepository import dev.ohs.player.reference.app.data.repository.GroupRepository import dev.ohs.player.reference.app.data.repository.PatientRepository +import dev.ohs.player.reference.app.data.sync.DataStoreInitialSyncStore +import dev.ohs.player.reference.app.data.sync.InitialSyncStore +import dev.ohs.player.reference.app.data.sync.createSyncTimestampDataStore import dev.ohs.player.reference.app.feature.group.list.GroupListViewModel import dev.ohs.player.reference.app.feature.group.profile.GroupProfileViewModel import dev.ohs.player.reference.app.feature.home.HomeViewModel @@ -59,12 +62,15 @@ internal val repositoryModule = module { internal val serviceModule = module { factory { QuestionnaireService(get()) } } /** - * [SyncNowUseCase] isn't bound here — each platform's `initKoin` caller supplies its own + * [SyncManager] isn't bound here — each platform's `initKoin` caller supplies its own * implementation, constructing `AppFhirSyncTask` directly rather than through Koin (see - * `WorkManagerSyncNowUseCase` on Android, `ForegroundSyncNowUseCase` on JVM/web, and - * `IosSyncNowUseCase` on iOS). + * `WorkManagerSyncManager` on Android, `ForegroundSyncManager` on JVM/web, and `IosSyncManager` on + * iOS). */ -internal val syncModule = module { single { FhirEngineProvider.getFhirDataStore() } } +internal val syncModule = module { + single { FhirEngineProvider.getFhirDataStore() } + single { DataStoreInitialSyncStore(createSyncTimestampDataStore()) } +} /** * `SessionStore`/`SessionRepository`/`AuthService` — everything downstream of the plain @@ -87,5 +93,5 @@ internal val viewModelModule = module { } viewModel { HomeViewModel(get(), get()) } viewModel { AuthViewModel(get(), get()) } - viewModel { InitialSyncViewModel(get(), get(), get()) } + viewModel { InitialSyncViewModel(get(), get()) } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt index 62b6410c..eeb91aa6 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt @@ -69,10 +69,6 @@ class FhirEngineRepository(private val fhirEngine: FhirEngine) : FhirRepository } } - override suspend fun hasAnyData(): Boolean = - fhirEngine.count(Search(ResourceType.Patient)) > 0 || - fhirEngine.count(Search(ResourceType.Group)) > 0 - private suspend fun upsertResource(resource: Resource) { val withId = if (resource.id == null) resource.withId(generateId()) else resource val type = ResourceType.valueOf(withId.resourceType) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt index 55f691da..6557a11e 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirRepository.kt @@ -37,9 +37,4 @@ interface FhirRepository { suspend fun get(resourceType: String, id: String): Resource? suspend fun all(resourceType: String): List - - /** - * True once any `Patient` or `Group` has been persisted locally. Drives the initial sync gate. - */ - suspend fun hasAnyData(): Boolean } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InitialSyncStore.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InitialSyncStore.kt new file mode 100644 index 00000000..9c20bcd0 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/sync/InitialSyncStore.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data.sync + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import kotlinx.coroutines.flow.first + +/** + * Persisted "the first sync has succeeded at least once" marker that drives the initial-sync gate. + * A plain data-count check ([dev.ohs.player.reference.app.data.repository.FhirRepository]) can't do + * this: an account that syncs successfully but holds no resources would look un-synced forever. The + * marker is set only on a successful sync, so a failed first sync still shows the blocking gate. + */ +interface InitialSyncStore { + suspend fun isComplete(): Boolean + + suspend fun markComplete() +} + +internal class DataStoreInitialSyncStore(private val dataStore: DataStore) : + InitialSyncStore { + override suspend fun isComplete(): Boolean = + dataStore.data.first()[INITIAL_SYNC_COMPLETE_KEY] == true + + override suspend fun markComplete() { + dataStore.edit { it[INITIAL_SYNC_COMPLETE_KEY] = true } + } + + private companion object { + val INITIAL_SYNC_COMPLETE_KEY = booleanPreferencesKey("initial_sync_complete") + } +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt index 0d8fc908..5be80b83 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModel.kt @@ -18,9 +18,8 @@ package dev.ohs.player.reference.app.feature.sync import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dev.ohs.fhir.engine.sync.SyncJobStatus -import dev.ohs.player.reference.app.data.repository.FhirRepository -import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.InitialSyncStore +import dev.ohs.player.reference.app.data.sync.SyncManager import kotlin.coroutines.cancellation.CancellationException import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -34,23 +33,22 @@ sealed interface InitialSyncGateState { data object Syncing : InitialSyncGateState - data class Failed(val message: String) : InitialSyncGateState + data object Failed : InitialSyncGateState data object Passed : InitialSyncGateState } /** - * Gates a freshly-authenticated session behind a one-time blocking sync when the local FHIR - * database is empty. [start] deliberately has no "already ran" guard — it must fully re-run its - * check every time it's called, because Koin can hand back the same ViewModel instance across a - * logout → login round-trip (the top-level `App()` composables aren't scoped per auth session), and - * a fresh login must always be re-evaluated (e.g. after - * [dev.ohs.player.reference.app.auth.AuthViewModel.logout] wipes local data). + * Gates a freshly-authenticated session behind a one-time blocking sync until a first sync has + * succeeded (tracked by [InitialSyncStore], not by whether the local database happens to hold any + * resources — a legitimately-empty account is still "synced"). [start] deliberately has no "already + * ran" guard — it must fully re-run its check every time it's called, because Koin can hand back + * the same ViewModel instance across a logout → login round-trip (the top-level `App()` composables + * aren't scoped per auth session), so a fresh login must always be re-evaluated. */ class InitialSyncViewModel( - private val fhirRepository: FhirRepository, - private val syncNowUseCase: SyncNowUseCase, - private val periodicSyncUseCase: PeriodicSyncUseCase, + private val syncManager: SyncManager, + private val initialSyncStore: InitialSyncStore, ) : ViewModel() { private val _state = MutableStateFlow(InitialSyncGateState.Checking) @@ -64,7 +62,7 @@ class InitialSyncViewModel( private suspend fun runCheck() { _state.value = InitialSyncGateState.Checking - if (fhirRepository.hasAnyData()) { + if (initialSyncStore.isComplete()) { passGate() } else { runSync() @@ -75,22 +73,23 @@ class InitialSyncViewModel( _state.value = InitialSyncGateState.Syncing val result = try { - syncNowUseCase.invoke() + syncManager.syncNow() } catch (e: CancellationException) { throw e } catch (e: Exception) { SyncJobStatus.Failed() } when (result) { - is SyncJobStatus.Succeeded -> passGate() - else -> - _state.value = - InitialSyncGateState.Failed("Sync failed. Please check your connection and try again.") + is SyncJobStatus.Succeeded -> { + initialSyncStore.markComplete() + passGate() + } + else -> _state.value = InitialSyncGateState.Failed } } private suspend fun passGate() { - periodicSyncUseCase.start() + syncManager.startPeriodicSync() _state.value = InitialSyncGateState.Passed } } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt index 70dafb7c..54ff3c41 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/data/repository/InMemorySampleFhirRepository.kt @@ -48,8 +48,6 @@ class InMemorySampleFhirRepository : FhirRepository { return resourcesByType[resourceType]?.values?.toList().orEmpty() } - override suspend fun hasAnyData(): Boolean = resourcesByType.values.any { it.isNotEmpty() } - private fun store(resource: Resource) { val id = resource.id ?: return resourcesByType.getOrPut(resource.resourceType) { mutableMapOf() }[id] = resource diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt index 1106a23f..6ba24702 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncViewModelTest.kt @@ -16,98 +16,72 @@ package dev.ohs.player.reference.app.feature.sync import dev.ohs.fhir.engine.sync.SyncJobStatus -import dev.ohs.fhir.model.r4.Patient -import dev.ohs.player.reference.app.data.repository.InMemorySampleFhirRepository -import dev.ohs.player.reference.app.data.sync.PeriodicSyncUseCase -import dev.ohs.player.reference.app.data.sync.SyncNowUseCase +import dev.ohs.player.reference.app.data.sync.FakeSyncManager +import dev.ohs.player.reference.app.data.sync.InitialSyncStore import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertIs +import kotlin.test.assertTrue import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json -class InitialSyncViewModelTest { - - private class FakeSyncNowUseCase(private val result: suspend () -> SyncJobStatus) : - SyncNowUseCase { - var invocationCount = 0 - private set - - override suspend fun invoke(): SyncJobStatus { - invocationCount++ - return result() - } - - override suspend fun cancel() {} - } +private class FakeInitialSyncStore(private var complete: Boolean = false) : InitialSyncStore { + override suspend fun isComplete(): Boolean = complete - private class FakePeriodicSyncUseCase : PeriodicSyncUseCase { - var startCount = 0 - private set - - override suspend fun start() { - startCount++ - } - - override suspend fun cancel() {} + override suspend fun markComplete() { + complete = true } +} - private val json = Json { ignoreUnknownKeys = true } - - private fun testPatient() = - json.decodeFromString(Patient.serializer(), """{"resourceType": "Patient", "id": "p1"}""") +class InitialSyncViewModelTest { @Test - fun start_withExistingData_goesPassedWithoutSyncing() = runTest { - val repository = InMemorySampleFhirRepository() - repository.upsert(testPatient()) - val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Succeeded() } - val periodicSyncUseCase = FakePeriodicSyncUseCase() - val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) + fun start_whenAlreadyComplete_passesWithoutSyncing() = runTest { + val syncManager = FakeSyncManager() + val viewModel = InitialSyncViewModel(syncManager, FakeInitialSyncStore(complete = true)) viewModel.start().join() assertEquals(InitialSyncGateState.Passed, viewModel.state.value) - assertEquals(0, syncNowUseCase.invocationCount) - assertEquals(1, periodicSyncUseCase.startCount) + assertEquals(0, syncManager.syncNowCount) + assertEquals(1, syncManager.startPeriodicCount) } @Test - fun start_withNoData_syncsThenPasses() = runTest { - val repository = InMemorySampleFhirRepository() - val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Succeeded() } - val periodicSyncUseCase = FakePeriodicSyncUseCase() - val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) + fun start_whenNotComplete_syncsThenPassesAndMarksComplete() = runTest { + val syncManager = FakeSyncManager() + val store = FakeInitialSyncStore() + val viewModel = InitialSyncViewModel(syncManager, store) viewModel.start().join() assertEquals(InitialSyncGateState.Passed, viewModel.state.value) - assertEquals(1, syncNowUseCase.invocationCount) - assertEquals(1, periodicSyncUseCase.startCount) + assertEquals(1, syncManager.syncNowCount) + assertEquals(1, syncManager.startPeriodicCount) + assertTrue(store.isComplete()) } @Test - fun start_withNoDataAndSyncFails_goesFailed() = runTest { - val repository = InMemorySampleFhirRepository() - val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Failed() } - val periodicSyncUseCase = FakePeriodicSyncUseCase() - val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) + fun start_whenSyncFails_goesFailedAndStaysIncomplete() = runTest { + val syncManager = FakeSyncManager { SyncJobStatus.Failed() } + val store = FakeInitialSyncStore() + val viewModel = InitialSyncViewModel(syncManager, store) viewModel.start().join() assertIs(viewModel.state.value) - assertEquals(0, periodicSyncUseCase.startCount) + assertEquals(0, syncManager.startPeriodicCount) + assertFalse(store.isComplete()) } @Test - fun retry_afterFailure_syncsAgain() = runTest { - val repository = InMemorySampleFhirRepository() + fun retry_afterFailure_syncsAgainAndMarksComplete() = runTest { var shouldFail = true - val syncNowUseCase = FakeSyncNowUseCase { + val syncManager = FakeSyncManager { if (shouldFail) SyncJobStatus.Failed() else SyncJobStatus.Succeeded() } - val periodicSyncUseCase = FakePeriodicSyncUseCase() - val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) + val store = FakeInitialSyncStore() + val viewModel = InitialSyncViewModel(syncManager, store) viewModel.start().join() assertIs(viewModel.state.value) @@ -115,22 +89,23 @@ class InitialSyncViewModelTest { viewModel.retry().join() assertEquals(InitialSyncGateState.Passed, viewModel.state.value) - assertEquals(2, syncNowUseCase.invocationCount) - assertEquals(1, periodicSyncUseCase.startCount) + assertEquals(2, syncManager.syncNowCount) + assertEquals(1, syncManager.startPeriodicCount) + assertTrue(store.isComplete()) } @Test - fun continueAnyway_movesToPassedAndStartsPeriodicSync() = runTest { - val repository = InMemorySampleFhirRepository() - val syncNowUseCase = FakeSyncNowUseCase { SyncJobStatus.Failed() } - val periodicSyncUseCase = FakePeriodicSyncUseCase() - val viewModel = InitialSyncViewModel(repository, syncNowUseCase, periodicSyncUseCase) + fun continueAnyway_passesButDoesNotMarkComplete() = runTest { + val syncManager = FakeSyncManager { SyncJobStatus.Failed() } + val store = FakeInitialSyncStore() + val viewModel = InitialSyncViewModel(syncManager, store) viewModel.start().join() assertIs(viewModel.state.value) viewModel.continueAnyway().join() assertEquals(InitialSyncGateState.Passed, viewModel.state.value) - assertEquals(1, periodicSyncUseCase.startCount) + assertEquals(1, syncManager.startPeriodicCount) + assertFalse(store.isComplete()) } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt index a7af2b02..eb4c4fa2 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt @@ -224,25 +224,4 @@ class FhirEngineRepositoryTest { assertEquals(1, groups.size) assertEquals("Patient/patient-abs-1", groups.first().member.first().entity.reference?.value) } - - @Test - fun hasAnyData_onEmptyDatabase_isFalse() = runTest { - val repository = FhirEngineRepository(fhirEngine) - - assertEquals(false, repository.hasAnyData()) - } - - @Test - fun hasAnyData_afterUpsertingPatient_isTrue() = runTest { - val repository = FhirEngineRepository(fhirEngine) - val patient = - json.decodeFromString( - Patient.serializer(), - """{"resourceType": "Patient", "id": "patient-has-data"}""", - ) - - repository.upsert(patient) - - assertEquals(true, repository.hasAnyData()) - } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt index c8bb8032..b9bc1bce 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/sync/InitialSyncScreenTest.kt @@ -48,8 +48,7 @@ class InitialSyncScreenTest { setContent { MaterialTheme { InitialSyncScreen( - state = - InitialSyncGateState.Failed("Sync failed. Please check your connection and try again."), + state = InitialSyncGateState.Failed, onRetry = { retried = true }, onContinueAnyway = {}, ) @@ -67,8 +66,7 @@ class InitialSyncScreenTest { setContent { MaterialTheme { InitialSyncScreen( - state = - InitialSyncGateState.Failed("Sync failed. Please check your connection and try again."), + state = InitialSyncGateState.Failed, onRetry = {}, onContinueAnyway = { continued = true }, ) From 1f991bbee43a6db3bb4ec36caf1101bfe8232c7d Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 17:03:24 +0300 Subject: [PATCH 56/73] Rebrand the app to Player Reference with the new logo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the ohs_logo mark as the application icon on every platform — Android adaptive + legacy mipmaps, the iOS AppIcon, the desktop window and installer icons, and a web SVG favicon — and show it as the login brand mark. Rename the user-facing application to "Player Reference" across the desktop window title, Android app_name, iOS CFBundleDisplayName, the login strings, and the web title. Internal identifiers (Gradle project, module, package) are left unchanged. --- .../AppIcon.appiconset/app-icon-1024.png | Bin 67285 -> 681501 bytes iosApp/iosApp/Info.plist | 2 + ohs-player-reference-app/build.gradle.kts | 7 +- .../desktop-icons/app-icon.icns | Bin 0 -> 20426 bytes .../desktop-icons/app-icon.ico | Bin 0 -> 35447 bytes .../desktop-icons/app-icon.png | Bin 0 -> 20426 bytes .../drawable-v24/ic_launcher_foreground.xml | 30 ---- .../res/drawable/ic_launcher_background.xml | 164 +----------------- .../res/mipmap-anydpi-v26/ic_launcher.xml | 4 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 4 +- .../res/mipmap-hdpi/ic_launcher.png | Bin 3593 -> 5867 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 5634 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 5339 -> 6665 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 2636 -> 2781 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 3323 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 3388 -> 3128 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 4926 -> 8768 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 7603 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 7472 -> 9867 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 7909 -> 18942 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 12675 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 11873 -> 21239 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 10652 -> 32155 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 17573 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 16570 -> 35868 bytes .../src/androidMain/res/values/strings.xml | 2 +- .../composeResources/drawable/app_logo.png | Bin 0 -> 20426 bytes .../composeResources/values/strings.xml | 4 +- .../app/feature/login/LoginScreen.kt | 47 +++-- .../dev/ohs/player/reference/app/main.kt | 11 +- .../src/webMain/resources/favicon.ico | Bin 0 -> 15086 bytes .../src/webMain/resources/favicon.svg | 6 + .../src/webMain/resources/index.html | 4 +- 33 files changed, 71 insertions(+), 214 deletions(-) create mode 100644 ohs-player-reference-app/desktop-icons/app-icon.icns create mode 100644 ohs-player-reference-app/desktop-icons/app-icon.ico create mode 100644 ohs-player-reference-app/desktop-icons/app-icon.png delete mode 100644 ohs-player-reference-app/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 ohs-player-reference-app/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 ohs-player-reference-app/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 ohs-player-reference-app/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 ohs-player-reference-app/src/commonMain/composeResources/drawable/app_logo.png create mode 100644 ohs-player-reference-app/src/webMain/resources/favicon.ico create mode 100644 ohs-player-reference-app/src/webMain/resources/favicon.svg diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png index 53fc536fb9ac5c1dbb27c7e1da13db3760070a11..d89b412489fd9425c1e9081c1981d8d7579ca8e9 100644 GIT binary patch literal 681501 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:Lu%Gx<>M8+)^2EM<=fI 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 eknF-Gst&*2cU)o@?nk2tuPxBL>)CAh#U7qpN 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%0Dg;scQa##Ys^aEyHYClu@^`L}{+cz0poyIx6~ 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

i&NpMtdilYK`Gy~xVAo)I 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 KH?KGK`~813M`8^C literal 67285 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 BXNCX( diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 4ed514f7..ba74a8db 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -2,6 +2,8 @@ + CFBundleDisplayName + Player Reference CADisableMinimumFrameDurationOnPhone BGTaskSchedulerPermittedIdentifiers diff --git a/ohs-player-reference-app/build.gradle.kts b/ohs-player-reference-app/build.gradle.kts index 24d4a796..f29b0c22 100644 --- a/ohs-player-reference-app/build.gradle.kts +++ b/ohs-player-reference-app/build.gradle.kts @@ -402,8 +402,13 @@ compose.desktop { nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Rpm) - packageName = "dev.ohs.player.reference.app" + packageName = "PlayerReference" packageVersion = composePackageVersion + + val iconsDir = project.layout.projectDirectory.dir("desktop-icons") + macOS { iconFile.set(iconsDir.file("app-icon.icns")) } + windows { iconFile.set(iconsDir.file("app-icon.ico")) } + linux { iconFile.set(iconsDir.file("app-icon.png")) } } } } diff --git a/ohs-player-reference-app/desktop-icons/app-icon.icns b/ohs-player-reference-app/desktop-icons/app-icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..855b8469c1d0b8e08cf05661ec2dc39b17e74b1f GIT binary patch literal 20426 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 literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/desktop-icons/app-icon.ico b/ohs-player-reference-app/desktop-icons/app-icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ccd8f2d42e8860c38d298a99352cede97c99617c GIT binary patch literal 35447 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 DqpQ}} literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/desktop-icons/app-icon.png b/ohs-player-reference-app/desktop-icons/app-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..855b8469c1d0b8e08cf05661ec2dc39b17e74b1f GIT binary patch literal 20426 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 literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/ohs-player-reference-app/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d11..00000000 --- a/ohs-player-reference-app/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/ohs-player-reference-app/src/androidMain/res/drawable/ic_launcher_background.xml b/ohs-player-reference-app/src/androidMain/res/drawable/ic_launcher_background.xml index e93e11ad..4eb5b929 100644 --- a/ohs-player-reference-app/src/androidMain/res/drawable/ic_launcher_background.xml +++ b/ohs-player-reference-app/src/androidMain/res/drawable/ic_launcher_background.xml @@ -5,166 +5,6 @@ android:viewportWidth="108" android:viewportHeight="108"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml index eca70cfe..3ba4e35c 100644 --- a/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + + diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cfe..3ba4e35c 100644 --- a/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/ohs-player-reference-app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + + diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher.png index a571e60098c92c2baca8a5df62f2929cbff01b52..162cbcf666255cc226b9a124c5679b4c02fd1863 100644 GIT binary patch literal 5867 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 P00000NkvXXu0mjfP=x?Y diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.png b/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..bfed47d2d8463de7f32ae848ef7a0b287ba2872b GIT binary patch literal 5634 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 literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png index 61da551c5594a1f9d26193983d2cd69189014603..49db6858a8e18753fa402d6d20709be425cf6d1b 100644 GIT binary patch literal 6665 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ΝOI;U3;56Xa}}As0Z;! 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 T$;l)E00000NkvXXu0mjfNY3@+ literal 5339 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 literal 4926 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 ZPwGmuuU-_#e_hX{7)F literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png index da31a871c8dc461a1ea114510bfac640307b0a90..98644631947901f61dd677a3394dac952680971c 100644 GIT binary patch literal 9867 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 literal 7472 zcmV-09na#4P)! z;P!3tj(sx=w_Y;NUw>m_{`wMv#{|y_Ub1-3epZZSuq+;f$KpBgTzJmvqStkVy|*s` zM7`DU*~KB<%nCwg%`Dow)2uKggWyjBFe?a#HD!ljS;;<_ksr(p*2VkiF?cKmbFM4& z+~gW~t?C^C>-4Ya@sh;rW(KqwmFF{kRIbk7OSAYiGH)Iyv5bNP|Oc%MLy< zDcH#LMkFZP`;8>w)lnA#s)G}RUX#6^Nq!Juov?0LN3Ooo=BM}OB}u$qk$-#rTyG!J zz^B;bZA%Yeqp7)&MS6V+P+bhH1J-3#$pLOeJjJ?Vou#$qz3BDm>Tz#J<@(Mhjmi_7 z8q(lZr3ZwQ^MZI2T3-Tiz`9_a=p2(RHcfeYc|LQ*E-<#K!H)(uQpJDA=KFRbjX2B^ z&zTu)AojKfCjgEB92Km2qTgZNNgJ>&+}zM$13Jk`OFz$h66yIRv;j;b%OxA!kOh!{ z1{j|kP)<-m0P^5adYGmR6qVz!tav}nFAU{f9?Rk} ze9L29uueS6V%y4%^VWky!J*^{34#uP%Shnt-=fStZCuKJPTch<3hYY{mD`mb1U}gD z;1amsISPEsZ@hON{O+FOT^`HgF?`EoU9e7k%VS$ZA4Y;>{(+=v#|7=)>72lM05p@C z>l=nWe@*F6%}wTW_isUE?vmQiY5L0f4cw@DRj`za4Q*f%)GmDJtIs&F-fRK z#NPcxd%r}G^+5pcb1ym{XeK%xC0sR@;7vKbU-!1>EH1YrnO^uHfJADW@S}T!n4&P7 zc}f`t+=Mbb%~5q!j!zDo6REPy_d$TF%cs;7rMc#P5jv-1ohN1X;6}Qco?h(4E396b z4+2#CKG#R6ds{#z6a%OdN=cDO+ zSNB6MEo%}RaJJt#Gr--XAP7wIH;5+ZZ2)PQo*xVzWyfefMOK;W*m*w^p1gSu_uu>h zmc{>5SRT!TdC?x;=f|>)nNxh;7v+D^x?r97o*&zaZN|3CDnob^8UMBp3@$qO)o3md zu(=HNBi60;vb}Ce^L*-Rf^16;LfF%5AQFk-*C#1pnB(`(O^{J;AVfd=jn?7JlPk1N zN;5&(m7HlLIAnIWozOv&TVA$b`?}jSX@0-5CgFueyP^26hw$jlpESk$t_46d^+Na; zt;52?UCQ%KC5*W6*q3Cp?s=7P%Tt+DPc!2v}}i**qIC%@o(7vVLT3(}tFgF&|M zI}>0c>HRsc?$T>x9k4FS7C;;wXL`bj2-{x>r%e<`$LtW96eZ|N6fBkHdMe8e9h>71 z*IyJ9BFd>3qMz*}Q-B4em(D8KN+&tDJ4a#donv&-1wASc@;`otn{v(aL*ToDoiYV5 zB=y`)yqpwu`(ic6}Qm@e#8oiZY&!zPc7LgOB-9MjYT=b_D(` ze+ii{%jnV|euhHe_X~@5!KQm*kor6iN?$*M-(Nq0r{yoG>3B(iBqH!V;xRF2cV0h+ zlD{57+_Nky>Vm>hFwR{szV>&8JE4q}!E55Rl^%%6FhhpF+RjIA)sIx$CNIVNX>6Lg zaT}lBuM7e3_{e9s=wygJb86lu8Y3X-&j?BQd0l{lCH|QMn~9LPf_3_7I{iHSkLzLr z>q`J`6zKit2@}Fy|A*Yl_J+6_die0BGjcblzAFJZn~m-u`s1&Juj@>@Ea18E8h9-9e6FgCSLoU z2tdrxSLy4X4%s$$2y)D=AxjltOtQzj$4T$B*UK9XSQo5Qy$HZe z#G>h$n?UQtDj(_dK&5~B(d^q>_Slylf<;B&3l|etP7%=cLwC@kcn|O?zp~^9$ar4Z zAjp>#0b>!Y8=p2{Td~d9c0T177w-|;7X1h&7u*jLj+?#}4@iW_%}jsWbP;ceBR;nf z{cc6TU1;d;;a(g?WtSH3g{v=$K-fTtmju=c>xOky)DCPbwi(;bha)oK3$2Uxf^nqB zWx{dGx6=~Ln?{`s)mu-<^uLP1jJ*6$ZA_49{uYRNmP!3~Q3DhJfpx<=PRrk{G!w+- zg^*LjSm&E<)w_3~dx#`GAujvb%Xey*3E2Vp$`%0A3>W^mMqR*$NSu#p8Y-d!qre1ZX_q2lFqDa{`|zQvh`D?!A8c-U)zpmgSn(T7Xo+Q#HYqVQ+at zVgYu~8)Tdt_)J*>U=HTWivop>Eq!($Hm4t@$a_+MaY6ReQrLX+I0WB13HM(l_h{dwhwH(AFj~dEdJvjn4WQmK?fF57#_2Q z`!Aj-o%}n`AA#;!TNrj~8O4IQAo%^oWBKlB`D+L%IS=|-$`e4%)mRI;mMTF1t#j0s zWrA?I4l|RAh>0(|0YeX(GXfkWIJ6j|ORp(ifUuHOG5NzzF9WS}t04J)ro!XOUOa@U z8S6kV(@QBPsJFxT5i$kn=lAs&6SCJSWfI2BCLdxl?&W~qFDu04BW^y-SGoXc53u0{a z!>e(x%iqAyS&{JdSr0Hhw-!RK{t7~&@?(W^a?V|u=V0b#KZ;)pV(5w(pJQ)7Ee4Y~ zFVISIq9dW!ZfLAaQKzZH)R60{`5-0`Ym7mH(Jj9^2V%HdRg+W$5?=JjT_}Eb4_=km zV>+6gyX5(O3SkWb!oNr-alXDEMn>9#R*DN4Wck!gfLtFMh#5pW-fY#gQ&+lqw@ONy zT?Zy;JMG5$@VcfVa53e5b2}9w>0u_AL<_(q#uH4h1cL9KlQm977+r9|R73~LwV+BW z0vZ_#3~@-bo}Ll7w=T&z`_e=3_|5ZwoB)qr{Q;Iq!7wv!9n6U*0%ZOIO9`n8IV#*O zPR30*<#3pA+=g;peQ};$Bxp&7i3d$bGk1yCI34X&_A_0d{ig}={LL${z4kpZLw2AQ zWe*la48wGRcw$zNj;=7hy%9$2HOCFREu}8Vupc(p_}O~SOm?NHrVBEdKRNg)u0duy z>z*wY!v4ZblzgqIHBBdM zwONuJo3l>5!2VA}#JvpAk9Gp>%asCX#H_)c&@x8?wSNZ>e}818zFaQg}6 zSRiAIqS^}MkIA3*Qxd#FYqKlDBsU1MpOwMA=a1#$(Tk@v-9X>JkcB5=Jbd{FJb3xE z^0Sxn@sO0oNt1hjUm9Lj;=!w@@c7lUDxXP1_Mc^76u%a6<&bHj*TJnsQthpiRE^nw6PFLEI6UO0mlQNdslxe-hwyukDlL8LcKuZ}1m z2A6%nGIk5t#P5I^(Y`Pvh9K6j3e4jC8N?&j!Gfes;F`9V)_rDDH6#bXtmHtLmBK(L z#sRcr7y%68T*Ty4#5;mchMQOfZex~qnk$U(pSv8n?I~E$T=v#PCOBx(<15YndN&2d ze9TaFFG%mUCk#Kol1VK{q!$o_e=?_-dE5hZk1U75KU=`yBMgT8VhKZzT2KvUgQrwzLXK* zj3Y1dho4&k#uwdSIvFi|$VZHhbcTg-8+nmW1&AdAq;0DdK!SYC86mV$glw;JG(Q6m zE^|HZmU?bLUEJ5Nt?DAh0-M@6_mMgk#SEWlv~vreo9-J>gbkxvCUivl?D zB3~@PC2wBjkGy0HqoZ6{0Th!@C)_wG0whQXkmLlK$xan`%c@q2GpM;wwnk3n+JA9k zjxj?mKklsBM=QRwJ(1X8j(7@Uc4nPq1mHtHnw_uDdBB9TPQ1uRvtt}y zRRDS9W3R6+fIRZ)WEA2V^&$s{?i(7)@x~~$ozM=Z z;F2S?^&HUbjE-V3CB_SuC2oV!(JnA1+7-sc5X2(fh}-E7W8&RmEF!^!!YEMyb{XHp zjSDAkC}7=!&-p&oMY~RxonOa?0<;nxVG+%|>ZhXYamS*PHZK z7VU?5(Sb1Y)LIJruwa;f#usLt7QpN?o(#@nY~PZh-l53~)tkK|Eq3EKAx3 zUTFtlVd5rONIas2$(vwN@@80+vIQ2UZh^&!v|w1A9t`H`Az+!l4FYcc0?RUXfiwG+IuR%c^6*fQvoh{fLW9eFY*y+b`~XW=0!dgAVER^3G&hAYot1h(C;U0 zdeG6J&uHYZr(w_LwYgcoQAgdr_-Oa;gAXkZ!W)m3ai=_v1oXM}j<4cHJ{5ojXcNO+ zc#)42?&L@mz?T>KIN^?oaf3xko8^-);qB-o5&?+$F-Uf=LO%9>;<$)Ll5>9UXSyA^ z>)5wrn;Q52N|#6-=YkH+y0jml5$BL8EiS0d?r59BA7EUJJ0V>$`Dk`9DxMhT%8PvL z^;Ce%e!R%XUXKDSPTHcd=X0KpZlVh;y-EZ~@eq@b&`xm{YNfis-~)?uns!qiMi*cB z`2IXb!6$0|rq(*wJ%D>uSzYfBn3T1i5uM5FmvUz(s^v(cz>XpV^FEjhuDRRBK!N-e39pNTqvQTt@3N`1sOeXo_%+ zQyF*2pgE!M99i{WEmBK^gMY%mT9;b zjc)nocBlX`{=9QLW8*x)90ibLb|k$W-DFp=zP^hHu$Cb|)wP_OoYY(%V4+ zmfhF|W70e*`6I$@q0ic>n~@uqqk4IsbR(7S-CL-%YK8k+`VBg;_%PmpY?L1;vMWBQ zln1xsNI(**dpnrdF($zk-`tK#G!YYXgTKTXNCprXN1WS2!lezd|XGF3$3y z3mzKhZ5V{vfEkHuO(Hx%;k$yT|(53 zW`PSTv5pj&)zpc1qPZQb^zAgjq9A@gdO8$j!o?m>k;*_n&Anp9?L9)ncsEer_Dv+= zVi4to;ileyVWSB*AE-2KI%MH_{{-AYY+rUrXj^iiLKzS5wk`e1yO+%PI0@y zHg-EKh~5ATV_1-2Zc*GuF&4*fVvw*I)}-tP_tbr0PJDawWCj*wlC>aq9$}e=`JAm3 zR_WWoHe)x2SaRkivJ0uehhS#Uv zmu`xPd(~R4YbWxzXVaEVhc7tmpE&-8FEvLvCn)3b_2aVq!61?JxQnY{Zlpg#E+b+dpCZAPrj#+O zxjZA3rWP=|r64}OL24xo)7HXhV)I952t?TP&GtE_G;PsT136&1_^3Wjk2DduNx2un z&>@E{!nui=J|98Oh9$la?Zb_*nsIArVr>$MZu#bRro?)|?Dzo1xgB=W#gww;mF+TZ zKDwHmw}Upn|JJ!^c5s_{FNsO_o&UlTUa(oKUY+q5hVWPD2KWE|yCYa}=1D8elVt1q z)I=0vZu&-=Uf`SCnG)v>vl9Y%CDw4l#eBXcF+H-#M?atOc2>a`>*<7xj~wXDw!PWk zL4Fkx*dd4`VPL<&85>5%*uO!y5+i1M$9**+YWmp9Mftnn>(q5H;u62y4iz9VkQe!g z@yVW*0!Sv-Fugz`Tnw^?o?QN>kIN)a>m6*1yT@$Q41QeS6jBUEAT4p}uU>yOW;!?(a@uBXKlvKd6a9)b_!xXpWF1 zMG@}Q1Rt24v|eFWle77_jA%tX9@^`1EjP_oguNc)kiHwtPPP8D6Rv7~N!!*=rCmcK zUs42g!&Tsa_RU*LR3;B?}i*Mv|C9egC4Y&#VmXSs(v%woR?rHa6&=G{iup zIZjZxvx5BJzeR_(TK$4%Y$Z|bUG$Xbk9ihste|s*0*^`RL;Ki~AS=S1nur2ykZX1{ zlPE;k-$|o^63;vqnf~}Py(dA67}B1ah$8{FhD&obze*wk zq-=Pbd?Y^6u|g}+QAh-&8B8=gxGiPYNx|=5_)Xi_erR`NcB1{9t$Uk>YI69Rq~@$nZ3wOip{H@Y{ z;f@&z)w~@PU@j3rBW_KFMuMYgWFi6S?V8EXBF??U+&wOy4ESN;tpNhl;QtQlIgvFt zeQ8}uo!MUBXVGqSsH}S|| zVNv|OXinjFAzcXKei@s93YFz4(oS_2YR1?Li2y>FfuyvJgF8&U^Nw#WBv-b1yw3S(|sz3a&KUCj+Rlw0Ba(5@%-me4e*6A}iu z>(g~~|5cOhbat2@81t)b`ozl~52mL1il$u;gjIR_U`fFqn31;y%nE|RtT3c1@`GX8 zjX=B!0!)&;V1CL*uuKjHCnBoYIAN>3_xNCMt0FtoAUYcu{Hw(%z{SmvHscc zCz~jplQtQ;VXJdTML3ihL_6OzjB$C0!2d@@tSQqvx;%H}K8p<9T^3O~n-(1I?>;T4 z&q9Nh9kqH*!E>^t51_rBT(d=o4&B=@K7Gr71M#xv2zpNf+FYFUSkFm~=GPgr1`*D+7~fG#ZOVVf_5BKg|Kn%P|J!~PmSM{dVQu;V_FQUsZaT3t_PsTG z?I!;;Q&Sru8nZU{V`>IeRomkY&FFihd0|McUYzm9)ri?Ia+mU z)m24Rr9Eq6K4!1g_}@-EA3>VYn;MWf5@pk!2Ho0pM0Lj3z9plHfjXEJ1dIC;b1Kq#ey`7v5d~0000FU3`B41`ZMfa*^5fx^6q%*(4z(iB4 z2v1Z7f0JRQmH`I`hoB~fi^drbEs{v$h6TdXR5d&s zoQ;J>bUHXE8-v7hKwdr;h0U0fQX(3c89j|uoIbURfFKwN5dlw~`Ri6x2)()RAmMs2 zF}4d{CwJJ+IDqFhl0bhC8??ve3@Nxi%S`PelBT2%UXTod0D*vjlp3Zanh;Hv1Wr?3f|Q^#g1keSxEw?|qe8S` z9U&Vnf}SpcQS}WKiF^nWS5f<$Xe7*+qlHJJ+!9!AZgtS5{s``_Dfo|Vw4j0e9XGNZ zhXWZ5bKR}g-o4am{zo%pJBN+Qm8hs?$jy&9IVr=*8Q}Ajy$!_wz;$s*;3KHC2 zG~*&#A6aWeVo6~O8rrB1xeT?05m}Tp>?%sV3F&?buWboY2~mV{qYQQe}rEz;Hr z)vl;O`9v6x3C&d6*>cXIku~n(_f!m{O5ywDDRtz{% zE#eX*!oIH%z6)RFqmYmwMR2TxEu$mAP{RX;scyXP-gQ~yVDpjX*|xuILK(95!3Uy6 zvoO+aLOtL@q&H1+-8$L2sB@+6oO_}P)PC?EG8@=@Tk zS*A#J+ir_HT$f0hsl1}lAoL!RIutsUq?TFL7|&LIs})ITy}q_Pl%-|H6(h8&m9@1j z_}<#qGw5*n>jeL_Hgrlc@&}@)2{Fz^sSR`O_c6Xd#=#;HNQqX;kd2`6MzmLw!ZIS( zNY+QGYMaR4?P^E%9j{#JRtW`z2CH5GyJ7`hC_AiY^^3V|&!H{)%+?(zW1hl+_Ur00 z!XtTFGfLhP)2~a7DDKRHc=#^3&`*wmaR?-4(q<%)1QbqkFQZlFA+7n#iAybgOugb! zs|PY>;Z6ihdUbG0+&lDpqu6JE%hf@37J1%ST!;80lJ5hG>cZ?qL-2bGtB?JR#-Ow= zpX_*{M=gI`$D5{8PRT=(Gn5NLILMcV`d*J~-y?RffVN7{IWit`gIu9CJ(a?X&155m ztWdwVw*)28KVq80KeHCTJ7Z=&@dP54Pne`CMW)F(3-Q)I^DPvGUIBX;uZi!}lB~rs zm1K=lC6;8D-iP4UkQ4oetfh>!BwVWPy1J{l{|uLQt}|6B$}{;;hmuDyqmjbP7^z|@ zrCdm(^~PG5v*yIRYKP@pKOdG|1!Z22S%CEcAO+F+T*A(*gs$ILZOq7>&Wy@8SoY0@ zZtE9;Y+O%%)pE_LuLiBeu6~feZj*YJwnPl>6g2psvj*W-131Ox%qThv$FBWe@o?o* zrGxdy;4k@kLq7PfNWSJ90StGqyq*jK69lD>(WLJpQp_V}XlpV1+%;O#v43<!r1vE_oZ-+*jB0t-?cS+OZNOVt(_H5_;Mq-BU&Hpw%^DNoCj6j|$;^(&(Iyc% zr%J%Ei+n6}W=Rmn#4{zs**SB+#ze-;o z`bpZpH#{se*98I{ona2(8OKM@t7}PUhG*N&mt*Cfj;f=J#@Sbdi}<@Tq?J%?88n?ZMwIva zI7=TG(SsKsKmTD4K?0$L1oGgCYr!j`fUOoi*U>?h^(FB!D0rr&4#9nI9h zhB)73s$B~UkK3WHb^GRgUuCa+q_UQ!f0E9uYm~T=K2|UPGF>9}iF<=0i10U=WibCt z=i*xn_pgQ3PRVuS0P8z=>@t!}AbT!S*_tz=#{$ekM@02yF~;OD(oe9l1u%6SX-P)W z%FwUe4MbK~^W4*uH{ui;N@nEF$_#)>Z)94CTcl2_FZeAkIf!pltt>WoJ#(}Ms%INa zm^&t}@0K2#nhKk$9_nor4T?8=0x+IH*?uqkrdQII zr0-@^Y0snlXW3*K33O+V>d$SvhUTV9&i63IRy2i^&`D1=hKgwVs4c zef0jO)f65WnUe}57ViDZY5F4pEy9rEAA56>#el0qCd+ULPIxot!EEEob=+r?U3S~O zo`T=P3A488f$~orH-WxE7WU_5^d)dUcCMMWm^XkuOS_f6G@rEA>vw`p~mo+&g4 zJO6{bY<{a*b&cp%f;A%|d@WXx-&B1gIy#=$=DE&r%9H8*BUyn(l<*CN6^Wgv5wykH@V2IixYXcVx|#?_-L%sh@ZU zPH5an1OLP$haxDcNi`@6dLjPd*L8vdJ(;&A{@CkARk4h(s-;^Hv4WEK0*~}N_fgS7 zQ@Ky0&ACG6&&I!mn9Q-65p!L#P*SC<;FJ}_<1dhR(n9dhV5Q)pbmptD)%;!sCHYvnpiP5SWz$oEq}Fex zTuoQs;I5!zWQ=&5ESSo~N$||IJ|(mr_OzB#&?J<>9qTW%f%>{VHNv1fnlzD{DzPD{ zG?7JbH2>A7b3Jb8dfrzh+ums3d?@CSt}&)5kG)>KtyiB$^E|RQ=xS4zqnRV-oQO@V z&)&wuj?`b)X#uz#yw(9ljI>2tAi}==?ITXJj+8La5YK4`j3$YDIlRTwZL;mI*cG0G=yo~a3rX7U#5U7y-km}t8H44| zZy6;cfHK}8{s+Wr#tkL!x$+4n%?7~lR->OSf_*h_Pe4(3skO@N3W5u13x{qBIzpzp zV*K%&pw2YW1TuJBKJN4Uy+w)&m7J||jwd%-HbUv%K z4-ih2(2i74ee!2+LX_HEc7c*LyyX2}8(z=p^L8Pbl0WYAzmK6MPy#pOdOSfY*%k{#TShLbZk zM(DP6x#>Fmx>l&jh@3^pOGkJ~-^DoM!$U z6E1t4tBLvwroBb|_5oz%9St7&ZbnV(i3L2*qU+#?sr-4DHrh9GeLJ}23GwlIu)rS# z*d)H19lvxAho!bnesr<|xCygM8I1*q>(hp6jk_B#fBFWzLBF@Yr{2S#;KF@9Z*JN+ z=_KJ`n#n8)5A60h^z}0OnR{W#13F37-@j*jPuQ616lCB6s-eiN|1QZ>RsY$&=gH@* z?pZfes|sFqBJ*!yYXt6VfdrM0=MwqFIi# z`)-`RQ%G}xju)p^}cc%)mHQUb-nj_X})Jm)$EI%Q@W^v z`lu>FObbwYs0{}~g{_5d8Q@J9_qn}2$pqxYL}+tLRo<5ED}J!GUpv`o#KCY9b0 zvmMPTmhA38P#Vh~}ib76WdIe@RF zOhRK`CYv7zReK*i@c}gmN-!-zsAf){r6QxX6u^xiUF)rucJM=+LrUGRhsjHy zNT57Dd8M4xeY-<&dS~Du*%LR6ZQ{I>+~}5d)GQRnKXd-3Q9E3PuecQRC%z?Vy!651 zr~X}*x+kuWj%QF!8%w#`2Kr?EL%~ER;p@KQ=4t7mYLZr`GdN#V+Bl0RF5%;-akX%$ z3tzpvqK}8$(!wp2Y)%dDQErCk27D)vu41J=^K9i*CJuvl=l{}pK2};E8g{ut&y1qbnREbItpp%D$A<1wZX8et*IK_EkEo z^p8X=!t5WtcJ5XRk0=}DKta-^E0VNMh6X7GW?8GOhmT4d6v-Bq3g z(Q2Z4SI`&F45Q#b);8-qCD#GY%`D78i4R?ZLJykvO13xalq=@A739k_8P>4QUO94b_*cdv@7ea`-=cu<-2p?f4zY-B7t)F#En_Yz#7yiq#J* z-qZ|0w6>`TR45r|ZwNH!&y_As(x2&7BF)*Mf3MwyiCIPQ9M_)flemz6iRPEoS;`ec z^_OVJm%Fj9EPU77x7>8o9cww-xH`7`DY;P*d+nV8kI5Vek(J^!&0ckbCaYvY@B-`? z?5`>y-beubP4^S;K5;taE74N_2~7TLjJX2wo~>o}o)7b%)84h`M1T(8mLJDQQ=ofHl_yAsaYe; z4n9T z=8)Y{YcLIdfP+_>+N7q?qd}8*RzMUeL9s7)BlG!T@Z`Nd{;%$jrPSXhrP}E6F)h>M z==Ug|X!q!z8U5T$5`IF;GeI-iKQvXKn%Fb#{b%*AG04f3i-1xt@H~?27ejUt-)-Cy z+@26c?cz;3RR-Oj*Lw=X?rsVD5RWl-@_fY#M^+mn`NMJlY49>^TO6_~)RBj_#HMgDXi<^8&FS5SGQ05@sF1a}o% z0E1ov5jFT0DJLl$STep%c}lp^F8&kiMp%|GqnSOa6f?$QRLEdJ$W3HPgK{Q|_KRlj zz~-)pH~Kq6n<*|5X&qSk$AH+QHsxBJ)*=yLt;gFr_CAeeNJ}NBrhnV0eDU|m;mOJz zKkfhx*vSII6&NEa8BmiH_GH+=zc15XeVE$p>#qc*jFjgab84T$7EyS65jJZusMov6 z2zYHU!NVSg`o!MTU>Ahkgu7?;^98p%Rr^vyiTb(K_u-e1ft8Q71>X@NpZV%TyA|7j z-h2AyW16m9CR38O1fk)zYrN&SZ(ktS%L`zM+3QTXHzx|L+#d1H%1jQR7xe%-fzm>= zu8-e?yg!mY{*>NDTWW`*BM|f;LqMNzkp(M(Gs6tbfd$_rsV%=wfqg==k^eu&_TTY! z)sEm5P`LDcvYfRTgs7T5wr}j%1iC3UOAaGOeeB!eK#+G3BLCH4|IvrjjS|sN-N?Br zT!0Y)$p+TkZCI;jNK>%e9zwwJ?MhC8j zyzYeU@)lEWlhy77flIMqB^=$9@##>Zt>&a>NW$(N@^^-H#1f^nNB=qeJ4UD95lle} zt9i}BBO-`$5*KwyZ%1LrV$#@~V^(t6Gh^$-$d3NABRKH~{t?7A?oS!tWYm<;DSC)# zNO34|sLsCjj4gaN({bq_f~h>K%)TkxUpz4l86`?w8&16oDt1uj1jzX)DoD~Jt+}=@ z^DAOz7C%#NJ1$WqbJSG8rTr5AOaxsT;u`iyCNVYSpFhhXZ>kcdjLwk7b*x8DI#tiP zzS#<6i$_8`0ZP!Fdutn7`B;s9WuI&*!D&m&B_0oXZ8Jh~i9_d39rG&B;#nquo2~;n zw%0y`;4Q+cjQi-DfRs?l*pxatp|9dfu8!o0z5gY;_dSZD>FouF2%Z7^jf5 ze-ZXj8FrIvcSG+g*)=M6cr6IO(I-u|{zk3vr=RmuSey;pLTN%b4Mk=gx`kG(PQ`s#(Tm4cjCLWF=Et7F(8CR$9*R zvs>g+r8|(-;%JPe81*&GrkBTNOn#vjqa9F5o-AXi`lgvJE`p(_@RQ8on8xBSNo)Mu zUl$!;PJj(D>1YR(j#q3HL+!}wqU2OE)r8eQ-5t&}VSn0~mu9)(TbHp!w;i#}b!NA_ z3M-I5H^F};rPafKctwlt2w5#^JIFwQA&dSOIJKmpefE97j$AR8miVT|pZwTczNu&S zX@En+U)RtiU-CtT>P*s8O4o81_CJ?_*eb#`IYAKrQ4UFf{nPq$`DI#z5w(Q7x-cd; zy!tbbD*DwnkM8H3DK~x)A-8aHIyI``($9Uf3-9ufhrX-C?^pY4G*;6f;TflW~S8g^&>bA>c^Smz7l=GBQfyzHJ0aZA7FNs0*{$*JfrLQxK?ePjvV`7+hCWkR6 zkpAz>{WL??RjEaPlYd3{v9uH9Q~cCcdk~+(MYhinpKvvlFGJNC{0GyAw~zP(f%i25 zb@L3z?T667u8!gu3;O)+FR;}NnEZ*>75wARO%AEXFoiHYQ8#Y3J= z@7he0QQe3`g;+(D#)XK+;>7+#LRs2pooaLeYAv~9O+Df*vzxaffp>~`)Km0Ov>R@2 ziLhT}CnS2+K>m?yL^uKeh;j7{IR@`f{=) z*a9F%OlF>{PH9Ie=YW7G!Q?^k1Cm+&uDu$!>>k;$%@1cCUnnvd=8Lu&pK+;+RRe-;xrlP}Nj<3xpUR0-hB5GLxh% z?z~2gsl53N;a}!)lt?nUENL&szO!9|p9*ZCt38T&X4j9b;e@!Q6od-x5z>nhs>Zi{ zs|QY)3+ykDVVEQGh*&gXQ8~lt>|(ou*Y0&;g>WVwW54zJGuSFldmnkLUu9okKNRS) z-0f1F`xYnw&Hap`G}cMp8579SkEoks35Ik!lpCtG8I-{nhLqglJFN|beHRqn`Ho}( zmsQCsz>i4Xxonh&nw!u?62{dWGURCoVt<*n&`9k>mPw{(o&5uYMl>rG#2DuWr)A!a zsmF|uY3S=Jer%^kj~>_$wBbggEK-IyrIiR&YvIjr2jeK;Wn;|*9mI^j+1W5$v+KmP zJ|fcW!rDtHoyZ@72T(YMm$vngGfs5ME5@%K$fxzk5}sx=&BXrQ9?vvtIB~5r28))w ztmph(I7dl|uv9ZOhm}EUiJTUXi;wav_>-)fmBWi02*(GLbo2&li`d%V1NG~tb3j|1w_96VPEx*rcZ5_C z`?4r!sY>~EQ>O(nU>W!|CmvOh)l=wfTGmuAH9d0e%8D;|{@n!!rRe=@NVh66|%NjLLMFD9C&|!l^8>^h(<> z;{vbY%>eh#qJTuFP&s)M?R-)WEuQ^#*JB@%0G4E9~%_#Hz}+Az-=L0k$R5#V~vi+#DN8)vWK;e zq5J#!$8t)b!@q!ur{*!qwA-JCdwTseYMnl{1>S>74UYwXRbq( zH#rrWfLGdKh)Gu~TX}A?#BK!Ok40uwoFtF}%wXjUh%?U8xkUy$5D)B!flQQ|k7QyZSKzSScXB{}$VS#!)b5Le3( zWzC>~`$!zMO3@oZ&ZXF>W}XzU;b-=VV4s(8`k3zt?Vajp_K6U zUcs;UFK|fpbpP|hTnd`gI%Iu&Q1039DPN4@jnza_Y4wp4B_^Jox=_K{iqPlPCwEHa$MOVrB>L0pvC zAbBY2zocHR)PeFQQzKSK@nMN8yw=s_)0b+w?-=h;Iy-T1(NnxJJ}oO$X1#*QAa%%4 z9Jd_PB1xe>CQLSWIM#l0b8GjC5y+)`^YNQYY=XZlw})wO!pjC?P5Z^uq^R3R-G_$7 z)@~a0NmyRfpH5D9Jvh9Aeud>`yVG9WBkoDoaQk)Ue}m@X3ZgdK!}jzN`BLSD!ssKy z;pzj`{+`k(LsYRzzH3n`*5pJ#ETU&pX!U5w+Vcu^CbhL>NQ%X7ao3=14E)6oTz%wq z1?r`EvTYw!N4>N2f)oU(1d{E;P_!on+?$R zPy5pO5LH{8s+7skWpQmuaaDBe1vm?$lV+7l%=j>fZy)SW#2`X_i}qz!cCIdblJoho zO6-;3WYlTqNk66im6rRJYBsy7Qr0Z~o|@NWpKqmYs=N>WOTMj*?Q-W+pSd`N&(GD8 zt&IVCv+5s}bInk%NShIJ;|``pY_8GJ0x<|vo83A&)ye}XGdB$dSvy|>zgfumA!N{B zd-_f;0R+VkxA9JB7!%+(Srb1~C^bRUKeX>Xdxq=FroZX)&7c4V=|Zktw_nQ*GzYPm z*3pEL?SLFZl!LObE&PNbWH=G+D&@ijAJw?vmqtDH|0*JdA`hnl=*13Ft8hMRLGLlP znI>~jG+Xnug8mC@T0T6Zg<}~E$U#?dvS;zgZwjqTD?4uni<_>{&@t~3L2az#+&`ca zcusvhzk30Eb))PDPH3M8$0VvB{6vo*f72g^=%~eB<}WS@ag_uqxgcYnBZ^yk8$x=5 z*T}&ck`vBx6y?yk1k-XnNb~oIwgNQ?j{Mj@vI9A`u0KrW)x4>vaBBEkS^a4)28=*@ z6Pm7hE#kDD;pSz+FS$X8$#9qmqdpF&dxYNTfywJsUyf+2RIz-9x0~Kcd#r+fxyWmS z2)3#yHC+ns<rdoEyXRyJhSFHouuCAAx{YjkG3ss3fY4l$@UH{{UsSwQ!0aL zy!ZO~6|u;5KQ8z_>^3B*)Y0%#7nT(_(W;YAhLGJM0&paP?HOv?QFSOB{aY zE;eCiF~LKW_P2c$G0fzi_!@0&y(o6p2cgx}FPS;n&S+zOq<)N;Jrc3zn5S{>GAeE_ zZ4xmJKtnhE{Eo$V-c~#_2)kc_h z?s@62U#?E_a;~#j4EeH76Xi1Bhg_dJI`ZpE+F^%9fk?F~a6v_ovY0>qH3o#>k0x7R zkx0>rMubJZ*vCkA6v$$zDu*)?;=}hLqi|%4ayLzXB&gD$xs2V`IV7kQ3%Q ze^nwuUYVcAVL*lEBY<4zGjeW14UUB9NVFFpgi6t4!Rk(skl$UJ&w38b#Ee#L!_|4( zq@4ixy~|8aJr@sDuae*UU+O4uV9{)zjA<9N4N33+inpy1avB&jMz2ezIE<{y4m(hx z`63huCjvd}?4Km}YH&KFaE%YxktybU3*%4bf|*O+%ZyNU(p~k`pk?f_5TXpAIQ1fG zd!_B!I<<~L2k`YJ%fWy5*SH1JvqTb6a5WN_&ZGRi6cwRb6#i0^Oyp%Bp*Q&cQbh`5 z?<_ok`xC z+5R68sD9dE8H(;Asfk+>Yaqd%AQXR>iD2c!>N5lC2-OxJo_r~VYD-Z<5!;|TV`l5Q z$DO4qwXC@3)+{%FHPLN#xa2gSjb==Z-lV|=a)rJV1d5rxn zVY5R~FkNT~(J>gbOI!bs_mZW8Sy;lpUJ0>Tiu>eeV!Wjt#TyNf7%AlhkU(9vu*!@g z9Fu=qI_jmIuHuJ^ffO{`9=2ri{zi|X1Zk2$iCqX5Eo(`%Lu7Nxm0^uT9C55|PS>li zvO@-sX&WUvgF~CR?uW+n#UW(jUkMDku1 zK@|i;Ybc{TrVHaPqbA^tHw+n zlBRBjK<=T!<5!j7FK((@;UVgxD??Wd@-U3=INxETf&%&k67lo2a@ zH$V?pMiNem#r1CEwFtGs?zM+ysB)CzKK}}eX+pfMKxlNoiO(Oee(dif_<;)+Pi6G6 zb1Caj+?2KNAc!%JK-3MXovMV$XiBSbLiZnPN9s-Cj>Q5KhO2tq(&zAJkO|hJE{$A2 z8NQC#m6cy+7*olp7jsO2lLCEl;!Uz+tC9d3gm)+rCCg^G;$Xaiqa@@z?26wakoTB& zr1~O*rq^&25W|A%go^Vm^)D*4Orvg_SIIN0<`(-+YvaAmNcF7OnPo3irUxBeV$=eY zl!j^E=~U-~*+tNfOGsivz=Ce_aF_?2zertyVL$4W6_Xu>%9i2LC}aYyzYE(BNrFf_ z$=Nagx|er|zlIWFU&PK>*M0qo9nqn0FRHWvH3XR|j-Tvl4r!S-YMp>rnc&SXCYSDu zu{u^_{ROL!hTxq=j$<)`%D*}LZG7ws_xle_vzSS`!MYl8XWy;Lcdfgv_CsHkJ@>|Zj}kKyBt8=3=-kT+@$YN2+Z$1*e-}v z4a8j1=+|G4=bPLqbtq|pR=W;IHa>KCF!9UYLA(eNBJM zkjZ6;r1aqOx5&JRo?x9aSc(49zZFVFToc>3vfAR z+*A^bI=A%PZBilnwz!oO4JEddWzgc^|ukA;W27pKT|Zph><{A(f!n$Go66 zFG$*}s|;2#cZ$Yp4Vo8N)|DBOSuxVJ^<|8GQOXQMik66fb!*@%`y&Ph<9EV70>3zn z*gUG#lWZT@pGWcguabvj>~ww{+;qb6rF|qDtGdHI4m_>Dk*_QEC~dg>{jQOZg&-9l z*?ZM1+do1Z)pSaFOz2rHk0$&foThiK$TU;)ttI;gOY}K8`aR#@*NBqMy(9oN>s~Y3$RP(rp)r_)(^_l z+HgHT;c~7lNmnX;kyT@Sk1S;gtEMsZcM_selQ-h?S5`(+h&vyink_OG9HB-5z{~58 z@8x|aUjuaeit$!%nq#30C-8Ev{EAeTlyVS5xvo7AxEMHPaYPG}7x5u<|cIt4a68$ ze27`JY+mUHGI77zxSr$hoKLk}IPCv!TLQ9LE|RXt?i&@|1dnRygGgqwa-73|ELd-o z?M%3{4^xzxa+{(W4SbZpQhjP4Z(c&B+6ki_v#U*DzRh?5gma*y_1-e+p2?x#4&;a4 z2JCay`i9Qb@AXq*0QzL{x|`1M;@w7e-pnfC_1=fG_WOr|5xfA0JKbf9ibBPz!BTGF z&L3wS5!0x&bdrk_d+(r~P2PN3b!MVn0cWHGZy5s0bazpot4#Gs&E`HEClYQ#V(y3N zKL*|WB1cVn(FL>y8W58QHB=WfvqLd6-q;3Dp7gt6^rJPs;kK$0N1O)In^<4S3+qba z8j;Z(FF$UOaUnlCW&e@t>dD2fSOeLAuwrl8^^^)t@K$Cl{*aU(II1rvIS{?SRrd8g zG_mM^Z_uB-=sP(wBMv;?%#dJBg;26onhj)GUgu+J4{KW6{NtswRwhszXtub+`Hn9R zj9nDdF{R*=QHu_G;gKM6Ep8BcG!!Jz6vX(VBj3K=v94nH?RDS&_&a_5t+}{Hqhfpk zVfVT(GbfYaXdU%g`iZX(XL=Us?i06$8TjKGg>IhGor#sl0a1L2|1!SP(pSJo91VFl z5l=Nh%?TKWeI``y*2W!4d@x1yw1YA5+%W{6`3ho$3^ERbwA2-17q||e0w%?s&HKp{ z^6l_Op&PW#lTUe5SLJ&ib`w$N%@ZJM(9}OpPsE>V zcx2)fB(J*@CGbYaCTcVD=41h%a}!Gbpgo(d-1KnQlPEhJyj5R~8qmFwYY_L(2K$(j z5UtOv=j@cM)xEl)2}|I(HoJPM!gLit*sLLqld3(_$N+jUT_YGq!@@iPJ#qjDyXP%q zR?%v8>$#yjqsBcPzB^>yw%NB&W0RBU9!l<)a_|a8T*) zSy$Au(}%MNa4laU?weNSf-=C*eG_+`;OpOIP<#-iKhbjSn>ixrIwaorQ(7wyDEr)7 zH-UN}oN#P5KkzI~+B+(bC)XlDYoHg6v$V8)4lZ_{TFc|+cZ&zSzk=CX+W&WQaIs zNbD<)Oc14COeeGOhqT=!7IgkQb^;`iNAve8Ut4w8oPq8mqcVBq&YotsqI*Qm2P=HL z-;KxBHCcg50+YOUGc8)$25&u@8A+UpCkc-%@2oP`JBB(@L+!&6jY8;a?&Av2;9KT8`@6_C+E|D3N0Az_;sFq%EMa}4Nwtz%DPxJNgQL@_TA}6S*isXm?Gcq3fy!xctl!kBltOP5`s(K^MH|u|Z;IJR zN&TdBYOM8LNIOq-wgS{tTr%wfbLun3epDk9&>`=z~J z9G)(4;wGG#`5o4blhBm7>Fa{sCw#~5vCgE0&=gbOhAbPQW6Iyg9j_U$fc7ok?(JuD zoiQilAfubTo>rEua@XoMO^4Y`s4b<1WA2nFPjnaP92+4sww0(YDS?^mJJh7jHq#pUf1_nR-oo(5oEh$Mjz}=M8 zFZOJ|;toT>{HZu~u>09`17Ge*Qk7_ULTYKHEnvIl^=@PIIBpxK5mR7?_vEL;3W(@j z$I53^73eCfKdt0~-@_O*C-J_HddA9lvd!Qbv0V3W^U zsQ99h6BsmlhWY;dfV7Ttl$-Kw+eFndo3$rUE3n^8`!3)N=sJ;`imRv3usTue(ilz3 zZZ`6xefOg~Vxc6zCm3LT$5gn-0fFu;TwVPJ z5Y*_`l*K)O5PEX5;d0epsu;w5DD0N%&8R=`XXmpUC_llbw21HdwY49=j_FhGnkJUy z19y8NGR@?2X8%U(RkbQ~pKHrzXE91#+3@o^^w99CpFH04wmIL!pXNtq##DXM^OV;x z+ymh$?d&JBHO>PS&lk_LF9F+mE(mMw9Q`$+IfR_d?=?AQ{{Zxi^on{gk4N`e6KX*- zqzr7=;R50L;cwvvd#gdn{Cdm6uw>DWSBwJl0ZulSHbSasj}h0B8m9y&^1T7S#m zcwHlE^-r8SH_)wGweW_^}RoJ>XJtf3!`?!BsQvR`rBIlN8E1)i* zCXdWqr*>b7Vr2`ToxA%bY=IC~?hskG*29 z8fT`7e9cj;{8BUAKSuWWu2WlZ_Pnh_7BUUkpsr~vW7JZx@N|}0%)Zr^s3S2jukN=` z@YRfJE9t4F!|g&q zdxe6GZl2QPHcYIq+on}5uP=nBujbZR_0nXkXrGM|XYWbt@vCsFv|bE3K+BhSi6a@4 z`bR(>8;xCHLyo^}pd>Y>Had6Fo1>4K<|tK2=D=IOZl^QdGG?Aw!NS1ugj+h~;15X; zqpTk8TFu$X8iV7MJY!Etj^9}YUZTWR?g$y|8|Jw7S0BW6BA6Wi<`9FRFx z>?5_p|G+0IO?gM38)eXE+=6Sv-P~>pl*yfCeXSDG%`FI^Y1U$9d&`J&ds_o`m(AR1 z&CE>e5W#&dgb%E#H`__5cx3vg5F3vVu<}>ma{C}&z2Vt3_kuH!?!HZaW>tMz51Q0^ zd7s^t+35S1Ki54||E(~V0P8`zCwGvdBj%anR_t7<*5Whm z+lt3M`%Q_}zpE2YXX|gP@51(hm}gb2dPj2`W}A3QSE(IQdeqCq(4J<{j>l|wk~JF6 zPQnY%2WN9WbnM^a%;S-!rv)=t7_(0U_?(J>sw)Jszx^8V?cwJVKh;08TMpXBnPRkJ z*eue3K-sxvKbu+^eDs6$WI3&A1SjaKYx$kXD zcq2*3V4P2ENo3BUhi0w}PTG1@!k{(ghkQUWuhwC`D5bD|!+|w`*G&j}r+KP&KW94V zt{*zg!^^JN}t821HaRD1yMLSN3wBUcg7}y z?1@mb#ANN+%8NgO*Ywgu$9aQMLpLsYcbOzR#J8E@Aj@P{U7wXywbO&{R;c0WXSZ)b z)3&ug0D0ErvT=D0#hqX&J;-Vw*OE3q&Q0(uIbc=pECFl$M)^x?eRY_d3Rh+qc5PNV0b)sqm z_<&Im!gN2n{_W063Pcx32m%2IN(a_(}3mEh7rN>$1og$ zz322pBU_Oc+iU8RQQGy3w>CrVe?Y!alDPgjvliGc^{!ZB!Xvbe zh?|XHQJ>^gaKoZ}krObk0^)ijkmva5`uiJTxyO6QBH*%ao~$u|DK=CL>l(wbe&XG|ZZ zb&~ty`l&fRBW%NFjfNn4tY)aQH^{hb`atmtAcH!Y5jwt&ykXqGNuCz9jxYMuBuVXz z*nqgK5wCIaYd8b3eG)vEkL#-gld2M^ZEJQ15qls8kKD2u`dQS7SDu1_#wRnE%G;a> zqbOWd%9brGDV4?rM&42xNvulklE{rCb2HXv<%uHO*{nI0jEUsi0OkJ>{c({C`a?SB z`?S2H!KgkU4Vxa3Z&=RJU@(~+RNC$UajF&0dw(-)U`q4CdXQUUuW=UkHJ4k}R{FEt zbqhcJ?`rB^$38E4Mk_-r6^ujqHfu%aEnIELnKU^CkwC`2kuY8<4-L&JHU{hb>a44t z{>h?2ti3mDNtHHL2cO?Yn|f=#dmlybH{$d{jPrShTmDXhf5bJwd_}ZEozn^pIgcW5 z`*^*NrXQnDx1`mGXRBho$mv>J(A#O{N0)2yGp!iykVrLr+&JWs)|8CyoabPp_WI8A zU>bv=doSoof8FBbq-Y53XPkNnZCj#8v|W!~)w=qI zRe&LmuviQK0@)eaHB4y)M@wUQ{#|0j(o?Tp)T^m`!m`DZg$viV)pGOB6xD`{|Kq2@ zjNNcu3DL5lukm{CpKlL)FpzV!wvka-v`R9=Ljoqwp>F z-?OJqLY0fjUIL1BmF=|5xtgf(l_6sgf=@JSC74u*Z2KohM!}BzbLjHo{DKZdnmaw* ztcf!19;sOEPsqVPXleC4yI*fwZ%SKz0 zk%1GSGHC$#wYr`DM-MGx%s*Baq}hO1nM54|dyxtUx)_ETW{ z7sUsDJ>qvb<_GbTZzd5EF|B%G)TgM=WGL5os=6FhEGUiCv@o=LG>*k>E*LDzJ^wQh zs4ua0N0i;eE^?`+KSnKA?|;CFvz9 z%RBGPOxVC>L1NR9m`=wDt$$A|+ZE4qSLIv@PsocQ6v~~_V8O78{K_&U(eTO)CcvL4 zPRLD)OS?`KuB_%dv+1rEDo0uJcM-f9A*f)A6_B<{A6D#uQ-6lM6~=Vw zT#hejN^<8<#w+F2Np#uOP=)-P?i;JeRG>p)rdm|NF?(NbjcthS35)~_9_vAPy9hx+ z!&_*#&uLRkJ9f{`N>Kx(TXy4Ay z(06(YvZt$mxQ#J%_)AaE8BRa~ffiw+C5c|6vGOtCsi^tK;~xxUq;zmeC|& zK?S7E9rcv=)e4+LBWuB=i&nEiU=ZU=p{^O#bWdnG51J``(PdhQD9hv)i z*7mYa+@?3is~oH*f4xwB7BR#|8``PiCIL=)*>KkcEd`j^fRv~&s+%$QWqns;WjJBM&-laVyhjfs(CU4V&D1mE8 zLLayh1*t%-ltl$Z(Whw-vIaHjCM?E}WVUe4KG;4~IurFETC-j)_Hoic=d+>yL6<&U z93wtW&tz}P+!eg!%gt}8b_nhYII*IkuM&I+7Z{^;w8Q5r^IQS=GZ3|&+nD{f_5f9Ht$!hoiEz^ ztM;n@$^3l%SAVYWh+d1F_Se#9@>QI}#;O>#j8n#QIEPTAr|i$5RY)I<6;6v6XambB zYc(3QU3VN2XF8&ssp4;r;x3=~q*HR+q;{^`o)@Ha*LzWiq@1WZv90A)&v!Ae%Rbw4 zj;}by*p5_KV1-biR-zCC8{Wrqv_lW{L3N>oU?q#CO>%R( z-Fe6TlblbyWr?zEBwvm|`WZC00^Mg%uTIN&@md&e^^NdnAzgyl0 zrCR4>wh{U`)M+{E!CHQqbuwu9b2T=oa0?P`$P*>CF>(>u1NZGLR2_~o`G2_uWOb2N%Cn|RZ)#L?1OQ(7gBl|}lb zkR;Y5ivX~{-bvvCCPD&5Wz_nQui&p>JStN+txitL4J;>VUw4dWfV4#VNSZ*7*xNZ) z2+VuivqU7e`&n+`j+89lq0(mF|VQr$pO+2z&ZYTq^Io9 zAVE@|^b;(?U{XasWf>_nv_Iq6AnbGg>?tQTj-oLqyF#l2M%BTtVBhLlF1{*}wM`IL!^vWu1L}wvU zNTUmF*K94NdoeTOx?-N(-tvV|(LLO=&hkohQcOE*d)Hi-Lr$|)wSFp`r`_mC@(wS~ zZ}RQh_u4l0hnl6_GXkVGZI4f})u@1jk+%M)GctiLBfCW^rO}yI#d=*V=Gy9BCijlH z8#~%ICgEhUC62aAen=>uIMtCBb1Lp5TZ(5^)DPCD!_l%5>w1xhRw0wVi!x{)5k2C<>!rLAaPx`vbzdP+g^gS1A@r7?84P>vL5 zihi0`Qqw~N8A36^T;qOdL|_2*h?7Jc=}Mmx%Hm}#B`;th4&X8GqW^6yC7baiJu3{v z1aXo$kbEPJlm}3cb%kx4aNm*b8Ym{X6FhgM@==zU`to&GP0tl6)zZ^8TgaBalx(yV zZ76uj3VN8v-~f7{`hOg-{+fxLj)7>38ZTUfY&k^knS-xyCLp4KCs zF^_yAzC$jPW#m0t21)#9e4}*s*b?ap`xiWeZb%}V$&aKvSxd8sSLh>n$V4GaY)1M^-R0YKxBR+wfiT9B zZe1okVfn$DDO{A7$sOp+bcFCW=|o<^SEz*NIGTNNt2sn%R(iz2n%wEZG}XNO~_QLWJgts{sDoxGgXd78P@QnjIp>iUVq)^|iTQgl6O$su448>Y5n=|+#H0u@ z17>1UgqQ&{F)2dKfSH&SA!fi#Oo|XQU?wI-$o~Vo?*y!Df?X#70000 C2jJiU literal 7909 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 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..9312aa740c64f9a78c0e31b3411ac44be342e278 GIT binary patch literal 12675 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 S<7l%e>24cY8P@1~#{LI>g$B(4 literal 11873 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 DTZg?@ literal 10652 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 Kx8kAQ>;D7lMe}9= literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png index e96783ccce844001359f968f5cd9d85b341bd3fb..dba530eb0bc4bafa5c31f4fa042c2e21d76f5cfd 100644 GIT binary patch literal 35868 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 diff --git a/ohs-player-reference-app/src/androidMain/res/values/strings.xml b/ohs-player-reference-app/src/androidMain/res/values/strings.xml index d789456c..d18a0426 100644 --- a/ohs-player-reference-app/src/androidMain/res/values/strings.xml +++ b/ohs-player-reference-app/src/androidMain/res/values/strings.xml @@ -1,3 +1,3 @@ - OHS Player + Player Reference \ No newline at end of file diff --git a/ohs-player-reference-app/src/commonMain/composeResources/drawable/app_logo.png b/ohs-player-reference-app/src/commonMain/composeResources/drawable/app_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..448f291511904a0b8e71ce05c446d48af71b6cf6 GIT binary patch literal 20426 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 z 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 literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index 220e1ebe..c6c95129 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -8,10 +8,10 @@ Non-relative - OHS Player + Player Reference Care coordination, offline-first Sign in to sync households and patient records with your organization\'s FHIR server. - Sign in to OHS Player + Sign in to Player Reference You\'ll be securely redirected to your organization\'s sign-in page to continue. Sign in Sign-in failed diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt index e7fc2b35..0d028d8b 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt @@ -15,6 +15,7 @@ */ package dev.ohs.player.reference.app.feature.login +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -52,6 +53,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.app_logo import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_brand import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_card_title import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_error_dismiss @@ -60,6 +62,7 @@ import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources. import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_sign_in import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_subtitle import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_tagline +import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource /** Material 3's "expanded" window size class breakpoint (matches HomeWidthBreakpoint.kt). */ @@ -140,12 +143,22 @@ private fun ExpandedLogin(signingIn: Boolean, onSignIn: () -> Unit) { modifier = Modifier.padding(40.dp), verticalArrangement = Arrangement.spacedBy(20.dp), ) { - Text( - text = stringResource(Res.string.login_brand), - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Bold, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + Image( + painter = painterResource(Res.drawable.app_logo), + contentDescription = null, + modifier = Modifier.size(28.dp), + ) + Text( + text = stringResource(Res.string.login_brand), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.primary, + fontWeight = FontWeight.Bold, + ) + } Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text( stringResource(Res.string.login_card_title), @@ -223,12 +236,22 @@ private fun CompactLogin(signingIn: Boolean, onSignIn: () -> Unit) { @Composable private fun Wordmark(color: androidx.compose.ui.graphics.Color) { - Text( - text = stringResource(Res.string.login_brand), - style = MaterialTheme.typography.titleLarge, - color = color, - fontWeight = FontWeight.Bold, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Image( + painter = painterResource(Res.drawable.app_logo), + contentDescription = null, + modifier = Modifier.size(32.dp), + ) + Text( + text = stringResource(Res.string.login_brand), + style = MaterialTheme.typography.titleLarge, + color = color, + fontWeight = FontWeight.Bold, + ) + } } @Composable diff --git a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt index c3ede0b6..6962a19e 100644 --- a/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt +++ b/ohs-player-reference-app/src/jvmMain/kotlin/dev/ohs/player/reference/app/main.kt @@ -29,6 +29,9 @@ import dev.ohs.player.reference.app.data.di.initKoin import dev.ohs.player.reference.app.data.sync.ForegroundSyncManager import dev.ohs.player.reference.app.data.sync.SYNC_TIMEOUT_DURATION import dev.ohs.player.reference.app.data.sync.SyncManager +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.app_logo +import org.jetbrains.compose.resources.painterResource import org.koin.dsl.module fun main() = application { @@ -55,5 +58,11 @@ fun main() = application { single { ForegroundSyncManager() } } ) - Window(onCloseRequest = ::exitApplication, title = "OHS Player Reference App") { App() } + Window( + onCloseRequest = ::exitApplication, + title = "Player Reference", + icon = painterResource(Res.drawable.app_logo), + ) { + App() + } } diff --git a/ohs-player-reference-app/src/webMain/resources/favicon.ico b/ohs-player-reference-app/src/webMain/resources/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3f857a7fa9280fd647d76ab6e590626141ec7a9d GIT binary patch literal 15086 zcmeI3PiP!f9LL|Xu{9SHQrraFG>b+sjlq&b@ggK`D2mXV)mkk%8KD;|ZER?atB3X? zjsMylv^gk5FG5Qx;w|7-NKkK~^kN&5>OrX%7228*Ki}CmyOZZkX4WjG3$yUy&Aj=2 zf4|>*^XB!v$<&yP$(rV7Lzy?PHW;(Z7?aPJ&cC33lDeHc?fIj|?0wLf$FSiNic&p= zG1$4knHP?9zsc!0WGk`0x|W z*nJn!1$``{8xPTXKG2Vy>FjMqyAYw5-A3z=kwaAbhho})1MN0A2lKFC(eWg-+5Ufy z_WuBjwl7qE4}-HWzhPkW>`TKrXtm_LI<9@tvfI4hv^#UAt-tx~Lw&72I#+#h;KeNT z!3h|%=(r!U#gSd6c&exE@~NI7cnijCIYj+dY@UGySaDb^?m5_B{5-d*c(P{_mSH8p z-yB~+^RA<-_}O#tMDe3Xa@buD=z>0$(2lR6A4A`Yb|FHqSnJiVH-Gitd`KG>=)XSO zf9J@5$!Wt~&d;N30QpcJYKzdZ>Pq+TWY0Eihv7XKx8*U)4pVSv1ba^X+qx#AeudIJ zljnLLXnyj3ns_39jOlim0)L)}DrM6i=7w?K4pR`EOTvz2Q}$tFN5zY(FYA?yjU5(i ztvb&~!gjlPOa=QOVY}U2JRb@A+>Py>JMmrLiO%ICjUPMa)BX>c7t--p>45}uUg+~^ zpT8vYt~&;6Isa(xcm2nm*KGc^eXwEw{#5Q_dbCkJz?!HXrl7LU3_q65svlySK&7J`Nfs8l}1|m zkT3c4{3{M*U^6@o6Ywqk0@dU%d+DU#3yn5L3*~*&$}_6|GQLjR7(WhzFU4(w?a&2}Lw5|ak*ze+%7@qY!3HRnMf+gI)_zQT8tn^` zB75nim!DvW^f>K-|I`D#UPepl}5w-Hbtzrh`-CV$yW zC%wK0%h?!Rl==qn2E^qWcU?Z@OJgaRe-rWc!9}Ph=P27R@pH(=*iTvQn2BnKKbG`7 z1oPicya|}EQT|u)bIHayO8FN=yyG{qf&LyqOy1u6uGDZrK>4HO$>_Yr)CC zn|O0I%)d0QO7}R;|KDRT?f)e6R62hA=hZFK@so}pcT7}so{pd7po~K>3PsI{U&H_x)!pLtu)fgN8J9sg>`g448dp>$X+_> yz5V;DmjrcqGHtTI^MMfm?TL?-_}y!zpPyfrK94fSE&c11_i8)5y!<@<{Qm)pz8ks# literal 0 HcmV?d00001 diff --git a/ohs-player-reference-app/src/webMain/resources/favicon.svg b/ohs-player-reference-app/src/webMain/resources/favicon.svg new file mode 100644 index 00000000..f648e718 --- /dev/null +++ b/ohs-player-reference-app/src/webMain/resources/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/ohs-player-reference-app/src/webMain/resources/index.html b/ohs-player-reference-app/src/webMain/resources/index.html index 20421019..807b52da 100644 --- a/ohs-player-reference-app/src/webMain/resources/index.html +++ b/ohs-player-reference-app/src/webMain/resources/index.html @@ -3,7 +3,9 @@ - OhsPlayerReferenceClientApp + Player Reference + + From b43750a18988bdc44d09e6152d5e09762bc0f1a9 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 17:19:02 +0300 Subject: [PATCH 57/73] Refresh the registers after a sync completes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Screens observe FhirRepository.revision, which only ticked on app-side upserts — sync downloads write straight to the engine database, so a register stayed stale until the next local edit. Route the revision through a process-wide DataChangeSignal that both upserts and completed syncs bump (one-time and periodic, on every platform), so the list re-queries as soon as new data lands. --- .../app/data/sync/AppFhirSyncWorker.kt | 3 +- .../reference/app/data/DataChangeSignal.kt | 36 +++++++++++++++++++ .../data/repository/FhirEngineRepository.kt | 9 +++-- .../player/reference/app/data/sync/Sync.kt | 2 ++ .../app/data/sync/IosBgSyncScheduler.kt | 2 ++ .../reference/app/data/sync/IosSyncManager.kt | 2 ++ .../repository/FhirEngineRepositoryTest.kt | 12 ++++--- 7 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/DataChangeSignal.kt diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt index 6634be83..4ca4be8c 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt @@ -23,6 +23,7 @@ import dev.ohs.fhir.engine.sync.DownloadWorkManager import dev.ohs.fhir.engine.sync.FhirSyncWorker import dev.ohs.fhir.engine.sync.upload.UploadStrategy import dev.ohs.player.reference.app.auth.ensureFreshSessionForSync +import dev.ohs.player.reference.app.data.DataChangeSignal /** * WorkManager entry point for this app's sync, enqueued via [dev.ohs.fhir.engine.sync.Sync]. Built @@ -40,7 +41,7 @@ class AppFhirSyncWorker(appContext: Context, workerParams: WorkerParameters) : */ override suspend fun doWork(): Result { ensureFreshSessionForSync() - return super.doWork() + return super.doWork().also { if (it is Result.Success) DataChangeSignal.notifyChanged() } } override fun getFhirEngine() = syncTask.getFhirEngine() diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/DataChangeSignal.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/DataChangeSignal.kt new file mode 100644 index 00000000..c2088a21 --- /dev/null +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/DataChangeSignal.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2026 Open Health Stack Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.ohs.player.reference.app.data + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * Process-wide "the local FHIR data changed" tick that screens observe to re-query. Both app writes + * (questionnaire extraction) and completed syncs bump it: sync downloads write straight to the + * engine database, bypassing the repository's own write path, so without this signal a register + * would stay stale until the next app-side edit. WorkManager and `BGProcessingTask` run in the app + * process, so this singleton reaches the foreground collectors from a background sync too. + */ +object DataChangeSignal { + private val _revision = MutableStateFlow(0L) + val revision: StateFlow = _revision.asStateFlow() + + fun notifyChanged() { + _revision.value += 1 + } +} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt index eeb91aa6..f61f3766 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepository.kt @@ -22,9 +22,9 @@ import dev.ohs.fhir.engine.search.Search import dev.ohs.fhir.model.r4.Bundle import dev.ohs.fhir.model.r4.Resource import dev.ohs.fhir.model.r4.terminologies.ResourceType +import dev.ohs.player.reference.app.data.DataChangeSignal import dev.ohs.player.reference.app.generateId import dev.ohs.player.reference.app.util.FhirJson -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement @@ -41,20 +41,19 @@ import kotlinx.serialization.json.jsonObject class FhirEngineRepository(private val fhirEngine: FhirEngine) : FhirRepository { private val json = FhirJson.instance - private val _revision = MutableStateFlow(0L) - override val revision: StateFlow = _revision + override val revision: StateFlow = DataChangeSignal.revision override suspend fun upsert(resource: Resource) { upsertResource(resource) - _revision.value += 1 + DataChangeSignal.notifyChanged() } override suspend fun upsert(bundle: Bundle): Int { val normalized = normalizeBundleResources(bundle) if (normalized.isEmpty()) return 0 fhirEngine.withTransaction { normalized.forEach { upsertResource(it) } } - _revision.value += 1 + DataChangeSignal.notifyChanged() return normalized.size } diff --git a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt index 10269c1e..caff42a5 100644 --- a/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt +++ b/ohs-player-reference-app/src/foregroundSyncMain/kotlin/dev/ohs/player/reference/app/data/sync/Sync.kt @@ -26,6 +26,7 @@ import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.engine.sync.defaultRetryConfiguration import dev.ohs.fhir.engine.sync.runSync import dev.ohs.fhir.engine.sync.syncDispatcher +import dev.ohs.player.reference.app.data.DataChangeSignal import kotlin.coroutines.cancellation.CancellationException import kotlin.time.Clock import kotlin.time.Duration @@ -228,6 +229,7 @@ internal object Sync { if (lastResult is SyncJobStatus.Succeeded) break attempt++ } + if (lastResult is SyncJobStatus.Succeeded) DataChangeSignal.notifyChanged() return lastResult } diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt index 2cdf7255..8bf3ec59 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosBgSyncScheduler.kt @@ -20,6 +20,7 @@ import dev.ohs.fhir.engine.sync.FhirSyncTask import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.engine.sync.runSync import dev.ohs.player.reference.app.auth.ensureFreshSessionForSync +import dev.ohs.player.reference.app.data.DataChangeSignal import kotlin.concurrent.AtomicInt import kotlin.coroutines.cancellation.CancellationException import kotlinx.cinterop.BetaInteropApi @@ -108,6 +109,7 @@ internal class IosBgSyncScheduler( ensureFreshSessionForSync() val status = taskFactory().runSync(taskName = taskIdentifier, onProgress = {}) Logger.d { "IosBgSyncScheduler: sync completed with $status" } + if (status is SyncJobStatus.Succeeded) DataChangeSignal.notifyChanged() completeOnce(status is SyncJobStatus.Succeeded) } catch (e: CancellationException) { throw e diff --git a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncManager.kt b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncManager.kt index 89bf1df1..8caee56b 100644 --- a/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncManager.kt +++ b/ohs-player-reference-app/src/iosMain/kotlin/dev/ohs/player/reference/app/data/sync/IosSyncManager.kt @@ -19,6 +19,7 @@ import co.touchlab.kermit.Logger import dev.ohs.fhir.engine.FhirEngineProvider import dev.ohs.fhir.engine.sync.SyncJobStatus import dev.ohs.fhir.engine.sync.runSync +import dev.ohs.player.reference.app.data.DataChangeSignal import kotlin.coroutines.cancellation.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -125,6 +126,7 @@ class IosSyncManager : SyncManager { Logger.e(e) { "IosSyncManager: one-time sync failed" } SyncJobStatus.Failed() } + if (result is SyncJobStatus.Succeeded) DataChangeSignal.notifyChanged() statusFlow.emit(result) } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt index eb4c4fa2..52f5e0bf 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/data/repository/FhirEngineRepositoryTest.kt @@ -63,11 +63,12 @@ class FhirEngineRepositoryTest { .trimIndent(), ) + val revisionBefore = repository.revision.value repository.upsert(patient) val stored = repository.get("Patient", "patient-1") as? Patient assertEquals("patient-1", stored?.id) - assertEquals(1L, repository.revision.value) + assertEquals(revisionBefore + 1, repository.revision.value) } @Test @@ -84,13 +85,14 @@ class FhirEngineRepositoryTest { """{"resourceType": "Patient", "id": "patient-2", "active": false}""", ) + val revisionBefore = repository.revision.value repository.upsert(original) repository.upsert(updated) val stored = repository.get("Patient", "patient-2") as? Patient assertEquals(false, stored?.active?.value) assertEquals(listOf("patient-2"), repository.all("Patient").mapNotNull { it.id }) - assertEquals(2L, repository.revision.value) + assertEquals(revisionBefore + 2, repository.revision.value) } @Test @@ -136,10 +138,11 @@ class FhirEngineRepositoryTest { .trimIndent(), ) + val revisionBefore = repository.revision.value val storedCount = repository.upsert(bundle) assertEquals(2, storedCount) - assertEquals(1L, repository.revision.value) + assertEquals(revisionBefore + 1, repository.revision.value) val patients = repository.all("Patient").filterIsInstance() assertEquals(1, patients.size) val patientId = patients.first().id.orEmpty() @@ -198,12 +201,13 @@ class FhirEngineRepositoryTest { .trimIndent(), ) + val revisionBefore = repository.revision.value val storedCount = repository.upsert(bundle) // 4 stored: absolute-fullUrl patient, request-url patient, generated-id patient, and the // group. The 5th entry (resource == null) is silently dropped and does not count. assertEquals(4, storedCount) - assertEquals(1L, repository.revision.value) + assertEquals(revisionBefore + 1, repository.revision.value) val patients = repository.all("Patient").filterIsInstance() assertEquals(3, patients.size) From 296ff110929c1e6b642ef57b5faf8750d7e9e913 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 17:21:06 +0300 Subject: [PATCH 58/73] Seed the theme palette from the app logo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-key the Material 3 light and dark color schemes off the logo's four tiles — indigo primary, blue secondary, light-purple tertiary — replacing the generic blue palette so the app reads as one brand. --- .../player/reference/app/OhsPlayerTheme.kt | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/OhsPlayerTheme.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/OhsPlayerTheme.kt index 8c3f5ae6..30e8c086 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/OhsPlayerTheme.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/OhsPlayerTheme.kt @@ -22,32 +22,32 @@ import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -private val OhsPrimary = Color(0xFF0B57D0) +private val OhsPrimary = Color(0xFF5A44C4) private val OhsOnPrimary = Color.White -private val OhsPrimaryContainer = Color(0xFFD3E3FD) -private val OhsOnPrimaryContainer = Color(0xFF041E49) +private val OhsPrimaryContainer = Color(0xFFE5DEFF) +private val OhsOnPrimaryContainer = Color(0xFF190261) -private val OhsSecondary = Color(0xFF00639B) +private val OhsSecondary = Color(0xFF2D86C4) private val OhsOnSecondary = Color.White -private val OhsSecondaryContainer = Color(0xFFC2E7FF) -private val OhsOnSecondaryContainer = Color(0xFF001E31) +private val OhsSecondaryContainer = Color(0xFFD1E4FF) +private val OhsOnSecondaryContainer = Color(0xFF001C38) -private val OhsTertiary = Color(0xFF146C2E) +private val OhsTertiary = Color(0xFF7158C9) private val OhsOnTertiary = Color.White -private val OhsTertiaryContainer = Color(0xFFC4EED0) -private val OhsOnTertiaryContainer = Color(0xFF072711) +private val OhsTertiaryContainer = Color(0xFFEADDFF) +private val OhsOnTertiaryContainer = Color(0xFF230A5E) private val OhsError = Color(0xFFB3261E) private val OhsOnError = Color.White private val OhsErrorContainer = Color(0xFFF9DEDC) private val OhsOnErrorContainer = Color(0xFF601410) -private val OhsBackground = Color(0xFFFFFFFF) -private val OhsSurface = Color(0xFFFFFFFF) -private val OhsOnSurface = Color(0xFF1F1F1F) -private val OhsSurfaceVariant = Color(0xFFE1E3F8) -private val OhsOnSurfaceVariant = Color(0xFF45464F) -private val OhsOutline = Color(0xFF757680) +private val OhsBackground = Color(0xFFFFFBFF) +private val OhsSurface = Color(0xFFFFFBFF) +private val OhsOnSurface = Color(0xFF1C1B1F) +private val OhsSurfaceVariant = Color(0xFFE5E0EC) +private val OhsOnSurfaceVariant = Color(0xFF48454E) +private val OhsOutline = Color(0xFF79767F) private val OhsLightColorScheme = lightColorScheme( @@ -78,29 +78,29 @@ private val OhsLightColorScheme = private val OhsDarkColorScheme = darkColorScheme( - primary = Color(0xFFA8C7FA), - onPrimary = Color(0xFF0842A0), - primaryContainer = Color(0xFF0842A0), - onPrimaryContainer = Color(0xFFD3E3FD), - secondary = Color(0xFF7FCFFF), - onSecondary = Color(0xFF004A77), - secondaryContainer = Color(0xFF004A77), - onSecondaryContainer = Color(0xFFC2E7FF), - tertiary = Color(0xFF91D5A3), - onTertiary = Color(0xFF0F5223), - tertiaryContainer = Color(0xFF0F5223), - onTertiaryContainer = Color(0xFFC4EED0), + primary = Color(0xFFC9BFFF), + onPrimary = Color(0xFF2A1478), + primaryContainer = Color(0xFF422F91), + onPrimaryContainer = Color(0xFFE5DEFF), + secondary = Color(0xFF9FCAFF), + onSecondary = Color(0xFF00325B), + secondaryContainer = Color(0xFF004A80), + onSecondaryContainer = Color(0xFFD1E4FF), + tertiary = Color(0xFFD3BBFF), + onTertiary = Color(0xFF3A1D8F), + tertiaryContainer = Color(0xFF5840B0), + onTertiaryContainer = Color(0xFFEADDFF), error = Color(0xFFF2B8B5), onError = Color(0xFF601410), errorContainer = Color(0xFF8C1D18), onErrorContainer = Color(0xFFF9DEDC), - background = Color(0xFF1F1F1F), - onBackground = Color(0xFFE3E3E3), - surface = Color(0xFF1F1F1F), - onSurface = Color(0xFFE3E3E3), - surfaceVariant = Color(0xFF45464F), - onSurfaceVariant = Color(0xFFC5C6D0), - outline = Color(0xFF8F9099), + background = Color(0xFF1C1B1F), + onBackground = Color(0xFFE6E1E9), + surface = Color(0xFF1C1B1F), + onSurface = Color(0xFFE6E1E9), + surfaceVariant = Color(0xFF48454E), + onSurfaceVariant = Color(0xFFC9C5D0), + outline = Color(0xFF938F99), ) @Composable From 34acf0d64c713f0ba82b7886a2a5d52e55b06a85 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 17:27:54 +0300 Subject: [PATCH 59/73] Unescape apostrophes in string resources composeResources renders Android's `\'` escape literally, so the backslash showed on screen. Use plain apostrophes. --- .../src/commonMain/composeResources/values/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index c6c95129..b96b605d 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -10,9 +10,9 @@ Player Reference Care coordination, offline-first - Sign in to sync households and patient records with your organization\'s FHIR server. + Sign in to sync households and patient records with your organization's FHIR server. Sign in to Player Reference - You\'ll be securely redirected to your organization\'s sign-in page to continue. + You'll be securely redirected to your organization's sign-in page to continue. Sign in Sign-in failed Dismiss @@ -32,7 +32,7 @@ Setting up your data… This may take a moment the first time. - Couldn\'t sync your data + Couldn't sync your data Sync failed. Please check your connection and try again. Retry Continue without syncing From 45b2685ed3ea2a52192170606c776280a17b8206 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 17:36:07 +0300 Subject: [PATCH 60/73] Center the questionnaire form on wide screens Cap the questionnaire content at 720dp and center it, so the form no longer stretches edge to edge on desktop and large windows. --- .../questionnaire/QuestionnaireHostScreen.kt | 110 +++++++++--------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt index 89b242b9..84ed498b 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack @@ -118,66 +119,71 @@ fun QuestionnaireHostScreen( ) } ) { padding -> - Column( - modifier = Modifier.fillMaxSize().padding(padding).padding(6.dp), - verticalArrangement = Arrangement.spacedBy(6.dp), + Box( + modifier = Modifier.fillMaxSize().padding(padding), + contentAlignment = Alignment.TopCenter, ) { - when (val state = uiState) { - is QuestionnaireHostUiState.Submitted -> - SubmissionBanner(message = state.result.successMessage, isSuccess = true) + Column( + modifier = Modifier.widthIn(max = 720.dp).fillMaxSize().padding(6.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + when (val state = uiState) { + is QuestionnaireHostUiState.Submitted -> + SubmissionBanner(message = state.result.successMessage, isSuccess = true) - is QuestionnaireHostUiState.Error -> Unit // rendered below, inline with a dismiss action - else -> Unit - } + is QuestionnaireHostUiState.Error -> Unit // rendered below, inline with a dismiss action + else -> Unit + } - Box(modifier = Modifier.fillMaxWidth().weight(1f), contentAlignment = Alignment.Center) { - when (val state = uiState) { - is QuestionnaireHostUiState.Loading -> CircularProgressIndicator() + Box(modifier = Modifier.fillMaxWidth().weight(1f), contentAlignment = Alignment.Center) { + when (val state = uiState) { + is QuestionnaireHostUiState.Loading -> CircularProgressIndicator() - is QuestionnaireHostUiState.Error -> { - Column( - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - text = state.message, - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(16.dp), - ) - TextButton(onClick = viewModel::load) { - Text(stringResource(Res.string.questionnaire_retry)) + is QuestionnaireHostUiState.Error -> { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = state.message, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(16.dp), + ) + TextButton(onClick = viewModel::load) { + Text(stringResource(Res.string.questionnaire_retry)) + } } } - } - is QuestionnaireHostUiState.Ready, - is QuestionnaireHostUiState.Submitting -> { - val questionnaireJson = - when (state) { - is QuestionnaireHostUiState.Ready -> state.questionnaireJson - is QuestionnaireHostUiState.Submitting -> state.questionnaireJson - else -> "" - } - Questionnaire( - questionnaireJson = questionnaireJson, - questionnaireLaunchContextMap = emptyMap(), - config = - QuestionnaireConfig( - showReviewPage = true, - showReviewPageFirst = false, - isReadOnly = false, - showCancelButton = false, - ), - onSubmit = { getResponse -> - coroutineScope.launch { viewModel.onSubmit(getResponse()) } - }, - matchersProvider = viewItemMatchersProvider, - onCancel = {}, - ) - } + is QuestionnaireHostUiState.Ready, + is QuestionnaireHostUiState.Submitting -> { + val questionnaireJson = + when (state) { + is QuestionnaireHostUiState.Ready -> state.questionnaireJson + is QuestionnaireHostUiState.Submitting -> state.questionnaireJson + else -> "" + } + Questionnaire( + questionnaireJson = questionnaireJson, + questionnaireLaunchContextMap = emptyMap(), + config = + QuestionnaireConfig( + showReviewPage = true, + showReviewPageFirst = false, + isReadOnly = false, + showCancelButton = false, + ), + onSubmit = { getResponse -> + coroutineScope.launch { viewModel.onSubmit(getResponse()) } + }, + matchersProvider = viewItemMatchersProvider, + onCancel = {}, + ) + } - is QuestionnaireHostUiState.Submitted -> Unit + is QuestionnaireHostUiState.Submitted -> Unit + } } } } From 9dce755872e9cf7808cf7cf014d4783094954ab8 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 17:49:19 +0300 Subject: [PATCH 61/73] Show households alongside their detail on wide screens Adopt the Material 3 adaptive window size class in HomeScreen: on expanded widths the household register sits in a list pane beside a detail pane (the selected household's profile, or a placeholder), with the drawer permanent; compact widths keep the single-pane push navigation and hamburger drawer. Wire member and add-member actions from the detail pane through to the host. --- gradle/libs.versions.toml | 5 ++ ohs-player-reference-app/build.gradle.kts | 4 ++ .../composeResources/values/strings.xml | 1 + .../dev/ohs/player/reference/app/App.kt | 9 +++ .../reference/app/feature/home/HomeScreen.kt | 69 ++++++++++++++++--- .../app/feature/home/HomeScreenTest.kt | 40 +++++++++-- 6 files changed, 113 insertions(+), 15 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a3ebdb95..1586e90f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,6 +30,7 @@ ktfmt = "0.54" ktlint = "1.5.0" navigation-compose = "2.9.2" material3 = "1.10.0-alpha05" +composeAdaptive = "1.2.0" kotlinxSerializationJson = "1.11.0" spotless = "8.6.0" ohsFhirEngine = "2.0.0-alpha02" @@ -61,6 +62,10 @@ compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.re compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" } compose-material = { module = "org.jetbrains.compose.material:material", version.ref = "composeMultiplatform" } compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" } +compose-adaptive = { module = "org.jetbrains.compose.material3.adaptive:adaptive", version.ref = "composeAdaptive" } +compose-adaptive-layout = { module = "org.jetbrains.compose.material3.adaptive:adaptive-layout", version.ref = "composeAdaptive" } +compose-adaptive-navigation = { module = "org.jetbrains.compose.material3.adaptive:adaptive-navigation", version.ref = "composeAdaptive" } +compose-material3-adaptive-navigation-suite = { module = "org.jetbrains.compose.material3:material3-adaptive-navigation-suite", version.ref = "material3" } compose-materialIconsCore = { module = "org.jetbrains.compose.material:material-icons-core", version.ref = "composeMaterialIcons" } compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" } compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" } diff --git a/ohs-player-reference-app/build.gradle.kts b/ohs-player-reference-app/build.gradle.kts index f29b0c22..58475318 100644 --- a/ohs-player-reference-app/build.gradle.kts +++ b/ohs-player-reference-app/build.gradle.kts @@ -91,6 +91,10 @@ kotlin { implementation(libs.compose.foundation) implementation(libs.compose.material) implementation(libs.compose.material3) + implementation(libs.compose.adaptive) + implementation(libs.compose.adaptive.layout) + implementation(libs.compose.adaptive.navigation) + implementation(libs.compose.material3.adaptive.navigation.suite) implementation(libs.compose.materialIconsCore) implementation(libs.compose.ui) implementation(libs.compose.components.resources) diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index b96b605d..58efa89f 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -28,6 +28,7 @@ Open navigation menu Sync failed. Please try again. Sync cancelled. + Select a household to view details Setting up your data… diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index 14062d11..988aa0f3 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -104,6 +104,15 @@ fun App() { ) ) }, + onMemberClick = { id -> navController.navigate("patientProfile/$id") }, + onAddMembers = { groupId -> + navController.navigate( + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.HOUSEHOLD_MEMBERS, + groupId = groupId, + ) + ) + }, onSignOut = { authViewModel.logout() }, ) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index 3381d31b..5f14b38e 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -16,10 +16,11 @@ package dev.ohs.player.reference.app.feature.home import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons @@ -44,6 +45,9 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.VerticalDivider +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -51,20 +55,25 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.Dp +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.window.core.layout.WindowSizeClass import dev.ohs.player.reference.app.feature.group.list.GroupListScreen +import dev.ohs.player.reference.app.feature.group.profile.GroupProfileScreen import kotlinx.coroutines.launch import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_cancel_sync import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_last_synced import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_open_navigation_menu import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_registers +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_select_household import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sign_out import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_cancelled import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_failed @@ -73,22 +82,20 @@ import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources. import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel -/** - * Material 3's "expanded" window size class breakpoint: at or above it the drawer stays permanent. - */ -private val HOME_EXPANDED_WIDTH_BREAKPOINT: Dp = 840.dp - -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3AdaptiveApi::class) @Composable fun HomeScreen( onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit, + onMemberClick: (String) -> Unit, + onAddMembers: (String) -> Unit, onSignOut: () -> Unit, ) { val homeViewModel: HomeViewModel = koinViewModel() val uiState by homeViewModel.uiState.collectAsStateWithLifecycle() var selectedDestination by remember { mutableStateOf(HomeDestination.Households) } + var selectedGroupId by rememberSaveable { mutableStateOf(null) } val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } @@ -106,8 +113,11 @@ fun HomeScreen( } } - BoxWithConstraints { - val isExpandedWidth = maxWidth >= HOME_EXPANDED_WIDTH_BREAKPOINT + Box(modifier = Modifier.fillMaxSize()) { + val isExpandedWidth = + currentWindowAdaptiveInfo() + .windowSizeClass + .isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) fun closeDrawerIfCompact() { if (!isExpandedWidth) scope.launch { drawerState.close() } @@ -193,7 +203,32 @@ fun HomeScreen( val content: @Composable () -> Unit = { when (selectedDestination) { HomeDestination.Households -> - GroupListScreen(onGroupClick = onGroupClick, onDataCaptureClick = onDataCaptureClick) + if (isExpandedWidth) { + Row(modifier = Modifier.fillMaxSize()) { + Box(modifier = Modifier.weight(1f).fillMaxSize()) { + GroupListScreen( + onGroupClick = { selectedGroupId = it }, + onDataCaptureClick = onDataCaptureClick, + ) + } + VerticalDivider() + Box(modifier = Modifier.weight(1.5f).fillMaxSize()) { + val groupId = selectedGroupId + if (groupId != null) { + GroupProfileScreen( + groupId = groupId, + onBack = { selectedGroupId = null }, + onMemberClick = onMemberClick, + onAddMembers = { onAddMembers(groupId) }, + ) + } else { + EmptyDetailPlaceholder() + } + } + } + } else { + GroupListScreen(onGroupClick = onGroupClick, onDataCaptureClick = onDataCaptureClick) + } } } @@ -230,3 +265,15 @@ fun HomeScreen( } } } + +@Composable +private fun EmptyDetailPlaceholder() { + Box(modifier = Modifier.fillMaxSize().padding(24.dp), contentAlignment = Alignment.Center) { + Text( + text = stringResource(Res.string.home_select_household), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) + } +} diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index ffa3ac67..6156760e 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -73,7 +73,15 @@ class HomeScreenTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onMemberClick = {}, + onAddMembers = {}, + onSignOut = {}, + ) + } } } @@ -98,7 +106,15 @@ class HomeScreenTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onMemberClick = {}, + onAddMembers = {}, + onSignOut = {}, + ) + } } } @@ -131,7 +147,15 @@ class HomeScreenTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onMemberClick = {}, + onAddMembers = {}, + onSignOut = {}, + ) + } } } @@ -159,7 +183,15 @@ class HomeScreenTest { val registry = buildAppViewRegistry() setContent { CompositionLocalProvider(LocalViewRegistry provides registry) { - MaterialTheme { HomeScreen(onGroupClick = {}, onDataCaptureClick = {}, onSignOut = {}) } + MaterialTheme { + HomeScreen( + onGroupClick = {}, + onDataCaptureClick = {}, + onMemberClick = {}, + onAddMembers = {}, + onSignOut = {}, + ) + } } } From f151e6e63e3f1585ac96232a8c4a89a978feada5 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 18:08:15 +0300 Subject: [PATCH 62/73] Add member detail pane, a nav rail, and shell polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the adaptive home: on expanded widths a tapped member now opens the patient profile in the detail pane (household → member drill-down in place), and a navigation rail carries the medium breakpoint between the compact modal drawer and the expanded permanent drawer. Give the drawer/rail a distinct tinted surface with a divider so it reads apart from the content, tighten its width, make the register FAB an extended "Add household" button, switch the questionnaire dismiss control to a close icon, and render the shared cards as elevated cards. --- .../composeResources/values/strings.xml | 3 +- .../dev/ohs/player/reference/app/App.kt | 9 +- .../app/feature/component/common/CardView.kt | 4 +- .../app/feature/group/list/GroupListScreen.kt | 13 +-- .../reference/app/feature/home/HomeScreen.kt | 110 +++++++++++++++--- .../questionnaire/QuestionnaireHostScreen.kt | 8 +- .../app/feature/home/HomeScreenTest.kt | 8 +- 7 files changed, 119 insertions(+), 36 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index 58efa89f..d34b3086 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -39,7 +39,7 @@ Continue without syncing - Register household + Add household No households @@ -61,5 +61,6 @@ Questionnaire Back + Close Retry \ No newline at end of file diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index 988aa0f3..b4fa6798 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -104,7 +104,6 @@ fun App() { ) ) }, - onMemberClick = { id -> navController.navigate("patientProfile/$id") }, onAddMembers = { groupId -> navController.navigate( questionnaireHostRoute( @@ -113,6 +112,14 @@ fun App() { ) ) }, + onAddClinicalData = { patientId -> + navController.navigate( + questionnaireHostRoute( + questionnaireId = QuestionnaireIds.PATIENT_CLINICAL_DATA, + patientId = patientId, + ) + ) + }, onSignOut = { authViewModel.logout() }, ) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt index e8dc1df5..912cd1f8 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt @@ -20,8 +20,8 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -58,7 +58,7 @@ fun CardView( } else { Modifier.fillMaxWidth() } - Card( + ElevatedCard( modifier = cardModifier, elevation = CardDefaults.elevatedCardElevation(defaultElevation = elevationDp.dp), ) { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt index d16db610..c1cc4867 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt @@ -22,7 +22,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -56,12 +56,11 @@ fun GroupListScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Un Scaffold( floatingActionButton = { - FloatingActionButton(onClick = onDataCaptureClick) { - Icon( - Icons.Filled.Add, - contentDescription = stringResource(Res.string.group_list_register_household), - ) - } + ExtendedFloatingActionButton( + onClick = onDataCaptureClick, + icon = { Icon(Icons.Filled.Add, contentDescription = null) }, + text = { Text(stringResource(Res.string.group_list_register_household)) }, + ) } ) { padding -> Box(modifier = Modifier.fillMaxSize().padding(padding)) { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index 5f14b38e..de662cb9 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ExitToApp import androidx.compose.material.icons.filled.Close @@ -38,11 +39,12 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.NavigationDrawerItem -import androidx.compose.material3.PermanentDrawerSheet -import androidx.compose.material3.PermanentNavigationDrawer +import androidx.compose.material3.NavigationRail +import androidx.compose.material3.NavigationRailItem import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.VerticalDivider @@ -67,6 +69,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.window.core.layout.WindowSizeClass import dev.ohs.player.reference.app.feature.group.list.GroupListScreen import dev.ohs.player.reference.app.feature.group.profile.GroupProfileScreen +import dev.ohs.player.reference.app.feature.patient.profile.PatientProfileScreen import kotlinx.coroutines.launch import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_cancel_sync @@ -87,8 +90,8 @@ import org.koin.compose.viewmodel.koinViewModel fun HomeScreen( onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit, - onMemberClick: (String) -> Unit, onAddMembers: (String) -> Unit, + onAddClinicalData: (String) -> Unit, onSignOut: () -> Unit, ) { val homeViewModel: HomeViewModel = koinViewModel() @@ -96,6 +99,7 @@ fun HomeScreen( var selectedDestination by remember { mutableStateOf(HomeDestination.Households) } var selectedGroupId by rememberSaveable { mutableStateOf(null) } + var selectedPatientId by rememberSaveable(selectedGroupId) { mutableStateOf(null) } val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } @@ -114,10 +118,12 @@ fun HomeScreen( } Box(modifier = Modifier.fillMaxSize()) { + val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass val isExpandedWidth = - currentWindowAdaptiveInfo() - .windowSizeClass - .isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) + windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) + val isMediumWidth = + !isExpandedWidth && + windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) fun closeDrawerIfCompact() { if (!isExpandedWidth) scope.launch { drawerState.close() } @@ -213,16 +219,23 @@ fun HomeScreen( } VerticalDivider() Box(modifier = Modifier.weight(1.5f).fillMaxSize()) { + val patientId = selectedPatientId val groupId = selectedGroupId - if (groupId != null) { - GroupProfileScreen( - groupId = groupId, - onBack = { selectedGroupId = null }, - onMemberClick = onMemberClick, - onAddMembers = { onAddMembers(groupId) }, - ) - } else { - EmptyDetailPlaceholder() + when { + patientId != null -> + PatientProfileScreen( + patientId = patientId, + onBack = { selectedPatientId = null }, + onAddClinicalData = { onAddClinicalData(patientId) }, + ) + groupId != null -> + GroupProfileScreen( + groupId = groupId, + onBack = { selectedGroupId = null }, + onMemberClick = { selectedPatientId = it }, + onAddMembers = { onAddMembers(groupId) }, + ) + else -> EmptyDetailPlaceholder() } } } @@ -233,7 +246,63 @@ fun HomeScreen( } if (isExpandedWidth) { - PermanentNavigationDrawer(drawerContent = { PermanentDrawerSheet { drawerItems() } }) { + Row(modifier = Modifier.fillMaxSize()) { + Surface( + modifier = Modifier.width(280.dp).fillMaxHeight(), + color = MaterialTheme.colorScheme.surfaceContainerHigh, + ) { + drawerItems() + } + VerticalDivider() + Scaffold(snackbarHost = { SnackbarHost(snackbarHostState) }) { padding -> + Box(modifier = Modifier.padding(padding)) { content() } + } + } + } else if (isMediumWidth) { + Row(modifier = Modifier.fillMaxSize()) { + NavigationRail(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh) { + val syncInProgressDescription = stringResource(Res.string.home_sync_in_progress) + HomeDestination.entries.forEach { destination -> + NavigationRailItem( + selected = destination == selectedDestination, + onClick = { selectedDestination = destination }, + icon = { Icon(destination.icon, contentDescription = null) }, + label = { Text(stringResource(destination.label)) }, + ) + } + Spacer(modifier = Modifier.weight(1f)) + NavigationRailItem( + selected = false, + onClick = { + if (uiState.isSyncing) homeViewModel.cancelSync() else homeViewModel.syncNow() + }, + icon = { + if (uiState.isSyncing) { + Icon( + Icons.Filled.Close, + contentDescription = null, + modifier = Modifier.semantics { contentDescription = syncInProgressDescription }, + ) + } else { + Icon(Icons.Filled.Refresh, contentDescription = null) + } + }, + label = { + Text( + stringResource( + if (uiState.isSyncing) Res.string.home_cancel_sync else Res.string.home_sync_now + ) + ) + }, + ) + NavigationRailItem( + selected = false, + onClick = onSignOut, + icon = { Icon(Icons.AutoMirrored.Filled.ExitToApp, contentDescription = null) }, + label = { Text(stringResource(Res.string.home_sign_out)) }, + ) + } + VerticalDivider() Scaffold(snackbarHost = { SnackbarHost(snackbarHostState) }) { padding -> Box(modifier = Modifier.padding(padding)) { content() } } @@ -241,7 +310,14 @@ fun HomeScreen( } else { ModalNavigationDrawer( drawerState = drawerState, - drawerContent = { ModalDrawerSheet { drawerItems() } }, + drawerContent = { + ModalDrawerSheet( + modifier = Modifier.width(300.dp), + drawerContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + ) { + drawerItems() + } + }, ) { Scaffold( topBar = { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt index 84ed498b..6d057562 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt @@ -24,7 +24,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Close import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -54,7 +54,7 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.delay import kotlinx.coroutines.launch import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res -import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_back +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_close import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_retry import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_title import org.jetbrains.compose.resources.stringResource @@ -105,8 +105,8 @@ fun QuestionnaireHostScreen( navigationIcon = { IconButton(onClick = onBack) { Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(Res.string.questionnaire_back), + imageVector = Icons.Filled.Close, + contentDescription = stringResource(Res.string.questionnaire_close), tint = MaterialTheme.colorScheme.onPrimary, ) } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index 6156760e..97639679 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -77,8 +77,8 @@ class HomeScreenTest { HomeScreen( onGroupClick = {}, onDataCaptureClick = {}, - onMemberClick = {}, onAddMembers = {}, + onAddClinicalData = {}, onSignOut = {}, ) } @@ -110,8 +110,8 @@ class HomeScreenTest { HomeScreen( onGroupClick = {}, onDataCaptureClick = {}, - onMemberClick = {}, onAddMembers = {}, + onAddClinicalData = {}, onSignOut = {}, ) } @@ -151,8 +151,8 @@ class HomeScreenTest { HomeScreen( onGroupClick = {}, onDataCaptureClick = {}, - onMemberClick = {}, onAddMembers = {}, + onAddClinicalData = {}, onSignOut = {}, ) } @@ -187,8 +187,8 @@ class HomeScreenTest { HomeScreen( onGroupClick = {}, onDataCaptureClick = {}, - onMemberClick = {}, onAddMembers = {}, + onAddClinicalData = {}, onSignOut = {}, ) } From 42c1df5801f528c64165c91e16b0bec322196ee7 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 18:24:57 +0300 Subject: [PATCH 63/73] Flatten cards, brand the drawer, and drop marketing copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch the shared card to an outlined (flat) card, no elevation. Give the navigation drawer and rail the brand primary surface with light content, a signed-in user header (avatar + name), and a slightly narrower width. Strip the login hero's tagline and subtitle — this is a reference app meant to be customized, so it carries only the wordmark and the sign-in action. --- .../composeResources/values/strings.xml | 4 +- .../dev/ohs/player/reference/app/App.kt | 4 + .../app/feature/component/common/CardView.kt | 8 +- .../reference/app/feature/home/HomeScreen.kt | 80 ++++++++++++++++--- .../app/feature/login/LoginScreen.kt | 34 +------- .../app/feature/home/HomeScreenTest.kt | 4 + 6 files changed, 81 insertions(+), 53 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index d34b3086..1bfeb9bf 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -9,8 +9,6 @@ Player Reference - Care coordination, offline-first - Sign in to sync households and patient records with your organization's FHIR server. Sign in to Player Reference You'll be securely redirected to your organization's sign-in page to continue. Sign in @@ -20,6 +18,7 @@ Households Registers + Signed in Last synced: %1$s Sync now Cancel sync @@ -60,7 +59,6 @@ Questionnaire - Back Close Retry \ No newline at end of file diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt index b4fa6798..1f8c2b5a 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/App.kt @@ -76,6 +76,9 @@ fun App() { onErrorDismiss = { authViewModel.clearError() }, ) is AuthState.Authenticated -> { + val session = (authState as AuthState.Authenticated).session + val userName = + session.user.fullName.ifBlank { session.user.username }.ifBlank { session.user.email } val initialSyncViewModel: InitialSyncViewModel = koinViewModel() val gateState by initialSyncViewModel.state.collectAsStateWithLifecycle() LaunchedEffect(Unit) { initialSyncViewModel.start() } @@ -96,6 +99,7 @@ fun App() { // Screen 1: Home (adaptive navigation drawer shell around the household list) composable("home") { HomeScreen( + userName = userName, onGroupClick = { id -> navController.navigate("groupProfile/$id") }, onDataCaptureClick = { navController.navigate( diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt index 912cd1f8..a1b8fe29 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt @@ -20,8 +20,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -58,10 +57,7 @@ fun CardView( } else { Modifier.fillMaxWidth() } - ElevatedCard( - modifier = cardModifier, - elevation = CardDefaults.elevatedCardElevation(defaultElevation = elevationDp.dp), - ) { + OutlinedCard(modifier = cardModifier) { Column( modifier = Modifier.padding(contentPaddingDp.dp), verticalArrangement = Arrangement.spacedBy(6.dp), diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index de662cb9..57054c99 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -15,6 +15,8 @@ */ package dev.ohs.player.reference.app.feature.home +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -24,6 +26,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ExitToApp import androidx.compose.material.icons.filled.Close @@ -39,8 +42,10 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.NavigationDrawerItem +import androidx.compose.material3.NavigationDrawerItemDefaults import androidx.compose.material3.NavigationRail import androidx.compose.material3.NavigationRailItem +import androidx.compose.material3.NavigationRailItemDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -61,8 +66,11 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -78,6 +86,7 @@ import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources. import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_registers import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_select_household import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sign_out +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_signed_in import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_cancelled import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_failed import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.home_sync_in_progress @@ -88,6 +97,7 @@ import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3AdaptiveApi::class) @Composable fun HomeScreen( + userName: String, onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Unit, onAddMembers: (String) -> Unit, @@ -129,20 +139,54 @@ fun HomeScreen( if (!isExpandedWidth) scope.launch { drawerState.close() } } + val onDrawer = MaterialTheme.colorScheme.onPrimary + val drawerItemColors = + NavigationDrawerItemDefaults.colors( + selectedContainerColor = onDrawer.copy(alpha = 0.20f), + unselectedContainerColor = Color.Transparent, + selectedTextColor = onDrawer, + unselectedTextColor = onDrawer, + selectedIconColor = onDrawer, + unselectedIconColor = onDrawer, + ) val drawerItems: @Composable () -> Unit = { val syncInProgressDescription = stringResource(Res.string.home_sync_in_progress) - Column(modifier = Modifier.fillMaxHeight()) { + Column(modifier = Modifier.fillMaxHeight().padding(horizontal = 12.dp)) { + Row( + modifier = Modifier.padding(start = 16.dp, top = 24.dp, bottom = 20.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = + Modifier.size(40.dp).clip(CircleShape).background(onDrawer.copy(alpha = 0.2f)), + contentAlignment = Alignment.Center, + ) { + Text( + text = userName.trim().firstOrNull()?.uppercaseChar()?.toString() ?: "?", + style = MaterialTheme.typography.titleMedium, + color = onDrawer, + ) + } + Text( + text = userName.ifBlank { stringResource(Res.string.home_signed_in) }, + style = MaterialTheme.typography.titleMedium, + color = onDrawer, + fontWeight = FontWeight.SemiBold, + ) + } Text( text = stringResource(Res.string.home_registers), style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(start = 28.dp, top = 16.dp, bottom = 8.dp), + color = onDrawer.copy(alpha = 0.7f), + modifier = Modifier.padding(start = 16.dp, bottom = 8.dp), ) HomeDestination.entries.forEach { destination -> NavigationDrawerItem( label = { Text(stringResource(destination.label)) }, icon = { Icon(destination.icon, contentDescription = null) }, selected = destination == selectedDestination, + colors = drawerItemColors, onClick = { selectedDestination = destination closeDrawerIfCompact() @@ -152,16 +196,17 @@ fun HomeScreen( Spacer(modifier = Modifier.weight(1f)) - HorizontalDivider() + HorizontalDivider(color = onDrawer.copy(alpha = 0.2f)) uiState.lastSyncedAt?.let { lastSyncedAt -> Text( text = stringResource(Res.string.home_last_synced, lastSyncedAt), style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(start = 28.dp, top = 8.dp), + color = onDrawer.copy(alpha = 0.7f), + modifier = Modifier.padding(start = 16.dp, top = 8.dp), ) } NavigationDrawerItem( + colors = drawerItemColors, label = { Text( stringResource( @@ -181,6 +226,7 @@ fun HomeScreen( modifier = Modifier.size(16.dp).semantics { contentDescription = syncInProgressDescription }, strokeWidth = 2.dp, + color = onDrawer, ) } }, @@ -195,6 +241,7 @@ fun HomeScreen( }, ) NavigationDrawerItem( + colors = drawerItemColors, label = { Text(stringResource(Res.string.home_sign_out)) }, icon = { Icon(Icons.AutoMirrored.Filled.ExitToApp, contentDescription = null) }, selected = false, @@ -248,8 +295,8 @@ fun HomeScreen( if (isExpandedWidth) { Row(modifier = Modifier.fillMaxSize()) { Surface( - modifier = Modifier.width(280.dp).fillMaxHeight(), - color = MaterialTheme.colorScheme.surfaceContainerHigh, + modifier = Modifier.width(260.dp).fillMaxHeight(), + color = MaterialTheme.colorScheme.primary, ) { drawerItems() } @@ -260,12 +307,21 @@ fun HomeScreen( } } else if (isMediumWidth) { Row(modifier = Modifier.fillMaxSize()) { - NavigationRail(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh) { + val railItemColors = + NavigationRailItemDefaults.colors( + indicatorColor = onDrawer.copy(alpha = 0.20f), + selectedIconColor = onDrawer, + unselectedIconColor = onDrawer, + selectedTextColor = onDrawer, + unselectedTextColor = onDrawer, + ) + NavigationRail(containerColor = MaterialTheme.colorScheme.primary) { val syncInProgressDescription = stringResource(Res.string.home_sync_in_progress) HomeDestination.entries.forEach { destination -> NavigationRailItem( selected = destination == selectedDestination, onClick = { selectedDestination = destination }, + colors = railItemColors, icon = { Icon(destination.icon, contentDescription = null) }, label = { Text(stringResource(destination.label)) }, ) @@ -273,6 +329,7 @@ fun HomeScreen( Spacer(modifier = Modifier.weight(1f)) NavigationRailItem( selected = false, + colors = railItemColors, onClick = { if (uiState.isSyncing) homeViewModel.cancelSync() else homeViewModel.syncNow() }, @@ -297,6 +354,7 @@ fun HomeScreen( ) NavigationRailItem( selected = false, + colors = railItemColors, onClick = onSignOut, icon = { Icon(Icons.AutoMirrored.Filled.ExitToApp, contentDescription = null) }, label = { Text(stringResource(Res.string.home_sign_out)) }, @@ -312,8 +370,8 @@ fun HomeScreen( drawerState = drawerState, drawerContent = { ModalDrawerSheet( - modifier = Modifier.width(300.dp), - drawerContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + modifier = Modifier.width(280.dp), + drawerContainerColor = MaterialTheme.colorScheme.primary, ) { drawerItems() } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt index 0d028d8b..8bf89a0f 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt @@ -33,7 +33,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -60,8 +59,6 @@ import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources. import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_error_title import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_redirect_hint import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_sign_in -import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_subtitle -import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.login_tagline import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource @@ -101,26 +98,9 @@ private fun ExpandedLogin(signingIn: Boolean, onSignIn: () -> Unit) { Box(Modifier.weight(1f).fillMaxHeight().background(MaterialTheme.colorScheme.primary)) { Column( modifier = Modifier.fillMaxSize().safeDrawingPadding().padding(48.dp), - verticalArrangement = Arrangement.SpaceBetween, + verticalArrangement = Arrangement.Center, ) { Wordmark(MaterialTheme.colorScheme.onPrimary) - Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { - Text( - text = stringResource(Res.string.login_tagline), - style = MaterialTheme.typography.displaySmall, - color = MaterialTheme.colorScheme.onPrimary, - ) - Box( - Modifier.width(56.dp) - .height(4.dp) - .background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(2.dp)) - ) - Text( - text = stringResource(Res.string.login_subtitle), - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.9f), - ) - } } } @@ -191,18 +171,6 @@ private fun CompactLogin(signingIn: Boolean, onSignIn: () -> Unit) { verticalArrangement = Arrangement.spacedBy(12.dp), ) { Wordmark(MaterialTheme.colorScheme.onPrimary) - Text( - text = stringResource(Res.string.login_tagline), - style = MaterialTheme.typography.headlineSmall, - color = MaterialTheme.colorScheme.onPrimary, - textAlign = TextAlign.Center, - ) - Text( - text = stringResource(Res.string.login_subtitle), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.9f), - textAlign = TextAlign.Center, - ) } Column( diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt index 97639679..f3cf9ef3 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreenTest.kt @@ -75,6 +75,7 @@ class HomeScreenTest { CompositionLocalProvider(LocalViewRegistry provides registry) { MaterialTheme { HomeScreen( + userName = "Test User", onGroupClick = {}, onDataCaptureClick = {}, onAddMembers = {}, @@ -108,6 +109,7 @@ class HomeScreenTest { CompositionLocalProvider(LocalViewRegistry provides registry) { MaterialTheme { HomeScreen( + userName = "Test User", onGroupClick = {}, onDataCaptureClick = {}, onAddMembers = {}, @@ -149,6 +151,7 @@ class HomeScreenTest { CompositionLocalProvider(LocalViewRegistry provides registry) { MaterialTheme { HomeScreen( + userName = "Test User", onGroupClick = {}, onDataCaptureClick = {}, onAddMembers = {}, @@ -185,6 +188,7 @@ class HomeScreenTest { CompositionLocalProvider(LocalViewRegistry provides registry) { MaterialTheme { HomeScreen( + userName = "Test User", onGroupClick = {}, onDataCaptureClick = {}, onAddMembers = {}, From f874e9de2fb7df8b6aa8f64f4af2dffb175056a7 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 18:33:29 +0300 Subject: [PATCH 64/73] Render list rows as flat rows with dividers Drop the outline/elevation from the shared list card: each row is now a borderless rounded, padded, clickable row separated by a full-width divider. --- .../app/feature/component/common/CardView.kt | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt index a1b8fe29..09316346 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt @@ -20,9 +20,11 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.OutlinedCard +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp class CardDslScope { @@ -51,20 +53,17 @@ fun CardView( builder: CardDslScope.() -> Unit, ) { val scope = CardDslScope().apply(builder) - val cardModifier = - if (onClick != null) { - Modifier.fillMaxWidth().clickable(onClick = onClick) - } else { - Modifier.fillMaxWidth() - } - OutlinedCard(modifier = cardModifier) { - Column( - modifier = Modifier.padding(contentPaddingDp.dp), - verticalArrangement = Arrangement.spacedBy(6.dp), - ) { + val rowModifier = + Modifier.fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier) + .padding(contentPaddingDp.dp) + Column(modifier = Modifier.fillMaxWidth()) { + Column(modifier = rowModifier, verticalArrangement = Arrangement.spacedBy(6.dp)) { scope.headerContent?.invoke() scope.bodyContent?.invoke() scope.footerContent?.invoke() } + HorizontalDivider() } } From c9055ed147a940736054cc2c020a0ffe213aa00b Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 19:46:23 +0300 Subject: [PATCH 65/73] Replace list cards with flat selectable rows Register rows dropped their filled cards. Each household now sits flat on the surface with no border or divider; in the two-pane layout the open household carries a rounded tonal highlight. Selection reaches the row through a composition local, so the library list scaffold is untouched. CardView is gone and the patient row matches. --- .../app/feature/component/common/CardView.kt | 69 ---------- .../app/feature/group/list/GroupCard.kt | 99 +++++++------- .../group/list/GroupListRegistrations.kt | 5 +- .../reference/app/feature/home/HomeScreen.kt | 12 +- .../app/feature/patient/list/PatientCard.kt | 122 ++++++++---------- .../patient/list/PatientListRegistrations.kt | 5 +- 6 files changed, 116 insertions(+), 196 deletions(-) delete mode 100644 ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt deleted file mode 100644 index 09316346..00000000 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/CardView.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2026 Open Health Stack Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.ohs.player.reference.app.feature.component.common - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.HorizontalDivider -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp - -class CardDslScope { - internal var headerContent: (@Composable () -> Unit)? = null - internal var bodyContent: (@Composable () -> Unit)? = null - internal var footerContent: (@Composable () -> Unit)? = null - - fun header(content: @Composable () -> Unit) { - headerContent = content - } - - fun body(content: @Composable () -> Unit) { - bodyContent = content - } - - fun footer(content: @Composable () -> Unit) { - footerContent = content - } -} - -@Composable -fun CardView( - elevationDp: Float = 2f, - contentPaddingDp: Float = 16f, - onClick: (() -> Unit)? = null, - builder: CardDslScope.() -> Unit, -) { - val scope = CardDslScope().apply(builder) - val rowModifier = - Modifier.fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier) - .padding(contentPaddingDp.dp) - Column(modifier = Modifier.fillMaxWidth()) { - Column(modifier = rowModifier, verticalArrangement = Arrangement.spacedBy(6.dp)) { - scope.headerContent?.invoke() - scope.bodyContent?.invoke() - scope.footerContent?.invoke() - } - HorizontalDivider() - } -} diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt index d95b8d85..d7e960b2 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt @@ -16,27 +16,33 @@ package dev.ohs.player.reference.app.feature.group.list 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.Column 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.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight -import androidx.compose.material3.Icon +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import dev.ohs.player.generated.config.GroupCardConfig import dev.ohs.player.generated.state.GroupListState -import dev.ohs.player.reference.app.feature.component.common.CardView + +/** + * Selected household id for the two-pane list-detail highlight; null when there is no open pane. + */ +val LocalSelectedGroupId = compositionLocalOf { null } @Composable fun GroupCard( @@ -48,54 +54,47 @@ fun GroupCard( val initials = name.trim().firstOrNull()?.uppercaseChar()?.toString() ?: "H" val count = group.memberCount ?: "0" val memberLabel = if (count == "1") "1 member" else "$count members" + val selected = group.groupId != null && group.groupId == LocalSelectedGroupId.current - CardView( - elevationDp = config.elevation?.floatValue() ?: 2f, - contentPaddingDp = config.padding?.floatValue() ?: 16f, - onClick = onClick, + Row( + modifier = + Modifier.fillMaxWidth() + .clip(RoundedCornerShape(18.dp)) + .background(if (selected) MaterialTheme.colorScheme.primaryContainer else Color.Transparent) + .then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier) + .padding(horizontal = 12.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + verticalAlignment = Alignment.CenterVertically, ) { - header { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Box( - modifier = - Modifier.size(40.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primaryContainer), - contentAlignment = Alignment.Center, - ) { - Text( - text = initials, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onPrimaryContainer, - fontWeight = FontWeight.Bold, - ) - } - Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { - Text( - text = name, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - ) - if (config.showMemberCount != false) { - Text( - text = memberLabel, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - } - if (onClick != null) { - Icon( - imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, - contentDescription = null, - tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f), - modifier = Modifier.size(16.dp), - ) - } + Box( + modifier = + Modifier.size(44.dp).clip(CircleShape).background(MaterialTheme.colorScheme.primary), + contentAlignment = Alignment.Center, + ) { + Text( + text = initials, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onPrimary, + fontWeight = FontWeight.Bold, + ) + } + Column(modifier = Modifier.weight(1f)) { + Text( + text = name, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold, + color = + if (selected) MaterialTheme.colorScheme.onPrimaryContainer + else MaterialTheme.colorScheme.onSurface, + ) + if (config.showMemberCount != false) { + Text( + text = memberLabel, + style = MaterialTheme.typography.bodyMedium, + color = + if (selected) MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f) + else MaterialTheme.colorScheme.onSurfaceVariant, + ) } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListRegistrations.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListRegistrations.kt index 3e8e8a4d..35be7dc0 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListRegistrations.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListRegistrations.kt @@ -33,6 +33,9 @@ fun ViewRegistry.registerGroupList() { ) registerLayout( VerticalListRenderer.VIEW_TYPE, - VerticalListRenderer(contentPadding = PaddingValues(16.dp), itemSpacing = 12.dp), + VerticalListRenderer( + contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp), + itemSpacing = 2.dp, + ), ) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt index 57054c99..f894a240 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/home/HomeScreen.kt @@ -57,6 +57,7 @@ import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -76,6 +77,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.window.core.layout.WindowSizeClass import dev.ohs.player.reference.app.feature.group.list.GroupListScreen +import dev.ohs.player.reference.app.feature.group.list.LocalSelectedGroupId import dev.ohs.player.reference.app.feature.group.profile.GroupProfileScreen import dev.ohs.player.reference.app.feature.patient.profile.PatientProfileScreen import kotlinx.coroutines.launch @@ -259,10 +261,12 @@ fun HomeScreen( if (isExpandedWidth) { Row(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.weight(1f).fillMaxSize()) { - GroupListScreen( - onGroupClick = { selectedGroupId = it }, - onDataCaptureClick = onDataCaptureClick, - ) + CompositionLocalProvider(LocalSelectedGroupId provides selectedGroupId) { + GroupListScreen( + onGroupClick = { selectedGroupId = it }, + onDataCaptureClick = onDataCaptureClick, + ) + } } VerticalDivider() Box(modifier = Modifier.weight(1.5f).fillMaxSize()) { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt index d3967b28..5a95eef2 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt @@ -16,16 +16,16 @@ package dev.ohs.player.reference.app.feature.patient.list 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.Column 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.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight -import androidx.compose.material3.Icon +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -36,7 +36,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import dev.ohs.player.generated.config.PatientCardConfig import dev.ohs.player.generated.state.PatientSummaryState -import dev.ohs.player.reference.app.feature.component.common.CardView import dev.ohs.player.reference.app.feature.component.common.StatusChip import kotlin.time.Clock import kotlinx.datetime.LocalDate @@ -58,77 +57,58 @@ fun PatientCard( val fullName = listOfNotNull(patient.givenName, patient.familyName).joinToString(" ").ifBlank { "Unknown" } - CardView( - elevationDp = config.elevation?.floatValue() ?: 2f, - contentPaddingDp = config.padding?.floatValue() ?: 16f, - onClick = onClick, + Row( + modifier = + Modifier.fillMaxWidth() + .clip(RoundedCornerShape(18.dp)) + .then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier) + .padding(horizontal = 12.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + verticalAlignment = Alignment.CenterVertically, ) { - header { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Box( - modifier = - Modifier.size(52.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primaryContainer), - contentAlignment = Alignment.Center, - ) { - Text( - text = initials, - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer, - fontWeight = FontWeight.Bold, - ) - } - Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { - Text( - text = fullName, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - ) - val subtitleParts = buildList { - if (config.showAge != false) { - calculateAge(patient.birthDate?.toString())?.let { add("Age $it") } - } - if (config.showGender != false) { - patient.gender?.let { add(it.replaceFirstChar { c -> c.uppercaseChar() }) } - } - } - if (subtitleParts.isNotEmpty()) { - Text( - text = subtitleParts.joinToString(" · "), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - patient.mrn?.let { - Text( - text = it, - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.outline, - ) - } + Box( + modifier = + Modifier.size(44.dp).clip(CircleShape).background(MaterialTheme.colorScheme.primary), + contentAlignment = Alignment.Center, + ) { + Text( + text = initials, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onPrimary, + fontWeight = FontWeight.Bold, + ) + } + Column(modifier = Modifier.weight(1f)) { + Text( + text = fullName, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold, + ) + val subtitleParts = buildList { + if (config.showAge != false) { + calculateAge(patient.birthDate?.toString())?.let { add("Age $it") } } - Column( - horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(4.dp), - ) { - if (config.showStatusChip != false) { - StatusChip(isActive = patient.active ?: false) - } - if (onClick != null) { - Icon( - imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, - contentDescription = null, - tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f), - modifier = Modifier.size(16.dp), - ) - } + if (config.showGender != false) { + patient.gender?.let { add(it.replaceFirstChar { c -> c.uppercaseChar() }) } } } + if (subtitleParts.isNotEmpty()) { + Text( + text = subtitleParts.joinToString(" · "), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + patient.mrn?.let { + Text( + text = it, + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.outline, + ) + } + } + if (config.showStatusChip != false) { + StatusChip(isActive = patient.active ?: false) } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListRegistrations.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListRegistrations.kt index 74e6fc0d..f2f41db0 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListRegistrations.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientListRegistrations.kt @@ -35,7 +35,10 @@ fun ViewRegistry.registerPatientList() { ) registerLayout( VerticalListRenderer.VIEW_TYPE, - VerticalListRenderer(contentPadding = PaddingValues(16.dp), itemSpacing = 12.dp), + VerticalListRenderer( + contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp), + itemSpacing = 2.dp, + ), ) registerLayout( HorizontalListRenderer.VIEW_TYPE, From 976f6a2f752529d074e1a69fa1d875dd259acb40 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 19:46:48 +0300 Subject: [PATCH 66/73] Redesign login as a flat brand-led screen The sign-in side is now plain surface with real hierarchy instead of an elevated card: a solid brand panel with a monochrome logo mark sits beside it on wide screens and stacks above a rounded sheet on narrow ones. Copy is trimmed to a title, one redirect line, and the action. --- .../composeResources/values/strings.xml | 6 +- .../app/feature/login/LoginScreen.kt | 212 ++++++++++-------- .../app/feature/login/LoginScreenTest.kt | 2 +- 3 files changed, 125 insertions(+), 95 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index 1bfeb9bf..808bc756 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -9,9 +9,9 @@ Player Reference - Sign in to Player Reference - You'll be securely redirected to your organization's sign-in page to continue. - Sign in + Sign in + We'll hand you over to your organization's sign-in page, then bring you straight back. + Continue to sign in Sign-in failed Dismiss diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt index 8bf89a0f..abe56d05 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreen.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -36,20 +37,23 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton 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.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.app_logo @@ -67,9 +71,9 @@ private val LOGIN_EXPANDED_WIDTH_BREAKPOINT = 840.dp /** * PKCE redirect login — no password form; the primary action hands off to the identity provider. - * Branches on width (never on platform), matching this app's existing manual-breakpoint convention - * (`HomeWidthBreakpoint.kt`): Expanded (≥840dp) shows a brand panel beside a centered sign-in card; - * Compact/Medium show a brand band over a sign-in sheet. + * Branches on width (never on platform): Expanded (>= 840dp) sets a solid brand panel beside a flat + * sign-in side; Compact/Medium stack a brand band over a rounded sign-in sheet. Both are flat — the + * brand color carries the identity, so there is no elevated card. */ @Composable fun LoginScreen( @@ -91,67 +95,39 @@ fun LoginScreen( } } -/** Expanded: brand panel (left) + centered sign-in card on a grey field (right). */ +/** Expanded: solid brand panel (left) beside a flat sign-in side (right). */ @Composable private fun ExpandedLogin(signingIn: Boolean, onSignIn: () -> Unit) { Row(Modifier.fillMaxSize()) { - Box(Modifier.weight(1f).fillMaxHeight().background(MaterialTheme.colorScheme.primary)) { - Column( - modifier = Modifier.fillMaxSize().safeDrawingPadding().padding(48.dp), - verticalArrangement = Arrangement.Center, - ) { - Wordmark(MaterialTheme.colorScheme.onPrimary) - } + Column( + modifier = + Modifier.weight(5f) + .fillMaxHeight() + .background(MaterialTheme.colorScheme.primary) + .safeDrawingPadding() + .padding(44.dp) + ) { + MonoLogoMark(64.dp) + Spacer(Modifier.height(22.dp)) + Text( + text = stringResource(Res.string.login_brand), + style = MaterialTheme.typography.displaySmall, + color = MaterialTheme.colorScheme.onPrimary, + fontWeight = FontWeight.Bold, + ) } Box( modifier = - Modifier.weight(1f) + Modifier.weight(6f) .fillMaxHeight() - .background(MaterialTheme.colorScheme.background) + .background(MaterialTheme.colorScheme.surface) .safeDrawingPadding() - .padding(32.dp), + .padding(horizontal = 48.dp), contentAlignment = Alignment.Center, ) { - Surface( - modifier = Modifier.widthIn(max = 424.dp).fillMaxWidth(), - shape = RoundedCornerShape(20.dp), - color = MaterialTheme.colorScheme.surface, - shadowElevation = 8.dp, - ) { - Column( - modifier = Modifier.padding(40.dp), - verticalArrangement = Arrangement.spacedBy(20.dp), - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(10.dp), - ) { - Image( - painter = painterResource(Res.drawable.app_logo), - contentDescription = null, - modifier = Modifier.size(28.dp), - ) - Text( - text = stringResource(Res.string.login_brand), - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Bold, - ) - } - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - Text( - stringResource(Res.string.login_card_title), - style = MaterialTheme.typography.headlineSmall, - ) - Text( - text = stringResource(Res.string.login_redirect_hint), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - SignInButton(signingIn, onSignIn) - } + Column(modifier = Modifier.widthIn(max = 420.dp).fillMaxWidth()) { + SignInContent(signingIn, onSignIn, showBrandRow = true) } } } @@ -160,17 +136,23 @@ private fun ExpandedLogin(signingIn: Boolean, onSignIn: () -> Unit) { /** Compact/Medium: brand band over a rounded sign-in sheet; button pinned low. */ @Composable private fun CompactLogin(signingIn: Boolean, onSignIn: () -> Unit) { - Column(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) { + Column(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface)) { Column( modifier = Modifier.fillMaxWidth() .background(MaterialTheme.colorScheme.primary) .statusBarsPadding() - .padding(horizontal = 32.dp, vertical = 32.dp), + .padding(horizontal = 28.dp, vertical = 44.dp), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(12.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), ) { - Wordmark(MaterialTheme.colorScheme.onPrimary) + MonoLogoMark(56.dp) + Text( + text = stringResource(Res.string.login_brand), + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onPrimary, + fontWeight = FontWeight.Bold, + ) } Column( @@ -182,44 +164,59 @@ private fun CompactLogin(signingIn: Boolean, onSignIn: () -> Unit) { RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp), ) .navigationBarsPadding() - .padding(horizontal = 24.dp) - .padding(top = 28.dp, bottom = 20.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), + .padding(horizontal = 26.dp) + .padding(top = 32.dp, bottom = 22.dp), + verticalArrangement = Arrangement.spacedBy(10.dp), ) { - Text( - stringResource(Res.string.login_card_title), - style = MaterialTheme.typography.headlineSmall, + SignInContent(signingIn, onSignIn, showBrandRow = false, buttonAtBottom = true) + } + } +} + +@Composable +private fun ColumnScope.SignInContent( + signingIn: Boolean, + onSignIn: () -> Unit, + showBrandRow: Boolean, + buttonAtBottom: Boolean = false, +) { + if (showBrandRow) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + Image( + painter = painterResource(Res.drawable.app_logo), + contentDescription = null, + modifier = Modifier.size(24.dp), ) Text( - text = stringResource(Res.string.login_redirect_hint), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, + text = stringResource(Res.string.login_brand).uppercase(), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.primary, + fontWeight = FontWeight.Bold, ) - Spacer(Modifier.weight(1f)) - SignInButton(signingIn, onSignIn) - Spacer(Modifier.height(8.dp)) } + Spacer(Modifier.height(26.dp)) } -} -@Composable -private fun Wordmark(color: androidx.compose.ui.graphics.Color) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - Image( - painter = painterResource(Res.drawable.app_logo), - contentDescription = null, - modifier = Modifier.size(32.dp), - ) - Text( - text = stringResource(Res.string.login_brand), - style = MaterialTheme.typography.titleLarge, - color = color, - fontWeight = FontWeight.Bold, - ) + Text( + text = stringResource(Res.string.login_card_title), + style = MaterialTheme.typography.headlineMedium, + ) + Spacer(Modifier.height(10.dp)) + Text( + text = stringResource(Res.string.login_redirect_hint), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + if (buttonAtBottom) { + Spacer(Modifier.weight(1f)) + } else { + Spacer(Modifier.height(28.dp)) } + SignInButton(signingIn, onSignIn) } @Composable @@ -227,9 +224,9 @@ private fun SignInButton(signingIn: Boolean, onSignIn: () -> Unit) { Button( onClick = onSignIn, enabled = !signingIn, - shape = RoundedCornerShape(8.dp), + shape = RoundedCornerShape(14.dp), contentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp), - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().height(52.dp), ) { if (signingIn) { CircularProgressIndicator( @@ -238,7 +235,40 @@ private fun SignInButton(signingIn: Boolean, onSignIn: () -> Unit) { color = MaterialTheme.colorScheme.onPrimary, ) } else { - Text(stringResource(Res.string.login_sign_in).uppercase()) + Text( + text = stringResource(Res.string.login_sign_in), + style = MaterialTheme.typography.titleMedium, + ) + Spacer(Modifier.size(8.dp)) + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowForward, + contentDescription = null, + modifier = Modifier.size(18.dp), + ) + } + } +} + +/** The 2x2 brand mark rendered monochrome for the solid brand panel. */ +@Composable +private fun MonoLogoMark(size: Dp) { + val gap = size * 0.09f + val cell = (size - gap) / 2 + val corner = cell * 0.28f + val alphas = listOf(0.9f, 0.28f, 0.28f, 0.55f) + val cellModifier = { index: Int -> + Modifier.size(cell) + .clip(RoundedCornerShape(corner)) + .background(Color.White.copy(alpha = alphas[index])) + } + Column(verticalArrangement = Arrangement.spacedBy(gap)) { + Row(horizontalArrangement = Arrangement.spacedBy(gap)) { + Box(cellModifier(0)) + Box(cellModifier(1)) + } + Row(horizontalArrangement = Arrangement.spacedBy(gap)) { + Box(cellModifier(2)) + Box(cellModifier(3)) } } } diff --git a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt index fd2cf2e8..6715ed47 100644 --- a/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt +++ b/ohs-player-reference-app/src/jvmTest/kotlin/dev/ohs/player/reference/app/feature/login/LoginScreenTest.kt @@ -41,7 +41,7 @@ class LoginScreenTest { } } - onNodeWithText("Sign in", ignoreCase = true).performClick() + onNodeWithText("Continue to sign in", ignoreCase = true).performClick() assertTrue(clicked) } From 52a718ab9e8d5c32c5a19fd2ec446aa67091743d Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 19:47:08 +0300 Subject: [PATCH 67/73] Rework the questionnaire top bar The back arrow returns to the start of the bar and close moves to the end. On wide screens the bar's contents track the centered form column, so the controls sit at the form's edges rather than the screen's. --- .../composeResources/values/strings.xml | 1 + .../questionnaire/QuestionnaireHostScreen.kt | 59 +++++++++++++------ 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index 808bc756..6ae64f0f 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -60,5 +60,6 @@ Questionnaire Close + Back Retry \ No newline at end of file diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt index 6d057562..e444c170 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/questionnaire/QuestionnaireHostScreen.kt @@ -18,15 +18,18 @@ package dev.ohs.player.reference.app.feature.questionnaire import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Close import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -34,8 +37,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -45,6 +46,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import dev.ohs.fhir.datacapture.Questionnaire import dev.ohs.fhir.datacapture.QuestionnaireConfig @@ -54,6 +56,7 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.delay import kotlinx.coroutines.launch import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_back import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_close import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_retry import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.questionnaire_title @@ -61,7 +64,6 @@ import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf -@OptIn(ExperimentalMaterial3Api::class) @Composable fun QuestionnaireHostScreen( questionnaireId: String, @@ -100,23 +102,42 @@ fun QuestionnaireHostScreen( Scaffold( topBar = { - TopAppBar( - title = { Text(title ?: stringResource(Res.string.questionnaire_title)) }, - navigationIcon = { - IconButton(onClick = onBack) { - Icon( - imageVector = Icons.Filled.Close, - contentDescription = stringResource(Res.string.questionnaire_close), - tint = MaterialTheme.colorScheme.onPrimary, + Surface( + color = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + ) { + Box(modifier = Modifier.fillMaxWidth().statusBarsPadding()) { + Row( + modifier = + Modifier.align(Alignment.Center) + .widthIn(max = 720.dp) + .fillMaxWidth() + .height(64.dp) + .padding(horizontal = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + IconButton(onClick = onBack) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(Res.string.questionnaire_back), + ) + } + Text( + text = title ?: stringResource(Res.string.questionnaire_title), + style = MaterialTheme.typography.titleLarge, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f).padding(horizontal = 4.dp), ) + IconButton(onClick = onBack) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = stringResource(Res.string.questionnaire_close), + ) + } } - }, - colors = - TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.primary, - titleContentColor = MaterialTheme.colorScheme.onPrimary, - ), - ) + } + } } ) { padding -> Box( From 6c1e455344604b2b29f220cf3e6d5aa2a2619a3c Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 20:26:25 +0300 Subject: [PATCH 68/73] Flatten the patient and household detail screens Detail sections lose their cards: each is now an overline label with a divider and flat rows, collapsible via a plus/minus toggle. The patient hero matches the household one, and the shared clinical row shows a leading severity dot instead of an accent bar. User-facing strings in these renderers move to Compose resources. --- .../composeResources/values/strings.xml | 21 +++ .../common/SectionCardLayoutRenderer.kt | 155 +++++++++--------- .../app/feature/component/common/StatusRow.kt | 28 +--- .../app/feature/group/list/GroupCard.kt | 13 +- .../group/profile/GroupHeaderRenderer.kt | 111 ++++++------- .../group/profile/MemberItemRenderer.kt | 16 +- .../app/feature/patient/list/PatientCard.kt | 12 +- .../patient/profile/AllergyItemRenderer.kt | 9 +- .../profile/AllergyReactionItemRenderer.kt | 5 +- .../patient/profile/ConditionItemRenderer.kt | 13 +- .../patient/profile/ContactItemRenderer.kt | 5 +- .../profile/ImmunizationItemRenderer.kt | 15 +- .../patient/profile/MedicationItemRenderer.kt | 9 +- .../patient/profile/PatientHeaderRenderer.kt | 84 +++------- .../profile/PatientProfileScreenTest.kt | 4 +- 15 files changed, 244 insertions(+), 256 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml index 6ae64f0f..906ae8ee 100644 --- a/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml +++ b/ohs-player-reference-app/src/commonMain/composeResources/values/strings.xml @@ -62,4 +62,25 @@ Close Back Retry + + + Age %1$s + MRN %1$s + Unknown + + Collapse + Expand + + Household + Unknown Household + Head: %1$s + %1$s member + %1$s members + + Unknown condition + Since %1$s + Unknown medication + Unknown substance + Unknown vaccine + Given %1$s \ No newline at end of file diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/SectionCardLayoutRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/SectionCardLayoutRenderer.kt index c1567c98..22dd13e4 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/SectionCardLayoutRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/SectionCardLayoutRenderer.kt @@ -19,6 +19,7 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically 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.Column @@ -29,15 +30,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.KeyboardArrowDown -import androidx.compose.material.icons.filled.KeyboardArrowUp -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -50,18 +45,26 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import dev.ohs.player.generated.config.SectionCardConfig import dev.ohs.player.library.renderer.ConfiguredRenderer import dev.ohs.player.library.renderer.LayoutRenderer import dev.ohs.player.library.renderer.RenderOptions +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.section_collapse +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.section_expand +import org.jetbrains.compose.resources.stringResource /** - * Layout renderer that wraps a list of items inside a titled section card. Registered under + * Layout renderer that introduces a list of items with a flat overline section label — no card, + * border, or per-row divider. Registered under * [dev.ohs.player.generated.viewtype.ViewTypeCS.SectionCard]. * - * Supports optional item-count badge and collapsible behavior driven by [SectionCardConfig]. + * Supports an optional item count and collapsible behavior driven by [SectionCardConfig]. */ class SectionCardLayoutRenderer( private val title: String, @@ -80,86 +83,74 @@ class SectionCardLayoutRenderer( ) { var expanded by rememberSaveable { mutableStateOf(true) } val tint = iconTint ?: MaterialTheme.colorScheme.primary + val collapsible = config.collapsible == true - Card( - modifier = modifier.fillMaxWidth(), - elevation = - CardDefaults.elevatedCardElevation( - defaultElevation = (config.elevation?.floatValue() ?: 2f).dp - ), - ) { - Column { - Box(modifier = Modifier.fillMaxWidth().height(3.dp).background(tint)) - Column(modifier = Modifier.padding((config.padding?.floatValue() ?: 16f).dp)) { - Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Column(modifier = modifier.fillMaxWidth().padding(horizontal = 4.dp)) { + Row( + modifier = + Modifier.fillMaxWidth() + .then(if (collapsible) Modifier.clickable { expanded = !expanded } else Modifier) + .padding(top = 12.dp, bottom = 8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = tint, + modifier = Modifier.size(18.dp), + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = title.uppercase(), + style = MaterialTheme.typography.labelLarge, + letterSpacing = 0.8.sp, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.weight(1f), + ) + if (config.showItemCount != false) { + Text( + text = items.size.toString(), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + if (collapsible) { + Spacer(modifier = Modifier.width(12.dp)) + val toggleLabel = + stringResource(if (expanded) Res.string.section_collapse else Res.string.section_expand) + val barColor = MaterialTheme.colorScheme.onSurfaceVariant + Box( + modifier = Modifier.size(20.dp).semantics { contentDescription = toggleLabel }, + contentAlignment = Alignment.Center, + ) { Box( - modifier = - Modifier.size(32.dp).clip(CircleShape).background(tint.copy(alpha = 0.12f)), - contentAlignment = Alignment.Center, - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = tint, - modifier = Modifier.size(18.dp), - ) - } - Spacer(modifier = Modifier.width(10.dp)) - Text( - text = title, - style = MaterialTheme.typography.titleSmall, - fontWeight = FontWeight.SemiBold, - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.weight(1f), + Modifier.width(12.dp).height(2.dp).clip(RoundedCornerShape(1.dp)).background(barColor) ) - if (config.showItemCount != false) { + if (!expanded) { Box( - modifier = - Modifier.clip(CircleShape) - .background(tint.copy(alpha = 0.12f)) - .padding(horizontal = 8.dp, vertical = 2.dp) - ) { - Text( - text = items.size.toString(), - style = MaterialTheme.typography.labelMedium, - color = tint, - fontWeight = FontWeight.Bold, - ) - } - } - if (config.collapsible == true) { - IconButton(onClick = { expanded = !expanded }, modifier = Modifier.size(32.dp)) { - Icon( - imageVector = - if (expanded) Icons.Default.KeyboardArrowUp - else Icons.Default.KeyboardArrowDown, - contentDescription = if (expanded) "Collapse" else "Expand", - tint = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.size(20.dp), - ) - } + Modifier.width(2.dp) + .height(12.dp) + .clip(RoundedCornerShape(1.dp)) + .background(barColor) + ) } } + } + } - AnimatedVisibility( - visible = expanded, - enter = expandVertically(), - exit = shrinkVertically(), - ) { - Column(verticalArrangement = Arrangement.spacedBy(0.dp)) { - HorizontalDivider( - modifier = Modifier.padding(top = 10.dp, bottom = 4.dp), - color = MaterialTheme.colorScheme.outlineVariant, - ) - items.forEachIndexed { i, item -> - if (i > 0) - HorizontalDivider( - modifier = Modifier.padding(vertical = 2.dp), - color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f), - ) - component.Render(item, RenderOptions(onClick = { onItemClick(item) })) - } - } + AnimatedVisibility( + visible = expanded, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + HorizontalDivider( + modifier = Modifier.padding(bottom = 4.dp), + color = MaterialTheme.colorScheme.outlineVariant, + ) + items.forEach { item -> + component.Render(item, RenderOptions(onClick = { onItemClick(item) })) } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/StatusRow.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/StatusRow.kt index 212c3888..7efa3c9d 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/StatusRow.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/component/common/StatusRow.kt @@ -21,10 +21,9 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -39,10 +38,10 @@ import androidx.compose.ui.unit.dp data class StatusChipData(val label: String, val containerColor: Color, val contentColor: Color) /** - * A list row with an optional left accent bar, a bold [title] with an optional [subtitle], and an - * optional trailing status [Chip]. Shared by the medical profile item renderers (allergy, - * condition, medication, immunization), which differ only in how they map their state to these - * fields. + * A flat list row with an optional leading severity dot, a bold [title] with an optional + * [subtitle], and an optional trailing status [Chip]. Shared by the medical profile item renderers + * (allergy, condition, medication, immunization), which differ only in how they map their state to + * these fields. */ @Composable fun StatusRow( @@ -51,24 +50,15 @@ fun StatusRow( subtitle: String? = null, subtitleColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, accentColor: Color? = null, - rowBackground: Color = Color.Transparent, status: StatusChipData? = null, ) { Row( - modifier = - modifier - .fillMaxWidth() - .clip(RoundedCornerShape(6.dp)) - .background(rowBackground) - .padding(vertical = 6.dp), + modifier = modifier.fillMaxWidth().padding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(10.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), ) { if (accentColor != null) { - Box( - modifier = - Modifier.width(3.dp).height(36.dp).clip(RoundedCornerShape(2.dp)).background(accentColor) - ) + Box(modifier = Modifier.size(10.dp).clip(CircleShape).background(accentColor)) } Column(modifier = Modifier.weight(1f)) { Text( diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt index d7e960b2..8cb62aae 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupCard.kt @@ -38,6 +38,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import dev.ohs.player.generated.config.GroupCardConfig import dev.ohs.player.generated.state.GroupListState +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_member_count_one +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_member_count_other +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_unknown_name +import org.jetbrains.compose.resources.stringResource /** * Selected household id for the two-pane list-detail highlight; null when there is no open pane. @@ -50,10 +55,14 @@ fun GroupCard( config: GroupCardConfig = GroupCardConfig(), onClick: (() -> Unit)? = null, ) { - val name = group.groupName ?: "Unknown Household" + val name = group.groupName ?: stringResource(Res.string.group_unknown_name) val initials = name.trim().firstOrNull()?.uppercaseChar()?.toString() ?: "H" val count = group.memberCount ?: "0" - val memberLabel = if (count == "1") "1 member" else "$count members" + val memberLabel = + stringResource( + if (count == "1") Res.string.group_member_count_one else Res.string.group_member_count_other, + count, + ) val selected = group.groupId != null && group.groupId == LocalSelectedGroupId.current Row( diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupHeaderRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupHeaderRenderer.kt index 1ad51dde..1b104daa 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupHeaderRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/GroupHeaderRenderer.kt @@ -24,12 +24,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -42,7 +36,12 @@ import dev.ohs.player.generated.config.GroupHeaderConfig import dev.ohs.player.generated.state.GroupHeaderState import dev.ohs.player.library.renderer.ComponentRenderer import dev.ohs.player.library.renderer.RenderOptions -import dev.ohs.player.reference.app.feature.component.common.Chip +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_default_name +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_head +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_member_count_one +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.group_member_count_other +import org.jetbrains.compose.resources.stringResource class GroupHeaderRenderer : ComponentRenderer { @Composable @@ -57,72 +56,58 @@ fun GroupHeaderCard( config: GroupHeaderConfig = GroupHeaderConfig(), modifier: Modifier = Modifier, ) { - val name = item.groupName ?: "Household" + val name = item.groupName ?: stringResource(Res.string.group_default_name) val initials = name.trim().firstOrNull()?.uppercaseChar()?.toString() ?: "H" val memberCount = item.memberCount ?: "0" val headName = listOfNotNull(item.headGivenName, item.headFamilyName).joinToString(" ").ifBlank { null } - - Card( - modifier = modifier.fillMaxWidth(), - elevation = - CardDefaults.elevatedCardElevation( - defaultElevation = (config.elevation?.floatValue() ?: 2f).dp - ), - ) { - Row( - modifier = Modifier.fillMaxWidth().padding((config.padding?.floatValue() ?: 20f).dp), - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Box( - modifier = - Modifier.size(72.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primaryContainer), - contentAlignment = Alignment.Center, - ) { - Text( - text = initials, - style = MaterialTheme.typography.headlineMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer, - fontWeight = FontWeight.Bold, - ) - } - Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) { - Text(text = name, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold) - HorizontalDivider( - modifier = Modifier.padding(top = 4.dp, bottom = 4.dp), - color = MaterialTheme.colorScheme.outlineVariant, - ) + val meta = + buildList { if (config.showMemberCount != false) { - Row { - Chip( - label = "$memberCount member${if (memberCount == "1") "" else "s"}", - containerColor = MaterialTheme.colorScheme.surfaceVariant, - contentColor = MaterialTheme.colorScheme.onSurfaceVariant, + add( + stringResource( + if (memberCount == "1") Res.string.group_member_count_one + else Res.string.group_member_count_other, + memberCount, ) - } + ) } if (config.showHeadName != false && headName != null) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp), - ) { - Icon( - imageVector = Icons.Default.Person, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.size(14.dp), - ) - Text( - text = "Head: $headName", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } + add(stringResource(Res.string.group_head, headName)) } } + .joinToString(" · ") + + Row( + modifier = modifier.fillMaxWidth().padding((config.padding?.floatValue() ?: 20f).dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = + Modifier.size(72.dp).clip(CircleShape).background(MaterialTheme.colorScheme.primary), + contentAlignment = Alignment.Center, + ) { + Text( + text = initials, + style = MaterialTheme.typography.headlineMedium, + color = MaterialTheme.colorScheme.onPrimary, + fontWeight = FontWeight.Bold, + ) + } + Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) { + Text( + text = name, + style = MaterialTheme.typography.headlineSmall, + fontWeight = FontWeight.Bold, + ) + if (meta.isNotEmpty()) { + Text( + text = meta, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/MemberItemRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/MemberItemRenderer.kt index 3b992f5b..9f31cdad 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/MemberItemRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/profile/MemberItemRenderer.kt @@ -43,6 +43,8 @@ import dev.ohs.player.library.renderer.RenderOptions import dev.ohs.player.reference.app.feature.component.common.Chip import dev.ohs.player.reference.app.feature.patient.list.calculateAge import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.label_age +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.name_unknown import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.relationship_child import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.relationship_guardian import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.relationship_non_relative @@ -79,7 +81,7 @@ fun MemberItemRow( .ifBlank { "?" } val fullName = listOfNotNull(item.memberGivenName, item.memberFamilyName).joinToString(" ").ifBlank { - "Unknown" + stringResource(Res.string.name_unknown) } val relationshipLabel = item.relationshipCode?.toRelationshipLabel() @@ -94,15 +96,13 @@ fun MemberItemRow( ) { Box( modifier = - Modifier.size(40.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primaryContainer), + Modifier.size(44.dp).clip(CircleShape).background(MaterialTheme.colorScheme.primary), contentAlignment = Alignment.Center, ) { Text( text = initials, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onPrimaryContainer, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onPrimary, fontWeight = FontWeight.Bold, ) } @@ -114,7 +114,9 @@ fun MemberItemRow( ) val subtitleParts = buildList { if (config.showAge != false) { - calculateAge(item.memberBirthDate?.toString())?.let { add("Age $it") } + calculateAge(item.memberBirthDate?.toString())?.let { + add(stringResource(Res.string.label_age, it)) + } } if (config.showGender != false) { item.memberGender?.let { add(it.replaceFirstChar { c -> c.uppercaseChar() }) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt index 5a95eef2..99c88080 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/list/PatientCard.kt @@ -41,6 +41,10 @@ import kotlin.time.Clock import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.todayIn +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.label_age +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.name_unknown +import org.jetbrains.compose.resources.stringResource @Composable fun PatientCard( @@ -55,7 +59,9 @@ fun PatientCard( } .ifBlank { "?" } val fullName = - listOfNotNull(patient.givenName, patient.familyName).joinToString(" ").ifBlank { "Unknown" } + listOfNotNull(patient.givenName, patient.familyName).joinToString(" ").ifBlank { + stringResource(Res.string.name_unknown) + } Row( modifier = @@ -86,7 +92,9 @@ fun PatientCard( ) val subtitleParts = buildList { if (config.showAge != false) { - calculateAge(patient.birthDate?.toString())?.let { add("Age $it") } + calculateAge(patient.birthDate?.toString())?.let { + add(stringResource(Res.string.label_age, it)) + } } if (config.showGender != false) { patient.gender?.let { add(it.replaceFirstChar { c -> c.uppercaseChar() }) } diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/AllergyItemRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/AllergyItemRenderer.kt index 9650dfe8..f71a6b02 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/AllergyItemRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/AllergyItemRenderer.kt @@ -24,6 +24,9 @@ import dev.ohs.player.library.renderer.ComponentRenderer import dev.ohs.player.library.renderer.RenderOptions import dev.ohs.player.reference.app.feature.component.common.StatusChipData import dev.ohs.player.reference.app.feature.component.common.StatusRow +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.allergy_unknown +import org.jetbrains.compose.resources.stringResource class AllergyItemRenderer : ComponentRenderer { @Composable @@ -34,16 +37,12 @@ class AllergyItemRenderer : ComponentRenderer { @@ -56,7 +59,7 @@ fun AllergyReactionItemRow( ) { Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { Text( - text = item.substance ?: "Unknown substance", + text = item.substance ?: stringResource(Res.string.allergy_unknown), style = MaterialTheme.typography.bodyMedium, ) if (config.showManifestation != false) { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ConditionItemRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ConditionItemRenderer.kt index 54196158..f90631ff 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ConditionItemRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ConditionItemRenderer.kt @@ -24,6 +24,10 @@ import dev.ohs.player.library.renderer.ComponentRenderer import dev.ohs.player.library.renderer.RenderOptions import dev.ohs.player.reference.app.feature.component.common.StatusChipData import dev.ohs.player.reference.app.feature.component.common.StatusRow +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.condition_since +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.condition_unknown +import org.jetbrains.compose.resources.stringResource private val AmberAccent = Color(0xFFE37400) @@ -35,7 +39,6 @@ class ConditionItemRenderer : ComponentRenderer AmberAccent @@ -43,11 +46,13 @@ class ConditionItemRenderer : ComponentRenderer MaterialTheme.colorScheme.outline } StatusRow( - title = item.conditionCode ?: "Unknown condition", + title = item.conditionCode ?: stringResource(Res.string.condition_unknown), modifier = options.modifier, - subtitle = if (config.showOnsetDate != false) item.onsetDate?.let { "Since $it" } else null, + subtitle = + if (config.showOnsetDate != false) + item.onsetDate?.let { stringResource(Res.string.condition_since, it) } + else null, accentColor = accentColor, - rowBackground = if (isActive) AmberAccent.copy(alpha = 0.06f) else Color.Transparent, status = if (config.showStatus != false) item.conditionStatus?.let { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ContactItemRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ContactItemRenderer.kt index 14209486..f5dfe16a 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ContactItemRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ContactItemRenderer.kt @@ -36,6 +36,9 @@ import dev.ohs.player.generated.state.PatientContactState import dev.ohs.player.library.renderer.ComponentRenderer import dev.ohs.player.library.renderer.RenderOptions import dev.ohs.player.reference.app.feature.component.common.Chip +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.name_unknown +import org.jetbrains.compose.resources.stringResource class ContactItemRenderer : ComponentRenderer { @Composable @@ -62,7 +65,7 @@ fun ContactItemRow( .ifBlank { "?" } val fullName = listOfNotNull(item.contactGivenName, item.contactFamilyName).joinToString(" ").ifBlank { - "Unknown" + stringResource(Res.string.name_unknown) } Row( diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ImmunizationItemRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ImmunizationItemRenderer.kt index 3e1af976..da979c89 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ImmunizationItemRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/ImmunizationItemRenderer.kt @@ -17,13 +17,16 @@ package dev.ohs.player.reference.app.feature.patient.profile import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color import dev.ohs.player.generated.config.ImmunizationItemConfig import dev.ohs.player.generated.state.PatientImmunizationState import dev.ohs.player.library.renderer.ComponentRenderer import dev.ohs.player.library.renderer.RenderOptions import dev.ohs.player.reference.app.feature.component.common.StatusChipData import dev.ohs.player.reference.app.feature.component.common.StatusRow +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.immunization_given +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.immunization_unknown +import org.jetbrains.compose.resources.stringResource class ImmunizationItemRenderer : ComponentRenderer { @@ -35,14 +38,14 @@ class ImmunizationItemRenderer : ) { val isCompleted = item.immunizationStatus?.lowercase() == "completed" StatusRow( - title = item.vaccineName ?: "Unknown vaccine", + title = item.vaccineName ?: stringResource(Res.string.immunization_unknown), modifier = options.modifier, - subtitle = if (config.showDate != false) item.occurrenceDate?.let { "Given $it" } else null, + subtitle = + if (config.showDate != false) + item.occurrenceDate?.let { stringResource(Res.string.immunization_given, it) } + else null, accentColor = if (isCompleted) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.outline, - rowBackground = - if (isCompleted) MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = 0.25f) - else Color.Transparent, status = if (config.showStatus != false) item.immunizationStatus?.let { diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/MedicationItemRenderer.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/MedicationItemRenderer.kt index 9a557fb1..859fb514 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/MedicationItemRenderer.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/MedicationItemRenderer.kt @@ -17,13 +17,15 @@ package dev.ohs.player.reference.app.feature.patient.profile import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color import dev.ohs.player.generated.config.MedicationItemConfig import dev.ohs.player.generated.state.PatientMedicationState import dev.ohs.player.library.renderer.ComponentRenderer import dev.ohs.player.library.renderer.RenderOptions import dev.ohs.player.reference.app.feature.component.common.StatusChipData import dev.ohs.player.reference.app.feature.component.common.StatusRow +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.Res +import ohsplayerreferenceclientapp.ohs_player_reference_app.generated.resources.medication_unknown +import org.jetbrains.compose.resources.stringResource class MedicationItemRenderer : ComponentRenderer { @Composable @@ -34,14 +36,11 @@ class MedicationItemRenderer : ComponentRenderer { @Composable @@ -67,7 +67,22 @@ fun PatientHeaderCard( } .ifBlank { "?" } val fullName = - listOfNotNull(patient.givenName, patient.familyName).joinToString(" ").ifBlank { "Unknown" } + listOfNotNull(patient.givenName, patient.familyName).joinToString(" ").ifBlank { + stringResource(Res.string.name_unknown) + } + val meta = + buildList { + calculateAge(patient.birthDate?.toString())?.let { + add(stringResource(Res.string.label_age, it)) + } + if (config.showGender != false) { + patient.gender?.let { add(it.replaceFirstChar { c -> c.uppercaseChar() }) } + } + if (config.showMrn != false) + patient.mrn?.let { add(stringResource(Res.string.label_mrn, it)) } + patient.phone?.let { add(it) } + } + .joinToString(" · ") Row( modifier = modifier.fillMaxWidth(), @@ -76,74 +91,29 @@ fun PatientHeaderCard( ) { Box( modifier = - Modifier.size(80.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primaryContainer), + Modifier.size(72.dp).clip(CircleShape).background(MaterialTheme.colorScheme.primary), contentAlignment = Alignment.Center, ) { Text( text = initials, style = MaterialTheme.typography.headlineMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer, + color = MaterialTheme.colorScheme.onPrimary, fontWeight = FontWeight.Bold, ) } - Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) { + Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(6.dp)) { Text( text = fullName, - style = MaterialTheme.typography.titleLarge, + style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold, ) - val ageParts = buildList { - calculateAge(patient.birthDate?.toString())?.let { add("Age $it") } - if (config.showGender != false) { - patient.gender?.let { add(it.replaceFirstChar { c -> c.uppercaseChar() }) } - } - } - if (ageParts.isNotEmpty()) { + if (meta.isNotEmpty()) { Text( - text = ageParts.joinToString(" · "), + text = meta, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) } - HorizontalDivider( - modifier = Modifier.padding(top = 4.dp, bottom = 4.dp), - color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f), - ) - if (config.showMrn != false) { - patient.mrn?.let { mrn -> - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = "MRN", - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.outline, - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = mrn, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface, - ) - } - } - } - patient.phone?.let { phone -> - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = "Phone", - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.outline, - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = phone, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface, - ) - } - } - Spacer(modifier = Modifier.height(4.dp)) if (config.showStatus != false) { StatusChip(isActive = patient.active ?: false) } diff --git a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreenTest.kt b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreenTest.kt index 5ef3a036..b484ee14 100644 --- a/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreenTest.kt +++ b/ohs-player-reference-app/src/commonTest/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreenTest.kt @@ -70,9 +70,9 @@ class PatientProfileScreenTest { val scrollable = onNode(hasScrollAction()) listOf("Amina Diallo", "Allergies", "Medications", "Conditions", "Immunizations").forEach { text -> - scrollable.performScrollToNode(hasText(text)) + scrollable.performScrollToNode(hasText(text, ignoreCase = true)) assertTrue( - onAllNodesWithText(text).fetchSemanticsNodes().isNotEmpty(), + onAllNodesWithText(text, ignoreCase = true).fetchSemanticsNodes().isNotEmpty(), "Expected to find '$text' after scrolling the patient profile", ) } From 3a9e069c2752a382d1acf2a1c4e05535d2fa7257 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 20:26:38 +0300 Subject: [PATCH 69/73] Drop the empty band under the register title The register list nested its own Scaffold inside the home Scaffold, which re-applied the status-bar inset and left a gap under the title. Zero the inner Scaffold's window insets since the parent already handles them. --- .../reference/app/feature/group/list/GroupListScreen.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt index c1cc4867..07ef5b00 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListScreen.kt @@ -16,6 +16,7 @@ package dev.ohs.player.reference.app.feature.group.list import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -55,13 +56,14 @@ fun GroupListScreen(onGroupClick: (String) -> Unit, onDataCaptureClick: () -> Un } Scaffold( + contentWindowInsets = WindowInsets(0, 0, 0, 0), floatingActionButton = { ExtendedFloatingActionButton( onClick = onDataCaptureClick, icon = { Icon(Icons.Filled.Add, contentDescription = null) }, text = { Text(stringResource(Res.string.group_list_register_household)) }, ) - } + }, ) { padding -> Box(modifier = Modifier.fillMaxSize().padding(padding)) { ListScaffold( From e502f045deedb9f4cae4a720b32fc037a7514741 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 20:26:49 +0300 Subject: [PATCH 70/73] Recenter the launcher icon in its safe zone The adaptive foreground filled too much of the canvas, so the circular mask clipped the logo's corners. Rescale it to about 62% centered and refresh the legacy square and round icons to match. --- .../res/mipmap-hdpi/ic_launcher.png | Bin 5867 -> 1429 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 5634 -> 2324 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 6665 -> 1706 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 2781 -> 1203 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 3323 -> 1853 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 3128 -> 1382 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 8768 -> 1718 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 7603 -> 3204 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 9867 -> 2172 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 18942 -> 3510 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 12675 -> 5131 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 21239 -> 4410 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 32155 -> 4636 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 17573 -> 7753 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 35868 -> 5906 bytes 15 files changed, 0 insertions(+), 0 deletions(-) diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher.png index 162cbcf666255cc226b9a124c5679b4c02fd1863..5884014f5b81d10e48d81274ee3da13df6f1e101 100644 GIT binary patch delta 1380 zcmbV~{Xf$Q0LSMkrRU3CxD=H;*A6FMO|j z?j(fi%(Bg9t)ekc8{3$%*(2L`&fjpK-#(wuPw&_3EwDoGE;ZV;Fch(QrBPo>yE<^-NtktFD68)-XRdB2+5WEP4otRnp?T4A5w9;g~Qqg}o||2^g85k*WXqc@wQc_J?SGh?YT%D+DDwCRkwmFV>G-|? zYt(8$r2;rJ(zhLpm}Z_JilOlr;4^rW!aYxqO@>IJ*sp{wFwkt6B4hH^5W=oQ<6|WEQ=|W%T(p4&vy}H z`N@oDXcn6#i4Ex#@D)p3xk$L-%9@jGD|0B>|6%}WmqTX1U~MqV@n~h$vHX)7we1BL z8}IE~E5h5uU299*YG%9WCwuxr^_TTc_gk6o@ffWEfwX%)-Or-}gNB4(6agQ@j0VML zrz^VpqQKqY;ZsN`&cPtpUmw|;3x^-xNp5S+wybA^^bR)U_D;2?r{dWV+_5}Cd8)~T0aIrYTg;)4^kp87@hLnH&pqZBYODML=HW=;OC`v zfzxTRvBx!x$y&Wc}%d(54rpM$-K_zMmkXq^vSO>P`yYe12l3F!n9nl#=Q9UYy+KZ zRp441YpG}6Q{v*0*qE3RT;@<&=n4n_m7ryC1u+CkpE}q)NhvB!HKWr)jYi;>si_mM zI8L{RX#>Cko%6h$yAN1L&%EKqrxe?qc24YE4nmm15ur=sL#aRNUUKv%cay>W9}7A)w4(UN8;nfj3_Kvw)fZcKHSozj0>}BM+noJ z8%|jAiB$MOp8RQPJq?RIlKkd-%hjW|O^X$7jTx$VKNq&{v2&3`U-Kpp9V5Q2fm1p; zglT7Ja^zpg*k!(+By_huhAx6N(ka`JsBOwE^n!cGQVZT9oWIvV))z%=x3qV>pEJ|% zQNKStnl)Yk;biSG#BNz8I^T(YFAVKJ&ZFoOAn|{13Eu1*SaZorj2l`VxPEsi6T>-_ zkojk31%68e>?II&+)iZP2q$%z!NQ_MczXzR2SeM!7^ZI+Y39^}Z-k5|>iJO#ZLu&& zbop*whz0ItK_q6x`r1hQ9lo6f?iDWh9@xsi)iyfz#7$;|y$?=-kvbx@1-h@jS`Iwn Z9((yUFn6YFabjC;GN;x59LNuOf?i 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 literal 5634 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 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/ohs-player-reference-app/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png index 49db6858a8e18753fa402d6d20709be425cf6d1b..8d57df0e5e3af77cbd6e674fbc8ab790a489c6a1 100644 GIT binary patch literal 1706 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ΝOI;U3;56Xa}} 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 LNkvXXu0mjfA`}Fv diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-mdpi/ic_launcher.png b/ohs-player-reference-app/src/androidMain/res/mipmap-mdpi/ic_launcher.png index f49ec687781527476f90869cb7d83168696ada0c..30e9cebc740892e0b3e622c7a42b34984ba87faa 100644 GIT binary patch delta 1152 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 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.png b/ohs-player-reference-app/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.png index 2974ec25588c51623b85c2a32adce157978a133f..33e3bc6647b21a8afd8863a1dce066b42bc2f087 100644 GIT binary patch literal 1853 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz3?z5#SpFYKDI|LY`7$t6sWC7#v@kII0tz*} zU|=XUU|@Kaz`$TNgMmT3V9u^U8wLiZI{`i+u0RD*E=Q1&Zrc%Lge-;0MplW&MpKL? z3*34uHr4Nr(s<7j7V2JsJbeaENSS#7PLU)tv`Ha1pEw zxH7B^G%c8F@F+l*f@_2eLPYRzk)m)xenaqWIj_#ZtFPwDi z>pW$TWB>kaHTxoB|Ll_1&A+0%7jRyX2>kWh;Dbro@8}=C%d38dFmJ92-n7NOH}8>Z z(r3pNhL6ul&3$O`HB#lzySd3iAG~=*I{2I%ATzy9+}HtE>l*!&%@FSq#kgKqe4#Rz5BTve-*JeO=jK zvCH!7$|`GYkOj&e@N{tu$+-9S+V}9Yg)+xK=9hXu-nO|o^(gPm)DV%@GZP|D+`Bzh zX{K*r(56Kqf{My6-?&cRt3Gf2k7;i4{_XqZzyJRCu-20A`JVaq`_EO*dH>_#*+LQ5 zKp?sjf9k}I7SE(r=7y`&&ZQ(~PDsg8-MVd~Xj=OF2QQA~th&A;a*n&+b4>CV3awpgeQ8scYMy4acj;ua+)#(N zw;mg;6gqWy^8At)9Eo$S7V9Wy3w-|c%-n9m(pQ&6TP1Hd%vdk~zq*9kFtl7GEr~nn zALA|0{&@!1x(yC-DEDl*fAmd)&V=Jt|22Isu^)L-`_tE6;&uNa3w8l%fg}EGP1hD2 zv^W#Umv~U9&L~Brg;%=o@EkXWYbONm9yxP7^bVJ7>t>6*Eq^5pwSB@$MQ8W(if%sE z>n;~Ay3^BkPtD(%F^8I*)i~6|&Izmg&O2mMdh3g%@x`#CtxsRR6<5$H2)mF|mXv9i z{?2CE&v)EByT56^lK0wZFvnAIQ>4g2kxYfH6`ofvy+{pp)sNp;6l!4{DJv{3K3_q@ zuF&^Vl#S>G zkCQ(yD1E5Jw|fdx;`H?&CNEhnP_|a2eQ6x~8fo*=$(wi%>5N z^rxMX{Bm|qQF9)BoV@&e-NaAx_gDP%+HkV&@3MgR)q8d8+qY^}{eSp)`FWrCeRav_ t|88n}?|Rfn)!u8OyD~U|iKHgjGgj4EZs>R)$PY~344$rjF6*2UngF4MLd*aF literal 3323 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 auV<8a*B6$axKO6`6Y%p6@M`dk%KR5XuOPSp delta 3093 zcmV+w4C?dd3b+`M7zq#t00007k|o5EAufLa24YJ`MF0Q)|Ns99Sohoj000McNliru z>IWSSI0SQYDyIMd3$95-K~#9!?VNjD)K%Wc-)AmdW(F990WlB|B}GL*%S#fP=5}#4 zamzMY^Knbc+T1mDcg<{{U2R*@eN|7bY2`~zO>50dX1R+O^ij4=5J5x{K>|e-VPJnQ zFwEt*f5^zb8b={Y@Qw`&-nNTz+FS(%w zD&8rLHU)q&vgwc3gCL%ltrq{?`I#=Qa%4)cfu=g}x5_aP`(k{h+5m%34n2P!><2-c zgR_;ZL4=9>_<08mYFWrWXx3Ou9E%|@qpGm+2pm^d_$=(8)-p*jfiFp*0+5W+Q~IxK*5#%J&& zj>7p-We(Fv@b!h96?GHgR9=6EnOj4#|h%mt%%& z_kgdDLL)4l5nwMnr~&=&uTHl1g1wt}j=y*sb}jl?R$X^}9(Zg`pY-}q9g(3hK|gt1 zgbae4LWcPS0jW$Ntet0+TEpz?;i#@$qn`&$Grl@}ZXD?MH*c`c^;EP%qz;K28=M4l zPNiCV?}LGP5viIXtz-7&NNU0@jt zIO)nO%f8afd>p>oTvACbj#iQ?d)6nnq{F8h3ZA(%(LK|x0MaVyPn?B{ zRi%AQ0dRg`S)FOE_ugWz`G@jDXHtwgAZ?UZQ{l?HVARiP$%KDz{#iM#F#s$FZL_8O zJs)qa!SI#kbxF$6ojuTy?w;qq11c!R2&QGm7~5E|Icy6ZZ+SiuC!GFG^>hvx9#f(2Y96|x@W#;AxYgG;AM0R;AON4aD$~M zmUuLvenoR$aR3xD#d4X)_EKEo^snBh8KfuyqKHN1%JW;Stdh-wpeOuKDJO&2AR5Jj zPP@Bp_>NK}$owEATm7`pO}+@a?Evw-_%;0?_WAg7wE=%(iu;DDXL&vmC!GHK+<$kz zCJjV}$RyU4cb7){7r)hT&%M!GgACw1MzKa5@_fAE0Ra#BE`qcx{l*4==AP$%30lcI zhQi?dlz#*(Az^#+%%E|ePs9nQ|HLc9RN5DSSIA%dU&pc^(k6HKfpgFidG0vN3!=O#6R4#T1bNeRFlm>c`!WZ#xgT_3Nip zE$wFw=*{(ot>d9E@K}TXZ74r@W{5Edj3=6steZg0lWi1BJD-U3XGey#7&Joy3zXBL z&zQSUXwo3DX;`rKCD5vRB>H57h!hVny8{N*yv*OhTx8wjcoB|8d>hgD0_5jcg*07+ zi^qQ&Yb+muDCc*iLFdlHKr)lb9`HFQ|1Yr#`t{cRT>TP^ER5Z!ZHBlvbxTxskoOmb zQ~{x=@wp8FXvT~kIK~mK0?U}sNsu;5?@<9%a26AwK-sxe+F-+q1jN!0HHZxH9I?PF ztY8o@jQa?|?FpC*TCw8*N~y&R%ws#JL0W&rE=qyFahf_heH3{ejhcrqGXE*r%-fxR+5%id-Tk30qo>@`n{2vFuI{zthIWT{a zp#*op;4VJp4A}B*3mrA^?F;8y8}EVY*Lb3Qy}!X?pLZB;G(ebGT?{*jjHW_39_}_(nPpZ^6eGIu-hpVfPLhB4mk$s2fe7;SIx4 z*q`?0{)%<(d2Npc=jGG(PLPgqm^#oue05{%CNPaOOtnqIYcP#8OtsZO{o%SY%c~$c zq;IIl!D~%6X_QQXLEmhX0Y2J(1xX$o?YXKNX7-)?Ipzub#?$=N+qgwzk<2wR) z|360le?f@u4)8ME2+-XLcNXpS?d||C;}!yRcS6^~wVoa9fNmjf5AN&dUH}zp6d+HO z?-TcWJArm6#wm(LDhPj6H`-m4AC^1BEKnpV-jMYKp5SK;^n8DuvgxvwA`yIE@-@qc z--PGx1My=qohaz}TGVmXW1t%uVe;$Yy-v91A0i^6mZ??%4~WS`+<@1%Eu2u1_!FHT zl$Vw1axYv<=@A#Y!9Q4tlt0|_Uc^mqWw`Hvg=8@p;?oCJ1tvq^l?erb%bXv2UI?fE z__V>+z$_q>-*A7oD=+pJalN$n!kBL!42-z|zDxWfO2=vf@;>#v1@qf&~szk~rLNxs@QAoR(eSN*buP*(XIl$CW|=uUgr?6>=K z`j7ZU)Lzvizy#9i{-+A^L-?(T jJCeu`RS?g;Wc&*h)2GpZRLw5{0000*k9B)gq7o0NVna|2Af`S)(=z1sdM0g2R?`r{&xk`PzzFr8MY`CXrG%`$;*mEN8CB z=Y&dw$!yS@Mh8Wht5X+}Xql*3Y9?~zTmlW9%k1kC@I{j=DRWI-7SSszENNX@|e8fH&OO=fdx7w6_{ZthQG!Zl4JZKeIgT1o`vNdw!p=J<$MB2w34sPJza zgMAtzzH1^@(>5fUwPiIXpLso-aToXAfHIg`>lYqW&T2Krk-??Npr#LF0{Mo%Mw{{Wi zKo5E4&Oo>DAMI>Vl+sif3N2F8vby3|RWRdtYR>c{Rh^HL;Ifhx&m zS&i&(yzQ?_s&lz-Vhpm*Gd+-b6b|n-zhA4FcC~n^xHNFp)kUT} z(A0ld1nvzne1rzu=AAZKj}{c-L&yEfZ>XaQi|g=kwwGOMNybQfVaf0;4)2(TO-Myx zq!4KN?vBvo;Zlf4xx)^J$iPC|7>j)=yf4AQ`}j-ifgdn`^v3|I25yyxBv|c$Kz0P! zyEvnq+P|jUsDMIuG~0i3UTf`S;f=&q*93STiq6EqVcENfJf-%QH8p93drdJT)hs~u z-tlB|ot;HM!2RDm!%U>nxYssZbZA&U#4!d8CY|MiC6i%c=UaU119m!%MJ{sML)N{c*G#K+d)Nvcc zX%#7RPwc$+25{4_%(B~q=!>n;-}Mo=A&1*i)g7lZF^c&A8Kzrdhb_a2FD{mxcgK!x z@7{|&JNGBOR(C3U8GZplT|F3jCNDClr?R@W4ph`jFD%}Nbu|Z_EJ_5U)9j6?_n^G# zi_T3aTmH+9J9D&+?=$n}(q`Gm|5#A0Y+a8Wjl%8o^6~2MKap~+w*~(Pxn~)Q-Q#%< z3(sw7?|WN4iADTsnMFExuda>#*Uf_M0G~E4c)8mdquBLWn;z`(dTKcNa`zsG7llX} zaU1?A7wjq~^XCeLpA+ArqY2OXs9^;1^3(XcPCpKy+v6E%j#C*d)!t|Y5=FrRduQe! zIt3LR^ydG6X8vz0h@>!1W|1hh%XPxzLf>YXY@=B_A8($uEd=HjZ&QY#9>%b+c(U@K zEwD&Mk~MCww#i%qyZmbwlRp&?!Ap<)YEeBYZ>!RGC=MtUE%>H&sW}K(N~0#_VRV4%np*b2Y~vP^W!i&h;~w zePgZ-QX?$1uY!#?v3s?z8u$x`8^3>Lg3tBm?9Dce`<3M_E5E4Gx@}z(5CMyZwjWJJ F{{sLfx7h#y literal 8768 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 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.png index fe5109fa1dfa3ab8ad828ffa5decc7838960fa25..cadeb09865353ea18e3eed07c8672d50be953387 100644 GIT binary patch literal 3204 zcmb`KXIK-<8pk&YNr(ZFejpST0W}mWMNkB(Ql&`?1gvO)C?HYV2BQ=YAW|g=iUIWaMz~-;T$%=%+d(U#>xt*%6BXE?FRt- zXEQ^6d)mnSX!xHQ4h?OKtG=lcCm$t^fPDDf8-PxFRCRjSm|NGzR6M^(LU-#^XZ_ez z+Gzh=e5|o>*{L&b;`f4MSy9d1KO>d)ZaaKu*Md#mhQNKVU2i{lnR`DvUQ+(%S~#!m zx`qZrgVW%nRu)lu%~3{?5Lp&mfNX72uZUYSete{xXW zxYHpY6kO4CM4U!`L%U-s$Q!nU4nt@PsuHNOwiuB2nN1utCgh<8fp35s!reSQVeZxp z-6W=Q?I`!xjA^I`JG;X+8Wey09t}y-gG7p7KVa|UN=_(EG!8v>-Y*z(nCm^^GSSs9 zOBFEzJ&@zb%0njXvV8m{hZO{Y;Zyca!Np_!73CL?QWZQ;<%(7GwYp3sLOJ<_!R%#d8Ga?&NtU_|V+3eI zp85)T-jmU80v~N!?Ms~ct0j0;wsG>b9-RQszRHC?&KP*9O5#{z9Dw+Rm7O$pu*B?_baX+JfAt{ny;oB-aZ8~1ZPQjXxsFofQs6Q6?$|GWwfuAXYJuB{SDKB_u z-JJSQ>eNeABWUVW3`;4H)TVv%53_UzIyM;gueWL{`tsvgxrsn%mQDmKAa=j(_lU+d zW$rdEnta*+$3ojnxdAsPj>&*Ap1Gv zrOi*l@r}6zxq_W{!kj&RSk}p6GIw%5_$W|z=>fXQQ(=SA_G;ud7$N}(B0Uu~cX2bg z=M}?p{3cA1I2X!On4F+p{2cRIaOD@~g+j62e}l9A*XM--LR|skKDxKK4#{dG!JBdD zrXs{lt5UEIn>D9(bvR{>Q(Sh}W2BW2jK+92=JoZF(NkS^vg26enCazwjed7yPr zbfJ6kN>j@WPgo_nrO_FWLfF$Z1=!aUh(2OiyugR9|I7WVjI#|fB@IDV5yt@)pFBVMq z05sz)SBz^^1K<&ea}g6|(^smBjOuVzQ+3yCwSUmqUM>mdkuRRYp8Jvkp5EJ!)j0xH zC??!tmfF(8meJZsNxFN+He>;zf!(vJ}Uu`e~IU2RD_D0Bx?+ zcL7PpYVS8xvBkeQ%!RtXLV0w{{L9{b(oL57>Z)@fyc~gyRk!C>qty~V!0q)*Q#s3) zb>|5CnvyruyFReK*iQJv!%$T}{$rrE4tIKEHP6@HYz*l(c$)BSy5+uT$M}?`6^7~SePb-IJ{`l$nnPKbJ0J5RPijs_LIfx%{RUee{+cmgnh_UTYcGrb5a-| zTsCyvmiuP7VP?2tTTAQrmi)P>>2r)#o6dm~Jc{Qv z@|6<;ql~OQ1=-?%uqyL8g9Llk8jM9p#Qz<+|3-3c3`IigF1!_NMfFy4d1&%!wz1&> ztc)3{+4h_ytE7Ew7M%a>z8TpXAFqe`Xrd)mI73}bP39mSX*&ZE3AzV0hJ&d!LYcl> zyAk2JHxNwhf?}6@>AlO)3ZP>vd8+g=R4Ed-w0n~A?4C23DCMqoRdC4a;r-gEd$K@f zQb2}@t!D1oHgFz}Xik`2c!Ix zS@8mPLB-LetQ;P2(NOLD`?V^c+KorqvZ+9!-C#MNKcr4)!u;5DS$Vm+$m^X^*P89X z(kmb$csQR>&xgg?eeAV{bvL%eIY~M$bsKvgK7FC&(Hl2=TpVFmCw7X#t~i)6xcG}k?nEB6H_*%*#>%`#u1gU=;LzKo(Ux5hO%MpzN6#8CERHa zF=jJJ`QkSuS75TCSpq&R)j2)%E^Hj3NqTMx-N6#B7NF@+<-8*_teu*xQ(4ilcEmOV z;?_yZwl$#YUgL7#rgndq#leLYIfW2fkVG1R$(fH5&Et2~^b~XDFt6zt@FPFXZfnYP literal 7603 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 ZPwGmuuU-_#e_hX{7)F diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png index 98644631947901f61dd677a3394dac952680971c..03aa9da915c7f228cd5986f7c924bb338050461c 100644 GIT binary patch literal 2172 zcmc&$`#;l*8~-c|F-`fF^!=()NvGq|sg6pn5sMbWx($(BIvn?FW+8@H`4F4FE2^0sz&3f)@MB0I-W{XX|LW zW03y8OCphQw?(W4?i7_n8{L=~+nAtiEHbvYHiZI_czKggWo{15ZIBonnCzU(ieHY8YXSlh+fQCsSLe9@_U1I+uAuu}9@_$a{)NEa_=q;tE7AWkbQWW4+&& z+{#IAHs42w*2EGCrh)dfX(G_IkG04xCopOU7hT}^sv^2rETOZvkwpWHHSWEuUCB}8 zm1TbO=#pt>@0Bb<+c-PyLH~W*?}09Tt8D&{nKj=k@^8ubGg!P|^{7ZB+2jeo(b%06 zD_eY#fG_6r#R7qdyDj8#g==g+{tNS6(R6P7XeOGR6-&-}GFp{4)!wkcn&#%?XkL0ZlqHLzf_U? zjIg@GU7Xv-hLF(S{Vy|q2!-OB!s+~EDvvAb{x*Bfuwt-pVPjPg^N`4x*~-r*mKO|# z+#`$*tj0yO)5kec9(}=g2{FMqI*l{`b1M`@*jN*!gpy*shy^*s%0g0Y3B4ed3OB5< zHL4^HET)E$Fuwg$)D4kHJWbtr5kK}UVtAx)#pZJ5+A5c`E+BR-e<+(Nc}^?Ipw$#j z_uyD;7OyOSu%d8C7+0#gbKIvx91u4FId$U)≺Z_{lxdcf1z}R| z+7}#<>-hlbNU<|bOr7~VrAZDE5I3u*-|IPH#BH7n;$B9hTL-F(!&id_xN6fXV2`>= zY*^CS^F+&gK2?Y$l*EZD$TWs@l2Rj;mPQXo zUn4sg$66o?UB`*qCTqH9Vx>N%>vp~R#mekgy2vp(S3gvnK(dbvJw&139Df5je0gr< z??i}t$l=`qKCG-396zbhxHZtiHtm>IT5THDtKj0p<=YBrhQfm2Q*rSGJ*S0Y16;B* zCm|6VBYAZ&O$$*b&tjps?{LRw`$f#v1EjwdCP;mKW@wZXr70KVwe@Dt#31CVAUDh)JNZ;$!ujW%YC zWMD9(w>7xqOZxOEfzyd0GUTg-IUsMR+&`dI032_f|1l%mlMQN~0H*BFS~Ie+(uVMZ zR;IWUfL!Z@K7UG>clSVEuryAt%%2tyO+yJjnA>z4{pM4&*VN1Cm+Sdw zf}L$YE%dv-u0N_SqU?Wq!KNDXu~(pt(S25gPO8cl?S>0I;XQvyj%7$d_k32)M)K$);>0~$7|R? zTS_~2&XjtfbiScw)l|YYNkB>Wf){z21Z0uwo)ezFoHxbivn4|wLtd@H8S_WbOkOSl zSsY>S6_y~{79Nc~f?Vu!jXix&ii>T5o*EIa?lTc%7SmH-SO3g@fj{o!>`Zcb+Wz2n z=|zKgn!h?#vKQ8j`JT~XF?E=^hc8@ZID9S?oIe(IA$b@yGu^pIxn}x`f9qpO%sEM+ zY3gi;sou>GgK42^OW$Q#S)Go!PH&ToC8WBF*=#0OQ4}ozZRsL3aXljjnu%@I+WK24 zOFL?0vQ?OWI#ch8Ibm7lz42{vqos4ptzp#!uaQIiOA97!3$6bsQ1Lr&1OUJ-5f-)P HZgKwrYGB|t literal 9867 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 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png index 54ac93a76f1c834ef16c7f23a0ca3b03fc334d6c..b266ed8a848e5cc9941ea06abf9cd1df91a5305e 100644 GIT binary patch literal 3510 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 b9Y(L*0XaFZX?IY+Qoy~tX8IL+PS5`js#C~Z literal 18942 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 C2jJiU diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.png index 9312aa740c64f9a78c0e31b3411ac44be342e278..8e5a46eddc9838680535d8102642117022ed1d0f 100644 GIT binary patch literal 5131 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 S<7l%e>24cY8P@1~#{LI>g$B(4 diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png index a6172b33afc62a114e5be1b2dbb77dc739a6c2c1..4529a854e9462ed4dcdd5999cff096f51838af30 100644 GIT binary patch literal 4636 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 DTZg?@ diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 51d16882a0ee7822cbdd31cfbe7c977d4b3e3155..b661eb1862e69084b8801443806d9e6378be28ee 100644 GIT binary patch literal 7753 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 Jk%jlo{{ps>CJ+Ds literal 17573 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 Kx8kAQ>;D7lMe}9= diff --git a/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/ohs-player-reference-app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png index dba530eb0bc4bafa5c31f4fa042c2e21d76f5cfd..592aa84023588d20cc3f637204b746181188df0c 100644 GIT binary patch delta 5887 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 Date: Wed, 29 Jul 2026 20:28:57 +0300 Subject: [PATCH 71/73] Render the patient header without a card The patient header sat in an elevated Card while the household header renders straight into the list. Drop the card so the two profiles share one flat header on the surface. --- .../patient/profile/PatientProfileScreen.kt | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt index fea27d1b..7b852c14 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/patient/profile/PatientProfileScreen.kt @@ -19,14 +19,11 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton @@ -170,16 +167,7 @@ fun PatientProfileScreen(patientId: String, onBack: () -> Unit, onAddClinicalDat contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - item(key = "patient_header") { - Card( - modifier = Modifier.fillMaxWidth(), - elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), - ) { - Box(modifier = Modifier.padding(20.dp)) { - headerRenderer.Render(s.patient, RenderOptions()) - } - } - } + item(key = "patient_header") { headerRenderer.Render(s.patient, RenderOptions()) } if (s.allergies.isNotEmpty()) { item(key = "allergies") { From 08808c4576ae1dc75c9a5c3591869f34393de0b8 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 29 Jul 2026 20:43:34 +0300 Subject: [PATCH 72/73] Order households by last updated, newest first The register listed groups in storage order flipped by a blanket reverse. Sort the Group search results by meta.lastUpdated descending in the data source instead, so the most recently touched household leads and the view model no longer second-guesses the order. --- .../reference/app/data/datasource/SampleDataStore.kt | 9 +++++++-- .../app/feature/group/list/GroupListViewModel.kt | 4 +--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/datasource/SampleDataStore.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/datasource/SampleDataStore.kt index f7e94564..f3dd96fb 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/datasource/SampleDataStore.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/data/datasource/SampleDataStore.kt @@ -86,10 +86,15 @@ suspend fun patientProfileSearchResult( /** * Group list: root = Group only. Member count is derived from `Group.member.size` on the resource - * itself — no additional includes needed. + * itself — no additional includes needed. Ordered newest first: `meta.lastUpdated` is a UTC + * instant, so its ISO-8601 string sorts chronologically; groups without one fall to the end. */ suspend fun groupListSearchResults(repository: FhirRepository): List> = - repository.all("Group").filterIsInstance().map { group -> SearchResult(resource = group) } + repository + .all("Group") + .filterIsInstance() + .sortedByDescending { it.meta?.lastUpdated?.value?.toString() } + .map { group -> SearchResult(resource = group) } /** * Group profile: root = Group, member Patients in included. Each `Group.member.entity` is a FHIR diff --git a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListViewModel.kt b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListViewModel.kt index ad7d9568..99c5d7d3 100644 --- a/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListViewModel.kt +++ b/ohs-player-reference-app/src/commonMain/kotlin/dev/ohs/player/reference/app/feature/group/list/GroupListViewModel.kt @@ -30,8 +30,6 @@ class GroupListViewModel(private val groupRepository: GroupRepository) : ViewMod val groups: StateFlow?> = _groups.asStateFlow() init { - viewModelScope.launch { - groupRepository.observeGroups().collect { _groups.value = it.reversed() } - } + viewModelScope.launch { groupRepository.observeGroups().collect { _groups.value = it } } } } From b45432682ff619409927cfe14ca2c59e98acebb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:15:11 +0300 Subject: [PATCH 73/73] Suppress RestrictedApi for Result.Success check in sync worker WorkManager marks Result.Success as library-group-restricted with no public accessor to check success, so this type check has no sanctioned alternative. --- .../dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt index 4ca4be8c..a6b35e5c 100644 --- a/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt +++ b/ohs-player-reference-app/src/androidMain/kotlin/dev/ohs/player/reference/app/data/sync/AppFhirSyncWorker.kt @@ -39,6 +39,7 @@ class AppFhirSyncWorker(appContext: Context, workerParams: WorkerParameters) : * bootstrap never ran and [dev.ohs.player.reference.app.auth.SessionRepository] is empty. Hydrate * and refresh the session first so the sync's requests carry a valid Bearer token. */ + @Suppress("RestrictedApi") override suspend fun doWork(): Result { ensureFreshSessionForSync() return super.doWork().also { if (it is Result.Success) DataChangeSignal.notifyChanged() }