diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/PreferenceDataStore.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/PreferenceDataStore.kt index 8fc21ee943..c9e3d5f12f 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/PreferenceDataStore.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/PreferenceDataStore.kt @@ -54,6 +54,7 @@ class PreferenceDataStore @Inject constructor(@ApplicationContext val context: C companion object Keys { val APP_ID by lazy { stringPreferencesKey("appId") } val LANG by lazy { stringPreferencesKey("lang") } + val SYNC_LOCATION_IDS by lazy { stringPreferencesKey("syncLocationIds") } val MIGRATION_VERSION by lazy { intPreferencesKey("migrationVersion") } } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/PractitionerDetailsDataStoreSerializer.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/PractitionerDetailsDataStoreSerializer.kt index 80606aa94b..ebd6510a04 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/PractitionerDetailsDataStoreSerializer.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/PractitionerDetailsDataStoreSerializer.kt @@ -34,8 +34,8 @@ object PractitionerDetailsDataStoreSerializer : Serializer deserializer = PractitionerDetails.serializer(), string = input.readBytes().decodeToString(), ) - } catch (e: SerializationException) { - Timber.tag(SerializerConstants.PROTOSTORE_SERIALIZER_TAG).d(e) + } catch (serializationException: SerializationException) { + Timber.e(serializationException) defaultValue } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/SerializerConstants.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/SerializerConstants.kt deleted file mode 100644 index f2bb36eadb..0000000000 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/SerializerConstants.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2021-2024 Ona Systems, Inc - * - * 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 org.smartregister.fhircore.engine.datastore.serializers - -object SerializerConstants { - const val PROTOSTORE_SERIALIZER_TAG = "Proto DataStore" -} diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/UserInfoDataStoreSerializer.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/UserInfoDataStoreSerializer.kt index fecd42c5a6..aa234d699c 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/UserInfoDataStoreSerializer.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/serializers/UserInfoDataStoreSerializer.kt @@ -34,8 +34,8 @@ object UserInfoDataStoreSerializer : Serializer { deserializer = UserInfo.serializer(), string = input.readBytes().decodeToString(), ) - } catch (e: SerializationException) { - Timber.tag(SerializerConstants.PROTOSTORE_SERIALIZER_TAG).d(e) + } catch (serializationException: SerializationException) { + Timber.e(serializationException) defaultValue } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt index e00bfe6c32..5817f16fd3 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt @@ -52,8 +52,8 @@ import org.smartregister.fhircore.engine.ui.theme.AppTheme @Composable fun ColumnScope.MultiSelectView( rootNodeId: String, - treeNodeMap: SnapshotStateMap>, - selectedNodes: SnapshotStateMap, + treeNodeMap: Map>, + selectedNodes: MutableMap, depth: Int = 0, content: @Composable (TreeNode) -> Unit, ) { @@ -91,7 +91,7 @@ fun ColumnScope.MultiSelectView( @Composable fun MultiSelectCheckbox( - selectedNodes: SnapshotStateMap, + selectedNodes: MutableMap, treeNodeMap: Map>, currentTreeNode: TreeNode, depth: Int, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/TreeMap.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/TreeMap.kt index 9689eb1e45..147b183711 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/TreeMap.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/TreeMap.kt @@ -31,7 +31,7 @@ object TreeMap { fun populateLookupMap( items: List>, lookup: MutableMap>, - ): MutableMap> { + ): Map> { items.forEach { item -> val childNode = findOrCreate(item.id, item, lookup) val parentNode = findOrCreate(item.parentId, item, lookup) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetFragment.kt index d4c0550a9a..f8f6a92f6c 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetFragment.kt @@ -56,7 +56,8 @@ class MultiSelectBottomSheetFragment() : BottomSheetDialogFragment() { title = bottomSheetArgs.screenTitle, onDismiss = { dismiss() }, searchTextState = multiSelectViewModel.searchTextState, - onTextChanged = multiSelectViewModel::onTextChanged, + onSearchTextChanged = multiSelectViewModel::onTextChanged, + onSelectionDone = multiSelectViewModel::onSelectionDone, ) } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetView.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetView.kt index 3fdc11e57e..e0b657d486 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetView.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectBottomSheetView.kt @@ -47,7 +47,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -59,12 +58,13 @@ import org.smartregister.fhircore.engine.ui.theme.DividerColor @Composable fun MultiSelectBottomSheetView( rootNodeIds: SnapshotStateList, - treeNodeMap: SnapshotStateMap>, + treeNodeMap: Map>, selectedNodes: SnapshotStateMap, title: String?, onDismiss: () -> Unit, - searchTextState: MutableState, - onTextChanged: (String) -> Unit, + searchTextState: MutableState, + onSearchTextChanged: (String) -> Unit, + onSelectionDone: (() -> Unit) -> Unit, ) { Column(modifier = Modifier.fillMaxWidth()) { Row( @@ -91,20 +91,12 @@ fun MultiSelectBottomSheetView( ) { OutlinedTextField( value = searchTextState.value, - onValueChange = { value -> - searchTextState.value = value - onTextChanged(value.text) - }, + onValueChange = { value -> onSearchTextChanged(value) }, modifier = Modifier.fillMaxWidth(), textStyle = TextStyle(fontSize = 18.sp), trailingIcon = { - if (searchTextState.value.text.isNotEmpty()) { - IconButton( - onClick = { - searchTextState.value = TextFieldValue("") - onTextChanged(searchTextState.value.text) - }, - ) { + if (searchTextState.value.isNotEmpty()) { + IconButton(onClick = { onSearchTextChanged("") }) { Icon( Icons.Default.Close, contentDescription = "", @@ -137,7 +129,7 @@ fun MultiSelectBottomSheetView( item { Button( - onClick = { /*TODO Get selected nodes*/}, + onClick = { onSelectionDone(onDismiss) }, modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp, horizontal = 8.dp), ) { Text( diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt index 98fc6b3305..4aa8dbf4c2 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt @@ -21,14 +21,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.state.ToggleableState -import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.android.fhir.logicalId import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import org.smartregister.fhircore.engine.data.local.DefaultRepository +import org.smartregister.fhircore.engine.datastore.PreferenceDataStore import org.smartregister.fhircore.engine.domain.model.MultiSelectViewConfig import org.smartregister.fhircore.engine.ui.multiselect.TreeMap import org.smartregister.fhircore.engine.ui.multiselect.TreeNode @@ -41,15 +42,29 @@ class MultiSelectViewModel constructor( val defaultRepository: DefaultRepository, val fhirPathDataExtractor: FhirPathDataExtractor, + val preferenceDataStore: PreferenceDataStore, ) : ViewModel() { - val searchTextState: MutableState = mutableStateOf(TextFieldValue()) + val searchTextState: MutableState = mutableStateOf("") val rootNodeIds: SnapshotStateList = SnapshotStateList() - val lookupMap: SnapshotStateMap> = SnapshotStateMap() val selectedNodes: SnapshotStateMap = SnapshotStateMap() + val lookupMap = SnapshotStateMap>() fun populateLookupMap(multiSelectViewConfig: MultiSelectViewConfig) { + // Mark previously selected nodes viewModelScope.launch { + val previouslySelectedNodes = + preferenceDataStore.read(PreferenceDataStore.SYNC_LOCATION_IDS).firstOrNull() + if (!previouslySelectedNodes.isNullOrEmpty()) { + previouslySelectedNodes + .split(",") + .asSequence() + .map { it.split(":") } + .filter { it.size == 2 } + .map { Pair(it.first(), it.last()) } + .forEach { selectedNodes[it.first] = ToggleableState.valueOf(it.second) } + } + val repositoryResourceDataList = defaultRepository.searchResourcesRecursively( fhirResourceConfig = multiSelectViewConfig.resourceConfig, @@ -85,6 +100,17 @@ constructor( } fun onTextChanged(searchTerm: String) { - searchTextState.value = TextFieldValue(searchTerm) + searchTextState.value = searchTerm + } + + fun onSelectionDone(dismiss: () -> Unit) { + viewModelScope.launch { + // Consider using a proto-datastore here + preferenceDataStore.write( + PreferenceDataStore.SYNC_LOCATION_IDS, + selectedNodes.map { "${it.key}:${it.value}" }.joinToString(","), + ) + dismiss() + } } }