From e99177cbb7b2e082b393883592dd62cbfc2f6ad5 Mon Sep 17 00:00:00 2001 From: Mikailo01 Date: Sun, 22 Sep 2024 17:19:29 +0200 Subject: [PATCH] dependency updates, refactoring, bug fixes --- app/build.gradle.kts | 6 +- .../data/repository/WordsRepositoryImpl.kt | 2 + .../lenslex/ui/components/EmailField.kt | 6 + .../ui/components/NetworkUnavailableDialog.kt | 7 + .../lenslex/ui/components/PasswordField.kt | 1 + .../bytecause/lenslex/ui/components/SignIn.kt | 2 +- .../bytecause/lenslex/ui/components/SignUp.kt | 2 +- .../ui/events/AccountSettingsUiEvent.kt | 6 +- .../lenslex/ui/interfaces/CredentialType.kt | 1 - .../lenslex/ui/screens/AccountScreen.kt | 2 +- .../ui/screens/AccountSettingsScreen.kt | 238 ++++++++++++------ .../bytecause/lenslex/ui/screens/AddScreen.kt | 3 +- .../lenslex/ui/screens/ExtractedTextScreen.kt | 76 +++--- .../lenslex/ui/screens/HomeScreen.kt | 4 +- .../lenslex/ui/screens/LoginScreen.kt | 2 +- .../ui/screens/ModifiedImagePreviewScreen.kt | 2 +- .../ui/screens/SendEmailResetScreen.kt | 2 +- .../ui/screens/UpdatePasswordScreen.kt | 2 +- .../AccountSettingsState.kt | 4 +- .../{uistate => model}/AccountState.kt | 2 +- .../ui/screens/{uistate => model}/AddState.kt | 2 +- .../screens/{uistate => model}/HomeState.kt | 2 +- .../screens/{uistate => model}/LoginState.kt | 2 +- .../ModifiedImagePreviewState.kt | 2 +- .../{uistate => model}/RecognizedTextState.kt | 2 +- .../{uistate => model}/SendEmailResetState.kt | 2 +- .../{uistate => model}/UpdatePasswordState.kt | 2 +- .../viewmodel/AccountSettingsViewModel.kt | 71 ++++-- .../ui/screens/viewmodel/AccountViewModel.kt | 2 +- .../ui/screens/viewmodel/AddViewModel.kt | 2 +- .../viewmodel/ExtractedTextViewModel.kt | 2 +- .../ui/screens/viewmodel/HomeViewModel.kt | 2 +- .../ui/screens/viewmodel/LoginViewModel.kt | 2 +- .../ModifiedImagePreviewViewModel.kt | 2 +- .../viewmodel/SendEmailResetViewModel.kt | 2 +- .../viewmodel/UpdatePasswordViewModel.kt | 2 +- .../com/bytecause/lenslex/ui/theme/Theme.kt | 1 - .../java/com/bytecause/lenslex/util/Util.kt | 4 +- gradle/libs.versions.toml | 24 +- 39 files changed, 327 insertions(+), 173 deletions(-) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/AccountSettingsState.kt (83%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/AccountState.kt (89%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/AddState.kt (94%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/HomeState.kt (96%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/LoginState.kt (94%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/ModifiedImagePreviewState.kt (87%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/RecognizedTextState.kt (95%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/SendEmailResetState.kt (87%) rename app/src/main/java/com/bytecause/lenslex/ui/screens/{uistate => model}/UpdatePasswordState.kt (94%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 60754cb..4eebf9d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,15 +11,15 @@ plugins { android { namespace = "com.bytecause.lenslex" - compileSdk = 34 + compileSdk = 35 android.buildFeatures.buildConfig = true defaultConfig { applicationId = "com.bytecause.lenslex" minSdk = 24 - targetSdk = 34 + targetSdk = 35 versionCode = 1 - versionName = "1.0" + versionName = "1.0.0-rc1" testInstrumentationRunner = "com.bytecause.lenslex.InstrumentationTestRunner" vectorDrawables { diff --git a/app/src/main/java/com/bytecause/lenslex/data/repository/WordsRepositoryImpl.kt b/app/src/main/java/com/bytecause/lenslex/data/repository/WordsRepositoryImpl.kt index e8c04fe..f25bc3a 100644 --- a/app/src/main/java/com/bytecause/lenslex/data/repository/WordsRepositoryImpl.kt +++ b/app/src/main/java/com/bytecause/lenslex/data/repository/WordsRepositoryImpl.kt @@ -24,6 +24,7 @@ class WordsRepositoryImpl( private fun user(): FirebaseUser? = auth.getAuth().currentUser + @Suppress("UNCHECKED_CAST") override fun getWords( originLangCode: String, targetLangCode: String @@ -63,6 +64,7 @@ class WordsRepositoryImpl( awaitClose { listener?.remove() } } + @Suppress("UNCHECKED_CAST") private fun documentToWords(document: DocumentSnapshot): Words { return document.data?.let { field -> Words( diff --git a/app/src/main/java/com/bytecause/lenslex/ui/components/EmailField.kt b/app/src/main/java/com/bytecause/lenslex/ui/components/EmailField.kt index 14c9129..544215b 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/components/EmailField.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/components/EmailField.kt @@ -12,6 +12,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -27,8 +28,13 @@ fun EmailField( emailValue: String, isEmailError: Boolean, modifier: Modifier = Modifier, + shouldHideSoftKeyboard: Boolean = false, onEmailValueChanged: (String) -> Unit ) { + val keyboardController = LocalSoftwareKeyboardController.current + + if (shouldHideSoftKeyboard) keyboardController?.hide() + OutlinedTextField( modifier = modifier.fillMaxWidth(), value = emailValue, diff --git a/app/src/main/java/com/bytecause/lenslex/ui/components/NetworkUnavailableDialog.kt b/app/src/main/java/com/bytecause/lenslex/ui/components/NetworkUnavailableDialog.kt index dc962e3..36bfba0 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/components/NetworkUnavailableDialog.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/components/NetworkUnavailableDialog.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.bytecause.lenslex.R @@ -55,4 +56,10 @@ fun NetworkUnavailableDialog( .height(15.dp)) } } +} + +@Composable +@Preview +private fun NetworkUnavailableDialogPreview() { + NetworkUnavailableDialog(text = "", onTryAgainClick = {}, onDismiss = {}) } \ No newline at end of file diff --git a/app/src/main/java/com/bytecause/lenslex/ui/components/PasswordField.kt b/app/src/main/java/com/bytecause/lenslex/ui/components/PasswordField.kt index f677331..acd87be 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/components/PasswordField.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/components/PasswordField.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction diff --git a/app/src/main/java/com/bytecause/lenslex/ui/components/SignIn.kt b/app/src/main/java/com/bytecause/lenslex/ui/components/SignIn.kt index f4c65b3..f1efe66 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/components/SignIn.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/components/SignIn.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.unit.dp import com.bytecause.lenslex.R import com.bytecause.lenslex.navigation.Screen import com.bytecause.lenslex.ui.events.LoginUiEvent -import com.bytecause.lenslex.ui.screens.uistate.LoginState +import com.bytecause.lenslex.ui.screens.model.LoginState import com.bytecause.lenslex.util.CredentialValidationResult import com.bytecause.lenslex.util.PasswordValidationResult import com.bytecause.lenslex.util.TestTags diff --git a/app/src/main/java/com/bytecause/lenslex/ui/components/SignUp.kt b/app/src/main/java/com/bytecause/lenslex/ui/components/SignUp.kt index 1242495..22dd6b6 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/components/SignUp.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/components/SignUp.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.bytecause.lenslex.R import com.bytecause.lenslex.ui.events.LoginUiEvent -import com.bytecause.lenslex.ui.screens.uistate.LoginState +import com.bytecause.lenslex.ui.screens.model.LoginState import com.bytecause.lenslex.util.CredentialValidationResult import com.bytecause.lenslex.util.PasswordValidationResult diff --git a/app/src/main/java/com/bytecause/lenslex/ui/events/AccountSettingsUiEvent.kt b/app/src/main/java/com/bytecause/lenslex/ui/events/AccountSettingsUiEvent.kt index 7b7a075..ccfc8b7 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/events/AccountSettingsUiEvent.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/events/AccountSettingsUiEvent.kt @@ -8,7 +8,7 @@ import com.google.firebase.auth.AuthCredential sealed interface AccountSettingsUiEvent { data object OnNavigateBack : AccountSettingsUiEvent data object OnDeleteAccountButtonClick : AccountSettingsUiEvent - data object OnConfirmConfirmationDialog : AccountSettingsUiEvent + data object OnConfirmDeleteConfirmationDialog : AccountSettingsUiEvent data object OnDismissConfirmationDialog : AccountSettingsUiEvent data object OnLaunchReauthenticationGoogleIntent : AccountSettingsUiEvent data object OnCredentialsDialogDismiss : AccountSettingsUiEvent @@ -16,8 +16,10 @@ sealed interface AccountSettingsUiEvent { data class OnLinkGoogleProvider(val value: AuthCredential) : AccountSettingsUiEvent data class OnReauthenticateWithGoogle(val value: AuthCredential) : AccountSettingsUiEvent data class OnShowCredentialDialog(val value: CredentialType) : AccountSettingsUiEvent + data class OnShowReauthorizationDialog(val boolean: Boolean) : AccountSettingsUiEvent + data class OnReauthorizationDialogDoneClick(val credentials: Credentials) : AccountSettingsUiEvent data class OnLinkButtonClick(val value: Provider) : AccountSettingsUiEvent - data class OnEnteredCredential(val value: Credentials) : AccountSettingsUiEvent + data class OnEnteredCredential(val credentials: Credentials) : AccountSettingsUiEvent data class OnDialogCredentialChanged(val value: Credentials.Sensitive) : AccountSettingsUiEvent } diff --git a/app/src/main/java/com/bytecause/lenslex/ui/interfaces/CredentialType.kt b/app/src/main/java/com/bytecause/lenslex/ui/interfaces/CredentialType.kt index ab3c6fc..cd0c013 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/interfaces/CredentialType.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/interfaces/CredentialType.kt @@ -1,7 +1,6 @@ package com.bytecause.lenslex.ui.interfaces sealed interface CredentialType { - data object Reauthorization : CredentialType data object AccountLink : CredentialType data object Username : CredentialType data object Email : CredentialType diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountScreen.kt index f53dd26..ffa784c 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountScreen.kt @@ -61,7 +61,7 @@ import com.bytecause.lenslex.ui.components.RowItem import com.bytecause.lenslex.ui.components.TopAppBar import com.bytecause.lenslex.ui.events.AccountUiEffect import com.bytecause.lenslex.ui.events.AccountUiEvent -import com.bytecause.lenslex.ui.screens.uistate.AccountState +import com.bytecause.lenslex.ui.screens.model.AccountState import com.bytecause.lenslex.ui.screens.viewmodel.AccountViewModel import com.bytecause.lenslex.util.BlurTransformation import com.bytecause.lenslex.util.compressImage diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountSettingsScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountSettingsScreen.kt index 18ccb89..0c2c4ad 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountSettingsScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/AccountSettingsScreen.kt @@ -32,12 +32,14 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -49,6 +51,7 @@ import com.bytecause.lenslex.data.remote.auth.FirebaseAuthClient import com.bytecause.lenslex.ui.components.AccountInfoItem import com.bytecause.lenslex.ui.components.AccountInfoType import com.bytecause.lenslex.ui.components.ConfirmationDialog +import com.bytecause.lenslex.ui.components.Dialog import com.bytecause.lenslex.ui.components.EmailField import com.bytecause.lenslex.ui.components.LinkAccountItem import com.bytecause.lenslex.ui.components.PasswordField @@ -60,7 +63,7 @@ import com.bytecause.lenslex.ui.interfaces.AccountActionResult import com.bytecause.lenslex.ui.interfaces.CredentialType import com.bytecause.lenslex.ui.interfaces.Credentials import com.bytecause.lenslex.ui.interfaces.Provider -import com.bytecause.lenslex.ui.screens.uistate.AccountSettingsState +import com.bytecause.lenslex.ui.screens.model.AccountSettingsState import com.bytecause.lenslex.ui.screens.viewmodel.AccountSettingsViewModel import com.bytecause.lenslex.util.CredentialValidationResult import com.bytecause.lenslex.util.PasswordErrorType @@ -89,6 +92,7 @@ fun AccountSettingsScreenContent( onEvent: (AccountSettingsUiEvent) -> Unit ) { val context = LocalContext.current + val keyboardController = LocalSoftwareKeyboardController.current Scaffold( topBar = { @@ -509,7 +513,7 @@ fun AccountSettingsScreenContent( } state.showCredentialUpdateDialog?.let { - CredentialsDialog( + StatefulCredentialsDialog( credentialValidationResult = state.credentialValidationResult, credentialType = it, onEvent = { event -> onEvent(event) } @@ -523,7 +527,7 @@ fun AccountSettingsScreenContent( .wrapContentSize(), onDismiss = { onEvent(AccountSettingsUiEvent.OnDismissConfirmationDialog) }, onConfirm = { - onEvent(AccountSettingsUiEvent.OnConfirmConfirmationDialog) + onEvent(AccountSettingsUiEvent.OnConfirmDeleteConfirmationDialog) } ) { Row { @@ -543,6 +547,13 @@ fun AccountSettingsScreenContent( } } + if (state.showReauthorizationDialog) { + StatefulReauthorizationDialog( + credentialValidationResult = state.credentialValidationResult, + onEvent = { onEvent(it) } + ) + } + SnackbarHost( hostState = state.snackbarHostState, modifier = Modifier.align(Alignment.BottomCenter) @@ -656,9 +667,7 @@ fun AccountSettingsScreen( } else { // Shows dialog with email and password inputs for reauthorization viewModel.uiEventHandler( - AccountSettingsUiEvent.OnShowCredentialDialog( - CredentialType.Reauthorization - ) + AccountSettingsUiEvent.OnShowReauthorizationDialog(true) ) } } @@ -680,17 +689,7 @@ fun AccountSettingsScreen( } @Composable -@Preview -fun AccountSettingsScreenPreview() { - AccountSettingsScreenContent( - isExpandedScreen = false, - state = AccountSettingsState(), - onEvent = {} - ) -} - -@Composable -fun CredentialsDialog( +fun StatefulCredentialsDialog( credentialValidationResult: CredentialValidationResult?, modifier: Modifier = Modifier, credentialType: CredentialType, @@ -773,48 +772,6 @@ fun CredentialsDialog( verticalArrangement = Arrangement.spacedBy(10.dp) ) { when (credentialType) { - is CredentialType.Reauthorization -> { - Text(text = stringResource(id = R.string.reauthorization)) - - EmailField( - emailValue = email, - isEmailError = isEmailError, - onEmailValueChanged = { - email = it - onEvent( - AccountSettingsUiEvent.OnDialogCredentialChanged( - Credentials.Sensitive.SignInCredentials( - email, - password - ) - ) - ) - } - ) - - PasswordField( - password = password, - passwordErrors = isPasswordError, - isPasswordEnabled = !isEmailError, - isPasswordVisible = isPasswordVisible, - onPasswordVisibilityClick = { isPasswordVisible = it }, - onPasswordValueChange = { - password = it - }, - onCredentialChanged = { - onEvent( - AccountSettingsUiEvent.OnDialogCredentialChanged( - Credentials.Sensitive.SignInCredentials( - email, - password - ) - ) - ) - } - ) - - } - is CredentialType.AccountLink -> { EmailField( emailValue = email, @@ -858,7 +815,7 @@ fun CredentialsDialog( leadingIcon = { Icon( painter = painterResource(id = R.drawable.baseline_perm_identity_24), - contentDescription = "Enter new username" + contentDescription = null ) }, label = { @@ -912,10 +869,6 @@ fun CredentialsDialog( onEvent( AccountSettingsUiEvent.OnEnteredCredential( when (credentialType) { - is CredentialType.Reauthorization -> { - Credentials.Sensitive.SignInCredentials(email, password) - } - is CredentialType.AccountLink -> { Credentials.Sensitive.SignInCredentials(email, password) } @@ -953,10 +906,156 @@ fun CredentialsDialog( } } +@Composable +fun StatefulReauthorizationDialog( + credentialValidationResult: CredentialValidationResult?, + onEvent: (AccountSettingsUiEvent) -> Unit +) { + + var email by rememberSaveable { + mutableStateOf("") + } + + var isEmailError by rememberSaveable { + mutableStateOf(false) + } + + var password by rememberSaveable { + mutableStateOf("") + } + + var isPasswordVisible by rememberSaveable { + mutableStateOf(false) + } + + var isPasswordError by rememberSaveable { + mutableStateOf>(emptyList()) + } + + // we can't use keyboard controller directly, because EditText is in different composable + // so we have to update the state of this variable and after recomposition, soft keyboard + // will be hidden directly inside the corresponding composable + var shouldHideSoftKeyboard by remember { + mutableStateOf(false) + } + + LaunchedEffect(key1 = credentialValidationResult) { + isEmailError = when (credentialValidationResult) { + is CredentialValidationResult.Invalid -> { + credentialValidationResult.isEmailValid != true + } + + else -> false + } + } + + LaunchedEffect(key1 = password) { + when (credentialValidationResult) { + is CredentialValidationResult.Invalid -> { + isPasswordError = + when (val passwordError = credentialValidationResult.passwordError) { + is PasswordValidationResult.Invalid -> { + passwordError.cause + } + + else -> emptyList() + } + } + + else -> { + isPasswordError = emptyList() + } + } + } + + // hide keyboard is fire and forget event, so it's state has to be cleared after each change + LaunchedEffect(shouldHideSoftKeyboard) { + if (shouldHideSoftKeyboard) shouldHideSoftKeyboard = false + } + + Dialog( + title = stringResource(R.string.reauthorization), + onDismiss = { onEvent(AccountSettingsUiEvent.OnShowReauthorizationDialog(false)) }) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(20.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + EmailField( + emailValue = email, + isEmailError = isEmailError, + shouldHideSoftKeyboard = shouldHideSoftKeyboard, + onEmailValueChanged = { + email = it + onEvent( + AccountSettingsUiEvent.OnDialogCredentialChanged( + Credentials.Sensitive.SignInCredentials( + email, + password + ) + ) + ) + } + ) + + PasswordField( + password = password, + passwordErrors = isPasswordError, + isPasswordEnabled = !isEmailError, + isPasswordVisible = isPasswordVisible, + onPasswordVisibilityClick = { isPasswordVisible = it }, + onPasswordValueChange = { password = it }, + onCredentialChanged = { + onEvent( + AccountSettingsUiEvent.OnDialogCredentialChanged( + Credentials.Sensitive.SignInCredentials( + email, + password + ) + ) + ) + } + ) + + OutlinedButton(onClick = { + if (email.isBlank() || password.isBlank()) { + if (email.isBlank()) isEmailError = true + if (password.isBlank()) isPasswordError = + listOf(PasswordErrorType.PASSWORD_EMPTY) + return@OutlinedButton + } + + shouldHideSoftKeyboard = true + + onEvent( + AccountSettingsUiEvent.OnReauthorizationDialogDoneClick( + Credentials.Sensitive.SignInCredentials(email, password) + ) + ) + }) { + Text(text = stringResource(id = R.string.done)) + } + } + } +} + +@Composable +@Preview +fun AccountSettingsScreenPreview() { + AccountSettingsScreenContent( + isExpandedScreen = false, + state = AccountSettingsState(), + onEvent = {} + ) +} + @Composable @Preview(showBackground = true) fun CredentialsEmailDialogPreview() { - CredentialsDialog( + StatefulCredentialsDialog( credentialValidationResult = null, credentialType = CredentialType.Email, onEvent = {} @@ -966,7 +1065,7 @@ fun CredentialsEmailDialogPreview() { @Composable @Preview(showBackground = true) fun CredentialsPasswordDialogPreview() { - CredentialsDialog( + StatefulCredentialsDialog( credentialValidationResult = null, credentialType = CredentialType.Password, onEvent = {} @@ -976,7 +1075,7 @@ fun CredentialsPasswordDialogPreview() { @Composable @Preview(showBackground = true) fun CredentialsUsernameDialogPreview() { - CredentialsDialog( + StatefulCredentialsDialog( credentialValidationResult = null, credentialType = CredentialType.Username, onEvent = {} @@ -986,9 +1085,8 @@ fun CredentialsUsernameDialogPreview() { @Composable @Preview(showBackground = true) fun CredentialsReauthorizationDialogPreview() { - CredentialsDialog( + StatefulReauthorizationDialog( credentialValidationResult = null, - credentialType = CredentialType.Reauthorization, onEvent = {} ) } @@ -996,7 +1094,7 @@ fun CredentialsReauthorizationDialogPreview() { @Composable @Preview(showBackground = true) fun CredentialsAccountLinkDialogPreview() { - CredentialsDialog( + StatefulCredentialsDialog( credentialValidationResult = null, credentialType = CredentialType.AccountLink, onEvent = {} diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/AddScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/AddScreen.kt index 14d3f9b..6b5c509 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/AddScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/AddScreen.kt @@ -25,14 +25,13 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.bytecause.lenslex.R -import com.bytecause.lenslex.navigation.Screen import com.bytecause.lenslex.ui.components.LanguageDialog import com.bytecause.lenslex.ui.components.LanguagePreferences import com.bytecause.lenslex.ui.components.NetworkUnavailableDialog import com.bytecause.lenslex.ui.components.TopAppBar import com.bytecause.lenslex.ui.events.AddUiEffect import com.bytecause.lenslex.ui.events.AddUiEvent -import com.bytecause.lenslex.ui.screens.uistate.AddState +import com.bytecause.lenslex.ui.screens.model.AddState import com.bytecause.lenslex.ui.screens.viewmodel.AddViewModel import com.bytecause.lenslex.util.Util.readJsonAsMapFromAssets import com.ehsanmsz.mszprogressindicator.progressindicator.BallGridPulseProgressIndicator diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/ExtractedTextScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/ExtractedTextScreen.kt index 00296a5..81eef99 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/ExtractedTextScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/ExtractedTextScreen.kt @@ -77,12 +77,11 @@ import com.bytecause.lenslex.ui.components.LanguageDialog import com.bytecause.lenslex.ui.components.LanguagePreferences import com.bytecause.lenslex.ui.components.NetworkUnavailableDialog import com.bytecause.lenslex.ui.components.TopAppBar -import com.bytecause.lenslex.ui.events.AddUiEvent import com.bytecause.lenslex.ui.events.ExtractedTextUiEffect import com.bytecause.lenslex.ui.events.ExtractedTextUiEvent import com.bytecause.lenslex.ui.mappers.textListToWordList import com.bytecause.lenslex.ui.models.Word -import com.bytecause.lenslex.ui.screens.uistate.RecognizedTextState +import com.bytecause.lenslex.ui.screens.model.RecognizedTextState import com.bytecause.lenslex.ui.screens.viewmodel.ExtractedTextViewModel import com.bytecause.lenslex.util.introShowcaseBackgroundAlpha import com.bytecause.lenslex.util.then @@ -261,22 +260,9 @@ private fun ExtractedTextScreenContent( } if (state.showLanguageInferenceErrorDialog) { - Dialog( - title = stringResource(id = R.string.language_cannot_be_inferred_title), - onDismiss = { onEvent(ExtractedTextUiEvent.OnDismissLanguageInferenceErrorDialog) }) { - Icon( - painter = painterResource(id = R.drawable.failed), - contentDescription = null, - tint = Color.Unspecified, - modifier = Modifier.size(64.dp) - ) - Text( - text = stringResource(id = R.string.language_cannot_be_inferred_message), - textAlign = TextAlign.Center, - style = MaterialTheme.typography.bodyLarge, - fontStyle = FontStyle.Italic - ) - } + LanguageSelectionRequiredDialog( + onDismiss = { onEvent(ExtractedTextUiEvent.OnDismissLanguageInferenceErrorDialog) } + ) } if (state.isLoading) { @@ -436,22 +422,9 @@ private fun ExtractedTextScreenContent( } if (state.showLanguageInferenceErrorDialog) { - Dialog( - title = stringResource(id = R.string.language_cannot_be_inferred_title), - onDismiss = { onEvent(ExtractedTextUiEvent.OnDismissLanguageInferenceErrorDialog) }) { - Icon( - painter = painterResource(id = R.drawable.failed), - contentDescription = null, - tint = Color.Unspecified, - modifier = Modifier.size(64.dp) - ) - Text( - text = stringResource(id = R.string.language_cannot_be_inferred_message), - textAlign = TextAlign.Center, - style = MaterialTheme.typography.bodyLarge, - fontStyle = FontStyle.Italic - ) - } + LanguageSelectionRequiredDialog( + onDismiss = { onEvent(ExtractedTextUiEvent.OnDismissLanguageInferenceErrorDialog) } + ) } if (state.isLoading) { @@ -666,6 +639,35 @@ fun ExtractedTextScreen( ) } +@Composable +fun LanguageSelectionRequiredDialog(onDismiss: () -> Unit) { + Dialog( + title = stringResource(id = R.string.language_cannot_be_inferred_title), + onDismiss = onDismiss, + ) { + Column(modifier = Modifier.padding(10.dp)) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.failed), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.size(80.dp) + ) + Text( + text = stringResource(id = R.string.language_cannot_be_inferred_message), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyLarge, + fontStyle = FontStyle.Italic, + color = MaterialTheme.colorScheme.onPrimaryContainer + ) + } + } + } +} + @OptIn(ExperimentalFoundationApi::class) @Composable private fun WordDisplay( @@ -905,4 +907,10 @@ private fun ExtractedTextScreenPreview() { isExpandedScreen = false, onEvent = {} ) +} + +@Composable +@Preview +private fun LanguageSelectionRequiredDialogPreview() { + LanguageSelectionRequiredDialog(onDismiss = {}) } \ No newline at end of file diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/HomeScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/HomeScreen.kt index 175b804..850502c 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/HomeScreen.kt @@ -82,7 +82,7 @@ import com.bytecause.lenslex.ui.components.TopAppBar import com.bytecause.lenslex.ui.components.launchPermissionRationaleDialog import com.bytecause.lenslex.ui.events.HomeUiEffect import com.bytecause.lenslex.ui.events.HomeUiEvent -import com.bytecause.lenslex.ui.screens.uistate.HomeState +import com.bytecause.lenslex.ui.screens.model.HomeState import com.bytecause.lenslex.ui.screens.viewmodel.HomeViewModel import com.bytecause.lenslex.util.PulsingAnimation import com.bytecause.lenslex.util.introShowcaseBackgroundAlpha @@ -203,6 +203,8 @@ fun HomeScreenContent( ?: R.drawable.default_account_image, contentDescription = stringResource(id = R.string.app_settings), contentScale = ContentScale.Crop, + colorFilter = if (state.profilePictureUrl != "null") null + else ColorFilter.tint(MaterialTheme.colorScheme.onPrimary), modifier = Modifier .padding(end = 10.dp) .size(42.dp) diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/LoginScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/LoginScreen.kt index 2b378a7..165d209 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/LoginScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/LoginScreen.kt @@ -44,7 +44,7 @@ import com.bytecause.lenslex.ui.events.LoginUiEffect import com.bytecause.lenslex.ui.events.LoginUiEvent import com.bytecause.lenslex.ui.interfaces.Credentials import com.bytecause.lenslex.ui.models.SignInResult -import com.bytecause.lenslex.ui.screens.uistate.LoginState +import com.bytecause.lenslex.ui.screens.model.LoginState import com.bytecause.lenslex.ui.screens.viewmodel.LoginViewModel import com.bytecause.lenslex.util.TestTags import com.bytecause.lenslex.util.Util.withOrientationLocked diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/ModifiedImagePreviewScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/ModifiedImagePreviewScreen.kt index 6e43aea..1428da8 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/ModifiedImagePreviewScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/ModifiedImagePreviewScreen.kt @@ -34,7 +34,7 @@ import com.bytecause.lenslex.navigation.Screen import com.bytecause.lenslex.ui.components.TopAppBar import com.bytecause.lenslex.ui.events.ModifiedImagePreviewUiEffect import com.bytecause.lenslex.ui.events.ModifiedImagePreviewUiEvent -import com.bytecause.lenslex.ui.screens.uistate.ModifiedImagePreviewState +import com.bytecause.lenslex.ui.screens.model.ModifiedImagePreviewState import com.bytecause.lenslex.ui.screens.viewmodel.ModifiedImagePreviewViewModel import com.canhub.cropper.CropImageContract import com.canhub.cropper.CropImageContractOptions diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/SendEmailResetScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/SendEmailResetScreen.kt index 067aa3b..343822a 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/SendEmailResetScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/SendEmailResetScreen.kt @@ -37,7 +37,7 @@ import com.bytecause.lenslex.ui.components.UserAuthBackground import com.bytecause.lenslex.ui.components.UserAuthBackgroundExpanded import com.bytecause.lenslex.ui.events.SendEmailResetUiEffect import com.bytecause.lenslex.ui.events.SendEmailResetUiEvent -import com.bytecause.lenslex.ui.screens.uistate.SendEmailResetState +import com.bytecause.lenslex.ui.screens.model.SendEmailResetState import com.bytecause.lenslex.ui.screens.viewmodel.SendEmailResetViewModel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/UpdatePasswordScreen.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/UpdatePasswordScreen.kt index 8cf6e69..866c2ac 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/UpdatePasswordScreen.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/UpdatePasswordScreen.kt @@ -43,7 +43,7 @@ import com.bytecause.lenslex.ui.components.UserAuthBackground import com.bytecause.lenslex.ui.components.UserAuthBackgroundExpanded import com.bytecause.lenslex.ui.events.UpdatePasswordUiEffect import com.bytecause.lenslex.ui.events.UpdatePasswordUiEvent -import com.bytecause.lenslex.ui.screens.uistate.UpdatePasswordState +import com.bytecause.lenslex.ui.screens.model.UpdatePasswordState import com.bytecause.lenslex.ui.screens.viewmodel.UpdatePasswordViewModel import com.bytecause.lenslex.util.CredentialValidationResult import com.bytecause.lenslex.util.PasswordValidationResult diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AccountSettingsState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/AccountSettingsState.kt similarity index 83% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AccountSettingsState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/AccountSettingsState.kt index 3406ae4..04ac3d6 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AccountSettingsState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/AccountSettingsState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable @@ -13,6 +13,8 @@ data class AccountSettingsState( val linkedProviders: List = emptyList(), val credentialValidationResult: CredentialValidationResult? = null, val showCredentialUpdateDialog: CredentialType? = null, + val showReauthorizationDialog: Boolean = false, val showConfirmationDialog: Boolean = false, + val shouldReauthenticate: Boolean = false, val snackbarHostState: SnackbarHostState = SnackbarHostState() ) diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AccountState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/AccountState.kt similarity index 89% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AccountState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/AccountState.kt index 1a9a6f3..ee30c6b 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AccountState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/AccountState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.runtime.Immutable import com.bytecause.lenslex.domain.models.UserData diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AddState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/AddState.kt similarity index 94% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AddState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/AddState.kt index bef593a..49ca4de 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/AddState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/AddState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/HomeState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/HomeState.kt similarity index 96% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/HomeState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/HomeState.kt index b25c071..c8a3ae3 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/HomeState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/HomeState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.LazyGridState diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/LoginState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/LoginState.kt similarity index 94% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/LoginState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/LoginState.kt index 1dcbce0..05a2e56 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/LoginState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/LoginState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/ModifiedImagePreviewState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/ModifiedImagePreviewState.kt similarity index 87% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/ModifiedImagePreviewState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/ModifiedImagePreviewState.kt index cbd96c6..50e03e0 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/ModifiedImagePreviewState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/ModifiedImagePreviewState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import android.net.Uri import androidx.compose.material3.SnackbarHostState diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/RecognizedTextState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/RecognizedTextState.kt similarity index 95% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/RecognizedTextState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/RecognizedTextState.kt index 0abb30c..1b47ef7 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/RecognizedTextState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/RecognizedTextState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/SendEmailResetState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/SendEmailResetState.kt similarity index 87% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/SendEmailResetState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/SendEmailResetState.kt index 182f7dc..d92d36e 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/SendEmailResetState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/SendEmailResetState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/UpdatePasswordState.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/UpdatePasswordState.kt similarity index 94% rename from app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/UpdatePasswordState.kt rename to app/src/main/java/com/bytecause/lenslex/ui/screens/model/UpdatePasswordState.kt index 647a197..33f58c9 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/uistate/UpdatePasswordState.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/model/UpdatePasswordState.kt @@ -1,4 +1,4 @@ -package com.bytecause.lenslex.ui.screens.uistate +package com.bytecause.lenslex.ui.screens.model import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountSettingsViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountSettingsViewModel.kt index 1f6ef64..408af51 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountSettingsViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountSettingsViewModel.kt @@ -11,7 +11,7 @@ import com.bytecause.lenslex.ui.interfaces.CredentialType import com.bytecause.lenslex.ui.interfaces.Credentials import com.bytecause.lenslex.ui.interfaces.Provider import com.bytecause.lenslex.ui.screens.AccountSettingsMessage -import com.bytecause.lenslex.ui.screens.uistate.AccountSettingsState +import com.bytecause.lenslex.ui.screens.model.AccountSettingsState import com.bytecause.lenslex.util.CredentialValidationResult import com.bytecause.lenslex.util.ValidationUtil import com.google.firebase.auth.AuthCredential @@ -57,7 +57,7 @@ class AccountSettingsViewModel( fun uiEventHandler(event: AccountSettingsUiEvent) { when (event) { AccountSettingsUiEvent.OnDeleteAccountButtonClick -> onDeleteAccountClick() - AccountSettingsUiEvent.OnConfirmConfirmationDialog -> onConfirmConfirmationDialog() + AccountSettingsUiEvent.OnConfirmDeleteConfirmationDialog -> onConfirmDeleteConfirmationDialog() AccountSettingsUiEvent.OnDismissConfirmationDialog -> onDismissConfirmationDialog() AccountSettingsUiEvent.OnLaunchReauthenticationGoogleIntent -> sendEffect( AccountSettingsUiEffect.ReauthenticateWithGoogleProvider @@ -73,7 +73,7 @@ class AccountSettingsViewModel( is AccountSettingsUiEvent.OnReauthenticateWithGoogle -> reauthenticateWithGoogle(event.value) is AccountSettingsUiEvent.OnShowCredentialDialog -> onShowCredentialDialog(event.value) is AccountSettingsUiEvent.OnLinkButtonClick -> onLinkButtonClick(event.value) - is AccountSettingsUiEvent.OnEnteredCredential -> onEnteredCredential(event.value) + is AccountSettingsUiEvent.OnEnteredCredential -> onEnteredCredential(event.credentials) is AccountSettingsUiEvent.OnCredentialsDialogDismiss -> onCredentialsDialogDismiss() is AccountSettingsUiEvent.OnDialogCredentialChanged -> onDialogCredentialChanged( @@ -81,6 +81,13 @@ class AccountSettingsViewModel( ) is AccountSettingsUiEvent.OnLinkGoogleProvider -> linkGoogleProvider(event.value) + is AccountSettingsUiEvent.OnShowReauthorizationDialog -> onShowReauthorizationDialog( + event.boolean + ) + + is AccountSettingsUiEvent.OnReauthorizationDialogDoneClick -> onReauthorizationDialogDoneClick( + event.credentials + ) } } @@ -95,13 +102,19 @@ class AccountSettingsViewModel( _effect.trySend(effect) } + private fun onShowReauthorizationDialog(boolean: Boolean) { + _uiState.update { + it.copy(showReauthorizationDialog = boolean) + } + } + private fun onDeleteAccountClick() { _uiState.update { it.copy(showConfirmationDialog = true) } } - private fun onConfirmConfirmationDialog() { + private fun onConfirmDeleteConfirmationDialog() { deleteAccount() _uiState.update { it.copy(showConfirmationDialog = false) @@ -125,32 +138,39 @@ class AccountSettingsViewModel( private fun onLinkButtonClick(provider: Provider) { when (provider) { Provider.Email -> { - if (_uiState.value.linkedProviders.contains(provider)) unlinkProvider( + if (uiState.value.linkedProviders.contains(provider)) unlinkProvider( provider ) else _uiState.update { it.copy(showCredentialUpdateDialog = CredentialType.AccountLink) } } Provider.Google -> - if (_uiState.value.linkedProviders.contains(provider)) unlinkProvider( + if (uiState.value.linkedProviders.contains(provider)) unlinkProvider( provider ) else sendEffect(AccountSettingsUiEffect.LinkGoogleProvider) } } + private fun onReauthorizationDialogDoneClick(credentials: Credentials) { + val validationResult = uiState.value.credentialValidationResult + + if (validationResult is CredentialValidationResult.Valid) { + val signInCredentials = + credentials as Credentials.Sensitive.SignInCredentials + reauthenticateUsingEmailAndPassword(signInCredentials) + } + } + private fun onEnteredCredential(credentials: Credentials) { - val validationResult = _uiState.value.credentialValidationResult + if (uiState.value.shouldReauthenticate) { + onShowReauthorizationDialog(true) + return + } - when (_uiState.value.showCredentialUpdateDialog) { - is CredentialType.Reauthorization -> { - if (validationResult is CredentialValidationResult.Valid) { - val signInCredentials = - credentials as Credentials.Sensitive.SignInCredentials - reauthenticateUsingEmailAndPassword(signInCredentials) - } - } + val validationResult = uiState.value.credentialValidationResult + when (_uiState.value.showCredentialUpdateDialog) { is CredentialType.AccountLink -> { if (validationResult is CredentialValidationResult.Valid) { linkEmailProvider( @@ -201,15 +221,15 @@ class AccountSettingsViewModel( authCredential: AuthCredential ) { viewModelScope.launch { - auth.reauthenticateWithGoogle(authCredential).firstOrNull()?.let { result -> - result.onFailure { exception -> + auth.reauthenticateWithGoogle(authCredential).firstOrNull() + ?.onSuccess { _uiState.update { it.copy(shouldReauthenticate = false) } } + ?.onFailure { exception -> sendEffect( AccountSettingsUiEffect.AccountActionResult( AccountActionResult.Failure.Error(exception) ) ) } - } } } @@ -274,6 +294,7 @@ class AccountSettingsViewModel( AccountActionResult.Failure.ReauthorizationRequired ) ) + _uiState.update { it.copy(shouldReauthenticate = true) } return@let } @@ -310,6 +331,7 @@ class AccountSettingsViewModel( AccountActionResult.Failure.ReauthorizationRequired ) ) + _uiState.update { it.copy(shouldReauthenticate = true) } return@let } @@ -371,15 +393,19 @@ class AccountSettingsViewModel( auth.reauthenticateWithEmailAndPassword( email = credentials.email, password = credentials.password - ).firstOrNull()?.let { result -> - result.onFailure { exception -> + ).firstOrNull() + ?.onSuccess { + // hide dialog + onShowReauthorizationDialog(false) + _uiState.update { it.copy(shouldReauthenticate = false) } + } + ?.onFailure { exception -> sendEffect( AccountSettingsUiEffect.AccountActionResult( AccountActionResult.Failure.Error(exception) ) ) } - } } } @@ -405,6 +431,7 @@ class AccountSettingsViewModel( AccountActionResult.Failure.ReauthorizationRequired ) ) + _uiState.update { it.copy(shouldReauthenticate = true) } return@let } @@ -436,6 +463,7 @@ class AccountSettingsViewModel( AccountActionResult.Failure.ReauthorizationRequired ) ) + _uiState.update { it.copy(shouldReauthenticate = true) } return@let } @@ -469,6 +497,7 @@ class AccountSettingsViewModel( AccountActionResult.Failure.ReauthorizationRequired ) ) + _uiState.update { it.copy(shouldReauthenticate = true) } return@let } diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountViewModel.kt index 861ab01..e65dfad 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AccountViewModel.kt @@ -9,7 +9,7 @@ import com.bytecause.lenslex.data.repository.abstraction.UserRepository import com.bytecause.lenslex.domain.models.UserData import com.bytecause.lenslex.ui.events.AccountUiEffect import com.bytecause.lenslex.ui.events.AccountUiEvent -import com.bytecause.lenslex.ui.screens.uistate.AccountState +import com.bytecause.lenslex.ui.screens.model.AccountState import com.google.firebase.auth.FirebaseAuth import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AddViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AddViewModel.kt index 1d3b92f..1c00118 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AddViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/AddViewModel.kt @@ -12,7 +12,7 @@ import com.bytecause.lenslex.domain.models.Words import com.bytecause.lenslex.ui.events.AddUiEffect import com.bytecause.lenslex.ui.events.AddUiEvent import com.bytecause.lenslex.ui.interfaces.TranslationOption -import com.bytecause.lenslex.ui.screens.uistate.AddState +import com.bytecause.lenslex.ui.screens.model.AddState import com.bytecause.lenslex.util.NetworkUtil import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ExtractedTextViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ExtractedTextViewModel.kt index c340569..50a5fa6 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ExtractedTextViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ExtractedTextViewModel.kt @@ -15,7 +15,7 @@ import com.bytecause.lenslex.ui.events.ExtractedTextUiEffect import com.bytecause.lenslex.ui.events.ExtractedTextUiEvent import com.bytecause.lenslex.ui.interfaces.TranslationOption import com.bytecause.lenslex.ui.models.Word -import com.bytecause.lenslex.ui.screens.uistate.RecognizedTextState +import com.bytecause.lenslex.ui.screens.model.RecognizedTextState import com.bytecause.lenslex.util.NetworkUtil import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/HomeViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/HomeViewModel.kt index abeefed..92edbeb 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/HomeViewModel.kt @@ -14,7 +14,7 @@ import com.bytecause.lenslex.domain.models.Words import com.bytecause.lenslex.ui.events.HomeUiEffect import com.bytecause.lenslex.ui.events.HomeUiEvent import com.bytecause.lenslex.ui.interfaces.TranslationOption -import com.bytecause.lenslex.ui.screens.uistate.HomeState +import com.bytecause.lenslex.ui.screens.model.HomeState import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/LoginViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/LoginViewModel.kt index d004efa..1f0921a 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/LoginViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/LoginViewModel.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope import com.bytecause.lenslex.data.remote.auth.abstraction.Authenticator import com.bytecause.lenslex.ui.events.LoginUiEffect import com.bytecause.lenslex.ui.models.SignInResult -import com.bytecause.lenslex.ui.screens.uistate.LoginState +import com.bytecause.lenslex.ui.screens.model.LoginState import com.bytecause.lenslex.ui.events.LoginUiEvent import com.bytecause.lenslex.ui.interfaces.Credentials import com.bytecause.lenslex.util.CredentialValidationResult diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ModifiedImagePreviewViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ModifiedImagePreviewViewModel.kt index 64c66c3..4883672 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ModifiedImagePreviewViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/ModifiedImagePreviewViewModel.kt @@ -6,7 +6,7 @@ import androidx.lifecycle.viewModelScope import com.bytecause.lenslex.data.repository.abstraction.TextRecognitionRepository import com.bytecause.lenslex.ui.events.ModifiedImagePreviewUiEffect import com.bytecause.lenslex.ui.events.ModifiedImagePreviewUiEvent -import com.bytecause.lenslex.ui.screens.uistate.ModifiedImagePreviewState +import com.bytecause.lenslex.ui.screens.model.ModifiedImagePreviewState import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/SendEmailResetViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/SendEmailResetViewModel.kt index 7bcac30..30de8c2 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/SendEmailResetViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/SendEmailResetViewModel.kt @@ -6,7 +6,7 @@ import com.bytecause.lenslex.data.remote.auth.FirebaseAuthClient import com.bytecause.lenslex.ui.events.SendEmailResetUiEffect import com.bytecause.lenslex.ui.events.SendEmailResetUiEvent import com.bytecause.lenslex.ui.interfaces.Credentials -import com.bytecause.lenslex.ui.screens.uistate.SendEmailResetState +import com.bytecause.lenslex.ui.screens.model.SendEmailResetState import com.bytecause.lenslex.util.CredentialValidationResult import com.bytecause.lenslex.util.ValidationUtil import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/UpdatePasswordViewModel.kt b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/UpdatePasswordViewModel.kt index f70360a..cd3e6f8 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/UpdatePasswordViewModel.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/screens/viewmodel/UpdatePasswordViewModel.kt @@ -7,7 +7,7 @@ import com.bytecause.lenslex.data.repository.abstraction.VerifyOobRepository import com.bytecause.lenslex.ui.events.UpdatePasswordUiEffect import com.bytecause.lenslex.ui.events.UpdatePasswordUiEvent import com.bytecause.lenslex.ui.interfaces.Credentials -import com.bytecause.lenslex.ui.screens.uistate.UpdatePasswordState +import com.bytecause.lenslex.ui.screens.model.UpdatePasswordState import com.bytecause.lenslex.util.ApiResult import com.bytecause.lenslex.util.CredentialValidationResult import com.bytecause.lenslex.util.NetworkUtil diff --git a/app/src/main/java/com/bytecause/lenslex/ui/theme/Theme.kt b/app/src/main/java/com/bytecause/lenslex/ui/theme/Theme.kt index 4a92a12..2db0320 100644 --- a/app/src/main/java/com/bytecause/lenslex/ui/theme/Theme.kt +++ b/app/src/main/java/com/bytecause/lenslex/ui/theme/Theme.kt @@ -99,7 +99,6 @@ fun AppTheme( if (!view.isInEditMode) { SideEffect { val window = (view.context as Activity).window - window.statusBarColor = colorScheme.primary.toArgb() WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme } } diff --git a/app/src/main/java/com/bytecause/lenslex/util/Util.kt b/app/src/main/java/com/bytecause/lenslex/util/Util.kt index 3a9e9d9..0450c12 100644 --- a/app/src/main/java/com/bytecause/lenslex/util/Util.kt +++ b/app/src/main/java/com/bytecause/lenslex/util/Util.kt @@ -40,7 +40,7 @@ object Util { } } - private fun forceOrientation(context: Context, activityInfo: Int) { + fun forceOrientation(context: Context, activityInfo: Int) { (context as? Activity)?.requestedOrientation = activityInfo } @@ -48,7 +48,7 @@ object Util { // from being cancelled when the orientation is changed. // I couldn't think of a better way to solve this problem, because viewModel // should not hold an instance of the activity context. - suspend fun withOrientationLocked(context: Context, block: suspend () -> Unit) { + inline fun withOrientationLocked(context: Context, block: () -> Unit) { try { forceOrientation(context, ActivityInfo.SCREEN_ORIENTATION_LOCKED) block() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 907f14b..75b2683 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,21 +5,21 @@ retrofit = "2.11.0" coil = "2.6.0" blurry = "4.0.1" core-ktx = "1.13.1" -navigation-compose = "2.8.0-beta06" +navigation-compose = "2.8.1" kotlinx-serialization-json = "1.7.0" -mlkit-text-recognition = "16.0.0" -mlkit-translate = "17.0.2" -mlkit-language-id = "17.0.5" +mlkit-text-recognition = "16.0.1" +mlkit-translate = "17.0.3" +mlkit-language-id = "17.0.6" credentials = "1.2.2" credentials-play-services-auth = "1.2.2" indentity-googleid = "1.1.1" preferences-datastore = "1.1.1" -firebase-bom = "33.1.2" +firebase-bom = "33.3.0" play-services-auth = "21.2.0" -activity-compose = "1.9.1" -lifecycle-runtime-compose = "2.8.4" +activity-compose = "1.9.2" +lifecycle-runtime-compose = "2.8.6" constraintlayout-compose = "1.0.1" -compose-bom = "2024.06.00" +compose-bom = "2024.09.02" image-cropper = "4.5.0" accompanist = "0.34.0" koin = "3.5.3" @@ -31,12 +31,12 @@ ext-junit = "1.2.1" espresso = "3.6.1" # plugins -android-application = "8.5.1" -kotlin-android = "2.0.0" -ksp = "2.0.0-1.0.22" +android-application = "8.6.1" +kotlin-android = "2.0.20" +ksp = "2.0.20-1.0.24" google-services = "4.4.2" kotlin-serialization = "2.0.0" -kotlin-compose = "2.0.0" +kotlin-compose = "2.0.20" [libraries]