From 9d75efc36ef9d282726eededd9e95b195c814352 Mon Sep 17 00:00:00 2001 From: GunHyung Ham Date: Tue, 13 Feb 2024 23:38:26 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[feat]=20:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?UI=20=EB=AA=A8=EB=8D=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/moya/funch/uimodel/ProfileUiModel.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt diff --git a/feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt b/feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt new file mode 100644 index 00000000..096f27c2 --- /dev/null +++ b/feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt @@ -0,0 +1,17 @@ +package com.moya.funch.uimodel + +import com.moya.funch.entity.Blood +import com.moya.funch.entity.Club +import com.moya.funch.entity.Job + +data class ProfileUiModel( + val name: String = "", + val job: Job = Job.IDLE, + val clubs: List = emptyList(), + val eOrI: MbtiItem = MbtiItem.E, + val nOrS: MbtiItem = MbtiItem.N, + val tOrF: MbtiItem = MbtiItem.T, + val jOrP: MbtiItem = MbtiItem.J, + val bloodType: Blood = Blood.A, + val subway: String = "" +) From 2e01cb3855e50b1253ddff20c5b5e457d43494be Mon Sep 17 00:00:00 2001 From: GunHyung Ham Date: Tue, 13 Feb 2024 23:39:05 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[feat]=20:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/moya/funch/CreatePofileViewModel.kt | 127 ++++++++++-------- .../com/moya/funch/CreateProflieScreen.kt | 98 ++++++++++---- 2 files changed, 145 insertions(+), 80 deletions(-) diff --git a/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt b/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt index c84be10d..90d5e53e 100644 --- a/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt +++ b/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt @@ -9,29 +9,57 @@ import com.moya.funch.entity.Mbti import com.moya.funch.entity.SubwayStation import com.moya.funch.entity.profile.Profile import com.moya.funch.uimodel.MbtiItem +import com.moya.funch.uimodel.ProfileUiModel +import com.moya.funch.usecase.CreateUserProfileUseCase import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -data class MbtiState( - val eOrI: MbtiItem = MbtiItem.E, - val nOrS: MbtiItem = MbtiItem.N, - val tOrF: MbtiItem = MbtiItem.T, - val jOrP: MbtiItem = MbtiItem.J -) +internal sealed class CreateProfileUiState { + data object Loading : CreateProfileUiState() + data object Enabled : CreateProfileUiState() + data object Disabled : CreateProfileUiState() +} + +internal sealed class CreateProfileEvent { + data object NavigateToHome : CreateProfileEvent() + data class ShowError(val message: String) : CreateProfileEvent() +} +@OptIn(ExperimentalCoroutinesApi::class) @HiltViewModel internal class CreateProfileViewModel @Inject constructor( - // private val createUserProfileUseCase: CreateUserProfileUseCase + private val createUserProfileUseCase: CreateUserProfileUseCase ) : ViewModel() { - private val _profile = MutableStateFlow(Profile()) + private val _profile = MutableStateFlow(ProfileUiModel()) val profile = _profile.asStateFlow() - private val mbtiState = MutableStateFlow(MbtiState()) + private val _uiState = MutableStateFlow(CreateProfileUiState.Disabled) + val uiState: StateFlow = _profile.mapLatest { uiModel -> + if (isCreateProfile(uiModel)) { + CreateProfileUiState.Enabled + } else { + CreateProfileUiState.Disabled + } + }.stateIn( + viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreateProfileUiState.Disabled + ) + + private val _event = MutableSharedFlow() + val event = _event.asSharedFlow() fun setNickname(nickname: String) { _profile.value = _profile.value.copy(name = nickname) @@ -48,66 +76,57 @@ internal class CreateProfileViewModel @Inject constructor( } fun setBloodType(blood: Blood) { - _profile.value = _profile.value.copy(blood = blood) + _profile.value = _profile.value.copy(bloodType = blood) } fun setMbti(item: MbtiItem) { viewModelScope.launch { when (item) { - MbtiItem.E, MbtiItem.I -> mbtiState.update { uiModel -> uiModel.copy(eOrI = item) } - MbtiItem.N, MbtiItem.S -> mbtiState.update { uiModel -> uiModel.copy(nOrS = item) } - MbtiItem.T, MbtiItem.F -> mbtiState.update { uiModel -> uiModel.copy(tOrF = item) } - MbtiItem.J, MbtiItem.P -> mbtiState.update { uiModel -> uiModel.copy(jOrP = item) } + MbtiItem.E, MbtiItem.I -> _profile.update { uiModel -> uiModel.copy(eOrI = item) } + MbtiItem.N, MbtiItem.S -> _profile.update { uiModel -> uiModel.copy(nOrS = item) } + MbtiItem.T, MbtiItem.F -> _profile.update { uiModel -> uiModel.copy(tOrF = item) } + MbtiItem.J, MbtiItem.P -> _profile.update { uiModel -> uiModel.copy(jOrP = item) } } - _profile.value = _profile.value.copy( - mbti = Mbti.valueOf( - mbtiState.value.eOrI.name + - mbtiState.value.nOrS.name + - mbtiState.value.tOrF.name + - mbtiState.value.jOrP.name - ) - ) - } - } - - fun isSelectMbti(mbtiItem: MbtiItem): Boolean { - return when (mbtiItem) { - MbtiItem.E -> mbtiState.value.eOrI == MbtiItem.E - MbtiItem.I -> mbtiState.value.eOrI == MbtiItem.I - MbtiItem.N -> mbtiState.value.nOrS == MbtiItem.N - MbtiItem.S -> mbtiState.value.nOrS == MbtiItem.S - MbtiItem.T -> mbtiState.value.tOrF == MbtiItem.T - MbtiItem.F -> mbtiState.value.tOrF == MbtiItem.F - MbtiItem.J -> mbtiState.value.jOrP == MbtiItem.J - MbtiItem.P -> mbtiState.value.jOrP == MbtiItem.P } } fun setSubwayName(subway: String) { - _profile.value = _profile.value.copy( - subways = - listOf( - SubwayStation(name = subway) - ) - ) + _profile.value = _profile.value.copy(subway = subway) } - fun isCreateProfile(profile: Profile): Boolean { - return profile.job != Job.IDLE && + private fun isCreateProfile(profile: ProfileUiModel): Boolean { + return profile.name.isNotBlank() && + profile.job != Job.IDLE && profile.clubs.isNotEmpty() && - profile.mbti != Mbti.IDLE && - profile.blood != Blood.IDLE && - profile.name.isNotBlank() && - profile.subways[0].name.isNotBlank() + profile.subway.isNotBlank() } fun createProfile() { viewModelScope.launch { - /*createUserProfileUseCase(_profile.value).onSuccess { - // TODO : navigate to main + _uiState.update { CreateProfileUiState.Loading } + val profile = Profile( + name = _profile.value.name, + job = _profile.value.job, + clubs = _profile.value.clubs, + mbti = Mbti.valueOf( + _profile.value.eOrI.name + + _profile.value.nOrS.name + + _profile.value.tOrF.name + + _profile.value.jOrP.name + ), + blood = _profile.value.bloodType, + subways = listOf( + SubwayStation( + name = _profile.value.subway + ) + ) + ) + createUserProfileUseCase(profile).onSuccess { + _event.emit(CreateProfileEvent.NavigateToHome) }.onFailure { - // TODO : show error - }*/ + _uiState.update { CreateProfileUiState.Enabled } + _event.emit(CreateProfileEvent.ShowError(it.message ?: "Error")) + } } } } @@ -119,9 +138,3 @@ private fun List.toggleElement(element: T): List { this + element } } - -internal sealed class CreateProfileState { - data object Loading : CreateProfileState() - data object Success : CreateProfileState() - data object Error : CreateProfileState() -} diff --git a/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt b/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt index 4e92734c..263baa57 100644 --- a/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt +++ b/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt @@ -3,6 +3,7 @@ package com.moya.funch import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.layout.Arrangement @@ -35,6 +36,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalFocusManager @@ -57,7 +59,6 @@ import com.moya.funch.component.FunchSmallLabel import com.moya.funch.entity.Blood import com.moya.funch.entity.Club import com.moya.funch.entity.Job -import com.moya.funch.entity.profile.Profile import com.moya.funch.icon.FunchIconAsset import com.moya.funch.profile.R import com.moya.funch.theme.FunchTheme @@ -73,43 +74,75 @@ import com.moya.funch.ui.FunchDropDownMenu import com.moya.funch.ui.FunchTopBar import com.moya.funch.uimodel.MbtiItem import com.moya.funch.uimodel.ProfileLabel +import com.moya.funch.uimodel.ProfileUiModel @Composable internal fun CreateProfileRoute(onNavigateToHome: () -> Unit, viewModel: CreateProfileViewModel = hiltViewModel()) { val profile by viewModel.profile.collectAsStateWithLifecycle() + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + var isEnabledButton by remember { mutableStateOf(false) } + + LaunchedEffect(uiState) { + when (uiState) { + is CreateProfileUiState.Enabled -> { + isEnabledButton = true + } + + is CreateProfileUiState.Disabled -> { + isEnabledButton = false + } + + is CreateProfileUiState.Loading -> { + // @Gun Hyung TODO : 로딩 UI 디자인시스템에 정의하고 그리기 + } + } + } + + LaunchedEffect(Unit) { + viewModel.event.collect { event -> + when (event) { + is CreateProfileEvent.NavigateToHome -> { + onNavigateToHome() + } + + is CreateProfileEvent.ShowError -> { + // @Gun Hyung TODO : 에러 메시지 호출 + } + } + } + } CreateProfileScreen( profile = profile, - isSelectMbti = viewModel::isSelectMbti, - isCreateProfile = viewModel::isCreateProfile, + isCreateProfile = isEnabledButton, onSelectJob = viewModel::setJob, onSelectClub = viewModel::setClub, onSelectMbti = viewModel::setMbti, onSelectBloodType = viewModel::setBloodType, onNicknameChange = viewModel::setNickname, onSubwayStationChange = viewModel::setSubwayName, - onNavigateToHome = onNavigateToHome, + onCreateProfile = viewModel::createProfile, onSendFeedback = {} ) } @Composable fun CreateProfileScreen( - profile: Profile, - isSelectMbti: (MbtiItem) -> Boolean, - isCreateProfile: (Profile) -> Boolean, + profile: ProfileUiModel, + isCreateProfile: Boolean, onSelectJob: (Job) -> Unit, onSelectClub: (Club) -> Unit, onSelectMbti: (MbtiItem) -> Unit, onSelectBloodType: (Blood) -> Unit, onNicknameChange: (String) -> Unit, onSubwayStationChange: (String) -> Unit, - onNavigateToHome: () -> Unit, + onCreateProfile: () -> Unit, onSendFeedback: () -> Unit ) { val scrollState = rememberScrollState() val backgroundColor = LocalBackgroundTheme.current.color var isKeyboardVisible by remember { mutableStateOf(false) } + val focusManager = LocalFocusManager.current Scaffold( topBar = { @@ -123,8 +156,8 @@ fun CreateProfileScreen( if (!isKeyboardVisible) { BottomBar( backgroundColor = backgroundColor, - isCreateProfile = isCreateProfile(profile), - onNavigateToHome = onNavigateToHome + isCreateProfile = isCreateProfile, + onCreateProfile = onCreateProfile ) } }, @@ -132,6 +165,11 @@ fun CreateProfileScreen( ) { padding -> Column( modifier = Modifier + .pointerInput(Unit) { + detectTapGestures(onTap = { + focusManager.clearFocus() + }) + } .fillMaxSize() .verticalScroll(state = scrollState) .padding(padding) @@ -161,10 +199,10 @@ fun CreateProfileScreen( ) { JobRow(profile = profile, onSelected = onSelectJob) ClubRow(onSelectClub = onSelectClub) - MbtiRow(onSelectMbti = onSelectMbti, isSelectMbti = isSelectMbti) + MbtiRow(profile = profile, onSelectMbti = onSelectMbti) BooldTypeRow(onSelectBloodType = onSelectBloodType) SubwayRow( - subwayStation = profile.subways[0].name, + subwayStation = profile.subway, onSubwayStationChange = onSubwayStationChange, isKeyboardVisible = { isKeyboardVisible = it } ) @@ -174,8 +212,8 @@ fun CreateProfileScreen( if (isKeyboardVisible) { BottomBar( backgroundColor = backgroundColor, - isCreateProfile = isCreateProfile(profile), - onNavigateToHome = onNavigateToHome + isCreateProfile = isCreateProfile, + onCreateProfile = onCreateProfile ) } } @@ -232,7 +270,7 @@ private fun NicknameRow(nickname: String, onNicknameChange: (String) -> Unit, is @OptIn(ExperimentalLayoutApi::class) @Composable -private fun JobRow(profile: Profile, onSelected: (Job) -> Unit) { +private fun JobRow(profile: ProfileUiModel, onSelected: (Job) -> Unit) { Row { FunchSmallLabel(text = ProfileLabel.JOB.labelName) FlowRow( @@ -327,14 +365,20 @@ private fun ClubRow(onSelectClub: (Club) -> Unit) { } @Composable -private fun MbtiRow(onSelectMbti: (MbtiItem) -> Unit, isSelectMbti: (MbtiItem) -> Boolean) { +private fun MbtiRow(profile: ProfileUiModel, onSelectMbti: (MbtiItem) -> Unit) { + val eOrI = profile.eOrI + val nOrS = profile.nOrS + val tOrF = profile.tOrF + val jOrP = profile.jOrP + val currentMbti = listOf(eOrI, nOrS, tOrF, jOrP) + Row { FunchSmallLabel(text = ProfileLabel.MBTI.labelName) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - MbtiItem.entries.chunked(2).forEach { pair -> + MbtiItem.entries.chunked(2).forEachIndexed { i, pair -> Column( modifier = Modifier .background(color = Gray800, shape = FunchTheme.shapes.medium) @@ -343,7 +387,7 @@ private fun MbtiRow(onSelectMbti: (MbtiItem) -> Unit, isSelectMbti: (MbtiItem) - pair.forEach { mbti -> MbtiButton( mbtiItem = mbti, - isSelected = isSelectMbti(mbti), + isSelected = currentMbti[i] == mbti, onSelected = { onSelectMbti(it) } @@ -457,7 +501,7 @@ private fun SubwayRow( } @Composable -private fun BottomBar(backgroundColor: Color, isCreateProfile: Boolean, onNavigateToHome: () -> Unit) { +private fun BottomBar(backgroundColor: Color, isCreateProfile: Boolean, onCreateProfile: () -> Unit) { Box( modifier = Modifier .background(color = backgroundColor) @@ -472,7 +516,7 @@ private fun BottomBar(backgroundColor: Color, isCreateProfile: Boolean, onNaviga enabled = isCreateProfile, modifier = Modifier.fillMaxWidth(), buttonType = FunchButtonType.Full, - onClick = onNavigateToHome, + onClick = onCreateProfile, text = stringResource(id = R.string.bottom_button_title) ) } @@ -493,9 +537,17 @@ private fun Preview1() { modifier = Modifier.fillMaxSize(), color = backgroundColor ) { - CreateProfileRoute( - onNavigateToHome = {}, - viewModel = CreateProfileViewModel() + CreateProfileScreen( + profile = ProfileUiModel(), + isCreateProfile = false, + onSelectJob = {}, + onSelectClub = {}, + onSelectMbti = {}, + onSelectBloodType = {}, + onNicknameChange = {}, + onSubwayStationChange = {}, + onCreateProfile = {}, + onSendFeedback = {} ) } } From d1851cb99209f66e8e32331609ffa2b5f8f5f021 Mon Sep 17 00:00:00 2001 From: GunHyung Ham Date: Wed, 14 Feb 2024 00:41:54 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[feat]=20:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B2=84=ED=8A=BC=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20UiModel=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/moya/funch/uimodel/ProfileUiModel.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt b/feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt index 096f27c2..abfa8408 100644 --- a/feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt +++ b/feature/profile/src/main/java/com/moya/funch/uimodel/ProfileUiModel.kt @@ -14,4 +14,10 @@ data class ProfileUiModel( val jOrP: MbtiItem = MbtiItem.J, val bloodType: Blood = Blood.A, val subway: String = "" -) +) { + val isButtonEnabled: Boolean + get() = name.isNotBlank() && + job != Job.IDLE && + clubs.isNotEmpty() && + subway.isNotBlank() +} From a8dc9e33441c07d16872634aaef4e468479816e1 Mon Sep 17 00:00:00 2001 From: GunHyung Ham Date: Wed, 14 Feb 2024 00:42:43 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[refactor]=20:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EB=B7=B0=EB=AA=A8=EB=8D=B8=20profile=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20uiState=EB=A1=9C=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/moya/funch/CreatePofileViewModel.kt | 110 +++++++++--------- .../com/moya/funch/CreateProflieScreen.kt | 38 +++--- 2 files changed, 73 insertions(+), 75 deletions(-) diff --git a/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt b/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt index 90d5e53e..3e9d355b 100644 --- a/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt +++ b/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt @@ -13,118 +13,120 @@ import com.moya.funch.uimodel.ProfileUiModel import com.moya.funch.usecase.CreateUserProfileUseCase import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -internal sealed class CreateProfileUiState { - data object Loading : CreateProfileUiState() - data object Enabled : CreateProfileUiState() - data object Disabled : CreateProfileUiState() -} +data class CreateProfileUiState( + val profile: ProfileUiModel = ProfileUiModel(), + val isLoading: Boolean = false +) internal sealed class CreateProfileEvent { data object NavigateToHome : CreateProfileEvent() data class ShowError(val message: String) : CreateProfileEvent() } -@OptIn(ExperimentalCoroutinesApi::class) @HiltViewModel internal class CreateProfileViewModel @Inject constructor( private val createUserProfileUseCase: CreateUserProfileUseCase ) : ViewModel() { - - private val _profile = MutableStateFlow(ProfileUiModel()) - val profile = _profile.asStateFlow() - - private val _uiState = MutableStateFlow(CreateProfileUiState.Disabled) - val uiState: StateFlow = _profile.mapLatest { uiModel -> - if (isCreateProfile(uiModel)) { - CreateProfileUiState.Enabled - } else { - CreateProfileUiState.Disabled - } - }.stateIn( - viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreateProfileUiState.Disabled - ) + private val _uiState = MutableStateFlow(CreateProfileUiState()) + val uiState: StateFlow = _uiState.asStateFlow() private val _event = MutableSharedFlow() val event = _event.asSharedFlow() fun setNickname(nickname: String) { - _profile.value = _profile.value.copy(name = nickname) + _uiState.value = _uiState.value.copy( + profile = _uiState.value.profile.copy( + name = nickname + ) + ) } fun setJob(job: Job) { - _profile.value = _profile.value.copy(job = job) + _uiState.value = _uiState.value.copy( + profile = _uiState.value.profile.copy( + job = job + ) + ) } fun setClub(club: Club) { - _profile.value = _profile.value.copy( - clubs = _profile.value.clubs.toggleElement(club) + _uiState.value = _uiState.value.copy( + profile = _uiState.value.profile.copy( + clubs = _uiState.value.profile.clubs.toggleElement(club) + ) ) } fun setBloodType(blood: Blood) { - _profile.value = _profile.value.copy(bloodType = blood) + _uiState.value = _uiState.value.copy( + profile = _uiState.value.profile.copy( + bloodType = blood + ) + ) } fun setMbti(item: MbtiItem) { viewModelScope.launch { when (item) { - MbtiItem.E, MbtiItem.I -> _profile.update { uiModel -> uiModel.copy(eOrI = item) } - MbtiItem.N, MbtiItem.S -> _profile.update { uiModel -> uiModel.copy(nOrS = item) } - MbtiItem.T, MbtiItem.F -> _profile.update { uiModel -> uiModel.copy(tOrF = item) } - MbtiItem.J, MbtiItem.P -> _profile.update { uiModel -> uiModel.copy(jOrP = item) } + MbtiItem.E, MbtiItem.I -> _uiState.update { uiModel -> + uiModel.copy(profile = uiModel.profile.copy(eOrI = item)) + } + + MbtiItem.N, MbtiItem.S -> _uiState.update { uiModel -> + uiModel.copy(profile = uiModel.profile.copy(nOrS = item)) + } + + MbtiItem.T, MbtiItem.F -> _uiState.update { uiModel -> + uiModel.copy(profile = uiModel.profile.copy(tOrF = item)) + } + + MbtiItem.J, MbtiItem.P -> _uiState.update { uiModel -> + uiModel.copy(profile = uiModel.profile.copy(jOrP = item)) + } } } } fun setSubwayName(subway: String) { - _profile.value = _profile.value.copy(subway = subway) - } - - private fun isCreateProfile(profile: ProfileUiModel): Boolean { - return profile.name.isNotBlank() && - profile.job != Job.IDLE && - profile.clubs.isNotEmpty() && - profile.subway.isNotBlank() + _uiState.value = _uiState.value.copy( + profile = _uiState.value.profile.copy( + subway = subway + ) + ) } fun createProfile() { viewModelScope.launch { - _uiState.update { CreateProfileUiState.Loading } + _uiState.update { currentState -> currentState.copy(isLoading = true) } val profile = Profile( - name = _profile.value.name, - job = _profile.value.job, - clubs = _profile.value.clubs, + name = _uiState.value.profile.name, + job = _uiState.value.profile.job, + clubs = _uiState.value.profile.clubs, mbti = Mbti.valueOf( - _profile.value.eOrI.name + - _profile.value.nOrS.name + - _profile.value.tOrF.name + - _profile.value.jOrP.name + _uiState.value.profile.eOrI.name + + _uiState.value.profile.nOrS.name + + _uiState.value.profile.tOrF.name + + _uiState.value.profile.jOrP.name ), - blood = _profile.value.bloodType, + blood = _uiState.value.profile.bloodType, subways = listOf( SubwayStation( - name = _profile.value.subway + name = _uiState.value.profile.subway ) ) ) createUserProfileUseCase(profile).onSuccess { _event.emit(CreateProfileEvent.NavigateToHome) }.onFailure { - _uiState.update { CreateProfileUiState.Enabled } + _uiState.update { currentState -> currentState.copy(isLoading = false) } _event.emit(CreateProfileEvent.ShowError(it.message ?: "Error")) } } diff --git a/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt b/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt index 263baa57..8a5234a1 100644 --- a/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt +++ b/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt @@ -78,25 +78,7 @@ import com.moya.funch.uimodel.ProfileUiModel @Composable internal fun CreateProfileRoute(onNavigateToHome: () -> Unit, viewModel: CreateProfileViewModel = hiltViewModel()) { - val profile by viewModel.profile.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle() - var isEnabledButton by remember { mutableStateOf(false) } - - LaunchedEffect(uiState) { - when (uiState) { - is CreateProfileUiState.Enabled -> { - isEnabledButton = true - } - - is CreateProfileUiState.Disabled -> { - isEnabledButton = false - } - - is CreateProfileUiState.Loading -> { - // @Gun Hyung TODO : 로딩 UI 디자인시스템에 정의하고 그리기 - } - } - } LaunchedEffect(Unit) { viewModel.event.collect { event -> @@ -104,7 +86,6 @@ internal fun CreateProfileRoute(onNavigateToHome: () -> Unit, viewModel: CreateP is CreateProfileEvent.NavigateToHome -> { onNavigateToHome() } - is CreateProfileEvent.ShowError -> { // @Gun Hyung TODO : 에러 메시지 호출 } @@ -113,8 +94,8 @@ internal fun CreateProfileRoute(onNavigateToHome: () -> Unit, viewModel: CreateP } CreateProfileScreen( - profile = profile, - isCreateProfile = isEnabledButton, + profile = uiState.profile, + isCreateProfile = uiState.profile.isButtonEnabled, onSelectJob = viewModel::setJob, onSelectClub = viewModel::setClub, onSelectMbti = viewModel::setMbti, @@ -124,6 +105,21 @@ internal fun CreateProfileRoute(onNavigateToHome: () -> Unit, viewModel: CreateP onCreateProfile = viewModel::createProfile, onSendFeedback = {} ) + + if (uiState.isLoading) { + Box( + modifier = Modifier + .fillMaxSize() + .clickable( + onClick = { }, + indication = null, + interactionSource = remember { MutableInteractionSource() } + ), + contentAlignment = Alignment.Center + ) { + // @Gun Hyung TODO : 로딩 UI 디자인시스템에 정의하고 그리기 + } + } } @Composable