diff --git a/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordContract.kt b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordContract.kt new file mode 100644 index 0000000..43e978f --- /dev/null +++ b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordContract.kt @@ -0,0 +1,12 @@ +package org.sopt.mypage.modifypassword + +data class ModifyPasswordState( + val previousPassword: String = "", + val newPassword: String = "", + val newPasswordVerification: String = "", +) + +sealed interface ModifyPasswordSideEffect { + data class ShowSnackbar(val msg: String) : ModifyPasswordSideEffect + data object ModifySuccess : ModifyPasswordSideEffect +} \ No newline at end of file diff --git a/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordScreen.kt b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordScreen.kt new file mode 100644 index 0000000..769660e --- /dev/null +++ b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordScreen.kt @@ -0,0 +1,146 @@ +package org.sopt.mypage.modifypassword + +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.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import kotlinx.coroutines.launch +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect +import org.sopt.designsystem.component.button.RegularButton +import org.sopt.designsystem.component.textfield.RegularTextField + +@Composable +fun ModifyPasswordRoute( + modifier: Modifier = Modifier, + viewModel: ModifyPasswordViewModel = hiltViewModel(), + navigateMypage: () -> Unit, +) { + val state by viewModel.collectAsState() + val snackBarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + viewModel.collectSideEffect { sideEffect -> + when (sideEffect) { + ModifyPasswordSideEffect.ModifySuccess -> navigateMypage() + is ModifyPasswordSideEffect.ShowSnackbar -> { + scope.launch { + snackBarHostState.currentSnackbarData?.dismiss() + snackBarHostState.showSnackbar( + sideEffect.msg, + duration = SnackbarDuration.Short + ) + } + } + } + } + + ModifyPasswordScreen( + modifier = modifier, + snackbarHostState = snackBarHostState, + state = state, + onClickModifyButton = viewModel::modifyPassword, + onValueChangePreviousPassword = viewModel::updatePreviousPassword, + onValueChangeNewPassword = viewModel::updateNewPassword, + onValueChangeNewPasswordVerification = viewModel::updateNewPasswordVerification + ) +} + +@Composable +fun ModifyPasswordScreen( + modifier: Modifier = Modifier, + snackbarHostState: SnackbarHostState, + state: ModifyPasswordState, + onClickModifyButton: () -> Unit, + onValueChangePreviousPassword: (String) -> Unit, + onValueChangeNewPassword: (String) -> Unit, + onValueChangeNewPasswordVerification: (String) -> Unit, +) { + Scaffold( + modifier = modifier, + snackbarHost = { SnackbarHost(hostState = snackbarHostState) } + ) { + Box( + modifier = Modifier + .fillMaxSize() + .padding( + top = 50.dp + it.calculateTopPadding(), + bottom = 20.dp + it.calculateBottomPadding(), + start = 20.dp, + end = 20.dp + ) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Text( + text = "비밀번호 변경", + style = MaterialTheme.typography.headlineMedium, + color = Color.Black, + ) + } + + Spacer(modifier = Modifier.height(70.dp)) + + RegularTextField( + modifier = Modifier.padding(end = 20.dp), + textStyle = MaterialTheme.typography.headlineSmall, + value = state.previousPassword, + placeholder = "현재 비밀번호", + onValueChange = onValueChangePreviousPassword + ) + + Spacer(modifier = Modifier.height(70.dp)) + + RegularTextField( + modifier = Modifier.padding(end = 20.dp), + textStyle = MaterialTheme.typography.headlineSmall, + value = state.newPassword, + placeholder = "변경할 비밀번호", + onValueChange = onValueChangeNewPassword + ) + + Spacer(modifier = Modifier.height(70.dp)) + + RegularTextField( + modifier = Modifier.padding(end = 20.dp), + textStyle = MaterialTheme.typography.headlineSmall, + value = state.newPasswordVerification, + placeholder = "비밀번호 확인", + onValueChange = onValueChangeNewPasswordVerification + ) + + Spacer(modifier = Modifier.weight(2f)) + + RegularButton( + text = "비밀번호 변경", + clickable = true, + onClick = onClickModifyButton + ) + } + } + } +} \ No newline at end of file diff --git a/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordViewModel.kt b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordViewModel.kt new file mode 100644 index 0000000..053a3cc --- /dev/null +++ b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/ModifyPasswordViewModel.kt @@ -0,0 +1,53 @@ +package org.sopt.mypage.modifypassword + +import android.util.Log +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.viewmodel.container +import org.sopt.domain.repo.AuthRepository +import org.sopt.ui.orbit.updateState +import javax.inject.Inject + +@HiltViewModel +class ModifyPasswordViewModel @Inject constructor( + private val authRepository: AuthRepository, +) : ContainerHost, ViewModel() { + override val container: Container = container( + ModifyPasswordState() + ) + + fun modifyPassword() = intent { + authRepository.patchPassword( + state.previousPassword, + state.newPassword, + state.newPasswordVerification + ) + .onSuccess { + if (it.code !in 200..299) { + postSideEffect(ModifyPasswordSideEffect.ShowSnackbar(it.message)) + } else { + postSideEffect(ModifyPasswordSideEffect.ModifySuccess) + } + } + .onFailure { + Log.e("error", it.message.toString()) + } + } + + fun updatePreviousPassword(query: String) = updateState { + copy(previousPassword = query) + } + + fun updateNewPassword(query: String) = updateState { + copy(newPassword = query) + } + + fun updateNewPasswordVerification(query: String) = updateState { + copy(newPasswordVerification = query) + } + +} \ No newline at end of file diff --git a/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/navigation/ModifyPasswordNavigation.kt b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/navigation/ModifyPasswordNavigation.kt new file mode 100644 index 0000000..e83a93b --- /dev/null +++ b/feature/mypage/src/main/java/org/sopt/mypage/modifypassword/navigation/ModifyPasswordNavigation.kt @@ -0,0 +1,27 @@ +package org.sopt.mypage.modifypassword.navigation + +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import org.sopt.mypage.modifypassword.ModifyPasswordRoute + +fun NavController.navigateModifyPassword() { + navigate(ModifyPasswordRoute.route) +} + +fun NavGraphBuilder.modifyPasswordNavGraph( + modifier: Modifier = Modifier, + navigateMypage: () -> Unit = {}, +) { + composable(route = ModifyPasswordRoute.route) { navBackStackEntry -> + ModifyPasswordRoute( + modifier = modifier, + navigateMypage = navigateMypage + ) + } +} + +object ModifyPasswordRoute { + const val route = "modifyPassword" +} \ No newline at end of file