diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/LineSpinFadeLoaderProgressIndicator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/LineSpinFadeLoaderProgressIndicator.kt similarity index 98% rename from android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/LineSpinFadeLoaderProgressIndicator.kt rename to android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/LineSpinFadeLoaderProgressIndicator.kt index 420ca1a1eb..78d287df1d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/LineSpinFadeLoaderProgressIndicator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/LineSpinFadeLoaderProgressIndicator.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.smartregister.fhircore.quest.ui.shared.components +package org.smartregister.fhircore.engine.ui.components import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/LoaderDialog.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/LoaderDialog.kt index da98fa0606..604aea90f3 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/LoaderDialog.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/LoaderDialog.kt @@ -19,10 +19,9 @@ package org.smartregister.fhircore.engine.ui.components.register 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.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Surface @@ -35,8 +34,8 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog @@ -44,6 +43,7 @@ import androidx.compose.ui.window.DialogProperties import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import org.smartregister.fhircore.engine.R +import org.smartregister.fhircore.engine.ui.components.LineSpinFadeLoaderProgressIndicator import org.smartregister.fhircore.engine.util.annotation.PreviewWithBackgroundExcludeGenerated const val LOADER_DIALOG_PROGRESS_BAR_TAG = "loaderDialogProgressBarTag" @@ -52,58 +52,113 @@ const val LOADER_DIALOG_PROGRESS_MSG_TAG = "loaderDialogProgressMsgTag" @Composable fun LoaderDialog( modifier: Modifier = Modifier, - dialogMessage: String, + dialogMessage: String? = null, percentageProgressFlow: Flow = flowOf(0), showPercentageProgress: Boolean = false, + boxWidth: Dp = 240.dp, + boxHeight: Dp = 180.dp, + progressBarSize: Dp = 40.dp, + showBackground: Boolean = true, + showLineSpinIndicator: Boolean = false, + showOverlay: Boolean = true, + alignment: Alignment = Alignment.Center, ) { val currentPercentage = percentageProgressFlow.collectAsState(0).value + + if (showOverlay) { + Dialog(onDismissRequest = {}, properties = DialogProperties(dismissOnBackPress = false)) { + LoaderContent( + modifier = modifier, + dialogMessage = dialogMessage, + currentPercentage = currentPercentage, + showPercentageProgress = showPercentageProgress, + boxWidth = boxWidth, + boxHeight = boxHeight, + progressBarSize = progressBarSize, + showBackground = showBackground, + showLineSpinIndicator = showLineSpinIndicator, + ) + } + } else { + Box( + modifier = modifier.wrapContentSize(), + contentAlignment = alignment, + ) { + LoaderContent( + modifier = modifier, + dialogMessage = dialogMessage, + currentPercentage = currentPercentage, + showPercentageProgress = showPercentageProgress, + boxWidth = boxWidth, + boxHeight = boxHeight, + progressBarSize = progressBarSize, + showBackground = showBackground, + showLineSpinIndicator = showLineSpinIndicator, + ) + } + } +} + +@Composable +private fun LoaderContent( + modifier: Modifier, + dialogMessage: String?, + currentPercentage: Int, + showPercentageProgress: Boolean, + boxWidth: Dp, + boxHeight: Dp, + progressBarSize: Dp, + showBackground: Boolean, + showLineSpinIndicator: Boolean, +) { val openDialog = remember { mutableStateOf(true) } if (openDialog.value) { - Dialog( - onDismissRequest = { openDialog.value = true }, - properties = DialogProperties(dismissOnBackPress = true), - ) { - Box(modifier.size(240.dp, 180.dp)) { - Column( - modifier = modifier.padding(8.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, + Box(modifier.size(boxWidth, boxHeight)) { + Column( + modifier = modifier.padding(8.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Surface( + modifier = modifier.size(boxWidth, boxHeight), + shape = RoundedCornerShape(8.dp), + color = if (showBackground) Color.Black.copy(alpha = 0.56f) else Color.Transparent, ) { - Surface( - color = Color.Black.copy(alpha = 0.56f), - modifier = modifier.fillMaxSize(), - shape = RoundedCornerShape(8), + Column( + modifier = modifier.padding(8.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, ) { - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { + if (showLineSpinIndicator) { + LineSpinFadeLoaderProgressIndicator( + color = Color.White, + lineLength = 8f, + innerRadius = 12f, + ) + } else { CircularProgressIndicator( color = Color.White, strokeWidth = 3.dp, - modifier = modifier.testTag(LOADER_DIALOG_PROGRESS_BAR_TAG).size(40.dp), + modifier = modifier.size(progressBarSize), ) - Row( - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - fontSize = 16.sp, - color = Color.White, - text = dialogMessage, - modifier = - modifier.testTag(LOADER_DIALOG_PROGRESS_MSG_TAG).padding(vertical = 16.dp), - ) + } - if (showPercentageProgress) { - Text( - fontSize = 15.sp, - color = Color.White, - text = stringResource(id = R.string.percentage_progress, currentPercentage), - modifier = modifier.padding(horizontal = 3.dp, vertical = 16.dp), - ) - } - } + dialogMessage?.let { + Text( + text = it, + color = Color.White, + fontSize = 14.sp, + modifier = modifier.padding(top = 8.dp), + ) + } + + if (showPercentageProgress) { + Text( + fontSize = 15.sp, + color = Color.White, + text = "$currentPercentage%", + modifier = modifier.padding(top = 4.dp), + ) } } } @@ -122,3 +177,16 @@ fun LoaderDialog( fun LoaderDialogPreview() { LoaderDialog(dialogMessage = stringResource(id = R.string.syncing)) } + +@PreviewWithBackgroundExcludeGenerated +@Composable +fun LoaderDialogPreviewTest() { + LoaderDialog( + boxWidth = 50.dp, + boxHeight = 50.dp, + progressBarSize = 25.dp, + showBackground = false, + showLineSpinIndicator = true, + showOverlay = false, + ) +} diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherFragment.kt index 2b4d302b1e..c5a5928b73 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherFragment.kt @@ -194,6 +194,7 @@ class GeoWidgetLauncherFragment : Fragment(), OnSyncListener { launchQuestionnaire = geoWidgetLauncherViewModel::launchQuestionnaire, decodeImage = geoWidgetLauncherViewModel::getImageBitmap, onAppMainEvent = appMainViewModel::onEvent, + isSyncing = geoWidgetLauncherViewModel.isSyncing, ) } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherScreen.kt index 86dc61b6bc..14a0a0825f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherScreen.kt @@ -18,24 +18,33 @@ package org.smartregister.fhircore.quest.ui.geowidget import android.content.Context import android.graphics.Bitmap +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import androidx.fragment.app.FragmentManager import androidx.fragment.compose.AndroidFragment import androidx.fragment.compose.rememberFragmentState import androidx.lifecycle.MutableLiveData import androidx.navigation.NavController +import kotlinx.coroutines.flow.StateFlow import org.hl7.fhir.r4.model.ResourceType import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig import org.smartregister.fhircore.engine.configuration.geowidget.GeoWidgetConfiguration import org.smartregister.fhircore.engine.domain.model.ResourceData import org.smartregister.fhircore.engine.domain.model.ToolBarHomeNavigation +import org.smartregister.fhircore.engine.ui.components.register.LoaderDialog import org.smartregister.fhircore.engine.util.extension.showToast import org.smartregister.fhircore.geowidget.model.GeoJsonFeature import org.smartregister.fhircore.geowidget.screens.GeoWidgetFragment @@ -65,8 +74,11 @@ fun GeoWidgetLauncherScreen( launchQuestionnaire: (QuestionnaireConfig, GeoJsonFeature, Context) -> Unit, decodeImage: ((String) -> Bitmap?)?, onAppMainEvent: (AppMainEvent) -> Unit, + isSyncing: StateFlow, ) { val context = LocalContext.current + val syncing by isSyncing.collectAsState() + Scaffold( topBar = { Column { @@ -118,14 +130,20 @@ fun GeoWidgetLauncherScreen( }, ) { innerPadding -> val fragmentState = rememberFragmentState() - Box(modifier = modifier.padding(innerPadding)) { + Box( + modifier = modifier.padding(innerPadding).fillMaxSize(), + ) { AndroidFragment(fragmentState = fragmentState) { fragment -> fragment .setUseGpsOnAddingLocation(false) .setAddLocationButtonVisibility(geoWidgetConfiguration.showAddLocation) .setOnAddLocationListener { feature: GeoJsonFeature -> if (feature.geometry?.coordinates == null) return@setOnAddLocationListener - launchQuestionnaire(geoWidgetConfiguration.registrationQuestionnaire, feature, context) + launchQuestionnaire( + geoWidgetConfiguration.registrationQuestionnaire, + feature, + context, + ) } .setOnCancelAddingLocationListener { context.showToast(context.getString(R.string.on_cancel_adding_location)) @@ -153,6 +171,23 @@ fun GeoWidgetLauncherScreen( observerGeoJsonFeatures(geoJsonFeatures) } } + if (syncing) { + Box( + modifier = + Modifier.fillMaxSize().padding(16.dp).pointerInput(Unit) { detectTapGestures {} }, + contentAlignment = Alignment.Center, + ) { + LoaderDialog( + boxWidth = 50.dp, + boxHeight = 50.dp, + progressBarSize = 25.dp, + showBackground = true, + showLineSpinIndicator = true, + showOverlay = false, + modifier = Modifier.align(Alignment.Center), + ) + } + } } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt index 5c9eef34ff..bc6b7388da 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt @@ -29,6 +29,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -78,6 +80,9 @@ constructor( private val _snackBarStateFlow = MutableSharedFlow() val snackBarStateFlow = _snackBarStateFlow.asSharedFlow() + private val _isSyncing = MutableStateFlow(false) + val isSyncing: StateFlow = _isSyncing + private val _noLocationFoundDialog = MutableLiveData() val noLocationFoundDialog: LiveData get() = _noLocationFoundDialog @@ -101,150 +106,124 @@ constructor( searchText: String?, ) { viewModelScope.launch { - val totalCount = - withContext(dispatcherProvider.io()) { - defaultRepository.countResources( - filterByRelatedEntityLocation = - geoWidgetConfig.filterDataByRelatedEntityLocation == true, - baseResourceConfig = geoWidgetConfig.resourceConfig.baseResource, - filterActiveResources = - listOf( - ActiveResourceFilterConfig( - resourceType = ResourceType.Patient, - active = true, - ), - ActiveResourceFilterConfig( - resourceType = ResourceType.Group, - active = true, - ), - ), - configComputedRuleValues = emptyMap(), - ) - } - if (totalCount == 0L) { - showNoLocationDialog(geoWidgetConfig) - return@launch - } - var count = 0 - var pageNumber = 0 - var locationsWithoutCoordinatesCount = 0L - var registerDataCount = 0L - while (count < totalCount) { - val (locationsWithCoordinates, locationsWithoutCoordinates) = - defaultRepository - .searchResourcesRecursively( - filterActiveResources = null, - fhirResourceConfig = geoWidgetConfig.resourceConfig, - configRules = null, - secondaryResourceConfigs = null, - filterByRelatedEntityLocationMetaTag = + // isSyncing is set to true as soon as the retrieveLocations function is executed + _isSyncing.value = true + try { + val totalCount = + withContext(dispatcherProvider.io()) { + defaultRepository.countResources( + filterByRelatedEntityLocation = geoWidgetConfig.filterDataByRelatedEntityLocation == true, - currentPage = pageNumber, - pageSize = DefaultRepository.DEFAULT_BATCH_SIZE, - ) - .asSequence() - .filter { it.resource is Location } - .partition { - with((it.resource as Location).position) { hasLongitude() && hasLatitude() } - } - - val registerData = - locationsWithCoordinates - .asSequence() - .map { - Pair( - it.resource as Location, - resourceDataRulesExecutor.processResourceData( - repositoryResourceData = it, - ruleConfigs = geoWidgetConfig.servicePointConfig?.rules ?: emptyList(), - params = emptyMap(), + baseResourceConfig = geoWidgetConfig.resourceConfig.baseResource, + filterActiveResources = + listOf( + ActiveResourceFilterConfig( + resourceType = ResourceType.Patient, + active = true, + ), + ActiveResourceFilterConfig( + resourceType = ResourceType.Group, + active = true, + ), ), + configComputedRuleValues = emptyMap(), + ) + } + if (totalCount == 0L) { + showNoLocationDialog(geoWidgetConfig) + return@launch + } + var count = 0 + var pageNumber = 0 + var locationsWithoutCoordinatesCount = 0L + var registerDataCount = 0L + while (count < totalCount) { + val (locationsWithCoordinates, locationsWithoutCoordinates) = + defaultRepository + .searchResourcesRecursively( + filterActiveResources = null, + fhirResourceConfig = geoWidgetConfig.resourceConfig, + configRules = null, + secondaryResourceConfigs = null, + filterByRelatedEntityLocationMetaTag = + geoWidgetConfig.filterDataByRelatedEntityLocation == true, + currentPage = pageNumber, + pageSize = DefaultRepository.DEFAULT_BATCH_SIZE, ) - } - .map { (location, resourceData) -> - GeoJsonFeature( - id = location.logicalId, - geometry = - Geometry( - coordinates = // MapBox coordinates are represented as Long,Lat (NOT Lat,Long) - listOf( - location.position.longitude.toDouble(), - location.position.latitude.toDouble(), - ), + .asSequence() + .filter { it.resource is Location } + .partition { + with((it.resource as Location).position) { hasLongitude() && hasLatitude() } + } + + val registerData = + locationsWithCoordinates + .asSequence() + .map { + Pair( + it.resource as Location, + resourceDataRulesExecutor.processResourceData( + repositoryResourceData = it, + ruleConfigs = geoWidgetConfig.servicePointConfig?.rules ?: emptyList(), + params = emptyMap(), ), - properties = - geoWidgetConfig.servicePointConfig?.servicePointProperties?.mapValues { - JsonPrimitive(it.value.interpolate(resourceData.computedValuesMap)) - } ?: emptyMap(), - ) - } - .toList() - val features = - if (searchText.isNullOrBlank()) { - registerData - } else { - registerData.filter { geoJsonFeature: GeoJsonFeature -> - geoWidgetConfig.topScreenSection?.searchBar?.computedRules?.any { ruleName -> - // if ruleName not found in map return {-1}; check always return false hence no data - val value = geoJsonFeature.properties[ruleName]?.toString() ?: "{-1}" - value.contains(other = searchText, ignoreCase = true) - } == true + ) + } + .map { (location, resourceData) -> + GeoJsonFeature( + id = location.logicalId, + geometry = + Geometry( + coordinates = // MapBox coordinates are represented as Long,Lat (NOT Lat,Long) + listOf( + location.position.longitude.toDouble(), + location.position.latitude.toDouble(), + ), + ), + properties = + geoWidgetConfig.servicePointConfig?.servicePointProperties?.mapValues { + JsonPrimitive(it.value.interpolate(resourceData.computedValuesMap)) + } ?: emptyMap(), + ) + } + .toList() + val features = + if (searchText.isNullOrBlank()) { + registerData + } else { + registerData.filter { geoJsonFeature: GeoJsonFeature -> + geoWidgetConfig.topScreenSection?.searchBar?.computedRules?.any { ruleName -> + // if ruleName not found in map return {-1}; check always return false hence no + // data + val value = geoJsonFeature.properties[ruleName]?.toString() ?: "{-1}" + value.contains(other = searchText, ignoreCase = true) + } == true + } } - } - - geoJsonFeatures.postValue(features) - Timber.w( - locationsWithoutCoordinates.joinToString("\n") { - val position = (it.resource as Location).position - "Location id ${it.resource.logicalId} coordinates (${position.longitude},${position.latitude}) invalid." - }, - ) - pageNumber++ - count += DefaultRepository.DEFAULT_BATCH_SIZE - registerDataCount += features.size - locationsWithoutCoordinatesCount += locationsWithoutCoordinates.size - } - - val locationsCount = if (searchText.isNullOrBlank()) totalCount else registerDataCount + geoJsonFeatures.postValue(features) - // Account for locations without coordinates - if (locationsWithoutCoordinatesCount in 1..locationsCount) { - val message = - context.getString( - R.string.locations_without_coordinates, - locationsWithoutCoordinatesCount, - locationsCount, + Timber.w( + locationsWithoutCoordinates.joinToString("\n") { + val position = (it.resource as Location).position + "Location id ${it.resource.logicalId} coordinates (${position.longitude},${position.latitude}) invalid." + }, ) - Timber.w(message) - emitSnackBarState( - SnackBarMessageConfig( - message = message, - actionLabel = context.getString(org.smartregister.fhircore.engine.R.string.ok), - duration = SnackbarDuration.Long, - ), - ) - } else { - val message = - if (searchText.isNullOrBlank()) { - context.getString(R.string.all_locations_rendered) - } else context.getString(R.string.all_matching_locations_rendered, locationsCount) - emitSnackBarState( - SnackBarMessageConfig( - message = message, - actionLabel = context.getString(org.smartregister.fhircore.engine.R.string.ok), - duration = SnackbarDuration.Short, - ), - ) - } + pageNumber++ + count += DefaultRepository.DEFAULT_BATCH_SIZE + registerDataCount += features.size + locationsWithoutCoordinatesCount += locationsWithoutCoordinates.size + } + + val locationsCount = if (searchText.isNullOrBlank()) totalCount else registerDataCount - // Account for missing locations - if (locationsCount == 0L) { - if (!searchText.isNullOrBlank()) { + // Account for locations without coordinates + if (locationsWithoutCoordinatesCount in 1..locationsCount) { val message = context.getString( - R.string.no_found_locations_matching_text, - searchText, + R.string.locations_without_coordinates, + locationsWithoutCoordinatesCount, + locationsCount, ) Timber.w(message) emitSnackBarState( @@ -255,12 +234,46 @@ constructor( ), ) } else { - SnackBarMessageConfig( - message = context.getString(R.string.no_locations_to_render), - actionLabel = context.getString(org.smartregister.fhircore.engine.R.string.ok), - duration = SnackbarDuration.Long, + val message = + if (searchText.isNullOrBlank()) { + context.getString(R.string.all_locations_rendered) + } else context.getString(R.string.all_matching_locations_rendered, locationsCount) + emitSnackBarState( + SnackBarMessageConfig( + message = message, + actionLabel = context.getString(org.smartregister.fhircore.engine.R.string.ok), + duration = SnackbarDuration.Short, + ), ) } + + // Account for missing locations + if (locationsCount == 0L) { + if (!searchText.isNullOrBlank()) { + val message = + context.getString( + R.string.no_found_locations_matching_text, + searchText, + ) + Timber.w(message) + emitSnackBarState( + SnackBarMessageConfig( + message = message, + actionLabel = context.getString(org.smartregister.fhircore.engine.R.string.ok), + duration = SnackbarDuration.Long, + ), + ) + } else { + SnackBarMessageConfig( + message = context.getString(R.string.no_locations_to_render), + actionLabel = context.getString(org.smartregister.fhircore.engine.R.string.ok), + duration = SnackbarDuration.Long, + ) + } + } + } finally { + // isSyncing is set to false after the location retrieval operation is executed + _isSyncing.value = false } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt index fc1cbd1ba7..cb371dfb20 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt @@ -60,8 +60,11 @@ import androidx.compose.ui.unit.sp import com.google.android.fhir.sync.CurrentSyncJobStatus import com.google.android.fhir.sync.SyncJobStatus import com.google.android.fhir.sync.SyncOperation +import java.time.OffsetDateTime +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import org.smartregister.fhircore.engine.ui.components.LineSpinFadeLoaderProgressIndicator import org.smartregister.fhircore.engine.ui.theme.AppTheme import org.smartregister.fhircore.engine.ui.theme.DangerColor import org.smartregister.fhircore.engine.ui.theme.DefaultColor @@ -72,8 +75,6 @@ import org.smartregister.fhircore.engine.util.annotation.PreviewWithBackgroundEx import org.smartregister.fhircore.quest.ui.main.AppMainEvent import org.smartregister.fhircore.quest.ui.shared.models.AppDrawerUIState import org.smartregister.fhircore.quest.util.extensions.conditional -import java.time.OffsetDateTime -import kotlin.time.Duration.Companion.seconds const val TRANSPARENCY = 0.2f const val SYNC_PROGRESS_INDICATOR_TEST_TAG = "syncProgressIndicatorTestTag" @@ -314,7 +315,7 @@ fun SyncStatusView( Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(start = 16.dp) + modifier = Modifier.padding(start = 16.dp), ) { if (currentSyncJobStatus is CurrentSyncJobStatus.Running) { LineSpinFadeLoaderProgressIndicator(