Skip to content

Commit b0d72a7

Browse files
committed
Refactor: HomeViewModel 에서 MviViewModel 구현 제거
- homeState, homeSideEffect 수정 - homeIntent 제거
1 parent a94e1d5 commit b0d72a7

5 files changed

Lines changed: 94 additions & 179 deletions

File tree

presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt

Lines changed: 25 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,17 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
2222
import androidx.compose.ui.tooling.preview.Preview
2323
import androidx.compose.ui.unit.dp
2424
import androidx.hilt.navigation.compose.hiltViewModel
25-
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2625
import com.threegap.bitnagil.designsystem.BitnagilTheme
2726
import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple
28-
import com.threegap.bitnagil.presentation.common.flow.collectAsEffect
2927
import com.threegap.bitnagil.presentation.home.component.template.CollapsibleHomeHeader
3028
import com.threegap.bitnagil.presentation.home.component.template.EmptyRoutineView
3129
import com.threegap.bitnagil.presentation.home.component.template.RoutineSection
3230
import com.threegap.bitnagil.presentation.home.component.template.WeeklyDatePicker
33-
import com.threegap.bitnagil.presentation.home.model.HomeIntent
3431
import com.threegap.bitnagil.presentation.home.model.HomeSideEffect
3532
import com.threegap.bitnagil.presentation.home.model.HomeState
3633
import com.threegap.bitnagil.presentation.home.util.rememberCollapsibleHeaderState
34+
import org.orbitmvi.orbit.compose.collectAsState
35+
import org.orbitmvi.orbit.compose.collectSideEffect
3736
import java.time.LocalDate
3837

3938
@Composable
@@ -44,57 +43,28 @@ fun HomeScreenContainer(
4443
navigateToRoutineList: (String) -> Unit,
4544
viewModel: HomeViewModel = hiltViewModel(),
4645
) {
47-
val uiState by viewModel.stateFlow.collectAsStateWithLifecycle()
46+
val uiState by viewModel.collectAsState()
4847

49-
viewModel.sideEffectFlow.collectAsEffect { sideEffect ->
48+
viewModel.collectSideEffect { sideEffect ->
5049
when (sideEffect) {
51-
is HomeSideEffect.NavigateToGuide -> {
52-
navigateToGuide()
53-
}
54-
55-
is HomeSideEffect.NavigateToRegisterRoutine -> {
56-
navigateToRegisterRoutine()
57-
}
58-
59-
is HomeSideEffect.NavigateToEmotion -> {
60-
navigateToEmotion()
61-
}
62-
63-
is HomeSideEffect.NavigateToRoutineList -> {
64-
navigateToRoutineList(sideEffect.selectedDate)
65-
}
50+
is HomeSideEffect.NavigateToGuide -> navigateToGuide()
51+
is HomeSideEffect.NavigateToRegisterRoutine -> navigateToRegisterRoutine()
52+
is HomeSideEffect.NavigateToEmotion -> navigateToEmotion()
53+
is HomeSideEffect.NavigateToRoutineList -> navigateToRoutineList(sideEffect.selectedDate)
6654
}
6755
}
6856

6957
HomeScreen(
7058
uiState = uiState,
71-
onDateSelect = { date ->
72-
viewModel.sendIntent(HomeIntent.OnDateSelect(date))
73-
},
74-
onPreviousWeekClick = {
75-
viewModel.sendIntent(HomeIntent.OnPreviousWeekClick)
76-
},
77-
onNextWeekClick = {
78-
viewModel.sendIntent(HomeIntent.OnNextWeekClick)
79-
},
80-
onRoutineCompletionToggle = { routineId ->
81-
viewModel.toggleRoutineCompletion(routineId)
82-
},
83-
onSubRoutineCompletionToggle = { routineId, subRoutineIndex ->
84-
viewModel.toggleSubRoutineCompletion(routineId, subRoutineIndex)
85-
},
86-
onHelpClick = {
87-
viewModel.sendIntent(HomeIntent.OnHelpClick)
88-
},
89-
onRegisterRoutineClick = {
90-
viewModel.sendIntent(HomeIntent.OnRegisterRoutineClick)
91-
},
92-
onRegisterEmotionClick = {
93-
viewModel.sendIntent(HomeIntent.OnRegisterEmotionClick)
94-
},
95-
onShowMoreRoutinesClick = {
96-
viewModel.sendIntent((HomeIntent.OnShowMoreRoutinesClick))
97-
},
59+
onDateSelect = viewModel::selectDate,
60+
onPreviousWeekClick = viewModel::getPreviousWeek,
61+
onNextWeekClick = viewModel::getNextWeek,
62+
onRoutineToggle = viewModel::toggleRoutine,
63+
onSubRoutineToggle = viewModel::toggleSubRoutine,
64+
onHelpClick = viewModel::navigateToGuide,
65+
onRegisterRoutineClick = viewModel::navigateToRegisterRoutine,
66+
onRegisterEmotionClick = viewModel::navigateToEmotion,
67+
onShowMoreRoutinesClick = viewModel::navigateToRoutineList
9868
)
9969
}
10070

@@ -104,8 +74,8 @@ private fun HomeScreen(
10474
onDateSelect: (LocalDate) -> Unit,
10575
onPreviousWeekClick: () -> Unit,
10676
onNextWeekClick: () -> Unit,
107-
onRoutineCompletionToggle: (String) -> Unit,
108-
onSubRoutineCompletionToggle: (String, Int) -> Unit,
77+
onRoutineToggle: (String) -> Unit,
78+
onSubRoutineToggle: (String, Int) -> Unit,
10979
onHelpClick: () -> Unit,
11080
onRegisterRoutineClick: () -> Unit,
11181
onRegisterEmotionClick: () -> Unit,
@@ -125,7 +95,7 @@ private fun HomeScreen(
12595
WeeklyDatePicker(
12696
selectedDate = uiState.selectedDate,
12797
weeklyDates = uiState.currentWeeks,
128-
routines = uiState.routines,
98+
routines = uiState.routineSchedule,
12999
onDateSelect = onDateSelect,
130100
onPreviousWeekClick = onPreviousWeekClick,
131101
onNextWeekClick = onNextWeekClick,
@@ -187,9 +157,9 @@ private fun HomeScreen(
187157
) { routine ->
188158
RoutineSection(
189159
routine = routine,
190-
onRoutineToggle = { onRoutineCompletionToggle(routine.id) },
160+
onRoutineToggle = { onRoutineToggle(routine.id) },
191161
onSubRoutineToggle = { subRoutineIndex ->
192-
onSubRoutineCompletionToggle(routine.id, subRoutineIndex)
162+
onSubRoutineToggle(routine.id, subRoutineIndex)
193163
},
194164
)
195165
}
@@ -211,12 +181,12 @@ private fun HomeScreen(
211181
@Composable
212182
private fun HomeScreenPreview() {
213183
HomeScreen(
214-
uiState = HomeState(),
184+
uiState = HomeState.INIT,
215185
onDateSelect = {},
216186
onPreviousWeekClick = {},
217187
onNextWeekClick = {},
218-
onRoutineCompletionToggle = { _ -> },
219-
onSubRoutineCompletionToggle = { _, _ -> },
188+
onRoutineToggle = { _ -> },
189+
onSubRoutineToggle = { _, _ -> },
220190
onHelpClick = {},
221191
onRegisterRoutineClick = {},
222192
onRegisterEmotionClick = {},

presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt

Lines changed: 50 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.threegap.bitnagil.presentation.home
22

33
import android.util.Log
44
import androidx.lifecycle.SavedStateHandle
5+
import androidx.lifecycle.ViewModel
56
import androidx.lifecycle.viewModelScope
67
import com.threegap.bitnagil.domain.emotion.usecase.FetchTodayEmotionUseCase
78
import com.threegap.bitnagil.domain.emotion.usecase.GetEmotionChangeEventFlowUseCase
@@ -13,7 +14,6 @@ import com.threegap.bitnagil.domain.routine.usecase.RoutineCompletionUseCase
1314
import com.threegap.bitnagil.domain.user.usecase.FetchUserProfileUseCase
1415
import com.threegap.bitnagil.domain.writeroutine.usecase.GetWriteRoutineEventFlowUseCase
1516
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel
16-
import com.threegap.bitnagil.presentation.home.model.HomeIntent
1717
import com.threegap.bitnagil.presentation.home.model.HomeSideEffect
1818
import com.threegap.bitnagil.presentation.home.model.HomeState
1919
import com.threegap.bitnagil.presentation.home.model.RoutineScheduleUiModel
@@ -28,24 +28,27 @@ import kotlinx.coroutines.flow.distinctUntilChanged
2828
import kotlinx.coroutines.flow.drop
2929
import kotlinx.coroutines.flow.map
3030
import kotlinx.coroutines.launch
31+
import org.orbitmvi.orbit.Container
32+
import org.orbitmvi.orbit.ContainerHost
3133
import org.orbitmvi.orbit.syntax.Syntax
34+
import org.orbitmvi.orbit.viewmodel.container
3235
import java.time.LocalDate
3336
import javax.inject.Inject
3437

3538
@HiltViewModel
3639
class HomeViewModel @Inject constructor(
37-
savedStateHandle: SavedStateHandle,
3840
private val fetchWeeklyRoutinesUseCase: FetchWeeklyRoutinesUseCase,
3941
private val fetchUserProfileUseCase: FetchUserProfileUseCase,
4042
private val fetchTodayEmotionUseCase: FetchTodayEmotionUseCase,
4143
private val routineCompletionUseCase: RoutineCompletionUseCase,
4244
private val getWriteRoutineEventFlowUseCase: GetWriteRoutineEventFlowUseCase,
4345
private val getEmotionChangeEventFlowUseCase: GetEmotionChangeEventFlowUseCase,
4446
private val getOnBoardingRecommendRoutineEventFlowUseCase: GetOnBoardingRecommendRoutineEventFlowUseCase,
45-
) : MviViewModel<HomeState, HomeSideEffect, HomeIntent>(
46-
initState = HomeState(),
47-
savedStateHandle = savedStateHandle,
48-
) {
47+
private val toggleRoutineUseCase: ToggleRoutineUseCase,
48+
) : ContainerHost<HomeState, HomeSideEffect>, ViewModel() {
49+
50+
override val container: Container<HomeState, HomeSideEffect> = container(initialState = HomeState.INIT)
51+
4952
private val pendingChangesByDate = mutableMapOf<String, MutableList<RoutineCompletionInfo>>()
5053
private val backupStatesByDate = mutableMapOf<String, RoutineScheduleUiModel>()
5154
private val routineSyncTrigger = MutableSharedFlow<LocalDate>()
@@ -56,92 +59,54 @@ class HomeViewModel @Inject constructor(
5659
observeRecommendRoutineEvent()
5760
observeWeekChanges()
5861
observeRoutineUpdates()
59-
fetchWeeklyRoutines(stateFlow.value.currentWeeks)
62+
fetchWeeklyRoutines(container.stateFlow.value.currentWeeks)
6063
fetchUserProfile()
6164
fetchTodayEmotion(LocalDate.now())
6265
}
6366

64-
override suspend fun Syntax<HomeState, HomeSideEffect>.reduceState(
65-
intent: HomeIntent,
66-
state: HomeState,
67-
): HomeState? {
68-
val newState = when (intent) {
69-
is HomeIntent.UpdateLoading -> {
70-
state.copy(isLoading = intent.isLoading)
71-
}
67+
fun selectDate(data: LocalDate) {
68+
intent {
69+
reduce { state.copy(selectedDate = data) }
70+
}
71+
}
7272

73-
is HomeIntent.LoadUserProfile -> {
74-
state.copy(userNickname = intent.nickname)
75-
}
73+
fun getNextWeek() {
74+
intent {
75+
val newWeek = state.selectedDate.plusWeeks(1).getCurrentWeekDays()
76+
reduce { state.copy(currentWeeks = newWeek, selectedDate = newWeek.first()) }
77+
}
78+
}
7679

77-
is HomeIntent.LoadWeeklyRoutines -> {
78-
state.copy(routines = intent.routines)
79-
}
80+
fun getPreviousWeek() {
81+
intent {
82+
val newWeek = state.selectedDate.minusWeeks(1).getCurrentWeekDays()
83+
reduce { state.copy(currentWeeks = newWeek, selectedDate = newWeek.first()) }
84+
}
85+
}
8086

81-
is HomeIntent.OnDateSelect -> {
82-
state.copy(selectedDate = intent.date)
83-
}
8487

85-
is HomeIntent.OnNextWeekClick -> {
86-
val newWeek = state.selectedDate.plusWeeks(1).getCurrentWeekDays()
87-
state.copy(
88-
currentWeeks = newWeek,
89-
selectedDate = newWeek.first(),
90-
)
91-
}
9288

93-
is HomeIntent.OnPreviousWeekClick -> {
94-
val newWeek = state.selectedDate.minusWeeks(1).getCurrentWeekDays()
95-
state.copy(
96-
currentWeeks = newWeek,
97-
selectedDate = newWeek.first(),
98-
)
99-
}
10089

101-
is HomeIntent.OnRoutineCompletionToggle -> {
102-
updateMainRoutine(state, intent.routineId)
103-
}
10490

105-
is HomeIntent.OnSubRoutineCompletionToggle -> {
106-
updateSubRoutine(state, intent.routineId, intent.subRoutineIndex)
10791
}
10892

109-
is HomeIntent.LoadTodayEmotion -> {
110-
state.copy(todayEmotion = intent.emotion)
111-
}
11293

113-
is HomeIntent.OnHelpClick -> {
114-
sendSideEffect(HomeSideEffect.NavigateToGuide)
115-
null
11694
}
11795

118-
is HomeIntent.OnRegisterEmotionClick -> {
119-
sendSideEffect(HomeSideEffect.NavigateToEmotion)
120-
null
12196
}
12297

123-
is HomeIntent.OnRegisterRoutineClick -> {
124-
sendSideEffect(HomeSideEffect.NavigateToRegisterRoutine)
125-
null
12698
}
12799

128-
is HomeIntent.OnShowMoreRoutinesClick -> {
129-
val selectedDate = stateFlow.value.selectedDate.toString()
130-
sendSideEffect(HomeSideEffect.NavigateToRoutineList(selectedDate))
131-
null
132100
}
133101

134-
is HomeIntent.RoutineToggleCompletionFailure -> {
135-
null
136102
}
137103
}
138-
return newState
139104
}
140105

141106
private fun observeWriteRoutineEvent() {
142107
viewModelScope.launch {
143108
getWriteRoutineEventFlowUseCase().collect {
144-
fetchWeeklyRoutines(stateFlow.value.currentWeeks)
109+
fetchWeeklyRoutines(container.stateFlow.value.currentWeeks)
145110
}
146111
}
147112
}
@@ -158,7 +123,7 @@ class HomeViewModel @Inject constructor(
158123
private fun observeRecommendRoutineEvent() {
159124
viewModelScope.launch {
160125
getOnBoardingRecommendRoutineEventFlowUseCase().collect {
161-
fetchWeeklyRoutines(stateFlow.value.currentWeeks)
126+
fetchWeeklyRoutines(container.stateFlow.value.currentWeeks)
162127
}
163128
}
164129
}
@@ -189,50 +154,45 @@ class HomeViewModel @Inject constructor(
189154
}
190155

191156
private fun fetchUserProfile() {
192-
sendIntent(HomeIntent.UpdateLoading(true))
193-
viewModelScope.launch {
157+
intent {
158+
reduce { state.copy(isLoading = true) }
194159
fetchUserProfileUseCase().fold(
195160
onSuccess = {
196-
sendIntent(HomeIntent.LoadUserProfile(it.nickname))
197-
sendIntent(HomeIntent.UpdateLoading(false))
161+
reduce { state.copy(userNickname = it.nickname, isLoading = false) }
198162
},
199-
onFailure = { error ->
200-
Log.e("HomeViewModel", "유저 정보 가져오기 실패: ${error.message}")
201-
sendIntent(HomeIntent.UpdateLoading(false))
163+
onFailure = {
164+
Log.e("HomeViewModel", "유저 정보 가져오기 실패: ${it.message}")
165+
reduce { state.copy(isLoading = false) }
202166
},
203167
)
204168
}
205169
}
206170

207171
private fun fetchWeeklyRoutines(currentWeeks: List<LocalDate>) {
208-
sendIntent(HomeIntent.UpdateLoading(true))
209-
val startDate = currentWeeks.first().toString()
210-
val endDate = currentWeeks.last().toString()
211-
viewModelScope.launch {
212-
fetchWeeklyRoutinesUseCase(startDate, endDate).fold(
213-
onSuccess = { routines ->
214-
sendIntent(HomeIntent.LoadWeeklyRoutines(routines.toUiModel()))
215-
sendIntent(HomeIntent.UpdateLoading(false))
172+
intent {
173+
reduce { state.copy(isLoading = true) }
174+
fetchWeeklyRoutinesUseCase(currentWeeks).fold(
175+
onSuccess = {
176+
reduce { state.copy(isLoading = false, routineSchedule = it.toUiModel()) }
216177
},
217-
onFailure = { error ->
218-
Log.e("HomeViewModel", "루틴 가져오기 실패: ${error.message}")
219-
sendIntent(HomeIntent.UpdateLoading(false))
178+
onFailure = {
179+
Log.e("HomeViewModel", "루틴 가져오기 실패: ${it.message}")
180+
reduce { state.copy(isLoading = false) }
220181
},
221182
)
222183
}
223184
}
224185

225186
private fun fetchTodayEmotion(currentDate: LocalDate) {
226-
sendIntent(HomeIntent.UpdateLoading(true))
227-
viewModelScope.launch {
187+
intent {
188+
reduce { state.copy(isLoading = true) }
228189
fetchTodayEmotionUseCase(currentDate.toString()).fold(
229-
onSuccess = { todayEmotion ->
230-
sendIntent(HomeIntent.LoadTodayEmotion(todayEmotion?.toUiModel()))
231-
sendIntent(HomeIntent.UpdateLoading(false))
190+
onSuccess = {
191+
reduce { state.copy(isLoading = false, todayEmotion = it?.toUiModel()) }
232192
},
233-
onFailure = { error ->
234-
Log.e("HomeViewModel", "나의 감정 실패: ${error.message}")
235-
sendIntent(HomeIntent.UpdateLoading(false))
193+
onFailure = {
194+
Log.e("HomeViewModel", "나의 감정 실패: ${it.message}")
195+
reduce { state.copy(isLoading = false) }
236196
},
237197
)
238198
}

0 commit comments

Comments
 (0)