refactor(feature:profile) : move all hardcoded strings to string resources (#1888)

Co-authored-by: Sk Niyaj Ali <niyaj639@gmail.com>
This commit is contained in:
Nithish Sri Ram 2025-07-23 22:50:20 +05:30 committed by GitHub
parent f8d0d0daf2
commit 4d99c505a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 69 additions and 31 deletions

View File

@ -34,4 +34,20 @@
<string name="feature_profile_change_profile_picture">Pick profile picture from device</string>
<string name="feature_profile_remove_profile_picture">Remove profile picture</string>
<string name="feature_profile_updated_sucessfully">Updated Successfully</string>
<!-- New error messages for EditProfileViewModel -->
<string name="feature_profile_error_empty_firstname">Please enter client firstname.</string>
<string name="feature_profile_error_empty_lastname">Please enter client lastname.</string>
<string name="feature_profile_error_empty_email">Please enter your email.</string>
<string name="feature_profile_error_invalid_email">Please enter a valid email.</string>
<string name="feature_profile_error_empty_phone">Please enter your mobile number.</string>
<string name="feature_profile_error_invalid_phone_length">Mobile number must be 10 digits long.</string>
<string name="feature_profile_error_loading_client_image">Failed to load client image.</string>
<string name="feature_profile_error_updating_client_image">Failed to update client image.</string>
<!-- Success messages -->
<string name="feature_profile_profile_updated_successfully">Profile updated successfully</string>
<string name="feature_profile_profile_image_updated_successfully">Profile image updated successfully</string>
<string name="feature_profile_profile_image_description">Profile Image</string>
</resources>

View File

@ -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,
)

View File

@ -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
}
}

View File

@ -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)

View File

@ -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,

View File

@ -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 {