From cd9a378b3e98d2b876b14a34547120c49777b73f Mon Sep 17 00:00:00 2001 From: Strone Date: Tue, 16 Apr 2024 01:26:52 +0900 Subject: [PATCH 001/131] Modify wrong code --- .../in/koreatech/koin/domain/util/regex/InputValidationUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain/src/main/java/in/koreatech/koin/domain/util/regex/InputValidationUtils.kt b/domain/src/main/java/in/koreatech/koin/domain/util/regex/InputValidationUtils.kt index 8391f7144..746e7c1ac 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/util/regex/InputValidationUtils.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/util/regex/InputValidationUtils.kt @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.util.regex +package `in`.koreatech.koin.domain.util.regex class InputValidationUtils { } \ No newline at end of file From c783d207b3c649020d0542c5e5542fd754f244bf Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 17 Apr 2024 02:38:31 +0900 Subject: [PATCH 002/131] Add firebase crashlytics buildtools gradle --- gradle/libs.versions.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e60c6fe6d..7a0288ba1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,6 +48,7 @@ googleServiceVersion = "4.3.14" composeNavigationVersion = "2.7.7" orbitVersion="7.0.1" hiltComposeVersion="1.0.0" +firebaseCrashlyticsBuildtoolsVersion = "2.9.9" [libraries] androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtxVersion" } @@ -125,6 +126,7 @@ orbit-viewmodel = {module= "org.orbit-mvi:orbit-viewmodel", version.ref = "orbit orbit-compose = {module= "org.orbit-mvi:orbit-compose", version.ref = "orbitVersion"} hilt-compose= {module= "androidx.hilt:hilt-navigation-compose", version.ref = "hiltComposeVersion"} +firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtoolsVersion" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradleVersion" } From 5fb0a4021d9e0ebc71bdfcb8adff692f8f503cad Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 17 Apr 2024 13:39:54 +0900 Subject: [PATCH 003/131] Modify email auth logic --- .../business/BusinessMainActivity.kt | 2 +- .../signup/accountsetup/AccountSetupScreen.kt | 9 ++------ .../accountsetup/AccountSetupViewModel.kt | 21 +++++++------------ .../signup/navigator/SignupNavigator.kt | 3 +-- .../business/SendSignupEmailUseCase.kt | 17 ++++----------- 5 files changed, 16 insertions(+), 36 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/BusinessMainActivity.kt b/business/src/main/java/in/koreatech/business/BusinessMainActivity.kt index cd0122720..868337e89 100644 --- a/business/src/main/java/in/koreatech/business/BusinessMainActivity.kt +++ b/business/src/main/java/in/koreatech/business/BusinessMainActivity.kt @@ -12,7 +12,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import dagger.hilt.android.AndroidEntryPoint -import `in`.koreatech.business.feature_signup.navigator.SignupNavigator +import `in`.koreatech.business.feature.signup.navigator.SignupNavigator import `in`.koreatech.business.ui.theme.KOIN_ANDROIDTheme @AndroidEntryPoint diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index 3ed6b5133..bd2a4c99c 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -165,16 +165,11 @@ fun AccountSetupScreen( disabledContentColor = Color.White, ), onClick = { - viewModel.checkInfo( - state.email, - state.password, - state.passwordConfirm, + viewModel.postEmailVerification( + state.email, state.password, state.passwordConfirm ) if (state.signupContinuationState == SignupContinuationState.CheckComplete) viewModel.onNextButtonClicked() - viewModel.postEmailVerification( - state.email, - ) }) { Text( text = stringResource(id = R.string.email_authentication), diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 6f3b2a2c4..3cabf34ff 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -3,6 +3,7 @@ package `in`.koreatech.business.feature.signup.accountsetup import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.koreatech.koin.domain.state.signup.SignupContinuationState import `in`.koreatech.koin.domain.usecase.business.SendSignupEmailUseCase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -29,18 +30,21 @@ class AccountSetupViewModel @Inject constructor( reduce { state.copy(password = password) } + reduce { state.copy(signupContinuationState = SignupContinuationState.RequestedEmailValidation) } } fun onPasswordConfirmChanged(passwordConfirm: String) = intent { reduce { state.copy(passwordConfirm = passwordConfirm) } + reduce { state.copy(signupContinuationState = SignupContinuationState.RequestedEmailValidation) } } fun onEmailChanged(email: String) = intent { reduce { state.copy(email = email) } + reduce { state.copy(signupContinuationState = SignupContinuationState.RequestedEmailValidation) } } fun onNextButtonClicked() = intent { @@ -51,30 +55,21 @@ class AccountSetupViewModel @Inject constructor( postSideEffect(AccountAuthSideEffect.NavigateToBackScreen) } - fun checkInfo(email: String, password: String, passwordConfirm: String) { + + fun postEmailVerification(email: String, password: String, passwordConfirm: String) { intent { reduce { state.copy(isLoading = true) } } viewModelScope.launch(Dispatchers.IO) { sendSignupEmailUseCase(email, password, passwordConfirm) .onSuccess { intent { - reduce { state.copy(signupContinuationState = it)} - reduce { state.copy(signUpContinuationError = null)} + reduce { state.copy(signupContinuationState = it) } + reduce { state.copy(signUpContinuationError = null) } } } .onFailure { intent { reduce { state.copy(signUpContinuationError = it) } } } - intent { reduce { state.copy(isLoading = false) } } - } - } - fun postEmailVerification(email: String) { - intent { reduce { state.copy(isLoading = true) } } - viewModelScope.launch(Dispatchers.IO) { sendSignupEmailUseCase.sendEmail(email) - .onSuccess {} - .onFailure { - intent { reduce { state.copy(signUpContinuationError = it) } } - } intent { reduce { state.copy(isLoading = false) } } } } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt index 54f25af33..98b9199c1 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt @@ -30,8 +30,7 @@ fun SignupNavigator(modifier: Modifier) { ) { AccountSetupScreen( onBackClicked = { navController.popBackStack() }, - onNextClicked = { - navController.navigate("${SignupRoute.ACCOUNT_AUTH.name}/$it") + onNextClicked = { navController.navigate("${SignupRoute.ACCOUNT_AUTH.name}/$it") }, ) } diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt index 597ea5174..6f3faf954 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt @@ -8,7 +8,7 @@ import javax.inject.Inject class SendSignupEmailUseCase @Inject constructor( private val ownerSignupRepository: OwnerSignupRepository ) { - operator fun invoke( + suspend operator fun invoke( email: String, password: String, passwordConfirm: String, @@ -16,18 +16,9 @@ class SendSignupEmailUseCase @Inject constructor( return when { password != passwordConfirm -> Result.success(SignupContinuationState.PasswordNotMatching) email.trim().isNotBusinessValidEmail() -> Result.success(SignupContinuationState.EmailIsNotValidate) - else -> Result.success(SignupContinuationState.CheckComplete) + else -> ownerSignupRepository.requestEmailVerification( + email = email + ).map {SignupContinuationState.CheckComplete } } } - - suspend fun sendEmail( - email: String, - ): Result { - return ownerSignupRepository.requestEmailVerification( - email = email - ).map { SignupContinuationState.CheckComplete } - } - - - } \ No newline at end of file From 0f9ab6a9fb902eee831f7c73543e0161c86fca04 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 17 Apr 2024 13:40:58 +0900 Subject: [PATCH 004/131] Delete wrong code --- .../feature/signup/accountsetup/AccountSetupViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 3cabf34ff..025eb6608 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -69,7 +69,6 @@ class AccountSetupViewModel @Inject constructor( .onFailure { intent { reduce { state.copy(signUpContinuationError = it) } } } - sendSignupEmailUseCase.sendEmail(email) intent { reduce { state.copy(isLoading = false) } } } } From 35a1e97f0612b1afa27cdc216e1f52c31b605971 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 17 Apr 2024 14:08:02 +0900 Subject: [PATCH 005/131] Account set up bug fix --- .../feature/signup/accountsetup/AccountSetupScreen.kt | 6 ++---- .../feature/signup/accountsetup/AccountSetupViewModel.kt | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index bd2a4c99c..47723efd9 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -30,10 +30,10 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R import `in`.koreatech.business.feature.textfield.LinedTextField -import `in`.koreatech.business.ui.theme.ColorPrimary import `in`.koreatech.business.ui.theme.ColorDisabledButton -import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorHelper +import `in`.koreatech.business.ui.theme.ColorPrimary +import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorUnarchived import `in`.koreatech.business.ui.theme.KOIN_ANDROIDTheme import `in`.koreatech.koin.domain.state.signup.SignupContinuationState @@ -168,8 +168,6 @@ fun AccountSetupScreen( viewModel.postEmailVerification( state.email, state.password, state.passwordConfirm ) - if (state.signupContinuationState == SignupContinuationState.CheckComplete) - viewModel.onNextButtonClicked() }) { Text( text = stringResource(id = R.string.email_authentication), diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 025eb6608..fc4c129a7 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -70,6 +70,10 @@ class AccountSetupViewModel @Inject constructor( intent { reduce { state.copy(signUpContinuationError = it) } } } intent { reduce { state.copy(isLoading = false) } } + intent { + if (state.signupContinuationState == SignupContinuationState.CheckComplete) + onNextButtonClicked() + } } } } \ No newline at end of file From 60aafda67fc2f6bc597e5ca6aae2b0f5debe6d38 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 12:05:33 +0900 Subject: [PATCH 006/131] Modify class name --- .../feature/signup/accountsetup/AccountSetupScreen.kt | 4 ++-- ...AccountAuthSideEffect.kt => AccountSetupSideEffect.kt} | 6 +++--- .../{AccountAuthState.kt => AccountSetupState.kt} | 2 +- .../feature/signup/accountsetup/AccountSetupViewModel.kt | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) rename business/src/main/java/in/koreatech/business/feature/signup/accountsetup/{AccountAuthSideEffect.kt => AccountSetupSideEffect.kt} (51%) rename business/src/main/java/in/koreatech/business/feature/signup/accountsetup/{AccountAuthState.kt => AccountSetupState.kt} (95%) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index 47723efd9..ea3a33be0 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -180,8 +180,8 @@ fun AccountSetupScreen( viewModel.collectSideEffect { when (it) { - is AccountAuthSideEffect.NavigateToNextScreen -> onNextClicked(it.email) - AccountAuthSideEffect.NavigateToBackScreen -> onBackClicked() + is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(it.email) + AccountSetupSideEffect.NavigateToBackScreen -> onBackClicked() } } } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountAuthSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt similarity index 51% rename from business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountAuthSideEffect.kt rename to business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt index b7ecf8133..556e5be7d 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountAuthSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt @@ -1,6 +1,6 @@ package `in`.koreatech.business.feature.signup.accountsetup -sealed class AccountAuthSideEffect { - data object NavigateToBackScreen : AccountAuthSideEffect() - data class NavigateToNextScreen(val email: String) : AccountAuthSideEffect() +sealed class AccountSetupSideEffect { + data object NavigateToBackScreen : AccountSetupSideEffect() + data class NavigateToNextScreen(val email: String) : AccountSetupSideEffect() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt similarity index 95% rename from business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountAuthState.kt rename to business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt index 7ec787d9d..8c0ef33cf 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt @@ -2,7 +2,7 @@ package `in`.koreatech.business.feature.signup.accountsetup import `in`.koreatech.koin.domain.state.signup.SignupContinuationState -data class AccountAuthState( +data class AccountSetupState( val id: String = "", val password: String = "", val passwordConfirm: String = "", diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index fc4c129a7..87e1a56cd 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -17,8 +17,8 @@ import javax.inject.Inject @HiltViewModel class AccountSetupViewModel @Inject constructor( private val sendSignupEmailUseCase: SendSignupEmailUseCase, -) : ViewModel(), ContainerHost { - override val container = container(AccountAuthState()) +) : ViewModel(), ContainerHost { + override val container = container(AccountSetupState()) fun onIdChanged(id: String) = intent { reduce { @@ -48,11 +48,11 @@ class AccountSetupViewModel @Inject constructor( } fun onNextButtonClicked() = intent { - postSideEffect(AccountAuthSideEffect.NavigateToNextScreen(state.email)) + postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.email)) } fun onBackButtonClicked() = intent { - postSideEffect(AccountAuthSideEffect.NavigateToBackScreen) + postSideEffect(AccountSetupSideEffect.NavigateToBackScreen) } From 2ca29a0d412bc453f55114a3b3e20320e328b921 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 12:30:37 +0900 Subject: [PATCH 007/131] Modify isError default value --- .../in/koreatech/business/feature/textfield/AuthTextField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/feature/textfield/AuthTextField.kt b/business/src/main/java/in/koreatech/business/feature/textfield/AuthTextField.kt index db3fc659a..e31106a88 100644 --- a/business/src/main/java/in/koreatech/business/feature/textfield/AuthTextField.kt +++ b/business/src/main/java/in/koreatech/business/feature/textfield/AuthTextField.kt @@ -37,7 +37,7 @@ fun AuthTextField( label: String, textStyle: TextStyle = TextStyle.Default.copy(fontSize = 15.sp), isPassword: Boolean = false, - isError: Boolean = true, + isError: Boolean = false, ) { var focused by remember { mutableStateOf(false) } From d4bf783e24c2a39c5ee7b3af27277b0b741e7f17 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 16:01:34 +0900 Subject: [PATCH 008/131] Change name AccountAuthScreen->EmailAuthScreen --- ...ccountAuthScreen.kt => EmailAuthScreen.kt} | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) rename business/src/main/java/in/koreatech/business/feature/signup/accountauth/{AccountAuthScreen.kt => EmailAuthScreen.kt} (87%) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/AccountAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt similarity index 87% rename from business/src/main/java/in/koreatech/business/feature/signup/accountauth/AccountAuthScreen.kt rename to business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt index fa85b26c1..349a500ac 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/AccountAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt @@ -33,21 +33,28 @@ import androidx.compose.ui.text.font.FontWeight.Companion.Bold import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R +import `in`.koreatech.business.feature.signup.accountsetup.AccountSetupViewModel import `in`.koreatech.business.feature.textfield.AuthTextField import `in`.koreatech.business.ui.theme.ColorDescription import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorUnarchived import kotlinx.coroutines.delay +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect @Composable -fun AccountAuthScreen( +fun EmailAuthScreen( modifier: Modifier = Modifier, email: String, + verifyViewModel: EmailAuthViewModel= hiltViewModel(), + sendEmailViewModel: AccountSetupViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, ) { - var authCode by remember { mutableStateOf("") } + val state = verifyViewModel.collectAsState().value + Column( modifier = modifier, ) { @@ -124,12 +131,13 @@ fun AccountAuthScreen( ) Spacer(modifier = Modifier.height(38.dp)) AuthTextField( - value = authCode, - onValueChange = { authCode = it }, + value = state.authCode, + onValueChange = { verifyViewModel.onAuthCodeChanged(it)}, modifier = Modifier.fillMaxWidth(), label = stringResource(id = R.string.enter_verification_code), textStyle = TextStyle.Default.copy(fontSize = 20.sp), isPassword = true, + isError = state.signUpContinuationError != null, ) Spacer(modifier = Modifier.height(8.dp)) @@ -148,7 +156,7 @@ fun AccountAuthScreen( contentColor = Color.White, disabledContentColor = Color.White, ), - onClick = { } + onClick = { sendEmailViewModel.postEmailVerification(email,"","") } ) { Text(text = stringResource(id = R.string.resend)) } @@ -163,11 +171,19 @@ fun AccountAuthScreen( disabledContentColor = Color.White, ), onClick = { + verifyViewModel.verifyEmail(email, state.authCode) }) { Text(text = stringResource(id = R.string.next)) } } } + + verifyViewModel.collectSideEffect { + when (it) { + is EmailAuthSideEffect.NavigateToNextScreen-> onNextClicked() + EmailAuthSideEffect.NavigateToBackScreen -> onBackClicked() + } + } } } From a6511da91488921d9f080c77d1059b68280cbfed Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 16:01:59 +0900 Subject: [PATCH 009/131] Add email auth side effect --- .../feature/signup/accountauth/EmailAuthSideEffect.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt new file mode 100644 index 000000000..aab76b670 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt @@ -0,0 +1,6 @@ +package `in`.koreatech.business.feature.signup.accountauth + +sealed class EmailAuthSideEffect { + data object NavigateToBackScreen : EmailAuthSideEffect() + data object NavigateToNextScreen : EmailAuthSideEffect() +} \ No newline at end of file From 13d157d18c5527bc797eee88e46c932fd6b8f77c Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 17:46:18 +0900 Subject: [PATCH 010/131] Add email auth usecase --- .../usecase/business/EmailAuthUseCase.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt new file mode 100644 index 000000000..94eed81fd --- /dev/null +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt @@ -0,0 +1,23 @@ +package `in`.koreatech.koin.domain.usecase.business + +import `in`.koreatech.koin.domain.repository.OwnerVerificationCodeRepository +import `in`.koreatech.koin.domain.repository.TokenRepository +import javax.inject.Inject + +class EmailAuthUseCase @Inject constructor( + private val ownerVerificationCodeRepository: OwnerVerificationCodeRepository, + private val tokenRepository: TokenRepository, +) { + suspend operator fun invoke( + address: String, + verificationCode: String + ): Result { + return try { + val authToken = ownerVerificationCodeRepository.compareVerificationCode(address, verificationCode) + tokenRepository.saveOwnerAccessToken(authToken.getOrDefault(defaultValue = null)!!.token) + Result.success(Unit) + } catch (t: Throwable) { + Result.failure(t) + } + } +} \ No newline at end of file From 751e67e45c957e0d7ebcf6c1267635bd882d32bf Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 17:49:06 +0900 Subject: [PATCH 011/131] Add email auth viewModel --- .../signup/accountauth/EmailAuthViewModel.kt | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt new file mode 100644 index 000000000..1eadd300c --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt @@ -0,0 +1,76 @@ +package `in`.koreatech.business.feature.signup.accountauth + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.koreatech.koin.domain.usecase.business.EmailAuthUseCase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container +import javax.inject.Inject + + +@HiltViewModel +class EmailAuthViewModel @Inject constructor( + private val emailAuthUseCase: EmailAuthUseCase, +) : ViewModel(), ContainerHost { + override val container = container(EmailAuthState()) + + fun onAuthCodeChanged(authCode: String) = intent { + reduce { + state.copy(authCode = authCode) + } + } + + private fun onNextButtonClicked() = intent { + postSideEffect(EmailAuthSideEffect.NavigateToNextScreen) + } + + fun onBackButtonClicked() = intent { + postSideEffect(EmailAuthSideEffect.NavigateToBackScreen) + } + + fun onResendButtonClicked() = intent { + reduce { + state.copy(timeLeft = 300, minutes = 5, seconds = 0, formattedTimeLeft = "05:00") + } + } + + fun verifyEmail(email: String, verificationCode: String) { + intent { reduce { state.copy(isLoading = true) } } + viewModelScope.launch(Dispatchers.IO) { + + emailAuthUseCase(email, verificationCode) + .onSuccess { + onNextButtonClicked() + intent { + reduce { state.copy(signupContinuationState = it) } + reduce { state.copy(signUpContinuationError = null) } + } + } + .onFailure { + intent { reduce { state.copy(signUpContinuationError = it) } } + } + intent { reduce { state.copy(isLoading = false) } } + } + } + + fun onTimerTick() = intent { + reduce { + val timeLeft = state.timeLeft - 1 + val minutes = timeLeft / 60 + val seconds = timeLeft % 60 + val formattedTimeLeft = "%02d:%02d".format(minutes, seconds) + state.copy( + timeLeft = timeLeft, + minutes = minutes, + seconds = seconds, + formattedTimeLeft = formattedTimeLeft + ) + } + } +} From b5406df50890a8f9894ea7384637e47027c88399 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 17:49:21 +0900 Subject: [PATCH 012/131] Add email auth state --- .../feature/signup/accountauth/EmailAuthState.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthState.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthState.kt new file mode 100644 index 000000000..53207c33e --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthState.kt @@ -0,0 +1,12 @@ +package `in`.koreatech.business.feature.signup.accountauth + +data class EmailAuthState( + val authCode: String = "", + val isLoading: Boolean = false, + val signupContinuationState: Unit = Unit, + val signUpContinuationError: Throwable? = null, + val timeLeft: Int = 300, + val minutes: Int = 0, + val seconds: Int = 0, + val formattedTimeLeft: String = "05:00" +) From ee864748f4d426464b6041910ba3574499193f73 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 17:51:24 +0900 Subject: [PATCH 013/131] Change ACCOUNT_AUTH->EMAIL_AUTH --- .../feature/signup/navigator/SignupNavigator.kt | 11 +++++------ .../business/feature/signup/navigator/SignupRoute.kt | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt index 98b9199c1..bee9ed2cd 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt @@ -9,7 +9,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument -import `in`.koreatech.business.feature.signup.accountauth.AccountAuthScreen +import `in`.koreatech.business.feature.signup.accountauth.EmailAuthScreen import `in`.koreatech.business.feature.signup.accountsetup.AccountSetupScreen import `in`.koreatech.business.feature.signup.businessauth.BusinessAuthScreen import `in`.koreatech.business.feature.signup.businessauth.SearchStoreScreen @@ -21,7 +21,7 @@ fun SignupNavigator(modifier: Modifier) { val navController = rememberNavController() NavHost( navController = navController, - startDestination = SignupRoute.BASIC_INFO_INPUT.name, + startDestination = SignupRoute.EMAIL_AUTH.name, modifier = Modifier.padding(16.dp) ) { @@ -30,14 +30,13 @@ fun SignupNavigator(modifier: Modifier) { ) { AccountSetupScreen( onBackClicked = { navController.popBackStack() }, - onNextClicked = { navController.navigate("${SignupRoute.ACCOUNT_AUTH.name}/$it") + onNextClicked = { navController.navigate("${SignupRoute.EMAIL_AUTH.name}/$it") }, ) } composable( - route = "${SignupRoute.ACCOUNT_AUTH.name}/{email}", - + route = "${SignupRoute.EMAIL_AUTH.name}/{email}", arguments = listOf( navArgument("email") { type = NavType.StringType @@ -46,7 +45,7 @@ fun SignupNavigator(modifier: Modifier) { ) ) { val email = it.arguments?.getString("email") ?: "" - AccountAuthScreen( + EmailAuthScreen( email = email, onBackClicked = { navController.popBackStack() }, onNextClicked = { diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupRoute.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupRoute.kt index 3942fb0f8..c6a3f107d 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupRoute.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupRoute.kt @@ -4,7 +4,7 @@ enum class SignupRoute(){ LOGIN, TERMS_OF_SERVICE, BASIC_INFO_INPUT, - ACCOUNT_AUTH, + EMAIL_AUTH, BUSINESS_AUTH, STORE_SETUP, SIGNUP_COMPLETED From c1ebf7a8a527fb5bef77a86c2197793f5a04819e Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 18 Apr 2024 17:51:40 +0900 Subject: [PATCH 014/131] Delete typography --- business/src/main/java/in/koreatech/business/ui/theme/Theme.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt b/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt index 628aceb39..4d0a9019a 100644 --- a/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt +++ b/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt @@ -49,7 +49,7 @@ fun KOIN_ANDROIDTheme( MaterialTheme( colors = colorScheme, - typography = Typography, + // typography = Typography, shapes = Shapes, content = content ) From 0ad0c1aaa39442c1f7c72ee6e15f83476c3cc291 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 19 Apr 2024 00:11:25 +0900 Subject: [PATCH 015/131] Delete count down timer function --- .../signup/accountauth/EmailAuthScreen.kt | 62 +++++++------------ 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt index 349a500ac..565a5290c 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt @@ -17,10 +17,6 @@ import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color @@ -48,7 +44,7 @@ import org.orbitmvi.orbit.compose.collectSideEffect fun EmailAuthScreen( modifier: Modifier = Modifier, email: String, - verifyViewModel: EmailAuthViewModel= hiltViewModel(), + verifyViewModel: EmailAuthViewModel = hiltViewModel(), sendEmailViewModel: AccountSetupViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, @@ -60,7 +56,7 @@ fun EmailAuthScreen( ) { IconButton( modifier = Modifier.padding(vertical = 24.dp), - onClick = { onBackClicked() } + onClick = { verifyViewModel.onBackButtonClicked() } ) { Icon( modifier = Modifier.padding(start = 10.dp), @@ -132,16 +128,30 @@ fun EmailAuthScreen( Spacer(modifier = Modifier.height(38.dp)) AuthTextField( value = state.authCode, - onValueChange = { verifyViewModel.onAuthCodeChanged(it)}, + onValueChange = { verifyViewModel.onAuthCodeChanged(it) }, modifier = Modifier.fillMaxWidth(), label = stringResource(id = R.string.enter_verification_code), textStyle = TextStyle.Default.copy(fontSize = 20.sp), isPassword = true, isError = state.signUpContinuationError != null, - ) + ) Spacer(modifier = Modifier.height(8.dp)) - CountdownTimer() + + LaunchedEffect(key1 = state.timeLeft) { + while (state.timeLeft > 0) { + delay(1000L) + verifyViewModel.onTimerTick() + } + } + + + Text( + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + text = stringResource(id = R.string.time_limit) + state.formattedTimeLeft + ) + Spacer(modifier = Modifier.height(183.dp)) Row( modifier = Modifier.fillMaxWidth(), @@ -156,7 +166,10 @@ fun EmailAuthScreen( contentColor = Color.White, disabledContentColor = Color.White, ), - onClick = { sendEmailViewModel.postEmailVerification(email,"","") } + onClick = { + sendEmailViewModel.postEmailVerification(email, "", "") + verifyViewModel.onResendButtonClicked() + } ) { Text(text = stringResource(id = R.string.resend)) } @@ -180,36 +193,9 @@ fun EmailAuthScreen( verifyViewModel.collectSideEffect { when (it) { - is EmailAuthSideEffect.NavigateToNextScreen-> onNextClicked() + is EmailAuthSideEffect.NavigateToNextScreen -> onNextClicked() EmailAuthSideEffect.NavigateToBackScreen -> onBackClicked() } } } } - - -@Composable -fun CountdownTimer() { - - var timeLeft by remember { mutableStateOf(300) } - var minutes by remember { mutableStateOf(0) } - var seconds by remember { mutableStateOf(0) } - - LaunchedEffect(key1 = timeLeft) { - while (timeLeft > 0) { - delay(1000L) - timeLeft-- - minutes = timeLeft / 60 - seconds = timeLeft % 60 - } - } - - val formattedTimeLeft = "%02d".format(minutes) - val formattedSeconds = "%02d".format(seconds) - - Text( - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center, - text = stringResource(id = R.string.time_limit) + "$formattedTimeLeft : $formattedSeconds" - ) -} From 4fa20890d1d99419a013545299dacccfba76fc78 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 19 Apr 2024 02:43:10 +0900 Subject: [PATCH 016/131] Add business auth state --- .../feature/signup/businessauth/BusinessAuthState.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index 55e87565f..0c66d9164 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -1,5 +1,11 @@ package `in`.koreatech.business.feature.signup.businessauth data class BusinessAuthState( - val count: Int = 0 + val name: String = "", + val storeName: String = "", + val storeNumber: String = "", + val phoneNumber: String = "", + val openAlertDialog: Boolean = false, + val selectedImageUris :MutableList = mutableListOf(), + val dialogVisibility:Boolean = false ) From a5389f92fd36fcfe389ddddf6fb3e2ecf5f33f9d Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 19 Apr 2024 02:45:01 +0900 Subject: [PATCH 017/131] Add business auth viewmodel --- .../businessauth/BusinessAuthViewModel.kt | 54 +++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index b58e0e2e2..c2e7eecba 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -5,6 +5,7 @@ import `in`.koreatech.koin.core.viewmodel.BaseViewModel import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.syntax.simple.intent import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import javax.inject.Inject @@ -12,8 +13,55 @@ import javax.inject.Inject class BusinessAuthViewModel @Inject constructor( ) : ContainerHost, BaseViewModel() { - override val container = container(BusinessAuthState()) - fun onShowDialog() = intent { - postSideEffect(BusinessAuthSideEffect.ShowDialog) + override val container = + container(BusinessAuthState()) + + fun onNameChanged(name: String) = intent { + reduce { + state.copy(name = name) + } + } + + fun onStoreNameChanged(storeName: String) = intent { + reduce { + state.copy(storeName = storeName) + } + } + + fun onStoreNumberChanged(storeNumber: String) = intent { + reduce { + state.copy(storeNumber = storeNumber) + } + } + + fun onPhoneNumberChanged(phoneNumber: String) = intent { + reduce { + state.copy(phoneNumber = phoneNumber) + } + } + + fun onImageUrisChanged(selectedImageUris: MutableList) = intent { + reduce { + state.copy(selectedImageUris = selectedImageUris) + } } + + fun onDialogVisibilityChanged(dialogVisibility: Boolean) = intent { + reduce { + state.copy(dialogVisibility = dialogVisibility) + } + } + + fun onNavigateToSearchStore() = intent { + postSideEffect(BusinessAuthSideEffect.NavigateToSearchStore) + } + + fun onNavigateToBackScreen() = intent { + postSideEffect(BusinessAuthSideEffect.NavigateToBackScreen) + } + + fun onNavigateToNextScreen() = intent { + postSideEffect(BusinessAuthSideEffect.NavigateToNextScreen) + } + } \ No newline at end of file From ca3c076ead6d30153294deb43bab09e9fc1fb9c1 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 19 Apr 2024 02:45:29 +0900 Subject: [PATCH 018/131] Add business auth side effect --- .../feature/signup/businessauth/BusinessAuthSideEffect.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthSideEffect.kt index 5a6dd99db..140864280 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthSideEffect.kt @@ -1,5 +1,9 @@ package `in`.koreatech.business.feature.signup.businessauth +import `in`.koreatech.business.feature.signup.accountsetup.AccountSetupSideEffect + sealed class BusinessAuthSideEffect { - object ShowDialog : BusinessAuthSideEffect() + data object NavigateToSearchStore : BusinessAuthSideEffect() + data object NavigateToBackScreen : BusinessAuthSideEffect() + data object NavigateToNextScreen : BusinessAuthSideEffect() } \ No newline at end of file From d477214595048836ea0f3ddeff697e2f36329cc6 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 19 Apr 2024 02:49:37 +0900 Subject: [PATCH 019/131] Change business auth logic --- .../signup/businessauth/BusinessAuthScreen.kt | 75 ++++++++++++------- .../signup/dialog/BusinessAlertDialog.kt | 6 +- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index e2580d89c..2714e8b1e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -1,5 +1,8 @@ package `in`.koreatech.business.feature.signup.businessauth +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Canvas import androidx.compose.foundation.border @@ -37,6 +40,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight.Companion.Bold import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R import `in`.koreatech.business.feature.signup.dialog.BusinessAlertDialog import `in`.koreatech.business.feature.textfield.LinedTextField @@ -46,25 +50,28 @@ import `in`.koreatech.business.ui.theme.ColorDisabledButton import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorHelper import `in`.koreatech.business.ui.theme.ColorMinor +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect @Composable fun BusinessAuthScreen( modifier: Modifier = Modifier, + viewModel: BusinessAuthViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, onSearchClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, ) { - var name by remember { mutableStateOf("") } - var storeName by remember { mutableStateOf("") } - var storeNumber by remember { mutableStateOf("") } - var phoneNumber by remember { mutableStateOf("") } - val openAlertDialog = remember { mutableStateOf(false) } - val itemList: MutableList = mutableListOf() - + val state = viewModel.collectAsState().value + val multiplePhotoPickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.PickMultipleVisualMedia(), + onResult = { uris -> + viewModel.onImageUrisChanged(uris.map { it.toString() }.toMutableList()) + } + ) Column( modifier = modifier, ) { - IconButton(modifier = Modifier.padding(vertical = 24.dp), onClick = { onBackClicked() }) { + IconButton(modifier = Modifier.padding(vertical = 24.dp), onClick = { viewModel.onNavigateToBackScreen() }) { Icon( modifier = Modifier.padding(start = 10.dp), painter = painterResource(id = R.drawable.ic_arrow_back), @@ -112,8 +119,8 @@ fun BusinessAuthScreen( Spacer(modifier = Modifier.height(10.dp)) LinedTextField( - value = name, - onValueChange = { name = it }, + value = state.name, + onValueChange = { viewModel.onNameChanged(it) }, label = stringResource(id = R.string.master_name) ) @@ -122,13 +129,13 @@ fun BusinessAuthScreen( ) { LinedTextField( modifier = Modifier.width(197.dp), - value = storeName, - onValueChange = { storeName = it }, + value = state.storeName, + onValueChange = { viewModel.onStoreNameChanged(it) }, label = stringResource(id = R.string.enter_store_name) ) Button( modifier = Modifier, - onClick = { onSearchClicked() }, + onClick = { viewModel.onNavigateToSearchStore() }, shape = RectangleShape, colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, @@ -139,31 +146,30 @@ fun BusinessAuthScreen( } } LinedTextField( - value = storeNumber, - onValueChange = { storeNumber = it }, + value = state.storeNumber, + onValueChange = { viewModel.onStoreNumberChanged(it) }, label = stringResource(id = R.string.business_registration_number) ) LinedTextField( - value = phoneNumber, - onValueChange = { phoneNumber = it }, + value = state.phoneNumber, + onValueChange = { viewModel.onPhoneNumberChanged(it) }, label = stringResource(id = R.string.personal_contact) ) - Box( modifier = Modifier .fillMaxWidth() .height(125.dp), ) { - if (itemList.isNotEmpty()) UploadFileList(modifier, itemList) + if (state.selectedImageUris.isNotEmpty()) UploadFileList(modifier, state.selectedImageUris) else Column( modifier = Modifier .fillMaxSize() .height(125.dp) .border(BorderStroke(1.dp, ColorHelper)) - .clickable { }, + .clickable { viewModel.onDialogVisibilityChanged(true) }, verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -199,7 +205,7 @@ fun BusinessAuthScreen( contentColor = Color.White, disabledContentColor = Color.White, ), - onClick = { onNextClicked() }) { + onClick = { viewModel.onNavigateToNextScreen() }) { Text( text = stringResource(id = R.string.next), fontSize = 15.sp, @@ -207,17 +213,34 @@ fun BusinessAuthScreen( ) BusinessAlertDialog( - onDismissRequest = { openAlertDialog.value = false }, + onDismissRequest = { viewModel.onDialogVisibilityChanged(false)}, onConfirmation = { - openAlertDialog.value = false + multiplePhotoPickerLauncher.launch( + PickVisualMediaRequest( + ActivityResultContracts.PickVisualMedia.ImageOnly + ) + ) + viewModel.onDialogVisibilityChanged(false) }, dialogTitle = stringResource(id = R.string.file_upload), dialogText = stringResource(id = R.string.file_upload_requirements), - positiveButtonText = stringResource(id = R.string.select_file) + positiveButtonText = stringResource(id = R.string.select_file), + visibility = state.dialogVisibility ) - } - + } + viewModel.collectSideEffect { + when (it) { + BusinessAuthSideEffect.NavigateToSearchStore -> { + onSearchClicked() + } + BusinessAuthSideEffect.NavigateToBackScreen -> { + onBackClicked() + } + BusinessAuthSideEffect.NavigateToNextScreen -> { + onNextClicked() + } + } } } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/dialog/BusinessAlertDialog.kt b/business/src/main/java/in/koreatech/business/feature/signup/dialog/BusinessAlertDialog.kt index f1216edbf..d9d6d5f95 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/dialog/BusinessAlertDialog.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/dialog/BusinessAlertDialog.kt @@ -1,5 +1,6 @@ package `in`.koreatech.business.feature.signup.dialog +import android.opengl.Visibility import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -39,8 +40,11 @@ fun BusinessAlertDialog( dialogTitle: String, dialogText: String, positiveButtonText: String, + visibility: Boolean = false ) { - + if (!visibility) { + return + } Dialog(onDismissRequest = { onDismissRequest() }) { Card( modifier = Modifier From 77c8486d72fc716f73038cacac71929377a77492 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 23 Apr 2024 03:47:33 +0900 Subject: [PATCH 020/131] Add BusinessAuthState value --- .../feature/signup/businessauth/BusinessAuthState.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index 0c66d9164..d6723acf8 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -1,11 +1,19 @@ package `in`.koreatech.business.feature.signup.businessauth +import `in`.koreatech.koin.domain.model.store.AttachStore +import `in`.koreatech.koin.domain.model.store.StoreUrl +import java.io.InputStream + data class BusinessAuthState( val name: String = "", val storeName: String = "", val storeNumber: String = "", val phoneNumber: String = "", val openAlertDialog: Boolean = false, - val selectedImageUris :MutableList = mutableListOf(), - val dialogVisibility:Boolean = false + val selectedImages :MutableList = mutableListOf(), + val dialogVisibility:Boolean = false, + val fileInfo: MutableList = mutableListOf(), + val inputStream: MutableList = mutableListOf(), + val continuation :Boolean = false, + val error :Throwable? = null, ) From 0361eefebf1e1f8c9536303b879006847a803845 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 23 Apr 2024 16:12:26 +0900 Subject: [PATCH 021/131] Add presignedUrl uploadImage function to BusinessAuthViewModel --- .../businessauth/BusinessAuthViewModel.kt | 75 ++++++++++++++++++- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index c2e7eecba..b0144af0d 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -1,17 +1,29 @@ package `in`.koreatech.business.feature.signup.businessauth +import android.net.Uri +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.core.viewmodel.BaseViewModel +import `in`.koreatech.koin.domain.model.store.AttachStore +import `in`.koreatech.koin.domain.model.store.StoreUrl +import `in`.koreatech.koin.domain.usecase.owner.AttachStoreFileUseCase +import `in`.koreatech.koin.domain.usecase.owner.OwnerRegisterUseCase +import `in`.koreatech.koin.domain.usecase.presignedurl.UploadPreSignedUrlUseCase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.syntax.simple.intent import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container +import java.io.InputStream import javax.inject.Inject @HiltViewModel class BusinessAuthViewModel @Inject constructor( - + private val attachStoreFileUseCase: AttachStoreFileUseCase, + private val uploadPreSignedUrlUseCase: UploadPreSignedUrlUseCase, + private val ownerRegisterUseCase: OwnerRegisterUseCase ) : ContainerHost, BaseViewModel() { override val container = container(BusinessAuthState()) @@ -40,9 +52,9 @@ class BusinessAuthViewModel @Inject constructor( } } - fun onImageUrisChanged(selectedImageUris: MutableList) = intent { + fun onImageUrlsChanged(selectedImages: MutableList) = intent { reduce { - state.copy(selectedImageUris = selectedImageUris) + state.copy(selectedImages = selectedImages) } } @@ -64,4 +76,59 @@ class BusinessAuthViewModel @Inject constructor( postSideEffect(BusinessAuthSideEffect.NavigateToNextScreen) } -} \ No newline at end of file + fun presignedUrl(//url 생성 + uri: Uri, + fileSize: Long, + fileType: String, + fileName: String, + fileStream: InputStream + ) { + viewModelScope.launch { + attachStoreFileUseCase( + fileSize, fileType, fileName + ).onSuccess { + intent { + reduce { + state.copy( + fileInfo = state.fileInfo.toMutableList().apply { + add(StoreUrl(uri.toString(), it.first, fileName, fileType, it.second, fileSize)) + } + ) + } + reduce { + state.copy(inputStream = state.inputStream.toMutableList().apply { + add(fileStream) + }) + } + } + intent { + reduce { state.copy(error = null) } + } + }.onFailure { + intent { + reduce { state.copy(error = it) } + } + } + } + + } + + fun uploadImage(//파일 업로드 + url: String, + inputStream: InputStream, + mediaType: String, + mediaSize: Long + ) { + viewModelScope.launch(Dispatchers.IO) { + uploadPreSignedUrlUseCase(url, inputStream, mediaType, mediaSize).onSuccess { + intent { + reduce { state.copy(error = null) } + } + }.onFailure { + intent { + reduce { state.copy(error = it) } + } + } + } + } +} From 3e911da7db5f633d50cd1d0ab3c0662d1b973563 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 23 Apr 2024 16:13:29 +0900 Subject: [PATCH 022/131] Modify upload file logic --- .../signup/businessauth/BusinessAuthScreen.kt | 80 ++++++++++++++++--- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index 2714e8b1e..b32b7df7d 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -1,5 +1,7 @@ package `in`.koreatech.business.feature.signup.businessauth +import android.net.Uri +import android.provider.OpenableColumns import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts @@ -25,16 +27,13 @@ import androidx.compose.material.Icon import androidx.compose.material.IconButton 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.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight.Companion.Bold @@ -44,12 +43,13 @@ import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R import `in`.koreatech.business.feature.signup.dialog.BusinessAlertDialog import `in`.koreatech.business.feature.textfield.LinedTextField -import `in`.koreatech.business.ui.theme.ColorPrimary import `in`.koreatech.business.ui.theme.ColorDescription import `in`.koreatech.business.ui.theme.ColorDisabledButton -import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorHelper import `in`.koreatech.business.ui.theme.ColorMinor +import `in`.koreatech.business.ui.theme.ColorPrimary +import `in`.koreatech.business.ui.theme.ColorSecondary +import `in`.koreatech.koin.domain.model.store.AttachStore import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -61,17 +61,56 @@ fun BusinessAuthScreen( onSearchClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, ) { + val context = LocalContext.current + val state = viewModel.collectAsState().value + var fileName = "" + var fileSize = 0L + val multiplePhotoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickMultipleVisualMedia(), - onResult = { uris -> - viewModel.onImageUrisChanged(uris.map { it.toString() }.toMutableList()) + onResult = { uriList -> + state.inputStream.clear() + state.fileInfo.clear() + uriList.forEach { + state.selectedImages.add(AttachStore(it.toString(), it.lastPathSegment ?: "")) + val inputStream = context.contentResolver.openInputStream(it) + viewModel.onImageUrlsChanged(state.selectedImages) + + if (it.scheme.equals("content")) { + val cursor = context.contentResolver.query(it, null, null, null, null) + cursor.use { + if (cursor != null && cursor.moveToFirst()) { + val fileNameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + val fileSizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) + + if (fileNameIndex != -1 && fileSizeIndex != -1) { + fileName = cursor.getString(fileNameIndex) + fileSize = cursor.getLong(fileSizeIndex) + } + } + } + } + + if (inputStream != null) { + viewModel.presignedUrl( + uri = it, + fileName = it.lastPathSegment ?: "", + fileSize = fileSize, + fileType = "image/" + fileName.split(".")[1], + fileStream = inputStream + ) + + } + } } ) Column( modifier = modifier, ) { - IconButton(modifier = Modifier.padding(vertical = 24.dp), onClick = { viewModel.onNavigateToBackScreen() }) { + IconButton( + modifier = Modifier.padding(vertical = 24.dp), + onClick = { viewModel.onNavigateToBackScreen() }) { Icon( modifier = Modifier.padding(start = 10.dp), painter = painterResource(id = R.drawable.ic_arrow_back), @@ -162,7 +201,10 @@ fun BusinessAuthScreen( .height(125.dp), ) { - if (state.selectedImageUris.isNotEmpty()) UploadFileList(modifier, state.selectedImageUris) + if (state.selectedImages.isNotEmpty()) UploadFileList( + modifier, + state.selectedImages + ) else Column( modifier = Modifier @@ -205,7 +247,18 @@ fun BusinessAuthScreen( contentColor = Color.White, disabledContentColor = Color.White, ), - onClick = { viewModel.onNavigateToNextScreen() }) { + + onClick = { + state.fileInfo.forEach { + viewModel.uploadImage( + it.preSignedUrl, + state.inputStream[state.fileInfo.indexOf(it)], + it.mediaType, + it.fileSize, + ) + } + viewModel.onNavigateToNextScreen() + }) { Text( text = stringResource(id = R.string.next), fontSize = 15.sp, @@ -247,7 +300,7 @@ fun BusinessAuthScreen( } @Composable -fun UploadFileList(modifier: Modifier, item: MutableList) { +fun UploadFileList(modifier: Modifier, item: MutableList) { LazyColumn( modifier = modifier .fillMaxSize() @@ -269,9 +322,10 @@ fun UploadFileList(modifier: Modifier, item: MutableList) { contentDescription = stringResource(id = R.string.file_icon), tint = ColorMinor, ) - Text(text = "", fontSize = 15.sp, color = ColorMinor) + Text(text = item[it].title, fontSize = 15.sp, color = ColorMinor) } Spacer(modifier = Modifier.width(12.dp)) } } } + From fdcd1551bf32e571863f0a86b240474b227648b2 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 23 Apr 2024 16:14:02 +0900 Subject: [PATCH 023/131] Add TokenLocalDataSource --- .../di/resource/TokenLocalDataSource.kt | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/di/resource/TokenLocalDataSource.kt diff --git a/business/src/main/java/in/koreatech/business/di/resource/TokenLocalDataSource.kt b/business/src/main/java/in/koreatech/business/di/resource/TokenLocalDataSource.kt new file mode 100644 index 000000000..82fd89809 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/di/resource/TokenLocalDataSource.kt @@ -0,0 +1,106 @@ + +package `in`.koreatech.business.di.resource + +import android.content.Context +import android.util.Log +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineDispatcher +import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.job +import kotlinx.coroutines.withContext + +class TokenLocalDataSource @Inject constructor( + @ApplicationContext applicationContext: Context, + private val dispatchersIO: CoroutineDispatcher, +) { + var masterKey = MasterKey.Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + var sharedPreferences = EncryptedSharedPreferences.create( + applicationContext, + SHARED_PREF_FILENAME, + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + var sharedOwnerPreferences = EncryptedSharedPreferences.create( + applicationContext, + OWNER_SHARED_PREF_FILENAME, + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + suspend fun saveAccessToken( + accessToken: String, + ) = withContext(dispatchersIO) { + with(sharedPreferences.edit()) { + putString(SHARED_PREF_KEY, accessToken) + apply() + } + } + + suspend fun saveRefreshToken( + refreshToken: String, + ) = withContext(dispatchersIO) { + with(sharedPreferences.edit()) { + putString(SHARED_PREF_REFRESH_KEY, refreshToken) + apply() + } + } + + suspend fun getAccessToken(): String? = withContext(dispatchersIO) { + sharedPreferences.getString(SHARED_PREF_KEY, null) + } + + suspend fun getRefreshToken(): String? = withContext(dispatchersIO) { + sharedPreferences.getString(SHARED_PREF_REFRESH_KEY, null) + } + + suspend fun removeAccessToken() = withContext(dispatchersIO) { + with(sharedPreferences.edit()) { + remove(SHARED_PREF_KEY) + apply() + } + } + + suspend fun saveOwnerAccessToken( + accessToken: String + ) = with(Dispatchers.IO) { + with(sharedOwnerPreferences.edit()) { + putString(OWNER_SHARED_PREF_FILENAME, accessToken) + apply() + } + } + + suspend fun getOwnerAccessToken(): String? = withContext(Dispatchers.IO) { + sharedOwnerPreferences.getString(OWNER_SHARED_PREF_FILENAME, null) + } + + suspend fun removeOwnerAccessToken() = withContext(Dispatchers.IO) { + with(sharedOwnerPreferences.edit()) { + remove(OWNER_SHARED_PREF_FILENAME) + apply() + } + } + + suspend fun removeRefreshToken() = withContext(dispatchersIO) { + with(sharedPreferences.edit()) { + remove(SHARED_PREF_REFRESH_KEY) + apply() + } + } + + companion object { + private const val SHARED_PREF_FILENAME = "token" + private const val OWNER_SHARED_PREF_FILENAME = "ownerToken" + + private const val SHARED_PREF_KEY = "accessToken" + private const val SHARED_PREF_REFRESH_KEY = "refreshToken" + } +} From 1fcfdcd554b3aa6ca775bd613a28594e77391aad Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 23 Apr 2024 16:14:30 +0900 Subject: [PATCH 024/131] Add BusinessAuthNetworkModule --- .../business/di/network/AuthNetworkModule.kt | 122 +++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/di/network/AuthNetworkModule.kt b/business/src/main/java/in/koreatech/business/di/network/AuthNetworkModule.kt index 5eab72ff9..1eb12fcf0 100644 --- a/business/src/main/java/in/koreatech/business/di/network/AuthNetworkModule.kt +++ b/business/src/main/java/in/koreatech/business/di/network/AuthNetworkModule.kt @@ -6,10 +6,15 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import `in`.koreatech.business.util.OwnerTokenAuthenticator import `in`.koreatech.business.util.RefreshTokenInterceptor import `in`.koreatech.koin.core.qualifier.Auth +import `in`.koreatech.koin.core.qualifier.OwnerAuth +import `in`.koreatech.koin.core.qualifier.PreSignedUrl import `in`.koreatech.koin.core.qualifier.Refresh import `in`.koreatech.koin.core.qualifier.ServerUrl +import `in`.koreatech.koin.data.api.PreSignedUrlApi +import `in`.koreatech.koin.data.api.UploadUrlApi import `in`.koreatech.koin.data.api.UserApi import `in`.koreatech.koin.data.api.auth.UserAuthApi import `in`.koreatech.koin.data.source.local.TokenLocalDataSource @@ -90,7 +95,122 @@ object AuthNetworkModule { @Singleton fun provideUserAuthApi( @Auth retrofit: Retrofit - ) : UserAuthApi { + ): UserAuthApi { return retrofit.create(UserAuthApi::class.java) } + +} + + +@Module +@InstallIn(SingletonComponent::class) +object BusinessAuthNetworkModule { + @OwnerAuth + @Provides + @Singleton + fun provideOwnerAuthInterceptor( + tokenLocalDataSource: TokenLocalDataSource + ): Interceptor { + return Interceptor { chain: Interceptor.Chain -> + runBlocking { + val ownerAccessToken = tokenLocalDataSource.getOwnerAccessToken() ?: "" + val newRequest: Request = chain.request().newBuilder() + .addHeader("Authorization", "Bearer $ownerAccessToken") + .build() + chain.proceed(newRequest) + } + } + } + + @OwnerAuth + @Provides + @Singleton + fun provideTokenAuthenticator( + @ApplicationContext applicationContext: Context, + tokenLocalDataSource: TokenLocalDataSource + ) = OwnerTokenAuthenticator(applicationContext, tokenLocalDataSource) + + @OwnerAuth + @Provides + @Singleton + fun provideOwnerAuthOkHttpClient( + httpLoggingInterceptor: HttpLoggingInterceptor, + @OwnerAuth ownerAuthInterceptor: Interceptor, + @OwnerAuth tokenAuthenticator: OwnerTokenAuthenticator + ): OkHttpClient { + return OkHttpClient.Builder().apply { + connectTimeout(10, TimeUnit.SECONDS) + readTimeout(30, TimeUnit.SECONDS) + writeTimeout(15, TimeUnit.SECONDS) + addInterceptor(httpLoggingInterceptor) + addInterceptor(ownerAuthInterceptor) + authenticator(tokenAuthenticator) + }.build() + } + + @OwnerAuth + @Provides + @Singleton + fun provideOwnerAuthRetrofit( + @ServerUrl baseUrl: String, + @OwnerAuth ownerOkHttpClient: OkHttpClient + ): Retrofit { + return Retrofit.Builder() + .client(ownerOkHttpClient) + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .build() + } + + @Provides + @Singleton + fun provideUploadUrlApi( + @OwnerAuth retrofit: Retrofit + ): UploadUrlApi { + return retrofit.create(UploadUrlApi::class.java) + } +} + + + + +@Module +@InstallIn(SingletonComponent::class) +object PreSignedUrlNetworkModule { + @PreSignedUrl + @Provides + @Singleton + fun provideOwnerAuthOkHttpClient( + httpLoggingInterceptor: HttpLoggingInterceptor + ): OkHttpClient { + return OkHttpClient.Builder().apply { + connectTimeout(10, TimeUnit.SECONDS) + readTimeout(30, TimeUnit.SECONDS) + writeTimeout(15, TimeUnit.SECONDS) + addInterceptor(httpLoggingInterceptor) + }.build() + } + + @Provides + @Singleton + @PreSignedUrl + fun providePreSignedUrlRetrofit(): Retrofit { + return Retrofit.Builder() + .baseUrl("https://kap-test.s3.ap-northeast-2.amazonaws.com/") + .client( + OkHttpClient.Builder().addInterceptor( + HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.HEADERS + } + ).build() + ).build() + } + + @Provides + @Singleton + fun provideUploadUrlApi( + @PreSignedUrl retrofit: Retrofit + ): PreSignedUrlApi { + return retrofit.create(PreSignedUrlApi::class.java) + } } \ No newline at end of file From c6350a60097b709dfe2344f8229051f620163009 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 23 Apr 2024 16:14:49 +0900 Subject: [PATCH 025/131] Add OwnerTokenAuthenticator --- .../business/util/OwnerTokenAuthenticator.kt | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/util/OwnerTokenAuthenticator.kt diff --git a/business/src/main/java/in/koreatech/business/util/OwnerTokenAuthenticator.kt b/business/src/main/java/in/koreatech/business/util/OwnerTokenAuthenticator.kt new file mode 100644 index 000000000..e71031b8a --- /dev/null +++ b/business/src/main/java/in/koreatech/business/util/OwnerTokenAuthenticator.kt @@ -0,0 +1,74 @@ +package `in`.koreatech.business.util + +import android.content.Context +import android.content.Intent +import android.os.Looper +import android.widget.Toast +import androidx.core.os.HandlerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import `in`.koreatech.business.BusinessMainActivity +import `in`.koreatech.business.R +import `in`.koreatech.business.feature.signup.businessauth.BusinessAuthState +import `in`.koreatech.koin.data.source.local.TokenLocalDataSource + +import kotlinx.coroutines.runBlocking +import okhttp3.Authenticator +import okhttp3.Request +import okhttp3.Response +import okhttp3.Route +import java.net.HttpURLConnection +import javax.inject.Inject + +class OwnerTokenAuthenticator @Inject constructor( + @ApplicationContext private val context: Context, + private val tokenLocalDataSource: TokenLocalDataSource +): Authenticator { + override fun authenticate(route: Route?, response: Response): Request? = runBlocking { + val request = try { + if(response.code != HttpURLConnection.HTTP_UNAUTHORIZED) null + else { + val accessToken = tokenLocalDataSource.getOwnerAccessToken() + if(accessToken == response.request.header("OwnerAuthorization")) { + tokenLocalDataSource.removeAccessToken() + goToLoginActivity() + null + } else { + if(accessToken.isNullOrEmpty()) { + goToLoginActivity() + null + } else { + getRequest(response, accessToken) + } + } + } + } catch (e: Exception) { + goToLoginActivity() + null + } + + request + } + + private fun getRequest(response: Response, token: String): Request { + return response.request + .newBuilder() + .removeHeader("OwnerAuthorization") + .addHeader("OwnerAuthorization", "Bearer $token") + .build() + } + + private fun goToLoginActivity() { + val handler = HandlerCompat.createAsync(Looper.getMainLooper()) + Intent(context.applicationContext, BusinessMainActivity::class.java).run { + handler.post { + Toast.makeText( + context.applicationContext, + context.getString(R.string.token_out_dated), + Toast.LENGTH_SHORT + ).show() + } + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + context.startActivity(this) + } + } +} \ No newline at end of file From 3c19e9ffef411de5ea532157ab566760f9ac38b9 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 24 Apr 2024 01:20:50 +0900 Subject: [PATCH 026/131] Change value name storeNumber->shopNumber --- .../feature/signup/businessauth/BusinessAuthScreen.kt | 4 ++-- .../business/feature/signup/businessauth/BusinessAuthState.kt | 2 +- .../feature/signup/businessauth/BusinessAuthViewModel.kt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index b32b7df7d..b8e8f87d1 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -169,7 +169,7 @@ fun BusinessAuthScreen( LinedTextField( modifier = Modifier.width(197.dp), value = state.storeName, - onValueChange = { viewModel.onStoreNameChanged(it) }, + onValueChange = { viewModel.onShopNameChanged(it) }, label = stringResource(id = R.string.enter_store_name) ) Button( @@ -185,7 +185,7 @@ fun BusinessAuthScreen( } } LinedTextField( - value = state.storeNumber, + value = state.shopNumber, onValueChange = { viewModel.onStoreNumberChanged(it) }, label = stringResource(id = R.string.business_registration_number) ) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index d6723acf8..5d4053af3 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -7,7 +7,7 @@ import java.io.InputStream data class BusinessAuthState( val name: String = "", val storeName: String = "", - val storeNumber: String = "", + val shopNumber: String = "", val phoneNumber: String = "", val openAlertDialog: Boolean = false, val selectedImages :MutableList = mutableListOf(), diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index b0144af0d..b9230778a 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -34,7 +34,7 @@ class BusinessAuthViewModel @Inject constructor( } } - fun onStoreNameChanged(storeName: String) = intent { + fun onShopNameChanged(storeName: String) = intent { reduce { state.copy(storeName = storeName) } @@ -42,7 +42,7 @@ class BusinessAuthViewModel @Inject constructor( fun onStoreNumberChanged(storeNumber: String) = intent { reduce { - state.copy(storeNumber = storeNumber) + state.copy(shopNumber = storeNumber) } } From 3fd2c8ce23176bfc26eaadd14eda935425686c35 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 24 Apr 2024 22:00:45 +0900 Subject: [PATCH 027/131] Add email and password to NavigateToNextScreen data class --- .../signup/accountauth/EmailAuthScreen.kt | 7 +-- .../signup/accountauth/EmailAuthSideEffect.kt | 2 +- .../signup/accountauth/EmailAuthViewModel.kt | 8 ++-- .../signup/accountsetup/AccountSetupScreen.kt | 6 +-- .../accountsetup/AccountSetupSideEffect.kt | 2 +- .../accountsetup/AccountSetupViewModel.kt | 2 +- .../signup/businessauth/BusinessAuthScreen.kt | 17 ++++++- .../signup/businessauth/BusinessAuthState.kt | 6 ++- .../businessauth/BusinessAuthViewModel.kt | 48 ++++++++++++++++++- .../signup/navigator/SignupNavigator.kt | 32 ++++++++++--- 10 files changed, 105 insertions(+), 25 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt index 565a5290c..4985167f1 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt @@ -44,10 +44,11 @@ import org.orbitmvi.orbit.compose.collectSideEffect fun EmailAuthScreen( modifier: Modifier = Modifier, email: String, + password: String, verifyViewModel: EmailAuthViewModel = hiltViewModel(), sendEmailViewModel: AccountSetupViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, - onNextClicked: () -> Unit = {}, + onNextClicked: (String, String) -> Unit = { _, _ -> }, ) { val state = verifyViewModel.collectAsState().value @@ -184,7 +185,7 @@ fun EmailAuthScreen( disabledContentColor = Color.White, ), onClick = { - verifyViewModel.verifyEmail(email, state.authCode) + verifyViewModel.verifyEmail(email, password, state.authCode) }) { Text(text = stringResource(id = R.string.next)) } @@ -193,7 +194,7 @@ fun EmailAuthScreen( verifyViewModel.collectSideEffect { when (it) { - is EmailAuthSideEffect.NavigateToNextScreen -> onNextClicked() + is EmailAuthSideEffect.NavigateToNextScreen -> onNextClicked(email, password) EmailAuthSideEffect.NavigateToBackScreen -> onBackClicked() } } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt index aab76b670..4f8690a46 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt @@ -2,5 +2,5 @@ package `in`.koreatech.business.feature.signup.accountauth sealed class EmailAuthSideEffect { data object NavigateToBackScreen : EmailAuthSideEffect() - data object NavigateToNextScreen : EmailAuthSideEffect() + data class NavigateToNextScreen(val email:String, val password:String) : EmailAuthSideEffect() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt index 1eadd300c..85a5fac51 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt @@ -26,8 +26,8 @@ class EmailAuthViewModel @Inject constructor( } } - private fun onNextButtonClicked() = intent { - postSideEffect(EmailAuthSideEffect.NavigateToNextScreen) + private fun onNextButtonClicked(email: String, password: String) = intent { + postSideEffect(EmailAuthSideEffect.NavigateToNextScreen(email, password)) } fun onBackButtonClicked() = intent { @@ -40,13 +40,13 @@ class EmailAuthViewModel @Inject constructor( } } - fun verifyEmail(email: String, verificationCode: String) { + fun verifyEmail(email: String, password: String, verificationCode: String) { intent { reduce { state.copy(isLoading = true) } } viewModelScope.launch(Dispatchers.IO) { emailAuthUseCase(email, verificationCode) .onSuccess { - onNextButtonClicked() + onNextButtonClicked(email, password) intent { reduce { state.copy(signupContinuationState = it) } reduce { state.copy(signUpContinuationError = null) } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index ea3a33be0..aac09c766 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -45,7 +45,7 @@ fun AccountSetupScreen( modifier: Modifier = Modifier, viewModel: AccountSetupViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, - onNextClicked: (String) -> Unit = {}, + onNextClicked: (String, String) -> Unit = { _, _ ->}, ) { val state = viewModel.collectAsState().value Column( @@ -180,7 +180,7 @@ fun AccountSetupScreen( viewModel.collectSideEffect { when (it) { - is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(it.email) + is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(state.email, state.password) AccountSetupSideEffect.NavigateToBackScreen -> onBackClicked() } } @@ -191,7 +191,7 @@ fun AccountSetupScreen( fun AccountSetupScreenPreview() { KOIN_ANDROIDTheme { AccountSetupScreen( - onNextClicked = {}, + onNextClicked = { _, _ ->}, onBackClicked = {} ) } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt index 556e5be7d..8d32daaf9 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt @@ -2,5 +2,5 @@ package `in`.koreatech.business.feature.signup.accountsetup sealed class AccountSetupSideEffect { data object NavigateToBackScreen : AccountSetupSideEffect() - data class NavigateToNextScreen(val email: String) : AccountSetupSideEffect() + data class NavigateToNextScreen(val email:String , val password:String) : AccountSetupSideEffect() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 87e1a56cd..46661447f 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -48,7 +48,7 @@ class AccountSetupViewModel @Inject constructor( } fun onNextButtonClicked() = intent { - postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.email)) + postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.email, state.password)) } fun onBackButtonClicked() = intent { diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index b8e8f87d1..bfaac1b7e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -1,6 +1,5 @@ package `in`.koreatech.business.feature.signup.businessauth -import android.net.Uri import android.provider.OpenableColumns import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest @@ -57,6 +56,8 @@ import org.orbitmvi.orbit.compose.collectSideEffect fun BusinessAuthScreen( modifier: Modifier = Modifier, viewModel: BusinessAuthViewModel = hiltViewModel(), + email: String, + password: String, onBackClicked: () -> Unit = {}, onSearchClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, @@ -257,6 +258,16 @@ fun BusinessAuthScreen( it.fileSize, ) } + viewModel.sendRegisterRequest( + state.fileUrl, + state.shopNumber, + email, + state.name, + password, + state.phoneNumber, + state.shopId, + state.storeName, + ) viewModel.onNavigateToNextScreen() }) { Text( @@ -266,7 +277,7 @@ fun BusinessAuthScreen( ) BusinessAlertDialog( - onDismissRequest = { viewModel.onDialogVisibilityChanged(false)}, + onDismissRequest = { viewModel.onDialogVisibilityChanged(false) }, onConfirmation = { multiplePhotoPickerLauncher.launch( PickVisualMediaRequest( @@ -287,9 +298,11 @@ fun BusinessAuthScreen( BusinessAuthSideEffect.NavigateToSearchStore -> { onSearchClicked() } + BusinessAuthSideEffect.NavigateToBackScreen -> { onBackClicked() } + BusinessAuthSideEffect.NavigateToNextScreen -> { onNextClicked() } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index 5d4053af3..b41dea4a3 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -8,12 +8,14 @@ data class BusinessAuthState( val name: String = "", val storeName: String = "", val shopNumber: String = "", + val shopId: Int = 0, val phoneNumber: String = "", val openAlertDialog: Boolean = false, val selectedImages :MutableList = mutableListOf(), val dialogVisibility:Boolean = false, val fileInfo: MutableList = mutableListOf(), + val fileUrl: MutableList = mutableListOf(), val inputStream: MutableList = mutableListOf(), - val continuation :Boolean = false, - val error :Throwable? = null, + val continuation: Boolean = false, + val error: Throwable? = null, ) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index b9230778a..f22b1ced8 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -4,6 +4,7 @@ import android.net.Uri import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.core.viewmodel.BaseViewModel +import `in`.koreatech.koin.data.mapper.strToOwnerRegisterUrl import `in`.koreatech.koin.domain.model.store.AttachStore import `in`.koreatech.koin.domain.model.store.StoreUrl import `in`.koreatech.koin.domain.usecase.owner.AttachStoreFileUseCase @@ -40,6 +41,11 @@ class BusinessAuthViewModel @Inject constructor( } } + fun onShopIdChanged(shopId: Int) = intent { + reduce { + state.copy(shopId = shopId) + } + } fun onStoreNumberChanged(storeNumber: String) = intent { reduce { state.copy(shopNumber = storeNumber) @@ -91,7 +97,16 @@ class BusinessAuthViewModel @Inject constructor( reduce { state.copy( fileInfo = state.fileInfo.toMutableList().apply { - add(StoreUrl(uri.toString(), it.first, fileName, fileType, it.second, fileSize)) + add( + StoreUrl( + uri.toString(), + it.first, + fileName, + fileType, + it.second, + fileSize + ) + ) } ) } @@ -124,6 +139,9 @@ class BusinessAuthViewModel @Inject constructor( intent { reduce { state.copy(error = null) } } + intent { reduce { state.copy(fileUrl = state.fileUrl.toMutableList().apply{ + add(url) + }) } } }.onFailure { intent { reduce { state.copy(error = it) } @@ -131,4 +149,30 @@ class BusinessAuthViewModel @Inject constructor( } } } -} + + fun sendRegisterRequest(//등록 요청 + fileUrls: List, + companyNumber: String, + email: String, + name: String, + password: String, + phoneNumber: String, + shopId: Int, + shopName: String + ) { + viewModelScope.launch { + ownerRegisterUseCase( + fileUrls.strToOwnerRegisterUrl(), companyNumber, email, name, password, phoneNumber, shopId, shopName + ).onSuccess { + intent { + reduce { state.copy(continuation = true) } + } + }.onFailure { + intent { + reduce { state.copy(error = it) } + } + } + + } + } +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt index bee9ed2cd..03ef54892 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt @@ -21,7 +21,7 @@ fun SignupNavigator(modifier: Modifier) { val navController = rememberNavController() NavHost( navController = navController, - startDestination = SignupRoute.EMAIL_AUTH.name, + startDestination = SignupRoute.BASIC_INFO_INPUT.name, modifier = Modifier.padding(16.dp) ) { @@ -30,34 +30,54 @@ fun SignupNavigator(modifier: Modifier) { ) { AccountSetupScreen( onBackClicked = { navController.popBackStack() }, - onNextClicked = { navController.navigate("${SignupRoute.EMAIL_AUTH.name}/$it") + onNextClicked = { email, password -> + navController.navigate("${SignupRoute.EMAIL_AUTH.name}/$email/$password") + }, ) } composable( - route = "${SignupRoute.EMAIL_AUTH.name}/{email}", + route = "${SignupRoute.EMAIL_AUTH.name}/{email}/{password}", arguments = listOf( navArgument("email") { type = NavType.StringType nullable = true + }, + navArgument("password") { + type = NavType.StringType + nullable = true } ) ) { val email = it.arguments?.getString("email") ?: "" + val password = it.arguments?.getString("password") ?: "" EmailAuthScreen( email = email, + password = password, onBackClicked = { navController.popBackStack() }, - onNextClicked = { - navController.navigate(SignupRoute.BUSINESS_AUTH.name) + onNextClicked = { email, password-> + navController.navigate("${SignupRoute.BUSINESS_AUTH.name}/$email/$password") }, ) } composable( - route = SignupRoute.BUSINESS_AUTH.name, + route = "${SignupRoute.BUSINESS_AUTH.name}/{email}/{password}", + arguments = listOf( + navArgument("email") { + type = NavType.StringType + nullable = true + }, + navArgument("password") { + type = NavType.StringType + nullable = true + } + ) ) { BusinessAuthScreen( + email= it.arguments?.getString("email") ?: "", + password= it.arguments?.getString("password") ?: "", onBackClicked = { navController.popBackStack() }, onSearchClicked = { navController.navigate(SignupRoute.STORE_SETUP.name) From 81ef354eea07ea550415f3db52824fc0cf6968dd Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 24 Apr 2024 22:25:43 +0900 Subject: [PATCH 028/131] Add security-crypto dependency --- business/build.gradle.kts | 1 + gradle/libs.versions.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/business/build.gradle.kts b/business/build.gradle.kts index 1bf039773..5008740ce 100644 --- a/business/build.gradle.kts +++ b/business/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(libs.bundles.compose) implementation(libs.lifecycle.runtime.ktx) implementation(libs.compose.navigation) + implementation(libs.androidx.security.crypto) implementation(project(mapOf("path" to ":domain"))) implementation(project(mapOf("path" to ":data"))) implementation(project(mapOf("path" to ":core"))) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a0288ba1..cfaf6e578 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,6 +73,7 @@ compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } compose-navigation = {module = "androidx.navigation:navigation-compose", version.ref = "composeNavigationVersion"} +security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCryptoVersion" } android-gradle-tool = { module = "com.android.tools.build:gradle", version.ref = "androidGradleVersion" } android-gradle-crashlytics = { module = "com.google.firebase:firebase-crashlytics-gradle", version.ref = "firebaseGradleVersion" } From 186260872151ee259d39712ea460ac9d0c3d1361 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 25 Apr 2024 16:18:08 +0900 Subject: [PATCH 029/131] Modify wrong code --- .../feature/signup/businessauth/BusinessAuthScreen.kt | 7 +++---- .../feature/signup/businessauth/BusinessAuthState.kt | 1 - .../feature/signup/businessauth/BusinessAuthViewModel.kt | 4 +--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index bfaac1b7e..0b31115f0 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -74,7 +74,6 @@ fun BusinessAuthScreen( state.inputStream.clear() state.fileInfo.clear() uriList.forEach { - state.selectedImages.add(AttachStore(it.toString(), it.lastPathSegment ?: "")) val inputStream = context.contentResolver.openInputStream(it) viewModel.onImageUrlsChanged(state.selectedImages) @@ -92,11 +91,11 @@ fun BusinessAuthScreen( } } } - + state.selectedImages.add(AttachStore(it.toString(), fileName)) if (inputStream != null) { viewModel.presignedUrl( uri = it, - fileName = it.lastPathSegment ?: "", + fileName = fileName, fileSize = fileSize, fileType = "image/" + fileName.split(".")[1], fileStream = inputStream @@ -268,7 +267,7 @@ fun BusinessAuthScreen( state.shopId, state.storeName, ) - viewModel.onNavigateToNextScreen() + }) { Text( text = stringResource(id = R.string.next), diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index b41dea4a3..0be412e49 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -14,7 +14,6 @@ data class BusinessAuthState( val selectedImages :MutableList = mutableListOf(), val dialogVisibility:Boolean = false, val fileInfo: MutableList = mutableListOf(), - val fileUrl: MutableList = mutableListOf(), val inputStream: MutableList = mutableListOf(), val continuation: Boolean = false, val error: Throwable? = null, diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index f22b1ced8..aa8d8ae6b 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -139,9 +139,6 @@ class BusinessAuthViewModel @Inject constructor( intent { reduce { state.copy(error = null) } } - intent { reduce { state.copy(fileUrl = state.fileUrl.toMutableList().apply{ - add(url) - }) } } }.onFailure { intent { reduce { state.copy(error = it) } @@ -164,6 +161,7 @@ class BusinessAuthViewModel @Inject constructor( ownerRegisterUseCase( fileUrls.strToOwnerRegisterUrl(), companyNumber, email, name, password, phoneNumber, shopId, shopName ).onSuccess { + onNavigateToNextScreen() intent { reduce { state.copy(continuation = true) } } From 09045d87336f1a4f24f453e616ac55c86301c77c Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 30 Apr 2024 15:28:38 +0900 Subject: [PATCH 030/131] Modify data type --- .../signup/businessauth/BusinessAuthState.kt | 2 +- .../repository/OwnerRegisterRepositoryImpl.kt | 2 +- .../data/request/owner/OwnerRegisterRequest.kt | 16 ++++++++-------- .../domain/repository/OwnerRegisterRepository.kt | 2 +- .../domain/usecase/owner/OwnerRegisterUseCase.kt | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index 0be412e49..117b7a716 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -8,7 +8,7 @@ data class BusinessAuthState( val name: String = "", val storeName: String = "", val shopNumber: String = "", - val shopId: Int = 0, + val shopId: Int? = null, val phoneNumber: String = "", val openAlertDialog: Boolean = false, val selectedImages :MutableList = mutableListOf(), diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OwnerRegisterRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OwnerRegisterRepositoryImpl.kt index 7741ab552..b1b324fd8 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OwnerRegisterRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OwnerRegisterRepositoryImpl.kt @@ -18,7 +18,7 @@ class OwnerRegisterRepositoryImpl( name: String, password: String, phoneNumber: String, - shopId: Int, + shopId: Int?, shopName: String ): Result { return try { diff --git a/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerRegisterRequest.kt b/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerRegisterRequest.kt index 706c3346c..3992e8c91 100644 --- a/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerRegisterRequest.kt +++ b/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerRegisterRequest.kt @@ -3,12 +3,12 @@ package `in`.koreatech.koin.data.request.owner import com.google.gson.annotations.SerializedName data class OwnerRegisterRequest( - @SerializedName("attachment_urls") val attachmentUrls: List, - @SerializedName("company_number") val companyNumber: String, - @SerializedName("email") val email: String, - @SerializedName("name") val name: String, - @SerializedName("password") val password: String, - @SerializedName("phone_number") val phoneNumber: String, - @SerializedName("shop_id") val shopId: Int, - @SerializedName("shop_name") val shopName: String + @SerializedName("attachment_urls") val attachmentUrls: List?, + @SerializedName("company_number") val companyNumber: String?, + @SerializedName("email") val email: String?, + @SerializedName("name") val name: String?, + @SerializedName("password") val password: String?, + @SerializedName("phone_number") val phoneNumber: String?, + @SerializedName("shop_id") val shopId: Int?, + @SerializedName("shop_name") val shopName: String? ) \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerRegisterRepository.kt b/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerRegisterRepository.kt index c34a8af56..342841e17 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerRegisterRepository.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerRegisterRepository.kt @@ -10,7 +10,7 @@ interface OwnerRegisterRepository { name: String, password: String, phoneNumber: String, - shopId: Int, + shopId: Int?, shopName: String ): Result } \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/owner/OwnerRegisterUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/owner/OwnerRegisterUseCase.kt index 330f523b8..3d0152348 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/owner/OwnerRegisterUseCase.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/owner/OwnerRegisterUseCase.kt @@ -14,7 +14,7 @@ class OwnerRegisterUseCase @Inject constructor( name: String, password: String, phoneNumber: String, - shopId: Int, + shopId: Int?, shopName: String ): Result { return ownerRegisterRepository.ownerRegister(attachments, companyNumber, email, name, password, phoneNumber, shopId, shopName) From 8248bb1390838b2bdd3743b1b570e1c8b2072c74 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 30 Apr 2024 18:08:48 +0900 Subject: [PATCH 031/131] Add exception handling for store --- .../main/java/in/koreatech/koin/data/mapper/StoreMapper.kt | 4 ++-- .../java/in/koreatech/koin/domain/util/ext/TimeExtensions.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt b/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt index 499c349d4..597e6508d 100644 --- a/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt +++ b/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt @@ -28,7 +28,7 @@ fun StoreItemResponse.toStore(): Store = Store( openTime = it.openTime ?: "", closeTime = it.closeTime ?: "" ) - }.first(), + }.getOrElse(0){Store.OpenData(localDayOfWeekName, false, "00:00", "00:00") }, categoryIds = categoryIds.map { it.toStoreCategory() } ) @@ -49,7 +49,7 @@ fun StoreItemWithMenusResponse.toStoreWithMenu(): StoreWithMenu = StoreWithMenu( openTime = it.openTime ?: "", closeTime = it.closeTime ?: "" ) - }.orEmpty().first(), + }.orEmpty().getOrElse(0){Store.OpenData(localDayOfWeekName, false, "00:00", "00:00") }, imageUrls = imageUrls ?: emptyList(), shopCategories = shopCategories?.map { it.toCategory() }.orEmpty(), menuCategories = menuCategories?.map { it.toCategory() }.orEmpty() diff --git a/domain/src/main/java/in/koreatech/koin/domain/util/ext/TimeExtensions.kt b/domain/src/main/java/in/koreatech/koin/domain/util/ext/TimeExtensions.kt index f2158c9bb..f67d2365a 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/util/ext/TimeExtensions.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/util/ext/TimeExtensions.kt @@ -15,7 +15,7 @@ val localDayOfWeekName get() = when(localDateTimeNow.dayOfWeek.value) { 5 -> "FRIDAY" 6 -> "SATURDAY" 7 -> "SUNDAY" - else -> {} + else -> "" } val LocalTime.HHMM get() = this.format(DateTimeFormatter.ofPattern("HH:mm")) From 6f5022fa5dd2579b99d615861817ee91fc3c3284 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 30 Apr 2024 18:10:29 +0900 Subject: [PATCH 032/131] Add search store side effect --- .../feature/signup/businessauth/SearchStoreSideEffect.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt new file mode 100644 index 000000000..c5cbdd437 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt @@ -0,0 +1,8 @@ +package `in`.koreatech.business.feature.signup.businessauth + + +sealed class SearchStoreSideEffect { + data class SearchStore(val search: String) : SearchStoreSideEffect() + data object NavigateToBackScreen : SearchStoreSideEffect() + data class NavigateToNextScreen(val id:Int, val shopName:String) : SearchStoreSideEffect() +} \ No newline at end of file From a525ce043081f47da3abaca22c9bd34b08b5ad23 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 30 Apr 2024 18:10:37 +0900 Subject: [PATCH 033/131] Add search store state --- .../feature/signup/businessauth/SearchStoreState.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreState.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreState.kt new file mode 100644 index 000000000..06b1591da --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreState.kt @@ -0,0 +1,11 @@ +package `in`.koreatech.business.feature.signup.businessauth + +import `in`.koreatech.koin.domain.model.store.Store +import kotlinx.coroutines.Job + +data class SearchStoreState( + val search:String="", + val stores: List = emptyList(), + val itemIndex: Int = -1, + val searchJob: Job? = null +) \ No newline at end of file From 218f54e8395584c6a33377d2a2c82840bdc5e2bd Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 30 Apr 2024 18:11:30 +0900 Subject: [PATCH 034/131] Add search store viewModel --- .../businessauth/SearchStoreViewModel.kt | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt new file mode 100644 index 000000000..f316e65d0 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt @@ -0,0 +1,66 @@ +package `in`.koreatech.business.feature.signup.businessauth + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.koreatech.koin.core.viewmodel.BaseViewModel +import `in`.koreatech.koin.domain.usecase.store.GetStoresUseCase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container +import javax.inject.Inject + +@HiltViewModel +class SearchStoreViewModel @Inject constructor( + private val getStoresUseCase: GetStoresUseCase, +) : ContainerHost, BaseViewModel() { + override val container = + container(SearchStoreState()) + + fun onItemIndexChange(index: Int) = intent { + reduce { + state.copy(itemIndex = index) + } + } + + fun onSearchChanged(search: String) = intent { + postSideEffect(SearchStoreSideEffect.SearchStore(search)) + } + + fun onNavigateToBackScreen() = intent { + postSideEffect(SearchStoreSideEffect.NavigateToBackScreen) + } + + fun onNavigateToNextScreen(shopId: Int, shopName: String) = intent { + postSideEffect(SearchStoreSideEffect.NavigateToNextScreen(shopId, shopName)) + } + + init { + intent { + viewModelScope.launch(Dispatchers.IO) { + state.searchJob?.cancel() + val newSearchJob = launch { + + getStoresUseCase(null, "").let { stores -> + reduce { + state.copy(stores = stores) + } + } + } + reduce { + state.copy(searchJob = newSearchJob) + } + + } + } + } +} \ No newline at end of file From 6ea27dbd553ef0f7bf9ec6ac8ea8358662166a77 Mon Sep 17 00:00:00 2001 From: nazero Date: Tue, 30 Apr 2024 18:20:12 +0900 Subject: [PATCH 035/131] Add button enable logic --- .../feature/signup/businessauth/BusinessAuthScreen.kt | 1 + .../feature/signup/businessauth/BusinessAuthState.kt | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index 0b31115f0..32fff0105 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -241,6 +241,7 @@ fun BusinessAuthScreen( .fillMaxWidth() .height(44.dp), shape = RectangleShape, + enabled = state.isButtonEnabled, colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, disabledBackgroundColor = ColorDisabledButton, diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index 117b7a716..7779cb878 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -17,4 +17,7 @@ data class BusinessAuthState( val inputStream: MutableList = mutableListOf(), val continuation: Boolean = false, val error: Throwable? = null, -) +){ + val isButtonEnabled: Boolean + get() = name.isNotEmpty() && storeName.isNotEmpty() && shopNumber.isNotEmpty() && phoneNumber.isNotEmpty() && selectedImages.isNotEmpty() +} From e942c7b54bf84428680d69573739ba56ca16ea4e Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 19:07:38 +0900 Subject: [PATCH 036/131] Modify saveOwnerAccessToken call to use safe call operator --- .../koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt index 94eed81fd..97a01fee2 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/EmailAuthUseCase.kt @@ -14,7 +14,8 @@ class EmailAuthUseCase @Inject constructor( ): Result { return try { val authToken = ownerVerificationCodeRepository.compareVerificationCode(address, verificationCode) - tokenRepository.saveOwnerAccessToken(authToken.getOrDefault(defaultValue = null)!!.token) + authToken.getOrDefault(defaultValue = null) + ?.let { tokenRepository.saveOwnerAccessToken(it.token) } Result.success(Unit) } catch (t: Throwable) { Result.failure(t) From 05f2cb47c7ca212f636e5e1db7073c2652c90b7c Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 19:08:31 +0900 Subject: [PATCH 037/131] Add uploadFile function to PreSignedUrlRepository --- .../koin/domain/repository/PreSignedUrlRepository.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/domain/src/main/java/in/koreatech/koin/domain/repository/PreSignedUrlRepository.kt b/domain/src/main/java/in/koreatech/koin/domain/repository/PreSignedUrlRepository.kt index e53c63224..334d30931 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/repository/PreSignedUrlRepository.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/repository/PreSignedUrlRepository.kt @@ -10,4 +10,11 @@ interface PreSignedUrlRepository { mediaType: String, mediaSize: Long ): Result + + suspend fun uploadFile( + url: String, + bitmap: String, + mediaType: String, + mediaSize: Long + ): Result } \ No newline at end of file From 466aa3a0ad7f1d0349bb5fb5ee95724a54d47e55 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 19:08:46 +0900 Subject: [PATCH 038/131] Add UploadFileUseCase --- .../usecase/business/UploadFileUseCase.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 domain/src/main/java/in/koreatech/koin/domain/usecase/business/UploadFileUseCase.kt diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/UploadFileUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/UploadFileUseCase.kt new file mode 100644 index 000000000..6c978dcd6 --- /dev/null +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/UploadFileUseCase.kt @@ -0,0 +1,17 @@ +package `in`.koreatech.koin.domain.usecase.business + +import `in`.koreatech.koin.domain.repository.PreSignedUrlRepository +import javax.inject.Inject + +class UploadFileUseCase @Inject constructor( + private val preSignedUrlRepository: PreSignedUrlRepository +) { + suspend operator fun invoke( + url: String, + bitmap: String, + mediaType: String, + mediaSize: Long + ): Result { + return preSignedUrlRepository.uploadFile(url, bitmap, mediaType, mediaSize) + } +} \ No newline at end of file From 1992f9c7a3c3cef5192bcb5c438f051206681da9 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 19:10:55 +0900 Subject: [PATCH 039/131] Add uploadFile function to PreSignedUrlRepositoryImpl --- .../repository/PreSignedUrlRepositoryImpl.kt | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt index 9ffd7b04a..78ac5173a 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt @@ -4,6 +4,8 @@ import `in`.koreatech.koin.data.requestbody.S3RequestBody import `in`.koreatech.koin.data.source.remote.PreSignedUrlRemoteDataSource import `in`.koreatech.koin.domain.repository.PreSignedUrlRepository import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody import retrofit2.HttpException import java.io.InputStream import javax.inject.Inject @@ -24,4 +26,21 @@ class PreSignedUrlRepositoryImpl @Inject constructor( } } -} \ No newline at end of file + override suspend fun uploadFile( + url: String, + bitmap: String, + mediaType: String, + mediaSize: Long + ): Result { + return try { + val file = bitmap.toRequestBody(mediaType.toMediaTypeOrNull()) + preSignedUrlRemoteDataSource.putPreSignedUrl(url, file) + Result.success(Unit) + } catch (e: HttpException) { + Result.failure(e) + } catch (t: Throwable) { + t.printStackTrace() + Result.failure(t) + } + } +} From 49e7c92bfc748a56e198e27e6ec822e3f7e05bbf Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 19:21:56 +0900 Subject: [PATCH 040/131] Change inputStream to bitmap type --- .../signup/businessauth/BusinessAuthScreen.kt | 17 ++++++----- .../signup/businessauth/BusinessAuthState.kt | 4 +-- .../businessauth/BusinessAuthViewModel.kt | 30 +++++++++---------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index 32fff0105..5283f1db9 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -1,5 +1,6 @@ package `in`.koreatech.business.feature.signup.businessauth +import android.graphics.BitmapFactory.decodeStream import android.provider.OpenableColumns import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest @@ -71,12 +72,11 @@ fun BusinessAuthScreen( val multiplePhotoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickMultipleVisualMedia(), onResult = { uriList -> - state.inputStream.clear() + state.bitmap.clear() state.fileInfo.clear() uriList.forEach { val inputStream = context.contentResolver.openInputStream(it) - viewModel.onImageUrlsChanged(state.selectedImages) - + viewModel.onImageUrlsChanged(mutableListOf()) if (it.scheme.equals("content")) { val cursor = context.contentResolver.query(it, null, null, null, null) cursor.use { @@ -91,17 +91,18 @@ fun BusinessAuthScreen( } } } - state.selectedImages.add(AttachStore(it.toString(), fileName)) + viewModel.onImageUrlsChanged(uriList.map { AttachStore(it.toString(), fileName) }.toMutableList()) if (inputStream != null) { - viewModel.presignedUrl( + viewModel.getPreSignedUrl( uri = it, fileName = fileName, fileSize = fileSize, fileType = "image/" + fileName.split(".")[1], - fileStream = inputStream + bitmap = decodeStream(inputStream) ) } + inputStream?.close() } } ) @@ -253,13 +254,13 @@ fun BusinessAuthScreen( state.fileInfo.forEach { viewModel.uploadImage( it.preSignedUrl, - state.inputStream[state.fileInfo.indexOf(it)], + state.bitmap[state.fileInfo.indexOf(it)].toString(), it.mediaType, it.fileSize, ) } viewModel.sendRegisterRequest( - state.fileUrl, + state.fileInfo.map { it.resultUrl }, state.shopNumber, email, state.name, diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index 7779cb878..c1ffc9e5e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -1,8 +1,8 @@ package `in`.koreatech.business.feature.signup.businessauth +import android.graphics.Bitmap import `in`.koreatech.koin.domain.model.store.AttachStore import `in`.koreatech.koin.domain.model.store.StoreUrl -import java.io.InputStream data class BusinessAuthState( val name: String = "", @@ -14,7 +14,7 @@ data class BusinessAuthState( val selectedImages :MutableList = mutableListOf(), val dialogVisibility:Boolean = false, val fileInfo: MutableList = mutableListOf(), - val inputStream: MutableList = mutableListOf(), + val bitmap: MutableList = mutableListOf(), val continuation: Boolean = false, val error: Throwable? = null, ){ diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index aa8d8ae6b..2eb3923b3 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -1,5 +1,6 @@ package `in`.koreatech.business.feature.signup.businessauth +import android.graphics.Bitmap import android.net.Uri import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -7,9 +8,9 @@ import `in`.koreatech.koin.core.viewmodel.BaseViewModel import `in`.koreatech.koin.data.mapper.strToOwnerRegisterUrl import `in`.koreatech.koin.domain.model.store.AttachStore import `in`.koreatech.koin.domain.model.store.StoreUrl +import `in`.koreatech.koin.domain.usecase.business.UploadFileUseCase import `in`.koreatech.koin.domain.usecase.owner.AttachStoreFileUseCase import `in`.koreatech.koin.domain.usecase.owner.OwnerRegisterUseCase -import `in`.koreatech.koin.domain.usecase.presignedurl.UploadPreSignedUrlUseCase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost @@ -17,13 +18,12 @@ import org.orbitmvi.orbit.syntax.simple.intent import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container -import java.io.InputStream import javax.inject.Inject @HiltViewModel class BusinessAuthViewModel @Inject constructor( - private val attachStoreFileUseCase: AttachStoreFileUseCase, - private val uploadPreSignedUrlUseCase: UploadPreSignedUrlUseCase, + private val getPresignedUrlUseCase: AttachStoreFileUseCase, + private val uploadFilesUseCase: UploadFileUseCase, private val ownerRegisterUseCase: OwnerRegisterUseCase ) : ContainerHost, BaseViewModel() { override val container = @@ -82,15 +82,15 @@ class BusinessAuthViewModel @Inject constructor( postSideEffect(BusinessAuthSideEffect.NavigateToNextScreen) } - fun presignedUrl(//url 생성 + fun getPreSignedUrl( uri: Uri, fileSize: Long, fileType: String, fileName: String, - fileStream: InputStream + bitmap: Bitmap ) { viewModelScope.launch { - attachStoreFileUseCase( + getPresignedUrlUseCase( fileSize, fileType, fileName ).onSuccess { intent { @@ -111,8 +111,8 @@ class BusinessAuthViewModel @Inject constructor( ) } reduce { - state.copy(inputStream = state.inputStream.toMutableList().apply { - add(fileStream) + state.copy(bitmap = state.bitmap.toMutableList().apply { + add(bitmap) }) } } @@ -128,33 +128,33 @@ class BusinessAuthViewModel @Inject constructor( } - fun uploadImage(//파일 업로드 + fun uploadImage( url: String, - inputStream: InputStream, + bitmap: String, mediaType: String, mediaSize: Long ) { viewModelScope.launch(Dispatchers.IO) { - uploadPreSignedUrlUseCase(url, inputStream, mediaType, mediaSize).onSuccess { + uploadFilesUseCase(url, bitmap, mediaType, mediaSize).onSuccess { intent { reduce { state.copy(error = null) } } }.onFailure { - intent { + intent { reduce { state.copy(error = it) } } } } } - fun sendRegisterRequest(//등록 요청 + fun sendRegisterRequest( fileUrls: List, companyNumber: String, email: String, name: String, password: String, phoneNumber: String, - shopId: Int, + shopId: Int?, shopName: String ) { viewModelScope.launch { From 6f1f3bc7211becd5b4201ac50bc414e6ec376294 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 19:28:33 +0900 Subject: [PATCH 041/131] Modify StoreBottomSheet --- .../signup/businessauth/SearchStoreScreen.kt | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt index 9460c8e25..9674f9903 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt @@ -223,24 +223,28 @@ fun StoreList(item: List, onSelected: () -> Unit = {}) { } @Composable -fun StoreBottomSheet(onSelected: () -> Unit) { +fun StoreBottomSheet(store: Store, viewModel: SearchStoreViewModel= hiltViewModel()) { Row( - modifier = Modifier.background(Color.White) - .fillMaxWidth().height(118.dp) + modifier = Modifier + .background(Color.White) + .fillMaxWidth() + .height(118.dp) .padding(20.dp), ) { - Column(modifier = Modifier,) { + Column(modifier = Modifier) { Text( modifier = Modifier.padding(bottom = 8.dp), - text = stringResource(id = R.string.store_name), + text = store.name, fontSize = 18.sp, color = Color.Black, fontWeight = Bold, ) - Row( modifier=Modifier, + Row( + modifier = Modifier, horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically) { + verticalAlignment = Alignment.CenterVertically + ) { Text( text = stringResource(id = R.string.phone_number), fontSize = 14.sp, @@ -249,20 +253,17 @@ fun StoreBottomSheet(onSelected: () -> Unit) { ) Spacer(modifier = Modifier.width(8.dp)) Text( - text = "", fontSize = 14.sp, color = Color.Black, + text =store.phone , fontSize = 14.sp, color = Color.Black, fontWeight = Bold ) - } - } Button( modifier = Modifier .fillMaxWidth() - .padding(start = 100.dp) - .height(55.dp) - , - onClick = { onSelected() }, + .padding(start = 20.dp) + .height(55.dp), + onClick = { viewModel.onNavigateToNextScreen(store.uid, store.name) }, shape = RectangleShape, ) { Text(text = stringResource(id = R.string.select), fontWeight = Bold) From 27ed55f7c920120e849c59925d7d52d8f667e7ca Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 19:30:14 +0900 Subject: [PATCH 042/131] Modify StoreList --- .../signup/businessauth/SearchStoreScreen.kt | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt index 9674f9903..0421243db 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt @@ -139,8 +139,8 @@ fun SearchStoreScreen(modifier: Modifier = Modifier, onBackClicked: () -> Unit = @OptIn(ExperimentalMaterialApi::class) @Composable -fun StoreList(item: List, onSelected: () -> Unit = {}) { - var selectedItemIndex by remember { mutableStateOf(-1) } +fun StoreList(item: List, viewModel: SearchStoreViewModel= hiltViewModel()) { + val state= viewModel.collectAsState().value val sheetState = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Hidden ) @@ -160,15 +160,15 @@ fun StoreList(item: List, onSelected: () -> Unit = {}) { .fillMaxWidth() .height(59.dp) .clickable { - selectedItemIndex = index scope.launch { sheetState.show() } + viewModel.onItemIndexChange(index) } .border( BorderStroke( - width = if (selectedItemIndex == index) 1.5.dp else 1.dp, - color = if (selectedItemIndex == index) ColorPrimary else ColorHelper + width = if (state.itemIndex == index) 1.5.dp else 1.dp, + color = if (state.itemIndex == index) ColorPrimary else ColorHelper ) ), ) { @@ -181,7 +181,7 @@ fun StoreList(item: List, onSelected: () -> Unit = {}) { ) { Text( modifier = Modifier, - text = stringResource(id = R.string.store_name), + text = item[index].name, fontSize = 15.sp, color = Color.Black, fontWeight = Bold @@ -208,17 +208,15 @@ fun StoreList(item: List, onSelected: () -> Unit = {}) { } } - ModalBottomSheetLayout( - modifier = Modifier, - sheetState = sheetState, - sheetElevation = 8.dp, - sheetContent = { - StoreBottomSheet { - onSelected() + if(state.itemIndex > -1) + ModalBottomSheetLayout( + modifier = Modifier, + sheetState = sheetState, + sheetElevation = 8.dp, + sheetContent = { + StoreBottomSheet(item[state.itemIndex]) + }) { } - }) { - } - } } From 09b45b4659e273cfad83222ca0b52505ab442a02 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 1 May 2024 21:08:09 +0900 Subject: [PATCH 043/131] Change storeName->shopName --- .../feature/signup/businessauth/BusinessAuthScreen.kt | 4 ++-- .../business/feature/signup/businessauth/BusinessAuthState.kt | 4 ++-- .../feature/signup/businessauth/BusinessAuthViewModel.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index 5283f1db9..24981b52b 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -169,7 +169,7 @@ fun BusinessAuthScreen( ) { LinedTextField( modifier = Modifier.width(197.dp), - value = state.storeName, + value = state.shopName, onValueChange = { viewModel.onShopNameChanged(it) }, label = stringResource(id = R.string.enter_store_name) ) @@ -267,7 +267,7 @@ fun BusinessAuthScreen( password, state.phoneNumber, state.shopId, - state.storeName, + state.shopName, ) }) { diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt index c1ffc9e5e..e454ddfbb 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthState.kt @@ -6,7 +6,7 @@ import `in`.koreatech.koin.domain.model.store.StoreUrl data class BusinessAuthState( val name: String = "", - val storeName: String = "", + val shopName: String = "", val shopNumber: String = "", val shopId: Int? = null, val phoneNumber: String = "", @@ -19,5 +19,5 @@ data class BusinessAuthState( val error: Throwable? = null, ){ val isButtonEnabled: Boolean - get() = name.isNotEmpty() && storeName.isNotEmpty() && shopNumber.isNotEmpty() && phoneNumber.isNotEmpty() && selectedImages.isNotEmpty() + get() = name.isNotEmpty() && shopName.isNotEmpty() && shopNumber.isNotEmpty() && phoneNumber.isNotEmpty() && selectedImages.isNotEmpty() } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index 2eb3923b3..04be61ce3 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -37,7 +37,7 @@ class BusinessAuthViewModel @Inject constructor( fun onShopNameChanged(storeName: String) = intent { reduce { - state.copy(storeName = storeName) + state.copy(shopName = storeName) } } From 5e3c4366baedd7b6cf22c37d1966973295e827f4 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 2 May 2024 11:39:16 +0900 Subject: [PATCH 044/131] Modify Email auth NavigateToNextScreen logic --- .../feature/signup/accountauth/EmailAuthScreen.kt | 7 +++---- .../feature/signup/accountauth/EmailAuthSideEffect.kt | 2 +- .../feature/signup/accountauth/EmailAuthViewModel.kt | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt index 4985167f1..565a5290c 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthScreen.kt @@ -44,11 +44,10 @@ import org.orbitmvi.orbit.compose.collectSideEffect fun EmailAuthScreen( modifier: Modifier = Modifier, email: String, - password: String, verifyViewModel: EmailAuthViewModel = hiltViewModel(), sendEmailViewModel: AccountSetupViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, - onNextClicked: (String, String) -> Unit = { _, _ -> }, + onNextClicked: () -> Unit = {}, ) { val state = verifyViewModel.collectAsState().value @@ -185,7 +184,7 @@ fun EmailAuthScreen( disabledContentColor = Color.White, ), onClick = { - verifyViewModel.verifyEmail(email, password, state.authCode) + verifyViewModel.verifyEmail(email, state.authCode) }) { Text(text = stringResource(id = R.string.next)) } @@ -194,7 +193,7 @@ fun EmailAuthScreen( verifyViewModel.collectSideEffect { when (it) { - is EmailAuthSideEffect.NavigateToNextScreen -> onNextClicked(email, password) + is EmailAuthSideEffect.NavigateToNextScreen -> onNextClicked() EmailAuthSideEffect.NavigateToBackScreen -> onBackClicked() } } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt index 4f8690a46..bd0771b7a 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthSideEffect.kt @@ -2,5 +2,5 @@ package `in`.koreatech.business.feature.signup.accountauth sealed class EmailAuthSideEffect { data object NavigateToBackScreen : EmailAuthSideEffect() - data class NavigateToNextScreen(val email:String, val password:String) : EmailAuthSideEffect() + data class NavigateToNextScreen(val email:String) : EmailAuthSideEffect() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt index 85a5fac51..a103b4780 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountauth/EmailAuthViewModel.kt @@ -26,8 +26,8 @@ class EmailAuthViewModel @Inject constructor( } } - private fun onNextButtonClicked(email: String, password: String) = intent { - postSideEffect(EmailAuthSideEffect.NavigateToNextScreen(email, password)) + private fun onNextButtonClicked(email: String) = intent { + postSideEffect(EmailAuthSideEffect.NavigateToNextScreen(email)) } fun onBackButtonClicked() = intent { @@ -40,13 +40,13 @@ class EmailAuthViewModel @Inject constructor( } } - fun verifyEmail(email: String, password: String, verificationCode: String) { + fun verifyEmail(email: String, verificationCode: String) { intent { reduce { state.copy(isLoading = true) } } viewModelScope.launch(Dispatchers.IO) { emailAuthUseCase(email, verificationCode) .onSuccess { - onNextButtonClicked(email, password) + onNextButtonClicked(email) intent { reduce { state.copy(signupContinuationState = it) } reduce { state.copy(signUpContinuationError = null) } From 3ca749f3c8268fab37a326dfe9fb9fb297939b2c Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 2 May 2024 11:41:29 +0900 Subject: [PATCH 045/131] Modify AccountSetup NavigateToNextScreen logic --- .../feature/signup/accountsetup/AccountSetupScreen.kt | 6 +++--- .../feature/signup/accountsetup/AccountSetupSideEffect.kt | 2 +- .../feature/signup/accountsetup/AccountSetupViewModel.kt | 2 +- .../business/feature/signup/navigator/SignupNavigator.kt | 5 +++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index aac09c766..3eb1d7c88 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -45,7 +45,7 @@ fun AccountSetupScreen( modifier: Modifier = Modifier, viewModel: AccountSetupViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, - onNextClicked: (String, String) -> Unit = { _, _ ->}, + onNextClicked: (String) -> Unit = {}, ) { val state = viewModel.collectAsState().value Column( @@ -180,7 +180,7 @@ fun AccountSetupScreen( viewModel.collectSideEffect { when (it) { - is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(state.email, state.password) + is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(state.email) AccountSetupSideEffect.NavigateToBackScreen -> onBackClicked() } } @@ -191,7 +191,7 @@ fun AccountSetupScreen( fun AccountSetupScreenPreview() { KOIN_ANDROIDTheme { AccountSetupScreen( - onNextClicked = { _, _ ->}, + onNextClicked = {}, onBackClicked = {} ) } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt index 8d32daaf9..9572e8c3c 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupSideEffect.kt @@ -2,5 +2,5 @@ package `in`.koreatech.business.feature.signup.accountsetup sealed class AccountSetupSideEffect { data object NavigateToBackScreen : AccountSetupSideEffect() - data class NavigateToNextScreen(val email:String , val password:String) : AccountSetupSideEffect() + data class NavigateToNextScreen(val email:String) : AccountSetupSideEffect() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 46661447f..87e1a56cd 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -48,7 +48,7 @@ class AccountSetupViewModel @Inject constructor( } fun onNextButtonClicked() = intent { - postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.email, state.password)) + postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.email)) } fun onBackButtonClicked() = intent { diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt index 03ef54892..4a6dd6980 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt @@ -30,10 +30,11 @@ fun SignupNavigator(modifier: Modifier) { ) { AccountSetupScreen( onBackClicked = { navController.popBackStack() }, - onNextClicked = { email, password -> - navController.navigate("${SignupRoute.EMAIL_AUTH.name}/$email/$password") + onNextClicked = { email-> + navController.navigate("${SignupRoute.EMAIL_AUTH.name}/$email") }, + viewModel = accountSetupViewModel ) } From 046ed6665b0be1d49772614c0016094b25eedc35 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 2 May 2024 11:47:03 +0900 Subject: [PATCH 046/131] Modify BusinessAuth NavigateToNextScreen logic --- .../signup/businessauth/BusinessAuthScreen.kt | 84 ++++++++++--------- .../signup/navigator/SignupNavigator.kt | 25 ++---- 2 files changed, 50 insertions(+), 59 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index 24981b52b..08c0942a9 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R +import `in`.koreatech.business.feature.signup.accountsetup.AccountSetupViewModel import `in`.koreatech.business.feature.signup.dialog.BusinessAlertDialog import `in`.koreatech.business.feature.textfield.LinedTextField import `in`.koreatech.business.ui.theme.ColorDescription @@ -56,27 +57,29 @@ import org.orbitmvi.orbit.compose.collectSideEffect @Composable fun BusinessAuthScreen( modifier: Modifier = Modifier, - viewModel: BusinessAuthViewModel = hiltViewModel(), - email: String, - password: String, + accountSetupViewModel: AccountSetupViewModel = hiltViewModel(), + businessAuthViewModel: BusinessAuthViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, onSearchClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, ) { val context = LocalContext.current - val state = viewModel.collectAsState().value + val businessAuthState = businessAuthViewModel.collectAsState().value + val accountSetupState = accountSetupViewModel.collectAsState().value + var fileName = "" var fileSize = 0L + val multiplePhotoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickMultipleVisualMedia(), onResult = { uriList -> - state.bitmap.clear() - state.fileInfo.clear() + businessAuthState.bitmap.clear() + businessAuthState.fileInfo.clear() uriList.forEach { val inputStream = context.contentResolver.openInputStream(it) - viewModel.onImageUrlsChanged(mutableListOf()) + businessAuthViewModel.onImageUrlsChanged(mutableListOf()) if (it.scheme.equals("content")) { val cursor = context.contentResolver.query(it, null, null, null, null) cursor.use { @@ -91,9 +94,9 @@ fun BusinessAuthScreen( } } } - viewModel.onImageUrlsChanged(uriList.map { AttachStore(it.toString(), fileName) }.toMutableList()) + businessAuthViewModel.onImageUrlsChanged(uriList.map { AttachStore(it.toString(), fileName) }.toMutableList()) if (inputStream != null) { - viewModel.getPreSignedUrl( + businessAuthViewModel.getPreSignedUrl( uri = it, fileName = fileName, fileSize = fileSize, @@ -111,7 +114,7 @@ fun BusinessAuthScreen( ) { IconButton( modifier = Modifier.padding(vertical = 24.dp), - onClick = { viewModel.onNavigateToBackScreen() }) { + onClick = { businessAuthViewModel.onNavigateToBackScreen() }) { Icon( modifier = Modifier.padding(start = 10.dp), painter = painterResource(id = R.drawable.ic_arrow_back), @@ -149,7 +152,7 @@ fun BusinessAuthScreen( drawLine( color = ColorSecondary, - start = Offset(0f - 35, 0f), + start = Offset(-35f, 0f), end = Offset(size.width + 35, size.height), strokeWidth = 4.dp.toPx(), cap = StrokeCap.Round @@ -159,8 +162,8 @@ fun BusinessAuthScreen( Spacer(modifier = Modifier.height(10.dp)) LinedTextField( - value = state.name, - onValueChange = { viewModel.onNameChanged(it) }, + value = businessAuthState.name, + onValueChange = { businessAuthViewModel.onNameChanged(it) }, label = stringResource(id = R.string.master_name) ) @@ -169,13 +172,13 @@ fun BusinessAuthScreen( ) { LinedTextField( modifier = Modifier.width(197.dp), - value = state.shopName, - onValueChange = { viewModel.onShopNameChanged(it) }, + value = businessAuthState.shopName, + onValueChange = { businessAuthViewModel.onShopNameChanged(it) }, label = stringResource(id = R.string.enter_store_name) ) Button( modifier = Modifier, - onClick = { viewModel.onNavigateToSearchStore() }, + onClick = { businessAuthViewModel.onNavigateToSearchStore() }, shape = RectangleShape, colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, @@ -186,13 +189,13 @@ fun BusinessAuthScreen( } } LinedTextField( - value = state.shopNumber, - onValueChange = { viewModel.onStoreNumberChanged(it) }, + value = businessAuthState.shopNumber, + onValueChange = { businessAuthViewModel.onStoreNumberChanged(it) }, label = stringResource(id = R.string.business_registration_number) ) LinedTextField( - value = state.phoneNumber, - onValueChange = { viewModel.onPhoneNumberChanged(it) }, + value = businessAuthState.phoneNumber, + onValueChange = { businessAuthViewModel.onPhoneNumberChanged(it) }, label = stringResource(id = R.string.personal_contact) ) @@ -202,9 +205,9 @@ fun BusinessAuthScreen( .height(125.dp), ) { - if (state.selectedImages.isNotEmpty()) UploadFileList( + if (businessAuthState.selectedImages.isNotEmpty()) UploadFileList( modifier, - state.selectedImages + businessAuthState.selectedImages ) else Column( @@ -212,7 +215,7 @@ fun BusinessAuthScreen( .fillMaxSize() .height(125.dp) .border(BorderStroke(1.dp, ColorHelper)) - .clickable { viewModel.onDialogVisibilityChanged(true) }, + .clickable { businessAuthViewModel.onDialogVisibilityChanged(true) }, verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -234,7 +237,6 @@ fun BusinessAuthScreen( color = ColorDescription, ) } - } Spacer(modifier = Modifier.height(47.dp)) @@ -242,7 +244,7 @@ fun BusinessAuthScreen( .fillMaxWidth() .height(44.dp), shape = RectangleShape, - enabled = state.isButtonEnabled, + enabled = businessAuthState.isButtonEnabled, colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, disabledBackgroundColor = ColorDisabledButton, @@ -251,23 +253,23 @@ fun BusinessAuthScreen( ), onClick = { - state.fileInfo.forEach { - viewModel.uploadImage( + businessAuthState.fileInfo.forEach { + businessAuthViewModel.uploadImage( it.preSignedUrl, - state.bitmap[state.fileInfo.indexOf(it)].toString(), + businessAuthState.bitmap[businessAuthState.fileInfo.indexOf(it)].toString(), it.mediaType, it.fileSize, ) } - viewModel.sendRegisterRequest( - state.fileInfo.map { it.resultUrl }, - state.shopNumber, - email, - state.name, - password, - state.phoneNumber, - state.shopId, - state.shopName, + businessAuthViewModel.sendRegisterRequest( + businessAuthState.fileInfo.map { it.resultUrl }, + businessAuthState.shopNumber, + accountSetupState.email, + businessAuthState.name, + accountSetupState.password, + businessAuthState.phoneNumber, + businessAuthState.shopId, + businessAuthState.shopName, ) }) { @@ -278,23 +280,23 @@ fun BusinessAuthScreen( ) BusinessAlertDialog( - onDismissRequest = { viewModel.onDialogVisibilityChanged(false) }, + onDismissRequest = { businessAuthViewModel.onDialogVisibilityChanged(false) }, onConfirmation = { multiplePhotoPickerLauncher.launch( PickVisualMediaRequest( ActivityResultContracts.PickVisualMedia.ImageOnly ) ) - viewModel.onDialogVisibilityChanged(false) + businessAuthViewModel.onDialogVisibilityChanged(false) }, dialogTitle = stringResource(id = R.string.file_upload), dialogText = stringResource(id = R.string.file_upload_requirements), positiveButtonText = stringResource(id = R.string.select_file), - visibility = state.dialogVisibility + visibility = businessAuthState.dialogVisibility ) } } - viewModel.collectSideEffect { + businessAuthViewModel.collectSideEffect { when (it) { BusinessAuthSideEffect.NavigateToSearchStore -> { onSearchClicked() diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt index 4a6dd6980..00f8819f9 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt @@ -64,29 +64,18 @@ fun SignupNavigator(modifier: Modifier) { } composable( - route = "${SignupRoute.BUSINESS_AUTH.name}/{email}/{password}", - arguments = listOf( - navArgument("email") { - type = NavType.StringType - nullable = true - }, - navArgument("password") { - type = NavType.StringType - nullable = true - } - ) + route = SignupRoute.BUSINESS_AUTH.name, ) { BusinessAuthScreen( - email= it.arguments?.getString("email") ?: "", - password= it.arguments?.getString("password") ?: "", + accountSetupViewModel= accountSetupViewModel, + businessAuthViewModel = businessAuthViewModel, onBackClicked = { navController.popBackStack() }, - onSearchClicked = { + onSearchClicked ={ navController.navigate(SignupRoute.STORE_SETUP.name) }, - onNextClicked = { - navController.navigate(SignupRoute.SIGNUP_COMPLETED.name) - }, - ) + ) { + navController.navigate(SignupRoute.SIGNUP_COMPLETED.name) + } } composable( From 3f7e28d9a3543991cd14b8a60dc053272479140e Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 2 May 2024 12:03:54 +0900 Subject: [PATCH 047/131] Modify search store logic --- .../signup/businessauth/SearchStoreScreen.kt | 58 ++++++++++--------- .../businessauth/SearchStoreSideEffect.kt | 1 - .../businessauth/SearchStoreViewModel.kt | 5 -- .../signup/navigator/SignupNavigator.kt | 12 +++- 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt index 0421243db..14de7e736 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt @@ -27,11 +27,7 @@ import androidx.compose.material.TextField import androidx.compose.material.TextFieldDefaults import androidx.compose.material.rememberModalBottomSheetState 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 @@ -46,6 +42,7 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R import `in`.koreatech.business.ui.theme.ColorActiveButton import `in`.koreatech.business.ui.theme.ColorDescription @@ -53,14 +50,19 @@ import `in`.koreatech.business.ui.theme.ColorHelper import `in`.koreatech.business.ui.theme.ColorPrimary import `in`.koreatech.business.ui.theme.ColorSearch import `in`.koreatech.business.ui.theme.ColorSecondary -import `in`.koreatech.business.ui.theme.KOIN_ANDROIDTheme +import `in`.koreatech.koin.domain.model.store.Store import kotlinx.coroutines.launch -import okhttp3.internal.immutableListOf +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect @Composable -fun SearchStoreScreen(modifier: Modifier = Modifier, onBackClicked: () -> Unit = {}) { - var search by remember { mutableStateOf("") } - val storeItems = immutableListOf("") +fun SearchStoreScreen( + modifier: Modifier = Modifier, + viewModel: SearchStoreViewModel = hiltViewModel(), + businessAuthViewModel: BusinessAuthViewModel = hiltViewModel(), + onBackClicked: () -> Unit = {} +) { + val state = viewModel.collectAsState().value Column( modifier = modifier, @@ -105,7 +107,7 @@ fun SearchStoreScreen(modifier: Modifier = Modifier, onBackClicked: () -> Unit = modifier = Modifier .fillMaxWidth() .height(50.dp), - value = search, onValueChange = { search = it }, + value = state.search, onValueChange = { viewModel.onSearchChanged(it) }, textStyle = TextStyle(fontSize = 14.sp), colors = TextFieldDefaults.textFieldColors( @@ -131,15 +133,26 @@ fun SearchStoreScreen(modifier: Modifier = Modifier, onBackClicked: () -> Unit = } ) Spacer(modifier = Modifier.height(16.dp)) - StoreList(storeItems, onSelected = {}) + StoreList(state.stores, viewModel,businessAuthViewModel) + } + + viewModel.collectSideEffect { + when (it) { + is SearchStoreSideEffect.SearchStore -> { + } + SearchStoreSideEffect.NavigateToBackScreen -> { + onBackClicked() + } + } } } + } @OptIn(ExperimentalMaterialApi::class) @Composable -fun StoreList(item: List, viewModel: SearchStoreViewModel= hiltViewModel()) { +fun StoreList(item: List, viewModel: SearchStoreViewModel= hiltViewModel(), authViewModel: BusinessAuthViewModel= hiltViewModel() ) { val state= viewModel.collectAsState().value val sheetState = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Hidden @@ -214,14 +227,15 @@ fun StoreList(item: List, viewModel: SearchStoreViewModel= hiltViewModel( sheetState = sheetState, sheetElevation = 8.dp, sheetContent = { - StoreBottomSheet(item[state.itemIndex]) + StoreBottomSheet(item[state.itemIndex], viewModel, authViewModel) }) { } } } @Composable -fun StoreBottomSheet(store: Store, viewModel: SearchStoreViewModel= hiltViewModel()) { +fun StoreBottomSheet(store: Store, viewModel: SearchStoreViewModel= hiltViewModel(), authViewModel: BusinessAuthViewModel= hiltViewModel() ) { + val state=authViewModel.collectAsState().value Row( modifier = Modifier .background(Color.White) @@ -261,7 +275,10 @@ fun StoreBottomSheet(store: Store, viewModel: SearchStoreViewModel= hiltViewMode .fillMaxWidth() .padding(start = 20.dp) .height(55.dp), - onClick = { viewModel.onNavigateToNextScreen(store.uid, store.name) }, + onClick = { + viewModel.onNavigateToBackScreen() + authViewModel.onShopIdChanged(store.uid) + authViewModel.onShopNameChanged(store.name)}, shape = RectangleShape, ) { Text(text = stringResource(id = R.string.select), fontWeight = Bold) @@ -269,14 +286,3 @@ fun StoreBottomSheet(store: Store, viewModel: SearchStoreViewModel= hiltViewMode } } - - -@Preview -@Composable -fun PreviewSearchStoreScreen() { - KOIN_ANDROIDTheme { - // StoreList(ImmutableList.of("")) - StoreBottomSheet(onSelected = {}) - } - -} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt index c5cbdd437..0ebe93d6c 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreSideEffect.kt @@ -4,5 +4,4 @@ package `in`.koreatech.business.feature.signup.businessauth sealed class SearchStoreSideEffect { data class SearchStore(val search: String) : SearchStoreSideEffect() data object NavigateToBackScreen : SearchStoreSideEffect() - data class NavigateToNextScreen(val id:Int, val shopName:String) : SearchStoreSideEffect() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt index f316e65d0..fd57fe358 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt @@ -39,11 +39,6 @@ class SearchStoreViewModel @Inject constructor( fun onNavigateToBackScreen() = intent { postSideEffect(SearchStoreSideEffect.NavigateToBackScreen) } - - fun onNavigateToNextScreen(shopId: Int, shopName: String) = intent { - postSideEffect(SearchStoreSideEffect.NavigateToNextScreen(shopId, shopName)) - } - init { intent { viewModelScope.launch(Dispatchers.IO) { diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt index 00f8819f9..f60d709c9 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -11,13 +12,18 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import `in`.koreatech.business.feature.signup.accountauth.EmailAuthScreen import `in`.koreatech.business.feature.signup.accountsetup.AccountSetupScreen +import `in`.koreatech.business.feature.signup.accountsetup.AccountSetupViewModel import `in`.koreatech.business.feature.signup.businessauth.BusinessAuthScreen +import `in`.koreatech.business.feature.signup.businessauth.BusinessAuthViewModel import `in`.koreatech.business.feature.signup.businessauth.SearchStoreScreen import `in`.koreatech.business.feature.signup.completesignup.CompleteSignupScreen @Composable -fun SignupNavigator(modifier: Modifier) { +fun SignupNavigator( + modifier: Modifier, + accountSetupViewModel: AccountSetupViewModel= hiltViewModel(), + businessAuthViewModel: BusinessAuthViewModel = hiltViewModel(),) { val navController = rememberNavController() NavHost( navController = navController, @@ -82,7 +88,9 @@ fun SignupNavigator(modifier: Modifier) { route = SignupRoute.STORE_SETUP.name, ) { SearchStoreScreen( - onBackClicked = { navController.popBackStack() }) + businessAuthViewModel = businessAuthViewModel, + onBackClicked = { + navController.popBackStack() },) } From 64cc4dd2e062bcec2fae37ae606485623c911bcf Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 2 May 2024 12:04:28 +0900 Subject: [PATCH 048/131] Modify SignupNavigator --- .../signup/navigator/SignupNavigator.kt | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt index f60d709c9..1e31e378e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/navigator/SignupNavigator.kt @@ -45,26 +45,14 @@ fun SignupNavigator( } composable( - route = "${SignupRoute.EMAIL_AUTH.name}/{email}/{password}", - arguments = listOf( - navArgument("email") { - type = NavType.StringType - nullable = true - }, - navArgument("password") { - type = NavType.StringType - nullable = true - } - ) + route = "${SignupRoute.EMAIL_AUTH.name}/{email}", ) { val email = it.arguments?.getString("email") ?: "" - val password = it.arguments?.getString("password") ?: "" EmailAuthScreen( email = email, - password = password, onBackClicked = { navController.popBackStack() }, - onNextClicked = { email, password-> - navController.navigate("${SignupRoute.BUSINESS_AUTH.name}/$email/$password") + onNextClicked = { + navController.navigate(SignupRoute.BUSINESS_AUTH.name) }, ) } From 443397f6cb44bfd982a92279d6acdafac8837894 Mon Sep 17 00:00:00 2001 From: nazero Date: Mon, 13 May 2024 23:49:17 +0900 Subject: [PATCH 049/131] Add string containment check --- .../in/koreatech/koin/domain/util/HangulInitialSoundSearch.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/domain/src/main/java/in/koreatech/koin/domain/util/HangulInitialSoundSearch.kt b/domain/src/main/java/in/koreatech/koin/domain/util/HangulInitialSoundSearch.kt index fc30a23c4..571999d39 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/util/HangulInitialSoundSearch.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/util/HangulInitialSoundSearch.kt @@ -55,6 +55,7 @@ object HangulInitialSoundSearch { val slen = trimSearch.length if (seof < 0) return false + if (trimValue.contains(trimSearch)) return true for (i in 0..seof) { t = 0 From 24e8bad44b8adb8166dd66a5c6751757d58b2515 Mon Sep 17 00:00:00 2001 From: hsgo2430 Date: Wed, 15 May 2024 12:25:04 +0900 Subject: [PATCH 050/131] =?UTF-8?q?=EA=B0=80=EA=B2=8C=20=EC=B5=9C=EC=B4=88?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20UI=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../selectcategory/SelectCategoryScreen.kt | 232 ++++++++++++++++++ .../SelectCategoryScreenSideEffect.kt | 10 + .../SelectCategoryScreenState.kt | 7 + .../SelectCategoryScreenViewModel.kt | 24 ++ .../startinsetstore/StartInsertStore.kt | 117 +++++++++ .../PasswordAuthenticationSideEffect.kt | 2 - core/src/main/res/drawable/ic_edit.xml | 20 ++ core/src/main/res/values/strings.xml | 14 ++ 8 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt create mode 100644 business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt create mode 100644 business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt create mode 100644 business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt create mode 100644 business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt create mode 100644 core/src/main/res/drawable/ic_edit.xml diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt new file mode 100644 index 000000000..27eb2ac48 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt @@ -0,0 +1,232 @@ +package `in`.koreatech.business.feature.insertstore.selectcategory + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.LinearProgressIndicator +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.ProgressBarRangeInfo +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import `in`.koreatech.business.ui.theme.ColorPrimary +import `in`.koreatech.business.ui.theme.ColorSecondary +import `in`.koreatech.koin.core.R +import `in`.koreatech.koin.domain.model.store.StoreCategories +import org.orbitmvi.orbit.compose.collectAsState + +@Composable +fun SelectCategoryScreenImpl( + modifier: Modifier = Modifier, + navigateToFinish: () -> Unit, + onBackPressed: () -> Unit, + viewModel: SelectCategoryScreenViewModel = hiltViewModel() +) { + val state = viewModel.collectAsState().value + SelectCategoryScreen( + categories = state.categories + ) +} + + + +@Composable +fun SelectCategoryScreen( + modifier: Modifier = Modifier, + categories: List +) { + Column( + modifier = modifier.fillMaxSize() + ) { + Box( + modifier = modifier + .padding(top = 56.dp, start = 10.dp, bottom = 18.dp) + .width(40.dp) + .height(40.dp) + .clickable { } + ) { + Image( + painter = painterResource(R.drawable.ic_arrow_left), + contentDescription = "backArrow", + modifier = modifier + .width(40.dp) + .height(40.dp) + .clickable { } + ) + } + + Text( + modifier = modifier.padding(top = 35.dp, start = 40.dp), + text = stringResource(id = R.string.insert_store), + fontSize = 24.sp + ) + + Text( + modifier = modifier.padding(top = 34.dp, start = 32.dp), + text = stringResource(id = R.string.insert_store_main_info), + fontSize = 18.sp + ) + + InsertStoreProgressBar(modifier, 0.25f, R.string.insert_store_category_setting, R.string.page_one) + + Text( + modifier = modifier.padding(top = 24.dp, start = 40.dp), + text = stringResource(id = R.string.insert_store_choose_category), + fontSize = 18.sp + ) + + LazyHorizontalGrid( + rows = GridCells.Fixed(2), + modifier = modifier + .padding(top = 28.dp) + .padding(horizontal = 15.dp) + .height(200.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + items(categories) { category -> + CategoryItem( + modifier = Modifier, + imageUrl = category.imageUrl, + name = category.name + ) + } + } + + Button( + onClick = { /*TODO*/ }, + colors = ButtonDefaults.buttonColors(ColorPrimary), + shape = RectangleShape, + modifier = modifier + .padding(top = 57.dp, start = 240.dp, end = 16.dp) + .height(38.dp) + .width(105.dp) + ) { + Text( + text = stringResource(id = R.string.next), + fontSize = 15.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + } + + } + +} + +@Composable +fun InsertStoreProgressBar( + modifier: Modifier, + range: Float, + resourceId: Int, + pageId: Int +) { + Row( + modifier = modifier + .padding(top = 24.dp) + .padding(horizontal = 32.dp) + ) { + Text( + text = stringResource(id = resourceId), + fontSize = 15.sp, + color = ColorSecondary + ) + + Text( + modifier = modifier.padding(start = 160.dp), + text = stringResource(id = pageId), + fontSize = 15.sp, + color = ColorSecondary + ) + + } + LinearProgressIndicator( + modifier = modifier + .padding(top = 4.dp) + .padding(horizontal = 32.dp) + .fillMaxWidth() + .height(3.dp) + , + color = ColorSecondary, + backgroundColor = Color.Gray, + progress = range + ) +} + +@Composable +fun CategoryItem(modifier: Modifier, imageUrl: String, name: String) { + Column( + modifier = modifier + .width(70.dp) + .wrapContentHeight(), + horizontalAlignment = Alignment.CenterHorizontally + ){ + Image( + modifier = modifier.size(44.dp), + painter = painterResource(R.drawable.ic_chicken), + alignment = Alignment.Center, + contentDescription = "category_image" + ) + Text( + modifier = modifier.fillMaxWidth(), + text = "일반음식점", + textAlign = TextAlign.Center, + fontSize = 12.sp + ) + } +} + +@Preview +@Composable +fun PreviewCategoryItem() { + CategoryItem( + modifier = Modifier, + imageUrl = "", + name = "" + ) +} + +@Preview +@Composable +fun PreviewSelectCategoryScreen(){ + SelectCategoryScreen( + categories = sampleCategories + ) +} + +val sampleCategories = listOf( + StoreCategories(1,"imageUrl1", "일반음식점"), + StoreCategories(1, "imageUrl2", "패스트푸드"), + StoreCategories(1,"imageUrl3", "카페"), + StoreCategories(1,"imageUrl4", "디저트"), + StoreCategories(1,"imageUrl5", "바베큐"), + StoreCategories(1,"imageUrl5", "바베큐"), + StoreCategories(1,"imageUrl5", "바베큐"), + StoreCategories(1,"imageUrl5", "바베큐"), + StoreCategories(1,"imageUrl5", "바베큐") +) \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt new file mode 100644 index 000000000..712f257e0 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt @@ -0,0 +1,10 @@ +package `in`.koreatech.business.feature.insertstore.selectcategory + +import `in`.koreatech.business.feature_changepassword.passwordauthentication.ErrorType +import `in`.koreatech.business.feature_changepassword.passwordauthentication.PasswordAuthenticationSideEffect + +sealed class SelectCategoryScreenSideEffect { + data class GotoChangePasswordScreen(val email: String): SelectCategoryScreenSideEffect() + object SendAuthCode: SelectCategoryScreenSideEffect() + data class ShowMessage(val type: ErrorType): SelectCategoryScreenSideEffect() +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt new file mode 100644 index 000000000..ebd3b618f --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt @@ -0,0 +1,7 @@ +package `in`.koreatech.business.feature.insertstore.selectcategory + +import `in`.koreatech.koin.domain.model.store.StoreCategories + +data class SelectCategoryScreenState( + val categories: List = emptyList() +) \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt new file mode 100644 index 000000000..c7892ad57 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt @@ -0,0 +1,24 @@ +package `in`.koreatech.business.feature.insertstore.selectcategory + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.koreatech.koin.domain.usecase.business.OwnerChangePasswordUseCase +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.viewmodel.container +import javax.inject.Inject + + +@HiltViewModel +class SelectCategoryScreenViewModel @Inject constructor( + private val ownerChangePasswordUseCase: OwnerChangePasswordUseCase +) : ViewModel(), ContainerHost{ + override val container = container(SelectCategoryScreenState()) + + + + + + + +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt new file mode 100644 index 000000000..dc79abb02 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt @@ -0,0 +1,117 @@ +package `in`.koreatech.business.feature.insertstore.startinsetstore + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.layout.width +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import `in`.koreatech.koin.core.R +import `in`.koreatech.business.ui.theme.Blue1 +import `in`.koreatech.business.ui.theme.ColorPrimary + +@Composable +fun StartInsertScreen( + modifier: Modifier = Modifier +) { + Column( + modifier = modifier.fillMaxSize() + ) { + Box( + modifier = modifier + .padding(top = 56.dp, start = 10.dp , bottom = 18.dp) + .width(40.dp) + .height(40.dp) + .clickable { } + + ) { + Image( + painter = painterResource(R.drawable.ic_arrow_left), + contentDescription = "backArrow", + modifier = modifier + .width(40.dp) + .height(40.dp) + .clickable { } + ) + } + + Image( + painter = painterResource(id = R.drawable.ic_edit), + contentDescription = "finish_mark", + alignment = Alignment.Center, + modifier = modifier + .fillMaxWidth() + .padding(top = 103.dp, bottom = 30.dp) + .height(55.dp) + .width(55.dp) + ) + + Text( + text = stringResource(R.string.insert_store_info), + fontSize = 24.sp, + color = ColorPrimary, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 46.dp) + .padding(bottom = 16.dp) + ) + + Text( + text = stringResource(R.string.insert_store_guide), + fontSize = 16.sp, + color = Blue1, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 46.dp) + .padding(bottom = 51.dp) + ) + + Button( + onClick = { /*TODO*/ }, + colors = ButtonDefaults.buttonColors(ColorPrimary), + shape = RectangleShape, + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 33.dp) + .height(44.dp) + ) { + Text( + text = stringResource(id = R.string.insert_store_start), + fontSize = 15.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + } + } +} + + +@Preview +@Composable +fun PreviewStartInsertScreen(){ + StartInsertScreen( + modifier = Modifier + ) +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature_changepassword/passwordauthentication/PasswordAuthenticationSideEffect.kt b/business/src/main/java/in/koreatech/business/feature_changepassword/passwordauthentication/PasswordAuthenticationSideEffect.kt index 59a21fe3b..dbd9d92bd 100644 --- a/business/src/main/java/in/koreatech/business/feature_changepassword/passwordauthentication/PasswordAuthenticationSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature_changepassword/passwordauthentication/PasswordAuthenticationSideEffect.kt @@ -5,9 +5,7 @@ import `in`.koreatech.koin.domain.state.business.changepw.ChangePasswordExceptio sealed class PasswordAuthenticationSideEffect { data class GotoChangePasswordScreen(val email: String): PasswordAuthenticationSideEffect() - object SendAuthCode: PasswordAuthenticationSideEffect() - data class ShowMessage(val type: ErrorType): PasswordAuthenticationSideEffect() } diff --git a/core/src/main/res/drawable/ic_edit.xml b/core/src/main/res/drawable/ic_edit.xml new file mode 100644 index 000000000..178a06432 --- /dev/null +++ b/core/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,20 @@ + + + + diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index e97e247f1..2d43328dc 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -10,6 +10,11 @@ 확인 아니오 취소 + 다음 + 1 / 4 + 2 / 4 + 3 / 4 + 4 / 4 @@ -77,4 +82,13 @@ 님, 안녕하세요! 내 정보 햄버거 + + + 가게 등록 + 가게 정보 기입 + 가게의 다양한 정보를 입력 및 수정하여\n학생들에게 최신 가게 정보를 알려주세요. + 가게 정보 기입하기 + 등록 하시려는 업체의\n메인 정보를 입력해 주세요. + 1. 가게 카테고리 설정 + 카테고리를 골라주세요. From e0cf19fee53d66cb9095c622f9ad943ae52c736e Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 15 May 2024 20:53:06 +0900 Subject: [PATCH 051/131] Add resources --- .../in/koreatech/business/ui/theme/Color.kt | 4 + .../main/res/drawable/ic_complete_signup.xml | 317 ++++++++++++++++++ .../main/res/drawable/ic_delete_button.xml | 23 ++ business/src/main/res/values/strings.xml | 34 +- 4 files changed, 368 insertions(+), 10 deletions(-) create mode 100644 business/src/main/res/drawable/ic_complete_signup.xml create mode 100644 business/src/main/res/drawable/ic_delete_button.xml diff --git a/business/src/main/java/in/koreatech/business/ui/theme/Color.kt b/business/src/main/java/in/koreatech/business/ui/theme/Color.kt index a114b1ada..fbe7cca88 100644 --- a/business/src/main/java/in/koreatech/business/ui/theme/Color.kt +++ b/business/src/main/java/in/koreatech/business/ui/theme/Color.kt @@ -16,6 +16,10 @@ val ColorAccent = Color(0xFFF7941E) val ColorSecondaryText = Color(0xFFa1a1a1) val Black1 = Color(0xFF222222) val Gray5 = Color(0xFFC4C4C4) +val Gray1 = Color(0xFF4B4B4B) +val Gray2= Color(0xFFEEEEEE) +val Gray3= Color(0xFFCACACA) +val ColorTextField= Color(0xFFF5F5F5) val Red2 = Color(0xFFFF0000) val ColorError = Color(0xFFF05D3D) diff --git a/business/src/main/res/drawable/ic_complete_signup.xml b/business/src/main/res/drawable/ic_complete_signup.xml new file mode 100644 index 000000000..f6e56483b --- /dev/null +++ b/business/src/main/res/drawable/ic_complete_signup.xml @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/business/src/main/res/drawable/ic_delete_button.xml b/business/src/main/res/drawable/ic_delete_button.xml new file mode 100644 index 000000000..a6ebff22c --- /dev/null +++ b/business/src/main/res/drawable/ic_delete_button.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/business/src/main/res/values/strings.xml b/business/src/main/res/values/strings.xml index 22bd25301..01a05f0c0 100644 --- a/business/src/main/res/values/strings.xml +++ b/business/src/main/res/values/strings.xml @@ -12,7 +12,7 @@ 비밀번호가 변경되었습니다.\n새로운 비밀번호로 로그인 부탁드립니다. 비밀번호 변경 완료 재발송 - 인증번호발송 + 인증번호 발송 이메일 인증하기 비밀번호 확인 새 비밀번호 @@ -20,7 +20,7 @@ 로그인 화면 바로가기 SplashActivity - 1.기본 정보 입력 + 2.기본 정보 입력 아이디 비밀번호 비밀번호 확인 @@ -30,7 +30,7 @@ 이메일 인증하기 계정 인증 발송된 인증번호 6자리를 입력해주세요 - 인증번호 입력 + 인증번호 입력를 입력해주세요. 재발송 @@ -44,30 +44,44 @@ 3.사업자 인증 대표자명(실명) 가게명 직접 입력하기 - 가게검색 - 파일첨부 + 가게 검색 + 파일 첨부 파일을 첨부해주세요 - 사업자등록증, 영업신고증, 통장사본 이미지 첨부 + 사업자 등록증, 영업신고증, 통장사본을 첨부하세요. 사업자등록증, 영업신고증, 통장사본 이미지 필수\\n10mb 이하의 PDF 혹은 이미지 형식의 파일(e.g. jpg, png, gif 등)로 5개까지 업로드 가능합니다. 파일 선택하기 취소 사업자 등록번호 개인 연락처 - 상점 검색 + 가게를 검색해보세요. 식당이름 배달 카드결제 계좌이체 - 전화번호 + 휴대폰 번호 선택 - 가입 신청 완료 + 회원가입 완료 가입 신청이 완료되었습니다.\\n가입 허가가 승인되면 로그인이 가능합니다. - 로그인 화면으로 가기 + 로그인 화면 바로가기 BackArrow check upload file upload item image search 존재하지 않는 도메인입니다. + 아이디를 입력해주세요. + 비밀번호를 입력해주세요. + 비밀번호를 다시 입력해주세요. + -없이 번호를 입력해주세요. + 회원가입 + 존재하지 않는 휴대폰 번호입니다. + 중복 확인 + 인증번호 + 중복된 아이디입니다. + 가게명 + 이름을 입력해주세요. + 사업자 등록번호를 입력해주세요. + -없이 번호를 입력해주세요. + 사업자 인증 파일 From 5ab45d36d81cef9bef08deed5b7a15c980f6a16f Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 15 May 2024 20:56:13 +0900 Subject: [PATCH 052/131] Modify LinedTextField --- .../feature/textfield/LinedTextField.kt | 86 ++++++++----------- 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt b/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt index e9ca72326..21b4e147e 100644 --- a/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt +++ b/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt @@ -1,25 +1,31 @@ package `in`.koreatech.business.feature.textfield -import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.border 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.text.BasicTextField +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.PasswordVisualTransformation 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.sp -import `in`.koreatech.business.ui.theme.ColorError +import com.google.android.material.search.SearchBar +import `in`.koreatech.business.R import `in`.koreatech.business.ui.theme.ColorHelper +import `in`.koreatech.business.ui.theme.ColorSecondary +import `in`.koreatech.business.ui.theme.ColorTextField @Composable @@ -29,55 +35,37 @@ fun LinedTextField( modifier: Modifier = Modifier, label: String, textStyle: TextStyle = TextStyle.Default.copy(fontSize = 15.sp), - lineColor: Color = ColorHelper, - filledLineColor: Color = Color.Black, isPassword: Boolean = false, helperText: String = "", errorText: String = "", isError: Boolean = false, ) { - BasicTextField( + + OutlinedTextField( + modifier = modifier + .height(40.dp).fillMaxWidth() + .background(color = ColorTextField) + .border( + shape = RoundedCornerShape(4.dp), + width = 1.dp, + color = if (isError) ColorSecondary else ColorTextField, + ), value = value, onValueChange = onValueChange, - textStyle = textStyle, - modifier = modifier, - maxLines = 1, - + placeholder = { + Text(text = label, fontSize = 15.sp, color = ColorHelper) + }, + isError = isError, visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None, - decorationBox = { innerTextField -> - Column(modifier = Modifier.fillMaxWidth()) { - Spacer(modifier = Modifier.height(12.dp)) - Box { - if (value.isEmpty()) { - Text(label, fontSize = 15.sp, color = ColorHelper) - } - innerTextField() - Canvas( - modifier = Modifier - .fillMaxWidth() - .height(30.dp) - ) { - drawLine( - strokeWidth = 1.dp.toPx(), - color = if (value.isNotEmpty()) filledLineColor else if (isError) ColorError - else lineColor, - start = Offset(0f, size.height), - end = Offset(size.width, size.height), - cap = StrokeCap.Round - ) - } - } - Box { - Text( - modifier = Modifier, - text = helperText, - fontSize = 11.sp, - color = ColorHelper, - ) - if (isError) Text(text = errorText, fontSize = 11.sp, color = ColorError) - - } - } - } ) + Box { + Text( + modifier = Modifier, + text = helperText, + fontSize = 11.sp, + color = ColorHelper, + ) + if (isError) Text(text = errorText, fontSize = 11.sp, color = ColorSecondary) + + } } From ddec66fb639e43b1ebcc0cbc67a6ffbea4049f7a Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 15 May 2024 20:57:31 +0900 Subject: [PATCH 053/131] Add extension property to validate password --- .../in/koreatech/koin/domain/util/ext/StringExtensions.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt b/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt index 1bc0aaa99..c545a5736 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt @@ -14,6 +14,10 @@ val String.isValidStudentId: Boolean get() { return year in 1992..Calendar.getInstance().get(Calendar.YEAR) } +val String.isValidPassword: Boolean + get() = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#\$%^&+=])(?=\\S+\$).{8,}\$".toRegex().matches(this) + + fun String.toUnderlineForHtml() = "$this" fun String.toColorForHtml(color: String) = "$this" //color = #ff000000 형태 From 4ddb2dd608da2781f27c5e13b04ed09041e87354 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 15 May 2024 21:03:59 +0900 Subject: [PATCH 054/131] Modify value email -> phoneNumber --- .../signup/accountsetup/AccountSetupState.kt | 7 ++++--- .../signup/accountsetup/AccountSetupViewModel.kt | 15 ++++----------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt index 8c0ef33cf..2929f983e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt @@ -6,11 +6,12 @@ data class AccountSetupState( val id: String = "", val password: String = "", val passwordConfirm: String = "", - val email: String = "", + val phoneNumber: String = "", val signupContinuationState: SignupContinuationState = SignupContinuationState.RequestedEmailValidation, val signUpContinuationError:Throwable? = null, val isLoading: Boolean = false ){ - val isPasswordEnabled: Boolean - get() = id.isNotEmpty() && password.isNotEmpty() && passwordConfirm.isNotEmpty() && email.isNotEmpty() + val isButtonEnabled: Boolean + get() = id.isNotEmpty() && password.isNotEmpty() && passwordConfirm.isNotEmpty() && phoneNumber.isNotEmpty() + } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 87e1a56cd..64f56fb01 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -30,25 +30,22 @@ class AccountSetupViewModel @Inject constructor( reduce { state.copy(password = password) } - reduce { state.copy(signupContinuationState = SignupContinuationState.RequestedEmailValidation) } } fun onPasswordConfirmChanged(passwordConfirm: String) = intent { reduce { state.copy(passwordConfirm = passwordConfirm) } - reduce { state.copy(signupContinuationState = SignupContinuationState.RequestedEmailValidation) } } - fun onEmailChanged(email: String) = intent { + fun onPhoneNumChanged(phoneNumber: String) = intent { reduce { - state.copy(email = email) + state.copy(phoneNumber = phoneNumber) } - reduce { state.copy(signupContinuationState = SignupContinuationState.RequestedEmailValidation) } } fun onNextButtonClicked() = intent { - postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.email)) + postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.phoneNumber)) } fun onBackButtonClicked() = intent { @@ -56,7 +53,7 @@ class AccountSetupViewModel @Inject constructor( } - fun postEmailVerification(email: String, password: String, passwordConfirm: String) { + fun postPhoneVerification(email: String, password: String, passwordConfirm: String) { intent { reduce { state.copy(isLoading = true) } } viewModelScope.launch(Dispatchers.IO) { sendSignupEmailUseCase(email, password, passwordConfirm) @@ -70,10 +67,6 @@ class AccountSetupViewModel @Inject constructor( intent { reduce { state.copy(signUpContinuationError = it) } } } intent { reduce { state.copy(isLoading = false) } } - intent { - if (state.signupContinuationState == SignupContinuationState.CheckComplete) - onNextButtonClicked() - } } } } \ No newline at end of file From 5150200d6aeab87718181513d7f0a2dd353fcdf5 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 15 May 2024 21:14:07 +0900 Subject: [PATCH 055/131] Modify AccountSetupScreen --- .../signup/accountsetup/AccountSetupScreen.kt | 200 +++++++++++++----- 1 file changed, 147 insertions(+), 53 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index 3eb1d7c88..739726933 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -2,6 +2,7 @@ package `in`.koreatech.business.feature.signup.accountsetup import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -9,12 +10,16 @@ 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.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults 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.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color @@ -29,14 +34,17 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R +import `in`.koreatech.business.feature.signup.accountauth.EmailAuthViewModel +import `in`.koreatech.business.feature.textfield.AuthTextField import `in`.koreatech.business.feature.textfield.LinedTextField -import `in`.koreatech.business.ui.theme.ColorDisabledButton import `in`.koreatech.business.ui.theme.ColorHelper import `in`.koreatech.business.ui.theme.ColorPrimary -import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorUnarchived +import `in`.koreatech.business.ui.theme.Gray1 +import `in`.koreatech.business.ui.theme.Gray2 import `in`.koreatech.business.ui.theme.KOIN_ANDROIDTheme -import `in`.koreatech.koin.domain.state.signup.SignupContinuationState +import `in`.koreatech.koin.domain.util.ext.isValidPassword +import `in`.koreatech.koin.domain.util.ext.isValidStudentId import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -44,34 +52,44 @@ import org.orbitmvi.orbit.compose.collectSideEffect fun AccountSetupScreen( modifier: Modifier = Modifier, viewModel: AccountSetupViewModel = hiltViewModel(), + authViewModel: EmailAuthViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, onNextClicked: (String) -> Unit = {}, ) { + val state = viewModel.collectAsState().value + val authState = authViewModel.collectAsState().value Column( modifier = modifier.fillMaxSize(), ) { - IconButton( - modifier = Modifier.padding(vertical = 24.dp), - onClick = { onBackClicked() } + Box( + modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp) ) { - Icon( - modifier = Modifier.padding(start = 10.dp), - painter = painterResource(id = R.drawable.ic_arrow_back), - contentDescription = stringResource(id = R.string.back_icon), + IconButton( + onClick = { viewModel.onBackButtonClicked() }, + modifier = Modifier.align(Alignment.CenterStart) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_arrow_back), + contentDescription = stringResource(id = R.string.back_icon), + ) + } + + Text( + text = stringResource(id = R.string.sign_up), + fontSize = 18.sp, + fontWeight = Bold, + modifier = Modifier.align(Alignment.Center) ) } + + Spacer(modifier = Modifier.height(20.dp)) + Column( modifier = Modifier - .padding(horizontal = 32.dp), + .padding(horizontal = 24.dp), verticalArrangement = Arrangement.Center, ) { - Text( - text = stringResource(id = R.string.master_sign_up), - fontSize = 24.sp, - fontWeight = Bold, - ) - Spacer(modifier = Modifier.height(40.dp)) Row( modifier = Modifier .fillMaxWidth(), @@ -79,10 +97,11 @@ fun AccountSetupScreen( ) { Text( modifier = Modifier, - color = ColorSecondary, + color = ColorPrimary, + fontWeight = Bold, text = stringResource(id = R.string.input_basic_information), ) - Text(text = stringResource(id = R.string.one_third), color = ColorSecondary) + Text(text = stringResource(id = R.string.two_third), color = ColorPrimary, fontWeight = Bold,) } Canvas( @@ -98,81 +117,156 @@ fun AccountSetupScreen( cap = StrokeCap.Round ) drawLine( - color = ColorSecondary, + color = ColorPrimary, start = Offset(-40f, 0f), - end = Offset((size.width + 40) / 3, size.height), + end = Offset((size.width + 40) / 3 * 2, size.height), strokeWidth = 4.dp.toPx(), cap = StrokeCap.Round ) } - Spacer(modifier = Modifier.height(32.dp)) + Spacer(modifier = Modifier.height(10.dp)) - LinedTextField( - value = state.id, - onValueChange = { viewModel.onIdChanged(it) }, - modifier = Modifier.fillMaxWidth(), - label = stringResource(id = R.string.id), - textStyle = TextStyle.Default.copy(fontSize = 15.sp), - lineColor = ColorHelper, - ) + Text(text = stringResource(id = R.string.id), fontSize = 14.sp, fontWeight = Bold) + Row( + modifier=Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + LinedTextField( + value = state.id, + onValueChange = { viewModel.onIdChanged(it) }, + modifier = Modifier.width(203.dp), + label = stringResource(id = R.string.enter_id), + errorText = stringResource(id = R.string.id_not_validate), + textStyle = TextStyle.Default.copy(fontSize = 15.sp), + ) + + Button(modifier = Modifier + .width(115.dp) + .height(44.dp), + shape = RoundedCornerShape(4.dp), + enabled = state.id.isNotEmpty(), + colors = ButtonDefaults.buttonColors( + backgroundColor = ColorPrimary, + contentColor = Color.White, + disabledBackgroundColor = Gray2, + disabledContentColor = Gray1, + ), + onClick = { + /* 아이디 중복 확인 */ + }) { + Text( + text = stringResource(id = R.string.check_duplicate), + fontSize = 13.sp, + fontWeight = Bold, + ) + } + } + Spacer(modifier = Modifier.height(20.dp)) + + Text(text = stringResource(id = R.string.password), fontSize = 14.sp, fontWeight = Bold) LinedTextField( value = state.password, onValueChange = { viewModel.onPasswordChanged(it) }, modifier = Modifier.fillMaxWidth(), - label = stringResource(id = R.string.password), + label = stringResource(id = R.string.enter_password), textStyle = TextStyle.Default.copy(fontSize = 15.sp), - lineColor = ColorHelper, isPassword = true, - helperText = stringResource(id = R.string.password_requirements), + isError = state.password.isValidPassword, ) + Spacer(modifier = Modifier.height(10.dp)) + Text(text = stringResource(id = R.string.password_confirm), fontSize = 14.sp, fontWeight = Bold) LinedTextField( value = state.passwordConfirm, onValueChange = { viewModel.onPasswordConfirmChanged(it) }, modifier = Modifier.fillMaxWidth(), - label = stringResource(id = R.string.password_confirm), + label = stringResource(id = R.string.enter_password_confirm), textStyle = TextStyle.Default.copy(fontSize = 15.sp), - lineColor = ColorHelper, isPassword = true, errorText = stringResource(id = R.string.password_mismatch), - isError = state.signupContinuationState == SignupContinuationState.PasswordNotMatching, + isError = state.password !=state.passwordConfirm && state.passwordConfirm.isNotEmpty(), ) + Spacer(modifier = Modifier.height(10.dp)) + + Text(text = stringResource(id = R.string.phone_number), fontSize = 14.sp, fontWeight = Bold) + LinedTextField( - value = state.email, - onValueChange = { viewModel.onEmailChanged(it) }, + value = state.phoneNumber, + onValueChange = { viewModel.onPhoneNumChanged(it) }, modifier = Modifier.fillMaxWidth(), - label = stringResource(id = R.string.email_confirm), + label = stringResource(id = R.string.enter_phone_number), textStyle = TextStyle.Default.copy(fontSize = 15.sp), - lineColor = ColorHelper, - errorText = stringResource(id = R.string.email_not_validate), - isError = state.signupContinuationState == SignupContinuationState.EmailIsNotValidate, + errorText = stringResource(id = R.string.phone_number_not_validate), + isError = state.phoneNumber.length!=11 && state.phoneNumber.isNotEmpty(), ) + Spacer(modifier = Modifier.height(10.dp)) + + Text(text = stringResource(id = R.string.authentication_code), fontSize = 14.sp, fontWeight = Bold) + + Row( + modifier=Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + LinedTextField( + value = authState.authCode, + onValueChange = { authViewModel.onAuthCodeChanged(it) }, + modifier = Modifier.width(203.dp), + label = stringResource(id = R.string.enter_verification_code), + textStyle = TextStyle.Default.copy(fontSize = 20.sp), + isPassword = true, + isError = state.signUpContinuationError != null, + ) + + Button(modifier = Modifier + .width(115.dp) + .height(44.dp), + shape = RoundedCornerShape(4.dp), + enabled = state.phoneNumber.isNotEmpty(), + colors = ButtonDefaults.buttonColors( + backgroundColor = ColorPrimary, + contentColor = Color.White, + disabledBackgroundColor = Gray2, + disabledContentColor = Gray1, + ), + onClick = { + viewModel.postPhoneVerification( + state.phoneNumber, state.password, state.passwordConfirm + ) + }) { + Text( + text = stringResource(id = R.string.send_authentication_code), + fontWeight= Bold, + fontSize = 13.sp, - Spacer(modifier = Modifier.height(78.dp)) + ) + } + } + + Spacer(modifier = Modifier.height(55.dp)) Button(modifier = Modifier .fillMaxWidth() .height(44.dp), shape = RectangleShape, - enabled = state.isPasswordEnabled, + enabled = state.signUpContinuationError == null && state.isButtonEnabled, colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, contentColor = Color.White, - disabledBackgroundColor = ColorDisabledButton, - disabledContentColor = Color.White, + disabledBackgroundColor = Gray2, + disabledContentColor = Gray1, ), onClick = { - viewModel.postEmailVerification( - state.email, state.password, state.passwordConfirm - ) + viewModel.onNextButtonClicked() }) { Text( - text = stringResource(id = R.string.email_authentication), - fontSize = 15.sp, - color = Color.White, + text = stringResource(id = R.string.next), + fontSize = 13.sp, + fontWeight = Bold, ) } } @@ -180,7 +274,7 @@ fun AccountSetupScreen( viewModel.collectSideEffect { when (it) { - is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(state.email) + is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(state.phoneNumber) AccountSetupSideEffect.NavigateToBackScreen -> onBackClicked() } } From 12cd49fbf83409fa616d9d3497676fb67e81ba0c Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 15 May 2024 23:12:12 +0900 Subject: [PATCH 056/131] Modify SearchStore logic --- .../businessauth/SearchStoreViewModel.kt | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt index fd57fe358..fcf6b5399 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt @@ -26,6 +26,9 @@ class SearchStoreViewModel @Inject constructor( override val container = container(SearchStoreState()) + init { + searchStore() + } fun onItemIndexChange(index: Int) = intent { reduce { state.copy(itemIndex = index) @@ -33,29 +36,33 @@ class SearchStoreViewModel @Inject constructor( } fun onSearchChanged(search: String) = intent { - postSideEffect(SearchStoreSideEffect.SearchStore(search)) + reduce { + state.copy(search = search) + } } - fun onNavigateToBackScreen() = intent { - postSideEffect(SearchStoreSideEffect.NavigateToBackScreen) - } - init { - intent { - viewModelScope.launch(Dispatchers.IO) { - state.searchJob?.cancel() - val newSearchJob = launch { - - getStoresUseCase(null, "").let { stores -> - reduce { - state.copy(stores = stores) - } + + fun searchStore() = intent { + viewModelScope.launch(Dispatchers.IO) { + val newSearchJob = launch { + getStoresUseCase(null, state.search).let { stores -> + reduce { + state.copy(stores = stores) } } - reduce { - state.copy(searchJob = newSearchJob) - } - + } + reduce { + state.copy(searchJob = newSearchJob) } } } + + fun onSearchStore()= intent { + postSideEffect(SearchStoreSideEffect.SearchStore(state.search)) + } + + fun onNavigateToBackScreen() = intent { + postSideEffect(SearchStoreSideEffect.NavigateToBackScreen) + } + } \ No newline at end of file From a80dea1b5880a858dbb823430fec1472d112f571 Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 15 May 2024 23:14:30 +0900 Subject: [PATCH 057/131] Modify SearchStoreScreen --- .../signup/businessauth/SearchStoreScreen.kt | 225 ++++++++++-------- 1 file changed, 123 insertions(+), 102 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt index 14de7e736..5f91fb2fe 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreScreen.kt @@ -1,11 +1,15 @@ package `in`.koreatech.business.feature.signup.businessauth +import android.annotation.SuppressLint import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -15,7 +19,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.IconButton @@ -30,8 +36,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle @@ -50,6 +58,10 @@ import `in`.koreatech.business.ui.theme.ColorHelper import `in`.koreatech.business.ui.theme.ColorPrimary import `in`.koreatech.business.ui.theme.ColorSearch import `in`.koreatech.business.ui.theme.ColorSecondary +import `in`.koreatech.business.ui.theme.ColorTextField +import `in`.koreatech.business.ui.theme.ColorUnarchived +import `in`.koreatech.business.ui.theme.Gray1 +import `in`.koreatech.business.ui.theme.Gray2 import `in`.koreatech.koin.domain.model.store.Store import kotlinx.coroutines.launch import org.orbitmvi.orbit.compose.collectAsState @@ -60,60 +72,94 @@ fun SearchStoreScreen( modifier: Modifier = Modifier, viewModel: SearchStoreViewModel = hiltViewModel(), businessAuthViewModel: BusinessAuthViewModel = hiltViewModel(), - onBackClicked: () -> Unit = {} + onBackButtonClicked: () -> Unit = {} ) { val state = viewModel.collectAsState().value - Column( modifier = modifier, + ) { - IconButton( - modifier = Modifier.padding(vertical = 24.dp), - onClick = { onBackClicked() } - ) { - Icon( - modifier = Modifier.padding(start = 10.dp), - painter = painterResource(id = R.drawable.ic_arrow_back), - contentDescription = stringResource(id = R.string.back_icon), - ) - } - Column( + Box( modifier = Modifier - .padding(horizontal = 32.dp) - .background(Color.White), - verticalArrangement = Arrangement.Center, + .fillMaxWidth() + .padding(vertical = 12.dp) ) { + IconButton( + onClick = { viewModel.onNavigateToBackScreen() }, + modifier = Modifier.align(Alignment.CenterStart) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_arrow_back), + contentDescription = stringResource(id = R.string.back_icon), + ) + } - Text( - text = stringResource(id = R.string.master_sign_up), - fontSize = 24.sp, - fontWeight = Bold, - ) - Spacer(modifier = Modifier.height(8.dp)) Text( text = stringResource(id = R.string.search_store), fontSize = 18.sp, fontWeight = Bold, + modifier = Modifier.align(Alignment.Center) ) } + + Column( + modifier = Modifier + .padding(horizontal = 24.dp), + verticalArrangement = Arrangement.Center, + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + modifier = Modifier, + color = ColorPrimary, + fontWeight = Bold, + text = stringResource(id = R.string.business_auth), + ) + Text( + text = stringResource(id = R.string.three_third), + color = ColorPrimary, + fontWeight = Bold, + ) + } + + Canvas( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + + drawLine( + color = ColorPrimary, + start = Offset(-40f, 0f), + end = Offset((size.width + 40), size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + } + } Column( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) ) { - Spacer(modifier = Modifier.height(42.dp)) + Spacer(modifier = Modifier.height(10.dp)) TextField( modifier = Modifier .fillMaxWidth() - .height(50.dp), - value = state.search, onValueChange = { viewModel.onSearchChanged(it) }, + .height(45.dp), + shape = RoundedCornerShape(6.dp), + value = state.search, onValueChange = { viewModel.onSearchChanged(it) + viewModel.onSearchStore()}, textStyle = TextStyle(fontSize = 14.sp), colors = TextFieldDefaults.textFieldColors( - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - focusedLabelColor = ColorSearch, + focusedIndicatorColor = ColorTextField, + unfocusedIndicatorColor = ColorTextField, + focusedLabelColor = ColorTextField, ), placeholder = { Text( @@ -132,16 +178,20 @@ fun SearchStoreScreen( } } ) - Spacer(modifier = Modifier.height(16.dp)) - StoreList(state.stores, viewModel,businessAuthViewModel) + Spacer(modifier = Modifier.height(5.dp)) + StoreList(state.stores, viewModel, businessAuthViewModel) + } + viewModel.collectSideEffect { when (it) { is SearchStoreSideEffect.SearchStore -> { + viewModel.searchStore() } + SearchStoreSideEffect.NavigateToBackScreen -> { - onBackClicked() + onBackButtonClicked() } } } @@ -152,26 +202,36 @@ fun SearchStoreScreen( @OptIn(ExperimentalMaterialApi::class) @Composable -fun StoreList(item: List, viewModel: SearchStoreViewModel= hiltViewModel(), authViewModel: BusinessAuthViewModel= hiltViewModel() ) { - val state= viewModel.collectAsState().value +fun StoreList( + item: List, + viewModel: SearchStoreViewModel = hiltViewModel(), + businessAuthViewModel: BusinessAuthViewModel = hiltViewModel() +) { + val state = viewModel.collectAsState().value + val selectedStoreState = businessAuthViewModel.collectAsState().value val sheetState = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Hidden ) val scope = rememberCoroutineScope() - Scaffold() { contentPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .height(200.dp) + + ) { LazyColumn( modifier = Modifier + .weight(1f) + .padding(vertical = 8.dp) .fillMaxSize() - .padding(contentPadding), - horizontalAlignment = Alignment.CenterHorizontally, ) { items(item.size) { index -> Row( modifier = Modifier .padding(vertical = 8.dp) .fillMaxWidth() - .height(59.dp) + .height(55.dp) .clickable { scope.launch { sheetState.show() @@ -182,7 +242,8 @@ fun StoreList(item: List, viewModel: SearchStoreViewModel= hiltViewModel( BorderStroke( width = if (state.itemIndex == index) 1.5.dp else 1.dp, color = if (state.itemIndex == index) ColorPrimary else ColorHelper - ) + ), + shape = RoundedCornerShape(6.dp), ), ) { Row( @@ -197,92 +258,52 @@ fun StoreList(item: List, viewModel: SearchStoreViewModel= hiltViewModel( text = item[index].name, fontSize = 15.sp, color = Color.Black, - fontWeight = Bold - ) + + ) Spacer(modifier = Modifier.width(74.dp)) Text( buildAnnotatedString { - withStyle(style = SpanStyle(color = ColorSecondary)) { + withStyle(style = SpanStyle(color = ColorPrimary)) { append(text = stringResource(id = R.string.delivery)) } append(" ") - withStyle(style = SpanStyle(color = ColorSecondary)) { + withStyle(style = SpanStyle(color = ColorPrimary)) { append(text = stringResource(id = R.string.card_payment)) } append(" ") - withStyle(style = SpanStyle(color = ColorSecondary)) { + withStyle(style = SpanStyle(color = ColorPrimary)) { append(text = stringResource(id = R.string.account_transfer)) } }, - fontSize = 15.sp + fontSize = 12.sp ) } } } } + Spacer(modifier =Modifier.height(70.dp)) - if(state.itemIndex > -1) - ModalBottomSheetLayout( - modifier = Modifier, - sheetState = sheetState, - sheetElevation = 8.dp, - sheetContent = { - StoreBottomSheet(item[state.itemIndex], viewModel, authViewModel) - }) { - } - } -} - -@Composable -fun StoreBottomSheet(store: Store, viewModel: SearchStoreViewModel= hiltViewModel(), authViewModel: BusinessAuthViewModel= hiltViewModel() ) { - val state=authViewModel.collectAsState().value - Row( - modifier = Modifier - .background(Color.White) + Button(modifier = Modifier .fillMaxWidth() - .height(118.dp) - .padding(20.dp), - - ) { - Column(modifier = Modifier) { + .height(44.dp), + shape = RectangleShape, + enabled = state.itemIndex > -1, + colors = ButtonDefaults.buttonColors( + backgroundColor = ColorPrimary, + contentColor = Color.White, + disabledBackgroundColor = Gray2, + disabledContentColor = Gray1, + ), + onClick = { + viewModel.onNavigateToBackScreen() + selectedStoreState.shopId?.let { businessAuthViewModel.onShopIdChanged(state.itemIndex) } + businessAuthViewModel.onShopNameChanged(state.stores[state.itemIndex].name) + }) { Text( - modifier = Modifier.padding(bottom = 8.dp), - text = store.name, - fontSize = 18.sp, - color = Color.Black, + text = stringResource(id = R.string.next), + fontSize = 13.sp, fontWeight = Bold, ) - Row( - modifier = Modifier, - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = stringResource(id = R.string.phone_number), - fontSize = 14.sp, - color = ColorActiveButton, - fontWeight = Bold - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text =store.phone , fontSize = 14.sp, color = Color.Black, - fontWeight = Bold - ) - } - } - Button( - modifier = Modifier - .fillMaxWidth() - .padding(start = 20.dp) - .height(55.dp), - onClick = { - viewModel.onNavigateToBackScreen() - authViewModel.onShopIdChanged(store.uid) - authViewModel.onShopNameChanged(store.name)}, - shape = RectangleShape, - ) { - Text(text = stringResource(id = R.string.select), fontWeight = Bold) } } - } From 8056b6299aa64a000469134189270eb0730b9f53 Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 16 May 2024 02:37:03 +0900 Subject: [PATCH 058/131] Modify image resource name --- .../res/drawable/{ic_complete_signup.xml => complete_signup.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename business/src/main/res/drawable/{ic_complete_signup.xml => complete_signup.xml} (100%) diff --git a/business/src/main/res/drawable/ic_complete_signup.xml b/business/src/main/res/drawable/complete_signup.xml similarity index 100% rename from business/src/main/res/drawable/ic_complete_signup.xml rename to business/src/main/res/drawable/complete_signup.xml From 45f552c1a0ed65da37177cffccfae9976f26b04a Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 16 May 2024 11:51:01 +0900 Subject: [PATCH 059/131] Delte password check string extension --- .../in/koreatech/koin/domain/util/ext/StringExtensions.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt b/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt index c545a5736..1bc0aaa99 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt @@ -14,10 +14,6 @@ val String.isValidStudentId: Boolean get() { return year in 1992..Calendar.getInstance().get(Calendar.YEAR) } -val String.isValidPassword: Boolean - get() = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#\$%^&+=])(?=\\S+\$).{8,}\$".toRegex().matches(this) - - fun String.toUnderlineForHtml() = "$this" fun String.toColorForHtml(color: String) = "$this" //color = #ff000000 형태 From e5dc81ab32a5442ac5be83877cda36db9c274dd6 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:05:14 +0900 Subject: [PATCH 060/131] Add verification sms api --- .../src/main/java/in/koreatech/koin/data/api/OwnerApi.kt | 8 ++++++++ .../java/in/koreatech/koin/data/constant/URLConstant.kt | 2 ++ .../data/request/owner/OwnerVerificationCodeRequest.kt | 5 +++++ .../data/request/owner/OwnerVerificationEmailRequest.kt | 4 ++++ .../koin/data/source/remote/OwnerRemoteDataSource.kt | 9 +++++++++ 5 files changed, 28 insertions(+) diff --git a/data/src/main/java/in/koreatech/koin/data/api/OwnerApi.kt b/data/src/main/java/in/koreatech/koin/data/api/OwnerApi.kt index 8ed4ecc82..a969caf5b 100644 --- a/data/src/main/java/in/koreatech/koin/data/api/OwnerApi.kt +++ b/data/src/main/java/in/koreatech/koin/data/api/OwnerApi.kt @@ -4,6 +4,8 @@ import `in`.koreatech.koin.data.constant.URLConstant import `in`.koreatech.koin.data.request.owner.OwnerRegisterRequest import `in`.koreatech.koin.data.request.owner.OwnerVerificationCodeRequest import `in`.koreatech.koin.data.request.owner.OwnerVerificationEmailRequest +import `in`.koreatech.koin.data.request.owner.VerificationCodeSmsRequest +import `in`.koreatech.koin.data.request.owner.VerificationSmsRequest import `in`.koreatech.koin.data.response.owner.OwnerResponse import `in`.koreatech.koin.data.response.owner.OwnerVerificationCodeResponse import retrofit2.Response @@ -19,4 +21,10 @@ interface OwnerApi { @POST(URLConstant.OWNER.REGISTER) suspend fun postOwnerRegister(@Body ownerRegisterRequest: OwnerRegisterRequest): OwnerResponse + + @POST(URLConstant.OWNER.SMS) + suspend fun postVerificationSms(@Body ownerVerificationSms: VerificationSmsRequest) + + @POST(URLConstant.OWNER.CODE_SMS) + suspend fun postVerificationCodeSms(@Body ownerVerificationCode: VerificationCodeSmsRequest): OwnerVerificationCodeResponse } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/constant/URLConstant.kt b/data/src/main/java/in/koreatech/koin/data/constant/URLConstant.kt index 119e0873a..003e0035c 100644 --- a/data/src/main/java/in/koreatech/koin/data/constant/URLConstant.kt +++ b/data/src/main/java/in/koreatech/koin/data/constant/URLConstant.kt @@ -61,6 +61,8 @@ object URLConstant { const val REGISTER: String = "$OWNERS/register" const val CODE = "$OWNERS/$VERIFICATION/code" const val EMAIL = "$OWNERS/$VERIFICATION/email" + const val CODE_SMS = "$OWNERS/$VERIFICATION/code/sms" + const val SMS = "$OWNERS/$VERIFICATION/sms" const val PW = "password" } diff --git a/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationCodeRequest.kt b/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationCodeRequest.kt index 3910746bd..f82c699f1 100644 --- a/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationCodeRequest.kt +++ b/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationCodeRequest.kt @@ -6,3 +6,8 @@ data class OwnerVerificationCodeRequest( @SerializedName("address") val address: String, @SerializedName("certification_code") val certificationCode: String ) + +data class VerificationCodeSmsRequest( + @SerializedName("phone_number") val phoneNumber: String, + @SerializedName("certification_code") val certificationCode: String +) diff --git a/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationEmailRequest.kt b/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationEmailRequest.kt index 104f1b7ba..3a7b061c6 100644 --- a/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationEmailRequest.kt +++ b/data/src/main/java/in/koreatech/koin/data/request/owner/OwnerVerificationEmailRequest.kt @@ -5,3 +5,7 @@ import com.google.gson.annotations.SerializedName data class OwnerVerificationEmailRequest( @SerializedName("address") val address: String ) + +data class VerificationSmsRequest( + @SerializedName("phone_number") val phoneNumber: String? +) \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/source/remote/OwnerRemoteDataSource.kt b/data/src/main/java/in/koreatech/koin/data/source/remote/OwnerRemoteDataSource.kt index 95f5e4f61..37d6ceec8 100644 --- a/data/src/main/java/in/koreatech/koin/data/source/remote/OwnerRemoteDataSource.kt +++ b/data/src/main/java/in/koreatech/koin/data/source/remote/OwnerRemoteDataSource.kt @@ -4,6 +4,8 @@ import `in`.koreatech.koin.data.api.OwnerApi import `in`.koreatech.koin.data.request.owner.OwnerRegisterRequest import `in`.koreatech.koin.data.request.owner.OwnerVerificationCodeRequest import `in`.koreatech.koin.data.request.owner.OwnerVerificationEmailRequest +import `in`.koreatech.koin.data.request.owner.VerificationCodeSmsRequest +import `in`.koreatech.koin.data.request.owner.VerificationSmsRequest import `in`.koreatech.koin.data.response.owner.OwnerResponse import `in`.koreatech.koin.data.response.owner.OwnerVerificationCodeResponse @@ -19,4 +21,11 @@ class OwnerRemoteDataSource(private val ownerApi: OwnerApi) { suspend fun postOwnerRegister(ownerRegisterRequest: OwnerRegisterRequest): OwnerResponse { return ownerApi.postOwnerRegister(ownerRegisterRequest) } + + suspend fun postVerificationSms(ownerVerificationEmail: VerificationSmsRequest){ + return ownerApi.postVerificationSms(ownerVerificationEmail) + } + suspend fun postVerificationCodeSms(ownerVerificationCode: VerificationCodeSmsRequest): OwnerVerificationCodeResponse { + return ownerApi.postVerificationCodeSms(ownerVerificationCode) + } } \ No newline at end of file From 972e1f71eb91840da9e55f43a6b09322d27f808d Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:05:42 +0900 Subject: [PATCH 061/131] Add verification sms repository function --- .../repository/OwnerSignupRepositoryImpl.kt | 18 ++++++++++++++++++ .../OwnerVerificationCodeRepositoryImpl.kt | 19 +++++++++++++++++++ .../repository/OwnerSignupRepository.kt | 4 ++++ .../OwnerVerificationCodeRepository.kt | 5 +++++ 4 files changed, 46 insertions(+) diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt index 508fc7709..183bb0eed 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt @@ -2,6 +2,7 @@ package `in`.koreatech.koin.data.repository import `in`.koreatech.koin.data.mapper.httpExceptionMapper import `in`.koreatech.koin.data.request.owner.OwnerVerificationEmailRequest +import `in`.koreatech.koin.data.request.owner.VerificationSmsRequest import `in`.koreatech.koin.data.source.local.SignupTermsLocalDataSource import `in`.koreatech.koin.data.source.remote.OwnerRemoteDataSource import `in`.koreatech.koin.domain.repository.OwnerSignupRepository @@ -38,4 +39,21 @@ class OwnerSignupRepositoryImpl @Inject constructor( } } + override suspend fun requestSmsVerificationCode( + phoneNumber: String + ): Result { + return try { + ownerRemoteDataSource.postVerificationSms( + VerificationSmsRequest( + phoneNumber = phoneNumber + ) + ) + Result.success(Unit) + } catch (e: HttpException) { + e.httpExceptionMapper() + } catch (t: Throwable) { + Result.failure(t) + } + } + } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt index bda29bf68..0b3cddb23 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt @@ -3,6 +3,7 @@ package `in`.koreatech.koin.data.repository import `in`.koreatech.koin.data.mapper.httpExceptionMapper import `in`.koreatech.koin.data.mapper.toAuthToken import `in`.koreatech.koin.data.request.owner.OwnerVerificationCodeRequest +import `in`.koreatech.koin.data.request.owner.VerificationCodeSmsRequest import `in`.koreatech.koin.data.source.remote.OwnerRemoteDataSource import `in`.koreatech.koin.domain.error.owner.OwnerError import `in`.koreatech.koin.domain.model.owner.OwnerAuthToken @@ -33,4 +34,22 @@ class OwnerVerificationCodeRepositoryImpl @Inject constructor( } } + override suspend fun verifySmsCode( + phoneNumber: String, + verificationCode: String + ): Result { + return try { + val tempToken = ownerRemoteDataSource.postVerificationCodeSms( + VerificationCodeSmsRequest( + phoneNumber = phoneNumber, + certificationCode = verificationCode + ) + ) + Result.success(tempToken.toAuthToken()) + } catch (e: HttpException) { + Result.failure(e) + } catch (t: Throwable) { + Result.failure(t) + } + } } \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerSignupRepository.kt b/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerSignupRepository.kt index 8d3fd2018..2a13ad251 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerSignupRepository.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerSignupRepository.kt @@ -7,4 +7,8 @@ interface OwnerSignupRepository { suspend fun requestEmailVerification( email: String ): Result + + suspend fun requestSmsVerificationCode( + phoneNumber: String, + ): Result } \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerVerificationCodeRepository.kt b/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerVerificationCodeRepository.kt index dd7fa2951..81adb15c0 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerVerificationCodeRepository.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/repository/OwnerVerificationCodeRepository.kt @@ -8,4 +8,9 @@ interface OwnerVerificationCodeRepository { address: String, verificationCode: String ): Result + + suspend fun verifySmsCode( + phoneNumber: String, + verificationCode: String + ): Result } \ No newline at end of file From 8545fdc613b57bc097e5ba2ce55fe5926deaa52c Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:09:14 +0900 Subject: [PATCH 062/131] Add verification sms code usecase --- .../business/SmsVerificationUseCase.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 domain/src/main/java/in/koreatech/koin/domain/usecase/business/SmsVerificationUseCase.kt diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SmsVerificationUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SmsVerificationUseCase.kt new file mode 100644 index 000000000..b5ffa3b01 --- /dev/null +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SmsVerificationUseCase.kt @@ -0,0 +1,25 @@ +package `in`.koreatech.koin.domain.usecase.business + +import `in`.koreatech.koin.domain.repository.OwnerVerificationCodeRepository +import `in`.koreatech.koin.domain.repository.TokenRepository +import `in`.koreatech.koin.domain.state.signup.SignupContinuationState +import `in`.koreatech.koin.domain.util.ext.formatPhoneNumber +import javax.inject.Inject +class SmsVerificationUseCase @Inject constructor( + private val ownerVerificationCodeRepository: OwnerVerificationCodeRepository, + private val tokenRepository: TokenRepository, +) { + suspend operator fun invoke( + phoneNumber: String, + verificationCode: String + ): Result { + return try { + val authToken = ownerVerificationCodeRepository.verifySmsCode(phoneNumber.formatPhoneNumber(), verificationCode) + authToken.getOrDefault(defaultValue = null) + ?.let { tokenRepository.saveOwnerAccessToken(it.token) } + Result.success(SignupContinuationState.CheckComplete) + } catch (t: Exception) { + Result.failure(t) + } + } +} \ No newline at end of file From 4e219f6962c272894e2ebaf04d358b89bc4e9a12 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:09:27 +0900 Subject: [PATCH 063/131] Add business signup check usecase --- .../business/BusinessSignupCheckUseCase.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt new file mode 100644 index 000000000..40bdb855c --- /dev/null +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt @@ -0,0 +1,38 @@ +package `in`.koreatech.koin.domain.usecase.business + +import `in`.koreatech.koin.domain.repository.OwnerVerificationCodeRepository +import `in`.koreatech.koin.domain.repository.TokenRepository +import `in`.koreatech.koin.domain.state.signup.SignupContinuationState +import `in`.koreatech.koin.domain.util.ext.formatPhoneNumber +import `in`.koreatech.koin.domain.util.ext.isNotValidPassword +import javax.inject.Inject + +class BusinessSignupCheckUseCase @Inject constructor( + private val ownerVerificationCodeRepository: OwnerVerificationCodeRepository, + private val tokenRepository: TokenRepository, +) { + suspend operator fun invoke( + password: String, + passwordCheck: String, + phoneNumber: String, + verificationCode: String + ): Result { + return when { + (password.isNotValidPassword()) -> Result.success(SignupContinuationState.PasswordIsNotValidate) + (password != passwordCheck) -> Result.success(SignupContinuationState.PasswordNotMatching) + (phoneNumber.length != 11) -> Result.success(SignupContinuationState.PhoneNumberIsNotValidate) + else -> { + val authToken = ownerVerificationCodeRepository.verifySmsCode( + phoneNumber.formatPhoneNumber(), + verificationCode + ) + authToken.getOrDefault(defaultValue = null) + ?.let { tokenRepository.saveOwnerAccessToken(it.token) } + if (authToken.isFailure) { + Result.failure(authToken.exceptionOrNull()!!) + } else + Result.success(SignupContinuationState.CheckComplete) + } + } + } +} From 576d43d6ee3132b9953559a2b3be4fe4b16498d4 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:10:09 +0900 Subject: [PATCH 064/131] Add signup continuation state --- .../koin/domain/state/signup/SignupContinuationState.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/domain/src/main/java/in/koreatech/koin/domain/state/signup/SignupContinuationState.kt b/domain/src/main/java/in/koreatech/koin/domain/state/signup/SignupContinuationState.kt index 7d6e8e9d8..542d44b87 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/state/signup/SignupContinuationState.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/state/signup/SignupContinuationState.kt @@ -7,6 +7,7 @@ sealed class SignupContinuationState { object AvailableNickname: SignupContinuationState() // 이메일 중복 확인으로 사용 가능 object RequestedEmailValidation: SignupContinuationState() + object RequestedSmsValidation: SignupContinuationState() object CheckNickName: SignupContinuationState() // 닉네임 중복버튼 눌렀는지 확인 object CheckGender: SignupContinuationState() // 성별 라디오 버튼 눌렀는지 확인 object CheckGraduate: SignupContinuationState() // 졸업생 라디오 버튼을 눌렀는지 확인 @@ -15,6 +16,8 @@ sealed class SignupContinuationState { object InitPhoneNumber: SignupContinuationState() // 전화번호를 작성했는지 확인 object InitStudentId: SignupContinuationState() // 학번을 작성했는지 확인 + object SmsCodeIsNotValidate: SignupContinuationState() + object PhoneNumberIsNotValidate: SignupContinuationState() object EmailIsNotValidate: SignupContinuationState() object PasswordIsNotValidate: SignupContinuationState() object PasswordNotMatching: SignupContinuationState() From e985ea2f422f679e87c61136d16607b1807318b4 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:10:23 +0900 Subject: [PATCH 065/131] Add targetSdk --- .../src/main/java/in/koreatech/convention/AndroidProject.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build-logic/convention/src/main/java/in/koreatech/convention/AndroidProject.kt b/build-logic/convention/src/main/java/in/koreatech/convention/AndroidProject.kt index 2a92fa487..1be984891 100644 --- a/build-logic/convention/src/main/java/in/koreatech/convention/AndroidProject.kt +++ b/build-logic/convention/src/main/java/in/koreatech/convention/AndroidProject.kt @@ -1,5 +1,6 @@ package `in`.koreatech.convention +import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.CommonExtension import org.gradle.api.JavaVersion import org.gradle.api.plugins.ExtensionAware @@ -11,10 +12,11 @@ internal fun configureAndroidProject( ) { commonExtension.apply { compileSdk = 34 - + (this as? ApplicationExtension)?.let { + it.defaultConfig.targetSdk = 34 + } defaultConfig { minSdk = 23 - testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } From b3c70dd9a5ccdf7fb03c046f6b24fdaa35694085 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:11:00 +0900 Subject: [PATCH 066/131] Modify business signup logic --- .../signup/accountsetup/AccountSetupScreen.kt | 96 ++++++++++++------- .../signup/accountsetup/AccountSetupState.kt | 3 +- .../accountsetup/AccountSetupViewModel.kt | 51 +++++++--- 3 files changed, 98 insertions(+), 52 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index 739726933..ec52a0f13 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -1,6 +1,7 @@ package `in`.koreatech.business.feature.signup.accountsetup import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,7 +17,6 @@ import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults 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.Alignment @@ -34,17 +34,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R -import `in`.koreatech.business.feature.signup.accountauth.EmailAuthViewModel -import `in`.koreatech.business.feature.textfield.AuthTextField import `in`.koreatech.business.feature.textfield.LinedTextField -import `in`.koreatech.business.ui.theme.ColorHelper import `in`.koreatech.business.ui.theme.ColorPrimary +import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorUnarchived import `in`.koreatech.business.ui.theme.Gray1 import `in`.koreatech.business.ui.theme.Gray2 import `in`.koreatech.business.ui.theme.KOIN_ANDROIDTheme import `in`.koreatech.koin.domain.util.ext.isValidPassword -import `in`.koreatech.koin.domain.util.ext.isValidStudentId import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -52,18 +49,18 @@ import org.orbitmvi.orbit.compose.collectSideEffect fun AccountSetupScreen( modifier: Modifier = Modifier, viewModel: AccountSetupViewModel = hiltViewModel(), - authViewModel: EmailAuthViewModel = hiltViewModel(), onBackClicked: () -> Unit = {}, - onNextClicked: (String) -> Unit = {}, + onNextClicked: () -> Unit = {}, ) { val state = viewModel.collectAsState().value - val authState = authViewModel.collectAsState().value Column( modifier = modifier.fillMaxSize(), ) { Box( - modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp) + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp) ) { IconButton( onClick = { viewModel.onBackButtonClicked() }, @@ -101,7 +98,11 @@ fun AccountSetupScreen( fontWeight = Bold, text = stringResource(id = R.string.input_basic_information), ) - Text(text = stringResource(id = R.string.two_third), color = ColorPrimary, fontWeight = Bold,) + Text( + text = stringResource(id = R.string.two_third), + color = ColorPrimary, + fontWeight = Bold, + ) } Canvas( @@ -129,15 +130,17 @@ fun AccountSetupScreen( Text(text = stringResource(id = R.string.id), fontSize = 14.sp, fontWeight = Bold) Row( - modifier=Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.Top, horizontalArrangement = Arrangement.SpaceBetween ) { LinedTextField( value = state.id, onValueChange = { viewModel.onIdChanged(it) }, - modifier = Modifier.width(203.dp), + modifier = Modifier + .width(203.dp), label = stringResource(id = R.string.enter_id), errorText = stringResource(id = R.string.id_not_validate), textStyle = TextStyle.Default.copy(fontSize = 15.sp), @@ -145,7 +148,7 @@ fun AccountSetupScreen( Button(modifier = Modifier .width(115.dp) - .height(44.dp), + .height(41.dp), shape = RoundedCornerShape(4.dp), enabled = state.id.isNotEmpty(), colors = ButtonDefaults.buttonColors( @@ -155,7 +158,7 @@ fun AccountSetupScreen( disabledContentColor = Gray1, ), onClick = { - /* 아이디 중복 확인 */ + /* 아이디 중복 확인 */ }) { Text( text = stringResource(id = R.string.check_duplicate), @@ -164,7 +167,7 @@ fun AccountSetupScreen( ) } } - Spacer(modifier = Modifier.height(20.dp)) + Spacer(modifier = Modifier.height(10.dp)) Text(text = stringResource(id = R.string.password), fontSize = 14.sp, fontWeight = Bold) LinedTextField( @@ -173,12 +176,17 @@ fun AccountSetupScreen( modifier = Modifier.fillMaxWidth(), label = stringResource(id = R.string.enter_password), textStyle = TextStyle.Default.copy(fontSize = 15.sp), + errorText = stringResource(id = R.string.password_not_validate), isPassword = true, - isError = state.password.isValidPassword, + isError = !state.password.isValidPassword() && state.password.isNotEmpty(), ) Spacer(modifier = Modifier.height(10.dp)) - Text(text = stringResource(id = R.string.password_confirm), fontSize = 14.sp, fontWeight = Bold) + Text( + text = stringResource(id = R.string.password_confirm), + fontSize = 14.sp, + fontWeight = Bold + ) LinedTextField( value = state.passwordConfirm, onValueChange = { viewModel.onPasswordConfirmChanged(it) }, @@ -187,12 +195,16 @@ fun AccountSetupScreen( textStyle = TextStyle.Default.copy(fontSize = 15.sp), isPassword = true, errorText = stringResource(id = R.string.password_mismatch), - isError = state.password !=state.passwordConfirm && state.passwordConfirm.isNotEmpty(), + isError = state.password != state.passwordConfirm && state.passwordConfirm.isNotEmpty(), ) Spacer(modifier = Modifier.height(10.dp)) - Text(text = stringResource(id = R.string.phone_number), fontSize = 14.sp, fontWeight = Bold) + Text( + text = stringResource(id = R.string.phone_number), + fontSize = 14.sp, + fontWeight = Bold + ) LinedTextField( value = state.phoneNumber, @@ -201,31 +213,37 @@ fun AccountSetupScreen( label = stringResource(id = R.string.enter_phone_number), textStyle = TextStyle.Default.copy(fontSize = 15.sp), errorText = stringResource(id = R.string.phone_number_not_validate), - isError = state.phoneNumber.length!=11 && state.phoneNumber.isNotEmpty(), + isError = state.phoneNumber.length != 11 && state.phoneNumber.isNotEmpty(), ) Spacer(modifier = Modifier.height(10.dp)) - Text(text = stringResource(id = R.string.authentication_code), fontSize = 14.sp, fontWeight = Bold) + Text( + text = stringResource(id = R.string.authentication_code), + fontSize = 14.sp, + fontWeight = Bold + ) Row( - modifier=Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.Top, horizontalArrangement = Arrangement.SpaceBetween ) { LinedTextField( - value = authState.authCode, - onValueChange = { authViewModel.onAuthCodeChanged(it) }, - modifier = Modifier.width(203.dp), + modifier = Modifier + .width(203.dp), + value = state.authCode, + onValueChange = { viewModel.onAuthCodeChanged(it) }, label = stringResource(id = R.string.enter_verification_code), textStyle = TextStyle.Default.copy(fontSize = 20.sp), - isPassword = true, + errorText = stringResource(id = R.string.sms_code_not_validate), isError = state.signUpContinuationError != null, - ) + ) Button(modifier = Modifier .width(115.dp) - .height(44.dp), + .height(41.dp), shape = RoundedCornerShape(4.dp), enabled = state.phoneNumber.isNotEmpty(), colors = ButtonDefaults.buttonColors( @@ -235,15 +253,14 @@ fun AccountSetupScreen( disabledContentColor = Gray1, ), onClick = { - viewModel.postPhoneVerification( - state.phoneNumber, state.password, state.passwordConfirm + viewModel.sendSmsVerificationCode( + state.phoneNumber ) }) { Text( text = stringResource(id = R.string.send_authentication_code), - fontWeight= Bold, + fontWeight = Bold, fontSize = 13.sp, - ) } } @@ -261,7 +278,12 @@ fun AccountSetupScreen( disabledContentColor = Gray1, ), onClick = { - viewModel.onNextButtonClicked() + viewModel.verifySmsCode( + state.password, + state.passwordConfirm, + state.phoneNumber, + state.authCode, + ) }) { Text( text = stringResource(id = R.string.next), @@ -274,7 +296,7 @@ fun AccountSetupScreen( viewModel.collectSideEffect { when (it) { - is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked(state.phoneNumber) + is AccountSetupSideEffect.NavigateToNextScreen -> onNextClicked() AccountSetupSideEffect.NavigateToBackScreen -> onBackClicked() } } @@ -288,5 +310,5 @@ fun AccountSetupScreenPreview() { onNextClicked = {}, onBackClicked = {} ) - } + } } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt index 2929f983e..94354099e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt @@ -7,11 +7,12 @@ data class AccountSetupState( val password: String = "", val passwordConfirm: String = "", val phoneNumber: String = "", + val authCode: String = "", val signupContinuationState: SignupContinuationState = SignupContinuationState.RequestedEmailValidation, val signUpContinuationError:Throwable? = null, val isLoading: Boolean = false ){ val isButtonEnabled: Boolean - get() = id.isNotEmpty() && password.isNotEmpty() && passwordConfirm.isNotEmpty() && phoneNumber.isNotEmpty() + get() = id.isNotEmpty() && password.isNotEmpty() && passwordConfirm.isNotEmpty() && phoneNumber.isNotEmpty() && authCode.isNotEmpty() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 64f56fb01..adcd6d917 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.domain.state.signup.SignupContinuationState +import `in`.koreatech.koin.domain.usecase.business.BusinessSignupCheckUseCase import `in`.koreatech.koin.domain.usecase.business.SendSignupEmailUseCase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -17,8 +18,10 @@ import javax.inject.Inject @HiltViewModel class AccountSetupViewModel @Inject constructor( private val sendSignupEmailUseCase: SendSignupEmailUseCase, + private val businessSignupCheckUseCase: BusinessSignupCheckUseCase, ) : ViewModel(), ContainerHost { - override val container = container(AccountSetupState()) + override val container = + container(AccountSetupState()) fun onIdChanged(id: String) = intent { reduce { @@ -44,29 +47,49 @@ class AccountSetupViewModel @Inject constructor( } } - fun onNextButtonClicked() = intent { - postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.phoneNumber)) + fun onAuthCodeChanged(authCode: String) = intent { + reduce { + state.copy(authCode = authCode) + } + reduce { state.copy(signUpContinuationError = null) } } fun onBackButtonClicked() = intent { postSideEffect(AccountSetupSideEffect.NavigateToBackScreen) } + fun verifySmsCode( + password: String, passwordConfirm: String, phoneNumber: String, verifyCode: String + ) { + viewModelScope.launch { + businessSignupCheckUseCase( + password, passwordConfirm, phoneNumber, verifyCode + ).onSuccess { + intent { + reduce { state.copy(signupContinuationState = it) } + reduce { state.copy(signUpContinuationError = null) } + postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.phoneNumber)) + } + }.onFailure { + intent { + reduce { state.copy(signUpContinuationError = it) } + } + } + } + } - fun postPhoneVerification(email: String, password: String, passwordConfirm: String) { - intent { reduce { state.copy(isLoading = true) } } + fun sendSmsVerificationCode(phoneNumber: String) { viewModelScope.launch(Dispatchers.IO) { - sendSignupEmailUseCase(email, password, passwordConfirm) - .onSuccess { - intent { - reduce { state.copy(signupContinuationState = it) } - reduce { state.copy(signUpContinuationError = null) } - } + sendSignupEmailUseCase(phoneNumber).onSuccess { + intent { + reduce { state.copy(signupContinuationState = it) } + reduce { state.copy(signUpContinuationError = null) } } - .onFailure { - intent { reduce { state.copy(signUpContinuationError = it) } } + }.onFailure { + intent { + reduce { state.copy(signUpContinuationError = it) } } - intent { reduce { state.copy(isLoading = false) } } + } } } } \ No newline at end of file From 09d49dce5219f48ca7b5d405d9ec76686212df16 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 17:12:28 +0900 Subject: [PATCH 067/131] Add stirng, image resource --- business/src/main/res/drawable/attach_file_add.xml | 9 +++++++++ business/src/main/res/values/strings.xml | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 business/src/main/res/drawable/attach_file_add.xml diff --git a/business/src/main/res/drawable/attach_file_add.xml b/business/src/main/res/drawable/attach_file_add.xml new file mode 100644 index 000000000..e0296e60f --- /dev/null +++ b/business/src/main/res/drawable/attach_file_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/business/src/main/res/values/strings.xml b/business/src/main/res/values/strings.xml index 01a05f0c0..82117ee7a 100644 --- a/business/src/main/res/values/strings.xml +++ b/business/src/main/res/values/strings.xml @@ -48,7 +48,7 @@ 파일 첨부 파일을 첨부해주세요 사업자 등록증, 영업신고증, 통장사본을 첨부하세요. - 사업자등록증, 영업신고증, 통장사본 이미지 필수\\n10mb 이하의 PDF 혹은 이미지 형식의 파일(e.g. jpg, png, gif 등)로 5개까지 업로드 가능합니다. + 사업자등록증, 영업신고증, 통장사본 이미지 필수\n10mb 이하의 PDF 혹은 이미지 형식의 파일(e.g. jpg, png, gif 등)로 5개까지 업로드 가능합니다. 파일 선택하기 취소 사업자 등록번호 @@ -61,7 +61,7 @@ 휴대폰 번호 선택 회원가입 완료 - 가입 신청이 완료되었습니다.\\n가입 허가가 승인되면 로그인이 가능합니다. + 회원가입이 완료되었습니다!\n가입 승인 시 로그인이 가능합니다. 로그인 화면 바로가기 BackArrow check @@ -83,5 +83,9 @@ 사업자 등록번호를 입력해주세요. -없이 번호를 입력해주세요. 사업자 인증 파일 + 인증번호가 일치하지 않습니다. + 중복된 휴대폰 번호입니다. + 특수문자 포함 영어와 숫자 6~18 자리로 입력해주세요. + 파일 첨부 From 362e17bc957a362ebb81066bcdd425c8390c410a Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 18:54:53 +0900 Subject: [PATCH 068/131] Modify complete signup screen ui --- .../completesignup/CompleteSignupScreen.kt | 131 ++++++++++++++++-- 1 file changed, 117 insertions(+), 14 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupScreen.kt index 46d146ce2..d3c21dc5d 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupScreen.kt @@ -1,52 +1,128 @@ package `in`.koreatech.business.feature.signup.completesignup +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.Center +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.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults 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.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight.Companion.Bold import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R import `in`.koreatech.business.ui.theme.ColorDescription import `in`.koreatech.business.ui.theme.ColorDisabledButton import `in`.koreatech.business.ui.theme.ColorPrimary -import `in`.koreatech.business.ui.theme.ColorSecondary +import `in`.koreatech.business.ui.theme.ColorUnarchived +import org.orbitmvi.orbit.compose.collectSideEffect @Composable fun CompleteSignupScreen( modifier: Modifier = Modifier, + viewModel: CompleteSignupViewModel= hiltViewModel(), + onNavigateToLoginScreen: () -> Unit = {}, onBackClicked: () -> Unit = {} ) { Column( modifier = modifier, ) { - IconButton( - modifier = Modifier.padding(vertical = 24.dp), - onClick = { onBackClicked() } + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp) ) { - Icon( - painter = painterResource(id = R.drawable.ic_arrow_back), - contentDescription = stringResource(id = R.string.back_icon), + IconButton( + onClick = { viewModel.onBackButtonClicked() }, + modifier = Modifier.align(Alignment.CenterStart) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_arrow_back), + contentDescription = stringResource(id = R.string.back_icon), + ) + } + + Text( + text = stringResource(id = R.string.sign_up), + fontSize = 18.sp, + fontWeight = Bold, + modifier = Modifier.align(Alignment.Center) ) } + Column( + modifier = Modifier + .padding(horizontal = 32.dp), + verticalArrangement = Arrangement.Center, + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + modifier = Modifier, + color = ColorPrimary, + fontWeight = Bold, + text = stringResource(id = R.string.business_auth), + ) + Text( + text = stringResource(id = R.string.three_third), + color = ColorPrimary, + fontWeight = Bold, + ) + } + + Canvas( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + drawLine( + color = ColorUnarchived, + start = Offset(-40f, 0f), + end = Offset(size.width + 35, size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + drawLine( + color = ColorPrimary, + start = Offset(-40f, 0f), + end = Offset((size.width + 40), size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + } + + } Spacer(modifier = Modifier.height(123.dp)) Column( @@ -54,12 +130,12 @@ fun CompleteSignupScreen( verticalArrangement = Center, horizontalAlignment = Alignment.CenterHorizontally ) { - Icon( - modifier = Modifier.size(55.dp), - painter = painterResource(id = R.drawable.signup_check), - contentDescription = stringResource(id = R.string.check_icon), - tint = ColorSecondary + Image( + modifier = Modifier.size(276.dp), + painter = painterResource(id = R.drawable.complete_signup), + contentDescription = stringResource(id = R.string.signup_request_complete), ) + Spacer(modifier = Modifier.height(25.dp)) Text( @@ -82,14 +158,14 @@ fun CompleteSignupScreen( Button(modifier = Modifier .fillMaxWidth() .height(44.dp), - shape = RectangleShape, + shape = RoundedCornerShape(4.dp), colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, disabledBackgroundColor = ColorDisabledButton, contentColor = Color.White, disabledContentColor = Color.White, ), - onClick = { onBackClicked() }) { + onClick = { viewModel.onNavigateToLoginScreen() }) { Text( text = stringResource(id = R.string.navigate_to_login_screen), fontSize = 15.sp, @@ -99,6 +175,33 @@ fun CompleteSignupScreen( } } + viewModel.collectSideEffect { + when (it) { + is CompleteSignupSideEffect.NavigateToLoginScreen -> { + onNavigateToLoginScreen() + } + is CompleteSignupSideEffect.NavigateToBackScreen -> { + onBackClicked() + } + } + } + + } +} + + +@Composable +@Preview +fun Preview() { + Surface( + modifier = Modifier + .fillMaxSize() + + ) { + + CompleteSignupScreen( + onBackClicked = { } + ) } } From 0c105f1e48a513becede50f2abedbca17ea7b0f9 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 18:55:04 +0900 Subject: [PATCH 069/131] Add complete signup viewModel --- .../completesignup/CompleteSignupViewModel.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupViewModel.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupViewModel.kt new file mode 100644 index 000000000..e2503a5e3 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupViewModel.kt @@ -0,0 +1,28 @@ +package `in`.koreatech.business.feature.signup.completesignup + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +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 javax.inject.Inject + +@HiltViewModel +class CompleteSignupViewModel @Inject constructor() : + ContainerHost, ViewModel() { + override val container = + container(Unit) + + fun onNavigateToLoginScreen() { + intent { + postSideEffect(CompleteSignupSideEffect.NavigateToLoginScreen) + } + } + + fun onBackButtonClicked() { + intent { + postSideEffect(CompleteSignupSideEffect.NavigateToBackScreen) + } + } +} \ No newline at end of file From d08a2b633117409cb6d0667192d2391fc46584a0 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 18:55:24 +0900 Subject: [PATCH 070/131] Add complete singup sideEffect --- .../signup/completesignup/CompleteSignupSideEffect.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupSideEffect.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupSideEffect.kt new file mode 100644 index 000000000..d4c07c334 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/signup/completesignup/CompleteSignupSideEffect.kt @@ -0,0 +1,6 @@ +package `in`.koreatech.business.feature.signup.completesignup + +sealed class CompleteSignupSideEffect { + data object NavigateToLoginScreen : CompleteSignupSideEffect() + data object NavigateToBackScreen : CompleteSignupSideEffect() +} \ No newline at end of file From 937c4d17d63fc148615ac8d67af70d8451f90005 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 18:56:48 +0900 Subject: [PATCH 071/131] Modify LiendTextField --- .../feature/textfield/LinedTextField.kt | 89 ++++++++++++------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt b/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt index 21b4e147e..35086f1da 100644 --- a/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt +++ b/business/src/main/java/in/koreatech/business/feature/textfield/LinedTextField.kt @@ -1,29 +1,38 @@ package `in`.koreatech.business.feature.textfield +import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.Column +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.width +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField import androidx.compose.material.OutlinedTextField -import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.material.TextFieldDefaults import androidx.compose.runtime.Composable +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.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.PasswordVisualTransformation 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.sp -import com.google.android.material.search.SearchBar -import `in`.koreatech.business.R +import `in`.koreatech.business.ui.theme.ColorError import `in`.koreatech.business.ui.theme.ColorHelper +import `in`.koreatech.business.ui.theme.ColorPrimary import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.business.ui.theme.ColorTextField @@ -40,32 +49,50 @@ fun LinedTextField( errorText: String = "", isError: Boolean = false, ) { - - OutlinedTextField( - modifier = modifier - .height(40.dp).fillMaxWidth() - .background(color = ColorTextField) - .border( - shape = RoundedCornerShape(4.dp), - width = 1.dp, - color = if (isError) ColorSecondary else ColorTextField, - ), + var focused by remember { mutableStateOf(false) } + BasicTextField( value = value, onValueChange = onValueChange, - placeholder = { - Text(text = label, fontSize = 15.sp, color = ColorHelper) - }, - isError = isError, + textStyle = textStyle, + modifier = modifier.onFocusChanged { focused = it.isFocused }, + maxLines = 1, visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None, - ) - Box { - Text( - modifier = Modifier, - text = helperText, - fontSize = 11.sp, - color = ColorHelper, - ) - if (isError) Text(text = errorText, fontSize = 11.sp, color = ColorSecondary) + decorationBox = { innerTextField -> + Column(modifier = Modifier.fillMaxWidth()) { + + Column( + modifier = Modifier + .fillMaxWidth() + .height(41.dp) + .border( + width = 1.dp, + color = if (isError) ColorSecondary else if (focused) ColorPrimary else ColorTextField, + shape = RoundedCornerShape(4.dp) + ) + .background(color = ColorTextField, shape = RoundedCornerShape(4.dp)), + verticalArrangement = Arrangement.Center, + ) { + Row( + modifier = Modifier.padding(start = 12.dp), + ) { + innerTextField() + } + + } - } + Box { + Text( + modifier = Modifier, + text = helperText, + fontSize = 11.sp, + color = ColorHelper, + ) + if (isError) Text(text = errorText, fontSize = 11.sp, color = ColorSecondary) + + } + + } + + } + ) } From 8c51968b22827dd898a6ef5a9e223fa3dd930d48 Mon Sep 17 00:00:00 2001 From: nazero Date: Fri, 17 May 2024 18:58:12 +0900 Subject: [PATCH 072/131] Change BaseViewModel -> ViewModel --- .../signup/businessauth/BusinessAuthViewModel.kt | 4 ++-- .../signup/businessauth/SearchStoreViewModel.kt | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index 04be61ce3..49634b374 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -2,9 +2,9 @@ package `in`.koreatech.business.feature.signup.businessauth import android.graphics.Bitmap import android.net.Uri +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import `in`.koreatech.koin.core.viewmodel.BaseViewModel import `in`.koreatech.koin.data.mapper.strToOwnerRegisterUrl import `in`.koreatech.koin.domain.model.store.AttachStore import `in`.koreatech.koin.domain.model.store.StoreUrl @@ -25,7 +25,7 @@ class BusinessAuthViewModel @Inject constructor( private val getPresignedUrlUseCase: AttachStoreFileUseCase, private val uploadFilesUseCase: UploadFileUseCase, private val ownerRegisterUseCase: OwnerRegisterUseCase -) : ContainerHost, BaseViewModel() { +) : ContainerHost, ViewModel() { override val container = container(BusinessAuthState()) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt index fcf6b5399..b02e9192d 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/SearchStoreViewModel.kt @@ -1,16 +1,10 @@ package `in`.koreatech.business.feature.signup.businessauth +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import `in`.koreatech.koin.core.viewmodel.BaseViewModel import `in`.koreatech.koin.domain.usecase.store.GetStoresUseCase import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.syntax.simple.intent @@ -22,7 +16,7 @@ import javax.inject.Inject @HiltViewModel class SearchStoreViewModel @Inject constructor( private val getStoresUseCase: GetStoresUseCase, -) : ContainerHost, BaseViewModel() { +) : ContainerHost, ViewModel() { override val container = container(SearchStoreState()) From c91b8d4d1ce460cad89a2d2845ad0031d836bd26 Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 18 May 2024 00:09:23 +0900 Subject: [PATCH 073/131] Modify business auth screen --- .../signup/businessauth/BusinessAuthScreen.kt | 276 ++++++++++++------ .../businessauth/BusinessAuthViewModel.kt | 4 +- 2 files changed, 190 insertions(+), 90 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index 08c0942a9..3c9e9d626 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -5,9 +5,10 @@ import android.provider.OpenableColumns import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Canvas -import androidx.compose.foundation.border +import androidx.compose.foundation.Image +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -21,6 +22,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Icon @@ -39,6 +43,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight.Companion.Bold import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.times import androidx.hilt.navigation.compose.hiltViewModel import `in`.koreatech.business.R import `in`.koreatech.business.feature.signup.accountsetup.AccountSetupViewModel @@ -46,10 +51,14 @@ import `in`.koreatech.business.feature.signup.dialog.BusinessAlertDialog import `in`.koreatech.business.feature.textfield.LinedTextField import `in`.koreatech.business.ui.theme.ColorDescription import `in`.koreatech.business.ui.theme.ColorDisabledButton -import `in`.koreatech.business.ui.theme.ColorHelper import `in`.koreatech.business.ui.theme.ColorMinor import `in`.koreatech.business.ui.theme.ColorPrimary import `in`.koreatech.business.ui.theme.ColorSecondary +import `in`.koreatech.business.ui.theme.ColorTextField +import `in`.koreatech.business.ui.theme.ColorUnarchived +import `in`.koreatech.business.ui.theme.Gray1 +import `in`.koreatech.business.ui.theme.Gray2 +import `in`.koreatech.business.ui.theme.Gray3 import `in`.koreatech.koin.domain.model.store.AttachStore import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -59,6 +68,7 @@ fun BusinessAuthScreen( modifier: Modifier = Modifier, accountSetupViewModel: AccountSetupViewModel = hiltViewModel(), businessAuthViewModel: BusinessAuthViewModel = hiltViewModel(), + scrollState: ScrollState = rememberScrollState(), onBackClicked: () -> Unit = {}, onSearchClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, @@ -94,7 +104,12 @@ fun BusinessAuthScreen( } } } - businessAuthViewModel.onImageUrlsChanged(uriList.map { AttachStore(it.toString(), fileName) }.toMutableList()) + businessAuthViewModel.onImageUrlsChanged(uriList.map { + AttachStore( + it.toString(), + fileName + ) + }.toMutableList()) if (inputStream != null) { businessAuthViewModel.getPreSignedUrl( uri = it, @@ -112,138 +127,211 @@ fun BusinessAuthScreen( Column( modifier = modifier, ) { - IconButton( - modifier = Modifier.padding(vertical = 24.dp), - onClick = { businessAuthViewModel.onNavigateToBackScreen() }) { - Icon( - modifier = Modifier.padding(start = 10.dp), - painter = painterResource(id = R.drawable.ic_arrow_back), - contentDescription = stringResource(id = R.string.back_icon), + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp) + ) { + IconButton( + onClick = { businessAuthViewModel.onNavigateToBackScreen() }, + modifier = Modifier.align(Alignment.CenterStart) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_arrow_back), + contentDescription = stringResource(id = R.string.back_icon), + ) + } + + Text( + text = stringResource(id = R.string.sign_up), + fontSize = 18.sp, + fontWeight = Bold, + modifier = Modifier.align(Alignment.Center) ) } + + Spacer(modifier = Modifier.height(20.dp)) Column( modifier = Modifier - .padding(horizontal = 32.dp), + .padding(horizontal = 32.dp) + .verticalScroll(scrollState), verticalArrangement = Arrangement.Center, ) { - Text( - text = stringResource(id = R.string.master_sign_up), - fontSize = 24.sp, - fontWeight = Bold, - ) - Spacer(modifier = Modifier.height(40.dp)) Row( modifier = Modifier .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Text( - color = ColorSecondary, text = stringResource(id = R.string.business_auth) + modifier = Modifier, + color = ColorPrimary, + fontWeight = Bold, + text = stringResource(id = R.string.business_auth), ) Text( - color = ColorSecondary, text = stringResource(id = R.string.three_third) + text = stringResource(id = R.string.three_third), + color = ColorPrimary, + fontWeight = Bold, ) } + Canvas( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { - drawLine( - color = ColorSecondary, - start = Offset(-35f, 0f), + color = ColorUnarchived, + start = Offset(-40f, 0f), end = Offset(size.width + 35, size.height), strokeWidth = 4.dp.toPx(), cap = StrokeCap.Round ) + drawLine( + color = ColorPrimary, + start = Offset(-40f, 0f), + end = Offset((size.width + 40) , size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) } Spacer(modifier = Modifier.height(10.dp)) + Text( + text = stringResource(id = R.string.master_name), + fontSize = 14.sp, + fontWeight = Bold + ) LinedTextField( value = businessAuthState.name, onValueChange = { businessAuthViewModel.onNameChanged(it) }, - label = stringResource(id = R.string.master_name) + label = stringResource(id = R.string.enter_name) ) + Text( + text = stringResource(id = R.string.shop_name), + fontSize = 14.sp, + fontWeight = Bold + ) Row( - modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Top, ) { + LinedTextField( modifier = Modifier.width(197.dp), value = businessAuthState.shopName, onValueChange = { businessAuthViewModel.onShopNameChanged(it) }, label = stringResource(id = R.string.enter_store_name) ) - Button( - modifier = Modifier, - onClick = { businessAuthViewModel.onNavigateToSearchStore() }, - shape = RectangleShape, + + Button(modifier = Modifier + .height(41.dp), + shape = RoundedCornerShape(4.dp), colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, contentColor = Color.White, ), - ) { + onClick = { + businessAuthViewModel.onNavigateToSearchStore() + }) { Text(text = stringResource(id = R.string.search_store)) } } + Spacer(modifier = Modifier.height(20.dp)) + Text( + text = stringResource(id = R.string.business_registration_number), + fontSize = 14.sp, + fontWeight = Bold + ) LinedTextField( value = businessAuthState.shopNumber, onValueChange = { businessAuthViewModel.onStoreNumberChanged(it) }, - label = stringResource(id = R.string.business_registration_number) + label = stringResource(id = R.string.enter_business_registration_number) + ) + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = stringResource(id = R.string.personal_contact), + fontSize = 14.sp, + fontWeight = Bold ) LinedTextField( value = businessAuthState.phoneNumber, onValueChange = { businessAuthViewModel.onPhoneNumberChanged(it) }, - label = stringResource(id = R.string.personal_contact) + label = stringResource(id = R.string.enter_personal_contact) ) + Spacer(modifier = Modifier.height(10.dp)) - Box( + Text( + text = stringResource(id = R.string.instruction_file), + fontSize = 14.sp, + fontWeight = Bold + ) + Text( + text = stringResource(id = R.string.file_upload_instruction), + fontSize = 12.sp, + color = ColorDescription + ) + Column( modifier = Modifier - .fillMaxWidth() - .height(125.dp), + .fillMaxWidth(), ) { - if (businessAuthState.selectedImages.isNotEmpty()) UploadFileList( - modifier, - businessAuthState.selectedImages - ) - else - Column( - modifier = Modifier - .fillMaxSize() - .height(125.dp) - .border(BorderStroke(1.dp, ColorHelper)) - .clickable { businessAuthViewModel.onDialogVisibilityChanged(true) }, - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + if (businessAuthState.selectedImages.isNotEmpty()) { + UploadFileList( + modifier, + businessAuthState.selectedImages, ) { - Icon( - modifier = Modifier.size(24.dp), - painter = painterResource(id = R.drawable.plus_square), - contentDescription = stringResource(id = R.string.upload_file_icon), - tint = ColorSecondary - ) - Text( - text = stringResource(id = R.string.file_upload_prompt), - fontSize = 12.sp, - fontWeight = Bold, - color = Color.Black - ) - Text( - text = stringResource(id = R.string.file_upload_instruction), - fontSize = 11.sp, - color = ColorDescription, + val list = mutableListOf() + businessAuthState.selectedImages.forEach { + list.add(it.title) + } + list.removeAt(it) + businessAuthViewModel.onImageUrlsChanged( + list.map { + AttachStore( + it, + it + ) + }.toMutableList() ) } + Spacer(modifier = Modifier.height(10.dp)) + } + + Button( + modifier = Modifier + .fillMaxWidth() + .height(44.dp), + shape = RectangleShape, + enabled = businessAuthState.selectedImages.isEmpty(), + colors = ButtonDefaults.buttonColors( + backgroundColor = ColorTextField, + contentColor = Gray1, + disabledBackgroundColor = ColorTextField, + disabledContentColor = Gray3, + ), + onClick = { businessAuthViewModel.onDialogVisibilityChanged(true) }) { + Icon( + painter = painterResource(id = R.drawable.attach_file_add), + contentDescription = stringResource(id = R.string.attach_file) + ) + Text( + text = stringResource(id = R.string.file_upload), + fontSize = 13.sp, + fontWeight = Bold, + ) + } } - Spacer(modifier = Modifier.height(47.dp)) + Spacer(modifier = Modifier.height(60.dp)) Button(modifier = Modifier .fillMaxWidth() .height(44.dp), - shape = RectangleShape, + shape = RoundedCornerShape(4.dp), enabled = businessAuthState.isButtonEnabled, colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, @@ -264,7 +352,7 @@ fun BusinessAuthScreen( businessAuthViewModel.sendRegisterRequest( businessAuthState.fileInfo.map { it.resultUrl }, businessAuthState.shopNumber, - accountSetupState.email, + accountSetupState.phoneNumber, businessAuthState.name, accountSetupState.password, businessAuthState.phoneNumber, @@ -316,31 +404,41 @@ fun BusinessAuthScreen( } @Composable -fun UploadFileList(modifier: Modifier, item: MutableList) { - LazyColumn( - modifier = modifier - .fillMaxSize() - .border(BorderStroke(1.dp, ColorHelper)), - ) { +fun UploadFileList( + modifier: Modifier, + fileList: MutableList, + onDelete: (Int) -> Unit = {} +) { + Column(modifier = Modifier.height(fileList.size * 40.dp)) { - items(item.size) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( + LazyColumn( + modifier = modifier + .fillMaxSize(), + ) { + items(fileList.size) { + Row( modifier = Modifier - .size(24.dp) - .padding(end = 8.dp), - painter = painterResource(id = R.drawable.file_icon), - contentDescription = stringResource(id = R.string.file_icon), - tint = ColorMinor, - ) - Text(text = item[it].title, fontSize = 15.sp, color = ColorMinor) + .fillMaxWidth() + .background(ColorTextField) + .padding(6.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + + Image( + modifier = Modifier + .size(24.dp) + .clickable { + onDelete(it) + } + .padding(end = 8.dp), + painter = painterResource(id = R.drawable.ic_delete_button), + contentDescription = stringResource(id = R.string.file_icon), + ) + + Text(text = fileList[it].title, fontSize = 15.sp, color = ColorMinor) + } + Spacer(modifier = Modifier.width(12.dp)) } - Spacer(modifier = Modifier.width(12.dp)) } } } diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index 49634b374..4ab7323d0 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -11,6 +11,8 @@ import `in`.koreatech.koin.domain.model.store.StoreUrl import `in`.koreatech.koin.domain.usecase.business.UploadFileUseCase import `in`.koreatech.koin.domain.usecase.owner.AttachStoreFileUseCase import `in`.koreatech.koin.domain.usecase.owner.OwnerRegisterUseCase +import `in`.koreatech.koin.domain.util.ext.formatBusinessNumber +import `in`.koreatech.koin.domain.util.ext.formatPhoneNumber import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost @@ -159,7 +161,7 @@ class BusinessAuthViewModel @Inject constructor( ) { viewModelScope.launch { ownerRegisterUseCase( - fileUrls.strToOwnerRegisterUrl(), companyNumber, email, name, password, phoneNumber, shopId, shopName + fileUrls.strToOwnerRegisterUrl(), companyNumber.formatBusinessNumber(), email, name, password, phoneNumber.formatPhoneNumber(), shopId, shopName ).onSuccess { onNavigateToNextScreen() intent { From dc4315bb1c78c622c6e4f8d00d44807d75ab2863 Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 18 May 2024 01:04:59 +0900 Subject: [PATCH 074/131] Change usecase name sendSignupEmailUseCase -> sendSignupSmsCodeUseCase --- .../accountsetup/AccountSetupViewModel.kt | 7 +++--- .../business/SendSignupEmailUseCase.kt | 24 ------------------- .../business/SendSignupSmsCodeUseCase.kt | 21 ++++++++++++++++ 3 files changed, 24 insertions(+), 28 deletions(-) delete mode 100644 domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt create mode 100644 domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index adcd6d917..c05a1070d 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -3,9 +3,8 @@ package `in`.koreatech.business.feature.signup.accountsetup import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import `in`.koreatech.koin.domain.state.signup.SignupContinuationState import `in`.koreatech.koin.domain.usecase.business.BusinessSignupCheckUseCase -import `in`.koreatech.koin.domain.usecase.business.SendSignupEmailUseCase +import `in`.koreatech.koin.domain.usecase.business.SendSignupSmsCodeUseCase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost @@ -17,7 +16,7 @@ import javax.inject.Inject @HiltViewModel class AccountSetupViewModel @Inject constructor( - private val sendSignupEmailUseCase: SendSignupEmailUseCase, + private val sendSignupSmsCodeUseCase: SendSignupSmsCodeUseCase, private val businessSignupCheckUseCase: BusinessSignupCheckUseCase, ) : ViewModel(), ContainerHost { override val container = @@ -80,7 +79,7 @@ class AccountSetupViewModel @Inject constructor( fun sendSmsVerificationCode(phoneNumber: String) { viewModelScope.launch(Dispatchers.IO) { - sendSignupEmailUseCase(phoneNumber).onSuccess { + sendSignupSmsCodeUseCase(phoneNumber).onSuccess { intent { reduce { state.copy(signupContinuationState = it) } reduce { state.copy(signUpContinuationError = null) } diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt deleted file mode 100644 index 6f3faf954..000000000 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupEmailUseCase.kt +++ /dev/null @@ -1,24 +0,0 @@ -package `in`.koreatech.koin.domain.usecase.business - -import `in`.koreatech.koin.domain.repository.OwnerSignupRepository -import `in`.koreatech.koin.domain.state.signup.SignupContinuationState -import `in`.koreatech.koin.domain.util.ext.isNotBusinessValidEmail -import javax.inject.Inject - -class SendSignupEmailUseCase @Inject constructor( - private val ownerSignupRepository: OwnerSignupRepository -) { - suspend operator fun invoke( - email: String, - password: String, - passwordConfirm: String, - ): Result { - return when { - password != passwordConfirm -> Result.success(SignupContinuationState.PasswordNotMatching) - email.trim().isNotBusinessValidEmail() -> Result.success(SignupContinuationState.EmailIsNotValidate) - else -> ownerSignupRepository.requestEmailVerification( - email = email - ).map {SignupContinuationState.CheckComplete } - } - } -} \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt new file mode 100644 index 000000000..315ad202c --- /dev/null +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt @@ -0,0 +1,21 @@ +package `in`.koreatech.koin.domain.usecase.business + +import `in`.koreatech.koin.domain.repository.OwnerSignupRepository +import `in`.koreatech.koin.domain.state.signup.SignupContinuationState +import `in`.koreatech.koin.domain.util.ext.formatPhoneNumber +import javax.inject.Inject + +class SendSignupSmsCodeUseCase @Inject constructor( + private val ownerSignupRepository: OwnerSignupRepository +) { + suspend operator fun invoke( + phoneNumber: String, + ): Result { + return try { + ownerSignupRepository.requestSmsVerificationCode(phoneNumber.formatPhoneNumber()) + Result.success(SignupContinuationState.RequestedSmsValidation) + } catch (t: Throwable) { + Result.failure(t) + } + } +} \ No newline at end of file From ec2354e5b0f3b2594a516d4e9ad05d33892beb88 Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 18 May 2024 01:05:36 +0900 Subject: [PATCH 075/131] Add string extensions formatBusinessNumber --- .../in/koreatech/koin/domain/util/ext/StringExtensions.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt b/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt index 1bc0aaa99..6e26cc07f 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/util/ext/StringExtensions.kt @@ -19,3 +19,7 @@ fun String.toColorForHtml(color: String) = " Unit, + stroeBasicInfo: StoreBasicInfo = StoreBasicInfo(), + viewModel: InsertDetailInfoScreenViewModel = hiltViewModel() +) { + val state = viewModel.collectAsState().value + InsertDetailInfoScreen( + storeNumber = state.storeAddress + ) +} + + +@Composable +fun InsertDetailInfoScreen( + modifier: Modifier = Modifier, + storeNumber: String = "010-1111-1111" +) { + Column( + modifier = modifier + ) { + Text( + text = storeNumber + ) + } +} + +@Preview +@Composable +fun PreviewInsertDetailInfoScreen(){ + InsertDetailInfoScreen() +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenSideEffect.kt new file mode 100644 index 000000000..923731532 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenSideEffect.kt @@ -0,0 +1,16 @@ +package `in`.koreatech.business.feature.insertstore.insertdetailinfo + +import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenSideEffect + +sealed class InsertDetailInfoScreenSideEffect { + + data class ShowMessage(val type: ErrorType): InsertDetailInfoScreenSideEffect() + + data class NavigateToInsertDetailInfoScreen(val storeBasicInfo: InsertDetailInfoScreenSideEffect) : InsertDetailInfoScreenSideEffect() +} + +enum class ErrorType { + NullStoreName, + NullStoreAddress, + NullStoreImage +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenState.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenState.kt new file mode 100644 index 000000000..f5f9f5f08 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenState.kt @@ -0,0 +1,15 @@ +package `in`.koreatech.business.feature.insertstore.insertdetailinfo + +import android.net.Uri +import android.os.Parcelable +import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenState +import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo +import kotlinx.parcelize.Parcelize + +@Parcelize +data class InsertDetailInfoScreenState ( + val storeCategory: Int = -1, + val storeName: String = "", + val storeAddress: String = "", + val storeImage: String = "" +): Parcelable \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenViewmodel.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenViewmodel.kt new file mode 100644 index 000000000..e1d483be2 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenViewmodel.kt @@ -0,0 +1,48 @@ +package `in`.koreatech.business.feature.insertstore.insertdetailinfo + +import android.net.Uri +import android.util.Log +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import com.google.gson.Gson +import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenState +import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container +import javax.inject.Inject + +@HiltViewModel +class InsertDetailInfoScreenViewModel @Inject constructor( + savedStateHandle: SavedStateHandle +): ViewModel(), ContainerHost { + override val container: Container = + container(InsertDetailInfoScreenState(), savedStateHandle = savedStateHandle) { + + val storeBasicInfoJson: String? = savedStateHandle.get("storeBasicInfo") + checkNotNull(storeBasicInfoJson) + + val storeBasicInfo = Gson().fromJson( + storeBasicInfoJson, + StoreBasicInfo::class.java + ) + getStoreBasicInfo(storeBasicInfo) + } + + + private fun getStoreBasicInfo(storeBasicInfo: StoreBasicInfo){ + intent{ + reduce { + state.copy( + storeCategory = storeBasicInfo.storeCategory, + storeName = storeBasicInfo.storeName, + storeAddress = storeBasicInfo.storeAddress, + storeImage = storeBasicInfo.storeImage + ) + } + } + } +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreen.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreen.kt new file mode 100644 index 000000000..08cce410b --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreen.kt @@ -0,0 +1,303 @@ +package `in`.koreatech.business.feature.insertstore.insertmaininfo + +import android.app.Activity +import android.content.Intent +import android.net.Uri +import android.provider.MediaStore +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.foundation.layout.width +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +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 androidx.hilt.navigation.compose.hiltViewModel +import coil.compose.rememberAsyncImagePainter +import coil.compose.rememberImagePainter +import `in`.koreatech.business.feature.insertstore.selectcategory.InsertStoreProgressBar +import `in`.koreatech.business.ui.theme.ColorActiveButton +import `in`.koreatech.business.ui.theme.ColorDisabledButton +import `in`.koreatech.business.ui.theme.ColorMinor +import `in`.koreatech.business.ui.theme.ColorPrimary +import `in`.koreatech.koin.core.R +import `in`.koreatech.koin.core.toast.ToastUtil +import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect + + +@Composable +fun InsertBasicInfoScreenImpl( + onBackPressed: () -> Unit, + navigateToInsertDetailInfoScreen: (StoreBasicInfo) -> Unit, + viewModel: InsertBasicInfoScreenViewModel = hiltViewModel() +) { + val state = viewModel.collectAsState().value + + InsertBasicInfoScreen( + storeImage = state.storeImage, + storeName = state.storeName, + storeAddress = state.storeAddress, + storeImageIsEmpty = state.storeImageIsEmpty, + isBasicInfoValidate = state.isBasicInfoValidate, + onStoreImageChange = { + viewModel.insertStoreImage(it) + }, + onStoreNameChange = { + viewModel.insertStoreName(it) + }, + onStoreAddressChange = { + viewModel.insertStoreAddress(it) + }, + onNextButtonClicked = { + viewModel.onNextButtonClick() + }, + onBackPressed = onBackPressed + ) + + HandleSideEffects(viewModel, navigateToInsertDetailInfoScreen) +} + +@Composable +fun InsertBasicInfoScreen( + modifier: Modifier = Modifier, + storeImage: Uri = Uri.EMPTY, + storeImageIsEmpty: Boolean = true, + storeName: String = "맛있는 족발", + storeAddress: String = "층청남도 충절로 1600", + isBasicInfoValidate: Boolean = false, + onStoreImageChange: (Uri) -> Unit = {}, + onStoreNameChange: (String) -> Unit = {}, + onStoreAddressChange: (String) -> Unit = {}, + onNextButtonClicked: () -> Unit = {}, + onBackPressed: () -> Unit = {} +) { + + val galleryLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + result.data?.data?.let { + onStoreImageChange(it) + } + } + } + + Column( + modifier = modifier.fillMaxSize() + ) { + Box( + modifier = modifier + .padding(top = 56.dp, start = 10.dp, bottom = 18.dp) + .width(40.dp) + .height(40.dp) + .clickable { + onBackPressed() + } + ) { + Image( + painter = painterResource(R.drawable.ic_arrow_left), + contentDescription = "backArrow", + modifier = modifier + .width(40.dp) + .height(40.dp) + ) + } + + Text( + modifier = modifier.padding(top = 35.dp, start = 40.dp), + text = stringResource(id = R.string.insert_store), + fontSize = 24.sp + ) + + Text( + modifier = modifier.padding(top = 34.dp, start = 32.dp), + text = stringResource(id = R.string.insert_store_main_info), + fontSize = 18.sp + ) + + InsertStoreProgressBar(modifier, 0.50f, R.string.insert_store_basic_info, R.string.page_two) + + + Box( + modifier = Modifier + .padding(top = 32.dp) + .padding(horizontal = 32.dp) + .fillMaxWidth() + .height(200.dp) + .border(width = 1.dp, color = ColorMinor) + .clickable { + galleryLauncher.launch(takePhotoFromAlbumIntent) + } + , + contentAlignment = Alignment.Center + ){ + if(storeImageIsEmpty){ + Image( + modifier = Modifier + .padding(horizontal = 71.dp, vertical = 53.dp), + painter = painterResource(R.drawable.ic_no_store_image), + contentDescription = "enptyStoreImage" + ) + } + else{ + Image( + modifier = Modifier.fillMaxSize(), + painter = rememberAsyncImagePainter( + storeImage + ), + contentDescription = "storeImage", + contentScale = ContentScale.FillBounds + ) + } + } + + NameTextField( + stringResource(id = R.string.insert_store_store_name), + storeName, + onStoreNameChange, + 32.dp + ) + + NameTextField( + stringResource(id = R.string.insert_store_store_address), + storeAddress, + onStoreAddressChange, + 24.dp + ) + + Button( + onClick = onNextButtonClicked, + colors = if(isBasicInfoValidate) ButtonDefaults.buttonColors(ColorPrimary) + else ButtonDefaults.buttonColors(ColorDisabledButton), + shape = RectangleShape, + modifier = modifier + .padding(top = 57.dp, start = 240.dp, end = 16.dp) + .height(38.dp) + .width(105.dp) + ) { + Text( + text = stringResource(id = R.string.next), + fontSize = 15.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + } + + } + +} + +@Composable +private fun HandleSideEffects(viewModel: InsertBasicInfoScreenViewModel, navigateToInsertDetailInfoScreen: (StoreBasicInfo) -> Unit) { + val context = LocalContext.current + + viewModel.collectSideEffect { sideEffect -> + when (sideEffect) { + is InsertBasicInfoScreenSideEffect.NavigateToInsertDetailInfoScreen -> navigateToInsertDetailInfoScreen(sideEffect.storeBasicInfo) + is InsertBasicInfoScreenSideEffect.ShowMessage -> { + val message = when (sideEffect.type) { + ErrorType.NullStoreName -> context.getString(R.string.insert_store_null_store_name) + ErrorType.NullStoreAddress -> context.getString(R.string.insert_store_null_store_address) + ErrorType.NullStoreImage -> context.getString(R.string.insert_store_null_store_image) + } + ToastUtil.getInstance().makeShort(message) + } + } + } +} + +@Composable +fun NameTextField( + textString: String = "가게명", + inputString: String = "0", + onStringChange: (String) -> Unit = {}, + paddingTopValue: Dp = 10.dp +){ + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp) + .padding(top = paddingTopValue), + verticalAlignment = Alignment.CenterVertically + ){ + Text( + text = textString, + fontSize = 14.sp, + color = ColorActiveButton, + fontWeight = FontWeight.Bold + ) + + BorderTextField(inputString, onStringChange) + } +} + +@Composable +fun BorderTextField( + inputString: String = "", + onStringChange: (String) -> Unit = {}, +){ + Box( + modifier = Modifier + .padding(start = 30.dp) + .border(width = 1.dp, color = ColorMinor) + .height(37.dp), + contentAlignment = Alignment.CenterStart + ) { + BasicTextField( + value = inputString, + onValueChange = onStringChange, + textStyle = TextStyle( + color = Color.Black, + fontSize = 14.sp + ), + modifier = Modifier + .fillMaxWidth() + .padding(start = 12.dp) + ) + } +} + +private val takePhotoFromAlbumIntent = + Intent(Intent.ACTION_GET_CONTENT, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply { + type = "image/*" + action = Intent.ACTION_GET_CONTENT + putExtra( + Intent.EXTRA_MIME_TYPES, + arrayOf("image/jpeg", "image/png", "image/bmp", "image/webp") + ) + putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) + } + + + +@Preview +@Composable +fun PreviewInsertBasicInfo() { + InsertBasicInfoScreen() +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenSideEffect.kt new file mode 100644 index 000000000..fcf095114 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenSideEffect.kt @@ -0,0 +1,17 @@ +package `in`.koreatech.business.feature.insertstore.insertmaininfo + +import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo + + +sealed class InsertBasicInfoScreenSideEffect { + + data class ShowMessage(val type: ErrorType): InsertBasicInfoScreenSideEffect() + + data class NavigateToInsertDetailInfoScreen(val storeBasicInfo: StoreBasicInfo) : InsertBasicInfoScreenSideEffect() +} + +enum class ErrorType { + NullStoreName, + NullStoreAddress, + NullStoreImage +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenState.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenState.kt new file mode 100644 index 000000000..9d2e1922d --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenState.kt @@ -0,0 +1,15 @@ +package `in`.koreatech.business.feature.insertstore.insertmaininfo + +import android.net.Uri +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class InsertBasicInfoScreenState( + val storeName: String = "", + val storeAddress: String = "", + val storeImage: Uri = Uri.EMPTY, + val storeImageIsEmpty: Boolean = true, + val storeCategory: Int = 0, + val isBasicInfoValidate: Boolean = false +): Parcelable \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenViewModel.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenViewModel.kt new file mode 100644 index 000000000..31bdf5637 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenViewModel.kt @@ -0,0 +1,100 @@ +package `in`.koreatech.business.feature.insertstore.insertmaininfo + +import android.net.Uri +import android.util.Log +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo +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.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container +import javax.inject.Inject + +@HiltViewModel +class InsertBasicInfoScreenViewModel @Inject constructor( + savedStateHandle: SavedStateHandle +): ViewModel(), ContainerHost { + override val container: Container = + container(InsertBasicInfoScreenState(), savedStateHandle = savedStateHandle){ + val categoryId = savedStateHandle.get("categoryId") + checkNotNull(categoryId) + getCategoryId(categoryId) + } + + fun insertStoreName(storeName: String) = intent { + reduce { + state.copy(storeName = storeName) + } + isBasicInfoValidate() + } + + fun insertStoreAddress(storeAddress: String) = intent{ + reduce { + state.copy(storeAddress = storeAddress) + } + isBasicInfoValidate() + } + + fun insertStoreImage(storeImage: Uri) = intent{ + reduce { + state.copy(storeImage = storeImage) + } + isBasicInfoValidate() + storeImageIsEmpty() + } + + fun onNextButtonClick(){ + intent{ + if(state.isBasicInfoValidate){ + val storeBasicInfo: StoreBasicInfo = StoreBasicInfo( + storeName = state.storeName, + storeAddress = state.storeAddress, + storeImage = state.storeImage.toString(), + storeCategory = state.storeCategory + ) + postSideEffect(InsertBasicInfoScreenSideEffect.NavigateToInsertDetailInfoScreen(storeBasicInfo)) + } + else{ + if(state.storeImageIsEmpty){ + postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(ErrorType.NullStoreImage)) + } + else if(state.storeName.isEmpty()){ + postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(ErrorType.NullStoreName)) + } + else if(state.storeAddress.isEmpty()){ + postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(ErrorType.NullStoreAddress)) + } + } + } + } + + private fun storeImageIsEmpty() = intent{ + reduce { + state.copy(storeImageIsEmpty = state.storeImage == Uri.EMPTY) + } + isBasicInfoValidate() + } + + private fun getCategoryId(id: Int){ + intent{ + reduce { + state.copy( + storeCategory = id + ) + } + } + } + + private fun isBasicInfoValidate() = intent { + reduce { + state.copy( + isBasicInfoValidate = state.storeAddress.isNotBlank() && state.storeName.isNotBlank() && state.storeImage != Uri.EMPTY + ) + } + } + +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRoute.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRoute.kt new file mode 100644 index 000000000..09b17b8b3 --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRoute.kt @@ -0,0 +1,8 @@ +package `in`.koreatech.business.feature.insertstore.navigator + +enum class InsertStoreRoute(){ + START, + SELECT_CATEGORY, + BASIC_INFO, + DETAIL_INFO +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRouteNavigator.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRouteNavigator.kt new file mode 100644 index 000000000..5e0dec35f --- /dev/null +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRouteNavigator.kt @@ -0,0 +1,110 @@ +package `in`.koreatech.business.feature.insertstore.navigator + +import android.net.Uri +import android.util.Log +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavHostController +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import com.google.gson.Gson +import `in`.koreatech.business.feature.insertstore.insertdetailinfo.InsertDetailInfoScreenImpl +import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenImpl +import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenState +import `in`.koreatech.business.feature.insertstore.selectcategory.SelectCategoryScreenImpl +import `in`.koreatech.business.feature.insertstore.startinsetstore.StartInsertScreen +import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo + +@Composable +fun InsertStoreNavigator( + modifier: Modifier = Modifier, + navController: NavHostController = rememberNavController() +) { + NavHost( + navController = navController, + startDestination = InsertStoreRoute.START.name, + modifier = modifier + ) { + + composable( + route = InsertStoreRoute.START.name, + ) { + StartInsertScreen( + goToSelectCategoryScreen = { + navController.navigate(InsertStoreRoute.SELECT_CATEGORY.name) + }, + onBackPressed = { + navController.navigateUp() + } + ) + } + + composable( + route = InsertStoreRoute.SELECT_CATEGORY.name, + ) { + SelectCategoryScreenImpl( + navigateToInsertBasicInfoScreen = { + navigateToMainInfo(navController, it) + }, + onBackPressed = { + navController.navigateUp() + } + ) + } + + composable( + route = "${InsertStoreRoute.BASIC_INFO.name}/{categoryId}", + arguments = listOf( + navArgument("categoryId"){ + type = NavType.IntType + defaultValue = 0 + } + ) + ){ + InsertBasicInfoScreenImpl( + onBackPressed = { + navController.navigateUp() + }, + navigateToInsertDetailInfoScreen = { + navigateToDetailInfo(navController, it) + } + ) + } + + composable( + route = "${InsertStoreRoute.DETAIL_INFO.name}/{storeBasicInfo}", + arguments = listOf( + navArgument("storeBasicInfo") + { + type = NavType.StringType + defaultValue = "" + } + ) + ){ + InsertDetailInfoScreenImpl( + onBackPress = { + navController.navigateUp() + } + ) + } + } +} + +private fun navigateToMainInfo( + navController: NavController, + categoryId: Int +) { + navController.navigate("${InsertStoreRoute.BASIC_INFO}/${categoryId}") +} + +private fun navigateToDetailInfo( + navController: NavController, + storeBasicInfo: StoreBasicInfo +) { + val storeBasicInfoJson = Uri.encode(Gson().toJson(storeBasicInfo)) + navController.navigate("${InsertStoreRoute.DETAIL_INFO}/${storeBasicInfoJson}") +} \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt index 27eb2ac48..309357832 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt @@ -1,6 +1,7 @@ package `in`.koreatech.business.feature.insertstore.selectcategory import androidx.compose.foundation.Image +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -16,6 +17,7 @@ import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.LinearProgressIndicator @@ -25,6 +27,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.ProgressBarRangeInfo @@ -34,23 +37,42 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import coil.compose.rememberAsyncImagePainter +import coil.compose.rememberImagePainter +import coil.request.ImageRequest +import coil.transform.CircleCropTransformation +import `in`.koreatech.business.ui.theme.ColorDisabledButton import `in`.koreatech.business.ui.theme.ColorPrimary import `in`.koreatech.business.ui.theme.ColorSecondary import `in`.koreatech.koin.core.R +import `in`.koreatech.koin.core.toast.ToastUtil import `in`.koreatech.koin.domain.model.store.StoreCategories import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect @Composable fun SelectCategoryScreenImpl( modifier: Modifier = Modifier, - navigateToFinish: () -> Unit, + navigateToInsertBasicInfoScreen: (Int) -> Unit, onBackPressed: () -> Unit, viewModel: SelectCategoryScreenViewModel = hiltViewModel() ) { val state = viewModel.collectAsState().value SelectCategoryScreen( - categories = state.categories + modifier = modifier, + categories = state.categories, + categoryId = state.categoryId, + chooseCategory = { + viewModel.chooseCategory(it) + }, + categoryIdIsValid = state.categoryIdIsValid, + nextButtonClicked = { + viewModel.goToInsertBasicInfoScreen() + }, + onBackPressed = onBackPressed ) + + HandleSideEffects(viewModel, state.categoryId, navigateToInsertBasicInfoScreen) } @@ -58,17 +80,22 @@ fun SelectCategoryScreenImpl( @Composable fun SelectCategoryScreen( modifier: Modifier = Modifier, - categories: List + categories: List = emptyList(), + categoryId: Int = -1, + categoryIdIsValid: Boolean = true, + chooseCategory: (Int) -> Unit = {}, + nextButtonClicked: () -> Unit = {}, + onBackPressed: () -> Unit = {} ) { Column( modifier = modifier.fillMaxSize() ) { Box( - modifier = modifier + modifier = Modifier .padding(top = 56.dp, start = 10.dp, bottom = 18.dp) .width(40.dp) .height(40.dp) - .clickable { } + .clickable { onBackPressed() } ) { Image( painter = painterResource(R.drawable.ic_arrow_left), @@ -76,33 +103,32 @@ fun SelectCategoryScreen( modifier = modifier .width(40.dp) .height(40.dp) - .clickable { } ) } Text( - modifier = modifier.padding(top = 35.dp, start = 40.dp), + modifier = Modifier.padding(top = 35.dp, start = 40.dp), text = stringResource(id = R.string.insert_store), fontSize = 24.sp ) Text( - modifier = modifier.padding(top = 34.dp, start = 32.dp), + modifier = Modifier.padding(top = 34.dp, start = 32.dp), text = stringResource(id = R.string.insert_store_main_info), fontSize = 18.sp ) - InsertStoreProgressBar(modifier, 0.25f, R.string.insert_store_category_setting, R.string.page_one) + InsertStoreProgressBar(Modifier, 0.25f, R.string.insert_store_category_setting, R.string.page_one) Text( - modifier = modifier.padding(top = 24.dp, start = 40.dp), + modifier = Modifier.padding(top = 24.dp, start = 40.dp), text = stringResource(id = R.string.insert_store_choose_category), fontSize = 18.sp ) LazyHorizontalGrid( rows = GridCells.Fixed(2), - modifier = modifier + modifier = Modifier .padding(top = 28.dp) .padding(horizontal = 15.dp) .height(200.dp) @@ -111,16 +137,20 @@ fun SelectCategoryScreen( ) { items(categories) { category -> CategoryItem( - modifier = Modifier, + modifier = Modifier.clickable { + chooseCategory(category.id) + }, imageUrl = category.imageUrl, - name = category.name + name = category.name, + categoryId = category.id, + choosedCategoryId = categoryId ) } } Button( - onClick = { /*TODO*/ }, - colors = ButtonDefaults.buttonColors(ColorPrimary), + onClick = nextButtonClicked, + colors = if(categoryIdIsValid)ButtonDefaults.buttonColors(ColorPrimary) else ButtonDefaults.buttonColors(ColorDisabledButton), shape = RectangleShape, modifier = modifier .padding(top = 57.dp, start = 240.dp, end = 16.dp) @@ -158,7 +188,7 @@ fun InsertStoreProgressBar( ) Text( - modifier = modifier.padding(start = 160.dp), + modifier = Modifier.padding(start = 160.dp), text = stringResource(id = pageId), fontSize = 15.sp, color = ColorSecondary @@ -166,7 +196,7 @@ fun InsertStoreProgressBar( } LinearProgressIndicator( - modifier = modifier + modifier = Modifier .padding(top = 4.dp) .padding(horizontal = 32.dp) .fillMaxWidth() @@ -179,7 +209,28 @@ fun InsertStoreProgressBar( } @Composable -fun CategoryItem(modifier: Modifier, imageUrl: String, name: String) { +private fun HandleSideEffects( + viewModel: SelectCategoryScreenViewModel, + categoryId: Int, + navigateToInsertMainInfoScreen: (categoryId: Int) -> Unit +) { + val context = LocalContext.current + + viewModel.collectSideEffect { sideEffect -> + when (sideEffect) { + is SelectCategoryScreenSideEffect.NavigateToInsertBasicInfoScreen -> navigateToInsertMainInfoScreen(categoryId) + is SelectCategoryScreenSideEffect.NotSelectCategory -> ToastUtil.getInstance().makeShort(context.getString(R.string.insert_store_choose_category)) + } + } +} +@Composable +fun CategoryItem( + modifier: Modifier, + imageUrl: String, + name: String, + categoryId: Int, + choosedCategoryId: Int +) { Column( modifier = modifier .width(70.dp) @@ -187,16 +238,31 @@ fun CategoryItem(modifier: Modifier, imageUrl: String, name: String) { horizontalAlignment = Alignment.CenterHorizontally ){ Image( - modifier = modifier.size(44.dp), - painter = painterResource(R.drawable.ic_chicken), + modifier = modifier + .size(44.dp) + .border( + width = 2.dp, + color = if (categoryId == choosedCategoryId) ColorSecondary else Color.Transparent, + shape = CircleShape + ) + , + painter = rememberAsyncImagePainter( + ImageRequest.Builder(LocalContext.current).data(data = imageUrl) + .apply(block = fun ImageRequest.Builder.() { + crossfade(true) + transformations(CircleCropTransformation()) + }).build() + ), alignment = Alignment.Center, contentDescription = "category_image" ) Text( modifier = modifier.fillMaxWidth(), - text = "일반음식점", + text = name, textAlign = TextAlign.Center, - fontSize = 12.sp + fontSize = 12.sp, + fontWeight = FontWeight.Bold, + color = if(categoryId == choosedCategoryId) ColorSecondary else Color.Black ) } } @@ -207,7 +273,9 @@ fun PreviewCategoryItem() { CategoryItem( modifier = Modifier, imageUrl = "", - name = "" + name = "치킨", + categoryId = 0, + choosedCategoryId = 0 ) } diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt index 712f257e0..5d530d463 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenSideEffect.kt @@ -3,8 +3,7 @@ package `in`.koreatech.business.feature.insertstore.selectcategory import `in`.koreatech.business.feature_changepassword.passwordauthentication.ErrorType import `in`.koreatech.business.feature_changepassword.passwordauthentication.PasswordAuthenticationSideEffect -sealed class SelectCategoryScreenSideEffect { - data class GotoChangePasswordScreen(val email: String): SelectCategoryScreenSideEffect() - object SendAuthCode: SelectCategoryScreenSideEffect() - data class ShowMessage(val type: ErrorType): SelectCategoryScreenSideEffect() +sealed class SelectCategoryScreenSideEffect { + data class NavigateToInsertBasicInfoScreen(val categoryId: Int): SelectCategoryScreenSideEffect() + object NotSelectCategory: SelectCategoryScreenSideEffect() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt index ebd3b618f..99e9598da 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenState.kt @@ -3,5 +3,7 @@ package `in`.koreatech.business.feature.insertstore.selectcategory import `in`.koreatech.koin.domain.model.store.StoreCategories data class SelectCategoryScreenState( - val categories: List = emptyList() + val categories: List = emptyList(), + val categoryId: Int = -1, + val categoryIdIsValid: Boolean = false ) \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt index c7892ad57..9f3783364 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreenViewModel.kt @@ -1,24 +1,70 @@ package `in`.koreatech.business.feature.insertstore.selectcategory +import android.util.Log import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.domain.usecase.business.OwnerChangePasswordUseCase +import `in`.koreatech.koin.domain.usecase.store.GetStoreCategoriesUseCase +import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import javax.inject.Inject @HiltViewModel class SelectCategoryScreenViewModel @Inject constructor( - private val ownerChangePasswordUseCase: OwnerChangePasswordUseCase + private val getStoreCategoriesUseCase: GetStoreCategoriesUseCase ) : ViewModel(), ContainerHost{ override val container = container(SelectCategoryScreenState()) + init { + getCategory() + } + fun chooseCategory(categoryId: Int) = intent{ + reduce{ + state.copy( + categoryId = categoryId + ) + } + categoryIdIsValid() + } - - - + fun goToInsertBasicInfoScreen(){ + intent { + if(state.categoryIdIsValid){ + postSideEffect(SelectCategoryScreenSideEffect.NavigateToInsertBasicInfoScreen(state.categoryId)) + } + else{ + postSideEffect(SelectCategoryScreenSideEffect.NotSelectCategory) + } + } + } + private fun categoryIdIsValid(){ + intent{ + reduce{ + state.copy( + categoryIdIsValid = (state.categoryId != -1) + ) + } + } + } + private fun getCategory() { + intent { + viewModelScope.launch { + val categories = getStoreCategoriesUseCase().drop(1) + reduce { + state.copy( + categories = categories + ) + } + } + } + } } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt index dc79abb02..5c9cb8854 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/startinsetstore/StartInsertStore.kt @@ -30,7 +30,9 @@ import `in`.koreatech.business.ui.theme.ColorPrimary @Composable fun StartInsertScreen( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + goToSelectCategoryScreen: () -> Unit, + onBackPressed: () -> Unit ) { Column( modifier = modifier.fillMaxSize() @@ -40,7 +42,7 @@ fun StartInsertScreen( .padding(top = 56.dp, start = 10.dp , bottom = 18.dp) .width(40.dp) .height(40.dp) - .clickable { } + .clickable { onBackPressed } ) { Image( @@ -89,7 +91,7 @@ fun StartInsertScreen( ) Button( - onClick = { /*TODO*/ }, + onClick = goToSelectCategoryScreen, colors = ButtonDefaults.buttonColors(ColorPrimary), shape = RectangleShape, modifier = modifier @@ -112,6 +114,8 @@ fun StartInsertScreen( @Composable fun PreviewStartInsertScreen(){ StartInsertScreen( - modifier = Modifier + modifier = Modifier, + goToSelectCategoryScreen = {} , + onBackPressed = {} ) } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index e2580d89c..7febbaa63 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight.Companion.Bold +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import `in`.koreatech.business.R @@ -252,3 +253,11 @@ fun UploadFileList(modifier: Modifier, item: MutableList) { } } } + +@Preview +@Composable +fun PreviewScreen(){ + BusinessAuthScreen( + + ) +} diff --git a/business/src/main/java/in/koreatech/business/feature_changepassword/changepassword/ChangePasswordViewModel.kt b/business/src/main/java/in/koreatech/business/feature_changepassword/changepassword/ChangePasswordViewModel.kt index 3a5b26b73..f54eac56f 100644 --- a/business/src/main/java/in/koreatech/business/feature_changepassword/changepassword/ChangePasswordViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature_changepassword/changepassword/ChangePasswordViewModel.kt @@ -6,9 +6,6 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.domain.state.business.changepw.ChangePasswordExceptionState import `in`.koreatech.koin.domain.usecase.business.OwnerChangePasswordUseCase -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.launch import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost diff --git a/business/src/main/java/in/koreatech/business/ui/theme/Color.kt b/business/src/main/java/in/koreatech/business/ui/theme/Color.kt index 5b4659f47..4cfb3e55c 100644 --- a/business/src/main/java/in/koreatech/business/ui/theme/Color.kt +++ b/business/src/main/java/in/koreatech/business/ui/theme/Color.kt @@ -25,7 +25,6 @@ val Blue1 = Color(0xFFD2DAE2) val ColorPrimary = Color(0xFF175c8e) - val ColorActiveButton = Color(0xFF175c8e) val ColorDisabledButton = Color(0xFFC4C4C4) val ColorSecondary = Color(0xFFF7941E) diff --git a/core/src/main/res/drawable/ic_no_store_image.xml b/core/src/main/res/drawable/ic_no_store_image.xml new file mode 100644 index 000000000..9d7f132db --- /dev/null +++ b/core/src/main/res/drawable/ic_no_store_image.xml @@ -0,0 +1,12 @@ + + + + diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 2d43328dc..6dc084360 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -91,4 +91,11 @@ 등록 하시려는 업체의\n메인 정보를 입력해 주세요. 1. 가게 카테고리 설정 카테고리를 골라주세요. + + 2. 기본 정보 입력 + 가게이름 + 주소정보 + 가게이름을 입력해주세요. + 주소정보를 입력해주세요. + 가게사진을 등록해주세요. diff --git a/domain/src/main/java/in/koreatech/koin/domain/model/owner/insertstore/StoreBasicInfo.kt b/domain/src/main/java/in/koreatech/koin/domain/model/owner/insertstore/StoreBasicInfo.kt new file mode 100644 index 000000000..263735def --- /dev/null +++ b/domain/src/main/java/in/koreatech/koin/domain/model/owner/insertstore/StoreBasicInfo.kt @@ -0,0 +1,8 @@ +package `in`.koreatech.koin.domain.model.owner.insertstore + +data class StoreBasicInfo( + val storeName: String ="", + val storeAddress: String ="", + val storeImage: String ="", + val storeCategory: Int = -1 +) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d004726cc..78bb4c960 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ composeNavigationVersion = "2.7.7" orbitVersion="7.0.1" hiltComposeVersion="1.0.0" markermanVersion="2.3.0" -coilVersion = "1.2.0" +coilVersion = "2.6.0" napier = "2.6.1" [libraries] @@ -136,6 +136,7 @@ hilt-compose= {module= "androidx.hilt:hilt-navigation-compose", version.ref = "h markerman-roundedImageView={module= "com.makeramen:roundedimageview", version.ref = "markermanVersion"} coil ={module = "io.coil-kt:coil", version.ref ="coilVersion"} +coil-compose = {module = "io.coil-kt:coil-compose", version.ref = "coilVersion"} coil-svg = {module = "io.coil-kt:coil-svg", version.ref ="coilVersion"} [plugins] @@ -152,7 +153,7 @@ koin-orbit = { id = "in.koreatech.plugin.orbit", version = "unspecified" } koin-hilt = { id = "in.koreatech.plugin.hilt", version = "unspecified" } [bundles] -compose = ["compose-ui", "compose-graphics", "compose-graphics", "compose-preview", "compose-material2", "activity-compose"] +compose = ["compose-ui", "compose-graphics", "compose-graphics", "compose-preview", "compose-material2", "activity-compose", "compose-ui-tooling"] compose_debug_test = ["compose-ui-tooling", "compose-ui-test-manifest"] hilt = ["hilt-compose", "hilt-android"] orbit = ["orbit-core", "orbit-viewmodel", "orbit-compose"] From 1d74e5100b238a2f18daab50bf592659e4636928 Mon Sep 17 00:00:00 2001 From: nazero Date: Sun, 19 May 2024 18:58:10 +0900 Subject: [PATCH 102/131] Modify check term logic --- .../signup/checkterm/CheckTermScreen.kt | 27 +++++++++---------- .../signup/checkterm/CheckTermViewModel.kt | 14 +++++----- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt index 0d4262f83..5be1de923 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt @@ -158,14 +158,7 @@ fun CheckTermScreen( .height(24.dp) .width(24.dp) .clickable { - viewModel.onAllTermCheckedChanged(!state.isAllTermChecked) - if (!state.isAllTermChecked) { - viewModel.onPrivacyTermCheckedChanged(true) - viewModel.onKoinTermCheckedChanged(true) - } else { - viewModel.onPrivacyTermCheckedChanged(false) - viewModel.onKoinTermCheckedChanged(false) - } + viewModel.onAllTermCheckedChanged() } ) @@ -186,7 +179,9 @@ fun CheckTermScreen( ) { Image( - painter = if (state.isCheckedPrivacyTerms) painterResource(id = R.drawable.ic_check_selected) else painterResource( + painter = if (state.isCheckedPrivacyTerms || state.isAllTermChecked) painterResource( + id = R.drawable.ic_check_selected + ) else painterResource( id = R.drawable.ic_check ), contentDescription = stringResource(R.string.check), @@ -195,8 +190,9 @@ fun CheckTermScreen( .height(24.dp) .width(24.dp) .clickable { - viewModel.onPrivacyTermCheckedChanged(!state.isCheckedPrivacyTerms) - viewModel.onAllTermCheckedChanged(state.isCheckedPrivacyTerms && state.isCheckedKoinTerms) + viewModel.onPrivacyTermCheckedChanged() + if (state.isCheckedKoinTerms) + viewModel.onAllTermCheckedChanged() } ) Spacer(modifier = Modifier.height(10.dp)) @@ -228,7 +224,9 @@ fun CheckTermScreen( ) { Image( - painter = if (state.isCheckedKoinTerms) painterResource(id = R.drawable.ic_check_selected) else painterResource( + painter = if (state.isCheckedKoinTerms || state.isAllTermChecked) painterResource( + id = R.drawable.ic_check_selected + ) else painterResource( id = R.drawable.ic_check ), contentDescription = stringResource(R.string.check), @@ -237,8 +235,9 @@ fun CheckTermScreen( .height(24.dp) .width(24.dp) .clickable { - viewModel.onKoinTermCheckedChanged(!state.isCheckedKoinTerms) - viewModel.onAllTermCheckedChanged(state.isCheckedPrivacyTerms && state.isCheckedKoinTerms) + viewModel.onKoinTermCheckedChanged() + if (state.isCheckedPrivacyTerms) + viewModel.onAllTermCheckedChanged() } ) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt index 62d711414..48b1b7987 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt @@ -16,21 +16,23 @@ class CheckTermViewModel @Inject constructor() : override val container = container(CheckTermState()) - fun onAllTermCheckedChanged(isChecked: Boolean) { + fun onAllTermCheckedChanged() { intent { - reduce { state.copy(isAllTermChecked = isChecked) } + reduce { state.copy(isAllTermChecked = !state.isAllTermChecked) } + reduce { state.copy(isCheckedPrivacyTerms = state.isAllTermChecked) } + reduce { state.copy(isCheckedKoinTerms = state.isAllTermChecked) } } } - fun onPrivacyTermCheckedChanged(isChecked: Boolean) { + fun onPrivacyTermCheckedChanged() { intent { - reduce { state.copy(isCheckedPrivacyTerms = isChecked) } + reduce { state.copy(isCheckedPrivacyTerms = !state.isCheckedPrivacyTerms) } } } - fun onKoinTermCheckedChanged(isChecked: Boolean) { + fun onKoinTermCheckedChanged() { intent { - reduce { state.copy(isCheckedKoinTerms = isChecked) } + reduce { state.copy(isCheckedKoinTerms = !state.isCheckedKoinTerms) } } } From 08fbad5ada4063fc9d155d31fbbfe47fa6ab0ad5 Mon Sep 17 00:00:00 2001 From: nazero Date: Sun, 19 May 2024 19:11:28 +0900 Subject: [PATCH 103/131] Modify multiple reduce into one --- .../accountsetup/AccountSetupViewModel.kt | 11 +++++--- .../businessauth/BusinessAuthViewModel.kt | 26 +++++++++++-------- .../signup/checkterm/CheckTermViewModel.kt | 10 ++++--- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index c05a1070d..bc1ac0b6e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -48,9 +48,8 @@ class AccountSetupViewModel @Inject constructor( fun onAuthCodeChanged(authCode: String) = intent { reduce { - state.copy(authCode = authCode) + state.copy(authCode = authCode, signUpContinuationError = null) } - reduce { state.copy(signUpContinuationError = null) } } fun onBackButtonClicked() = intent { @@ -65,8 +64,12 @@ class AccountSetupViewModel @Inject constructor( password, passwordConfirm, phoneNumber, verifyCode ).onSuccess { intent { - reduce { state.copy(signupContinuationState = it) } - reduce { state.copy(signUpContinuationError = null) } + reduce { + state.copy( + signupContinuationState = it, + signUpContinuationError = null + ) + } postSideEffect(AccountSetupSideEffect.NavigateToNextScreen(state.phoneNumber)) } }.onFailure { diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index f094bcff8..63b02d6c3 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -48,6 +48,7 @@ class BusinessAuthViewModel @Inject constructor( state.copy(shopId = shopId) } } + fun onStoreNumberChanged(storeNumber: String) = intent { reduce { state.copy(shopNumber = storeNumber) @@ -109,17 +110,13 @@ class BusinessAuthViewModel @Inject constructor( fileSize ) ) - } + }, + bitmap = state.bitmap.toMutableList().apply { + add(bitmap) + }, + error = null ) } - reduce { - state.copy(bitmap = state.bitmap.toMutableList().apply { - add(bitmap) - }) - } - } - intent { - reduce { state.copy(error = null) } } }.onFailure { intent { @@ -142,7 +139,7 @@ class BusinessAuthViewModel @Inject constructor( reduce { state.copy(error = null) } } }.onFailure { - intent { + intent { reduce { state.copy(error = it) } } } @@ -161,7 +158,14 @@ class BusinessAuthViewModel @Inject constructor( ) { viewModelScope.launch { ownerRegisterUseCase( - fileUrls.strToOwnerRegisterUrl(), companyNumber.formatBusinessNumber(), email, name, password, phoneNumber.formatPhoneNumber(), shopId, shopName + fileUrls.strToOwnerRegisterUrl(), + companyNumber.formatBusinessNumber(), + email, + name, + password, + phoneNumber.formatPhoneNumber(), + shopId, + shopName ).onSuccess { onNavigateToNextScreen() intent { diff --git a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt index 48b1b7987..502431993 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt @@ -18,9 +18,13 @@ class CheckTermViewModel @Inject constructor() : fun onAllTermCheckedChanged() { intent { - reduce { state.copy(isAllTermChecked = !state.isAllTermChecked) } - reduce { state.copy(isCheckedPrivacyTerms = state.isAllTermChecked) } - reduce { state.copy(isCheckedKoinTerms = state.isAllTermChecked) } + reduce { + state.copy( + isAllTermChecked = !state.isAllTermChecked, + isCheckedPrivacyTerms = state.isAllTermChecked, + isCheckedKoinTerms = state.isAllTermChecked + ) + } } } From f4f854f8183884b1efff66f7b4d9a55ff45bd781 Mon Sep 17 00:00:00 2001 From: hsgo2430 <89733155+hsgo2430@users.noreply.github.com> Date: Mon, 20 May 2024 17:03:45 +0900 Subject: [PATCH 104/131] Update business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt Co-authored-by: Yunjae-Na <41162218+yunjaena@users.noreply.github.com> --- .../insertstore/insertdetailinfo/InsertDetailInfoScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt index 2d25ff06d..a0232e204 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt @@ -26,7 +26,7 @@ fun InsertDetailInfoScreenImpl( @Composable fun InsertDetailInfoScreen( modifier: Modifier = Modifier, - storeNumber: String = "010-1111-1111" + storeNumber: String = "" ) { Column( modifier = modifier From 3608263878307bbbb807ee2a747fb191a75c096d Mon Sep 17 00:00:00 2001 From: hsgo2430 Date: Mon, 20 May 2024 19:54:06 +0900 Subject: [PATCH 105/131] =?UTF-8?q?1=EC=B0=A8=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InsertDetailInfoScreen.kt | 10 ++--- .../InsertDetailInfoScreenSideEffect.kt | 4 +- .../InsertDetailInfoScreenState.kt | 4 +- .../InsertDetailInfoScreenViewmodel.kt | 12 ++--- .../insertmaininfo/InsertBasicInfoScreen.kt | 39 ++++++++-------- .../InsertBasicInfoScreenSideEffect.kt | 7 ++- .../InsertBasicInfoScreenState.kt | 2 +- .../InsertBasicInfoScreenViewModel.kt | 24 +++++----- .../navigator/InsertStoreRouteNavigator.kt | 44 ++++++++++++------- .../selectcategory/SelectCategoryScreen.kt | 8 ++-- domain/build.gradle | 1 + 11 files changed, 77 insertions(+), 78 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt index 2d25ff06d..6f4a7293f 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreen.kt @@ -10,23 +10,23 @@ import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo import org.orbitmvi.orbit.compose.collectAsState @Composable -fun InsertDetailInfoScreenImpl( +fun InsertDetailInfoScreen( modifier: Modifier = Modifier, onBackPress: () -> Unit, stroeBasicInfo: StoreBasicInfo = StoreBasicInfo(), viewModel: InsertDetailInfoScreenViewModel = hiltViewModel() ) { val state = viewModel.collectAsState().value - InsertDetailInfoScreen( + InsertDetailInfoScreenImpl( storeNumber = state.storeAddress ) } @Composable -fun InsertDetailInfoScreen( +fun InsertDetailInfoScreenImpl( modifier: Modifier = Modifier, - storeNumber: String = "010-1111-1111" + storeNumber: String = "" ) { Column( modifier = modifier @@ -40,5 +40,5 @@ fun InsertDetailInfoScreen( @Preview @Composable fun PreviewInsertDetailInfoScreen(){ - InsertDetailInfoScreen() + InsertDetailInfoScreenImpl() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenSideEffect.kt index 923731532..326af0dc9 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenSideEffect.kt @@ -4,12 +4,12 @@ import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInf sealed class InsertDetailInfoScreenSideEffect { - data class ShowMessage(val type: ErrorType): InsertDetailInfoScreenSideEffect() + data class ShowMessage(val type: DetailInfoErrorType): InsertDetailInfoScreenSideEffect() data class NavigateToInsertDetailInfoScreen(val storeBasicInfo: InsertDetailInfoScreenSideEffect) : InsertDetailInfoScreenSideEffect() } -enum class ErrorType { +enum class DetailInfoErrorType { NullStoreName, NullStoreAddress, NullStoreImage diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenState.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenState.kt index f5f9f5f08..8c87bfb75 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenState.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenState.kt @@ -2,8 +2,6 @@ package `in`.koreatech.business.feature.insertstore.insertdetailinfo import android.net.Uri import android.os.Parcelable -import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenState -import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo import kotlinx.parcelize.Parcelize @Parcelize @@ -11,5 +9,5 @@ data class InsertDetailInfoScreenState ( val storeCategory: Int = -1, val storeName: String = "", val storeAddress: String = "", - val storeImage: String = "" + val storeImage: Uri = Uri.EMPTY ): Parcelable \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenViewmodel.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenViewmodel.kt index e1d483be2..37cb372ea 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenViewmodel.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertdetailinfo/InsertDetailInfoScreenViewmodel.kt @@ -8,6 +8,7 @@ import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenState import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo +import kotlinx.android.parcel.Parcelize import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.syntax.simple.intent @@ -22,18 +23,13 @@ class InsertDetailInfoScreenViewModel @Inject constructor( override val container: Container = container(InsertDetailInfoScreenState(), savedStateHandle = savedStateHandle) { - val storeBasicInfoJson: String? = savedStateHandle.get("storeBasicInfo") + val storeBasicInfoJson: InsertBasicInfoScreenState? = savedStateHandle.get("storeBasicInfo") checkNotNull(storeBasicInfoJson) - - val storeBasicInfo = Gson().fromJson( - storeBasicInfoJson, - StoreBasicInfo::class.java - ) - getStoreBasicInfo(storeBasicInfo) + getStoreBasicInfo(storeBasicInfoJson) } - private fun getStoreBasicInfo(storeBasicInfo: StoreBasicInfo){ + private fun getStoreBasicInfo(storeBasicInfo: InsertBasicInfoScreenState){ intent{ reduce { state.copy( diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreen.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreen.kt index 08cce410b..b22aa4170 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreen.kt @@ -16,6 +16,7 @@ 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.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.BasicTextField import androidx.compose.material.Button @@ -52,19 +53,19 @@ import org.orbitmvi.orbit.compose.collectSideEffect @Composable -fun InsertBasicInfoScreenImpl( +fun InsertBasicInfoScreen( onBackPressed: () -> Unit, - navigateToInsertDetailInfoScreen: (StoreBasicInfo) -> Unit, + navigateToInsertDetailInfoScreen: (InsertBasicInfoScreenState) -> Unit, viewModel: InsertBasicInfoScreenViewModel = hiltViewModel() ) { val state = viewModel.collectAsState().value - InsertBasicInfoScreen( + InsertBasicInfoScreenImpl( storeImage = state.storeImage, storeName = state.storeName, storeAddress = state.storeAddress, storeImageIsEmpty = state.storeImageIsEmpty, - isBasicInfoValidate = state.isBasicInfoValidate, + isBasicInfoValid = state.isBasicInfoValid, onStoreImageChange = { viewModel.insertStoreImage(it) }, @@ -84,13 +85,13 @@ fun InsertBasicInfoScreenImpl( } @Composable -fun InsertBasicInfoScreen( +fun InsertBasicInfoScreenImpl( modifier: Modifier = Modifier, storeImage: Uri = Uri.EMPTY, storeImageIsEmpty: Boolean = true, - storeName: String = "맛있는 족발", - storeAddress: String = "층청남도 충절로 1600", - isBasicInfoValidate: Boolean = false, + storeName: String = "", + storeAddress: String = "", + isBasicInfoValid: Boolean = false, onStoreImageChange: (Uri) -> Unit = {}, onStoreNameChange: (String) -> Unit = {}, onStoreAddressChange: (String) -> Unit = {}, @@ -114,8 +115,7 @@ fun InsertBasicInfoScreen( Box( modifier = modifier .padding(top = 56.dp, start = 10.dp, bottom = 18.dp) - .width(40.dp) - .height(40.dp) + .size(40.dp) .clickable { onBackPressed() } @@ -124,8 +124,7 @@ fun InsertBasicInfoScreen( painter = painterResource(R.drawable.ic_arrow_left), contentDescription = "backArrow", modifier = modifier - .width(40.dp) - .height(40.dp) + .size(40.dp) ) } @@ -193,7 +192,7 @@ fun InsertBasicInfoScreen( Button( onClick = onNextButtonClicked, - colors = if(isBasicInfoValidate) ButtonDefaults.buttonColors(ColorPrimary) + colors = if(isBasicInfoValid) ButtonDefaults.buttonColors(ColorPrimary) else ButtonDefaults.buttonColors(ColorDisabledButton), shape = RectangleShape, modifier = modifier @@ -214,7 +213,7 @@ fun InsertBasicInfoScreen( } @Composable -private fun HandleSideEffects(viewModel: InsertBasicInfoScreenViewModel, navigateToInsertDetailInfoScreen: (StoreBasicInfo) -> Unit) { +private fun HandleSideEffects(viewModel: InsertBasicInfoScreenViewModel, navigateToInsertDetailInfoScreen: (InsertBasicInfoScreenState) -> Unit) { val context = LocalContext.current viewModel.collectSideEffect { sideEffect -> @@ -222,9 +221,9 @@ private fun HandleSideEffects(viewModel: InsertBasicInfoScreenViewModel, navigat is InsertBasicInfoScreenSideEffect.NavigateToInsertDetailInfoScreen -> navigateToInsertDetailInfoScreen(sideEffect.storeBasicInfo) is InsertBasicInfoScreenSideEffect.ShowMessage -> { val message = when (sideEffect.type) { - ErrorType.NullStoreName -> context.getString(R.string.insert_store_null_store_name) - ErrorType.NullStoreAddress -> context.getString(R.string.insert_store_null_store_address) - ErrorType.NullStoreImage -> context.getString(R.string.insert_store_null_store_image) + BasicInfoErrorType.NullStoreName -> context.getString(R.string.insert_store_null_store_name) + BasicInfoErrorType.NullStoreAddress -> context.getString(R.string.insert_store_null_store_address) + BasicInfoErrorType.NullStoreImage -> context.getString(R.string.insert_store_null_store_image) } ToastUtil.getInstance().makeShort(message) } @@ -234,8 +233,8 @@ private fun HandleSideEffects(viewModel: InsertBasicInfoScreenViewModel, navigat @Composable fun NameTextField( - textString: String = "가게명", - inputString: String = "0", + textString: String = "", + inputString: String = "", onStringChange: (String) -> Unit = {}, paddingTopValue: Dp = 10.dp ){ @@ -299,5 +298,5 @@ private val takePhotoFromAlbumIntent = @Preview @Composable fun PreviewInsertBasicInfo() { - InsertBasicInfoScreen() + InsertBasicInfoScreenImpl() } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenSideEffect.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenSideEffect.kt index fcf095114..6a19873ca 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenSideEffect.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenSideEffect.kt @@ -5,12 +5,11 @@ import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo sealed class InsertBasicInfoScreenSideEffect { - data class ShowMessage(val type: ErrorType): InsertBasicInfoScreenSideEffect() - - data class NavigateToInsertDetailInfoScreen(val storeBasicInfo: StoreBasicInfo) : InsertBasicInfoScreenSideEffect() + data class ShowMessage(val type: BasicInfoErrorType): InsertBasicInfoScreenSideEffect() + data class NavigateToInsertDetailInfoScreen(val storeBasicInfo: InsertBasicInfoScreenState) : InsertBasicInfoScreenSideEffect() } -enum class ErrorType { +enum class BasicInfoErrorType { NullStoreName, NullStoreAddress, NullStoreImage diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenState.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenState.kt index 9d2e1922d..f3eda5515 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenState.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenState.kt @@ -11,5 +11,5 @@ data class InsertBasicInfoScreenState( val storeImage: Uri = Uri.EMPTY, val storeImageIsEmpty: Boolean = true, val storeCategory: Int = 0, - val isBasicInfoValidate: Boolean = false + val isBasicInfoValid: Boolean = false ): Parcelable \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenViewModel.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenViewModel.kt index 31bdf5637..088271b79 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/insertmaininfo/InsertBasicInfoScreenViewModel.kt @@ -49,26 +49,22 @@ class InsertBasicInfoScreenViewModel @Inject constructor( fun onNextButtonClick(){ intent{ - if(state.isBasicInfoValidate){ - val storeBasicInfo: StoreBasicInfo = StoreBasicInfo( + if(state.isBasicInfoValid){ + val storeBasicInfo = InsertBasicInfoScreenState( storeName = state.storeName, storeAddress = state.storeAddress, - storeImage = state.storeImage.toString(), + storeImage = state.storeImage, storeCategory = state.storeCategory ) postSideEffect(InsertBasicInfoScreenSideEffect.NavigateToInsertDetailInfoScreen(storeBasicInfo)) + return@intent } - else{ - if(state.storeImageIsEmpty){ - postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(ErrorType.NullStoreImage)) - } - else if(state.storeName.isEmpty()){ - postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(ErrorType.NullStoreName)) - } - else if(state.storeAddress.isEmpty()){ - postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(ErrorType.NullStoreAddress)) + + when { + state.storeImageIsEmpty -> postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(BasicInfoErrorType.NullStoreImage)) + state.storeName.isEmpty() -> postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(BasicInfoErrorType.NullStoreName)) + state.storeAddress.isEmpty() -> postSideEffect(InsertBasicInfoScreenSideEffect.ShowMessage(BasicInfoErrorType.NullStoreAddress)) } - } } } @@ -92,7 +88,7 @@ class InsertBasicInfoScreenViewModel @Inject constructor( private fun isBasicInfoValidate() = intent { reduce { state.copy( - isBasicInfoValidate = state.storeAddress.isNotBlank() && state.storeName.isNotBlank() && state.storeImage != Uri.EMPTY + isBasicInfoValid = state.storeAddress.isNotBlank() && state.storeName.isNotBlank() && state.storeImage != Uri.EMPTY ) } } diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRouteNavigator.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRouteNavigator.kt index 5e0dec35f..60a8962dc 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRouteNavigator.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/navigator/InsertStoreRouteNavigator.kt @@ -1,21 +1,24 @@ package `in`.koreatech.business.feature.insertstore.navigator import android.net.Uri +import android.os.Bundle import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.navigation.NavHostController +import androidx.navigation.NavOptions import androidx.navigation.NavType +import androidx.navigation.Navigator import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import com.google.gson.Gson -import `in`.koreatech.business.feature.insertstore.insertdetailinfo.InsertDetailInfoScreenImpl -import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenImpl +import `in`.koreatech.business.feature.insertstore.insertdetailinfo.InsertDetailInfoScreen +import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreen import `in`.koreatech.business.feature.insertstore.insertmaininfo.InsertBasicInfoScreenState -import `in`.koreatech.business.feature.insertstore.selectcategory.SelectCategoryScreenImpl +import `in`.koreatech.business.feature.insertstore.selectcategory.SelectCategoryScreen import `in`.koreatech.business.feature.insertstore.startinsetstore.StartInsertScreen import `in`.koreatech.koin.domain.model.owner.insertstore.StoreBasicInfo @@ -46,7 +49,7 @@ fun InsertStoreNavigator( composable( route = InsertStoreRoute.SELECT_CATEGORY.name, ) { - SelectCategoryScreenImpl( + SelectCategoryScreen( navigateToInsertBasicInfoScreen = { navigateToMainInfo(navController, it) }, @@ -65,7 +68,7 @@ fun InsertStoreNavigator( } ) ){ - InsertBasicInfoScreenImpl( + InsertBasicInfoScreen( onBackPressed = { navController.navigateUp() }, @@ -76,16 +79,9 @@ fun InsertStoreNavigator( } composable( - route = "${InsertStoreRoute.DETAIL_INFO.name}/{storeBasicInfo}", - arguments = listOf( - navArgument("storeBasicInfo") - { - type = NavType.StringType - defaultValue = "" - } - ) + route = InsertStoreRoute.DETAIL_INFO.name ){ - InsertDetailInfoScreenImpl( + InsertDetailInfoScreen( onBackPress = { navController.navigateUp() } @@ -103,8 +99,22 @@ private fun navigateToMainInfo( private fun navigateToDetailInfo( navController: NavController, - storeBasicInfo: StoreBasicInfo + storeBasicInfo: InsertBasicInfoScreenState +) { + val bundle = Bundle() + bundle.putParcelable("storeBasicInfo", storeBasicInfo) + navController.navigate(InsertStoreRoute.DETAIL_INFO.name, args = bundle) +} + + +fun NavController.navigate( + route: String, + args: Bundle, + navOptions: NavOptions? = null, + navigatorExtras: Navigator.Extras? = null ) { - val storeBasicInfoJson = Uri.encode(Gson().toJson(storeBasicInfo)) - navController.navigate("${InsertStoreRoute.DETAIL_INFO}/${storeBasicInfoJson}") + val nodeId = graph.findNode(route = route)?.id + if (nodeId != null) { + navigate(nodeId, args, navOptions, navigatorExtras) + } } \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt index 309357832..f25c25152 100644 --- a/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/insertstore/selectcategory/SelectCategoryScreen.kt @@ -51,14 +51,14 @@ import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @Composable -fun SelectCategoryScreenImpl( +fun SelectCategoryScreen( modifier: Modifier = Modifier, navigateToInsertBasicInfoScreen: (Int) -> Unit, onBackPressed: () -> Unit, viewModel: SelectCategoryScreenViewModel = hiltViewModel() ) { val state = viewModel.collectAsState().value - SelectCategoryScreen( + SelectCategoryScreenImpl( modifier = modifier, categories = state.categories, categoryId = state.categoryId, @@ -78,7 +78,7 @@ fun SelectCategoryScreenImpl( @Composable -fun SelectCategoryScreen( +fun SelectCategoryScreenImpl( modifier: Modifier = Modifier, categories: List = emptyList(), categoryId: Int = -1, @@ -282,7 +282,7 @@ fun PreviewCategoryItem() { @Preview @Composable fun PreviewSelectCategoryScreen(){ - SelectCategoryScreen( + SelectCategoryScreenImpl( categories = sampleCategories ) } diff --git a/domain/build.gradle b/domain/build.gradle index 89646d9ce..cd57be316 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -12,4 +12,5 @@ dependencies { implementation libs.kotlinx.coroutines.core implementation libs.javax.inject implementation libs.napier + } From 03322bfa25a56c653eff99333a01be398a7b986e Mon Sep 17 00:00:00 2001 From: nazero Date: Thu, 23 May 2024 17:14:59 +0900 Subject: [PATCH 106/131] Delete unnecessary code --- .../business/feature/signup/accountsetup/AccountSetupScreen.kt | 2 +- business/src/main/java/in/koreatech/business/ui/theme/Theme.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index ec7f494c3..2dab29d7a 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -156,7 +156,7 @@ fun AccountSetupScreen( disabledContentColor = Gray1, ), onClick = { - /* 아이디 중복 확인 */ + TODO("아이디 중복 확인") }) { Text( text = stringResource(id = R.string.check_duplicate), diff --git a/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt b/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt index 6aadc993e..d10516845 100644 --- a/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt +++ b/business/src/main/java/in/koreatech/business/ui/theme/Theme.kt @@ -49,7 +49,6 @@ fun KOIN_ANDROIDTheme( MaterialTheme( colors = colorScheme, - // typography = Typography, shapes = Shapes, content = content ) From 749748842854f179a2f0bf2d90babaa91b47830f Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Sun, 2 Jun 2024 23:45:02 +0900 Subject: [PATCH 107/131] Add PowerSpinner library - Add PowerSpinner for Signup page --- gradle/libs.versions.toml | 4 ++++ koin/build.gradle | 1 + 2 files changed, 5 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d004726cc..873f7ebb4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,6 +51,7 @@ hiltComposeVersion="1.0.0" markermanVersion="2.3.0" coilVersion = "1.2.0" napier = "2.6.1" +powerSpinner = "1.2.7" [libraries] androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtxVersion" } @@ -138,6 +139,9 @@ markerman-roundedImageView={module= "com.makeramen:roundedimageview", version.re coil ={module = "io.coil-kt:coil", version.ref ="coilVersion"} coil-svg = {module = "io.coil-kt:coil-svg", version.ref ="coilVersion"} +powerSpinner = {module = "com.github.skydoves:powerspinner", version.ref = "powerSpinner"} + + [plugins] android-application = { id = "com.android.application", version.ref = "androidGradleVersion" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVersion" } diff --git a/koin/build.gradle b/koin/build.gradle index b60119c29..bd5f7cee0 100644 --- a/koin/build.gradle +++ b/koin/build.gradle @@ -130,4 +130,5 @@ dependencies { implementation(libs.coil) implementation(libs.napier) + implementation(libs.powerSpinner) } From 6ce821a122a20eea290f4ae1455d186fb15533fc Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Mon, 3 Jun 2024 03:56:39 +0900 Subject: [PATCH 108/131] Change signup major from edittext to spinner --- .../activity_sign_up_with_detail_info.xml | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml index d7a63fd0c..5c7ec823c 100644 --- a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml +++ b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml @@ -1,7 +1,7 @@ - + + app:layout_constraintGuide_begin="199dp" /> + app:layout_constraintTop_toBottomOf="@+id/guideline_horizontal_0.188" /> + app:layout_constraintTop_toBottomOf="@id/signup_title" /> + app:layout_constraintTop_toBottomOf="@id/signup_user_gender_textview" /> + app:layout_constraintTop_toBottomOf="@id/signup_user_phone_number_textview" /> - + app:layout_constraintTop_toTopOf="@id/signup_user_major_textview" + app:spinner_arrow_drawable="@drawable/ic_arrow_down_3" + app:spinner_arrow_gravity="end" + app:spinner_arrow_padding="5dp" + app:spinner_divider_color="@color/gray" + app:spinner_divider_show="true" + app:spinner_divider_size="1dp" + app:spinner_item_height="40dp" + app:spinner_popup_background="@color/cardview_light_background" + app:spinner_popup_height="300dp" /> + app:layout_constraintEnd_toEndOf="@id/spinner_signup_user_major" + app:layout_constraintStart_toStartOf="@id/spinner_signup_user_major" + app:layout_constraintTop_toBottomOf="@id/spinner_signup_user_major" + tools:text="올바른 학번을 입력해 주세요" /> Date: Mon, 3 Jun 2024 04:26:31 +0900 Subject: [PATCH 109/131] Change background of major spinner on Signup --- .../main/res/drawable/background_round_square.xml | 12 ++++++++++++ .../res/layout/activity_sign_up_with_detail_info.xml | 5 +++-- koin/src/main/res/values/colors.xml | 6 ++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 koin/src/main/res/drawable/background_round_square.xml diff --git a/koin/src/main/res/drawable/background_round_square.xml b/koin/src/main/res/drawable/background_round_square.xml new file mode 100644 index 000000000..790fc180f --- /dev/null +++ b/koin/src/main/res/drawable/background_round_square.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml index 5c7ec823c..bd42a4fee 100644 --- a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml +++ b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml @@ -396,12 +396,13 @@ android:layout_height="40dp" android:layout_marginStart="61dp" android:layout_marginEnd="16dp" + android:background="@drawable/background_round_square" android:gravity="center_vertical" android:hint="@string/major_title" android:includeFontPadding="false" android:paddingHorizontal="8dp" android:textColor="@color/black" - android:textColorHint="#8E8E8E" + android:textColorHint="@color/neutral_500" android:textSize="15sp" app:fontName="Custom6" app:layout_constraintBottom_toBottomOf="@+id/signup_user_major_textview" @@ -412,7 +413,7 @@ app:spinner_arrow_drawable="@drawable/ic_arrow_down_3" app:spinner_arrow_gravity="end" app:spinner_arrow_padding="5dp" - app:spinner_divider_color="@color/gray" + app:spinner_divider_color="@color/neutral_500" app:spinner_divider_show="true" app:spinner_divider_size="1dp" app:spinner_item_height="40dp" diff --git a/koin/src/main/res/values/colors.xml b/koin/src/main/res/values/colors.xml index f343c131f..2d8eaefe1 100644 --- a/koin/src/main/res/values/colors.xml +++ b/koin/src/main/res/values/colors.xml @@ -10,4 +10,10 @@ #FAFAFA #E1E1E1 #EEEEEE + + + #4590BB + + + #8E8E8E \ No newline at end of file From 8065e8cd60b700634529483e746ba591a250d244 Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Mon, 3 Jun 2024 04:41:31 +0900 Subject: [PATCH 110/131] Add loading department names logic --- .../koin/data/repository/DeptRepositoryImpl.kt | 12 +++++++++++- .../data/source/local/DeptLocalDataSource.kt | 13 +++++++++++++ data/src/main/res/values/strings.xml | 18 ++++++++++++++++-- .../koreatech/koin/domain/model/user/Dept.kt | 6 +++--- .../koin/domain/repository/DeptRepository.kt | 1 + .../domain/usecase/dept/GetDeptNamesUseCase.kt | 12 ++++++++++++ 6 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 domain/src/main/java/in/koreatech/koin/domain/usecase/dept/GetDeptNamesUseCase.kt diff --git a/data/src/main/java/in/koreatech/koin/data/repository/DeptRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/DeptRepositoryImpl.kt index 0f404fa6e..67047d818 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/DeptRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/DeptRepositoryImpl.kt @@ -9,7 +9,7 @@ import javax.inject.Inject class DeptRepositoryImpl @Inject constructor( private val deptRemoteDataSource: DeptRemoteDataSource, private val deptLocalDataSource: DeptLocalDataSource -) : DeptRepository{ +) : DeptRepository { override suspend fun getDeptNameFromDeptCode(deptCode: String): String { return try { val deptResponse = deptRemoteDataSource.getDeptFromDeptCode(deptCode) @@ -28,4 +28,14 @@ class DeptRepositoryImpl @Inject constructor( ) } } + + override suspend fun getDeptNames(): List { + return try { + deptRemoteDataSource.getAllDepts().map { + it.name + } + } catch (t: Throwable) { + deptLocalDataSource.getDeptNames() + } + } } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/source/local/DeptLocalDataSource.kt b/data/src/main/java/in/koreatech/koin/data/source/local/DeptLocalDataSource.kt index f19a01a64..b384d797e 100644 --- a/data/src/main/java/in/koreatech/koin/data/source/local/DeptLocalDataSource.kt +++ b/data/src/main/java/in/koreatech/koin/data/source/local/DeptLocalDataSource.kt @@ -22,4 +22,17 @@ class DeptLocalDataSource @Inject constructor( else -> throw IllegalArgumentException() } ) + + fun getDeptNames() = with(context) { + listOf( + getString(R.string.major_architectural_engineering), + getString(R.string.major_employment_service_policy), + getString(R.string.major_mechanical_engineering), + getString(R.string.major_architectural_engineering), + getString(R.string.major_architectural_engineering), + getString(R.string.major_architectural_engineering), + getString(R.string.major_architectural_engineering), + getString(R.string.major_architectural_engineering), + ) + } } \ No newline at end of file diff --git a/data/src/main/res/values/strings.xml b/data/src/main/res/values/strings.xml index d59d863ed..7869208f7 100644 --- a/data/src/main/res/values/strings.xml +++ b/data/src/main/res/values/strings.xml @@ -4,6 +4,8 @@ 서버 오류가 발생했습니다. 네트워크 연결을 확인해 주세요. 알 수 없는 오류가 발생했습니다. + 아이디 또는 비밀번호가 올바르지 않습니다. + 서버 오류가 발생했습니다. 가입되지 않은 아이디입니다. @@ -43,6 +45,7 @@ 셔틀버스 학교셔틀 %1$d번 버스 + 기계공학부 메카트로닉스공학부 @@ -56,8 +59,19 @@ HRD 융합 교양학부 - 아이디 또는 비밀번호가 올바르지 않습니다. - 서버 오류가 발생했습니다. + + + 건축공학부 + 고용서비스정책학과 + 기계공학부 + 디자인공학부 + 메카트로닉스공학부 + 산업경영학부 + 전기전자통신공학부 + 컴퓨터공학부 + 화학생명공학부 + 에너지신소재화학공학부 + 아침 점심 diff --git a/domain/src/main/java/in/koreatech/koin/domain/model/user/Dept.kt b/domain/src/main/java/in/koreatech/koin/domain/model/user/Dept.kt index 585ec28cc..acfd170a3 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/model/user/Dept.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/model/user/Dept.kt @@ -1,7 +1,7 @@ package `in`.koreatech.koin.domain.model.user data class Dept( - private val name: String, - private val curriculumUrl: String, - private val codes: List + val name: String, + val curriculumUrl: String, + val codes: List ) \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/repository/DeptRepository.kt b/domain/src/main/java/in/koreatech/koin/domain/repository/DeptRepository.kt index ea7107d33..9306ac919 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/repository/DeptRepository.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/repository/DeptRepository.kt @@ -5,4 +5,5 @@ import `in`.koreatech.koin.domain.model.user.Dept interface DeptRepository { suspend fun getDeptNameFromDeptCode(deptCode: String): String suspend fun getDepts(): List + suspend fun getDeptNames(): List } \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/dept/GetDeptNamesUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/dept/GetDeptNamesUseCase.kt new file mode 100644 index 000000000..2414468ad --- /dev/null +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/dept/GetDeptNamesUseCase.kt @@ -0,0 +1,12 @@ +package `in`.koreatech.koin.domain.usecase.dept + +import `in`.koreatech.koin.domain.repository.DeptRepository +import javax.inject.Inject + +class GetDeptNamesUseCase @Inject constructor( + private val deptRepository: DeptRepository, +) { + suspend operator fun invoke(): List { + return deptRepository.getDeptNames() + } +} \ No newline at end of file From 367d658e47431557a227c837c2dc07f383e5e9eb Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Mon, 3 Jun 2024 04:43:29 +0900 Subject: [PATCH 111/131] Change string resource name of major --- .../layout/timetable_select_major_dialog.xml | 40 +++++++++---------- koin/src/main/res/values/array.xml | 14 +++---- koin/src/main/res/values/strings.xml | 25 +++++++----- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/koin/src/main/res/layout/timetable_select_major_dialog.xml b/koin/src/main/res/layout/timetable_select_major_dialog.xml index 5e0709c0d..6a2544ef3 100644 --- a/koin/src/main/res/layout/timetable_select_major_dialog.xml +++ b/koin/src/main/res/layout/timetable_select_major_dialog.xml @@ -37,8 +37,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/computer_science_and_engineering" - android:textOn="@string/computer_science_and_engineering" + android:textOff="@string/major_computer_science_and_engineering" + android:textOn="@string/major_computer_science_and_engineering" android:textSize="12sp" app:fontName="Custom6" app:layout_constraintBottom_toTopOf="parent" @@ -57,8 +57,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/electrical_electronics_and_communication_engineering" - android:textOn="@string/electrical_electronics_and_communication_engineering" + android:textOff="@string/major_electrical_electronics_and_communication_engineering" + android:textOn="@string/major_electrical_electronics_and_communication_engineering" android:textSize="12sp" app:fontName="Custom6" /> @@ -75,8 +75,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/industrial_management" - android:textOn="@string/industrial_management" + android:textOff="@string/major_industrial_management" + android:textOn="@string/major_industrial_management" android:textSize="12sp" app:fontName="Custom6" /> @@ -93,8 +93,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/industrial_design_and_architectural_engineering" - android:textOn="@string/industrial_design_and_architectural_engineering" + android:textOff="@string/major_industrial_design_and_architectural_engineering" + android:textOn="@string/major_industrial_design_and_architectural_engineering" android:textSize="12sp" app:fontName="Custom6" /> @@ -112,8 +112,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/hrd" - android:textOn="@string/hrd" + android:textOff="@string/major_hrd" + android:textOn="@string/major_hrd" android:textSize="12sp" app:fontName="Custom6" /> @@ -130,8 +130,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/mechanical_engineering" - android:textOn="@string/mechanical_engineering" + android:textOff="@string/major_mechanical_engineering" + android:textOn="@string/major_mechanical_engineering" android:textSize="12sp" app:fontName="Custom6" /> @@ -148,8 +148,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/energy_meterials_chemical_engineering" - android:textOn="@string/energy_meterials_chemical_engineering" + android:textOff="@string/major_energy_meterials_chemical_engineering" + android:textOn="@string/major_energy_meterials_chemical_engineering" android:textSize="12sp" app:fontName="Custom6" app:layout_constraintBottom_toTopOf="parent" @@ -168,8 +168,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/mechatronics_engineering" - android:textOn="@string/mechatronics_engineering" + android:textOff="@string/major_mechatronics_engineering" + android:textOn="@string/major_mechatronics_engineering" android:textSize="12sp" app:fontName="Custom6" app:layout_constraintBottom_toTopOf="parent" @@ -188,8 +188,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/liberal_arts" - android:textOn="@string/liberal_arts" + android:textOff="@string/major_liberal_arts" + android:textOn="@string/major_liberal_arts" android:textSize="12sp" app:fontName="Custom6" app:layout_constraintBottom_toTopOf="parent" @@ -208,8 +208,8 @@ android:includeFontPadding="false" android:lineSpacingExtra="12sp" android:textColor="@color/gray7" - android:textOff="@string/department_of_future_technology" - android:textOn="@string/department_of_future_technology" + android:textOff="@string/major_department_of_future_technology" + android:textOn="@string/major_department_of_future_technology" android:textSize="12sp" app:fontName="Custom6" /> diff --git a/koin/src/main/res/values/array.xml b/koin/src/main/res/values/array.xml index 4996840a8..c371a1239 100644 --- a/koin/src/main/res/values/array.xml +++ b/koin/src/main/res/values/array.xml @@ -10,13 +10,13 @@ - @string/mechanical_engineering - @string/mechatronics_engineering - @string/electrical_electronics_and_communication_engineering - @string/computer_science_and_engineering - @string/industrial_design_and_architectural_engineering - @string/energy_meterials_chemical_engineering - @string/industrial_management + @string/major_mechanical_engineering + @string/major_mechatronics_engineering + @string/major_electrical_electronics_and_communication_engineering + @string/major_computer_science_and_engineering + @string/major_industrial_design_and_architectural_engineering + @string/major_energy_meterials_chemical_engineering + @string/major_industrial_management diff --git a/koin/src/main/res/values/strings.xml b/koin/src/main/res/values/strings.xml index 99c74c5cb..1aab9c7c8 100644 --- a/koin/src/main/res/values/strings.xml +++ b/koin/src/main/res/values/strings.xml @@ -151,16 +151,21 @@ 삭제에 실패했습니다. - 기계공학부 - 메카트로닉스공학부 - 전기전자통신공학부 - 컴퓨터공학부 - 디자인건축공학부 - 에너지신소재화학공학부 - 산업경영학부 - HRD - 융합 - 교양학부 + 학부 + 건축공학부 + 고용서비스정책학과 + 기계공학부 + 디자인건축공학부 + 디자인공학부 + 메카트로닉스공학부 + 산업경영학부 + 전기전자통신공학부 + 컴퓨터공학부 + 화학생명공학부 + 에너지신소재화학공학부 + HRD + 융합 + 교양학부 /Pictures/koin/ From 9a14c0d74ce2b7ec744903b6f5f7d0c40daf4983 Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Mon, 3 Jun 2024 05:05:54 +0900 Subject: [PATCH 112/131] Add spinner logic on Signup - Add Spinner Data Logic - Remove autoInputMajor temporary --- .../ui/signup/SignUpWithDetailInfoActivity.kt | 64 +++++++++++-------- .../ui/signup/viewmodel/SignupViewModel.kt | 45 +++++++------ .../activity_sign_up_with_detail_info.xml | 3 +- 3 files changed, 63 insertions(+), 49 deletions(-) diff --git a/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt b/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt index 87bb5509a..c1dc78073 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt @@ -2,12 +2,14 @@ package `in`.koreatech.koin.ui.signup import android.content.Intent import android.os.Bundle +import android.util.Log import android.view.MotionEvent import androidx.activity.viewModels import androidx.core.widget.addTextChangedListener import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import dagger.hilt.android.AndroidEntryPoint import `in`.koreatech.koin.R import `in`.koreatech.koin.core.activity.ActivityBase @@ -23,7 +25,6 @@ import `in`.koreatech.koin.ui.signup.SignupActivity.Companion.SIGN_UP_PASSWORD import `in`.koreatech.koin.ui.signup.viewmodel.SignupViewModel import `in`.koreatech.koin.util.SnackbarUtil import `in`.koreatech.koin.util.ext.hideKeyboard -import `in`.koreatech.koin.util.ext.observeLiveData import `in`.koreatech.koin.util.ext.textString import `in`.koreatech.koin.util.ext.withLoading import kotlinx.coroutines.launch @@ -50,31 +51,35 @@ class SignupWithDetailInfoActivity : ActivityBase() { getUserDataFromSignupActivity() checkNickname() continueSignup() - addTextChangedListenerWithNicknameAndDept() - autoInputMajor() - } + initSpinner() +// addTextChangedListenerWithNicknameAndDept() +// autoInputMajor() + - private fun autoInputMajor() { - binding.signupUserEdittextMajor.apply { - isEnabled = false - hint = context.getString(R.string.user_info_id_hint) - } } +// private fun autoInputMajor() { +// binding.signupUserEdittextMajor.apply { +// isEnabled = false +// hint = context.getString(R.string.user_info_id_hint) +// } +// } + private fun addTextChangedListenerWithNicknameAndDept() { binding.signupUserEdittextNickName.addTextChangedListener { if (signupViewModel.isCheckedNickname) signupViewModel.isCheckedNickname = false } - binding.signupUserEdittextStudentId.addTextChangedListener { - if (signupViewModel.isPerformDept) signupViewModel.isPerformDept = false - signupViewModel.getDept(it.toString()) - } +// binding.signupUserEdittextStudentId.addTextChangedListener { +// if (signupViewModel.isPerformDept) signupViewModel.isPerformDept = false +// signupViewModel.getDept(it.toString()) +// } } private fun continueSignup() { with(binding) { signupSendVerificationButton.setOnClickListener { + Log.d("dhk", "Submited major: ${spinnerSignupUserMajor.text}") signupViewModel.continueDetailSignup( portalAccount = signupViewModel.portalEmail, gender = when { @@ -87,7 +92,7 @@ class SignupWithDetailInfoActivity : ActivityBase() { signupUserRadiobuttonStudent.isChecked -> Graduated.Student else -> null }, - major = signupUserEdittextMajor.text.toString(), + major = spinnerSignupUserMajor.text.toString(), name = signupUserEdittextName.text.toString().trim(), nickName = signupUserEdittextNickName.text.toString().trim(), password = signupViewModel.password.trim(), @@ -104,6 +109,16 @@ class SignupWithDetailInfoActivity : ActivityBase() { } } + private fun initSpinner() = with(binding.spinnerSignupUserMajor) { + lifecycleOwner = this@SignupWithDetailInfoActivity + + + setOnSpinnerItemSelectedListener { oldIndex, oldItem, newIndex, newItem -> + Log.d("dhk", "major select from $oldItem to $newItem") + } + } + + private fun checkNickname() { binding.signupUserButtonNicknameCheck.setOnClickListener { if (binding.signupUserEdittextNickName.text.toString() != "") { @@ -130,19 +145,19 @@ class SignupWithDetailInfoActivity : ActivityBase() { when (state) { SignupContinuationState.InitName -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.signup_init_name)) - SignupContinuationState.InitPhoneNumber -> SnackbarUtil.makeShortSnackbar(binding.root,getString(R.string.signup_init_phone_number)) + SignupContinuationState.InitPhoneNumber -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.signup_init_phone_number)) SignupContinuationState.InitStudentId -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.signup_init_student_id)) - SignupContinuationState.CheckNickName -> SnackbarUtil.makeShortSnackbar(binding.root,getString(R.string.signup_check_nickname)) + SignupContinuationState.CheckNickName -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.signup_check_nickname)) - SignupContinuationState.CheckGender -> SnackbarUtil.makeShortSnackbar(binding.root,getString(R.string.signup_check_gender)) + SignupContinuationState.CheckGender -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.signup_check_gender)) SignupContinuationState.CheckGraduate -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.signup_check_graduate)) SignupContinuationState.CheckDept -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.user_info_no_major)) - SignupContinuationState.NicknameDuplicated -> SnackbarUtil.makeShortSnackbar(binding.root,getString(R.string.error_nickname_duplicated)) + SignupContinuationState.NicknameDuplicated -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.error_nickname_duplicated)) SignupContinuationState.AvailableNickname -> SnackbarUtil.makeShortSnackbar(binding.root, getString(R.string.signup_nickname_available)) @@ -164,13 +179,12 @@ class SignupWithDetailInfoActivity : ActivityBase() { } } - observeLiveData(dept) { - binding.signupUserEdittextMajor.setText(it) - binding.signupUserEdittextMajorError.text = "" - } - - observeLiveData(getDeptErrorMessage) { - binding.signupUserEdittextMajorError.text = it + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + signupViewModel.depts.collect { deptNames -> + binding.spinnerSignupUserMajor.setItems(deptNames) + } + } } } diff --git a/koin/src/main/java/in/koreatech/koin/ui/signup/viewmodel/SignupViewModel.kt b/koin/src/main/java/in/koreatech/koin/ui/signup/viewmodel/SignupViewModel.kt index 3486ebfaa..37e3cdd0c 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/signup/viewmodel/SignupViewModel.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/signup/viewmodel/SignupViewModel.kt @@ -1,22 +1,23 @@ package `in`.koreatech.koin.ui.signup.viewmodel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.core.viewmodel.BaseViewModel -import `in`.koreatech.koin.core.viewmodel.SingleLiveEvent import `in`.koreatech.koin.domain.model.user.Gender import `in`.koreatech.koin.domain.model.user.Graduated import `in`.koreatech.koin.domain.state.signup.SignupContinuationState -import `in`.koreatech.koin.domain.usecase.dept.GetDeptNameFromStudentIdUseCase +import `in`.koreatech.koin.domain.usecase.dept.GetDeptNamesUseCase import `in`.koreatech.koin.domain.usecase.signup.CheckEmailValidationUseCase import `in`.koreatech.koin.domain.usecase.signup.SignupCheckingUseCase import `in`.koreatech.koin.domain.usecase.signup.SignupRequestEmailVerificationUseCase import `in`.koreatech.koin.domain.usecase.user.CheckNicknameValidationUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @@ -26,21 +27,25 @@ class SignupViewModel @Inject constructor( private val signupRequestEmailVerificationUseCase: SignupRequestEmailVerificationUseCase, private val checkNicknameValidationUseCase: CheckNicknameValidationUseCase, private val checkEmailValidationUseCase: CheckEmailValidationUseCase, - private val deptNameFromStudentIdUseCase: GetDeptNameFromStudentIdUseCase, + private val getDeptNamesUseCase: GetDeptNamesUseCase, ) : BaseViewModel() { var portalEmail: String = "" var password: String = "" var isCheckedNickname: Boolean = false - var isPerformDept: Boolean = false +// var isPerformDept: Boolean = false private val _signupContinuationState = MutableSharedFlow() val signupContinuationState: SharedFlow = _signupContinuationState.asSharedFlow() - private val _dept = MutableLiveData() - val dept: LiveData get() = _dept +// 학부 선택을 Spinner 로 구현하도록 변경되며 주석처리 +// private val _dept = MutableLiveData() +// val dept: LiveData get() = _dept - private val _getDeptErrorMessage = MutableLiveData() - val getDeptErrorMessage: LiveData get() = _getDeptErrorMessage + val depts: StateFlow> = flow { emit(getDeptNamesUseCase()) } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), listOf()) + +// private val _getDeptErrorMessage = MutableLiveData() +// val getDeptErrorMessage: LiveData get() = _getDeptErrorMessage fun setAccount(portalEmail: String, password: String) { this.portalEmail = portalEmail @@ -54,9 +59,9 @@ class SignupViewModel @Inject constructor( isAgreedPrivacyTerms: Boolean, isAgreedKoinTerms: Boolean, ) { - viewModelScope.launch { - signupCheckingUseCase(portalAccount, password, passwordConfirm, isAgreedPrivacyTerms, isAgreedKoinTerms + signupCheckingUseCase( + portalAccount, password, passwordConfirm, isAgreedPrivacyTerms, isAgreedKoinTerms ).let { _signupContinuationState.emit(it) } @@ -78,16 +83,7 @@ class SignupViewModel @Inject constructor( if (isLoading.value == false) { viewModelScope.launch { signupRequestEmailVerificationUseCase( - portalAccount, - gender, - isGraduated, - major, - name, - nickName, - password, - phoneNumber, - studentNumber, - isCheckNickname + portalAccount, gender, isGraduated, major, name, nickName, password, phoneNumber, studentNumber, isCheckNickname ).onSuccess { _signupContinuationState.emit(it) }.onFailure { @@ -128,7 +124,9 @@ class SignupViewModel @Inject constructor( } } - fun getDept(studentId: String) = viewModelScope.launchIgnoreCancellation { + /* 학부 선택을 Spinner 로 구현하도록 변경되며 주석처리 + * TODO:: Spinner 자동 입력되도록 변경 + fun getDept(studentId: String) = viewModelScope.launchIgnoreCancellation { deptNameFromStudentIdUseCase(studentId).let { (deptName, error) -> deptName?.let { _dept.value = it @@ -137,4 +135,5 @@ class SignupViewModel @Inject constructor( error?.let { _getDeptErrorMessage.value = error.message } } } + */ } \ No newline at end of file diff --git a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml index bd42a4fee..f664e5600 100644 --- a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml +++ b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml @@ -420,6 +420,7 @@ app:spinner_popup_background="@color/cardview_light_background" app:spinner_popup_height="300dp" /> + Date: Wed, 5 Jun 2024 11:08:23 +0900 Subject: [PATCH 113/131] Modify exception logic --- .../signup/businessauth/BusinessAuthViewModel.kt | 4 +--- .../koin/data/repository/OwnerSignupRepositoryImpl.kt | 10 +++++----- .../repository/OwnerVerificationCodeRepositoryImpl.kt | 10 +++++----- .../koin/data/repository/PreSignedUrlRepositoryImpl.kt | 6 +++--- .../usecase/business/BusinessSignupCheckUseCase.kt | 6 +++--- .../usecase/business/SendSignupSmsCodeUseCase.kt | 3 +-- .../signup/SignupRequestEmailVerificationUseCase.kt | 2 +- 7 files changed, 19 insertions(+), 22 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt index 63b02d6c3..4b5638605 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthViewModel.kt @@ -12,7 +12,6 @@ import `in`.koreatech.koin.domain.usecase.business.UploadFileUseCase import `in`.koreatech.koin.domain.usecase.owner.AttachStoreFileUseCase import `in`.koreatech.koin.domain.usecase.owner.OwnerRegisterUseCase import `in`.koreatech.koin.domain.util.ext.formatBusinessNumber -import `in`.koreatech.koin.domain.util.ext.formatPhoneNumber import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost @@ -124,7 +123,6 @@ class BusinessAuthViewModel @Inject constructor( } } } - } fun uploadImage( @@ -163,7 +161,7 @@ class BusinessAuthViewModel @Inject constructor( email, name, password, - phoneNumber.formatPhoneNumber(), + phoneNumber, shopId, shopName ).onSuccess { diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt index 183bb0eed..be24c201a 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt @@ -8,11 +8,12 @@ import `in`.koreatech.koin.data.source.remote.OwnerRemoteDataSource import `in`.koreatech.koin.domain.repository.OwnerSignupRepository import retrofit2.HttpException import javax.inject.Inject +import kotlin.coroutines.cancellation.CancellationException class OwnerSignupRepositoryImpl @Inject constructor( private val ownerRemoteDataSource: OwnerRemoteDataSource, private val signupTermsLocalDataSource: SignupTermsLocalDataSource -): OwnerSignupRepository { +) : OwnerSignupRepository { override suspend fun getPrivacyTermText(): String { return signupTermsLocalDataSource.getPrivacyTermText() } @@ -25,7 +26,7 @@ class OwnerSignupRepositoryImpl @Inject constructor( email: String ): Result { return try { - ownerRemoteDataSource.postVerificationEmail ( + ownerRemoteDataSource.postVerificationEmail( OwnerVerificationEmailRequest( address = email ) @@ -49,11 +50,10 @@ class OwnerSignupRepositoryImpl @Inject constructor( ) ) Result.success(Unit) - } catch (e: HttpException) { - e.httpExceptionMapper() + } catch (e: CancellationException) { + throw e } catch (t: Throwable) { Result.failure(t) } } - } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt index 0b3cddb23..2cb1e648b 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt @@ -1,19 +1,19 @@ package `in`.koreatech.koin.data.repository -import `in`.koreatech.koin.data.mapper.httpExceptionMapper +import android.util.Log import `in`.koreatech.koin.data.mapper.toAuthToken import `in`.koreatech.koin.data.request.owner.OwnerVerificationCodeRequest import `in`.koreatech.koin.data.request.owner.VerificationCodeSmsRequest import `in`.koreatech.koin.data.source.remote.OwnerRemoteDataSource -import `in`.koreatech.koin.domain.error.owner.OwnerError import `in`.koreatech.koin.domain.model.owner.OwnerAuthToken import `in`.koreatech.koin.domain.repository.OwnerVerificationCodeRepository import retrofit2.HttpException import javax.inject.Inject +import kotlin.coroutines.cancellation.CancellationException class OwnerVerificationCodeRepositoryImpl @Inject constructor( private val ownerRemoteDataSource: OwnerRemoteDataSource -):OwnerVerificationCodeRepository { +) : OwnerVerificationCodeRepository { override suspend fun compareVerificationCode( address: String, verificationCode: String @@ -46,8 +46,8 @@ class OwnerVerificationCodeRepositoryImpl @Inject constructor( ) ) Result.success(tempToken.toAuthToken()) - } catch (e: HttpException) { - Result.failure(e) + } catch (e: CancellationException) { + throw e } catch (t: Throwable) { Result.failure(t) } diff --git a/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt index 78ac5173a..e94cca78d 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt @@ -9,6 +9,7 @@ import okhttp3.RequestBody.Companion.toRequestBody import retrofit2.HttpException import java.io.InputStream import javax.inject.Inject +import kotlin.coroutines.cancellation.CancellationException class PreSignedUrlRepositoryImpl @Inject constructor( private val preSignedUrlRemoteDataSource: PreSignedUrlRemoteDataSource @@ -36,10 +37,9 @@ class PreSignedUrlRepositoryImpl @Inject constructor( val file = bitmap.toRequestBody(mediaType.toMediaTypeOrNull()) preSignedUrlRemoteDataSource.putPreSignedUrl(url, file) Result.success(Unit) - } catch (e: HttpException) { - Result.failure(e) + } catch (e: CancellationException) { + throw e } catch (t: Throwable) { - t.printStackTrace() Result.failure(t) } } diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt index 40bdb855c..7faa466b5 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/BusinessSignupCheckUseCase.kt @@ -3,8 +3,8 @@ package `in`.koreatech.koin.domain.usecase.business import `in`.koreatech.koin.domain.repository.OwnerVerificationCodeRepository import `in`.koreatech.koin.domain.repository.TokenRepository import `in`.koreatech.koin.domain.state.signup.SignupContinuationState -import `in`.koreatech.koin.domain.util.ext.formatPhoneNumber import `in`.koreatech.koin.domain.util.ext.isNotValidPassword +import kotlinx.coroutines.CancellationException import javax.inject.Inject class BusinessSignupCheckUseCase @Inject constructor( @@ -23,13 +23,13 @@ class BusinessSignupCheckUseCase @Inject constructor( (phoneNumber.length != 11) -> Result.success(SignupContinuationState.PhoneNumberIsNotValidate) else -> { val authToken = ownerVerificationCodeRepository.verifySmsCode( - phoneNumber.formatPhoneNumber(), + phoneNumber, verificationCode ) authToken.getOrDefault(defaultValue = null) ?.let { tokenRepository.saveOwnerAccessToken(it.token) } if (authToken.isFailure) { - Result.failure(authToken.exceptionOrNull()!!) + Result.failure(authToken.exceptionOrNull() ?: CancellationException()) } else Result.success(SignupContinuationState.CheckComplete) } diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt index 315ad202c..6f8866c9e 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/business/SendSignupSmsCodeUseCase.kt @@ -2,7 +2,6 @@ package `in`.koreatech.koin.domain.usecase.business import `in`.koreatech.koin.domain.repository.OwnerSignupRepository import `in`.koreatech.koin.domain.state.signup.SignupContinuationState -import `in`.koreatech.koin.domain.util.ext.formatPhoneNumber import javax.inject.Inject class SendSignupSmsCodeUseCase @Inject constructor( @@ -12,7 +11,7 @@ class SendSignupSmsCodeUseCase @Inject constructor( phoneNumber: String, ): Result { return try { - ownerSignupRepository.requestSmsVerificationCode(phoneNumber.formatPhoneNumber()) + ownerSignupRepository.requestSmsVerificationCode(phoneNumber) Result.success(SignupContinuationState.RequestedSmsValidation) } catch (t: Throwable) { Result.failure(t) diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/signup/SignupRequestEmailVerificationUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/signup/SignupRequestEmailVerificationUseCase.kt index 290a35b4b..e60017130 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/signup/SignupRequestEmailVerificationUseCase.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/signup/SignupRequestEmailVerificationUseCase.kt @@ -41,7 +41,7 @@ class SignupRequestEmailVerificationUseCase @Inject constructor( name = name, nickName = nickName, password = password, - phoneNumber = phoneNumber.formatPhoneNumber(), + phoneNumber = phoneNumber, studentNumber = studentNumber, ).map { SignupContinuationState.RequestedEmailValidation From 15f9c6d3aa61fda2773882fc1d52fb7f90b7933a Mon Sep 17 00:00:00 2001 From: nazero Date: Wed, 5 Jun 2024 21:43:38 +0900 Subject: [PATCH 114/131] Add error state --- .../feature/signup/accountsetup/AccountSetupScreen.kt | 6 +++--- .../feature/signup/accountsetup/AccountSetupState.kt | 3 +++ .../feature/signup/accountsetup/AccountSetupViewModel.kt | 7 ++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index 2dab29d7a..17988a8a0 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -176,7 +176,7 @@ fun AccountSetupScreen( textStyle = TextStyle.Default.copy(fontSize = 15.sp), errorText = stringResource(id = R.string.password_not_validate), isPassword = true, - isError = !state.password.isValidPassword() && state.password.isNotEmpty(), + isError = state.isPasswordError, ) Spacer(modifier = Modifier.height(10.dp)) @@ -193,7 +193,7 @@ fun AccountSetupScreen( textStyle = TextStyle.Default.copy(fontSize = 15.sp), isPassword = true, errorText = stringResource(id = R.string.password_mismatch), - isError = state.password != state.passwordConfirm && state.passwordConfirm.isNotEmpty(), + isError = state.isPasswordConfirmError, ) Spacer(modifier = Modifier.height(10.dp)) @@ -211,7 +211,7 @@ fun AccountSetupScreen( label = stringResource(id = R.string.enter_phone_number), textStyle = TextStyle.Default.copy(fontSize = 15.sp), errorText = stringResource(id = R.string.phone_number_not_validate), - isError = state.phoneNumber.length != 11 && state.phoneNumber.isNotEmpty(), + isError = state.isPhoneNumberError, ) Spacer(modifier = Modifier.height(10.dp)) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt index 94354099e..5b525ac68 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt @@ -8,6 +8,9 @@ data class AccountSetupState( val passwordConfirm: String = "", val phoneNumber: String = "", val authCode: String = "", + val isPasswordError : Boolean = false, + val isPasswordConfirmError : Boolean = false, + val isPhoneNumberError : Boolean = false, val signupContinuationState: SignupContinuationState = SignupContinuationState.RequestedEmailValidation, val signUpContinuationError:Throwable? = null, val isLoading: Boolean = false diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index bc1ac0b6e..68f76e902 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.domain.usecase.business.BusinessSignupCheckUseCase import `in`.koreatech.koin.domain.usecase.business.SendSignupSmsCodeUseCase +import `in`.koreatech.koin.domain.util.ext.isValidPassword import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost @@ -30,19 +31,19 @@ class AccountSetupViewModel @Inject constructor( fun onPasswordChanged(password: String) = intent { reduce { - state.copy(password = password) + state.copy(password = password, isPasswordError = !password.isValidPassword()) } } fun onPasswordConfirmChanged(passwordConfirm: String) = intent { reduce { - state.copy(passwordConfirm = passwordConfirm) + state.copy(passwordConfirm = passwordConfirm, isPasswordConfirmError = state.password != passwordConfirm) } } fun onPhoneNumChanged(phoneNumber: String) = intent { reduce { - state.copy(phoneNumber = phoneNumber) + state.copy(phoneNumber = phoneNumber, isPhoneNumberError = phoneNumber.length != 11) } } From 53e40662b68355595fa586c18cfcead7bd769f8f Mon Sep 17 00:00:00 2001 From: Strone Date: Thu, 6 Jun 2024 23:43:10 +0900 Subject: [PATCH 115/131] Modify wrong event logging --- .../koin/ui/dining/adapter/DiningAdapter.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt b/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt index ecc0a2c35..3468bd969 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt @@ -62,12 +62,13 @@ class DiningAdapter : ListAdapter(diffCallback) textViewNoPhoto.visibility = View.VISIBLE imageViewNoPhoto.visibility = View.VISIBLE imageViewDining.visibility = View.INVISIBLE - cardViewDining.setOnClickListener(null) - EventLogger.logClickEvent( - AnalyticsConstant.Domain.CAMPUS, - AnalyticsConstant.Label.MENU_IMAGE, - DiningUtil.getKoreanName(dining.type) + "_" + dining.place - ) + cardViewDining.setOnClickListener { + EventLogger.logClickEvent( + AnalyticsConstant.Domain.CAMPUS, + AnalyticsConstant.Label.MENU_IMAGE, + DiningUtil.getKoreanName(dining.type) + "_" + dining.place + ) + } } if(dining.changedAt.isNotEmpty()) { From 2783eda6b78e81cbf17e2de99bfec21e98576599 Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 8 Jun 2024 15:01:39 +0900 Subject: [PATCH 116/131] Modify button enabled logic --- .../signup/accountsetup/AccountSetupScreen.kt | 7 ++- .../signup/accountsetup/AccountSetupState.kt | 17 +++--- .../accountsetup/AccountSetupViewModel.kt | 59 ++++++++++++++++++- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index 17988a8a0..5132d00d8 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -18,6 +18,8 @@ import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -40,8 +42,11 @@ import `in`.koreatech.business.ui.theme.Gray1 import `in`.koreatech.business.ui.theme.Gray2 import `in`.koreatech.business.ui.theme.KOIN_ANDROIDTheme import `in`.koreatech.koin.domain.util.ext.isValidPassword +import kotlinx.coroutines.flow.combine import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.reduce @Composable fun AccountSetupScreen( @@ -268,7 +273,7 @@ fun AccountSetupScreen( .fillMaxWidth() .height(44.dp), shape = RectangleShape, - enabled = state.signUpContinuationError == null && state.isButtonEnabled, + enabled = state.isButtonEnabled, colors = ButtonDefaults.buttonColors( backgroundColor = ColorPrimary, contentColor = Color.White, diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt index 5b525ac68..0b4b34500 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupState.kt @@ -8,14 +8,11 @@ data class AccountSetupState( val passwordConfirm: String = "", val phoneNumber: String = "", val authCode: String = "", - val isPasswordError : Boolean = false, - val isPasswordConfirmError : Boolean = false, - val isPhoneNumberError : Boolean = false, + val isPasswordError: Boolean = false, + val isPasswordConfirmError: Boolean = false, + val isPhoneNumberError: Boolean = false, val signupContinuationState: SignupContinuationState = SignupContinuationState.RequestedEmailValidation, - val signUpContinuationError:Throwable? = null, - val isLoading: Boolean = false -){ - val isButtonEnabled: Boolean - get() = id.isNotEmpty() && password.isNotEmpty() && passwordConfirm.isNotEmpty() && phoneNumber.isNotEmpty() && authCode.isNotEmpty() - -} \ No newline at end of file + val signUpContinuationError: Throwable? = null, + val isLoading: Boolean = false, + val isButtonEnabled: Boolean = false +) \ No newline at end of file diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt index 68f76e902..0e63cd979 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupViewModel.kt @@ -1,5 +1,12 @@ package `in`.koreatech.business.feature.signup.accountsetup +import android.util.Log +import androidx.compose.runtime.getValue +import androidx.compose.runtime.internal.composableLambdaInstance +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -7,6 +14,11 @@ import `in`.koreatech.koin.domain.usecase.business.BusinessSignupCheckUseCase import `in`.koreatech.koin.domain.usecase.business.SendSignupSmsCodeUseCase import `in`.koreatech.koin.domain.util.ext.isValidPassword import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.syntax.simple.intent @@ -23,6 +35,48 @@ class AccountSetupViewModel @Inject constructor( override val container = container(AccountSetupState()) + + private val passwordFlow = container.stateFlow + .map { it.password } + .distinctUntilChanged() + + private val idFlow = container.stateFlow + .map { it.id } + .distinctUntilChanged() + + private val passwordConfirmFlow = container.stateFlow + .map { it.passwordConfirm } + .distinctUntilChanged() + + private val phoneNumberFlow = container.stateFlow + .map { it.phoneNumber } + .distinctUntilChanged() + + private val authCodeFlow = container.stateFlow + .map { it.authCode } + .distinctUntilChanged() + + init { + combine( + passwordFlow, + idFlow, + passwordConfirmFlow, + phoneNumberFlow, + authCodeFlow + ) { password, id, passwordConfirm, phoneNumber, authCode -> + password.isNotEmpty() && id.isNotEmpty() && passwordConfirm.isNotEmpty() && phoneNumber.isNotEmpty() && authCode.isNotEmpty() + }.distinctUntilChanged() + .onEach { + updateButton(it) + }.launchIn(viewModelScope) + } + + private fun updateButton(enabled: Boolean) = intent { + reduce { + state.copy(isButtonEnabled = enabled) + } + } + fun onIdChanged(id: String) = intent { reduce { state.copy(id = id) @@ -37,7 +91,10 @@ class AccountSetupViewModel @Inject constructor( fun onPasswordConfirmChanged(passwordConfirm: String) = intent { reduce { - state.copy(passwordConfirm = passwordConfirm, isPasswordConfirmError = state.password != passwordConfirm) + state.copy( + passwordConfirm = passwordConfirm, + isPasswordConfirmError = state.password != passwordConfirm + ) } } From 16061bc5d93e437d3a05778d4a527e093b746290 Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 8 Jun 2024 16:01:58 +0900 Subject: [PATCH 117/131] Modify check terms logic --- .../signup/checkterm/CheckTermScreen.kt | 128 +++++++++--------- .../signup/checkterm/CheckTermViewModel.kt | 20 ++- 2 files changed, 82 insertions(+), 66 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt index 5be1de923..8fcd74aa9 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -57,86 +58,88 @@ fun CheckTermScreen( onNextClicked: () -> Unit = {} ) { val state = viewModel.collectAsState().value + val scrollState = rememberScrollState() val scrollStatePrivacy = rememberScrollState() val scrollStateKoin = rememberScrollState() Column( modifier = modifier.fillMaxSize(), ) { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp) - ) { - IconButton( - onClick = { viewModel.onBackButtonClicked() }, - modifier = Modifier.align(Alignment.CenterStart) - ) { - Icon( - painter = painterResource(id = R.drawable.ic_back), - contentDescription = stringResource(id = R.string.back_icon), - ) - } - - Text( - text = stringResource(id = R.string.sign_up), - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - modifier = Modifier.align(Alignment.Center) - ) - } - - Spacer(modifier = Modifier.height(20.dp)) - - Column( - modifier = Modifier - .padding(horizontal = 24.dp), - verticalArrangement = Arrangement.Center, - ) { - Row( + Column { + Box( modifier = Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween + .fillMaxWidth() + .padding(vertical = 12.dp) ) { + IconButton( + onClick = { viewModel.onBackButtonClicked() }, + modifier = Modifier.align(Alignment.CenterStart) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_back), + contentDescription = stringResource(id = R.string.back_icon), + ) + } + Text( - modifier = Modifier, - color = ColorPrimary, - fontWeight = FontWeight.Bold, - text = stringResource(id = R.string.check_terms) - ) - Text( - text = stringResource(id = R.string.one_third), - color = ColorPrimary, + text = stringResource(id = R.string.sign_up), + fontSize = 18.sp, fontWeight = FontWeight.Bold, + modifier = Modifier.align(Alignment.Center) ) } - Canvas( + Spacer(modifier = Modifier.height(20.dp)) + + Column( modifier = Modifier - .fillMaxWidth() - .padding(16.dp) + .padding(horizontal = 24.dp), + verticalArrangement = Arrangement.Center, ) { - drawLine( - color = ColorUnarchived, - start = Offset(-40f, 0f), - end = Offset(size.width + 35, size.height), - strokeWidth = 4.dp.toPx(), - cap = StrokeCap.Round - ) - drawLine( - color = ColorPrimary, - start = Offset(-40f, 0f), - end = Offset((size.width + 40) / 3 * 2, size.height), - strokeWidth = 4.dp.toPx(), - cap = StrokeCap.Round - ) + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + modifier = Modifier, + color = ColorPrimary, + fontWeight = FontWeight.Bold, + text = stringResource(id = R.string.check_terms) + ) + Text( + text = stringResource(id = R.string.one_third), + color = ColorPrimary, + fontWeight = FontWeight.Bold, + ) + } + + Canvas( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + drawLine( + color = ColorUnarchived, + start = Offset(-40f, 0f), + end = Offset(size.width + 35, size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + drawLine( + color = ColorPrimary, + start = Offset(-40f, 0f), + end = Offset((size.width + 40) / 3, size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + } } } - Column( modifier = Modifier - .padding(horizontal = 24.dp), + .padding(horizontal = 24.dp).verticalScroll(scrollState), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -191,8 +194,7 @@ fun CheckTermScreen( .width(24.dp) .clickable { viewModel.onPrivacyTermCheckedChanged() - if (state.isCheckedKoinTerms) - viewModel.onAllTermCheckedChanged() + } ) Spacer(modifier = Modifier.height(10.dp)) @@ -236,8 +238,6 @@ fun CheckTermScreen( .width(24.dp) .clickable { viewModel.onKoinTermCheckedChanged() - if (state.isCheckedPrivacyTerms) - viewModel.onAllTermCheckedChanged() } ) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt index 502431993..f159bde99 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/checkterm/CheckTermViewModel.kt @@ -21,8 +21,8 @@ class CheckTermViewModel @Inject constructor() : reduce { state.copy( isAllTermChecked = !state.isAllTermChecked, - isCheckedPrivacyTerms = state.isAllTermChecked, - isCheckedKoinTerms = state.isAllTermChecked + isCheckedPrivacyTerms = !state.isAllTermChecked, + isCheckedKoinTerms = !state.isAllTermChecked ) } } @@ -30,13 +30,29 @@ class CheckTermViewModel @Inject constructor() : fun onPrivacyTermCheckedChanged() { intent { + if (state.isAllTermChecked) { + reduce { state.copy(isAllTermChecked = false) } + } reduce { state.copy(isCheckedPrivacyTerms = !state.isCheckedPrivacyTerms) } + checkAllTermChecked() } } fun onKoinTermCheckedChanged() { intent { + if (state.isAllTermChecked) { + reduce { state.copy(isAllTermChecked = false) } + } reduce { state.copy(isCheckedKoinTerms = !state.isCheckedKoinTerms) } + checkAllTermChecked() + } + } + + private fun checkAllTermChecked() { + intent { + if (state.isCheckedPrivacyTerms && state.isCheckedKoinTerms) { + reduce { state.copy(isAllTermChecked = true) } + } } } From 30a779aeb22da6a5a3c2ebdf1175c09cd822ab25 Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 8 Jun 2024 16:48:27 +0900 Subject: [PATCH 118/131] Modify signup ui --- .../signup/accountsetup/AccountSetupScreen.kt | 134 +++++++++--------- .../signup/businessauth/BusinessAuthScreen.kt | 130 +++++++++-------- 2 files changed, 140 insertions(+), 124 deletions(-) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt index 5132d00d8..63d08599e 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/accountsetup/AccountSetupScreen.kt @@ -11,15 +11,15 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -41,12 +41,8 @@ import `in`.koreatech.business.ui.theme.ColorUnarchived import `in`.koreatech.business.ui.theme.Gray1 import `in`.koreatech.business.ui.theme.Gray2 import `in`.koreatech.business.ui.theme.KOIN_ANDROIDTheme -import `in`.koreatech.koin.domain.util.ext.isValidPassword -import kotlinx.coroutines.flow.combine import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect -import org.orbitmvi.orbit.syntax.simple.intent -import org.orbitmvi.orbit.syntax.simple.reduce @Composable fun AccountSetupScreen( @@ -55,79 +51,89 @@ fun AccountSetupScreen( onBackClicked: () -> Unit = {}, onNextClicked: () -> Unit = {}, ) { - + val scrollState = rememberScrollState() val state = viewModel.collectAsState().value Column( modifier = modifier.fillMaxSize(), ) { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp) - ) { - IconButton( - onClick = { viewModel.onBackButtonClicked() }, - modifier = Modifier.align(Alignment.CenterStart) + Column { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp) ) { - Icon( - painter = painterResource(id = R.drawable.ic_back), - contentDescription = stringResource(id = R.string.back_icon), + IconButton( + onClick = { viewModel.onBackButtonClicked() }, + modifier = Modifier.align(Alignment.CenterStart) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_back), + contentDescription = stringResource(id = R.string.back_icon), + ) + } + + Text( + text = stringResource(id = R.string.sign_up), + fontSize = 18.sp, + fontWeight = Bold, + modifier = Modifier.align(Alignment.Center) ) } - Text( - text = stringResource(id = R.string.sign_up), - fontSize = 18.sp, - fontWeight = Bold, - modifier = Modifier.align(Alignment.Center) - ) - } + Spacer(modifier = Modifier.height(20.dp)) + + Column( + modifier = Modifier + .padding(horizontal = 24.dp), + verticalArrangement = Arrangement.Center, + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + modifier = Modifier, + color = ColorPrimary, + fontWeight = Bold, + text = stringResource(id = R.string.input_basic_information) + ) + Text( + text = stringResource(id = R.string.two_third), + color = ColorPrimary, + fontWeight = Bold, + ) + } + Canvas( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + drawLine( + color = ColorUnarchived, + start = Offset(-40f, 0f), + end = Offset(size.width + 40, size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + drawLine( + color = ColorPrimary, + start = Offset(-40f, 0f), + end = Offset((size.width + 35) / 3 * 2, size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + } + } + } Spacer(modifier = Modifier.height(20.dp)) Column( modifier = Modifier - .padding(horizontal = 24.dp), + .padding(horizontal = 24.dp).verticalScroll(scrollState), verticalArrangement = Arrangement.Center, ) { - Row( - modifier = Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - modifier = Modifier, - color = ColorPrimary, - fontWeight = Bold, - text = stringResource(id = R.string.input_basic_information), - ) - Text( - text = stringResource(id = R.string.two_third), - color = ColorPrimary, - fontWeight = Bold, - ) - } - - Canvas( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - drawLine( - color = ColorUnarchived, - start = Offset(-40f, 0f), - end = Offset(size.width + 35, size.height), - strokeWidth = 4.dp.toPx(), - cap = StrokeCap.Round - ) - drawLine( - color = ColorPrimary, - start = Offset(-40f, 0f), - end = Offset((size.width + 40) / 3 * 2, size.height), - strokeWidth = 4.dp.toPx(), - cap = StrokeCap.Round - ) - } Spacer(modifier = Modifier.height(10.dp)) diff --git a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt index 65cc65477..d9e0276b7 100644 --- a/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt +++ b/business/src/main/java/in/koreatech/business/feature/signup/businessauth/BusinessAuthScreen.kt @@ -41,7 +41,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight.Companion.Bold -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.times @@ -73,7 +72,7 @@ fun BusinessAuthScreen( onNextClicked: () -> Unit = {}, ) { val context = LocalContext.current - + val scrollState = rememberScrollState() val businessAuthState = businessAuthViewModel.collectAsState().value val accountSetupState = accountSetupViewModel.collectAsState().value @@ -122,78 +121,89 @@ fun BusinessAuthScreen( } } ) + Column( - modifier = modifier, + modifier = modifier.fillMaxSize(), ) { - - Box( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp) - ) { - IconButton( - onClick = { businessAuthViewModel.onNavigateToBackScreen() }, - modifier = Modifier.align(Alignment.CenterStart) + Column { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp) ) { - Icon( - painter = painterResource(id = R.drawable.ic_back), - contentDescription = stringResource(id = R.string.back_icon), + IconButton( + onClick = { businessAuthViewModel.onNavigateToBackScreen() }, + modifier = Modifier.align(Alignment.CenterStart) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_back), + contentDescription = stringResource(id = R.string.back_icon), + ) + } + + Text( + text = stringResource(id = R.string.sign_up), + fontSize = 18.sp, + fontWeight = Bold, + modifier = Modifier.align(Alignment.Center) ) } - Text( - text = stringResource(id = R.string.sign_up), - fontSize = 18.sp, - fontWeight = Bold, - modifier = Modifier.align(Alignment.Center) - ) + Spacer(modifier = Modifier.height(20.dp)) + + Column( + modifier = Modifier + .padding(horizontal = 24.dp), + verticalArrangement = Arrangement.Center, + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + modifier = Modifier, + color = ColorPrimary, + fontWeight = Bold, + text = stringResource(id = R.string.business_auth) + ) + Text( + text = stringResource(id = R.string.three_third), + color = ColorPrimary, + fontWeight = Bold, + ) + } + + Canvas( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + drawLine( + color = ColorUnarchived, + start = Offset(-40f, 0f), + end = Offset(size.width + 35, size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + drawLine( + color = ColorPrimary, + start = Offset(-40f, 0f), + end = Offset(size.width + 40, size.height), + strokeWidth = 4.dp.toPx(), + cap = StrokeCap.Round + ) + } + } } Spacer(modifier = Modifier.height(20.dp)) Column( modifier = Modifier - .padding(horizontal = 32.dp) + .padding(horizontal = 24.dp) .verticalScroll(scrollState), verticalArrangement = Arrangement.Center, ) { - Row( - modifier = Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - modifier = Modifier, - color = ColorPrimary, - fontWeight = Bold, - text = stringResource(id = R.string.business_auth), - ) - Text( - text = stringResource(id = R.string.three_third), - color = ColorPrimary, - fontWeight = Bold, - ) - } - - Canvas( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - drawLine( - color = ColorUnarchived, - start = Offset(-40f, 0f), - end = Offset(size.width + 35, size.height), - strokeWidth = 4.dp.toPx(), - cap = StrokeCap.Round - ) - drawLine( - color = ColorPrimary, - start = Offset(-40f, 0f), - end = Offset((size.width + 40) , size.height), - strokeWidth = 4.dp.toPx(), - cap = StrokeCap.Round - ) - } Spacer(modifier = Modifier.height(10.dp)) From ce44cb62e48e0b55d72a2b2dfe547cbebb9af3de Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 8 Jun 2024 16:58:46 +0900 Subject: [PATCH 119/131] Add security-crypto dependency --- business/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/business/build.gradle.kts b/business/build.gradle.kts index 3ce14a0ba..cd09af40d 100644 --- a/business/build.gradle.kts +++ b/business/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(libs.bundles.compose) implementation(libs.lifecycle.runtime.ktx) implementation(libs.compose.navigation) + implementation(libs.androidx.security.crypto) implementation(libs.coil) implementation(libs.coil.compose) implementation(libs.androidx.security.crypto) From 093228b3f7e7f177096d832e73524d513fdf6c08 Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 8 Jun 2024 17:06:46 +0900 Subject: [PATCH 120/131] Modify TokenLocalDataSource name --- ...okenLocalDataSource.kt => BusinessTokenLocalDataSource.kt} | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename business/src/main/java/in/koreatech/business/di/resource/{TokenLocalDataSource.kt => BusinessTokenLocalDataSource.kt} (96%) diff --git a/business/src/main/java/in/koreatech/business/di/resource/TokenLocalDataSource.kt b/business/src/main/java/in/koreatech/business/di/resource/BusinessTokenLocalDataSource.kt similarity index 96% rename from business/src/main/java/in/koreatech/business/di/resource/TokenLocalDataSource.kt rename to business/src/main/java/in/koreatech/business/di/resource/BusinessTokenLocalDataSource.kt index 82fd89809..5c2f80c6d 100644 --- a/business/src/main/java/in/koreatech/business/di/resource/TokenLocalDataSource.kt +++ b/business/src/main/java/in/koreatech/business/di/resource/BusinessTokenLocalDataSource.kt @@ -2,17 +2,15 @@ package `in`.koreatech.business.di.resource import android.content.Context -import android.util.Log import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineDispatcher import javax.inject.Inject import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.job import kotlinx.coroutines.withContext -class TokenLocalDataSource @Inject constructor( +class BusinessTokenLocalDataSource @Inject constructor( @ApplicationContext applicationContext: Context, private val dispatchersIO: CoroutineDispatcher, ) { From 4943e53fe45f9cfcec3e0e542b803de3b9b25ea3 Mon Sep 17 00:00:00 2001 From: jangnayoung <128479811+skdud0629@users.noreply.github.com> Date: Sat, 8 Jun 2024 17:13:34 +0900 Subject: [PATCH 121/131] Update business/src/main/java/in/koreatech/business/feature/textfield/SearchTextField.kt Co-authored-by: Yunjae-Na <41162218+yunjaena@users.noreply.github.com> --- .../in/koreatech/business/feature/textfield/SearchTextField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/business/src/main/java/in/koreatech/business/feature/textfield/SearchTextField.kt b/business/src/main/java/in/koreatech/business/feature/textfield/SearchTextField.kt index 3189bfcb7..2b4088c7b 100644 --- a/business/src/main/java/in/koreatech/business/feature/textfield/SearchTextField.kt +++ b/business/src/main/java/in/koreatech/business/feature/textfield/SearchTextField.kt @@ -53,7 +53,7 @@ fun SearchTextField( decorationBox = { innerTextField -> Row( modifier = Modifier.fillMaxWidth().height(40.dp) - .background(color=Gray5, shape = RoundedCornerShape(4.dp)) + .background(color = Gray5, shape = RoundedCornerShape(4.dp)) .padding(8.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween From 13a89b2c4c5be9015ac0d698f9322adf0e70b8ee Mon Sep 17 00:00:00 2001 From: nazero Date: Sat, 8 Jun 2024 17:21:52 +0900 Subject: [PATCH 122/131] Add ApiMapper --- .../in/koreatech/koin/data/mapper/ApiMapper.kt | 13 +++++++++++++ .../repository/OwnerSignupRepositoryImpl.kt | 8 ++------ .../OwnerVerificationCodeRepositoryImpl.kt | 11 +++-------- .../repository/PreSignedUrlRepositoryImpl.kt | 18 +++++++++--------- 4 files changed, 27 insertions(+), 23 deletions(-) create mode 100644 data/src/main/java/in/koreatech/koin/data/mapper/ApiMapper.kt diff --git a/data/src/main/java/in/koreatech/koin/data/mapper/ApiMapper.kt b/data/src/main/java/in/koreatech/koin/data/mapper/ApiMapper.kt new file mode 100644 index 000000000..b69015b76 --- /dev/null +++ b/data/src/main/java/in/koreatech/koin/data/mapper/ApiMapper.kt @@ -0,0 +1,13 @@ +package `in`.koreatech.koin.data.mapper + +import kotlin.coroutines.cancellation.CancellationException + +suspend fun safeApiCall(call: suspend () -> T): Result { + return try { + Result.success(call()) + } catch (e: CancellationException) { + throw e + } catch (t: Throwable) { + Result.failure(t) + } +} diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt index be24c201a..b92187167 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OwnerSignupRepositoryImpl.kt @@ -1,6 +1,7 @@ package `in`.koreatech.koin.data.repository import `in`.koreatech.koin.data.mapper.httpExceptionMapper +import `in`.koreatech.koin.data.mapper.safeApiCall import `in`.koreatech.koin.data.request.owner.OwnerVerificationEmailRequest import `in`.koreatech.koin.data.request.owner.VerificationSmsRequest import `in`.koreatech.koin.data.source.local.SignupTermsLocalDataSource @@ -43,17 +44,12 @@ class OwnerSignupRepositoryImpl @Inject constructor( override suspend fun requestSmsVerificationCode( phoneNumber: String ): Result { - return try { + return safeApiCall { ownerRemoteDataSource.postVerificationSms( VerificationSmsRequest( phoneNumber = phoneNumber ) ) - Result.success(Unit) - } catch (e: CancellationException) { - throw e - } catch (t: Throwable) { - Result.failure(t) } } } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt index 2cb1e648b..d2874577c 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OwnerVerificationCodeRepositoryImpl.kt @@ -1,6 +1,6 @@ package `in`.koreatech.koin.data.repository -import android.util.Log +import `in`.koreatech.koin.data.mapper.safeApiCall import `in`.koreatech.koin.data.mapper.toAuthToken import `in`.koreatech.koin.data.request.owner.OwnerVerificationCodeRequest import `in`.koreatech.koin.data.request.owner.VerificationCodeSmsRequest @@ -9,7 +9,6 @@ import `in`.koreatech.koin.domain.model.owner.OwnerAuthToken import `in`.koreatech.koin.domain.repository.OwnerVerificationCodeRepository import retrofit2.HttpException import javax.inject.Inject -import kotlin.coroutines.cancellation.CancellationException class OwnerVerificationCodeRepositoryImpl @Inject constructor( private val ownerRemoteDataSource: OwnerRemoteDataSource @@ -38,18 +37,14 @@ class OwnerVerificationCodeRepositoryImpl @Inject constructor( phoneNumber: String, verificationCode: String ): Result { - return try { + return safeApiCall { val tempToken = ownerRemoteDataSource.postVerificationCodeSms( VerificationCodeSmsRequest( phoneNumber = phoneNumber, certificationCode = verificationCode ) ) - Result.success(tempToken.toAuthToken()) - } catch (e: CancellationException) { - throw e - } catch (t: Throwable) { - Result.failure(t) + tempToken.toAuthToken() } } } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt index e94cca78d..9502b24d5 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/PreSignedUrlRepositoryImpl.kt @@ -1,5 +1,6 @@ package `in`.koreatech.koin.data.repository +import `in`.koreatech.koin.data.mapper.safeApiCall import `in`.koreatech.koin.data.requestbody.S3RequestBody import `in`.koreatech.koin.data.source.remote.PreSignedUrlRemoteDataSource import `in`.koreatech.koin.domain.repository.PreSignedUrlRepository @@ -9,12 +10,16 @@ import okhttp3.RequestBody.Companion.toRequestBody import retrofit2.HttpException import java.io.InputStream import javax.inject.Inject -import kotlin.coroutines.cancellation.CancellationException class PreSignedUrlRepositoryImpl @Inject constructor( private val preSignedUrlRemoteDataSource: PreSignedUrlRemoteDataSource -): PreSignedUrlRepository { - override suspend fun putPreSignedUrl(url: String, inputStream: InputStream, mediaType: String, mediaSize: Long): Result { +) : PreSignedUrlRepository { + override suspend fun putPreSignedUrl( + url: String, + inputStream: InputStream, + mediaType: String, + mediaSize: Long + ): Result { return try { val file = S3RequestBody(inputStream, mediaType.toMediaType(), mediaSize) preSignedUrlRemoteDataSource.putPreSignedUrl(url, file) @@ -33,14 +38,9 @@ class PreSignedUrlRepositoryImpl @Inject constructor( mediaType: String, mediaSize: Long ): Result { - return try { + return safeApiCall { val file = bitmap.toRequestBody(mediaType.toMediaTypeOrNull()) preSignedUrlRemoteDataSource.putPreSignedUrl(url, file) - Result.success(Unit) - } catch (e: CancellationException) { - throw e - } catch (t: Throwable) { - Result.failure(t) } } } From f4a9c711a6f69fb19d5daf84428103bb935285bd Mon Sep 17 00:00:00 2001 From: nazero Date: Sun, 9 Jun 2024 22:11:58 +0900 Subject: [PATCH 123/131] Delete unnecessary code --- .../resource/BusinessTokenLocalDataSource.kt | 104 ------------------ 1 file changed, 104 deletions(-) delete mode 100644 business/src/main/java/in/koreatech/business/di/resource/BusinessTokenLocalDataSource.kt diff --git a/business/src/main/java/in/koreatech/business/di/resource/BusinessTokenLocalDataSource.kt b/business/src/main/java/in/koreatech/business/di/resource/BusinessTokenLocalDataSource.kt deleted file mode 100644 index 5c2f80c6d..000000000 --- a/business/src/main/java/in/koreatech/business/di/resource/BusinessTokenLocalDataSource.kt +++ /dev/null @@ -1,104 +0,0 @@ - -package `in`.koreatech.business.di.resource - -import android.content.Context -import androidx.security.crypto.EncryptedSharedPreferences -import androidx.security.crypto.MasterKey -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineDispatcher -import javax.inject.Inject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -class BusinessTokenLocalDataSource @Inject constructor( - @ApplicationContext applicationContext: Context, - private val dispatchersIO: CoroutineDispatcher, -) { - var masterKey = MasterKey.Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS) - .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) - .build() - - var sharedPreferences = EncryptedSharedPreferences.create( - applicationContext, - SHARED_PREF_FILENAME, - masterKey, - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM - ) - - var sharedOwnerPreferences = EncryptedSharedPreferences.create( - applicationContext, - OWNER_SHARED_PREF_FILENAME, - masterKey, - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM - ) - - suspend fun saveAccessToken( - accessToken: String, - ) = withContext(dispatchersIO) { - with(sharedPreferences.edit()) { - putString(SHARED_PREF_KEY, accessToken) - apply() - } - } - - suspend fun saveRefreshToken( - refreshToken: String, - ) = withContext(dispatchersIO) { - with(sharedPreferences.edit()) { - putString(SHARED_PREF_REFRESH_KEY, refreshToken) - apply() - } - } - - suspend fun getAccessToken(): String? = withContext(dispatchersIO) { - sharedPreferences.getString(SHARED_PREF_KEY, null) - } - - suspend fun getRefreshToken(): String? = withContext(dispatchersIO) { - sharedPreferences.getString(SHARED_PREF_REFRESH_KEY, null) - } - - suspend fun removeAccessToken() = withContext(dispatchersIO) { - with(sharedPreferences.edit()) { - remove(SHARED_PREF_KEY) - apply() - } - } - - suspend fun saveOwnerAccessToken( - accessToken: String - ) = with(Dispatchers.IO) { - with(sharedOwnerPreferences.edit()) { - putString(OWNER_SHARED_PREF_FILENAME, accessToken) - apply() - } - } - - suspend fun getOwnerAccessToken(): String? = withContext(Dispatchers.IO) { - sharedOwnerPreferences.getString(OWNER_SHARED_PREF_FILENAME, null) - } - - suspend fun removeOwnerAccessToken() = withContext(Dispatchers.IO) { - with(sharedOwnerPreferences.edit()) { - remove(OWNER_SHARED_PREF_FILENAME) - apply() - } - } - - suspend fun removeRefreshToken() = withContext(dispatchersIO) { - with(sharedPreferences.edit()) { - remove(SHARED_PREF_REFRESH_KEY) - apply() - } - } - - companion object { - private const val SHARED_PREF_FILENAME = "token" - private const val OWNER_SHARED_PREF_FILENAME = "ownerToken" - - private const val SHARED_PREF_KEY = "accessToken" - private const val SHARED_PREF_REFRESH_KEY = "refreshToken" - } -} From 5466d5af079fbc45eb2b60289e4e5eb5a61cf24c Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Tue, 11 Jun 2024 20:53:07 +0900 Subject: [PATCH 124/131] Remove log, hardcoded string --- .../ui/signup/SignUpWithDetailInfoActivity.kt | 7 ----- .../activity_sign_up_with_detail_info.xml | 28 +++++++++---------- koin/src/main/res/values/strings.xml | 14 +++++++++- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt b/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt index c1dc78073..e9121e735 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/signup/SignUpWithDetailInfoActivity.kt @@ -2,7 +2,6 @@ package `in`.koreatech.koin.ui.signup import android.content.Intent import android.os.Bundle -import android.util.Log import android.view.MotionEvent import androidx.activity.viewModels import androidx.core.widget.addTextChangedListener @@ -79,7 +78,6 @@ class SignupWithDetailInfoActivity : ActivityBase() { private fun continueSignup() { with(binding) { signupSendVerificationButton.setOnClickListener { - Log.d("dhk", "Submited major: ${spinnerSignupUserMajor.text}") signupViewModel.continueDetailSignup( portalAccount = signupViewModel.portalEmail, gender = when { @@ -111,11 +109,6 @@ class SignupWithDetailInfoActivity : ActivityBase() { private fun initSpinner() = with(binding.spinnerSignupUserMajor) { lifecycleOwner = this@SignupWithDetailInfoActivity - - - setOnSpinnerItemSelectedListener { oldIndex, oldItem, newIndex, newItem -> - Log.d("dhk", "major select from $oldItem to $newItem") - } } diff --git a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml index f664e5600..c96156526 100644 --- a/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml +++ b/koin/src/main/res/layout/activity_sign_up_with_detail_info.xml @@ -132,7 +132,7 @@ android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginEnd="233dp" - android:text="인적사항" + android:text="@string/signup_info" android:textColor="@color/black" android:textSize="24sp" app:fontName="Bold" @@ -150,7 +150,7 @@ android:layout_marginTop="30dp" android:includeFontPadding="false" android:lineSpacingExtra="7sp" - android:text="이름" + android:text="@string/signup_name" android:textColor="@color/black" android:textSize="15sp" app:fontName="Custom6" @@ -187,7 +187,7 @@ android:layout_marginTop="25dp" android:includeFontPadding="false" android:lineSpacingExtra="7sp" - android:text="닉네임" + android:text="@string/signup_nickname" android:textColor="@color/black" android:textSize="15sp" app:fontName="Custom6" @@ -234,7 +234,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:lineSpacingExtra="4sp" - android:text="중복확인" + android:text="@string/signup_nickname_check_duplicated" android:textColor="@color/black" android:textSize="8sp" app:fontName="Custom6" /> @@ -247,7 +247,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="25dp" android:lineSpacingExtra="7sp" - android:text="성별" + android:text="@string/signup_gender" android:textColor="@color/black" android:textSize="15sp" app:fontName="Custom6" @@ -277,7 +277,7 @@ android:drawableLeft="@drawable/radio_button_checked_24dp" android:drawablePadding="4dp" android:includeFontPadding="false" - android:text="남자" + android:text="@string/signup_gender_man" android:textColor="@color/colorPrimaryText" android:textSize="15sp" /> @@ -293,7 +293,7 @@ android:drawablePadding="4dp" android:includeFontPadding="false" android:paddingBottom="1dp" - android:text="여자" + android:text="@string/signup_gender_woman" android:textColor="@color/colorPrimaryText" android:textSize="15sp" /> @@ -306,7 +306,7 @@ android:layout_marginTop="25dp" android:includeFontPadding="false" android:lineSpacingExtra="7sp" - android:text="전화번호" + android:text="@string/signup_phone_number" android:textColor="@color/black" android:textSize="15sp" app:fontName="Custom6" @@ -343,7 +343,7 @@ android:layout_marginTop="25dp" android:includeFontPadding="false" android:lineSpacingExtra="7sp" - android:text="학번" + android:text="@string/signup_student_id" android:textColor="@color/black" android:textSize="15sp" app:fontName="Custom6" @@ -381,7 +381,7 @@ android:layout_marginBottom="36dp" android:includeFontPadding="false" android:lineSpacingExtra="7sp" - android:text="학부" + android:text="@string/major_title" android:textColor="@color/black" android:textSize="15sp" app:fontName="Custom6" @@ -443,7 +443,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="55dp" android:lineSpacingExtra="7sp" - android:text="졸업여부" + android:text="@string/signup_is_graduate" android:textColor="@color/black" android:textSize="15sp" app:fontName="Custom6" @@ -473,7 +473,7 @@ android:drawableLeft="@drawable/radio_button_checked_24dp" android:drawablePadding="4dp" android:includeFontPadding="false" - android:text="졸업생" + android:text="@string/signup_graduated_student" android:textColor="@color/colorPrimaryText" android:textSize="15sp" /> @@ -489,7 +489,7 @@ android:drawablePadding="4dp" android:includeFontPadding="false" android:paddingBottom="1dp" - android:text="재학생" + android:text="@string/signup_enrolled_student" android:textColor="@color/colorPrimaryText" android:textSize="15sp" /> @@ -501,7 +501,7 @@ android:layout_height="0dp" android:background="@color/colorAccent" android:gravity="center" - android:text="회원가입하기" + android:text="@string/signup_confirm" android:textColor="@color/white" android:textSize="15sp" app:fontName="Regular" diff --git a/koin/src/main/res/values/strings.xml b/koin/src/main/res/values/strings.xml index 1aab9c7c8..46b40d3ed 100644 --- a/koin/src/main/res/values/strings.xml +++ b/koin/src/main/res/values/strings.xml @@ -45,7 +45,6 @@ 뒤로가기 버튼을 한 번 더 누르면 종료됩니다. 익명 - 서버와 연결에 실패하였습니다 "준비중입니다. 조금만 기다려주세요" @@ -102,6 +101,19 @@ in.koreatech.koin.ui.timetable.UserLockBottomSheetBehavior + 인적사항 + 이름 + 닉네임 + 중복 확인 + 성별 + 남자 + 여자 + 전화번호 + 학번 + 졸업여부 + 졸업생 + 재학생 + 회원가입하기 이메일 전송 중 "동의" 체크 박스에 체크를 한 후 이메일 인증하기 버튼을 클릭하면 본 약관에 동의하는 것으로 간주합니다. 이미 가입한 계정이거나\n이메일 전송을 요청하였습니다. From e7d2cb3a9554c1610879fc05956e242880bcb73b Mon Sep 17 00:00:00 2001 From: Strone Date: Tue, 11 Jun 2024 22:48:32 +0900 Subject: [PATCH 125/131] =?UTF-8?q?Set=20'=EB=AF=B8=EC=9A=B4=EC=98=81'=20m?= =?UTF-8?q?enu=20not=20visible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/in/koreatech/koin/ui/dining/DiningItemsFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koin/src/main/java/in/koreatech/koin/ui/dining/DiningItemsFragment.kt b/koin/src/main/java/in/koreatech/koin/ui/dining/DiningItemsFragment.kt index b4398939f..1d9abfbc4 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/dining/DiningItemsFragment.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/dining/DiningItemsFragment.kt @@ -33,7 +33,7 @@ class DiningItemsFragment : Fragment(R.layout.fragment_dining_items) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.dining.collect { val diningList = it.filter { dining -> dining.type == type }.arrange() - diningAdapter.submitList( diningList.filter { dining -> dining.menu.isNotEmpty() } ) + diningAdapter.submitList( diningList.filter { dining -> dining.menu.isNotEmpty() && dining.menu.first() != "미운영" } ) } } } From 37a2e54b8e4797f566841113ca6280d263ced9e0 Mon Sep 17 00:00:00 2001 From: Strone Date: Wed, 12 Jun 2024 00:09:35 +0900 Subject: [PATCH 126/131] Fix scrollable view text cutting issue --- koin/src/main/res/layout/activity_dining.xml | 3 +- .../main/res/layout/fragment_dining_items.xml | 51 ++++++++++--------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/koin/src/main/res/layout/activity_dining.xml b/koin/src/main/res/layout/activity_dining.xml index c37638bf8..c2fe85977 100644 --- a/koin/src/main/res/layout/activity_dining.xml +++ b/koin/src/main/res/layout/activity_dining.xml @@ -63,7 +63,8 @@ android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/tabs_dining_time" - app:layout_constraintBottom_toBottomOf="parent"/> + app:layout_constraintBottom_toBottomOf="parent" + android:background="@color/background_dark"/> diff --git a/koin/src/main/res/layout/fragment_dining_items.xml b/koin/src/main/res/layout/fragment_dining_items.xml index 6b9cb2e22..5c59c8571 100644 --- a/koin/src/main/res/layout/fragment_dining_items.xml +++ b/koin/src/main/res/layout/fragment_dining_items.xml @@ -1,33 +1,38 @@ - - + android:layout_height="wrap_content"> - + android:paddingBottom="8dp" + android:background="@color/background_dark"> - + - + + + \ No newline at end of file From 7b93a316595f18b2f87d923158252d3e075f8f76 Mon Sep 17 00:00:00 2001 From: Strone Date: Wed, 12 Jun 2024 12:01:44 +0900 Subject: [PATCH 127/131] Delete unused variable --- .../java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt b/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt index ecc0a2c35..fca73320b 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/dining/adapter/DiningAdapter.kt @@ -134,7 +134,6 @@ class DiningAdapter : ListAdapter(diffCallback) } companion object { - private const val DIALOG_MIN_SCALE = 0.75f private val diffCallback = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( oldItem: Dining, From a66f08e604c55b3b8d62110666c0f406a18ca1d1 Mon Sep 17 00:00:00 2001 From: Strone Date: Wed, 12 Jun 2024 12:05:13 +0900 Subject: [PATCH 128/131] Change file name --- .../core/dialog/{ZoomableDialog.kt => ImageZoomableDialog.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/src/main/java/in/koreatech/koin/core/dialog/{ZoomableDialog.kt => ImageZoomableDialog.kt} (100%) diff --git a/core/src/main/java/in/koreatech/koin/core/dialog/ZoomableDialog.kt b/core/src/main/java/in/koreatech/koin/core/dialog/ImageZoomableDialog.kt similarity index 100% rename from core/src/main/java/in/koreatech/koin/core/dialog/ZoomableDialog.kt rename to core/src/main/java/in/koreatech/koin/core/dialog/ImageZoomableDialog.kt From 318c9833b40253c2ad1d9bcc64df00d7c8096689 Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Thu, 13 Jun 2024 20:53:02 +0900 Subject: [PATCH 129/131] Change Usereditinfo major from edittext to spinner --- .../koin/ui/userinfo/UserInfoEditActivity.kt | 59 +++++++------------ .../viewmodel/UserInfoEditViewModel.kt | 27 +++++---- .../res/layout/activity_user_info_edited.xml | 46 +++++++-------- 3 files changed, 57 insertions(+), 75 deletions(-) diff --git a/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt b/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt index 5aaae02f7..978e99881 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt @@ -2,7 +2,9 @@ package `in`.koreatech.koin.ui.userinfo import android.os.Bundle import androidx.activity.viewModels -import androidx.core.widget.addTextChangedListener +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import dagger.hilt.android.AndroidEntryPoint import `in`.koreatech.koin.R import `in`.koreatech.koin.core.toast.ToastUtil @@ -17,11 +19,10 @@ import `in`.koreatech.koin.ui.userinfo.contract.UserInfoEditContract import `in`.koreatech.koin.ui.userinfo.state.NicknameCheckState import `in`.koreatech.koin.ui.userinfo.viewmodel.UserInfoEditViewModel import `in`.koreatech.koin.util.ext.observeLiveData -import `in`.koreatech.koin.util.ext.setDefaultBackground -import `in`.koreatech.koin.util.ext.setTransparentBackground import `in`.koreatech.koin.util.ext.splitPhoneNumber import `in`.koreatech.koin.util.ext.textString import `in`.koreatech.koin.util.ext.withLoading +import kotlinx.coroutines.launch @AndroidEntryPoint class UserInfoEditActivity : KoinNavigationDrawerActivity() { @@ -65,13 +66,7 @@ class UserInfoEditActivity : KoinNavigationDrawerActivity() { } ) - userinfoeditedEdittextName.addTextChangedListener { - userInfoEditViewModel - } - - userinfoeditedEdittextStudentId.addTextChangedListener { - userInfoEditViewModel.getDept(it.toString()) - } + userinfoeditedSpinnerMajor.lifecycleOwner = this@UserInfoEditActivity userinfoeditedButtonNicknameCheck.setOnClickListener { userInfoEditViewModel.checkNickname(userinfoeditedEdittextNickName.textString) @@ -118,42 +113,17 @@ class UserInfoEditActivity : KoinNavigationDrawerActivity() { } } - userinfoeditedEdittextStudentId.apply { - setText(user.studentNumber) - if (user.studentNumber == null || user.studentNumber?.isEmpty() == false) { - isEnabled = true - setDefaultBackground() - userinfoeditedEdittextMajor.setText("") - } else { - isEnabled = false - setTransparentBackground() - userInfoEditViewModel.getDept(user.studentNumber!!) - } - } - - userinfoeditedEdittextMajor.apply { - isEnabled = false - hint = context.getString(R.string.user_info_id_hint) - } + userinfoeditedEdittextStudentId.setText(user.studentNumber) } } } - observeLiveData(dept) { - binding.userinfoeditedEdittextMajor.setText(it) - binding.userinfoeditedEdittextMajorError.text = "" - } - - observeLiveData(getDeptErrorMessage) { - binding.userinfoeditedEdittextMajorError.text = it - } - observeLiveData(toastErrorMessage) { ToastUtil.getInstance().makeShort(it) } observeLiveData(nicknameDuplicatedEvent) { - when(it) { + when (it) { NicknameCheckState.POSSIBLE -> { ToastUtil.getInstance().makeShort(R.string.nickname_available) } @@ -173,6 +143,21 @@ class UserInfoEditActivity : KoinNavigationDrawerActivity() { setResult(UserInfoEditContract.RESULT_USER_INFO_EDITED) finish() } + + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + depts.collect { (depts, userMajor) -> + binding.userinfoeditedSpinnerMajor.setItems(depts) + with(binding.userinfoeditedSpinnerMajor) { + setItems(depts) + val pos = depts.indexOf(userMajor) + if (pos != -1) { + selectItemByIndex(pos) + } + } + } + } + } } } } \ No newline at end of file diff --git a/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt b/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt index fc265f834..4a289cadb 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt @@ -2,13 +2,14 @@ package `in`.koreatech.koin.ui.userinfo.viewmodel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import `in`.koreatech.koin.core.viewmodel.BaseViewModel import `in`.koreatech.koin.core.viewmodel.SingleLiveEvent import `in`.koreatech.koin.domain.model.user.Gender import `in`.koreatech.koin.domain.model.user.User -import `in`.koreatech.koin.domain.usecase.dept.GetDeptNameFromStudentIdUseCase +import `in`.koreatech.koin.domain.usecase.dept.GetDeptNamesUseCase import `in`.koreatech.koin.domain.usecase.user.CheckNicknameValidationUseCase import `in`.koreatech.koin.domain.usecase.user.GetUserInfoUseCase import `in`.koreatech.koin.domain.usecase.user.UpdateStudentUserInfoUseCase @@ -16,21 +17,30 @@ import `in`.koreatech.koin.domain.util.onFailure import `in`.koreatech.koin.domain.util.onSuccess import `in`.koreatech.koin.ui.userinfo.state.NicknameCheckState import `in`.koreatech.koin.ui.userinfo.state.NicknameState +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import javax.inject.Inject @HiltViewModel class UserInfoEditViewModel @Inject constructor( private val getUserInfoUseCase: GetUserInfoUseCase, - private val deptNameFromStudentIdUseCase: GetDeptNameFromStudentIdUseCase, private val checkNicknameValidationUseCase: CheckNicknameValidationUseCase, - private val updateStudentUserInfoUseCase: UpdateStudentUserInfoUseCase + private val updateStudentUserInfoUseCase: UpdateStudentUserInfoUseCase, + private val getDeptNamesUseCase: GetDeptNamesUseCase, ) : BaseViewModel() { private val _user = MutableLiveData() val user: LiveData get() = _user - private val _dept = MutableLiveData() - val dept: LiveData get() = _dept + val depts = flow { emit(getDeptNamesUseCase()) } + .combine(user.asFlow()) { depts, user -> depts to user } + .filter{ it.second.isStudent } + .map { it.first to (it.second as User.Student).major } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), listOf() to null) private val _getDeptErrorMessage = MutableLiveData() val getDeptErrorMessage: LiveData get() = _getDeptErrorMessage @@ -64,13 +74,6 @@ class UserInfoEditViewModel @Inject constructor( } } - fun getDept(studentId: String) = viewModelScope.launchIgnoreCancellation { - deptNameFromStudentIdUseCase(studentId).let { (deptName, error) -> - deptName?.let { _dept.value = it } - error?.let { _getDeptErrorMessage.value = error.message } - } - } - fun checkNickname(nickname: String) = viewModelScope.launchWithLoading { _nicknameState.value = NicknameState.newNickname(nickname) diff --git a/koin/src/main/res/layout/activity_user_info_edited.xml b/koin/src/main/res/layout/activity_user_info_edited.xml index 43df57738..7d71315e4 100644 --- a/koin/src/main/res/layout/activity_user_info_edited.xml +++ b/koin/src/main/res/layout/activity_user_info_edited.xml @@ -465,39 +465,33 @@ app:layout_constraintTop_toBottomOf="@id/userinfoedited_student_id_textview" app:layout_constraintVertical_bias="0" /> - - - + app:layout_constraintStart_toStartOf="@id/userinfoedited_edittext_student_id" + app:layout_constraintTop_toTopOf="@id/userinfoedited_major_textview" + app:spinner_arrow_drawable="@drawable/ic_arrow_down_3" + app:spinner_arrow_gravity="end" + app:spinner_arrow_padding="5dp" + app:spinner_divider_color="@color/neutral_500" + app:spinner_divider_show="true" + app:spinner_divider_size="1dp" + app:spinner_item_height="40dp" + app:spinner_popup_background="@color/cardview_light_background" + app:spinner_popup_height="300dp" /> + app:layout_constraintTop_toBottomOf="@id/userinfoedited_spinner_major" /> From 03f3d74f9dc5e76713a4167bdf83912b5e7c53fe Mon Sep 17 00:00:00 2001 From: DoHyeok Kim Date: Fri, 14 Jun 2024 14:19:44 +0900 Subject: [PATCH 130/131] Change UpdateStudentUserInfUseCase to receive major from parameter --- .../user/UpdateStudentUserInfoUseCase.kt | 11 ++++------- .../koin/ui/userinfo/UserInfoEditActivity.kt | 3 ++- .../userinfo/viewmodel/UserInfoEditViewModel.kt | 17 ++++++++++------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/user/UpdateStudentUserInfoUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/user/UpdateStudentUserInfoUseCase.kt index 2d6dd232a..21d98f52c 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/usecase/user/UpdateStudentUserInfoUseCase.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/user/UpdateStudentUserInfoUseCase.kt @@ -7,13 +7,11 @@ import `in`.koreatech.koin.domain.error.user.UserErrorHandler import `in`.koreatech.koin.domain.model.error.ErrorHandler import `in`.koreatech.koin.domain.model.user.Gender import `in`.koreatech.koin.domain.model.user.User -import `in`.koreatech.koin.domain.repository.DeptRepository import `in`.koreatech.koin.domain.repository.UserRepository import `in`.koreatech.koin.domain.util.ext.isValidStudentId import javax.inject.Inject class UpdateStudentUserInfoUseCase @Inject constructor( - private val deptRepository: DeptRepository, private val userRepository: UserRepository, private val userErrorHandler: UserErrorHandler ) { @@ -24,6 +22,7 @@ class UpdateStudentUserInfoUseCase @Inject constructor( separatedPhoneNumber: List?, gender: Gender?, studentId: String, + major: String, checkedEmailValidation: Boolean ): ErrorHandler? { return try { @@ -35,15 +34,13 @@ class UpdateStudentUserInfoUseCase @Inject constructor( throw IllegalStateException(ERROR_USERINFO_GENDER_NOT_SET) } - if(!studentId.isValidStudentId) { + if (!studentId.isValidStudentId) { throw IllegalStateException(ERROR_INVALID_STUDENT_ID) } - val newUser: User = when(beforeUser) { + val newUser: User = when (beforeUser) { User.Anonymous -> throw IllegalAccessException() is User.Student -> { - val deptString = deptRepository.getDeptNameFromDeptCode(studentId.substring(5..6)) - beforeUser.copy( name = name.trim(), nickname = nickname.trim(), @@ -55,7 +52,7 @@ class UpdateStudentUserInfoUseCase @Inject constructor( }, gender = gender, studentNumber = studentId.trim().ifBlank { null }, - major = deptString + major = major ) } } diff --git a/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt b/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt index 978e99881..e63053061 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/userinfo/UserInfoEditActivity.kt @@ -61,7 +61,8 @@ class UserInfoEditActivity : KoinNavigationDrawerActivity() { binding.userinfoeditedRadiobuttonGenderWoman.isChecked -> Gender.Woman else -> null }, - studentId = binding.userinfoeditedEdittextStudentId.textString + studentId = binding.userinfoeditedEdittextStudentId.textString, + major = userinfoeditedSpinnerMajor.selected.text.toString() ) } ) diff --git a/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt b/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt index 4a289cadb..77b99c377 100644 --- a/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt +++ b/koin/src/main/java/in/koreatech/koin/ui/userinfo/viewmodel/UserInfoEditViewModel.kt @@ -18,6 +18,7 @@ import `in`.koreatech.koin.domain.util.onSuccess import `in`.koreatech.koin.ui.userinfo.state.NicknameCheckState import `in`.koreatech.koin.ui.userinfo.state.NicknameState import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flow @@ -36,12 +37,6 @@ class UserInfoEditViewModel @Inject constructor( private val _user = MutableLiveData() val user: LiveData get() = _user - val depts = flow { emit(getDeptNamesUseCase()) } - .combine(user.asFlow()) { depts, user -> depts to user } - .filter{ it.second.isStudent } - .map { it.first to (it.second as User.Student).major } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), listOf() to null) - private val _getDeptErrorMessage = MutableLiveData() val getDeptErrorMessage: LiveData get() = _getDeptErrorMessage @@ -57,6 +52,12 @@ class UserInfoEditViewModel @Inject constructor( private val _userInfoEditedEvent = SingleLiveEvent() val userInfoEditedEvent: LiveData get() = _userInfoEditedEvent + val depts: StateFlow, String?>> = flow { emit(getDeptNamesUseCase()) } + .combine(user.asFlow()) { depts, user -> depts to user } + .filter { it.second.isStudent } + .map { it.first to (it.second as User.Student).major } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), listOf() to null) + fun getUserInfo() = viewModelScope.launchWithLoading { getUserInfoUseCase() .onSuccess { user -> @@ -101,7 +102,8 @@ class UserInfoEditViewModel @Inject constructor( nickname: String, separatedPhoneNumber: List?, gender: Gender?, - studentId: String + studentId: String, + major: String ) { if (isLoading.value == false) { viewModelScope.launchWithLoading { @@ -113,6 +115,7 @@ class UserInfoEditViewModel @Inject constructor( separatedPhoneNumber = separatedPhoneNumber, gender = gender, studentId = studentId, + major = major, checkedEmailValidation = nicknameState.value?.let { it.nickname == nickname && it.isNicknameDuplicated == false } ?: false From a875ff0d0aabd6c5c89a839c166407169d922aec Mon Sep 17 00:00:00 2001 From: Strone Date: Fri, 14 Jun 2024 20:00:14 +0900 Subject: [PATCH 131/131] Fix typing error --- koin/src/main/res/layout/fragment_dining_items.xml | 4 ++-- koin/src/main/res/values/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/koin/src/main/res/layout/fragment_dining_items.xml b/koin/src/main/res/layout/fragment_dining_items.xml index 5c59c8571..b219f71eb 100644 --- a/koin/src/main/res/layout/fragment_dining_items.xml +++ b/koin/src/main/res/layout/fragment_dining_items.xml @@ -24,11 +24,11 @@ tools:listitem="@layout/item_dining" /> 버스 전체보기 회원가입 완료 - 식단 정보는 운영 상황에 따라 변동될 수 있습니다. + 식단 정보는 운영 상황에 따라 변동될 수 있습니다. 세트 메뉴 사이드 메뉴 대표 메뉴