diff --git a/feature/settings/src/commonMain/composeResources/values/strings.xml b/feature/settings/src/commonMain/composeResources/values/strings.xml
index f4c2c024d..d619c56c9 100644
--- a/feature/settings/src/commonMain/composeResources/values/strings.xml
+++ b/feature/settings/src/commonMain/composeResources/values/strings.xml
@@ -83,8 +83,11 @@
App Info
Find the app version details
- Logout
- Logout from all devices
+ Logout Now
+ Are You Sure You Want To Logout?
+ Logging out will end your current and all active sessions for security purposes. You’ll need to log in again to access your account.
+ I don’t want to logout?
+ Back to home
Settings
@@ -93,7 +96,6 @@
No internet connection.
Invalid profile image format.
Customer Account: %1$s
- Are you sure to logout?
About Us
diff --git a/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/componenets/MifosLogoutDilaog.kt b/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/componenets/MifosLogoutDilaog.kt
new file mode 100644
index 000000000..613cf3c93
--- /dev/null
+++ b/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/componenets/MifosLogoutDilaog.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.settings.componenets
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+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.material3.AlertDialog
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import mifos_mobile.feature.settings.generated.resources.Res
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_action
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_description
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_message
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_now
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_title
+import org.jetbrains.compose.resources.StringResource
+import org.jetbrains.compose.resources.stringResource
+import org.jetbrains.compose.ui.tooling.preview.Preview
+import org.mifos.mobile.core.designsystem.component.MifosButton
+import org.mifos.mobile.core.designsystem.theme.AppColors
+import org.mifos.mobile.core.designsystem.theme.DesignToken
+import org.mifos.mobile.core.designsystem.theme.MifosTypography
+
+@Composable
+fun MifosLogoutDialog(
+ visibilityState: LogoutDialogState,
+): Unit = when (visibilityState) {
+ LogoutDialogState.Hidden -> Unit
+ is LogoutDialogState.Shown -> {
+ AlertDialog(
+ onDismissRequest = visibilityState.onDismiss,
+ confirmButton = {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(
+ space = DesignToken.spacing.medium,
+ alignment = Alignment.CenterVertically,
+ ),
+ ) {
+ MifosButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(DesignToken.sizes.buttonHeight),
+ shape = DesignToken.shapes.medium,
+ onClick = visibilityState.onLogout,
+ ) {
+ Text(
+ text = stringResource(Res.string.feature_settings_logout_now),
+ style = MifosTypography.titleMedium,
+ )
+ }
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(
+ space = DesignToken.spacing.extraSmall,
+ alignment = Alignment.CenterHorizontally,
+ ),
+ ) {
+ Text(
+ text = stringResource(visibilityState.message),
+ style = MifosTypography.bodySmallEmphasized,
+ color = MaterialTheme.colorScheme.secondary,
+ )
+
+ Text(
+ text = stringResource(visibilityState.messageActionText),
+ style = MifosTypography.bodySmallEmphasized,
+ color = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.clickable {
+ visibilityState.onNavigateToHome.invoke()
+ },
+ )
+ }
+ }
+ },
+ title = {
+ Text(
+ text = stringResource(visibilityState.title),
+ style = MifosTypography.headlineSmallEmphasized,
+ color = AppColors.customBlack,
+ modifier = Modifier
+ .fillMaxWidth()
+ .testTag("AlertTitleText"),
+ )
+ },
+ text = {
+ Text(
+ text = stringResource(visibilityState.description),
+ style = MifosTypography.labelMediumEmphasized,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier
+ .fillMaxWidth()
+ .testTag("AlertContentText"),
+ )
+ },
+ containerColor = Color.White,
+ shape = DesignToken.shapes.medium,
+ )
+ }
+}
+
+sealed interface LogoutDialogState {
+
+ data object Hidden : LogoutDialogState
+
+ data class Shown(
+ val description: StringResource,
+ val title: StringResource,
+ val message: StringResource,
+ val messageActionText: StringResource,
+ val onLogout: () -> Unit,
+ val onNavigateToHome: () -> Unit,
+ val onDismiss: () -> Unit,
+ ) : LogoutDialogState
+}
+
+@Preview
+@Composable
+fun MifosLogoutDialogPreview() {
+ MifosLogoutDialog(
+ visibilityState = LogoutDialogState.Shown(
+ description = Res.string.feature_settings_logout_description,
+ title = Res.string.feature_settings_logout_title,
+ message = Res.string.feature_settings_logout_message,
+ messageActionText = Res.string.feature_settings_logout_action,
+ onLogout = { },
+ onNavigateToHome = { },
+ onDismiss = { },
+ ),
+ )
+}
diff --git a/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsScreen.kt b/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsScreen.kt
index ad1b7cd51..5ca1037e3 100644
--- a/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsScreen.kt
+++ b/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsScreen.kt
@@ -37,8 +37,8 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.collections.immutable.ImmutableList
import mifos_mobile.feature.settings.generated.resources.Res
-import mifos_mobile.feature.settings.generated.resources.feature_settings_action_logout
import mifos_mobile.feature.settings.generated.resources.feature_settings_customer_account_no
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_action
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_message
import mifos_mobile.feature.settings.generated.resources.feature_settings_top_bar_title
import org.jetbrains.compose.resources.stringResource
@@ -53,6 +53,8 @@ import org.mifos.mobile.core.ui.component.MifosActionCard
import org.mifos.mobile.core.ui.component.MifosProgressIndicator
import org.mifos.mobile.core.ui.component.MifosUserImage
import org.mifos.mobile.core.ui.utils.EventsEffect
+import org.mifos.mobile.feature.settings.componenets.LogoutDialogState
+import org.mifos.mobile.feature.settings.componenets.MifosLogoutDialog
import org.mifos.mobile.feature.settings.componenets.SettingsItems
@Composable
@@ -104,14 +106,17 @@ private fun SettingsDialog(
SettingsState.DialogState.Loading -> MifosProgressIndicator()
- SettingsState.DialogState.Logout -> {
- MifosBasicDialog(
- visibilityState = BasicDialogState.Shown(
- title = stringResource(Res.string.feature_settings_action_logout),
- message = stringResource(Res.string.feature_settings_logout_message),
+ is SettingsState.DialogState.Logout -> {
+ MifosLogoutDialog(
+ visibilityState = LogoutDialogState.Shown(
+ description = state.dialogState.message,
+ title = state.dialogState.title,
+ message = Res.string.feature_settings_logout_message,
+ messageActionText = Res.string.feature_settings_logout_action,
+ onLogout = { onAction(SettingsAction.Logout) },
+ onNavigateToHome = { onAction(SettingsAction.OnNavigateBack) },
+ onDismiss = { onAction(SettingsAction.DismissDialog) },
),
- onDismissRequest = { onAction(SettingsAction.DismissDialog) },
- onConfirm = { onAction(SettingsAction.Logout) },
)
}
diff --git a/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsViewModel.kt b/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsViewModel.kt
index 5c7dcba22..9ae5efd62 100644
--- a/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsViewModel.kt
+++ b/feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsViewModel.kt
@@ -17,9 +17,12 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import mifos_mobile.feature.settings.generated.resources.Res
import mifos_mobile.feature.settings.generated.resources.feature_settings_error_fetching_client
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_description
+import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_title
import org.jetbrains.compose.resources.StringResource
import org.mifos.mobile.core.common.DataState
import org.mifos.mobile.core.data.repository.HomeRepository
+import org.mifos.mobile.core.data.repository.UserDataRepository
import org.mifos.mobile.core.datastore.UserPreferencesRepository
import org.mifos.mobile.core.model.entity.client.Client
import org.mifos.mobile.core.ui.utils.BaseViewModel
@@ -44,7 +47,7 @@ import kotlin.io.encoding.ExperimentalEncodingApi
internal class SettingsViewModel(
private val homeRepositoryImpl: HomeRepository,
private val userPreferencesRepositoryImpl: UserPreferencesRepository,
-// private val userDataRepositoryImpl: UserDataRepository,
+ private val userDataRepositoryImpl: UserDataRepository,
) : BaseViewModel(
initialState = run {
SettingsState(
@@ -85,8 +88,8 @@ internal class SettingsViewModel(
when (action) {
SettingsAction.OnNavigateBack -> sendEvent(SettingsEvents.NavigateBack)
SettingsAction.DismissDialog -> setDialogState(null)
- SettingsAction.LogoutDialog -> setDialogState(SettingsState.DialogState.Logout)
- SettingsAction.Logout -> handleLogout()
+ SettingsAction.LogoutDialog -> handleLogout()
+ SettingsAction.Logout -> handleInternalLogOut()
is SettingsAction.Internal.ReceiveClientInfo -> handleClientResponse(action.dataState)
is SettingsAction.Internal.ReceiveClientImage -> handleClientImageResponse(action.dataState)
is SettingsAction.NavigateTo -> sendEvent(SettingsEvents.NavigateTo(action.item))
@@ -96,14 +99,42 @@ internal class SettingsViewModel(
/**
* Handles the user logout process.
*
- * This function would typically perform the following actions:
- * 1. Clear user-specific data, such as authentication tokens and client information, from the repository.
- * 2. Reset the application state to its initial, logged-out state.
- * 3. Send a navigation event to redirect the user to the login or welcome screen.
+ * This function initiates the logout flow by updating the state to show a confirmation dialog.
+ * The actual logout logic (clearing data, navigating) is handled by a separate function
+ * after the user confirms the action.
+ *
+ * This function sets the dialog state to a [SettingsState.DialogState.Logout] with
+ * a title and a descriptive message to prompt user confirmation.
*/
private fun handleLogout() {
+ mutableStateFlow.update {
+ it.copy(
+ dialogState = SettingsState.DialogState.Logout(
+ title = Res.string.feature_settings_logout_title,
+ message = Res.string.feature_settings_logout_description,
+ ),
+ )
+ }
+ }
+
+ /**
+ * Executes the internal logout logic.
+ *
+ * This function first dismisses any active dialog by setting the dialog state to null.
+ * It then triggers the actual logout operation on the `userDataRepository`. This includes
+ * clearing user-specific data and tokens from the repository, which is a critical
+ * step in securely logging out the user.
+ *
+ * The commented-out code `userDataRepository.logout(...)` represents the intended
+ * functionality to be implemented. The `reason` parameter provides context for the logout event.
+ */
+ private fun handleInternalLogOut() {
+ mutableStateFlow.update {
+ it.copy(dialogState = null)
+ }
+
viewModelScope.launch {
- userPreferencesRepositoryImpl.logOut()
+ userDataRepositoryImpl.logOut()
}
}
@@ -239,7 +270,10 @@ internal data class SettingsState(
data object Loading : DialogState
/** Represents a logout dialog. */
- data object Logout : DialogState
+ data class Logout(
+ val title: StringResource,
+ val message: StringResource,
+ ) : DialogState
}
}