Skip to content

Commit

Permalink
Manual sync in user settings (#3007)
Browse files Browse the repository at this point in the history
* Add sync functionality and view model Flows

* Inject synclistener and hook snackbar to lifecycle

* Add missing scaffoldState

* Remove Experimental tag

* Remove progress emissions
  • Loading branch information
kelvin-ngure authored Jan 24, 2024
1 parent 6d593b8 commit d35bd17
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,40 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
import androidx.compose.material.SnackbarDuration
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.platform.testTag
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import com.google.android.fhir.sync.SyncJobStatus
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch
import org.smartregister.fhircore.engine.BuildConfig
import org.smartregister.fhircore.engine.domain.model.SnackBarMessageConfig
import org.smartregister.fhircore.engine.sync.OnSyncListener
import org.smartregister.fhircore.engine.sync.SyncListenerManager
import org.smartregister.fhircore.engine.ui.theme.AppTheme
import org.smartregister.fhircore.quest.R
import org.smartregister.fhircore.quest.ui.main.AppMainViewModel
import org.smartregister.fhircore.quest.ui.shared.components.SnackBarMessage
import org.smartregister.fhircore.quest.util.extensions.hookSnackBar
import retrofit2.HttpException
import timber.log.Timber

@AndroidEntryPoint
class UserSettingFragment : Fragment() {
class UserSettingFragment : Fragment(), OnSyncListener {
@Inject lateinit var syncListenerManager: SyncListenerManager

val userSettingViewModel by viewModels<UserSettingViewModel>()
private val appMainViewModel by activityViewModels<AppMainViewModel>()
Expand All @@ -46,28 +66,116 @@ class UserSettingFragment : Fragment() {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
val appConfig = appMainViewModel.applicationConfiguration
val scaffoldState = rememberScaffoldState()

LaunchedEffect(Unit) {
userSettingViewModel.snackBarStateFlow.hookSnackBar(
scaffoldState = scaffoldState,
resourceData = null,
navController = findNavController(),
)
}

AppTheme {
UserSettingScreen(
appTitle = appMainViewModel.appMainUiState.value.appTitle,
username = userSettingViewModel.retrieveUsername(),
practitionerLocation = userSettingViewModel.practitionerLocation(),
fullname = userSettingViewModel.retrieveUserInfo()?.name,
allowSwitchingLanguages = userSettingViewModel.allowSwitchingLanguages(),
selectedLanguage = userSettingViewModel.loadSelectedLanguage(),
allowP2PSync = userSettingViewModel.enabledDeviceToDeviceSync(),
languages = userSettingViewModel.languages,
onEvent = userSettingViewModel::onEvent,
showDatabaseResetConfirmation =
userSettingViewModel.showDBResetConfirmationDialog.observeAsState(false).value,
progressBarState =
userSettingViewModel.progressBarState.observeAsState(Pair(false, 0)).value,
isDebugVariant = BuildConfig.DEBUG,
mainNavController = findNavController(),
lastSyncTime = userSettingViewModel.retrieveLastSyncTimestamp(),
showProgressIndicatorFlow = userSettingViewModel.showProgressIndicatorFlow,
Scaffold(
scaffoldState = scaffoldState,
snackbarHost = { snackBarHostState ->
SnackBarMessage(
snackBarHostState = snackBarHostState,
backgroundColorHex = appConfig.snackBarTheme.backgroundColor,
actionColorHex = appConfig.snackBarTheme.actionTextColor,
contentColorHex = appConfig.snackBarTheme.messageTextColor,
)
},
) {
Box(
modifier =
androidx.compose.ui.Modifier.padding(it).testTag(USER_SETTING_SCREEN_BOX_TAG),
) {
UserSettingScreen(
appTitle = appMainViewModel.appMainUiState.value.appTitle,
username = userSettingViewModel.retrieveUsername(),
practitionerLocation = userSettingViewModel.practitionerLocation(),
fullname = userSettingViewModel.retrieveUserInfo()?.name,
allowSwitchingLanguages = userSettingViewModel.allowSwitchingLanguages(),
selectedLanguage = userSettingViewModel.loadSelectedLanguage(),
allowP2PSync = userSettingViewModel.enabledDeviceToDeviceSync(),
languages = userSettingViewModel.languages,
onEvent = userSettingViewModel::onEvent,
showDatabaseResetConfirmation =
userSettingViewModel.showDBResetConfirmationDialog.observeAsState(false).value,
progressBarState =
userSettingViewModel.progressBarState.observeAsState(Pair(false, 0)).value,
isDebugVariant = BuildConfig.DEBUG,
mainNavController = findNavController(),
lastSyncTime = userSettingViewModel.retrieveLastSyncTimestamp(),
showProgressIndicatorFlow = userSettingViewModel.showProgressIndicatorFlow,
)
}
}
}
}
}
}

override fun onResume() {
super.onResume()
syncListenerManager.registerSyncListener(this, lifecycle)
}

override fun onSync(syncJobStatus: SyncJobStatus) {
when (syncJobStatus) {
is SyncJobStatus.Started ->
lifecycleScope.launch {
userSettingViewModel.emitSnackBarState(
SnackBarMessageConfig(message = getString(R.string.syncing)),
)
}
is SyncJobStatus.Finished -> {
lifecycleScope.launch {
userSettingViewModel.emitSnackBarState(
SnackBarMessageConfig(
message = getString(R.string.sync_completed),
actionLabel = getString(R.string.ok).uppercase(),
duration = SnackbarDuration.Long,
),
)
}
}
is SyncJobStatus.Failed -> {
val hasAuthError =
try {
Timber.e(syncJobStatus.exceptions.joinToString { it.exception.message ?: "" })
syncJobStatus.exceptions.any {
it.exception is HttpException && (it.exception as HttpException).code() == 401
}
} catch (nullPointerException: NullPointerException) {
false
}

lifecycleScope.launch {
userSettingViewModel.emitSnackBarState(
SnackBarMessageConfig(
message =
getString(
if (hasAuthError) {
R.string.sync_unauthorised
} else R.string.sync_completed_with_errors,
),
duration = SnackbarDuration.Long,
actionLabel = getString(R.string.ok).uppercase(),
),
)
}
}
else -> {
// Do nothing
}
}
}

companion object {
const val USER_SETTING_SCREEN_BOX_TAG = "fragmentUserSettingScreenTestTag"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ import java.util.Locale
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.smartregister.fhircore.engine.R
import org.smartregister.fhircore.engine.configuration.ConfigType
import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry
import org.smartregister.fhircore.engine.configuration.app.ApplicationConfiguration
import org.smartregister.fhircore.engine.data.remote.model.response.UserInfo
import org.smartregister.fhircore.engine.domain.model.SnackBarMessageConfig
import org.smartregister.fhircore.engine.sync.SyncBroadcaster
import org.smartregister.fhircore.engine.util.DispatcherProvider
import org.smartregister.fhircore.engine.util.SecureSharedPreference
Expand Down Expand Up @@ -77,6 +79,8 @@ constructor(
private val applicationConfiguration: ApplicationConfiguration by lazy {
configurationRegistry.retrieveConfiguration(ConfigType.Application)
}
private val _snackBarStateFlow = MutableSharedFlow<SnackBarMessageConfig>()
val snackBarStateFlow = _snackBarStateFlow.asSharedFlow()

val appVersionCode = BuildConfig.VERSION_CODE
val appVersionName = BuildConfig.VERSION_NAME
Expand Down Expand Up @@ -204,4 +208,8 @@ constructor(
}
}
}

suspend fun emitSnackBarState(snackBarMessageConfig: SnackBarMessageConfig) {
_snackBarStateFlow.emit(snackBarMessageConfig)
}
}

0 comments on commit d35bd17

Please sign in to comment.