Skip to content

Commit 3d973e6

Browse files
refactor: logout dialog (#2940)
1 parent 3b4bef2 commit 3d973e6

File tree

4 files changed

+210
-20
lines changed

4 files changed

+210
-20
lines changed

feature/settings/src/commonMain/composeResources/values/strings.xml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@
8383
<string name="feature_settings_action_app_info">App Info</string>
8484
<string name="feature_settings_action_app_info_tip">Find the app version details</string>
8585

86-
<string name="feature_settings_action_logout">Logout</string>
87-
<string name="feature_settings_action_logout_tip">Logout from all devices</string>
86+
<string name="feature_settings_logout_now">Logout Now</string>
87+
<string name="feature_settings_logout_title">Are You Sure You Want To Logout? </string>
88+
<string name="feature_settings_logout_description">Logging out will end your current and all active sessions for security purposes. You’ll need to log in again to access your account.</string>
89+
<string name="feature_settings_logout_message">I don’t want to logout?</string>
90+
<string name="feature_settings_logout_action">Back to home</string>
8891

8992
<string name="feature_settings_top_bar_title">Settings</string>
9093

@@ -93,7 +96,6 @@
9396
<string name="feature_settings_internet_not_connected">No internet connection.</string>
9497
<string name="feature_settings_error_invalid_profile_image">Invalid profile image format.</string>
9598
<string name="feature_settings_customer_account_no">Customer Account: %1$s</string>
96-
<string name="feature_settings_logout_message">Are you sure to logout?</string>
9799

98100
<!-- About Screen -->
99101
<string name="feature_settings_about_topbar_title">About Us</string>
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
9+
*/
10+
package org.mifos.mobile.feature.settings.componenets
11+
12+
import androidx.compose.foundation.clickable
13+
import androidx.compose.foundation.layout.Arrangement
14+
import androidx.compose.foundation.layout.Column
15+
import androidx.compose.foundation.layout.Row
16+
import androidx.compose.foundation.layout.fillMaxWidth
17+
import androidx.compose.foundation.layout.height
18+
import androidx.compose.material3.AlertDialog
19+
import androidx.compose.material3.MaterialTheme
20+
import androidx.compose.material3.Text
21+
import androidx.compose.runtime.Composable
22+
import androidx.compose.ui.Alignment
23+
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.graphics.Color
25+
import androidx.compose.ui.platform.testTag
26+
import mifos_mobile.feature.settings.generated.resources.Res
27+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_action
28+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_description
29+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_message
30+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_now
31+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_title
32+
import org.jetbrains.compose.resources.StringResource
33+
import org.jetbrains.compose.resources.stringResource
34+
import org.jetbrains.compose.ui.tooling.preview.Preview
35+
import org.mifos.mobile.core.designsystem.component.MifosButton
36+
import org.mifos.mobile.core.designsystem.theme.AppColors
37+
import org.mifos.mobile.core.designsystem.theme.DesignToken
38+
import org.mifos.mobile.core.designsystem.theme.MifosTypography
39+
40+
@Composable
41+
fun MifosLogoutDialog(
42+
visibilityState: LogoutDialogState,
43+
): Unit = when (visibilityState) {
44+
LogoutDialogState.Hidden -> Unit
45+
is LogoutDialogState.Shown -> {
46+
AlertDialog(
47+
onDismissRequest = visibilityState.onDismiss,
48+
confirmButton = {
49+
Column(
50+
horizontalAlignment = Alignment.CenterHorizontally,
51+
verticalArrangement = Arrangement.spacedBy(
52+
space = DesignToken.spacing.medium,
53+
alignment = Alignment.CenterVertically,
54+
),
55+
) {
56+
MifosButton(
57+
modifier = Modifier
58+
.fillMaxWidth()
59+
.height(DesignToken.sizes.buttonHeight),
60+
shape = DesignToken.shapes.medium,
61+
onClick = visibilityState.onLogout,
62+
) {
63+
Text(
64+
text = stringResource(Res.string.feature_settings_logout_now),
65+
style = MifosTypography.titleMedium,
66+
)
67+
}
68+
69+
Row(
70+
modifier = Modifier.fillMaxWidth(),
71+
verticalAlignment = Alignment.CenterVertically,
72+
horizontalArrangement = Arrangement.spacedBy(
73+
space = DesignToken.spacing.extraSmall,
74+
alignment = Alignment.CenterHorizontally,
75+
),
76+
) {
77+
Text(
78+
text = stringResource(visibilityState.message),
79+
style = MifosTypography.bodySmallEmphasized,
80+
color = MaterialTheme.colorScheme.secondary,
81+
)
82+
83+
Text(
84+
text = stringResource(visibilityState.messageActionText),
85+
style = MifosTypography.bodySmallEmphasized,
86+
color = MaterialTheme.colorScheme.primary,
87+
modifier = Modifier.clickable {
88+
visibilityState.onNavigateToHome.invoke()
89+
},
90+
)
91+
}
92+
}
93+
},
94+
title = {
95+
Text(
96+
text = stringResource(visibilityState.title),
97+
style = MifosTypography.headlineSmallEmphasized,
98+
color = AppColors.customBlack,
99+
modifier = Modifier
100+
.fillMaxWidth()
101+
.testTag("AlertTitleText"),
102+
)
103+
},
104+
text = {
105+
Text(
106+
text = stringResource(visibilityState.description),
107+
style = MifosTypography.labelMediumEmphasized,
108+
color = MaterialTheme.colorScheme.secondary,
109+
modifier = Modifier
110+
.fillMaxWidth()
111+
.testTag("AlertContentText"),
112+
)
113+
},
114+
containerColor = Color.White,
115+
shape = DesignToken.shapes.medium,
116+
)
117+
}
118+
}
119+
120+
sealed interface LogoutDialogState {
121+
122+
data object Hidden : LogoutDialogState
123+
124+
data class Shown(
125+
val description: StringResource,
126+
val title: StringResource,
127+
val message: StringResource,
128+
val messageActionText: StringResource,
129+
val onLogout: () -> Unit,
130+
val onNavigateToHome: () -> Unit,
131+
val onDismiss: () -> Unit,
132+
) : LogoutDialogState
133+
}
134+
135+
@Preview
136+
@Composable
137+
fun MifosLogoutDialogPreview() {
138+
MifosLogoutDialog(
139+
visibilityState = LogoutDialogState.Shown(
140+
description = Res.string.feature_settings_logout_description,
141+
title = Res.string.feature_settings_logout_title,
142+
message = Res.string.feature_settings_logout_message,
143+
messageActionText = Res.string.feature_settings_logout_action,
144+
onLogout = { },
145+
onNavigateToHome = { },
146+
onDismiss = { },
147+
),
148+
)
149+
}

feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsScreen.kt

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ import androidx.compose.ui.unit.dp
3737
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3838
import kotlinx.collections.immutable.ImmutableList
3939
import mifos_mobile.feature.settings.generated.resources.Res
40-
import mifos_mobile.feature.settings.generated.resources.feature_settings_action_logout
4140
import mifos_mobile.feature.settings.generated.resources.feature_settings_customer_account_no
41+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_action
4242
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_message
4343
import mifos_mobile.feature.settings.generated.resources.feature_settings_top_bar_title
4444
import org.jetbrains.compose.resources.stringResource
@@ -53,6 +53,8 @@ import org.mifos.mobile.core.ui.component.MifosActionCard
5353
import org.mifos.mobile.core.ui.component.MifosProgressIndicator
5454
import org.mifos.mobile.core.ui.component.MifosUserImage
5555
import org.mifos.mobile.core.ui.utils.EventsEffect
56+
import org.mifos.mobile.feature.settings.componenets.LogoutDialogState
57+
import org.mifos.mobile.feature.settings.componenets.MifosLogoutDialog
5658
import org.mifos.mobile.feature.settings.componenets.SettingsItems
5759

5860
@Composable
@@ -104,14 +106,17 @@ private fun SettingsDialog(
104106

105107
SettingsState.DialogState.Loading -> MifosProgressIndicator()
106108

107-
SettingsState.DialogState.Logout -> {
108-
MifosBasicDialog(
109-
visibilityState = BasicDialogState.Shown(
110-
title = stringResource(Res.string.feature_settings_action_logout),
111-
message = stringResource(Res.string.feature_settings_logout_message),
109+
is SettingsState.DialogState.Logout -> {
110+
MifosLogoutDialog(
111+
visibilityState = LogoutDialogState.Shown(
112+
description = state.dialogState.message,
113+
title = state.dialogState.title,
114+
message = Res.string.feature_settings_logout_message,
115+
messageActionText = Res.string.feature_settings_logout_action,
116+
onLogout = { onAction(SettingsAction.Logout) },
117+
onNavigateToHome = { onAction(SettingsAction.OnNavigateBack) },
118+
onDismiss = { onAction(SettingsAction.DismissDialog) },
112119
),
113-
onDismissRequest = { onAction(SettingsAction.DismissDialog) },
114-
onConfirm = { onAction(SettingsAction.Logout) },
115120
)
116121
}
117122

feature/settings/src/commonMain/kotlin/org/mifos/mobile/feature/settings/settings/SettingsViewModel.kt

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ import kotlinx.coroutines.flow.update
1717
import kotlinx.coroutines.launch
1818
import mifos_mobile.feature.settings.generated.resources.Res
1919
import mifos_mobile.feature.settings.generated.resources.feature_settings_error_fetching_client
20+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_description
21+
import mifos_mobile.feature.settings.generated.resources.feature_settings_logout_title
2022
import org.jetbrains.compose.resources.StringResource
2123
import org.mifos.mobile.core.common.DataState
2224
import org.mifos.mobile.core.data.repository.HomeRepository
25+
import org.mifos.mobile.core.data.repository.UserDataRepository
2326
import org.mifos.mobile.core.datastore.UserPreferencesRepository
2427
import org.mifos.mobile.core.model.entity.client.Client
2528
import org.mifos.mobile.core.ui.utils.BaseViewModel
@@ -44,7 +47,7 @@ import kotlin.io.encoding.ExperimentalEncodingApi
4447
internal class SettingsViewModel(
4548
private val homeRepositoryImpl: HomeRepository,
4649
private val userPreferencesRepositoryImpl: UserPreferencesRepository,
47-
// private val userDataRepositoryImpl: UserDataRepository,
50+
private val userDataRepositoryImpl: UserDataRepository,
4851
) : BaseViewModel<SettingsState, SettingsEvents, SettingsAction>(
4952
initialState = run {
5053
SettingsState(
@@ -85,8 +88,8 @@ internal class SettingsViewModel(
8588
when (action) {
8689
SettingsAction.OnNavigateBack -> sendEvent(SettingsEvents.NavigateBack)
8790
SettingsAction.DismissDialog -> setDialogState(null)
88-
SettingsAction.LogoutDialog -> setDialogState(SettingsState.DialogState.Logout)
89-
SettingsAction.Logout -> handleLogout()
91+
SettingsAction.LogoutDialog -> handleLogout()
92+
SettingsAction.Logout -> handleInternalLogOut()
9093
is SettingsAction.Internal.ReceiveClientInfo -> handleClientResponse(action.dataState)
9194
is SettingsAction.Internal.ReceiveClientImage -> handleClientImageResponse(action.dataState)
9295
is SettingsAction.NavigateTo -> sendEvent(SettingsEvents.NavigateTo(action.item))
@@ -96,14 +99,42 @@ internal class SettingsViewModel(
9699
/**
97100
* Handles the user logout process.
98101
*
99-
* This function would typically perform the following actions:
100-
* 1. Clear user-specific data, such as authentication tokens and client information, from the repository.
101-
* 2. Reset the application state to its initial, logged-out state.
102-
* 3. Send a navigation event to redirect the user to the login or welcome screen.
102+
* This function initiates the logout flow by updating the state to show a confirmation dialog.
103+
* The actual logout logic (clearing data, navigating) is handled by a separate function
104+
* after the user confirms the action.
105+
*
106+
* This function sets the dialog state to a [SettingsState.DialogState.Logout] with
107+
* a title and a descriptive message to prompt user confirmation.
103108
*/
104109
private fun handleLogout() {
110+
mutableStateFlow.update {
111+
it.copy(
112+
dialogState = SettingsState.DialogState.Logout(
113+
title = Res.string.feature_settings_logout_title,
114+
message = Res.string.feature_settings_logout_description,
115+
),
116+
)
117+
}
118+
}
119+
120+
/**
121+
* Executes the internal logout logic.
122+
*
123+
* This function first dismisses any active dialog by setting the dialog state to null.
124+
* It then triggers the actual logout operation on the `userDataRepository`. This includes
125+
* clearing user-specific data and tokens from the repository, which is a critical
126+
* step in securely logging out the user.
127+
*
128+
* The commented-out code `userDataRepository.logout(...)` represents the intended
129+
* functionality to be implemented. The `reason` parameter provides context for the logout event.
130+
*/
131+
private fun handleInternalLogOut() {
132+
mutableStateFlow.update {
133+
it.copy(dialogState = null)
134+
}
135+
105136
viewModelScope.launch {
106-
userPreferencesRepositoryImpl.logOut()
137+
userDataRepositoryImpl.logOut()
107138
}
108139
}
109140

@@ -239,7 +270,10 @@ internal data class SettingsState(
239270
data object Loading : DialogState
240271

241272
/** Represents a logout dialog. */
242-
data object Logout : DialogState
273+
data class Logout(
274+
val title: StringResource,
275+
val message: StringResource,
276+
) : DialogState
243277
}
244278
}
245279

0 commit comments

Comments
 (0)