From 554bd029fdb4e068d5661d2bbf37cd7501ea5af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=EC=8A=B9=EC=9A=B0?= Date: Thu, 9 May 2024 22:56:52 +0900 Subject: [PATCH] =?UTF-8?q?#29=20refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=20bottomSheet,=20checkbox=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/component/BottomSheets.kt | 23 +++++------ .../ui/screen/login/LoginScreen.kt | 13 +------ .../ui/screen/login/LoginUiState.kt | 30 ++++++++++++++ .../ui/screen/login/LoginViewModel.kt | 39 +++++-------------- .../login/participate/ParticipateScreen.kt | 2 +- .../login/participate/ParticipateUiState.kt | 22 +++++++++-- .../login/participate/ParticipateViewModel.kt | 15 ++++--- .../main/changeGarden/ChangeGardenScreen.kt | 17 ++++---- .../changeGarden/ChangeGardenViewModel.kt | 16 ++++---- .../presentation/ui/state/BottomSheetState.kt | 2 +- .../presentation/ui/state/EditTextState.kt | 5 ++- 11 files changed, 102 insertions(+), 82 deletions(-) diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/component/BottomSheets.kt b/presentation/src/main/java/io/tuttut/presentation/ui/component/BottomSheets.kt index a2ed438..b8a5126 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/component/BottomSheets.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/component/BottomSheets.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp import io.tuttut.data.model.dto.CropsInfo import io.tuttut.presentation.R import io.tuttut.presentation.theme.screenHorizontalPadding +import io.tuttut.presentation.ui.screen.login.PolicySheetState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -291,25 +292,19 @@ fun HarvestBottomSheet( @OptIn(ExperimentalMaterial3Api::class) @Composable fun PolicyBottomSheet( - showSheet: Boolean, - isLoading: Boolean, - policyChecked: Boolean, - personalChecked: Boolean, - onPolicyCheckedChange: (Boolean) -> Unit, - onPersonalCheckedChange: (Boolean) -> Unit, + policySheetState: PolicySheetState, showPolicy: () -> Unit, showPersonal: () -> Unit, onAgreement: () -> Unit, - onDismissRequest: () -> Unit, ) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val properties = ModalBottomSheetDefaults.properties(shouldDismissOnBackPress = false) TutTutBottomSheet( - showSheet = showSheet, + showSheet = policySheetState.showSheet, sheetState = sheetState, containerColor = MaterialTheme.colorScheme.background, properties = properties, - onDismissRequest = onDismissRequest + onDismissRequest = policySheetState::dismiss ) { Column( modifier = Modifier @@ -323,21 +318,21 @@ fun PolicyBottomSheet( Spacer(modifier = Modifier.height(20.dp)) PolicyButton( name = stringResource(id = R.string.service_policy_agreement), - checked = policyChecked, - onCheckedChange = { if (!isLoading) onPolicyCheckedChange(it) }, + checked = policySheetState.policyState.checked, + onCheckedChange = { if (!policySheetState.isLoading()) policySheetState.policyState.onCheckedChange(it) }, showPolicy = showPolicy ) Spacer(modifier = Modifier.height(10.dp)) PolicyButton( name = stringResource(id = R.string.service_policy_agreement), - checked = personalChecked, - onCheckedChange = { if (!isLoading) onPersonalCheckedChange(it) }, + checked = policySheetState.personalState.checked, + onCheckedChange = { if (!policySheetState.isLoading()) policySheetState.personalState.onCheckedChange(it) }, showPolicy = showPersonal ) Spacer(modifier = Modifier.height(30.dp)) TutTutButton( text = stringResource(id = R.string.continue_with_agree), - isLoading = isLoading, + isLoading = policySheetState.isLoading(), onClick = onAgreement ) } diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginScreen.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginScreen.kt index e000ad2..11ef432 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginScreen.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginScreen.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -32,8 +31,6 @@ fun LoginRoute( onShowSnackBar: suspend (String, String?) -> Boolean, viewModel: LoginViewModel = hiltViewModel() ) { - val uiState by viewModel.uiState - val policyUiState by viewModel.policyUiState val context = LocalContext.current val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartIntentSenderForResult(), @@ -41,20 +38,14 @@ fun LoginRoute( ) LoginScreen( modifier = modifier, - isLoading = uiState == LoginUiState.Loading, + isLoading = viewModel.uiState == LoginUiState.Loading, onLogin = { viewModel.onLogin(launcher) } ) PolicyBottomSheet( - showSheet = viewModel.showPolicySheet, - isLoading = policyUiState == PolicyUiState.Loading, - policyChecked = viewModel.policyChecked, - personalChecked = viewModel.personalChecked, - onPolicyCheckedChange = { viewModel.policyChecked = it }, - onPersonalCheckedChange = { viewModel.personalChecked = it }, + policySheetState = viewModel.policySheetState, showPolicy = { viewModel.openBrowser(context, SERVICE_POLICY_URL) }, showPersonal = { viewModel.openBrowser(context, PERSONAL_INFO_POLICY_URL) }, onAgreement = { viewModel.join(onShowSnackBar) }, - onDismissRequest = { viewModel.showPolicySheet = false } ) } diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginUiState.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginUiState.kt index b05ae80..d436ea8 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginUiState.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginUiState.kt @@ -1,5 +1,11 @@ package io.tuttut.presentation.ui.screen.login +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import io.tuttut.presentation.ui.state.BottomSheetState +import io.tuttut.presentation.ui.state.CheckBoxState + sealed interface LoginUiState { data object Loading : LoginUiState data object Nothing : LoginUiState @@ -8,4 +14,28 @@ sealed interface LoginUiState { sealed interface PolicyUiState { data object Loading : PolicyUiState data object Nothing : PolicyUiState +} + +class PolicySheetState : BottomSheetState() { + var uiState by mutableStateOf(PolicyUiState.Nothing) + val policyState = CheckBoxState() + val personalState = CheckBoxState() + + fun onContinue() { + uiState = PolicyUiState.Loading + policyState.onCheckedChange(true) + personalState.onCheckedChange(true) + } + + override fun dismiss() { + super.dismiss() + policyState.onCheckedChange(false) + personalState.onCheckedChange(false) + } + + fun toNothing() { + uiState = PolicyUiState.Nothing + } + + fun isLoading() = uiState == PolicyUiState.Loading } \ No newline at end of file diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginViewModel.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginViewModel.kt index c45f36d..f98b91e 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginViewModel.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/LoginViewModel.kt @@ -6,7 +6,6 @@ import android.os.Build import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult import androidx.activity.result.IntentSenderRequest -import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -29,26 +28,12 @@ class LoginViewModel @Inject constructor( private val prefs: PreferenceUtil, private val linkUtil: LinkUtil, ) : BaseViewModel() { - private val _uiState = mutableStateOf(Nothing) - val uiState: State = _uiState - + var uiState by mutableStateOf(Nothing) + val policySheetState = PolicySheetState() private val _userData = MutableStateFlow(UserData()) - private val _policyUiState = mutableStateOf(PolicyUiState.Nothing) - val policyUiState: State = _policyUiState - - var showPolicySheet by mutableStateOf(false) - var policyChecked by mutableStateOf(false) - var personalChecked by mutableStateOf(false) - - init { - showPolicySheet = false - policyChecked = false - personalChecked = false - } - fun onLogin(launcher: ManagedActivityResultLauncher) { - _uiState.value = Loading + uiState = Loading if (Build.VERSION.SDK_INT < Build.VERSION_CODES.DONUT) return viewModelScope.launch { val intentSender = authClient.signIn() @@ -66,7 +51,7 @@ class LoginViewModel @Inject constructor( onShowSnackBar: suspend (String, String?) -> Boolean ) { if (result.resultCode != RESULT_OK) { - _uiState.value = Nothing + uiState = Nothing return } viewModelScope.launch { @@ -74,8 +59,8 @@ class LoginViewModel @Inject constructor( _userData.value = userData authRepo.getUserResult(userData.userId).collect { when(it) { - Result.Loading -> _uiState.value = Loading - Result.NotFound -> { showPolicySheet = true } + Result.Loading -> uiState = Loading + Result.NotFound -> policySheetState.show() is Result.Error -> onShowSnackBar("회원 확인에 실패했어요", null) is Result.Success -> { if (it.data.gardenId.isEmpty()) { @@ -89,7 +74,7 @@ class LoginViewModel @Inject constructor( } } } - _uiState.value = Nothing + uiState = Nothing } } } @@ -97,19 +82,15 @@ class LoginViewModel @Inject constructor( fun openBrowser(context: Context, url: String) = linkUtil.openBrowser(context, url) fun join(onShowSnackBar: suspend (String, String?) -> Boolean) { - _policyUiState.value = PolicyUiState.Loading - policyChecked = true - personalChecked = true + policySheetState.onContinue() viewModelScope.launch { authRepo.join(_userData.value).collect { when (it) { is Result.Error -> onShowSnackBar("가입에 실패했어요", null) - is Result.Success -> { - showPolicySheet = false - } + is Result.Success -> policySheetState.dismiss() else -> {} } - _policyUiState.value = PolicyUiState.Nothing + policySheetState.toNothing() } } } diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateScreen.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateScreen.kt index 27f65f5..4bb1f4b 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateScreen.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateScreen.kt @@ -115,7 +115,7 @@ internal fun ParticipateScreen( TutTutButton( text = stringResource(id = R.string.confirm), isLoading = isLoading, - enabled = (tabState.isNew && nameState.isValidate()) || (!tabState.isNew && codeState.isValidate()), + enabled = nameState.isValidate() && codeState.isValidate(), onClick = onNext ) } diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateUiState.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateUiState.kt index a6a7c3a..ff65a64 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateUiState.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateUiState.kt @@ -41,12 +41,25 @@ class ParticipateDialogState : DialogState() { } } -class ParticipateCodeState : EditTextState("", 6) { +class ParticipateNameState( + private val isNew: Boolean, + maxLength: Int +) : EditTextState("", maxLength) { + override fun isValidate(): Boolean { + return if (isNew) super.isValidate() + else true + } +} + +class ParticipateCodeState( + private val isNew: Boolean, + private val codeLength: Int +) : EditTextState("", codeLength) { var supportingText by mutableStateOf("") var supportingTextType by mutableStateOf(SupportingTextType.NONE) override fun typeText(text: String) { - if (text.length <= 6) { + if (text.length <= codeLength) { typedText = text if (supportingTextType == SupportingTextType.ERROR) { supportingText = "" @@ -60,5 +73,8 @@ class ParticipateCodeState : EditTextState("", 6) { supportingTextType = SupportingTextType.ERROR } - override fun isValidate(): Boolean = typedText.trim().length == 6 + override fun isValidate(): Boolean { + return if (!isNew) getTrimText().length == codeLength + else true + } } \ No newline at end of file diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateViewModel.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateViewModel.kt index 3bc0107..4db3b42 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateViewModel.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/login/participate/ParticipateViewModel.kt @@ -10,7 +10,6 @@ import io.tuttut.presentation.ui.screen.login.participate.ParticipateUiState.* import io.tuttut.data.repository.garden.GardenRepository import io.tuttut.presentation.base.BaseViewModel import io.tuttut.presentation.model.PreferenceUtil -import io.tuttut.presentation.ui.state.EditTextState import io.tuttut.presentation.util.getCurrentDate import kotlinx.coroutines.launch import javax.inject.Inject @@ -23,8 +22,14 @@ class ParticipateViewModel @Inject constructor( private var _uiState by mutableStateOf(Nothing) val uiState = _uiState val tabState = ParticipateTabState() - val nameState = EditTextState(maxLength = 10) - val codeState = ParticipateCodeState() + val nameState = ParticipateNameState( + isNew = tabState.isNew, + maxLength = 10 + ) + val codeState = ParticipateCodeState( + isNew = tabState.isNew, + codeLength = 6 + ) val dialogState = ParticipateDialogState() fun onNext(hideKeyboard: () -> Unit, moveNext: () -> Unit, onShowSnackBar: suspend (String, String?) -> Boolean) { @@ -37,7 +42,7 @@ class ParticipateViewModel @Inject constructor( private suspend fun createGarden(moveNext: () -> Unit, onShowSnackBar: suspend (String, String?) -> Boolean) { val userId = authClient.getSignedInUser()?.userId ?: return - gardenRepo.createGarden(userId, nameState.typedText.trim(), getCurrentDate()).collect { + gardenRepo.createGarden(userId, nameState.getTrimText(), getCurrentDate()).collect { when (it) { Result.Loading -> _uiState = Loading is Result.Error -> onShowSnackBar("텃밭 생성에 실패했어요", null) @@ -52,7 +57,7 @@ class ParticipateViewModel @Inject constructor( } private suspend fun checkGardenExist(onShowSnackBar: suspend (String, String?) -> Boolean) { - gardenRepo.checkGardenExist(codeState.typedText.trim()).collect { + gardenRepo.checkGardenExist(codeState.getTrimText()).collect { when (it) { Result.Loading -> _uiState = Loading Result.NotFound -> codeState.showNotFoundError() diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenScreen.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenScreen.kt index 742ddbc..2cb3b1c 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenScreen.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenScreen.kt @@ -17,6 +17,7 @@ import io.tuttut.presentation.ui.component.TutTutButton import io.tuttut.presentation.ui.component.TutTutLabel import io.tuttut.presentation.ui.component.TutTutTextField import io.tuttut.presentation.ui.component.TutTutTopBar +import io.tuttut.presentation.ui.state.IEditTextState import io.tuttut.presentation.util.withScreenPadding @Composable @@ -29,9 +30,7 @@ fun ChangeGardenRoute( ChangeGardenScreen( modifier = modifier, uiState = viewModel.uiState, - typedGardenName = viewModel.nameState.typedText, - typeGardenName = viewModel.nameState::typeText, - resetGardenName = viewModel.nameState::resetText, + nameState = viewModel.nameState, onSubmit = { viewModel.onSubmit(onBack, onShowSnackBar) }, onBack = onBack ) @@ -42,9 +41,7 @@ fun ChangeGardenRoute( internal fun ChangeGardenScreen( modifier: Modifier, uiState: ChangeGardenUiState, - typedGardenName: String, - typeGardenName: (String) -> Unit, - resetGardenName: () -> Unit, + nameState: IEditTextState, onSubmit: () -> Unit, onBack: () -> Unit, ) { @@ -63,11 +60,11 @@ internal fun ChangeGardenScreen( title = stringResource(id = R.string.profile_name) ) TutTutTextField( - value = typedGardenName, + value = nameState.typedText, placeHolder = stringResource(id = R.string.garden_name_placeholder), supportingText = stringResource(id = R.string.text_limit), - onValueChange = typeGardenName, - onResetValue = resetGardenName + onValueChange = nameState::typeText, + onResetValue = nameState::resetText ) } Box( @@ -79,7 +76,7 @@ internal fun ChangeGardenScreen( TutTutButton( text = stringResource(id = R.string.change), isLoading = uiState == ChangeGardenUiState.Loading, - enabled = typedGardenName.trim().length in 1 .. 10, + enabled = nameState.isValidate(), onClick = onSubmit ) } diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenViewModel.kt b/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenViewModel.kt index 6d0921e..adb2d10 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenViewModel.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/screen/main/changeGarden/ChangeGardenViewModel.kt @@ -20,27 +20,29 @@ class ChangeGardenViewModel @Inject constructor( private val currentGarden = gardenRepo.currentGarden.value private val originName = currentGarden.name - private var _uiState by mutableStateOf(Nothing) - val uiState = _uiState - val nameState = EditTextState(initText = currentGarden.name, maxLength = 10) + var uiState by mutableStateOf(Nothing) + val nameState = EditTextState( + initText = currentGarden.name, + maxLength = 10 + ) fun onSubmit(moveBack: () -> Unit, onShowSnackBar: suspend (String, String?) -> Boolean) { - if (nameState.typedText.trim() == originName) { + if (nameState.getTrimText() == originName) { moveBack() return } viewModelScope.launch { - gardenRepo.updateGardenInfo(currentGarden.copy(name = nameState.typedText.trim())).collect { + gardenRepo.updateGardenInfo(currentGarden.copy(name = nameState.getTrimText())).collect { when (it) { is Result.Error -> onShowSnackBar("변경에 실패했어요", null) - Result.Loading -> _uiState = Loading + Result.Loading -> uiState = Loading is Result.Success -> { moveBack() onShowSnackBar("텃밭 정보를 변경했어요", null) } else -> {} } - _uiState = Nothing + uiState = Nothing } } } diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/state/BottomSheetState.kt b/presentation/src/main/java/io/tuttut/presentation/ui/state/BottomSheetState.kt index 050fa54..0cecf03 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/state/BottomSheetState.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/state/BottomSheetState.kt @@ -10,7 +10,7 @@ interface IBottomSheetState { fun dismiss() } -class BottomSheetState : IBottomSheetState { +open class BottomSheetState : IBottomSheetState { override var showSheet by mutableStateOf(false) override fun show() { diff --git a/presentation/src/main/java/io/tuttut/presentation/ui/state/EditTextState.kt b/presentation/src/main/java/io/tuttut/presentation/ui/state/EditTextState.kt index 4365824..70d2e07 100644 --- a/presentation/src/main/java/io/tuttut/presentation/ui/state/EditTextState.kt +++ b/presentation/src/main/java/io/tuttut/presentation/ui/state/EditTextState.kt @@ -8,6 +8,7 @@ interface IEditTextState { var typedText: String fun typeText(text: String) fun resetText() + fun getTrimText(): String fun isValidate(): Boolean } @@ -27,5 +28,7 @@ open class EditTextState( typedText = "" } - override fun isValidate() = typedText.trim().isNotEmpty() + override fun getTrimText() = typedText.trim() + + override fun isValidate() = getTrimText().isNotEmpty() } \ No newline at end of file