|
1 | 1 | package com.threegap.bitnagil.presentation.emotion |
2 | 2 |
|
3 | 3 | import androidx.lifecycle.SavedStateHandle |
| 4 | +import androidx.lifecycle.ViewModel |
4 | 5 | import androidx.lifecycle.viewModelScope |
5 | 6 | import com.threegap.bitnagil.domain.emotion.usecase.GetEmotionsUseCase |
6 | 7 | import com.threegap.bitnagil.domain.emotion.usecase.RegisterEmotionUseCase |
7 | 8 | import com.threegap.bitnagil.domain.onboarding.usecase.RegisterRecommendOnBoardingRoutinesUseCase |
8 | | -import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel |
9 | 9 | import com.threegap.bitnagil.presentation.emotion.model.EmotionRecommendRoutineUiModel |
10 | 10 | import com.threegap.bitnagil.presentation.emotion.model.EmotionScreenStep |
11 | 11 | import com.threegap.bitnagil.presentation.emotion.model.EmotionUiModel |
12 | | -import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionIntent |
13 | 12 | import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionSideEffect |
14 | 13 | import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionState |
15 | 14 | import dagger.hilt.android.lifecycle.HiltViewModel |
16 | 15 | import kotlinx.coroutines.delay |
17 | 16 | import kotlinx.coroutines.launch |
18 | | -import org.orbitmvi.orbit.syntax.Syntax |
| 17 | +import org.orbitmvi.orbit.Container |
| 18 | +import org.orbitmvi.orbit.ContainerHost |
| 19 | +import org.orbitmvi.orbit.viewmodel.container |
19 | 20 | import javax.inject.Inject |
20 | 21 |
|
21 | 22 | @HiltViewModel |
22 | 23 | class EmotionViewModel @Inject constructor( |
23 | | - savedStateHandle: SavedStateHandle, |
24 | 24 | private val getEmotionsUseCase: GetEmotionsUseCase, |
25 | 25 | private val registerEmotionUseCase: RegisterEmotionUseCase, |
26 | 26 | private val registerRecommendOnBoardingRoutinesUseCase: RegisterRecommendOnBoardingRoutinesUseCase, |
27 | | -) : MviViewModel<EmotionState, EmotionSideEffect, EmotionIntent>( |
28 | | - savedStateHandle = savedStateHandle, |
29 | | - initState = EmotionState.Init, |
30 | | -) { |
| 27 | + savedStateHandle: SavedStateHandle, |
| 28 | +) : ContainerHost<EmotionState, EmotionSideEffect>, ViewModel() { |
| 29 | + |
| 30 | + override val container: Container<EmotionState, EmotionSideEffect> = container(initialState = EmotionState.Init, savedStateHandle = savedStateHandle) |
| 31 | + |
31 | 32 | init { |
32 | 33 | loadEmotions() |
33 | 34 | } |
34 | 35 |
|
35 | | - private fun loadEmotions() { |
36 | | - viewModelScope.launch { |
| 36 | + private fun loadEmotions() = |
| 37 | + intent { |
37 | 38 | getEmotionsUseCase().fold( |
38 | 39 | onSuccess = { emotions -> |
39 | | - sendIntent( |
40 | | - EmotionIntent.EmotionListLoadSuccess(emotionTypeUiModels = emotions.map { EmotionUiModel.fromDomain(it) }), |
41 | | - ) |
| 40 | + reduce { |
| 41 | + state.copy( |
| 42 | + emotionTypeUiModels = emotions.map { EmotionUiModel.fromDomain(it) }, |
| 43 | + isLoading = false, |
| 44 | + ) |
| 45 | + } |
42 | 46 | }, |
43 | 47 | onFailure = { |
44 | 48 | // todo 실패 케이스 정의되면 처리 |
45 | 49 | }, |
46 | 50 | ) |
47 | 51 | } |
48 | | - } |
49 | 52 |
|
50 | | - override suspend fun Syntax<EmotionState, EmotionSideEffect>.reduceState(intent: EmotionIntent, state: EmotionState): EmotionState? { |
51 | | - when (intent) { |
52 | | - is EmotionIntent.EmotionListLoadSuccess -> { |
53 | | - return state.copy( |
54 | | - emotionTypeUiModels = intent.emotionTypeUiModels, |
55 | | - isLoading = false, |
56 | | - ) |
57 | | - } |
58 | | - is EmotionIntent.RegisterEmotionSuccess -> { |
59 | | - return state.copy( |
60 | | - recommendRoutines = intent.recommendRoutines, |
61 | | - step = EmotionScreenStep.RecommendRoutines, |
62 | | - isLoading = false, |
63 | | - showLoadingView = false, |
64 | | - ) |
65 | | - } |
66 | | - EmotionIntent.RegisterEmotionLoading -> { |
67 | | - return state.copy( |
| 53 | + fun selectEmotion(emotionType: String, minimumDelay: Long = 0) = |
| 54 | + intent { |
| 55 | + val isLoading = state.isLoading |
| 56 | + if (isLoading) return@intent |
| 57 | + |
| 58 | + reduce { |
| 59 | + state.copy( |
68 | 60 | isLoading = true, |
69 | 61 | showLoadingView = true, |
70 | 62 | ) |
71 | 63 | } |
72 | | - EmotionIntent.RegisterRecommendRoutinesLoading -> { |
73 | | - return state.copy( |
74 | | - isLoading = true, |
75 | | - ) |
76 | | - } |
77 | | - EmotionIntent.RegisterRecommendRoutinesFailure -> { |
78 | | - return state.copy( |
79 | | - isLoading = false, |
80 | | - ) |
81 | | - } |
82 | | - EmotionIntent.RegisterRecommendRoutinesSuccess -> { |
83 | | - sendSideEffect(EmotionSideEffect.NavigateToBack) |
84 | | - return null |
85 | | - } |
86 | | - EmotionIntent.BackToSelectEmotionStep -> { |
87 | | - return state.copy( |
88 | | - recommendRoutines = listOf(), |
89 | | - step = EmotionScreenStep.Emotion, |
90 | | - isLoading = false, |
91 | | - ) |
92 | | - } |
93 | 64 |
|
94 | | - is EmotionIntent.SelectRecommendRoutine -> { |
95 | | - val selectChangedRecommendRoutines = state.recommendRoutines.map { |
96 | | - if (it.id == intent.recommendRoutineId) { |
97 | | - it.copy(selected = !it.selected) |
98 | | - } else { |
99 | | - it |
100 | | - } |
| 65 | + viewModelScope.launch { |
| 66 | + if (minimumDelay > 0) { |
| 67 | + delay(minimumDelay) |
101 | 68 | } |
102 | | - return state.copy(recommendRoutines = selectChangedRecommendRoutines) |
103 | | - } |
104 | | - |
105 | | - EmotionIntent.NavigateToBack -> { |
106 | | - sendSideEffect(EmotionSideEffect.NavigateToBack) |
107 | | - return null |
108 | | - } |
109 | 69 |
|
110 | | - is EmotionIntent.RegisterEmotionFailure -> { |
111 | | - sendSideEffect(EmotionSideEffect.ShowToast(intent.message)) |
112 | | - sendSideEffect(EmotionSideEffect.NavigateToBack) |
113 | | - return null |
| 70 | + registerEmotionUseCase(emotionType = emotionType).fold( |
| 71 | + onSuccess = { emotionRecommendRoutines -> |
| 72 | + val recommendRoutines = emotionRecommendRoutines.map { EmotionRecommendRoutineUiModel.fromEmotionRecommendRoutine(it) } |
| 73 | + reduce { |
| 74 | + state.copy( |
| 75 | + recommendRoutines = recommendRoutines, |
| 76 | + step = EmotionScreenStep.RecommendRoutines, |
| 77 | + isLoading = false, |
| 78 | + showLoadingView = false, |
| 79 | + ) |
| 80 | + } |
| 81 | + }, |
| 82 | + onFailure = { |
| 83 | + postSideEffect(EmotionSideEffect.ShowToast(message = it.message ?: "에러가 발생했습니다. 잠시 후 시도해주세요.")) |
| 84 | + postSideEffect(EmotionSideEffect.NavigateToBack) |
| 85 | + }, |
| 86 | + ) |
114 | 87 | } |
115 | 88 | } |
116 | | - } |
117 | 89 |
|
118 | | - fun selectEmotion(emotionType: String, minimumDelay: Long = 0) { |
119 | | - val isLoading = stateFlow.value.isLoading |
120 | | - if (isLoading) return |
121 | | - |
122 | | - viewModelScope.launch { |
123 | | - sendIntent(EmotionIntent.RegisterEmotionLoading) |
124 | | - |
125 | | - if (minimumDelay > 0) { |
126 | | - delay(minimumDelay) |
| 90 | + fun selectRecommendRoutine(recommendRoutineId: String) = |
| 91 | + intent { |
| 92 | + val selectChangedRecommendRoutines = state.recommendRoutines.map { |
| 93 | + if (it.id == recommendRoutineId) { |
| 94 | + it.copy(selected = !it.selected) |
| 95 | + } else { |
| 96 | + it |
| 97 | + } |
127 | 98 | } |
128 | | - |
129 | | - registerEmotionUseCase(emotionType = emotionType).fold( |
130 | | - onSuccess = { emotionRecommendRoutines -> |
131 | | - val recommendRoutines = emotionRecommendRoutines.map { EmotionRecommendRoutineUiModel.fromEmotionRecommendRoutine(it) } |
132 | | - sendIntent(EmotionIntent.RegisterEmotionSuccess(recommendRoutines)) |
133 | | - }, |
134 | | - onFailure = { |
135 | | - sendIntent( |
136 | | - EmotionIntent.RegisterEmotionFailure(message = it.message ?: "에러가 발생했습니다. 잠시 후 시도해주세요."), |
137 | | - ) |
138 | | - }, |
139 | | - ) |
140 | | - } |
141 | | - } |
142 | | - |
143 | | - fun selectRecommendRoutine(recommendRoutineId: String) { |
144 | | - viewModelScope.launch { |
145 | | - sendIntent(EmotionIntent.SelectRecommendRoutine(recommendRoutineId)) |
| 99 | + reduce { state.copy(recommendRoutines = selectChangedRecommendRoutines) } |
146 | 100 | } |
147 | | - } |
148 | 101 |
|
149 | | - fun moveToPrev() { |
150 | | - viewModelScope.launch { |
151 | | - val currentState = stateFlow.value |
152 | | - |
153 | | - when (currentState.step) { |
154 | | - EmotionScreenStep.Emotion -> sendIntent(EmotionIntent.NavigateToBack) |
155 | | - EmotionScreenStep.RecommendRoutines -> sendIntent(EmotionIntent.BackToSelectEmotionStep) |
| 102 | + fun moveToPrev() = |
| 103 | + intent { |
| 104 | + when (state.step) { |
| 105 | + EmotionScreenStep.Emotion -> postSideEffect(EmotionSideEffect.NavigateToBack) |
| 106 | + EmotionScreenStep.RecommendRoutines -> reduce { |
| 107 | + state.copy( |
| 108 | + recommendRoutines = listOf(), |
| 109 | + step = EmotionScreenStep.Emotion, |
| 110 | + isLoading = false, |
| 111 | + ) |
| 112 | + } |
156 | 113 | } |
157 | 114 | } |
158 | | - } |
159 | 115 |
|
160 | | - fun registerRecommendRoutines() { |
161 | | - val isLoading = stateFlow.value.isLoading |
162 | | - if (isLoading) return |
163 | | - |
164 | | - viewModelScope.launch { |
165 | | - sendIntent(EmotionIntent.RegisterRecommendRoutinesLoading) |
166 | | - |
167 | | - val currentState = stateFlow.value |
168 | | - val selectedRecommendRoutineIds = currentState.recommendRoutines.filter { it.selected }.map { it.id } |
169 | | - registerRecommendOnBoardingRoutinesUseCase(selectedRecommendRoutineIds).fold( |
170 | | - onSuccess = { |
171 | | - sendIntent(EmotionIntent.RegisterRecommendRoutinesSuccess) |
172 | | - }, |
173 | | - onFailure = { |
174 | | - sendIntent(EmotionIntent.RegisterRecommendRoutinesFailure) |
175 | | - }, |
176 | | - ) |
| 116 | + fun registerRecommendRoutines() = |
| 117 | + intent { |
| 118 | + val isLoading = state.isLoading |
| 119 | + if (isLoading) return@intent |
| 120 | + |
| 121 | + viewModelScope.launch { |
| 122 | + reduce { state.copy(isLoading = true) } |
| 123 | + |
| 124 | + val selectedRecommendRoutineIds = state.recommendRoutines.filter { it.selected }.map { it.id } |
| 125 | + registerRecommendOnBoardingRoutinesUseCase(selectedRecommendRoutineIds).fold( |
| 126 | + onSuccess = { |
| 127 | + postSideEffect(EmotionSideEffect.NavigateToBack) |
| 128 | + }, |
| 129 | + onFailure = { |
| 130 | + reduce { state.copy(isLoading = false) } |
| 131 | + }, |
| 132 | + ) |
| 133 | + } |
177 | 134 | } |
178 | | - } |
179 | 135 | } |
0 commit comments