From 4d99c505a42e3cd6914f3ba7507bdc99d4372080 Mon Sep 17 00:00:00 2001 From: Nithish Sri Ram <115037539+Nithish-Sri-Ram@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:50:20 +0530 Subject: [PATCH] refactor(feature:profile) : move all hardcoded strings to string resources (#1888) Co-authored-by: Sk Niyaj Ali --- .../composeResources/values/strings.xml | 16 +++++++ .../mifospay/feature/profile/ProfileScreen.kt | 7 +-- .../feature/profile/ProfileViewModel.kt | 4 +- .../profile/components/ProfileImage.kt | 4 +- .../feature/profile/edit/EditProfileScreen.kt | 22 ++++++--- .../profile/edit/EditProfileViewModel.kt | 47 ++++++++++++------- 6 files changed, 69 insertions(+), 31 deletions(-) diff --git a/feature/profile/src/commonMain/composeResources/values/strings.xml b/feature/profile/src/commonMain/composeResources/values/strings.xml index f89cd59c..37521b0c 100644 --- a/feature/profile/src/commonMain/composeResources/values/strings.xml +++ b/feature/profile/src/commonMain/composeResources/values/strings.xml @@ -34,4 +34,20 @@ Pick profile picture from device Remove profile picture Updated Successfully + + + Please enter client firstname. + Please enter client lastname. + Please enter your email. + Please enter a valid email. + Please enter your mobile number. + Mobile number must be 10 digits long. + Failed to load client image. + Failed to update client image. + + + Profile updated successfully + Profile image updated successfully + + Profile Image \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileScreen.kt b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileScreen.kt index 0965eebb..8d87de48 100644 --- a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileScreen.kt +++ b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import mobile_wallet.feature.profile.generated.resources.Res import mobile_wallet.feature.profile.generated.resources.feature_profile_link_bank_account +import mobile_wallet.feature.profile.generated.resources.feature_profile_loading import mobile_wallet.feature.profile.generated.resources.feature_profile_personal_qr_code import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel @@ -99,7 +100,7 @@ internal fun ProfileScreenContent( when (clientState) { is ProfileState.ViewState.Loading -> { MifosOverlayLoadingWheel( - contentDesc = "ProfileLoading", + contentDesc = stringResource(Res.string.feature_profile_loading), modifier = Modifier.align(Alignment.Center), ) } @@ -161,7 +162,7 @@ private fun ProfileScreenContent( leadingIcon = { Icon( imageVector = MifosIcons.QrCode, - contentDescription = "Personal QR Code", + contentDescription = stringResource(Res.string.feature_profile_personal_qr_code), ) }, ) @@ -195,7 +196,7 @@ private fun ProfileDialogs( when (dialogState) { is ProfileState.DialogState.Error -> MifosBasicDialog( visibilityState = BasicDialogState.Shown( - message = dialogState.message, + message = stringResource(dialogState.message), ), onDismissRequest = onDismissRequest, ) diff --git a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileViewModel.kt b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileViewModel.kt index b3690efb..bcd06e26 100644 --- a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileViewModel.kt +++ b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/ProfileViewModel.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.jetbrains.compose.resources.StringResource import org.mifospay.core.common.DataState import org.mifospay.core.data.repository.ClientRepository import org.mifospay.core.datastore.UserPreferencesRepository @@ -120,8 +121,7 @@ internal data class ProfileState( sealed interface DialogState { data object Loading : DialogState - - data class Error(val message: String) : DialogState + data class Error(val message: StringResource) : DialogState } } diff --git a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/components/ProfileImage.kt b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/components/ProfileImage.kt index ef67fe72..f464a1bf 100644 --- a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/components/ProfileImage.kt +++ b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/components/ProfileImage.kt @@ -29,8 +29,10 @@ import coil3.compose.AsyncImage import coil3.compose.LocalPlatformContext import coil3.request.ImageRequest import mobile_wallet.feature.profile.generated.resources.Res +import mobile_wallet.feature.profile.generated.resources.feature_profile_profile_image_description import mobile_wallet.feature.profile.generated.resources.placeholder import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource import org.mifospay.core.designsystem.icon.MifosIcons import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi @@ -92,7 +94,7 @@ fun EditableProfileImage( error = painterResource(Res.drawable.placeholder), fallback = painterResource(Res.drawable.placeholder), imageLoader = ImageLoader(context), - contentDescription = "Profile Image", + contentDescription = stringResource(Res.string.feature_profile_profile_image_description), contentScale = ContentScale.Crop, modifier = Modifier .size(150.dp) diff --git a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt index 11b1f6be..ef336ff0 100644 --- a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt +++ b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt @@ -38,6 +38,7 @@ import mobile_wallet.feature.profile.generated.resources.feature_profile_lastnam import mobile_wallet.feature.profile.generated.resources.feature_profile_mobile import mobile_wallet.feature.profile.generated.resources.feature_profile_save import mobile_wallet.feature.profile.generated.resources.feature_profile_vpa +import org.jetbrains.compose.resources.getString import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.mifospay.core.designsystem.component.BasicDialogState @@ -66,7 +67,8 @@ internal fun EditProfileScreen( is EditProfileEvent.NavigateBack -> onBackClick.invoke() is EditProfileEvent.ShowToast -> { scope.launch { - snackbarHostState.showSnackbar(event.message) + val message = getString(event.message) + snackbarHostState.showSnackbar(message) } } } @@ -198,12 +200,18 @@ private fun EditProfileDialogs( onDismissRequest: () -> Unit, ) { when (dialogState) { - is EditProfileState.DialogState.Error -> MifosBasicDialog( - visibilityState = BasicDialogState.Shown( - message = dialogState.message, - ), - onDismissRequest = onDismissRequest, - ) + is EditProfileState.DialogState.Error -> { + val message = when (dialogState) { + is EditProfileState.DialogState.Error.StringMessage -> dialogState.message + is EditProfileState.DialogState.Error.ResourceMessage -> stringResource(dialogState.message) + } + MifosBasicDialog( + visibilityState = BasicDialogState.Shown( + message = message, + ), + onDismissRequest = onDismissRequest, + ) + } is EditProfileState.DialogState.Loading -> MifosLoadingDialog( visibilityState = LoadingDialogState.Shown, diff --git a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileViewModel.kt b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileViewModel.kt index b92ff857..18d889dc 100644 --- a/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileViewModel.kt +++ b/feature/profile/src/commonMain/kotlin/org/mifospay/feature/profile/edit/EditProfileViewModel.kt @@ -21,6 +21,16 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import kotlinx.serialization.Transient +import mobile_wallet.feature.profile.generated.resources.Res +import mobile_wallet.feature.profile.generated.resources.feature_profile_error_empty_email +import mobile_wallet.feature.profile.generated.resources.feature_profile_error_empty_firstname +import mobile_wallet.feature.profile.generated.resources.feature_profile_error_empty_lastname +import mobile_wallet.feature.profile.generated.resources.feature_profile_error_empty_phone +import mobile_wallet.feature.profile.generated.resources.feature_profile_error_invalid_email +import mobile_wallet.feature.profile.generated.resources.feature_profile_error_invalid_phone_length +import mobile_wallet.feature.profile.generated.resources.feature_profile_profile_image_updated_successfully +import mobile_wallet.feature.profile.generated.resources.feature_profile_profile_updated_successfully +import org.jetbrains.compose.resources.StringResource import org.mifospay.core.common.DataState import org.mifospay.core.common.getSerialized import org.mifospay.core.common.setSerialized @@ -131,9 +141,9 @@ internal class EditProfileViewModel( } is DataState.Error -> { -// mutableStateFlow.update { -// it.copy(dialogState = Error(action.result.exception.message ?: "")) -// } + mutableStateFlow.update { + it.copy(dialogState = Error.StringMessage(action.result.exception.message ?: "")) + } } is DataState.Loading -> { @@ -164,37 +174,37 @@ internal class EditProfileViewModel( private fun handleUpdateProfile() = when { state.firstNameInput.isEmpty() -> { mutableStateFlow.update { - it.copy(dialogState = Error("Please enter client firstname.")) + it.copy(dialogState = Error.ResourceMessage(Res.string.feature_profile_error_empty_firstname)) } } state.lastNameInput.isEmpty() -> { mutableStateFlow.update { - it.copy(dialogState = Error("Please enter client lastname.")) + it.copy(dialogState = Error.ResourceMessage(Res.string.feature_profile_error_empty_lastname)) } } state.emailInput.isEmpty() -> { mutableStateFlow.update { - it.copy(dialogState = Error("Please enter your email.")) + it.copy(dialogState = Error.ResourceMessage(Res.string.feature_profile_error_empty_email)) } } !state.emailInput.isValidEmail() -> { mutableStateFlow.update { - it.copy(dialogState = Error("Please enter a valid email.")) + it.copy(dialogState = Error.ResourceMessage(Res.string.feature_profile_error_invalid_email)) } } state.phoneNumberInput.isEmpty() -> { mutableStateFlow.update { - it.copy(dialogState = Error("Please enter your mobile number.")) + it.copy(dialogState = Error.ResourceMessage(Res.string.feature_profile_error_empty_phone)) } } state.phoneNumberInput.length < 10 -> { mutableStateFlow.update { - it.copy(dialogState = Error("Mobile number must be 10 digits long.")) + it.copy(dialogState = Error.ResourceMessage(Res.string.feature_profile_error_invalid_phone_length)) } } @@ -213,7 +223,7 @@ internal class EditProfileViewModel( when (action.result) { is DataState.Error -> { mutableStateFlow.update { - it.copy(dialogState = Error(action.result.exception.message ?: "")) + it.copy(dialogState = Error.StringMessage(action.result.exception.message ?: "")) } } @@ -237,8 +247,8 @@ internal class EditProfileViewModel( when (result) { is DataState.Success -> { - sendEvent(EditProfileEvent.ShowToast("Profile updated successfully")) - trySendAction(EditProfileAction.NavigateBack) + sendEvent(EditProfileEvent.ShowToast(Res.string.feature_profile_profile_updated_successfully)) + sendEvent(EditProfileEvent.NavigateBack) } else -> {} @@ -252,7 +262,7 @@ internal class EditProfileViewModel( when (action.result) { is DataState.Error -> { mutableStateFlow.update { - it.copy(dialogState = Error(action.result.exception.message ?: "")) + it.copy(dialogState = Error.StringMessage(action.result.exception.message ?: "")) } } @@ -263,7 +273,7 @@ internal class EditProfileViewModel( } is DataState.Success -> { - sendEvent(EditProfileEvent.ShowToast("Profile image updated successfully")) + sendEvent(EditProfileEvent.ShowToast(Res.string.feature_profile_profile_image_updated_successfully)) } } } @@ -291,11 +301,12 @@ internal data class EditProfileState( @Serializable sealed interface DialogState { - @Serializable data object Loading : DialogState - @Serializable - data class Error(val message: String) : DialogState + sealed interface Error : DialogState { + data class StringMessage(val message: String) : Error + data class ResourceMessage(val message: StringResource) : Error + } } override fun equals(other: Any?): Boolean { @@ -333,7 +344,7 @@ internal data class EditProfileState( sealed interface EditProfileEvent { data object NavigateBack : EditProfileEvent - data class ShowToast(val message: String) : EditProfileEvent + data class ShowToast(val message: StringResource) : EditProfileEvent } sealed interface EditProfileAction {