diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/co.touchlab-sqliter-driver-1.3.1-appleMain-cinterop/co.touchlab_sqliter-driver-cinterop-sqlite3-jZj4Kg.klib b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/co.touchlab-sqliter-driver-1.3.1-appleMain-cinterop/co.touchlab_sqliter-driver-cinterop-sqlite3-jZj4Kg.klib index 92b485ff0..b53519622 100644 Binary files a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/co.touchlab-sqliter-driver-1.3.1-appleMain-cinterop/co.touchlab_sqliter-driver-cinterop-sqlite3-jZj4Kg.klib and b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/co.touchlab-sqliter-driver-1.3.1-appleMain-cinterop/co.touchlab_sqliter-driver-cinterop-sqlite3-jZj4Kg.klib differ diff --git a/composeApp/src/commonMain/composeResources/values-es/strings.xml b/composeApp/src/commonMain/composeResources/values-es/strings.xml index 34f642587..6cfe1635f 100644 --- a/composeApp/src/commonMain/composeResources/values-es/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-es/strings.xml @@ -296,4 +296,7 @@ Eliminar Scope Habla con MoreStuff IA. + + Reenviar código + Podrás reenviar el código en %s segundos \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 116100390..6a92700a7 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -311,4 +311,8 @@ The email address is invalid or does not exist. An error occurred. Please try again. Chat with MoreStuff AI. + + + Resend code + You can resend the code in %s seconds diff --git a/composeApp/src/commonMain/kotlin/io/middlepoint/morestuff/shared/ui/screen/onboarding/OnBoardingSignInEmailScreen.kt b/composeApp/src/commonMain/kotlin/io/middlepoint/morestuff/shared/ui/screen/onboarding/OnBoardingSignInEmailScreen.kt index bc706db87..8fa830799 100644 --- a/composeApp/src/commonMain/kotlin/io/middlepoint/morestuff/shared/ui/screen/onboarding/OnBoardingSignInEmailScreen.kt +++ b/composeApp/src/commonMain/kotlin/io/middlepoint/morestuff/shared/ui/screen/onboarding/OnBoardingSignInEmailScreen.kt @@ -1,24 +1,31 @@ package io.middlepoint.morestuff.shared.ui.screen.onboarding +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Email import androidx.compose.material.icons.filled.VpnKey import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text +import androidx.compose.material3.TextButton 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 @@ -26,6 +33,7 @@ import androidx.compose.runtime.rememberCoroutineScope 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.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue @@ -35,6 +43,8 @@ import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.auth.OtpType import io.github.jan.supabase.auth.auth import io.github.jan.supabase.auth.providers.builtin.OTP +import io.middlepoint.morestuff.shared.ui.theme.onBoardingBrush +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import morestuff.composeapp.generated.resources.Res import morestuff.composeapp.generated.resources.`continue` @@ -43,8 +53,12 @@ import morestuff.composeapp.generated.resources.enter_code_sent_to_email import morestuff.composeapp.generated.resources.enter_your_email import morestuff.composeapp.generated.resources.error_generic import morestuff.composeapp.generated.resources.error_invalid_email +import morestuff.composeapp.generated.resources.ms_sign_in +import morestuff.composeapp.generated.resources.resend_code +import morestuff.composeapp.generated.resources.seconds_to_resend import morestuff.composeapp.generated.resources.six_digit_code_label import morestuff.composeapp.generated.resources.verify +import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import org.koin.compose.koinInject import org.koin.core.parameter.parametersOf @@ -65,6 +79,11 @@ fun SignInEmailScreen( var otpCode by remember { mutableStateOf("") } var isLoading by remember { mutableStateOf(false) } + var canResendOtp by remember { mutableStateOf(false) } + var secondsRemaining by remember { mutableStateOf(60) } + var hasResentOtp by remember { mutableStateOf(false) } + + val sendOtpToEmail: (String, () -> Unit, (String) -> Unit) -> Unit = { email, onSent, onError -> scope.launch { isLoading = true @@ -73,6 +92,8 @@ fun SignInEmailScreen( this.email = email } onSent() + secondsRemaining = 60 + canResendOtp = false } catch (e: Exception) { logger.e(e) { "Error sending otp to email" } emailError = when { @@ -109,23 +130,48 @@ fun SignInEmailScreen( } } + LaunchedEffect(isEmailSent) { + if (isEmailSent && !hasResentOtp) { + secondsRemaining = 60 + canResendOtp = false + while (secondsRemaining > 0) { + delay(1000) + secondsRemaining-- + } + canResendOtp = true + } + } + + val background = remember { onBoardingBrush } + Box( modifier = Modifier .fillMaxSize() + .background(background) .imePadding() ) { Column( modifier = Modifier .fillMaxSize() - .padding(32.dp), + .windowInsetsPadding(WindowInsets.safeDrawing) + .padding(horizontal = 32.dp) + .padding(bottom = 150.dp), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.SpaceBetween ) { + Spacer(modifier = Modifier.weight(0.4f)) + Icon( + painter = painterResource(Res.drawable.ms_sign_in), + tint = Color.Unspecified, + contentDescription = "signIn", + ) + //Spacer(modifier = Modifier.height(32.dp)) + Spacer(modifier = Modifier.weight(0.6f)) Text( text = if (!isEmailSent) stringResource(Res.string.enter_your_email) else stringResource(Res.string.enter_code_sent_to_email, email.text), style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurface + color = Color.White ) Spacer(modifier = Modifier.height(16.dp)) @@ -137,12 +183,12 @@ fun SignInEmailScreen( emailError = null } }, - label = { Text(stringResource(Res.string.email_label)) }, + label = { Text(text = stringResource(Res.string.email_label), color = Color.White) }, leadingIcon = { Icon( imageVector = Icons.Default.Email, contentDescription = null, - tint = MaterialTheme.colorScheme.primary + tint = Color.White ) }, singleLine = true, @@ -170,12 +216,17 @@ fun SignInEmailScreen( OutlinedTextField( value = otpCode, onValueChange = { if (it.length <= 6) otpCode = it }, - label = { Text(stringResource(Res.string.six_digit_code_label)) }, + label = { + Text( + text = stringResource(Res.string.six_digit_code_label), + color = Color.White + ) + }, leadingIcon = { Icon( imageVector = Icons.Default.VpnKey, contentDescription = null, - tint = MaterialTheme.colorScheme.primary + tint = Color.White ) }, singleLine = true, @@ -186,6 +237,45 @@ fun SignInEmailScreen( ), modifier = Modifier.fillMaxWidth() ) + + + Spacer(modifier = Modifier.height(8.dp)) + if (!hasResentOtp) { + if (!canResendOtp) { + Text( + text = stringResource(Res.string.seconds_to_resend).replace( + "%s", + "$secondsRemaining" + ), + color = Color.White.copy(alpha = 0.7f), + style = MaterialTheme.typography.bodySmall + ) + } else { + TextButton( + onClick = { + sendOtpToEmail( + email.text, + { + hasResentOtp = true + canResendOtp = false + }, + {} + ) + }, + enabled = !isLoading, + colors = ButtonDefaults.outlinedButtonColors( + contentColor = Color.White, + containerColor = Color(0xFF393AC5), + disabledContainerColor = Color(0xFF393AC5).copy(alpha = 0.5f) + ), + ) { + Text( + text = stringResource(Res.string.resend_code), + color = Color.White + ) + } + } + } } Spacer(modifier = Modifier.height(24.dp)) @@ -199,10 +289,15 @@ fun SignInEmailScreen( {} ) }, + colors = ButtonDefaults.outlinedButtonColors( + contentColor = Color.White, + containerColor = Color(0xFF393AC5), + disabledContainerColor = Color(0xFF393AC5).copy(alpha = 0.5f) + ), enabled = isValidEmail(email.text) && !isLoading, modifier = Modifier.fillMaxWidth() ) { - Text(stringResource(Res.string.`continue`)) + Text(text = stringResource(Res.string.`continue`), color = Color.White) } } else { Button( @@ -212,10 +307,15 @@ fun SignInEmailScreen( otpCode ) { } }, + colors = ButtonDefaults.outlinedButtonColors( + contentColor = Color.White, + containerColor = Color(0xFF393AC5), + disabledContainerColor = Color(0xFF393AC5).copy(alpha = 0.5f) + ), enabled = otpCode.length == 6 && !isLoading, modifier = Modifier.fillMaxWidth() ) { - Text(stringResource(Res.string.verify)) + Text(text = stringResource(Res.string.verify), color = Color.White) } } }