From 91826cb06f547a212ed37aa8500abb787ee10078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Fri, 25 Oct 2024 22:43:55 +0900 Subject: [PATCH 01/27] =?UTF-8?q?refactor:=20=EB=B9=84=EB=B2=88=20?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=ED=99=94=EB=A9=B4=EB=93=A4=20=EB=B3=84?= =?UTF-8?q?=EB=8F=84=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt | 1 + .../views/logged_out/{ => reset_password}/FindPasswordPage.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename app/src/main/java/com/wafflestudio/snutt2/views/logged_out/{ => reset_password}/FindPasswordPage.kt (99%) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index 3d3964f92..8d18fd312 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -77,6 +77,7 @@ import com.wafflestudio.snutt2.views.logged_in.table_lectures.LecturesOfTablePag import com.wafflestudio.snutt2.views.logged_in.vacancy_noti.VacancyPage import com.wafflestudio.snutt2.views.logged_in.vacancy_noti.VacancyViewModel import com.wafflestudio.snutt2.views.logged_out.* +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordPage import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/FindPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordPage.kt similarity index 99% rename from app/src/main/java/com/wafflestudio/snutt2/views/logged_out/FindPasswordPage.kt rename to app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordPage.kt index 9695cf71f..84daeef84 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/FindPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordPage.kt @@ -1,4 +1,4 @@ -package com.wafflestudio.snutt2.views.logged_out +package com.wafflestudio.snutt2.views.logged_out.reset_password import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalOnBackPressedDispatcherOwner From 70515b3e4faf969cdd7d306f1799ab0f0a758b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 03:14:17 +0900 Subject: [PATCH 02/27] =?UTF-8?q?feat:=20=EB=B9=84=EB=B2=88=20=EC=9E=AC?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=A9=94=EC=9D=B8=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=96=BC=EA=B0=9C=20=EC=9E=A1=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reset_password/FindPasswordViewModel.kt | 31 +++++++++++++++++ .../reset_password/ResetPasswordPage.kt | 34 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt new file mode 100644 index 000000000..7501cdff4 --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt @@ -0,0 +1,31 @@ +package com.wafflestudio.snutt2.views.logged_out.reset_password + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import com.wafflestudio.snutt2.data.user.UserRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +@HiltViewModel +class FindPasswordViewModel @Inject constructor( + private val userRepository: UserRepository, + private val savedStateHandle: SavedStateHandle, +) : ViewModel() { + + private val _uiState = MutableStateFlow(UIState.CheckId) + val uiState: StateFlow = _uiState + + sealed class UIState { + data object CheckId : UIState() + + data object EnterFullEmail : UIState() + + data object VerifyCode : UIState() + + data object EnterNewPassword : UIState() + } + + fun goToPreviousStep() {} +} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt new file mode 100644 index 000000000..6ee5614cc --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -0,0 +1,34 @@ +package com.wafflestudio.snutt2.views.logged_out.reset_password + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.components.compose.SimpleTopBar +import com.wafflestudio.snutt2.views.LocalNavController +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.CheckId + +@Composable +fun ResetPasswordPage() { + val navController = LocalNavController.current + val viewModel = hiltViewModel() + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + Column(modifier = Modifier.fillMaxSize()) { + SimpleTopBar( + title = stringResource(R.string.find_password_title), + onClickNavigateBack = { + if (uiState is CheckId) { + navController.popBackStack() + } else { + viewModel.goToPreviousStep() + } + }, + ) + } +} From f875bb97ddd53ef65c1a484e763a898508a12119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 03:15:59 +0900 Subject: [PATCH 03/27] =?UTF-8?q?feat:=20Step=201=20-=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94=20=EC=9E=85=EB=A0=A5=20=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wafflestudio/snutt2/ui/Colors.kt | 2 + .../logged_out/reset_password/CheckIdStep.kt | 106 ++++++++++++++++++ .../reset_password/FindPasswordViewModel.kt | 14 ++- .../reset_password/ResetPasswordPage.kt | 26 +++++ 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt b/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt index 503512fb7..5714d216c 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt @@ -58,6 +58,8 @@ object SNUTTColors { val VacancyRed = Color(0xffed6c58) + val EditTextLabel = Color(0xff8a898e) + val Colors.VacancyBlue @Composable get() = if (isLight) Color(0xff446cc2) else Color(0xff7aaaf3) val VacancyBlue @Composable get() = MaterialTheme.colors.VacancyBlue diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt new file mode 100644 index 000000000..59524a6cd --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt @@ -0,0 +1,106 @@ +package com.wafflestudio.snutt2.views.logged_out.reset_password + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.WebViewStyleButton +import com.wafflestudio.snutt2.lib.android.toast +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTypography + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun CheckIdStep( + userId: String, + onSubmit: (String) -> Unit, +) { + val focusManager = LocalFocusManager.current + val context = LocalContext.current + + var idField by remember { mutableStateOf(userId) } + val buttonEnabled by remember { + derivedStateOf { + idField.isNotEmpty() + } + } + + val sendIdAndRequestMaskedEmail = { + if (idField.isEmpty()) { + context.toast(context.getString(R.string.find_password_enter_id_hint)) + } else { + onSubmit(idField) + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 44.dp, horizontal = 20.dp), + ) { + Text( + text = stringResource(R.string.find_password_check_email_content), + style = SNUTTTypography.h2.copy(fontSize = 17.sp), + ) + + Spacer(modifier = Modifier.height(40.dp)) + + Text( + text = stringResource(R.string.sign_in_id_title), + style = SNUTTTypography.body1.copy(color = SNUTTColors.EditTextLabel), + ) + + Spacer(modifier = Modifier.height(12.dp)) + + EditText( + modifier = Modifier.fillMaxWidth(), + value = idField, + onValueChange = { idField = it }, + hint = stringResource(R.string.find_password_enter_id_hint), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus( + FocusDirection.Enter, + ) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + singleLine = true, + ) + + Spacer(modifier = Modifier.height(48.dp)) + + WebViewStyleButton( + modifier = Modifier.fillMaxWidth(), + enabled = buttonEnabled, + onClick = sendIdAndRequestMaskedEmail, + ) { + Text( + text = stringResource(R.string.common_ok), + style = SNUTTTypography.h3.copy(color = SNUTTColors.AllWhite), + ) + } + } +} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt index 7501cdff4..54835ece3 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt @@ -14,11 +14,13 @@ class FindPasswordViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle, ) : ViewModel() { - private val _uiState = MutableStateFlow(UIState.CheckId) + private val _uiState = MutableStateFlow(UIState.CheckId("")) val uiState: StateFlow = _uiState sealed class UIState { - data object CheckId : UIState() + data class CheckId( + val userId: String, + ) : UIState() data object EnterFullEmail : UIState() @@ -27,5 +29,11 @@ class FindPasswordViewModel @Inject constructor( data object EnterNewPassword : UIState() } - fun goToPreviousStep() {} + fun goToPreviousStep() { + when (_uiState.value) { + is UIState.CheckId -> {} + } + } + + suspend fun checkEmailById(userId: String) {} } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 6ee5614cc..ab49af02e 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -4,17 +4,29 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.wafflestudio.snutt2.R import com.wafflestudio.snutt2.components.compose.SimpleTopBar +import com.wafflestudio.snutt2.views.LocalApiOnError +import com.wafflestudio.snutt2.views.LocalApiOnProgress import com.wafflestudio.snutt2.views.LocalNavController +import com.wafflestudio.snutt2.views.launchSuspendApi import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.CheckId +import kotlinx.coroutines.launch +@OptIn(ExperimentalComposeUiApi::class) @Composable fun ResetPasswordPage() { + val scope = rememberCoroutineScope() + val apiOnProgress = LocalApiOnProgress.current + val apiOnError = LocalApiOnError.current + val keyboardManager = LocalSoftwareKeyboardController.current val navController = LocalNavController.current val viewModel = hiltViewModel() val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -30,5 +42,19 @@ fun ResetPasswordPage() { } }, ) + + when (uiState) { + is CheckId -> CheckIdStep( + userId = (uiState as CheckId).userId, + onSubmit = { userId -> + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + viewModel.checkEmailById(userId) + keyboardManager?.hide() + } + } + }, + ) + } } } From fc0f385a113102c3a54aa21d28ffa19b2537115b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 03:17:50 +0900 Subject: [PATCH 04/27] =?UTF-8?q?feat:=20Step=202=20-=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=9E=85=EB=A0=A5=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reset_password/EnterFullEmailStep.kt | 147 ++++++++++++++++++ .../reset_password/FindPasswordViewModel.kt | 18 ++- .../reset_password/ResetPasswordPage.kt | 14 ++ app/src/main/res/values/strings.xml | 7 +- 4 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt new file mode 100644 index 000000000..19036d63a --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt @@ -0,0 +1,147 @@ +package com.wafflestudio.snutt2.views.logged_out.reset_password + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.WebViewStyleButton +import com.wafflestudio.snutt2.components.compose.clicks +import com.wafflestudio.snutt2.lib.android.toast +import com.wafflestudio.snutt2.lib.data.SNUTTStringUtils.isEmailInvalid +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTypography + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun EnterFullEmailStep( + userId: String, + maskedEmail: String, + notMyEmail: () -> Unit, + onSubmitFullEmail: (String) -> Unit, +) { + val focusManager = LocalFocusManager.current + val context = LocalContext.current + + var emailField by remember { mutableStateOf("") } + val buttonEnabled by remember { + derivedStateOf { + !emailField.isEmailInvalid() + } + } + + val sendIdAndRequestMaskedEmail = { + if (emailField.isEmpty()) { + context.toast(context.getString(R.string.find_password_enter_id_hint)) + } else { + onSubmitFullEmail(emailField) + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 44.dp, horizontal = 20.dp), + ) { + Text( + text = stringResource(R.string.find_password_check_email_enter_full_email), + style = SNUTTTypography.h3, + ) + + Spacer(modifier = Modifier.height(40.dp)) + + Text( + text = stringResource(R.string.sign_in_id_title), + style = SNUTTTypography.body1.copy(color = SNUTTColors.EditTextLabel), + ) + + Spacer(modifier = Modifier.height(12.dp)) + + EditText( + modifier = Modifier.fillMaxWidth(), + value = "", + onValueChange = {}, + hint = userId, + enabled = false, + ) + + Spacer(modifier = Modifier.height(40.dp)) + + Text( + text = stringResource(R.string.find_password_check_email_enter_full_email_label), + style = SNUTTTypography.body1.copy(color = SNUTTColors.EditTextLabel), + ) + + Spacer(modifier = Modifier.height(12.dp)) + + EditText( + modifier = Modifier.fillMaxWidth(), + value = emailField, + onValueChange = { emailField = it }, + hint = stringResource(R.string.find_password_check_email_enter_full_email_hint), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus( + FocusDirection.Enter, + ) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + singleLine = true, + ) + + Spacer(modifier = Modifier.height(12.dp)) + + Text( + text = maskedEmail, + style = SNUTTTypography.body1.copy(color = SNUTTColors.EditTextLabel), + ) + + Spacer(modifier = Modifier.height(48.dp)) + + WebViewStyleButton( + modifier = Modifier.fillMaxWidth(), + enabled = buttonEnabled, + onClick = sendIdAndRequestMaskedEmail, + ) { + Text( + text = stringResource(R.string.find_password_check_email_enter_full_email_enter), + style = SNUTTTypography.h3.copy(color = SNUTTColors.VacancyGray), + ) + } + + Spacer(modifier = Modifier.height(20.dp)) + + Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { + Text( + text = stringResource(R.string.find_password_check_email_enter_full_email_not_mine), + style = SNUTTTypography.body1.copy(color = SNUTTColors.VacancyGray), + modifier = Modifier.clicks { + notMyEmail() + }, + ) + } + } +} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt index 54835ece3..03cdcb14d 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt @@ -22,7 +22,10 @@ class FindPasswordViewModel @Inject constructor( val userId: String, ) : UIState() - data object EnterFullEmail : UIState() + data class EnterFullEmail( + val userId: String, + val maskedEmail: String, + ) : UIState() data object VerifyCode : UIState() @@ -32,8 +35,19 @@ class FindPasswordViewModel @Inject constructor( fun goToPreviousStep() { when (_uiState.value) { is UIState.CheckId -> {} + is UIState.EnterFullEmail -> { + val savedUserId = savedStateHandle["userId"] ?: "" + _uiState.value = UIState.CheckId(savedUserId) + } } } - suspend fun checkEmailById(userId: String) {} + suspend fun checkEmailById(userId: String) { + savedStateHandle["userId"] = userId + val maskedEmail = userRepository.checkEmailById(userId) + savedStateHandle["maskedEmail"] = maskedEmail + _uiState.value = UIState.EnterFullEmail(userId, maskedEmail) + } + + suspend fun sendFullEmailAndRequestCode(fullEmail: String) {} } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index ab49af02e..3c6980bd9 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -18,6 +18,7 @@ import com.wafflestudio.snutt2.views.LocalApiOnProgress import com.wafflestudio.snutt2.views.LocalNavController import com.wafflestudio.snutt2.views.launchSuspendApi import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.CheckId +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.EnterFullEmail import kotlinx.coroutines.launch @OptIn(ExperimentalComposeUiApi::class) @@ -55,6 +56,19 @@ fun ResetPasswordPage() { } }, ) + is EnterFullEmail -> EnterFullEmailStep( + userId = (uiState as EnterFullEmail).userId, + maskedEmail = (uiState as EnterFullEmail).maskedEmail, + notMyEmail = viewModel::goToPreviousStep, + onSubmitFullEmail = { fullEmail -> + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + viewModel.sendFullEmailAndRequestCode(fullEmail) + keyboardManager?.hide() + } + } + }, + ) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8712349d..a68a5885d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -285,10 +285,15 @@ \"%s\"로 아이디가 전송되었습니다. 비밀번호 재설정 비밀번호 재설정을 위해\n연동된 아이디가 필요합니다. + 해당 아이디로 연동된 이메일입니다.\n전체 주소를 입력하여 인증코드를 받으세요. + 이메일 + 전체 주소를 입력하세요 + 인증코드 받기 + 나의 이메일 주소가 아닌가요? 이메일 확인 연동된 이메일은 %s입니다. 해당 이메일로 인증 코드를 발송하시겠습니까? 다른 아이디 확인하기 - 아이디를 입력해주세요. + 아이디를 입력하세요. 인증 코드 입력 인증 번호 다시 요청 From bd97c07ecb18c302df346f24fea9d27389739825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 03:26:39 +0900 Subject: [PATCH 05/27] =?UTF-8?q?feat:=20AnimatedContent=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reset_password/ResetPasswordPage.kt | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 3c6980bd9..636127622 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -1,5 +1,6 @@ package com.wafflestudio.snutt2.views.logged_out.reset_password +import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable @@ -44,31 +45,34 @@ fun ResetPasswordPage() { }, ) - when (uiState) { - is CheckId -> CheckIdStep( - userId = (uiState as CheckId).userId, - onSubmit = { userId -> - scope.launch { - launchSuspendApi(apiOnProgress, apiOnError) { - viewModel.checkEmailById(userId) - keyboardManager?.hide() + AnimatedContent(targetState = uiState, label = "") { state -> + when (state) { + is CheckId -> CheckIdStep( + userId = state.userId, + onSubmit = { userId -> + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + viewModel.checkEmailById(userId) + keyboardManager?.hide() + } } - } - }, - ) - is EnterFullEmail -> EnterFullEmailStep( - userId = (uiState as EnterFullEmail).userId, - maskedEmail = (uiState as EnterFullEmail).maskedEmail, - notMyEmail = viewModel::goToPreviousStep, - onSubmitFullEmail = { fullEmail -> - scope.launch { - launchSuspendApi(apiOnProgress, apiOnError) { - viewModel.sendFullEmailAndRequestCode(fullEmail) - keyboardManager?.hide() + }, + ) + + is EnterFullEmail -> EnterFullEmailStep( + userId = state.userId, + maskedEmail = state.maskedEmail, + notMyEmail = viewModel::goToPreviousStep, + onSubmitFullEmail = { fullEmail -> + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + viewModel.sendFullEmailAndRequestCode(fullEmail) + keyboardManager?.hide() + } } - } - }, - ) + }, + ) + } } } } From 0eb4bca6313d2d14be2284c6521cf0768231c0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 03:49:25 +0900 Subject: [PATCH 06/27] =?UTF-8?q?fix:=20Step=202=20-=20=EC=8A=A4=ED=85=9D?= =?UTF-8?q?=203=EC=97=90=EC=84=9C=20=EC=9D=B4=EC=A0=84=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=8F=8C=EC=95=84=EC=99=94=EC=9D=84=20=EB=95=8C=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EA=B0=92=20=EC=9C=A0=EC=A7=80=EB=90=98=EA=B2=8C=20?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logged_out/reset_password/EnterFullEmailStep.kt | 3 ++- .../reset_password/FindPasswordViewModel.kt | 11 ++++++++++- .../logged_out/reset_password/ResetPasswordPage.kt | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt index 19036d63a..c4354ace9 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt @@ -39,13 +39,14 @@ import com.wafflestudio.snutt2.ui.SNUTTTypography fun EnterFullEmailStep( userId: String, maskedEmail: String, + fullEmail: String, notMyEmail: () -> Unit, onSubmitFullEmail: (String) -> Unit, ) { val focusManager = LocalFocusManager.current val context = LocalContext.current - var emailField by remember { mutableStateOf("") } + var emailField by remember { mutableStateOf(fullEmail) } val buttonEnabled by remember { derivedStateOf { !emailField.isEmailInvalid() diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt index 03cdcb14d..f39988dc4 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt @@ -25,6 +25,7 @@ class FindPasswordViewModel @Inject constructor( data class EnterFullEmail( val userId: String, val maskedEmail: String, + val fullEmail: String, ) : UIState() data object VerifyCode : UIState() @@ -39,6 +40,13 @@ class FindPasswordViewModel @Inject constructor( val savedUserId = savedStateHandle["userId"] ?: "" _uiState.value = UIState.CheckId(savedUserId) } + is UIState.VerifyCode -> { + val savedUserId = savedStateHandle["userId"] ?: "" + val savedMaskedEmail = savedStateHandle["maskedEmail"] ?: "" + val savedFullEmail = savedStateHandle["fullEmail"] ?: "" + _uiState.value = UIState.EnterFullEmail(savedUserId, savedMaskedEmail, savedFullEmail) + } + else -> {} } } @@ -46,7 +54,8 @@ class FindPasswordViewModel @Inject constructor( savedStateHandle["userId"] = userId val maskedEmail = userRepository.checkEmailById(userId) savedStateHandle["maskedEmail"] = maskedEmail - _uiState.value = UIState.EnterFullEmail(userId, maskedEmail) + val savedFullEmail = savedStateHandle["fullEmail"] ?: "" + _uiState.value = UIState.EnterFullEmail(userId, maskedEmail, savedFullEmail) } suspend fun sendFullEmailAndRequestCode(fullEmail: String) {} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 636127622..c0f164554 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -18,8 +18,7 @@ import com.wafflestudio.snutt2.views.LocalApiOnError import com.wafflestudio.snutt2.views.LocalApiOnProgress import com.wafflestudio.snutt2.views.LocalNavController import com.wafflestudio.snutt2.views.launchSuspendApi -import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.CheckId -import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.EnterFullEmail +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.* import kotlinx.coroutines.launch @OptIn(ExperimentalComposeUiApi::class) @@ -62,6 +61,7 @@ fun ResetPasswordPage() { is EnterFullEmail -> EnterFullEmailStep( userId = state.userId, maskedEmail = state.maskedEmail, + fullEmail = state.fullEmail, notMyEmail = viewModel::goToPreviousStep, onSubmitFullEmail = { fullEmail -> scope.launch { From 9079d584424527347a2dda1de65931356b681a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 04:17:38 +0900 Subject: [PATCH 07/27] =?UTF-8?q?fix:=20Dialog=20=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=EB=A7=8C=20=EB=B3=B4=EC=9D=BC=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/components/compose/ProgressDialog.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/ProgressDialog.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/ProgressDialog.kt index 3647a8886..ffbac05e7 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/ProgressDialog.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/ProgressDialog.kt @@ -108,6 +108,13 @@ fun CustomDialog( } } } + } ?: positiveButtonText?.let { + Row(modifier = Modifier.padding(vertical = 20.dp, horizontal = 30.dp)) { + Box(modifier = Modifier.weight(1f)) + Box(modifier = Modifier.clicks { onConfirm() }) { + Text(text = positiveButtonText, style = SNUTTTypography.body1) + } + } } } } From b653b8e84581360f4a2978e724bb3e24954d5652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 17:22:57 +0900 Subject: [PATCH 08/27] =?UTF-8?q?feat:=20Step=203=20-=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=20=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reset_password/FindPasswordViewModel.kt | 12 +- .../reset_password/ResetPasswordPage.kt | 22 ++- .../reset_password/VerifyCodeStep.kt | 176 ++++++++++++++++++ app/src/main/res/values/strings.xml | 10 +- 4 files changed, 213 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt index f39988dc4..4f0296a63 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt @@ -28,7 +28,9 @@ class FindPasswordViewModel @Inject constructor( val fullEmail: String, ) : UIState() - data object VerifyCode : UIState() + data class VerifyCode( + val fullEmail: String, + ) : UIState() data object EnterNewPassword : UIState() } @@ -58,5 +60,11 @@ class FindPasswordViewModel @Inject constructor( _uiState.value = UIState.EnterFullEmail(userId, maskedEmail, savedFullEmail) } - suspend fun sendFullEmailAndRequestCode(fullEmail: String) {} + suspend fun sendFullEmailAndRequestCode(fullEmail: String) { + savedStateHandle["fullEmail"] = fullEmail + userRepository.sendPwResetCodeToEmail(fullEmail) + _uiState.value = UIState.VerifyCode(fullEmail) + } + + suspend fun verifyCode(code: String) {} } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index c0f164554..1a5947425 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -18,7 +18,9 @@ import com.wafflestudio.snutt2.views.LocalApiOnError import com.wafflestudio.snutt2.views.LocalApiOnProgress import com.wafflestudio.snutt2.views.LocalNavController import com.wafflestudio.snutt2.views.launchSuspendApi -import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.* +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.CheckId +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.EnterFullEmail +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.VerifyCode import kotlinx.coroutines.launch @OptIn(ExperimentalComposeUiApi::class) @@ -72,6 +74,24 @@ fun ResetPasswordPage() { } }, ) + + is VerifyCode -> VerifyCodeStep( + fullEmail = state.fullEmail, + onRequestResend = { + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + viewModel.sendFullEmailAndRequestCode(state.fullEmail) + } + } + }, + onSubmit = { code -> + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + viewModel.verifyCode(code) + } + } + }, + ) } } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt new file mode 100644 index 000000000..190a05b84 --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt @@ -0,0 +1,176 @@ +package com.wafflestudio.snutt2.views.logged_out.reset_password + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.components.compose.CustomDialog +import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.Timer +import com.wafflestudio.snutt2.components.compose.TimerValue +import com.wafflestudio.snutt2.components.compose.WebViewStyleButton +import com.wafflestudio.snutt2.components.compose.clicks +import com.wafflestudio.snutt2.components.compose.rememberTimerState +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTypography + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun VerifyCodeStep( + fullEmail: String, + onRequestResend: () -> Unit, + onSubmit: (String) -> Unit, +) { + var codeField by remember { mutableStateOf("") } + val buttonEnabled by remember { + derivedStateOf { + codeField.length == 8 + } + } + var showWhyNotCodeComingDialog by remember { mutableStateOf(false) } + val timerState = rememberTimerState( + initialValue = TimerValue.Initial, + durationInSecond = 180, + ) + LaunchedEffect(Unit) { + timerState.start() + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 44.dp, horizontal = 20.dp), + ) { + Text( + text = stringResource(R.string.find_password_verification_code_content, fullEmail), + style = SNUTTTypography.h2.copy(fontSize = 17.sp), + ) + + Spacer(modifier = Modifier.height(48.dp)) + + Text( + text = stringResource(R.string.find_password_send_code_label), + style = SNUTTTypography.body1.copy(color = SNUTTColors.EditTextLabel), + ) + + Spacer(modifier = Modifier.height(12.dp)) + + EditText( + modifier = Modifier.fillMaxWidth(), + value = codeField, + onValueChange = { codeField = it }, + hint = stringResource(R.string.find_password_send_code_hint), + keyboardActions = KeyboardActions( + onDone = { + if (buttonEnabled) { + onSubmit(codeField) + } + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + singleLine = true, + trailingIcon = { + Row( + modifier = Modifier.padding(start = 10.dp), + ) { + Timer( + state = timerState, + endMessage = stringResource(R.string.find_password_send_code_resend), + ) { timerText -> + Text( + text = timerText, + style = SNUTTTypography.subtitle2.copy( + fontSize = 15.sp, + color = if (timerState.isRunning) { + SNUTTColors.Red + } else { + SNUTTColors.SNUTTTheme + }, + ), + modifier = Modifier + .clicks { + if (timerState.isEnded) { + onRequestResend() + timerState.reset() + timerState.start() + } + } + .padding(end = 10.dp), + ) + } + } + }, + ) + + if (timerState.isEnded) { + Spacer(modifier = Modifier.height(12.dp)) + + Text( + text = stringResource(R.string.find_password_enter_verification_code_expire_message), + style = SNUTTTypography.body1.copy(fontSize = 13.sp, color = SNUTTColors.Red), + ) + } + + Spacer(modifier = Modifier.height(48.dp)) + + WebViewStyleButton( + modifier = Modifier.fillMaxWidth(), + enabled = buttonEnabled, + onClick = { + onSubmit(codeField) + }, + ) { + Text( + text = stringResource(R.string.common_ok), + style = SNUTTTypography.h3.copy(color = SNUTTColors.AllWhite), + ) + } + + Spacer(modifier = Modifier.height(20.dp)) + + Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { + Text( + text = stringResource(R.string.find_password_send_code_not_coming), + style = SNUTTTypography.body1.copy(color = SNUTTColors.VacancyGray), + modifier = Modifier.clicks { + showWhyNotCodeComingDialog = true + }, + ) + } + } + + if (showWhyNotCodeComingDialog) { + CustomDialog( + title = stringResource(R.string.find_password_enter_verification_code_why_not_coming), + onConfirm = { + showWhyNotCodeComingDialog = false + }, + onDismiss = {}, + positiveButtonText = stringResource(R.string.common_ok), + negativeButtonText = null, + ) { + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a68a5885d..a50c1d450 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -294,14 +294,16 @@ 연동된 이메일은 %s입니다. 해당 이메일로 인증 코드를 발송하시겠습니까? 다른 아이디 확인하기 아이디를 입력하세요. - 인증 코드 입력 - 인증 번호 + 인증코드 8자리를 입력하세요 + 인증코드 다시 요청 - %s으로 전송된\n인증번호를 입력해주세요. + 인증번호가 오지 않나요? + %s으로 전송된\n인증코드를 입력해주세요. 인증 코드를 입력해주세요. 인증 코드를 입력해주세요. - 시간이 초과되었습니다. 다시 시도해주세요. + 시간이 초과되었습니다. 다시 요청해주세요. 인증에 성공하였습니다. + 전송에 시간이 소요될 수 있습니다.\n스팸함을 확인하거나,\n3분 초과 시 재요청을 해주세요. 새로운 비밀번호 새로운 비밀번호 확인 새 비밀번호를 입력해주세요. From 2a5633fccd4195d565b34b97c51992376f7df0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 18:31:27 +0900 Subject: [PATCH 09/27] =?UTF-8?q?feat:=20Step=204=20-=20=EC=83=88=20?= =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/lib/data/SNUTTStringUtils.kt | 2 + .../reset_password/FindPasswordViewModel.kt | 15 +- .../reset_password/NewPasswordStep.kt | 195 ++++++++++++++++++ .../reset_password/ResetPasswordPage.kt | 21 ++ app/src/main/res/values/strings.xml | 8 +- 5 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/data/SNUTTStringUtils.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/data/SNUTTStringUtils.kt index 28ceb131d..c2696cee6 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/data/SNUTTStringUtils.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/data/SNUTTStringUtils.kt @@ -173,4 +173,6 @@ object SNUTTStringUtils { append("(${quota - freshmanQuota})") } }.toString() + + fun String.isValidPassword(): Boolean = Regex("^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{6,20}$").matches(this) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt index 4f0296a63..d5b21d0bf 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt @@ -48,7 +48,10 @@ class FindPasswordViewModel @Inject constructor( val savedFullEmail = savedStateHandle["fullEmail"] ?: "" _uiState.value = UIState.EnterFullEmail(savedUserId, savedMaskedEmail, savedFullEmail) } - else -> {} + is UIState.EnterNewPassword -> { + val savedUserId = savedStateHandle["userId"] ?: "" + _uiState.value = UIState.CheckId(savedUserId) + } } } @@ -66,5 +69,13 @@ class FindPasswordViewModel @Inject constructor( _uiState.value = UIState.VerifyCode(fullEmail) } - suspend fun verifyCode(code: String) {} + suspend fun verifyCode(code: String) { + userRepository.verifyEmailCode(code) + _uiState.value = UIState.EnterNewPassword + } + + suspend fun resetPassword(password: String) { + val savedUserId = savedStateHandle["userId"] ?: "" + userRepository.resetPassword(savedUserId, password) + } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt new file mode 100644 index 000000000..6c179020a --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -0,0 +1,195 @@ +package com.wafflestudio.snutt2.views.logged_out.reset_password + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager +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.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.components.compose.CustomDialog +import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.Timer +import com.wafflestudio.snutt2.components.compose.TimerValue +import com.wafflestudio.snutt2.components.compose.WebViewStyleButton +import com.wafflestudio.snutt2.components.compose.rememberTimerState +import com.wafflestudio.snutt2.lib.data.SNUTTStringUtils.isValidPassword +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTypography + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun NewPasswordStep( + onSubmit: (String) -> Unit, + showCompleteDialog: State, + onComplete: () -> Unit, +) { + val focusManager = LocalFocusManager.current + val context = LocalContext.current + val keyboardManager = LocalSoftwareKeyboardController.current + + var newPasswordField by remember { mutableStateOf("") } + var newPasswordConfirmField by remember { mutableStateOf("") } + + var showErrorDialog by remember { mutableStateOf(false) } + var errorDialogTitle by remember { mutableStateOf("") } + + val timerState = rememberTimerState( + initialValue = TimerValue.Initial, + durationInSecond = 180, + ) + LaunchedEffect(Unit) { + timerState.start() + } + + val buttonEnabled by remember { + derivedStateOf { + timerState.isRunning && newPasswordField.isNotBlank() && newPasswordConfirmField.isNotBlank() + } + } + + val validateNewPasswordAndSubmit = { + if (newPasswordField != newPasswordConfirmField) { + errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_fail_alert) + showErrorDialog = true + + } else if (newPasswordField.isValidPassword().not()) { + errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_validation_fail_alert) + showErrorDialog = true + } else { + keyboardManager?.hide() + onSubmit(newPasswordField) + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 44.dp, horizontal = 20.dp), + ) { + Text( + text = stringResource(R.string.find_password_enter_password_body), + style = SNUTTTypography.h2.copy(fontSize = 17.sp), + ) + + Spacer(modifier = Modifier.height(40.dp)) + + EditText( + modifier = Modifier.fillMaxWidth(), + value = newPasswordField, + onValueChange = { newPasswordField = it }, + hint = stringResource(R.string.find_password_enter_password_hint), + visualTransformation = PasswordVisualTransformation(), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus(FocusDirection.Down) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + singleLine = true, + trailingIcon = { + Row( + modifier = Modifier.padding(start = 10.dp), + ) { + Timer( + state = timerState, + endMessage = stringResource(R.string.find_password_send_code_resend), + ) { timerText -> + Text( + text = timerText, + style = SNUTTTypography.subtitle2.copy( + fontSize = 15.sp, + color = if (timerState.isRunning) { + SNUTTColors.Red + } else { + SNUTTColors.SNUTTTheme + }, + ), + modifier = Modifier + .padding(end = 10.dp), + ) + } + } + }, + ) + + Spacer(modifier = Modifier.height(24.dp)) + + EditText( + modifier = Modifier.fillMaxWidth(), + value = newPasswordConfirmField, + onValueChange = { newPasswordConfirmField = it }, + hint = stringResource(R.string.find_password_enter_password_confirm_hint), + visualTransformation = PasswordVisualTransformation(), + keyboardActions = KeyboardActions( + onDone = { + validateNewPasswordAndSubmit() + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + singleLine = true, + ) + + Spacer(modifier = Modifier.height(48.dp)) + + WebViewStyleButton( + modifier = Modifier.fillMaxWidth(), + enabled = buttonEnabled, + onClick = { + validateNewPasswordAndSubmit() + }, + ) { + Text( + text = stringResource(R.string.common_ok), + style = SNUTTTypography.h3.copy(color = SNUTTColors.AllWhite), + ) + } + } + + if (showErrorDialog) { + CustomDialog( + title = errorDialogTitle, + onConfirm = { + showErrorDialog = false + }, + onDismiss = {}, + positiveButtonText = stringResource(R.string.common_ok), + negativeButtonText = null, + ) { + } + } + + if (showCompleteDialog.value) { + CustomDialog( + title = stringResource(R.string.find_password_enter_password_success_alert), + onConfirm = onComplete, + onDismiss = {}, + positiveButtonText = stringResource(R.string.common_ok), + negativeButtonText = null, + ) { + } + } +} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 1a5947425..d82fd10fa 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -5,6 +5,8 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -20,6 +22,7 @@ import com.wafflestudio.snutt2.views.LocalNavController import com.wafflestudio.snutt2.views.launchSuspendApi import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.CheckId import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.EnterFullEmail +import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.EnterNewPassword import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.VerifyCode import kotlinx.coroutines.launch @@ -92,6 +95,24 @@ fun ResetPasswordPage() { } }, ) + is EnterNewPassword -> { + val showCompleteDialog = remember { mutableStateOf(false) } + NewPasswordStep( + onSubmit = { newPassword -> + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + viewModel.resetPassword(newPassword) + showCompleteDialog.value = true + } + } + }, + showCompleteDialog = showCompleteDialog, + onComplete = { + showCompleteDialog.value = false + navController.popBackStack() + } + ) + } } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a50c1d450..593e729ec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -304,11 +304,15 @@ 시간이 초과되었습니다. 다시 요청해주세요. 인증에 성공하였습니다. 전송에 시간이 소요될 수 있습니다.\n스팸함을 확인하거나,\n3분 초과 시 재요청을 해주세요. + 새로운 비밀번호를 입력해주세요. 새로운 비밀번호 + 영문, 숫자 모두 포함 6–20자 이내 + 비밀번호를 한번 더 입력하세요 새로운 비밀번호 확인 새 비밀번호를 입력해주세요. - 비밀번호 확인이 일치하지 않습니다. - 비밀번호 재설정이 완료되었습니다. + 비밀번호가 일치하지 않습니다.\n다시 시도해주세요. + 조건에 맞지 않는 비밀번호입니다.\n영문, 숫자 포함 6-20자 이내로\n입력해주세요. + 비밀번호가 변경되었습니다. 이메일 인증 강의평 서비스를 이용하기 위해\n이메일 인증이 필요합니다.\n%s에 대한 이메일 인증을\n진행하시겠습니까? "나중에 하기"를 선택하더라도 강의평 서비스를 제외한 SNUTT의 모든 기능을 이용할 수 있습니다. From e000eb65089a687eec15b5a455654bffc1276dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 18:34:21 +0900 Subject: [PATCH 10/27] =?UTF-8?q?chore:=20=ED=95=84=EC=9A=94=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reset_password/FindPasswordPage.kt | 331 ------------------ app/src/main/res/values/strings.xml | 6 - 2 files changed, 337 deletions(-) delete mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordPage.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordPage.kt deleted file mode 100644 index 84daeef84..000000000 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordPage.kt +++ /dev/null @@ -1,331 +0,0 @@ -package com.wafflestudio.snutt2.views.logged_out.reset_password - -import androidx.activity.compose.BackHandler -import androidx.activity.compose.LocalOnBackPressedDispatcherOwner -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.Text -import androidx.compose.runtime.* -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusDirection -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager -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 -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import com.wafflestudio.snutt2.R -import com.wafflestudio.snutt2.components.compose.* -import com.wafflestudio.snutt2.lib.android.toast -import com.wafflestudio.snutt2.ui.SNUTTColors -import com.wafflestudio.snutt2.ui.SNUTTTypography -import com.wafflestudio.snutt2.views.LocalApiOnError -import com.wafflestudio.snutt2.views.LocalApiOnProgress -import com.wafflestudio.snutt2.views.LocalNavController -import com.wafflestudio.snutt2.views.launchSuspendApi -import com.wafflestudio.snutt2.views.logged_in.home.settings.UserViewModel -import kotlinx.coroutines.launch - -private enum class FlowState { - CheckEmail, SendCode, ResetPassword, -} - -@OptIn(ExperimentalAnimationApi::class, ExperimentalComposeUiApi::class) -@Composable -fun FindPasswordPage() { - val navController = LocalNavController.current - val apiOnError = LocalApiOnError.current - val apiOnProgress = LocalApiOnProgress.current - val context = LocalContext.current - val coroutineScope = rememberCoroutineScope() - val focusManager = LocalFocusManager.current - val keyboardManager = LocalSoftwareKeyboardController.current - val onBackPressedDispatcherOwner = LocalOnBackPressedDispatcherOwner.current - - val userViewModel = hiltViewModel() - - var flowState by remember { mutableStateOf(FlowState.CheckEmail) } - - var idField by remember { mutableStateOf("") } - var codeField by remember { mutableStateOf("") } - var passwordField by remember { mutableStateOf("") } - var passwordConfirmField by remember { mutableStateOf("") } - var checkEmailDialogState by remember { mutableStateOf(false) } - - val buttonEnabled by remember { - derivedStateOf { - when (flowState) { - FlowState.CheckEmail -> idField.isNotEmpty() - FlowState.SendCode -> codeField.isNotEmpty() - FlowState.ResetPassword -> passwordField.isNotEmpty() && passwordConfirmField.isNotEmpty() - } - } - } - - var emailResponse by remember { mutableStateOf("") } - - val timerState = rememberTimerState( - initialValue = TimerValue.Initial, - durationInSecond = 180, - ) - val handleCheckEmailById = { - coroutineScope.launch { - if (idField.isEmpty()) { - context.toast(context.getString(R.string.find_password_enter_id_hint)) - } else { - launchSuspendApi(apiOnProgress, apiOnError) { - emailResponse = userViewModel.checkEmailById(idField) - keyboardManager?.hide() - checkEmailDialogState = true - } - } - } - } - - val handleEnterCode = { - coroutineScope.launch { - if (codeField.isEmpty()) { - context.toast(context.getString(R.string.find_password_enter_verification_code_empty_alert)) - } else if (timerState.isEnded) { - context.toast(context.getString(R.string.find_password_enter_verification_code_expire_message)) - } else { - launchSuspendApi(apiOnProgress, apiOnError) { - userViewModel.verifyPwResetCode(idField, codeField) - keyboardManager?.hide() - context.toast(context.getString(R.string.find_password_enter_verification_code_success_alert)) - timerState.pause() - flowState = FlowState.ResetPassword - } - } - } - } - - val handleResetPassword = { - coroutineScope.launch { - if (passwordField.isEmpty() || passwordConfirmField.isEmpty()) { - context.toast(context.getString(R.string.find_password_enter_password_empty_alert)) - } else if (passwordConfirmField != passwordField) { - context.toast(context.getString(R.string.find_password_enter_password_confirm_fail_alert)) - } else { - launchSuspendApi(apiOnProgress, apiOnError) { - userViewModel.resetPassword(idField, passwordField) - keyboardManager?.hide() - context.toast(context.getString(R.string.find_password_enter_password_success_alert)) - navController.popBackStack() - } - } - } - } - - val onBackPressed: () -> Unit = { - when (flowState) { - FlowState.CheckEmail -> navController.popBackStack() - FlowState.SendCode -> { - flowState = FlowState.CheckEmail - codeField = "" - timerState.reset() - } - FlowState.ResetPassword -> flowState = FlowState.SendCode - } - } - - BackHandler { - onBackPressed() - } - - Column( - modifier = Modifier - .fillMaxSize() - .background(SNUTTColors.White900) - .clicks { focusManager.clearFocus() }, - ) { - SimpleTopBar(title = stringResource(R.string.find_password_title), onClickNavigateBack = { onBackPressed() }) - AnimatedContent(targetState = flowState) { targetState -> - Column(modifier = Modifier.padding(horizontal = 25.dp)) { - when (targetState) { - FlowState.CheckEmail -> { - Text( - text = stringResource(R.string.find_password_check_email_content), - style = SNUTTTypography.h3, - modifier = Modifier.padding(vertical = 25.dp), - ) - Text( - text = stringResource(R.string.sign_in_id_hint), - style = SNUTTTypography.h4, - ) - EditText( - value = idField, - onValueChange = { idField = it }, - hint = stringResource(R.string.find_password_enter_id_hint), - keyboardActions = KeyboardActions(onNext = { - focusManager.moveFocus( - FocusDirection.Down, - ) - },), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - singleLine = true, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 10.dp), - ) - } - FlowState.SendCode -> { - Text( - text = stringResource(R.string.find_password_verification_code_content).format( - emailResponse, - ), - style = SNUTTTypography.h3, - modifier = Modifier.padding(vertical = 25.dp), - ) - Text( - text = stringResource(R.string.find_password_send_code_label), - style = SNUTTTypography.h4, - ) - EditText( - value = codeField, - onValueChange = { codeField = it }, - hint = stringResource(R.string.find_password_send_code_hint), - keyboardActions = KeyboardActions(onNext = { - focusManager.moveFocus( - FocusDirection.Down, - ) - },), - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Done, keyboardType = KeyboardType.Ascii, - ), - singleLine = true, - trailingIcon = { - Row( - modifier = Modifier.padding(start = 10.dp), - ) { - Timer( - state = timerState, - endMessage = stringResource(R.string.find_password_send_code_resend), - ) { timerText -> - Text( - text = timerText, - style = SNUTTTypography.subtitle2.copy( - color = if (timerState.isRunning) { - SNUTTColors.Red - } else { - SNUTTColors.SNUTTTheme - }, - ), - modifier = Modifier.clicks { - if (timerState.isEnded) { - coroutineScope.launch { - launchSuspendApi(apiOnProgress, apiOnError) { - userViewModel.sendPwResetCodeToEmail(emailResponse) - timerState.reset() - timerState.start() - } - } - } - }, - ) - } - } - }, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 10.dp), - ) - if (timerState.isEnded) { - Text( - text = stringResource(R.string.find_password_enter_verification_code_expire_message), - style = SNUTTTypography.body2.copy(color = SNUTTColors.Red), - ) - } - } - FlowState.ResetPassword -> { - Spacer(modifier = Modifier.height(25.dp)) - Text( - text = stringResource(R.string.find_password_enter_password_label), - style = SNUTTTypography.h4, - ) - EditText( - value = passwordField, - onValueChange = { passwordField = it }, - hint = stringResource(R.string.sign_up_password_hint), - keyboardActions = KeyboardActions(onNext = { - focusManager.moveFocus( - FocusDirection.Down, - ) - },), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - singleLine = true, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 10.dp), - ) - Spacer(modifier = Modifier.height(10.dp)) - Text( - text = stringResource(R.string.find_password_enter_password_confirm_label), - style = SNUTTTypography.h4, - ) - EditText( - value = passwordConfirmField, - onValueChange = { passwordConfirmField = it }, - hint = stringResource(R.string.sign_up_password_confirm_hint), - keyboardActions = KeyboardActions(onNext = { - focusManager.moveFocus( - FocusDirection.Down, - ) - },), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - singleLine = true, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 10.dp), - ) - } - } - Spacer(modifier = Modifier.height(30.dp)) - WebViewStyleButton( - modifier = Modifier.fillMaxWidth(), - enabled = buttonEnabled, - onClick = { - when (flowState) { - FlowState.CheckEmail -> handleCheckEmailById() - FlowState.SendCode -> handleEnterCode() - FlowState.ResetPassword -> handleResetPassword() - } - }, - ) { - Text( - text = stringResource(R.string.common_ok), - style = SNUTTTypography.h3.copy(color = SNUTTColors.AllWhite), - ) - } - } - } - } - - if (checkEmailDialogState) { - CustomDialog( - title = stringResource(R.string.find_password_check_email_dialog_title), - onDismiss = { checkEmailDialogState = false }, - onConfirm = { - checkEmailDialogState = false - coroutineScope.launch { - launchSuspendApi(apiOnProgress, apiOnError) { - userViewModel.sendPwResetCodeToEmail(emailResponse) - flowState = FlowState.SendCode - timerState.start() - } - } - }, - positiveButtonText = stringResource(R.string.common_ok), - negativeButtonText = stringResource(R.string.find_password_check_email_dialog_negative), - ) { - Text(text = stringResource(R.string.find_password_check_email_dialog_content).format(emailResponse), style = SNUTTTypography.body1) - } - } -} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 593e729ec..e15e4aa1c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -290,9 +290,6 @@ 전체 주소를 입력하세요 인증코드 받기 나의 이메일 주소가 아닌가요? - 이메일 확인 - 연동된 이메일은 %s입니다. 해당 이메일로 인증 코드를 발송하시겠습니까? - 다른 아이디 확인하기 아이디를 입력하세요. 인증코드 8자리를 입력하세요 인증코드 @@ -300,15 +297,12 @@ 인증번호가 오지 않나요? %s으로 전송된\n인증코드를 입력해주세요. 인증 코드를 입력해주세요. - 인증 코드를 입력해주세요. 시간이 초과되었습니다. 다시 요청해주세요. 인증에 성공하였습니다. 전송에 시간이 소요될 수 있습니다.\n스팸함을 확인하거나,\n3분 초과 시 재요청을 해주세요. 새로운 비밀번호를 입력해주세요. - 새로운 비밀번호 영문, 숫자 모두 포함 6–20자 이내 비밀번호를 한번 더 입력하세요 - 새로운 비밀번호 확인 새 비밀번호를 입력해주세요. 비밀번호가 일치하지 않습니다.\n다시 시도해주세요. 조건에 맞지 않는 비밀번호입니다.\n영문, 숫자 포함 6-20자 이내로\n입력해주세요. From 6bcfd736543095772e3582c827165b30764db51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 20:30:48 +0900 Subject: [PATCH 11/27] =?UTF-8?q?fix:=20EditText=20=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EA=B0=92=20=EC=97=86=EB=8A=94=EB=8D=B0=20?= =?UTF-8?q?=ED=8F=AC=EC=BB=A4=EC=8A=A4=20=EB=90=98=EB=A9=B4=20=ED=9E=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=82=AC=EB=9D=BC=EC=A7=80=EB=8A=94=20=EA=B2=83=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wafflestudio/snutt2/components/compose/EditText.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt index b1b9d0a4a..08ed961d3 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt @@ -86,7 +86,7 @@ fun EditText( modifier = Modifier.weight(1f), ) { it() - if (value.isEmpty() && isFocused.not()) { // FIXME: lectureDetail 에서는 focus 되어 있어도 empty이면 hint 가 나와야 한다. + if (value.isEmpty()) { hint?.let { Text( text = it, From cd0378f5734e0016a9460244ec74ca04ccff4a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 20:32:00 +0900 Subject: [PATCH 12/27] =?UTF-8?q?feat:=20=EA=B8=B0=EC=A1=B4=20EditText=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20String=20=EB=8C=80=EC=8B=A0=20TextFieldVal?= =?UTF-8?q?ue=20=EC=82=AC=EC=9A=A9=EB=8F=84=EB=A1=9D=EB=A7=8C=20=EB=B0=94?= =?UTF-8?q?=EB=80=90=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/compose/EditTextFieldValue.kt | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt new file mode 100644 index 000000000..d8b999fb4 --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt @@ -0,0 +1,114 @@ +package com.wafflestudio.snutt2.components.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.selection.LocalTextSelectionColors +import androidx.compose.foundation.text.selection.TextSelectionColors +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTypography + +@Composable +fun EditTextFieldValue( + modifier: Modifier = Modifier, + leadingIcon: @Composable (() -> Unit) = {}, + trailingIcon: @Composable (() -> Unit) = {}, + keyboardOptions: KeyboardOptions = KeyboardOptions(), + keyboardActions: KeyboardActions = KeyboardActions(), + singleLine: Boolean = false, + enabled: Boolean = true, + visualTransformation: VisualTransformation = VisualTransformation.None, + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + hint: String? = null, + underlineEnabled: Boolean = true, + underlineColor: Color = SNUTTColors.Gray200, + underlineColorFocused: Color = SNUTTColors.Black900, + underlineWidth: Dp = 1.dp, + clearFocusFlag: Boolean = false, + textStyle: TextStyle = SNUTTTypography.subtitle1.copy(color = SNUTTColors.Black900), +) { + val focusManager = LocalFocusManager.current + LaunchedEffect(clearFocusFlag) { + if (clearFocusFlag) focusManager.clearFocus() + } + + var isFocused by remember { mutableStateOf(false) } + val customTextSelectionColors = TextSelectionColors( + handleColor = SNUTTColors.Black900, + backgroundColor = SNUTTColors.Black300, + ) + CompositionLocalProvider( + LocalTextSelectionColors provides customTextSelectionColors, + ) { + BasicTextField( + modifier = modifier + .onFocusChanged { isFocused = it.isFocused }, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + value = value, + textStyle = textStyle, + enabled = enabled, + onValueChange = onValueChange, + singleLine = singleLine, + visualTransformation = visualTransformation, + cursorBrush = SolidColor(SNUTTColors.Black900), + decorationBox = { + Column { + Row(modifier = Modifier.fillMaxWidth()) { + leadingIcon() + Box( + modifier = Modifier.weight(1f), + ) { + it() + if (value.text.isEmpty()) { + hint?.let { + Text( + text = it, + style = textStyle.copy(color = SNUTTColors.Gray200), + ) + } + } + } + trailingIcon() + } + + if (underlineEnabled) { + Box( + modifier = Modifier + .padding(top = 8.dp) + .background(if (isFocused) underlineColorFocused else underlineColor) + .fillMaxWidth() + .height(underlineWidth), + ) + } + } + }, + ) + } +} From ae33c2d7a3d2ea56d6a14c99f0c91782a85ec60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 20:34:33 +0900 Subject: [PATCH 13/27] =?UTF-8?q?feat:=20Step=201=20-=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94=20=EC=9E=85=EB=A0=A5=20=ED=99=94=EB=A9=B4=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용성 개선 (자동 포커스, KeyboardActions) - TextFieldValue 쓰는 EditText 로 교체해서, EditText에 초기값 있을 때 포커스 가면 커서가 맨 끝으로 갈 수 있도록 하기 --- .../logged_out/reset_password/CheckIdStep.kt | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt index 59524a6cd..049e58cad 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -17,44 +18,57 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.wafflestudio.snutt2.R -import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.EditTextFieldValue import com.wafflestudio.snutt2.components.compose.WebViewStyleButton import com.wafflestudio.snutt2.lib.android.toast import com.wafflestudio.snutt2.ui.SNUTTColors import com.wafflestudio.snutt2.ui.SNUTTTypography -@OptIn(ExperimentalComposeUiApi::class) @Composable fun CheckIdStep( userId: String, onSubmit: (String) -> Unit, ) { - val focusManager = LocalFocusManager.current + val focusRequester = remember { FocusRequester() } val context = LocalContext.current - var idField by remember { mutableStateOf(userId) } + var idField by remember { + mutableStateOf( + TextFieldValue( + text = userId, + selection = TextRange(userId.length), // 초기 커서를 텍스트 끝으로 설정 + ), + ) + } val buttonEnabled by remember { derivedStateOf { - idField.isNotEmpty() + idField.text.isNotEmpty() } } val sendIdAndRequestMaskedEmail = { - if (idField.isEmpty()) { + if (idField.text.isEmpty()) { context.toast(context.getString(R.string.find_password_enter_id_hint)) } else { - onSubmit(idField) + onSubmit(idField.text) } } + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } + Column( modifier = Modifier .fillMaxSize() @@ -74,16 +88,19 @@ fun CheckIdStep( Spacer(modifier = Modifier.height(12.dp)) - EditText( - modifier = Modifier.fillMaxWidth(), + EditTextFieldValue( + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester) + .onFocusChanged { + idField = idField.copy(selection = TextRange(idField.text.length)) + }, value = idField, onValueChange = { idField = it }, hint = stringResource(R.string.find_password_enter_id_hint), keyboardActions = KeyboardActions( - onNext = { - focusManager.moveFocus( - FocusDirection.Enter, - ) + onDone = { + onSubmit(idField.text) }, ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), From db751462b024a0d8794b42458931b2601cd494a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 20:48:33 +0900 Subject: [PATCH 14/27] =?UTF-8?q?feat:=20Step=202=20-=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=9E=85=EB=A0=A5=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 비슷하게, 포커스 처리 + 커서 처리 + keyboardActions 처리 + imePadding --- .../reset_password/EnterFullEmailStep.kt | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt index c4354ace9..9a14148e2 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt @@ -6,11 +6,15 @@ import androidx.compose.foundation.layout.Spacer 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.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -19,14 +23,17 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import com.wafflestudio.snutt2.R import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.EditTextFieldValue import com.wafflestudio.snutt2.components.compose.WebViewStyleButton import com.wafflestudio.snutt2.components.compose.clicks import com.wafflestudio.snutt2.lib.android.toast @@ -34,7 +41,6 @@ import com.wafflestudio.snutt2.lib.data.SNUTTStringUtils.isEmailInvalid import com.wafflestudio.snutt2.ui.SNUTTColors import com.wafflestudio.snutt2.ui.SNUTTTypography -@OptIn(ExperimentalComposeUiApi::class) @Composable fun EnterFullEmailStep( userId: String, @@ -43,27 +49,42 @@ fun EnterFullEmailStep( notMyEmail: () -> Unit, onSubmitFullEmail: (String) -> Unit, ) { - val focusManager = LocalFocusManager.current + val focusRequester = remember { FocusRequester() } val context = LocalContext.current - var emailField by remember { mutableStateOf(fullEmail) } + var emailField by remember { + mutableStateOf( + TextFieldValue( + text = fullEmail, + selection = TextRange(fullEmail.length), // 초기 커서를 텍스트 끝으로 설정 + ), + ) + } val buttonEnabled by remember { derivedStateOf { - !emailField.isEmailInvalid() + !emailField.text.isEmailInvalid() } } - val sendIdAndRequestMaskedEmail = { - if (emailField.isEmpty()) { - context.toast(context.getString(R.string.find_password_enter_id_hint)) - } else { - onSubmitFullEmail(emailField) + val sendIdAndRequestMaskedEmail: () -> Unit = { + if (buttonEnabled) { + if (emailField.text.isEmpty()) { + context.toast(context.getString(R.string.find_password_enter_id_hint)) + } else { + onSubmitFullEmail(emailField.text) + } } } + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } + Column( modifier = Modifier .fillMaxSize() + .verticalScroll(rememberScrollState()) + .imePadding() .padding(vertical = 44.dp, horizontal = 20.dp), ) { Text( @@ -97,16 +118,16 @@ fun EnterFullEmailStep( Spacer(modifier = Modifier.height(12.dp)) - EditText( - modifier = Modifier.fillMaxWidth(), + EditTextFieldValue( + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester), value = emailField, onValueChange = { emailField = it }, hint = stringResource(R.string.find_password_check_email_enter_full_email_hint), keyboardActions = KeyboardActions( - onNext = { - focusManager.moveFocus( - FocusDirection.Enter, - ) + onDone = { + sendIdAndRequestMaskedEmail() }, ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), From 9f229d1f4d13acfc3c9f6286e9dc94e4560ba903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 20:49:15 +0900 Subject: [PATCH 15/27] =?UTF-8?q?feat:=20Step=203=20-=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=20=ED=99=94=EB=A9=B4=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 커서, 포커스 등 처리 --- .../reset_password/VerifyCodeStep.kt | 31 +++++++++++++++---- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt index 190a05b84..7e184fafe 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt @@ -7,9 +7,12 @@ import androidx.compose.foundation.layout.Spacer 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.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -21,6 +24,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp @@ -43,24 +49,35 @@ fun VerifyCodeStep( onRequestResend: () -> Unit, onSubmit: (String) -> Unit, ) { + val keyboardManager = LocalSoftwareKeyboardController.current + val focusRequester = remember { FocusRequester() } var codeField by remember { mutableStateOf("") } - val buttonEnabled by remember { - derivedStateOf { - codeField.length == 8 - } - } var showWhyNotCodeComingDialog by remember { mutableStateOf(false) } val timerState = rememberTimerState( initialValue = TimerValue.Initial, durationInSecond = 180, ) + val buttonEnabled by remember { + derivedStateOf { + codeField.length == 8 && timerState.isRunning + } + } + LaunchedEffect(Unit) { timerState.start() } + LaunchedEffect(showWhyNotCodeComingDialog) { + if (showWhyNotCodeComingDialog.not()) { + focusRequester.requestFocus() + keyboardManager?.show() + } + } Column( modifier = Modifier .fillMaxSize() + .verticalScroll(rememberScrollState()) + .imePadding() .padding(vertical = 44.dp, horizontal = 20.dp), ) { Text( @@ -78,7 +95,9 @@ fun VerifyCodeStep( Spacer(modifier = Modifier.height(12.dp)) EditText( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester), value = codeField, onValueChange = { codeField = it }, hint = stringResource(R.string.find_password_send_code_hint), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e15e4aa1c..c40ff395c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -306,6 +306,8 @@ 새 비밀번호를 입력해주세요. 비밀번호가 일치하지 않습니다.\n다시 시도해주세요. 조건에 맞지 않는 비밀번호입니다.\n영문, 숫자 포함 6-20자 이내로\n입력해주세요. + 시간이 초과되어\n인증코드 재입력이 필요합니다. + 만료 비밀번호가 변경되었습니다. 이메일 인증 강의평 서비스를 이용하기 위해\n이메일 인증이 필요합니다.\n%s에 대한 이메일 인증을\n진행하시겠습니까? From 9344573e9f30888ffbc4963c688054e1bf9e0a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 21:13:20 +0900 Subject: [PATCH 16/27] =?UTF-8?q?feat:=20Step=204=20-=20=EC=83=88=20?= =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 포커스 등등 --- .../reset_password/NewPasswordStep.kt | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt index 6c179020a..e244313c4 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -6,9 +6,12 @@ import androidx.compose.foundation.layout.Spacer 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.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -21,6 +24,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -48,6 +53,7 @@ fun NewPasswordStep( onComplete: () -> Unit, ) { val focusManager = LocalFocusManager.current + val focusRequester = remember { FocusRequester() } val context = LocalContext.current val keyboardManager = LocalSoftwareKeyboardController.current @@ -61,33 +67,44 @@ fun NewPasswordStep( initialValue = TimerValue.Initial, durationInSecond = 180, ) - LaunchedEffect(Unit) { - timerState.start() - } - val buttonEnabled by remember { derivedStateOf { timerState.isRunning && newPasswordField.isNotBlank() && newPasswordConfirmField.isNotBlank() } } - val validateNewPasswordAndSubmit = { - if (newPasswordField != newPasswordConfirmField) { - errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_fail_alert) + LaunchedEffect(Unit) { + focusRequester.requestFocus() + timerState.start() + } + LaunchedEffect(timerState.currentValue) { + if (timerState.isEnded) { + errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_expired_alert) showErrorDialog = true + } + } - } else if (newPasswordField.isValidPassword().not()) { - errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_validation_fail_alert) - showErrorDialog = true - } else { - keyboardManager?.hide() - onSubmit(newPasswordField) + val validateNewPasswordAndSubmit = { + if (timerState.isRunning) { + if (newPasswordField != newPasswordConfirmField) { + errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_fail_alert) + showErrorDialog = true + + } else if (newPasswordField.isValidPassword().not()) { + errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_validation_fail_alert) + showErrorDialog = true + } else { + keyboardManager?.hide() + onSubmit(newPasswordField) + } } } Column( modifier = Modifier .fillMaxSize() + .verticalScroll(rememberScrollState()) + .imePadding() .padding(vertical = 44.dp, horizontal = 20.dp), ) { Text( @@ -98,7 +115,9 @@ fun NewPasswordStep( Spacer(modifier = Modifier.height(40.dp)) EditText( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester), value = newPasswordField, onValueChange = { newPasswordField = it }, hint = stringResource(R.string.find_password_enter_password_hint), @@ -116,7 +135,7 @@ fun NewPasswordStep( ) { Timer( state = timerState, - endMessage = stringResource(R.string.find_password_send_code_resend), + endMessage = stringResource(R.string.find_password_enter_password_confirm_expired), ) { timerText -> Text( text = timerText, @@ -174,6 +193,7 @@ fun NewPasswordStep( title = errorDialogTitle, onConfirm = { showErrorDialog = false + focusRequester.requestFocus() }, onDismiss = {}, positiveButtonText = stringResource(R.string.common_ok), From ffa3f526028c9fc73519f10578559d6c3cadeb1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 21:26:51 +0900 Subject: [PATCH 17/27] =?UTF-8?q?feat:=20=EB=B9=84=EB=B2=88=20=EB=A6=AC?= =?UTF-8?q?=EC=85=8B=20API=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit code 도 함께 보내도록 변경한다. --- .../com/wafflestudio/snutt2/data/user/UserRepository.kt | 2 +- .../wafflestudio/snutt2/data/user/UserRepositoryImpl.kt | 4 ++-- .../snutt2/lib/network/dto/PostResetPasswordParams.kt | 1 + .../snutt2/views/logged_in/home/settings/UserViewModel.kt | 4 ++-- .../logged_out/reset_password/FindPasswordViewModel.kt | 7 +++++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepository.kt b/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepository.kt index 562b46b61..226a5487c 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepository.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepository.kt @@ -92,7 +92,7 @@ interface UserRepository { suspend fun verifyPwResetCode(id: String, code: String) - suspend fun resetPassword(id: String, password: String) + suspend fun resetPassword(id: String, password: String, code: String) suspend fun sendCodeToEmail(email: String) diff --git a/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepositoryImpl.kt b/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepositoryImpl.kt index 60aa671b6..bc887ed76 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepositoryImpl.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/data/user/UserRepositoryImpl.kt @@ -258,9 +258,9 @@ class UserRepositoryImpl @Inject constructor( ) } - override suspend fun resetPassword(id: String, password: String) { + override suspend fun resetPassword(id: String, password: String, code: String) { api._postResetPassword( - PostResetPasswordParams(id, password), + PostResetPasswordParams(id, password, code), ) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/PostResetPasswordParams.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/PostResetPasswordParams.kt index 581104807..9252bec40 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/PostResetPasswordParams.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/PostResetPasswordParams.kt @@ -7,4 +7,5 @@ import com.squareup.moshi.JsonClass data class PostResetPasswordParams( @Json(name = "user_id") val id: String, @Json(name = "password") val password: String, + @Json(name = "code") val code: String, ) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/UserViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/UserViewModel.kt index 9f6a5c840..d1900a8ab 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/UserViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/UserViewModel.kt @@ -136,8 +136,8 @@ class UserViewModel @Inject constructor( userRepository.verifyPwResetCode(id, code) } - suspend fun resetPassword(id: String, password: String) { - userRepository.resetPassword(id, password) + suspend fun resetPassword(id: String, password: String, code: String) { + userRepository.resetPassword(id, password, code) } suspend fun sendCodeToEmail(email: String) { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt index d5b21d0bf..eb6495b40 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/FindPasswordViewModel.kt @@ -70,12 +70,15 @@ class FindPasswordViewModel @Inject constructor( } suspend fun verifyCode(code: String) { - userRepository.verifyEmailCode(code) + val savedUserId = savedStateHandle["userId"] ?: "" + userRepository.verifyPwResetCode(savedUserId, code) + savedStateHandle["code"] = code _uiState.value = UIState.EnterNewPassword } suspend fun resetPassword(password: String) { val savedUserId = savedStateHandle["userId"] ?: "" - userRepository.resetPassword(savedUserId, password) + val savedCode = savedStateHandle["code"] ?: "" + userRepository.resetPassword(savedUserId, password, savedCode) } } From d709082a453486d60c17c5e9af576bb2d269681a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 21:27:36 +0900 Subject: [PATCH 18/27] =?UTF-8?q?feat:=20=EA=B8=B0=ED=83=80=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=84=B1=20=EC=B6=94=EA=B0=80=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logged_out/reset_password/NewPasswordStep.kt | 5 +++++ .../reset_password/ResetPasswordPage.kt | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt index e244313c4..aa74314d7 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -83,6 +83,11 @@ fun NewPasswordStep( showErrorDialog = true } } + LaunchedEffect(showCompleteDialog.value) { + if (showCompleteDialog.value) { + timerState.pause() + } + } val validateNewPasswordAndSubmit = { if (timerState.isRunning) { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index d82fd10fa..17a199440 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -1,5 +1,6 @@ package com.wafflestudio.snutt2.views.logged_out.reset_password +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -10,6 +11,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel @@ -30,6 +32,7 @@ import kotlinx.coroutines.launch @Composable fun ResetPasswordPage() { val scope = rememberCoroutineScope() + val context = LocalContext.current val apiOnProgress = LocalApiOnProgress.current val apiOnError = LocalApiOnError.current val keyboardManager = LocalSoftwareKeyboardController.current @@ -37,6 +40,14 @@ fun ResetPasswordPage() { val viewModel = hiltViewModel() val uiState by viewModel.uiState.collectAsStateWithLifecycle() + BackHandler { + if (uiState is CheckId) { + navController.popBackStack() + } else { + viewModel.goToPreviousStep() + } + } + Column(modifier = Modifier.fillMaxSize()) { SimpleTopBar( title = stringResource(R.string.find_password_title), @@ -82,7 +93,7 @@ fun ResetPasswordPage() { fullEmail = state.fullEmail, onRequestResend = { scope.launch { - launchSuspendApi(apiOnProgress, apiOnError) { + launchSuspendApi(apiOnProgress, apiOnError, loadingIndicatorTitle = context.getString(R.string.loading_indicator_message)) { viewModel.sendFullEmailAndRequestCode(state.fullEmail) } } @@ -95,6 +106,7 @@ fun ResetPasswordPage() { } }, ) + is EnterNewPassword -> { val showCompleteDialog = remember { mutableStateOf(false) } NewPasswordStep( @@ -110,7 +122,7 @@ fun ResetPasswordPage() { onComplete = { showCompleteDialog.value = false navController.popBackStack() - } + }, ) } } From 2a4dab08d70c54415f901644c638d01c7f9219fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 21:27:49 +0900 Subject: [PATCH 19/27] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/wafflestudio/snutt2/views/RootActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index 8d18fd312..fa425be8a 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -77,7 +77,7 @@ import com.wafflestudio.snutt2.views.logged_in.table_lectures.LecturesOfTablePag import com.wafflestudio.snutt2.views.logged_in.vacancy_noti.VacancyPage import com.wafflestudio.snutt2.views.logged_in.vacancy_noti.VacancyViewModel import com.wafflestudio.snutt2.views.logged_out.* -import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordPage +import com.wafflestudio.snutt2.views.logged_out.reset_password.ResetPasswordPage import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import javax.inject.Inject @@ -349,7 +349,7 @@ class RootActivity : AppCompatActivity() { } composable2(NavigationDestination.FindPassword) { - FindPasswordPage() + ResetPasswordPage() } composable2(NavigationDestination.EmailVerification) { From bae4b718b953ed66c273a8bd369f6b4e375f4ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 21:48:08 +0900 Subject: [PATCH 20/27] =?UTF-8?q?feat:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=84=B8=EB=B6=80=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81=20(?= =?UTF-8?q?=EC=95=88=20=EC=9D=BD=EC=96=B4=EB=8F=84=20=EB=90=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wafflestudio/snutt2/components/compose/EditText.kt | 5 ++++- .../snutt2/components/compose/EditTextFieldValue.kt | 5 ++++- .../snutt2/components/compose/WebViewStyleButton.kt | 5 +++-- app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt | 2 ++ .../snutt2/views/logged_out/reset_password/CheckIdStep.kt | 3 ++- .../views/logged_out/reset_password/EnterFullEmailStep.kt | 4 ++-- .../views/logged_out/reset_password/NewPasswordStep.kt | 4 +++- .../snutt2/views/logged_out/reset_password/VerifyCodeStep.kt | 3 ++- 8 files changed, 22 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt index 08ed961d3..b31c74e21 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditText.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.wafflestudio.snutt2.ui.SNUTTColors import com.wafflestudio.snutt2.ui.SNUTTTypography @@ -46,6 +47,8 @@ fun EditText( value: String, onValueChange: (String) -> Unit, hint: String? = null, + hintTextColor: Color = SNUTTColors.EditTextHint, + hintTextStyle: TextStyle = SNUTTTypography.body1.copy(fontSize = 15.sp), underlineEnabled: Boolean = true, underlineColor: Color = SNUTTColors.Gray200, underlineColorFocused: Color = SNUTTColors.Black900, @@ -90,7 +93,7 @@ fun EditText( hint?.let { Text( text = it, - style = textStyle.copy(color = SNUTTColors.Gray200), + style = hintTextStyle.copy(color = hintTextColor), ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt index d8b999fb4..ad9d81b39 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/EditTextFieldValue.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.wafflestudio.snutt2.ui.SNUTTColors import com.wafflestudio.snutt2.ui.SNUTTTypography @@ -46,6 +47,8 @@ fun EditTextFieldValue( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, hint: String? = null, + hintTextColor: Color = SNUTTColors.EditTextHint, + hintTextStyle: TextStyle = SNUTTTypography.body1.copy(fontSize = 15.sp), underlineEnabled: Boolean = true, underlineColor: Color = SNUTTColors.Gray200, underlineColorFocused: Color = SNUTTColors.Black900, @@ -90,7 +93,7 @@ fun EditTextFieldValue( hint?.let { Text( text = it, - style = textStyle.copy(color = SNUTTColors.Gray200), + style = hintTextStyle.copy(color = hintTextColor), ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/WebViewStyleButton.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/WebViewStyleButton.kt index 78afe6442..a3136f3e6 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/WebViewStyleButton.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/WebViewStyleButton.kt @@ -3,6 +3,7 @@ package com.wafflestudio.snutt2.components.compose import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -22,8 +23,8 @@ fun WebViewStyleButton( Box( modifier = modifier .clicks { if (enabled) onClick() } - .background(if (enabled) enabledColor else disabledColor) - .height(60.dp), + .background(shape = RoundedCornerShape(6.dp), color = if (enabled) enabledColor else disabledColor) + .height(47.dp), contentAlignment = Alignment.Center, ) { content() diff --git a/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt b/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt index 5714d216c..0f456a043 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/ui/Colors.kt @@ -59,6 +59,8 @@ object SNUTTColors { val VacancyRed = Color(0xffed6c58) val EditTextLabel = Color(0xff8a898e) + val EditTextHint = Color(0xffc4c4c4) + val EditTextUnderline = Color(0xffdcdcde) val Colors.VacancyBlue @Composable get() = if (isLight) Color(0xff446cc2) else Color(0xff7aaaf3) val VacancyBlue @Composable get() = MaterialTheme.colors.VacancyBlue diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt index 049e58cad..9e000fb38 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt @@ -105,6 +105,7 @@ fun CheckIdStep( ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), singleLine = true, + underlineColorFocused = if (buttonEnabled) SNUTTColors.SNUTTTheme else SNUTTColors.EditTextUnderline, ) Spacer(modifier = Modifier.height(48.dp)) @@ -116,7 +117,7 @@ fun CheckIdStep( ) { Text( text = stringResource(R.string.common_ok), - style = SNUTTTypography.h3.copy(color = SNUTTColors.AllWhite), + style = SNUTTTypography.h3.copy(color = if (buttonEnabled) SNUTTColors.AllWhite else SNUTTColors.VacancyGray), ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt index 9a14148e2..b85623d62 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt @@ -21,7 +21,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -132,6 +131,7 @@ fun EnterFullEmailStep( ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), singleLine = true, + underlineColorFocused = if (emailField.text.isBlank()) SNUTTColors.EditTextUnderline else SNUTTColors.SNUTTTheme, ) Spacer(modifier = Modifier.height(12.dp)) @@ -150,7 +150,7 @@ fun EnterFullEmailStep( ) { Text( text = stringResource(R.string.find_password_check_email_enter_full_email_enter), - style = SNUTTTypography.h3.copy(color = SNUTTColors.VacancyGray), + style = SNUTTTypography.h3.copy(color = if (buttonEnabled) SNUTTColors.AllWhite else SNUTTColors.VacancyGray), ) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt index aa74314d7..04a4c61af 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -134,6 +134,7 @@ fun NewPasswordStep( ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), singleLine = true, + underlineColorFocused = if (newPasswordField.isBlank()) SNUTTColors.EditTextUnderline else SNUTTColors.SNUTTTheme, trailingIcon = { Row( modifier = Modifier.padding(start = 10.dp), @@ -175,6 +176,7 @@ fun NewPasswordStep( ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), singleLine = true, + underlineColorFocused = if (newPasswordConfirmField.isBlank()) SNUTTColors.EditTextUnderline else SNUTTColors.SNUTTTheme, ) Spacer(modifier = Modifier.height(48.dp)) @@ -188,7 +190,7 @@ fun NewPasswordStep( ) { Text( text = stringResource(R.string.common_ok), - style = SNUTTTypography.h3.copy(color = SNUTTColors.AllWhite), + style = SNUTTTypography.h3.copy(color = if (buttonEnabled) SNUTTColors.AllWhite else SNUTTColors.VacancyGray), ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt index 7e184fafe..b6b23f632 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt @@ -110,6 +110,7 @@ fun VerifyCodeStep( ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), singleLine = true, + underlineColorFocused = if (codeField.isBlank()) SNUTTColors.EditTextUnderline else SNUTTColors.SNUTTTheme, trailingIcon = { Row( modifier = Modifier.padding(start = 10.dp), @@ -163,7 +164,7 @@ fun VerifyCodeStep( ) { Text( text = stringResource(R.string.common_ok), - style = SNUTTTypography.h3.copy(color = SNUTTColors.AllWhite), + style = SNUTTTypography.h3.copy(color = if (buttonEnabled) SNUTTColors.AllWhite else SNUTTColors.VacancyGray), ) } From f84ea201c2358499aa23aba401de5dfb2de429c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 22:10:03 +0900 Subject: [PATCH 21/27] =?UTF-8?q?feat:=20=ED=83=91=EB=B0=94=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=EB=8C=80=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/compose/IOSStyleTopBar.kt | 80 +++++++++++++++++++ .../reset_password/ResetPasswordPage.kt | 23 +++--- app/src/main/res/values/strings.xml | 4 + 3 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt new file mode 100644 index 000000000..195082397 --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt @@ -0,0 +1,80 @@ +package com.wafflestudio.snutt2.components.compose + +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTypography + +@Composable +fun IOSStyleTopBar( + modifier: Modifier = Modifier, + title: String, + backButtonText: String, + onBack: () -> Unit, +) { + Box( + modifier = modifier + .fillMaxWidth() + .background(color = SNUTTColors.White900) + .height(40.dp) + .drawWithCache { + onDrawWithContent { + drawLine( + color = SNUTTColors.EditTextHint, + start = Offset(0f, this.size.height), end = Offset(this.size.width, this.size.height), + strokeWidth = 0.5.dp.toPx(), + ) + drawContent() + } + } + .padding(horizontal = 5.dp), + contentAlignment = Alignment.CenterStart, + ) { + Row( + modifier = Modifier.clicks(1000L) { onBack() }, + verticalAlignment = Alignment.CenterVertically, + ) { + ArrowBackIcon( + colorFilter = ColorFilter.tint(SNUTTColors.Black900), + ) + AnimatedContent(backButtonText, label = "") { + Text( + text = it, + style = SNUTTTypography.button, + ) + } + } + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text( + text = title, + style = SNUTTTypography.h3, + ) + } + } +} + + +@Preview +@Composable +fun IOSStyleTopBarPreview() { + IOSStyleTopBar( + title = "비밀번호 재설정", + backButtonText = "로그인", + ) { } +} + diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 17a199440..ea2b7ed2b 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.wafflestudio.snutt2.R -import com.wafflestudio.snutt2.components.compose.SimpleTopBar +import com.wafflestudio.snutt2.components.compose.IOSStyleTopBar import com.wafflestudio.snutt2.views.LocalApiOnError import com.wafflestudio.snutt2.views.LocalApiOnProgress import com.wafflestudio.snutt2.views.LocalNavController @@ -49,16 +49,21 @@ fun ResetPasswordPage() { } Column(modifier = Modifier.fillMaxSize()) { - SimpleTopBar( + IOSStyleTopBar( title = stringResource(R.string.find_password_title), - onClickNavigateBack = { - if (uiState is CheckId) { - navController.popBackStack() - } else { - viewModel.goToPreviousStep() - } + backButtonText = when (uiState) { + is CheckId -> stringResource(R.string.find_password_back_login) + is EnterFullEmail -> stringResource(R.string.find_password_back_check_id) + is VerifyCode -> stringResource(R.string.find_password_back_check_email) + is EnterNewPassword -> stringResource(R.string.find_password_back_initial) }, - ) + ) { + if (uiState is CheckId) { + navController.popBackStack() + } else { + viewModel.goToPreviousStep() + } + } AnimatedContent(targetState = uiState, label = "") { state -> when (state) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c40ff395c..4678131bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,6 +284,10 @@ 올바른 형식의 이메일을 입력해주세요. \"%s\"로 아이디가 전송되었습니다. 비밀번호 재설정 + 로그인 + 아이디 입력 + 이메일 입력 + 처음으로 비밀번호 재설정을 위해\n연동된 아이디가 필요합니다. 해당 아이디로 연동된 이메일입니다.\n전체 주소를 입력하여 인증코드를 받으세요. 이메일 From b36691d3aa1ccfa137870fb3e12d0d0c5cd18b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 22:14:42 +0900 Subject: [PATCH 22/27] =?UTF-8?q?feat:=20imePadding=20=EC=9D=84=20Animated?= =?UTF-8?q?Content=20=EB=B0=96=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AnimatedContent 안에 imePadding 을 주면 화면이 출렁인다 --- .../logged_out/reset_password/EnterFullEmailStep.kt | 5 ----- .../views/logged_out/reset_password/NewPasswordStep.kt | 5 ----- .../logged_out/reset_password/ResetPasswordPage.kt | 10 +++++++++- .../views/logged_out/reset_password/VerifyCodeStep.kt | 5 ----- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt index b85623d62..dc0a22635 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt @@ -6,12 +6,9 @@ import androidx.compose.foundation.layout.Spacer 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.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -82,8 +79,6 @@ fun EnterFullEmailStep( Column( modifier = Modifier .fillMaxSize() - .verticalScroll(rememberScrollState()) - .imePadding() .padding(vertical = 44.dp, horizontal = 20.dp), ) { Text( diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt index 04a4c61af..ed90416e0 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -6,12 +6,9 @@ import androidx.compose.foundation.layout.Spacer 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.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -108,8 +105,6 @@ fun NewPasswordStep( Column( modifier = Modifier .fillMaxSize() - .verticalScroll(rememberScrollState()) - .imePadding() .padding(vertical = 44.dp, horizontal = 20.dp), ) { Text( diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index ea2b7ed2b..707897558 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -4,6 +4,9 @@ import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -48,7 +51,12 @@ fun ResetPasswordPage() { } } - Column(modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .imePadding(), + ) { IOSStyleTopBar( title = stringResource(R.string.find_password_title), backButtonText = when (uiState) { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt index b6b23f632..b694de2c8 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt @@ -7,12 +7,9 @@ import androidx.compose.foundation.layout.Spacer 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.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -76,8 +73,6 @@ fun VerifyCodeStep( Column( modifier = Modifier .fillMaxSize() - .verticalScroll(rememberScrollState()) - .imePadding() .padding(vertical = 44.dp, horizontal = 20.dp), ) { Text( From 664c38565819faace1d12594d7d177e5bd7de5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 22:16:29 +0900 Subject: [PATCH 23/27] chore: lint --- .../wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt | 2 -- .../snutt2/views/logged_out/reset_password/CheckIdStep.kt | 1 - .../snutt2/views/logged_out/reset_password/NewPasswordStep.kt | 1 - 3 files changed, 4 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt index 195082397..b03956e7b 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/IOSStyleTopBar.kt @@ -68,7 +68,6 @@ fun IOSStyleTopBar( } } - @Preview @Composable fun IOSStyleTopBarPreview() { @@ -77,4 +76,3 @@ fun IOSStyleTopBarPreview() { backButtonText = "로그인", ) { } } - diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt index 9e000fb38..1dea89040 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt index ed90416e0..aa751515b 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -91,7 +91,6 @@ fun NewPasswordStep( if (newPasswordField != newPasswordConfirmField) { errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_fail_alert) showErrorDialog = true - } else if (newPasswordField.isValidPassword().not()) { errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_validation_fail_alert) showErrorDialog = true From 30466c8492b04e59fe4dc83abe6b725bb8a8e1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 22:35:11 +0900 Subject: [PATCH 24/27] =?UTF-8?q?feat:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=8A=A4=ED=8E=99=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/views/logged_out/reset_password/NewPasswordStep.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt index aa751515b..39fcf5fb8 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -128,6 +128,7 @@ fun NewPasswordStep( ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), singleLine = true, + underlineColor = SNUTTColors.EditTextUnderline, underlineColorFocused = if (newPasswordField.isBlank()) SNUTTColors.EditTextUnderline else SNUTTColors.SNUTTTheme, trailingIcon = { Row( @@ -170,6 +171,7 @@ fun NewPasswordStep( ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), singleLine = true, + underlineColor = SNUTTColors.EditTextUnderline, underlineColorFocused = if (newPasswordConfirmField.isBlank()) SNUTTColors.EditTextUnderline else SNUTTColors.SNUTTTheme, ) From 58e632d0de4bbb99fd89d4e376b914e7b24a2491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 22:46:08 +0900 Subject: [PATCH 25/27] =?UTF-8?q?chore:=20=EB=A1=9C=EB=94=A9=20=EC=9D=B8?= =?UTF-8?q?=EB=94=94=EC=BC=80=EC=9D=B4=ED=84=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 없어도될듯 --- .../snutt2/views/logged_out/reset_password/ResetPasswordPage.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 707897558..38d8f6f59 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -106,7 +106,7 @@ fun ResetPasswordPage() { fullEmail = state.fullEmail, onRequestResend = { scope.launch { - launchSuspendApi(apiOnProgress, apiOnError, loadingIndicatorTitle = context.getString(R.string.loading_indicator_message)) { + launchSuspendApi(apiOnProgress, apiOnError) { viewModel.sendFullEmailAndRequestCode(state.fullEmail) } } From b26a307dfe64a366bc64764c23ce98f2660fd39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 22:56:09 +0900 Subject: [PATCH 26/27] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20LocalSoftwareKeyboardController=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/logged_out/reset_password/NewPasswordStep.kt | 5 ----- .../views/logged_out/reset_password/ResetPasswordPage.kt | 8 -------- 2 files changed, 13 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt index 39fcf5fb8..4a6e61444 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/NewPasswordStep.kt @@ -18,14 +18,12 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager -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.PasswordVisualTransformation @@ -42,7 +40,6 @@ import com.wafflestudio.snutt2.lib.data.SNUTTStringUtils.isValidPassword import com.wafflestudio.snutt2.ui.SNUTTColors import com.wafflestudio.snutt2.ui.SNUTTTypography -@OptIn(ExperimentalComposeUiApi::class) @Composable fun NewPasswordStep( onSubmit: (String) -> Unit, @@ -52,7 +49,6 @@ fun NewPasswordStep( val focusManager = LocalFocusManager.current val focusRequester = remember { FocusRequester() } val context = LocalContext.current - val keyboardManager = LocalSoftwareKeyboardController.current var newPasswordField by remember { mutableStateOf("") } var newPasswordConfirmField by remember { mutableStateOf("") } @@ -95,7 +91,6 @@ fun NewPasswordStep( errorDialogTitle = context.getString(R.string.find_password_enter_password_confirm_validation_fail_alert) showErrorDialog = true } else { - keyboardManager?.hide() onSubmit(newPasswordField) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 38d8f6f59..1f2aa0e72 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -12,10 +12,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -31,14 +28,11 @@ import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewM import com.wafflestudio.snutt2.views.logged_out.reset_password.FindPasswordViewModel.UIState.VerifyCode import kotlinx.coroutines.launch -@OptIn(ExperimentalComposeUiApi::class) @Composable fun ResetPasswordPage() { val scope = rememberCoroutineScope() - val context = LocalContext.current val apiOnProgress = LocalApiOnProgress.current val apiOnError = LocalApiOnError.current - val keyboardManager = LocalSoftwareKeyboardController.current val navController = LocalNavController.current val viewModel = hiltViewModel() val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -81,7 +75,6 @@ fun ResetPasswordPage() { scope.launch { launchSuspendApi(apiOnProgress, apiOnError) { viewModel.checkEmailById(userId) - keyboardManager?.hide() } } }, @@ -96,7 +89,6 @@ fun ResetPasswordPage() { scope.launch { launchSuspendApi(apiOnProgress, apiOnError) { viewModel.sendFullEmailAndRequestCode(fullEmail) - keyboardManager?.hide() } } }, From 889b285d71e1c5d3ebef8ab4a077bdcb679c7f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=A3=BC=ED=98=84?= Date: Sat, 26 Oct 2024 23:41:06 +0900 Subject: [PATCH 27/27] =?UTF-8?q?refactor:=20=ED=95=98=EC=9C=84=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20uiState=20=EB=A7=8C=20?= =?UTF-8?q?=EB=84=98=EA=B8=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/logged_out/reset_password/CheckIdStep.kt | 6 +++--- .../logged_out/reset_password/EnterFullEmailStep.kt | 12 +++++------- .../logged_out/reset_password/ResetPasswordPage.kt | 8 +++----- .../logged_out/reset_password/VerifyCodeStep.kt | 4 ++-- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt index 1dea89040..3ce8b611c 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/CheckIdStep.kt @@ -36,7 +36,7 @@ import com.wafflestudio.snutt2.ui.SNUTTTypography @Composable fun CheckIdStep( - userId: String, + uiState: FindPasswordViewModel.UIState.CheckId, onSubmit: (String) -> Unit, ) { val focusRequester = remember { FocusRequester() } @@ -45,8 +45,8 @@ fun CheckIdStep( var idField by remember { mutableStateOf( TextFieldValue( - text = userId, - selection = TextRange(userId.length), // 초기 커서를 텍스트 끝으로 설정 + text = uiState.userId, + selection = TextRange(uiState.userId.length), // 초기 커서를 텍스트 끝으로 설정 ), ) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt index dc0a22635..8c3fdb941 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/EnterFullEmailStep.kt @@ -39,9 +39,7 @@ import com.wafflestudio.snutt2.ui.SNUTTTypography @Composable fun EnterFullEmailStep( - userId: String, - maskedEmail: String, - fullEmail: String, + uiState: FindPasswordViewModel.UIState.EnterFullEmail, notMyEmail: () -> Unit, onSubmitFullEmail: (String) -> Unit, ) { @@ -51,8 +49,8 @@ fun EnterFullEmailStep( var emailField by remember { mutableStateOf( TextFieldValue( - text = fullEmail, - selection = TextRange(fullEmail.length), // 초기 커서를 텍스트 끝으로 설정 + text = uiState.fullEmail, + selection = TextRange(uiState.fullEmail.length), // 초기 커서를 텍스트 끝으로 설정 ), ) } @@ -99,7 +97,7 @@ fun EnterFullEmailStep( modifier = Modifier.fillMaxWidth(), value = "", onValueChange = {}, - hint = userId, + hint = uiState.userId, enabled = false, ) @@ -132,7 +130,7 @@ fun EnterFullEmailStep( Spacer(modifier = Modifier.height(12.dp)) Text( - text = maskedEmail, + text = uiState.maskedEmail, style = SNUTTTypography.body1.copy(color = SNUTTColors.EditTextLabel), ) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt index 1f2aa0e72..73d9e9d74 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/ResetPasswordPage.kt @@ -70,7 +70,7 @@ fun ResetPasswordPage() { AnimatedContent(targetState = uiState, label = "") { state -> when (state) { is CheckId -> CheckIdStep( - userId = state.userId, + uiState = state, onSubmit = { userId -> scope.launch { launchSuspendApi(apiOnProgress, apiOnError) { @@ -81,9 +81,7 @@ fun ResetPasswordPage() { ) is EnterFullEmail -> EnterFullEmailStep( - userId = state.userId, - maskedEmail = state.maskedEmail, - fullEmail = state.fullEmail, + uiState = state, notMyEmail = viewModel::goToPreviousStep, onSubmitFullEmail = { fullEmail -> scope.launch { @@ -95,7 +93,7 @@ fun ResetPasswordPage() { ) is VerifyCode -> VerifyCodeStep( - fullEmail = state.fullEmail, + uiState = state, onRequestResend = { scope.launch { launchSuspendApi(apiOnProgress, apiOnError) { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt index b694de2c8..8a118454a 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_out/reset_password/VerifyCodeStep.kt @@ -42,7 +42,7 @@ import com.wafflestudio.snutt2.ui.SNUTTTypography @OptIn(ExperimentalComposeUiApi::class) @Composable fun VerifyCodeStep( - fullEmail: String, + uiState: FindPasswordViewModel.UIState.VerifyCode, onRequestResend: () -> Unit, onSubmit: (String) -> Unit, ) { @@ -76,7 +76,7 @@ fun VerifyCodeStep( .padding(vertical = 44.dp, horizontal = 20.dp), ) { Text( - text = stringResource(R.string.find_password_verification_code_content, fullEmail), + text = stringResource(R.string.find_password_verification_code_content, uiState.fullEmail), style = SNUTTTypography.h2.copy(fontSize = 17.sp), )