From 7d6bff0a0e9838b7d5375ef3ca87f6ca37c9c2e6 Mon Sep 17 00:00:00 2001 From: kangyein9892 Date: Tue, 17 Dec 2024 23:22:46 +0900 Subject: [PATCH] =?UTF-8?q?mod/#12:=20my=20=EB=A7=88=EC=9D=B4=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20baseviewmodel,=20uiEvent,=20uiSideEffect,=20uiState?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/and/presentation/my/MyScreen.kt | 23 ++++++----- .../and/presentation/my/contract/MyEvent.kt | 8 ++++ .../sopt/and/presentation/my/model/MyState.kt | 3 +- .../my/sideeffect/MySideEffect.kt | 5 ++- .../presentation/my/viewmodel/MyViewModel.kt | 40 ++++++++++--------- 5 files changed, 46 insertions(+), 33 deletions(-) create mode 100644 presentation/src/main/java/org/sopt/and/presentation/my/contract/MyEvent.kt diff --git a/presentation/src/main/java/org/sopt/and/presentation/my/MyScreen.kt b/presentation/src/main/java/org/sopt/and/presentation/my/MyScreen.kt index 08512b4..953e755 100644 --- a/presentation/src/main/java/org/sopt/and/presentation/my/MyScreen.kt +++ b/presentation/src/main/java/org/sopt/and/presentation/my/MyScreen.kt @@ -41,6 +41,7 @@ import org.sopt.and.presentation.component.ContentsView import org.sopt.and.presentation.component.PairTextView import org.sopt.and.presentation.delegate.NetworkState import org.sopt.and.presentation.extension.noRippleClickable +import org.sopt.and.presentation.my.contract.MyEvent import org.sopt.and.presentation.my.sideeffect.MySideEffect import org.sopt.and.presentation.my.viewmodel.MyViewModel import org.sopt.and.presentation.ui.theme.FirstGrey @@ -55,33 +56,33 @@ fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = hiltViewModel() ) { - LaunchedEffect(viewModel.state) { - if(viewModel.state.value.hobby == DEFAULT_STRING) viewModel.getMyHobby() + LaunchedEffect(viewModel.uiState) { + if(viewModel.uiState.value.hobby == DEFAULT_STRING) viewModel.getMyHobby() } Column( modifier = modifier.fillMaxSize() ) { - val state by viewModel.state.collectAsStateWithLifecycle() + val state by viewModel.uiState.collectAsStateWithLifecycle() val lifecycleOwner = LocalLifecycleOwner.current val context = LocalContext.current val snackBarHostState = remember { SnackbarHostState() } - LaunchedEffect(viewModel.intent, lifecycleOwner){ - viewModel.intent.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) - .collect{ intent -> - when(intent) { - MySideEffect.Logout -> { + LaunchedEffect(viewModel.sideEffect, lifecycleOwner){ + viewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) + .collect{ sideEffect -> + when(sideEffect) { + MySideEffect.NavigateToSignIn -> { viewModel.clearUserPreference() snackBarHostState.showSnackbar( message = context.getString(R.string.my_logout_text) ) onLogout() } - is MySideEffect.SnackBar -> snackBarHostState.showSnackbar(context.getString(intent.message)) - is MySideEffect.SnackBarText -> snackBarHostState.showSnackbar(intent.message) + is MySideEffect.SnackBar -> snackBarHostState.showSnackbar(context.getString(sideEffect.message)) + is MySideEffect.SnackBarText -> snackBarHostState.showSnackbar(sideEffect.message) } } } @@ -183,7 +184,7 @@ fun MyScreen( .align(Alignment.CenterHorizontally) .noRippleClickable( onClick = { - viewModel.onLogOutButtonClick() + viewModel.setEvent(MyEvent.OnLogoutButtonClick) } ) ) diff --git a/presentation/src/main/java/org/sopt/and/presentation/my/contract/MyEvent.kt b/presentation/src/main/java/org/sopt/and/presentation/my/contract/MyEvent.kt new file mode 100644 index 0000000..d405a4b --- /dev/null +++ b/presentation/src/main/java/org/sopt/and/presentation/my/contract/MyEvent.kt @@ -0,0 +1,8 @@ +package org.sopt.and.presentation.my.contract + +import org.sopt.and.presentation.util.base.UiEvent + +sealed class MyEvent: UiEvent { + data class GetMyHobby(val token: String): MyEvent() + data object OnLogoutButtonClick: MyEvent() +} \ No newline at end of file diff --git a/presentation/src/main/java/org/sopt/and/presentation/my/model/MyState.kt b/presentation/src/main/java/org/sopt/and/presentation/my/model/MyState.kt index 75c7c8b..bd0772b 100644 --- a/presentation/src/main/java/org/sopt/and/presentation/my/model/MyState.kt +++ b/presentation/src/main/java/org/sopt/and/presentation/my/model/MyState.kt @@ -1,7 +1,8 @@ package org.sopt.and.presentation.my.model import org.sopt.and.presentation.util.KeyUtil.DEFAULT_STRING +import org.sopt.and.presentation.util.base.UiState data class MyState( val hobby: String = DEFAULT_STRING -) \ No newline at end of file +): UiState \ No newline at end of file diff --git a/presentation/src/main/java/org/sopt/and/presentation/my/sideeffect/MySideEffect.kt b/presentation/src/main/java/org/sopt/and/presentation/my/sideeffect/MySideEffect.kt index b466f70..931ccc6 100644 --- a/presentation/src/main/java/org/sopt/and/presentation/my/sideeffect/MySideEffect.kt +++ b/presentation/src/main/java/org/sopt/and/presentation/my/sideeffect/MySideEffect.kt @@ -1,9 +1,10 @@ package org.sopt.and.presentation.my.sideeffect import androidx.annotation.StringRes +import org.sopt.and.presentation.util.base.UiSideEffect -sealed class MySideEffect { +sealed class MySideEffect: UiSideEffect { data class SnackBar(@StringRes val message: Int): MySideEffect() data class SnackBarText(val message: String): MySideEffect() - data object Logout: MySideEffect() + data object NavigateToSignIn: MySideEffect() } \ No newline at end of file diff --git a/presentation/src/main/java/org/sopt/and/presentation/my/viewmodel/MyViewModel.kt b/presentation/src/main/java/org/sopt/and/presentation/my/viewmodel/MyViewModel.kt index 15ef96d..1f8af0b 100644 --- a/presentation/src/main/java/org/sopt/and/presentation/my/viewmodel/MyViewModel.kt +++ b/presentation/src/main/java/org/sopt/and/presentation/my/viewmodel/MyViewModel.kt @@ -1,59 +1,61 @@ package org.sopt.and.presentation.my.viewmodel -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.sopt.and.domain.exception.onError import org.sopt.and.domain.exception.onSuccess import org.sopt.and.domain.repository.UserRepository import org.sopt.and.presentation.delegate.NetworkDelegate +import org.sopt.and.presentation.my.contract.MyEvent import org.sopt.and.presentation.my.model.MyState import org.sopt.and.presentation.my.sideeffect.MySideEffect -import org.sopt.and.presentation.sign.signin.sideeffect.SignInSideEffect +import org.sopt.and.presentation.util.base.BaseViewModel import javax.inject.Inject @HiltViewModel class MyViewModel @Inject constructor( private val userRepository: UserRepository, private val networkDelegate: NetworkDelegate -): ViewModel() { +): BaseViewModel() { - private val _state = MutableStateFlow(MyState()) - val state = _state.asStateFlow() + override fun createInitialState(): MyState = MyState() - private val _intent = MutableSharedFlow() - val intent = _intent.asSharedFlow() + override suspend fun handleEvent(event: MyEvent) { + when(event) { + MyEvent.OnLogoutButtonClick -> { + navigateToSignIn() + } + is MyEvent.GetMyHobby -> { + getMyHobby() + } + } + } val networkState get() = networkDelegate.networkState fun getMyHobby() = viewModelScope.launch { val token = userRepository.getToken() userRepository.getMyHobby(token).onSuccess { result -> - _state.update { - it.copy(hobby = result.hobby) - } + setState { copy(hobby = result.hobby) } networkDelegate.handleNetworkSuccess() }.onError { networkDelegate.handleGetMyHobbyError(it) } } - fun onLogOutButtonClick() = viewModelScope.launch { - _intent.emit(MySideEffect.Logout) + private fun navigateToSignIn() = viewModelScope.launch { + delay(100) + setSideEffect(MySideEffect.NavigateToSignIn) } fun clearUserPreference() = viewModelScope.launch { userRepository.clearUserPreference() } - suspend fun handleMyIntentError(message: String) { - _intent.emit(MySideEffect.SnackBarText(message)) + fun handleMyIntentError(message: String) = viewModelScope.launch { + setSideEffect(MySideEffect.SnackBarText(message)) } } \ No newline at end of file