diff --git a/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/authentication/RegistrationUseCase.kt b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/authentication/RegistrationUseCase.kt new file mode 100644 index 00000000..0508574b --- /dev/null +++ b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/authentication/RegistrationUseCase.kt @@ -0,0 +1,29 @@ +package ac.dnd.bookkeeping.android.domain.usecase.authentication + +import ac.dnd.bookkeeping.android.domain.model.authentication.Register +import ac.dnd.bookkeeping.android.domain.repository.AuthenticationRepository +import javax.inject.Inject + +class RegistrationUseCase @Inject constructor( + private val authenticationRepository: AuthenticationRepository +) { + suspend operator fun invoke( + socialId: Long, + email: String, + profileImageUrl: String, + name: String, + nickname: String, + gender: String, + birth: String + ): Result { + return authenticationRepository.register( + socialId = socialId, + email = email, + profileImageUrl = profileImageUrl, + name = name, + nickname = nickname, + gender = gender, + birth = birth + ) + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainViewModel.kt index f63a8a0e..3a501342 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainViewModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainViewModel.kt @@ -37,9 +37,7 @@ class LoginMainViewModel @Inject constructor( fun onIntent(intent: LoginMainIntent) { when (intent) { - LoginMainIntent.Click -> { - loginFlow() - } + LoginMainIntent.Click -> loginFlow() } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingDestination.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingDestination.kt index 52a61d45..14b4f020 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingDestination.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingDestination.kt @@ -20,9 +20,13 @@ fun NavGraphBuilder.loginOnBoardingDestination( ?.savedStateHandle ?.get(LoginOnBoardingConstant.ROURE_ARGUMENT_USER_MODEL) ?: KakaoUserInformationModel(0L, "", "", "") - if (userModel.email.isEmpty()) appState.navController.popBackStack() val viewModel: LoginOnBoardingViewModel = hiltViewModel() + if (userModel.email.isEmpty()) { + appState.navController.popBackStack() + } else { + viewModel.initKakaoUserInfo(userModel) + } val model: LoginOnBoardingModel = let { val state by viewModel.state.collectAsStateWithLifecycle() diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt index 2a31644a..4bbcfa29 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt @@ -1,3 +1,9 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login.onboarding -sealed interface LoginOnBoardingEvent +import ac.dnd.bookkeeping.android.presentation.model.login.KakaoUserInformationModel + +sealed interface LoginOnBoardingEvent { + data class Submit( + val kakaoUserModel: KakaoUserInformationModel + ) : LoginOnBoardingEvent +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt index 07c09dae..324e69d3 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt @@ -1,3 +1,5 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login.onboarding -sealed interface LoginOnBoardingIntent +sealed interface LoginOnBoardingIntent { + data object Click : LoginOnBoardingIntent +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt index 02797ade..2d49358a 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt @@ -9,9 +9,9 @@ import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButton import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonProperties import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonSize import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonType +import ac.dnd.bookkeeping.android.presentation.model.login.KakaoUserInformationModel import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState -import ac.dnd.bookkeeping.android.presentation.ui.main.home.HomeConstant -import ac.dnd.bookkeeping.android.presentation.ui.main.login.LoginConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.RegistrationMainConstant import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.ExperimentalFoundationApi @@ -43,6 +43,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.navigation.NavHostController import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.launch import kotlinx.coroutines.plus @@ -61,13 +62,10 @@ fun LoginOnBoardingScreen( pageCount = { 3 } ) - fun navigateToHome() { - appState.navController.navigate(HomeConstant.ROUTE) { - popUpTo(LoginConstant.ROUTE) { - inclusive = true - } - } + fun navigateToRegistration(kakaoUserModel: KakaoUserInformationModel) { + appState.navController.sendKakaoUserModel(kakaoUserModel) } + Box( modifier = Modifier .fillMaxSize() @@ -125,7 +123,7 @@ fun LoginOnBoardingScreen( .align(Alignment.BottomCenter) .padding(25.dp), onClick = { - navigateToHome() + intent(LoginOnBoardingIntent.Click) } ) { style -> Text( @@ -137,7 +135,11 @@ fun LoginOnBoardingScreen( LaunchedEffectWithLifecycle(event, handler) { event.eventObserve { event -> - + when (event) { + is LoginOnBoardingEvent.Submit -> { + navigateToRegistration(event.kakaoUserModel) + } + } } } } @@ -162,6 +164,16 @@ private fun LoginOnBoardingPage( } } +private fun NavHostController.sendKakaoUserModel(kakaoUserModel: KakaoUserInformationModel) { + currentBackStackEntry?.savedStateHandle?.apply { + set( + key = RegistrationMainConstant.ROURE_ARGUMENT_USER_MODEL, + value = kakaoUserModel + ) + } + navigate(RegistrationMainConstant.CONTAIN_USER_MODEL) +} + @Preview @Composable fun LoginOnBoardingScreenPreview() { diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt index e937c3df..4f64443e 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt @@ -4,6 +4,7 @@ import ac.dnd.bookkeeping.android.presentation.common.base.BaseViewModel import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.asEventFlow +import ac.dnd.bookkeeping.android.presentation.model.login.KakaoUserInformationModel import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -23,7 +24,21 @@ class LoginOnBoardingViewModel @Inject constructor( private val _event: MutableEventFlow = MutableEventFlow() val event: EventFlow = _event.asEventFlow() - fun onIntent(intent: LoginOnBoardingIntent) { + private val _kakaoUserInfo: MutableStateFlow = + MutableStateFlow(KakaoUserInformationModel(0L, "", "", "")) + private val kakaoUserInfo: StateFlow = _kakaoUserInfo.asStateFlow() + + fun initKakaoUserInfo(userModel: KakaoUserInformationModel) { + _kakaoUserInfo.value = userModel + } + fun onIntent(intent: LoginOnBoardingIntent) { + when (intent) { + LoginOnBoardingIntent.Click -> { + launch { + _event.emit(LoginOnBoardingEvent.Submit(kakaoUserInfo.value)) + } + } + } } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainConstant.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainConstant.kt index 419ccc1e..62c75ce2 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainConstant.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainConstant.kt @@ -4,4 +4,7 @@ import ac.dnd.bookkeeping.android.presentation.ui.main.registration.Registration object RegistrationMainConstant { const val ROUTE: String = "${RegistrationConstant.ROUTE}/main" + + const val ROURE_ARGUMENT_USER_MODEL = "kakaoUserModel" + const val CONTAIN_USER_MODEL = "${ROUTE}/{${ROURE_ARGUMENT_USER_MODEL}}" } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt index 68db05ba..0efb5594 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt @@ -1,11 +1,10 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver +import ac.dnd.bookkeeping.android.presentation.model.login.KakaoUserInformationModel import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState -import ac.dnd.bookkeeping.android.presentation.ui.main.registration.RegistrationConstant -import ac.dnd.bookkeeping.android.presentation.ui.main.registration.RegistrationViewModel +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraphBuilder @@ -15,20 +14,30 @@ fun NavGraphBuilder.registrationNamingDestination( appState: ApplicationState ) { composable( - route = RegistrationMainConstant.ROUTE - ) { backStackEntry -> - val parentEntry = remember(backStackEntry) { - appState.navController.getBackStackEntry(RegistrationConstant.ROUTE) - } - val parentViewModel: RegistrationViewModel = hiltViewModel(parentEntry) + route = RegistrationMainConstant.CONTAIN_USER_MODEL + ) { + + val userModel = appState.navController.previousBackStackEntry + ?.savedStateHandle + ?.get(RegistrationMainConstant.ROURE_ARGUMENT_USER_MODEL) + ?: KakaoUserInformationModel(0L, "", "", "") + val viewModel: RegistrationMainViewModel = hiltViewModel() + if (userModel.email.isEmpty()) { + appState.navController.popBackStack() + } else { + LaunchedEffect(Unit) { + viewModel.initKakaoUserInfo(userModel) + } + } + val model: RegistrationMainModel = let { val state by viewModel.state.collectAsStateWithLifecycle() - val errorType by viewModel.errorType.collectAsStateWithLifecycle() + val namingErrorType by viewModel.namingErrorType.collectAsStateWithLifecycle() RegistrationMainModel( state = state, - errorType = errorType + namingErrorType = namingErrorType ) } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainErrorType.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainErrorType.kt deleted file mode 100644 index 2ccf924a..00000000 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainErrorType.kt +++ /dev/null @@ -1,9 +0,0 @@ -package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main - -sealed interface RegistrationMainErrorType { - data object Init : RegistrationMainErrorType - sealed interface InValid : RegistrationMainErrorType { - data object Duplication : InValid - data object NumberOutOfRange : InValid - } -} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainEvent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainEvent.kt index be65d179..59807dbd 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainEvent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainEvent.kt @@ -1,5 +1,7 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main +import ac.dnd.bookkeeping.android.domain.model.error.ServerException + sealed interface RegistrationMainEvent { sealed interface Check : RegistrationMainEvent { @@ -9,7 +11,7 @@ sealed interface RegistrationMainEvent { sealed interface Submit : RegistrationMainEvent { data object Success : Submit - data object Failure : Submit - data object Error : Submit + data class Failure(val exception: ServerException) : Submit + data class Error(val exception: Throwable) : Submit } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainIntent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainIntent.kt index 158d2974..4d5e3fc9 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainIntent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainIntent.kt @@ -1,6 +1,10 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main sealed interface RegistrationMainIntent { - data object OnCheckUserName : RegistrationMainIntent - data object OnClickSubmit : RegistrationMainIntent + data class OnCheckUserName(val name: String) : RegistrationMainIntent + data class OnClickSubmit( + val nickName: String, + val gender: String, + val birth: String + ) : RegistrationMainIntent } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainModel.kt index f9a1675f..7265bfe6 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainModel.kt @@ -1,6 +1,8 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main +import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.type.RegistrationMainNamingErrorType + data class RegistrationMainModel( val state: RegistrationMainState, - val errorType: RegistrationMainErrorType + val namingErrorType: RegistrationMainNamingErrorType ) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainScreen.kt index 5bcc997e..ef7d4d3c 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainScreen.kt @@ -2,27 +2,34 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main import ac.dnd.bookkeeping.android.presentation.R import ac.dnd.bookkeeping.android.presentation.common.theme.Body1 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray000 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray200 import ac.dnd.bookkeeping.android.presentation.common.theme.Gray400 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray500 import ac.dnd.bookkeeping.android.presentation.common.theme.Gray600 -import ac.dnd.bookkeeping.android.presentation.common.theme.Negative +import ac.dnd.bookkeeping.android.presentation.common.theme.Headline3 +import ac.dnd.bookkeeping.android.presentation.common.theme.Primary1 +import ac.dnd.bookkeeping.android.presentation.common.theme.Primary4 +import ac.dnd.bookkeeping.android.presentation.common.theme.Primary5 +import ac.dnd.bookkeeping.android.presentation.common.theme.Shapes import ac.dnd.bookkeeping.android.presentation.common.theme.Space12 import ac.dnd.bookkeeping.android.presentation.common.theme.Space20 import ac.dnd.bookkeeping.android.presentation.common.theme.Space32 -import ac.dnd.bookkeeping.android.presentation.common.theme.Space48 -import ac.dnd.bookkeeping.android.presentation.common.theme.Space8 import ac.dnd.bookkeeping.android.presentation.common.util.LaunchedEffectWithLifecycle import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.eventObserve import ac.dnd.bookkeeping.android.presentation.common.util.expansion.addFocusCleaner -import ac.dnd.bookkeeping.android.presentation.common.view.CustomTextField -import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButton -import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonProperties -import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonSize -import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonType +import ac.dnd.bookkeeping.android.presentation.common.view.DialogScreen +import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingTextField +import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingTextFieldType import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState +import ac.dnd.bookkeeping.android.presentation.ui.main.home.HomeConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.login.LoginConstant import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.component.ErrorUserNamingComponent import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.component.RegistraionUserDate +import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.type.RegistrationMainNamingErrorType +import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.type.UserGender import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.BorderStroke @@ -42,26 +49,28 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.material.Icon +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults import androidx.compose.material.IconButton -import androidx.compose.material.Surface import androidx.compose.material.Text 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.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.plus @Composable fun RegistrationNamingScreen( @@ -71,44 +80,59 @@ fun RegistrationNamingScreen( intent: (RegistrationMainIntent) -> Unit, handler: CoroutineExceptionHandler ) { + val scope = rememberCoroutineScope() + handler val focusManager = LocalFocusManager.current - var userNameText by remember { mutableStateOf(TextFieldValue("")) } - var userYearText by remember { mutableStateOf(TextFieldValue("")) } - var userMonthText by remember { mutableStateOf(TextFieldValue("")) } - var userDayText by remember { mutableStateOf(TextFieldValue("")) } - var isUserMale by remember { mutableStateOf(true) } - val userNameColorState = animateColorAsState( - targetValue = when (model.errorType) { - RegistrationMainErrorType.Init -> Gray400 - is RegistrationMainErrorType.InValid -> Negative - }, - label = "userName color type" + var isDialogShowing by remember { mutableStateOf(false) } + var isUserNameInValid by remember { mutableStateOf(false) } + var userNameText by remember { mutableStateOf("") } + var userYearText by remember { mutableStateOf("") } + var userMonthText by remember { mutableStateOf("") } + var userDayText by remember { mutableStateOf("") } + var userGender by remember { mutableStateOf(UserGender.NORMAL) } + var buttonClickState by remember { mutableStateOf(false) } + var checkNonDuplicationState by remember { mutableStateOf(false) } + val registrationButtonColorState = animateColorAsState( + targetValue = checkColorState( + buttonClickState = buttonClickState, + nameValid = checkNonDuplicationState && !isUserNameInValid, + gender = userGender, + year = userYearText, + month = userMonthText, + day = userDayText + ), + label = "registration button state" ) + fun navigateToHome() { + appState.navController.navigate(HomeConstant.ROUTE) { + popUpTo(RegistrationMainConstant.CONTAIN_USER_MODEL) { + inclusive = true + } + } + } + fun applyValidation(event: RegistrationMainEvent.Check) { when (event) { is RegistrationMainEvent.Check.Valid -> { - + checkNonDuplicationState = true } - is RegistrationMainEvent.Check.Invalid -> { - - } + is RegistrationMainEvent.Check.Invalid -> {} } } fun submit(event: RegistrationMainEvent.Submit) { when (event) { is RegistrationMainEvent.Submit.Success -> { - // TODO : Implement Success Case + navigateToHome() } is RegistrationMainEvent.Submit.Error -> { - // TODO : Implement Request Error Case + isDialogShowing = true } is RegistrationMainEvent.Submit.Failure -> { - // TODO : Implement Internal Server Error Case + isDialogShowing = true } } } @@ -164,66 +188,80 @@ fun RegistrationNamingScreen( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(Space12) ) { - Surface( - modifier = Modifier.weight(1f), - color = Color.Transparent - ) { - CustomTextField( - text = userNameText.text, - onTextChange = { - userNameText = TextFieldValue(it) + Box(modifier = Modifier.weight(1f)) { + TypingTextField( + textType = TypingTextFieldType.Basic, + text = userNameText, + hintText = "닉네임 입력 (15자 이내)", + onValueChange = { + userNameText = it + isUserNameInValid = userNameText.length >= 15 }, - height = Space48, - elevation = 0.dp, - cornerBorder = BorderStroke( - width = 1.dp, - color = userNameColorState.value - ), - modifier = Modifier.background(Color.White), - contentInnerPadding = PaddingValues(horizontal = 16.dp), + isEnabled = !checkNonDuplicationState, + isError = isUserNameInValid, + modifier = Modifier, trailingIconContent = { - if (userNameText.text.isNotEmpty()) { - IconButton( - onClick = { userNameText = TextFieldValue() } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_close), - contentDescription = "close icon", + if (userNameText.isNotEmpty()) { + if (isUserNameInValid) { + Image( + painter = painterResource(R.drawable.ic_warning), + contentDescription = null, + modifier = Modifier.size(Space20) ) + } else if (checkNonDuplicationState) { + Image( + painter = painterResource(R.drawable.ic_check_circle), + contentDescription = null, + modifier = Modifier.size(Space20) + ) + } else { + IconButton( + onClick = { + userNameText = "" + } + ) { + Image( + painter = painterResource(id = R.drawable.ic_close_circle), + contentDescription = "close icon", + modifier = Modifier.size(Space20) + ) + } } } }, - hintTextContent = { - Text( - text = stringResource(R.string.input_user_name), - style = Body1.merge(color = Gray600) - ) + errorMessageContent = { + if (isUserNameInValid) { + ErrorUserNamingComponent(RegistrationMainNamingErrorType.InValid.NumberOutOfRange) + } else { + ErrorUserNamingComponent(model.namingErrorType) + } } ) } - ConfirmButton( - modifier = Modifier.wrapContentWidth(), - properties = ConfirmButtonProperties( - size = ConfirmButtonSize.Large, - type = ConfirmButtonType.Secondary + Button( + modifier = Modifier + .height(48.dp) + .wrapContentWidth(), + shape = Shapes.large, + colors = ButtonDefaults.textButtonColors( + backgroundColor = if (checkNonDuplicationState) Gray500 else Gray200 ), + contentPadding = PaddingValues(horizontal = 13.dp), + enabled = !isUserNameInValid && !checkNonDuplicationState, onClick = { - //TODO : check user + intent(RegistrationMainIntent.OnCheckUserName(userNameText)) }, - content = { style -> - Text( - text = stringResource(R.string.confirm_button_check_duplication), - style = style.merge( - color = Gray600, - fontWeight = FontWeight.SemiBold - ) + elevation = null + ) { + Text( + text = if (checkNonDuplicationState) "확인 완료" else "중복 확인", + style = Body1.merge( + color = if (checkNonDuplicationState) Gray000 else Gray600, + fontWeight = FontWeight.SemiBold ) - } - ) + ) + } } - Spacer(Modifier.height(Space8)) - - ErrorUserNamingComponent(model.errorType) Spacer(Modifier.height(Space32)) RegistraionUserDate( @@ -237,9 +275,10 @@ fun RegistrationNamingScreen( onUserMonthTextChange = { newText -> userMonthText = newText }, - ) { newText -> - userDayText = newText - } + onUserDayTextChange = { newText -> + userDayText = newText + } + ) Spacer(modifier = Modifier.height(32.dp)) Text( @@ -251,55 +290,112 @@ fun RegistrationNamingScreen( ) Spacer(modifier = Modifier.height(12.dp)) Row { - ConfirmButton( - modifier = Modifier.weight(1f), - properties = ConfirmButtonProperties( - size = ConfirmButtonSize.Large, - type = ConfirmButtonType.Outline + Button( + modifier = Modifier + .weight(1f) + .height(48.dp), + shape = Shapes.large, + colors = ButtonDefaults.textButtonColors( + backgroundColor = if (userGender == UserGender.MALE) Primary1 else Gray000 + ), + border = BorderStroke( + color = if (userGender == UserGender.MALE) Primary4 else Gray400, + width = 1.dp, ), - content = { style -> - Text(text = "남자", style = style) - }, onClick = { - isUserMale = true + userGender = UserGender.MALE }, - ) + elevation = null + ) { + Text( + text = "남자", + style = Body1.merge(if (userGender == UserGender.MALE) Primary4 else Gray600) + ) + } Spacer(modifier = Modifier.width(12.dp)) - ConfirmButton( - modifier = Modifier.weight(1f), - properties = ConfirmButtonProperties( - size = ConfirmButtonSize.Large, - type = ConfirmButtonType.Outline + Button( + modifier = Modifier.weight(1f).height(48.dp), + shape = Shapes.large, + colors = ButtonDefaults.textButtonColors( + backgroundColor = if (userGender == UserGender.FEMALE) Primary1 else Gray000 + ), + border = BorderStroke( + color = if (userGender == UserGender.FEMALE) Primary4 else Gray400, + width = 1.dp, ), - content = { style -> - Text(text = "여자", style = style) - }, onClick = { - isUserMale = false - } - ) + userGender = UserGender.FEMALE + }, + elevation = null + ) { + Text( + text = "여자", + style = Body1.merge(if (userGender == UserGender.FEMALE) Primary4 else Gray600) + ) + } } } - ConfirmButton( + Box( modifier = Modifier .align(Alignment.BottomCenter) - .fillMaxWidth() .padding( horizontal = 20.dp, vertical = 12.dp + ) + ) { + Button( + modifier = Modifier + .height(52.dp) + .fillMaxWidth(), + shape = Shapes.large, + colors = ButtonDefaults.textButtonColors( + backgroundColor = registrationButtonColorState.value + ), + border = BorderStroke( + color = if (userGender == UserGender.MALE) Primary4 else Gray400, + width = 1.dp, ), - properties = ConfirmButtonProperties( - size = ConfirmButtonSize.Xlarge, - type = ConfirmButtonType.Primary - ), - content = { style -> - Text(text = "다음", style = style) + enabled = registrationButtonColorState.value == Primary4, + onClick = { + scope.launch { + buttonClickState = true + delay(200L) + intent( + RegistrationMainIntent.OnClickSubmit( + nickName = userNameText, + gender = if (userGender == UserGender.MALE) "male" else "female", + birth = listOf( + userYearText, + userMonthText, + userDayText + ).joinToString("-") + ) + ) + } + }, + elevation = null + ) { + Text( + text = "시작하기", + style = Headline3 + .merge( + color = Gray000, + fontWeight = FontWeight.SemiBold + ) + ) } - ) - + } } + DialogScreen( + isShowing = isDialogShowing, + title = "회원 가입에 실패하였습니다.", + onDismissRequest = { + isDialogShowing = false + } + ) + LaunchedEffectWithLifecycle(event, handler) { event.eventObserve { event -> when (event) { @@ -310,6 +406,18 @@ fun RegistrationNamingScreen( } } +private fun checkColorState( + buttonClickState: Boolean, + nameValid: Boolean, + gender: UserGender, + year: String, + month: String, + day: String +): Color = + if (buttonClickState) Primary5 + else if (nameValid && gender != UserGender.NORMAL && year.length == 4 && month.length == 2 && day.length == 2) Primary4 + else Gray400 + @Preview @Composable fun RegistrationNamingScreenPreview() { @@ -317,7 +425,7 @@ fun RegistrationNamingScreenPreview() { appState = rememberApplicationState(), model = RegistrationMainModel( state = RegistrationMainState.Init, - errorType = RegistrationMainErrorType.Init + namingErrorType = RegistrationMainNamingErrorType.Init ), event = MutableEventFlow(), intent = {}, diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainViewModel.kt index 4f68c498..f85c3f05 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainViewModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainViewModel.kt @@ -1,9 +1,14 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main +import ac.dnd.bookkeeping.android.domain.model.error.ServerException +import ac.dnd.bookkeeping.android.domain.usecase.authentication.RegistrationUseCase +import ac.dnd.bookkeeping.android.domain.usecase.member.CheckNicknameUseCase import ac.dnd.bookkeeping.android.presentation.common.base.BaseViewModel import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.asEventFlow +import ac.dnd.bookkeeping.android.presentation.model.login.KakaoUserInformationModel +import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.type.RegistrationMainNamingErrorType import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -13,6 +18,8 @@ import javax.inject.Inject @HiltViewModel class RegistrationMainViewModel @Inject constructor( + private val checkNicknameUseCase: CheckNicknameUseCase, + private val registrationUseCase: RegistrationUseCase, private val savedStateHandle: SavedStateHandle, ) : BaseViewModel() { @@ -20,29 +27,82 @@ class RegistrationMainViewModel @Inject constructor( MutableStateFlow(RegistrationMainState.Init) val state: StateFlow = _state.asStateFlow() - private val _errorType: MutableStateFlow = - MutableStateFlow(RegistrationMainErrorType.Init) - val errorType: StateFlow = _errorType.asStateFlow() + private val _namingErrorType: MutableStateFlow = + MutableStateFlow(RegistrationMainNamingErrorType.Init) + val namingErrorType: StateFlow = _namingErrorType.asStateFlow() private val _event: MutableEventFlow = MutableEventFlow() val event: EventFlow = _event.asEventFlow() + private val _kakaoUserInfo: MutableStateFlow = + MutableStateFlow(KakaoUserInformationModel(0L, "", "", "")) + private val kakaoUserInfo: StateFlow = _kakaoUserInfo.asStateFlow() + fun onIntent(intent: RegistrationMainIntent) { when (intent) { - RegistrationMainIntent.OnCheckUserName -> checkUserNameValid() - RegistrationMainIntent.OnClickSubmit -> goToNextStep() + is RegistrationMainIntent.OnCheckUserName -> checkUserNameValid(intent.name) + is RegistrationMainIntent.OnClickSubmit -> { + registerUser( + nickName = intent.nickName, + gender = intent.gender, + birth = intent.birth + ) + } } } - private fun checkUserNameValid() { - launch { + fun initKakaoUserInfo(userModel: KakaoUserInformationModel) { + _kakaoUserInfo.value = userModel + } + private fun checkUserNameValid(name: String) { + launch { + checkNicknameUseCase(name) + .onSuccess { + if (it) { + _event.emit(RegistrationMainEvent.Check.Valid) + } else { + _event.emit(RegistrationMainEvent.Check.Invalid) + _namingErrorType.value = RegistrationMainNamingErrorType.InValid.Duplication + } + } + .onFailure { + _namingErrorType.value = RegistrationMainNamingErrorType.Init + } } } - private fun goToNextStep() { + private fun registerUser( + nickName: String, + gender: String, + birth: String + ) { launch { + kakaoUserInfo.value.let { userModel -> + registrationUseCase( + socialId = userModel.socialId, + email = userModel.email, + profileImageUrl = userModel.profileImageUrl, + name = userModel.name, + nickname = nickName, + gender = gender, + birth = birth + ) + .onSuccess { + _event.emit(RegistrationMainEvent.Submit.Success) + } + .onFailure { exception -> + when (exception) { + is ServerException -> { + _event.emit(RegistrationMainEvent.Submit.Failure(exception)) + } + else -> { + _event.emit(RegistrationMainEvent.Submit.Error(exception)) + } + } + } + } } } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/ErrorUserNamingComponent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/ErrorUserNamingComponent.kt index e46119ad..80e873ba 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/ErrorUserNamingComponent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/ErrorUserNamingComponent.kt @@ -4,7 +4,7 @@ import ac.dnd.bookkeeping.android.presentation.R import ac.dnd.bookkeeping.android.presentation.common.theme.Body1 import ac.dnd.bookkeeping.android.presentation.common.theme.Negative import ac.dnd.bookkeeping.android.presentation.common.theme.Space4 -import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.RegistrationMainErrorType +import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.type.RegistrationMainNamingErrorType import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -12,16 +12,17 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp @Composable -fun ErrorUserNamingComponent(namingErrorType: RegistrationMainErrorType) { - Row { - when (namingErrorType) { - RegistrationMainErrorType.Init -> Spacer(Modifier.height(21.dp)) - is RegistrationMainErrorType.InValid.Duplication -> { +fun ErrorUserNamingComponent(namingType: RegistrationMainNamingErrorType) { + Row(verticalAlignment = Alignment.CenterVertically) { + when (namingType) { + RegistrationMainNamingErrorType.Init -> Spacer(Modifier.height(21.dp)) + is RegistrationMainNamingErrorType.InValid.Duplication -> { Image( painter = painterResource(R.drawable.ic_alert_triangle), contentDescription = null @@ -33,7 +34,7 @@ fun ErrorUserNamingComponent(namingErrorType: RegistrationMainErrorType) { ) } - is RegistrationMainErrorType.InValid.NumberOutOfRange -> { + is RegistrationMainNamingErrorType.InValid.NumberOutOfRange -> { Image( painter = painterResource(R.drawable.ic_alert_triangle), contentDescription = null diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/RegistraionUserDate.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/RegistraionUserDate.kt index b1b56af5..fb0bca70 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/RegistraionUserDate.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/component/RegistraionUserDate.kt @@ -2,42 +2,40 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.compon import ac.dnd.bookkeeping.android.presentation.R import ac.dnd.bookkeeping.android.presentation.common.theme.Body1 -import ac.dnd.bookkeeping.android.presentation.common.theme.Gray400 import ac.dnd.bookkeeping.android.presentation.common.theme.Gray600 import ac.dnd.bookkeeping.android.presentation.common.theme.Space12 -import ac.dnd.bookkeeping.android.presentation.common.theme.Space48 -import ac.dnd.bookkeeping.android.presentation.common.view.CustomTextField -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background +import ac.dnd.bookkeeping.android.presentation.common.theme.Space20 +import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingTextField +import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingTextFieldType +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.Icon import androidx.compose.material.IconButton -import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.unit.dp +import androidx.compose.ui.text.input.KeyboardType @Composable fun RegistraionUserDate( focusManager: FocusManager, - userYearText: TextFieldValue, - userMonthText: TextFieldValue, - userDayText: TextFieldValue, - onUserYearTextChange: (TextFieldValue) -> Unit, - onUserMonthTextChange: (TextFieldValue) -> Unit, - onUserDayTextChange: (TextFieldValue) -> Unit, + userYearText: String, + userMonthText: String, + userDayText: String, + onUserYearTextChange: (String) -> Unit, + onUserMonthTextChange: (String) -> Unit, + onUserDayTextChange: (String) -> Unit, ) { Text( text = "생년월일", @@ -50,99 +48,93 @@ fun RegistraionUserDate( Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(Space12) + horizontalArrangement = Arrangement.SpaceBetween ) { - Surface(modifier = Modifier.weight(114f), color = Color.Transparent) { - CustomTextField( - text = userYearText.text, - modifier = Modifier.background(Color.White), - elevation = 0.dp, - onTextChange = { - onUserYearTextChange(TextFieldValue(it)) + Box(modifier = Modifier.weight(114f)) { + TypingTextField( + textType = TypingTextFieldType.Basic, + text = userYearText, + hintText = "YYYY", + onValueChange = { + if (userYearText.length < 4) { + onUserYearTextChange(it) + if (it.length == 4) focusManager.moveFocus(FocusDirection.Right) + } }, - height = Space48, - cornerBorder = BorderStroke( - width = 1.dp, - color = Gray400 - ), - contentInnerPadding = PaddingValues(horizontal = 16.dp), trailingIconContent = { - if (userYearText.text.isNotEmpty()) { + if (userYearText.isNotEmpty()) { IconButton( onClick = { - onUserYearTextChange(TextFieldValue()) + onUserYearTextChange("") } ) { - Icon( - painter = painterResource(id = R.drawable.ic_close), + Image( + painter = painterResource(id = R.drawable.ic_close_circle), contentDescription = "close icon", + modifier = Modifier.size(Space20) ) } } }, - hintTextContent = { Text(text = "YYYY", style = Body1.merge(Gray600)) } + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) ) } - Surface(modifier = Modifier.weight(90f), color = Color.Transparent) { - CustomTextField( - text = userMonthText.text, - modifier = Modifier.background(Color.White), - elevation = 0.dp, - height = Space48, - onTextChange = { - onUserMonthTextChange(TextFieldValue(it)) + Spacer(Modifier.weight(10f)) + Box(modifier = Modifier.weight(90f)) { + TypingTextField( + textType = TypingTextFieldType.Basic, + text = userMonthText, + hintText = "MM", + onValueChange = { + if (userMonthText.length < 2) { + onUserMonthTextChange(it) + if (it.length == 2) focusManager.moveFocus(FocusDirection.Right) + } }, - cornerBorder = BorderStroke( - width = 1.dp, - color = Gray400 - ), - contentInnerPadding = PaddingValues(horizontal = 16.dp), trailingIconContent = { - if (userMonthText.text.isNotEmpty()) { + if (userMonthText.isNotEmpty()) { IconButton( onClick = { - onUserMonthTextChange(TextFieldValue()) + onUserMonthTextChange("") } ) { - Icon( - painter = painterResource(id = R.drawable.ic_close), + Image( + painter = painterResource(id = R.drawable.ic_close_circle), contentDescription = "close icon", + modifier = Modifier.size(Space20) ) } } }, - hintTextContent = { Text(text = "MM", style = Body1.merge(Gray600)) } + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) ) } - Surface(modifier = Modifier.weight(90f), color = Color.Transparent) { - CustomTextField( - text = userDayText.text, - height = Space48, - elevation = 0.dp, - modifier = Modifier.background(Color.White), - onTextChange = { - onUserDayTextChange(TextFieldValue(it)) + Spacer(Modifier.weight(10f)) + Box(modifier = Modifier.weight(90f)) { + TypingTextField( + textType = TypingTextFieldType.Basic, + text = userDayText, + hintText = "DD", + onValueChange = { + if (userDayText.length < 2) onUserDayTextChange(it) }, - contentInnerPadding = PaddingValues(horizontal = 16.dp), - cornerBorder = BorderStroke( - width = 1.dp, - color = Gray400 - ), trailingIconContent = { - if (userDayText.text.isNotEmpty()) { + if (userDayText.isNotEmpty()) { IconButton( onClick = { - onUserDayTextChange(TextFieldValue()) + onUserDayTextChange("") } ) { - Icon( - painter = painterResource(id = R.drawable.ic_close), + Image( + painter = painterResource(id = R.drawable.ic_close_circle), contentDescription = "close icon", + modifier = Modifier.size(Space20) ) + } } }, - hintTextContent = { Text(text = "DD", style = Body1.merge(Gray600)) } + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) ) } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/type/RegistrationMainNamingErrorType.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/type/RegistrationMainNamingErrorType.kt new file mode 100644 index 00000000..7e691f7f --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/type/RegistrationMainNamingErrorType.kt @@ -0,0 +1,9 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.type + +sealed interface RegistrationMainNamingErrorType { + data object Init : RegistrationMainNamingErrorType + sealed interface InValid : RegistrationMainNamingErrorType { + data object Duplication : InValid + data object NumberOutOfRange : InValid + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/type/UserGender.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/type/UserGender.kt new file mode 100644 index 00000000..5846f4f1 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/type/UserGender.kt @@ -0,0 +1,7 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.type + +enum class UserGender { + NORMAL, + MALE, + FEMALE +} diff --git a/presentation/src/main/res/drawable/ic_check_circle.xml b/presentation/src/main/res/drawable/ic_check_circle.xml new file mode 100644 index 00000000..986ef275 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_check_circle.xml @@ -0,0 +1,16 @@ + + + + diff --git a/presentation/src/main/res/drawable/ic_close_circle.xml b/presentation/src/main/res/drawable/ic_close_circle.xml new file mode 100644 index 00000000..47918ce6 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_close_circle.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index e607c8d6..6a4803c4 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -14,8 +14,7 @@ 테스트 테스트내용\n앗, 에러가 발생했어요!\n이렇게 됩니다. - - 중복 확인 + 카카오 로그인 실패 !