Skip to content

Commit e96a6e7

Browse files
committed
Refactor: DailyEmotion 데이터 흐름을 Flow 기반 관찰 방식으로 변경
1 parent 55c8f13 commit e96a6e7

9 files changed

Lines changed: 71 additions & 87 deletions

File tree

data/src/main/java/com/threegap/bitnagil/data/emotion/model/response/DailyEmotionResponse.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
44
import com.threegap.bitnagil.domain.emotion.model.EmotionMarbleType
55
import kotlinx.serialization.SerialName
66
import kotlinx.serialization.Serializable
7+
import java.time.LocalDate
78

89
@Serializable
910
data class DailyEmotionResponse(
@@ -23,4 +24,5 @@ fun DailyEmotionResponse.toDomain(): DailyEmotion =
2324
name = emotionMarbleName,
2425
imageUrl = imageUrl,
2526
homeMessage = emotionMarbleHomeMessage,
27+
fetchedDate = LocalDate.now(),
2628
)

data/src/main/java/com/threegap/bitnagil/data/emotion/repositoryImpl/EmotionRepositoryImpl.kt

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@ import com.threegap.bitnagil.data.emotion.datasource.EmotionDataSource
44
import com.threegap.bitnagil.data.emotion.model.response.toDomain
55
import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
66
import com.threegap.bitnagil.domain.emotion.model.Emotion
7-
import com.threegap.bitnagil.domain.emotion.model.EmotionChangeEvent
87
import com.threegap.bitnagil.domain.emotion.model.EmotionRecommendRoutine
98
import com.threegap.bitnagil.domain.emotion.repository.EmotionRepository
109
import kotlinx.coroutines.flow.Flow
11-
import kotlinx.coroutines.flow.MutableSharedFlow
12-
import kotlinx.coroutines.flow.asSharedFlow
10+
import kotlinx.coroutines.flow.MutableStateFlow
11+
import kotlinx.coroutines.flow.onSubscription
12+
import java.time.LocalDate
1313
import javax.inject.Inject
1414

1515
class EmotionRepositoryImpl @Inject constructor(
1616
private val emotionDataSource: EmotionDataSource,
1717
) : EmotionRepository {
18+
19+
private val _dailyEmotionFlow = MutableStateFlow(DailyEmotion.INIT)
20+
override val dailyEmotionFlow: Flow<DailyEmotion> = _dailyEmotionFlow
21+
.onSubscription {
22+
if (_dailyEmotionFlow.value.isStale) fetchDailyEmotion()
23+
}
24+
1825
override suspend fun getEmotions(): Result<List<Emotion>> {
1926
return emotionDataSource.getEmotions().map { response ->
2027
response.map { it.toDomain() }
@@ -23,20 +30,18 @@ class EmotionRepositoryImpl @Inject constructor(
2330

2431
override suspend fun registerEmotion(emotionMarbleType: String): Result<List<EmotionRecommendRoutine>> {
2532
return emotionDataSource.registerEmotion(emotionMarbleType).map {
26-
it.recommendedRoutines.map {
27-
emotionRecommendedRoutineDto ->
33+
it.recommendedRoutines.map { emotionRecommendedRoutineDto ->
2834
emotionRecommendedRoutineDto.toEmotionRecommendRoutine()
2935
}
3036
}.also {
31-
if (it.isSuccess) {
32-
_emotionChangeEventFlow.emit(EmotionChangeEvent.ChangeEmotion(emotionMarbleType))
33-
}
37+
if (it.isSuccess) fetchDailyEmotion()
3438
}
3539
}
3640

37-
override suspend fun fetchDailyEmotion(currentDate: String): Result<DailyEmotion> =
38-
emotionDataSource.fetchDailyEmotion(currentDate).map { it.toDomain() }
39-
40-
private val _emotionChangeEventFlow = MutableSharedFlow<EmotionChangeEvent>()
41-
override suspend fun getEmotionChangeEventFlow(): Flow<EmotionChangeEvent> = _emotionChangeEventFlow.asSharedFlow()
41+
override suspend fun fetchDailyEmotion(): Result<Unit> {
42+
val currentDate = LocalDate.now().toString()
43+
return emotionDataSource.fetchDailyEmotion(currentDate).map {
44+
_dailyEmotionFlow.value = it.toDomain()
45+
}
46+
}
4247
}
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
package com.threegap.bitnagil.domain.emotion.model
22

3+
import java.time.LocalDate
4+
35
data class DailyEmotion(
46
val type: EmotionMarbleType?,
57
val name: String?,
68
val imageUrl: String?,
79
val homeMessage: String?,
8-
)
10+
val fetchedDate: LocalDate = LocalDate.MIN,
11+
) {
12+
val isStale: Boolean
13+
get() = fetchedDate != LocalDate.now()
14+
15+
companion object {
16+
val INIT = DailyEmotion(
17+
type = null,
18+
name = null,
19+
imageUrl = null,
20+
homeMessage = null,
21+
fetchedDate = LocalDate.MIN,
22+
)
23+
}
24+
}

domain/src/main/java/com/threegap/bitnagil/domain/emotion/model/EmotionChangeEvent.kt

Lines changed: 0 additions & 5 deletions
This file was deleted.

domain/src/main/java/com/threegap/bitnagil/domain/emotion/repository/EmotionRepository.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package com.threegap.bitnagil.domain.emotion.repository
22

33
import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
44
import com.threegap.bitnagil.domain.emotion.model.Emotion
5-
import com.threegap.bitnagil.domain.emotion.model.EmotionChangeEvent
65
import com.threegap.bitnagil.domain.emotion.model.EmotionRecommendRoutine
76
import kotlinx.coroutines.flow.Flow
87

98
interface EmotionRepository {
9+
val dailyEmotionFlow: Flow<DailyEmotion>
1010
suspend fun getEmotions(): Result<List<Emotion>>
1111
suspend fun registerEmotion(emotionMarbleType: String): Result<List<EmotionRecommendRoutine>>
12-
suspend fun fetchDailyEmotion(currentDate: String): Result<DailyEmotion>
13-
suspend fun getEmotionChangeEventFlow(): Flow<EmotionChangeEvent>
12+
suspend fun fetchDailyEmotion(): Result<Unit>
1413
}

domain/src/main/java/com/threegap/bitnagil/domain/emotion/usecase/GetEmotionChangeEventFlowUseCase.kt

Lines changed: 0 additions & 12 deletions
This file was deleted.

domain/src/main/java/com/threegap/bitnagil/domain/emotion/usecase/FetchDailyEmotionUseCase.kt renamed to domain/src/main/java/com/threegap/bitnagil/domain/emotion/usecase/ObserveDailyEmotionUseCase.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ package com.threegap.bitnagil.domain.emotion.usecase
22

33
import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
44
import com.threegap.bitnagil.domain.emotion.repository.EmotionRepository
5-
import java.time.LocalDate
5+
import kotlinx.coroutines.flow.Flow
66
import javax.inject.Inject
77

8-
class FetchDailyEmotionUseCase @Inject constructor(
8+
class ObserveDailyEmotionUseCase @Inject constructor(
99
private val emotionRepository: EmotionRepository,
1010
) {
11-
suspend operator fun invoke(): Result<DailyEmotion> {
12-
val currentDate = LocalDate.now().toString()
13-
return emotionRepository.fetchDailyEmotion(currentDate)
14-
}
11+
operator fun invoke(): Flow<DailyEmotion> = emotionRepository.dailyEmotionFlow
1512
}

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

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ package com.threegap.bitnagil.presentation.screen.home
22

33
import android.util.Log
44
import androidx.lifecycle.ViewModel
5-
import com.threegap.bitnagil.domain.emotion.usecase.FetchDailyEmotionUseCase
6-
import com.threegap.bitnagil.domain.emotion.usecase.GetEmotionChangeEventFlowUseCase
5+
import com.threegap.bitnagil.domain.emotion.usecase.ObserveDailyEmotionUseCase
76
import com.threegap.bitnagil.domain.onboarding.usecase.GetOnBoardingRecommendRoutineEventFlowUseCase
87
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfo
98
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfos
@@ -34,12 +33,11 @@ import javax.inject.Inject
3433

3534
@HiltViewModel
3635
class HomeViewModel @Inject constructor(
36+
private val observeDailyEmotionUseCase: ObserveDailyEmotionUseCase,
3737
private val fetchWeeklyRoutinesUseCase: FetchWeeklyRoutinesUseCase,
3838
private val fetchUserProfileUseCase: FetchUserProfileUseCase,
39-
private val fetchDailyEmotionUseCase: FetchDailyEmotionUseCase,
4039
private val routineCompletionUseCase: RoutineCompletionUseCase,
4140
private val getWriteRoutineEventFlowUseCase: GetWriteRoutineEventFlowUseCase,
42-
private val getEmotionChangeEventFlowUseCase: GetEmotionChangeEventFlowUseCase,
4341
private val getOnBoardingRecommendRoutineEventFlowUseCase: GetOnBoardingRecommendRoutineEventFlowUseCase,
4442
private val toggleRoutineUseCase: ToggleRoutineUseCase,
4543
) : ContainerHost<HomeState, HomeSideEffect>, ViewModel() {
@@ -51,6 +49,17 @@ class HomeViewModel @Inject constructor(
5149

5250
init {
5351
initialize()
52+
observeDailyEmotion()
53+
}
54+
55+
private fun observeDailyEmotion() {
56+
intent {
57+
repeatOnSubscription {
58+
observeDailyEmotionUseCase()
59+
.map { it.toUiModel() }
60+
.collect { reduce { state.copy(dailyEmotion = it) } }
61+
}
62+
}
5463
}
5564

5665
fun selectDate(data: LocalDate) {
@@ -186,10 +195,8 @@ class HomeViewModel @Inject constructor(
186195
intent {
187196
coroutineScope {
188197
launch { fetchUserProfile() }
189-
launch { fetchDailyEmotion() }
190198
launch { fetchWeeklyRoutines(state.currentWeeks) }
191199
launch { observeWriteRoutineEvent() }
192-
launch { observeEmotionChangeEvent() }
193200
launch { observeRecommendRoutineEvent() }
194201
launch { observeWeekChanges() }
195202
launch { observeRoutineUpdates() }
@@ -205,14 +212,6 @@ class HomeViewModel @Inject constructor(
205212
}
206213
}
207214

208-
private suspend fun observeEmotionChangeEvent() {
209-
subIntent {
210-
getEmotionChangeEventFlowUseCase().collect {
211-
fetchDailyEmotion()
212-
}
213-
}
214-
}
215-
216215
private suspend fun observeRecommendRoutineEvent() {
217216
subIntent {
218217
getOnBoardingRecommendRoutineEventFlowUseCase().collect {
@@ -278,21 +277,6 @@ class HomeViewModel @Inject constructor(
278277
}
279278
}
280279

281-
private suspend fun fetchDailyEmotion() {
282-
subIntent {
283-
reduce { state.copy(loadingCount = state.loadingCount + 1) }
284-
fetchDailyEmotionUseCase().fold(
285-
onSuccess = {
286-
reduce { state.copy(dailyEmotion = it.toUiModel(), loadingCount = state.loadingCount - 1) }
287-
},
288-
onFailure = {
289-
Log.e("HomeViewModel", "나의 감정 실패: ${it.message}")
290-
reduce { state.copy(loadingCount = state.loadingCount - 1) }
291-
},
292-
)
293-
}
294-
}
295-
296280
private fun syncRoutineChangesForDate(dateKey: String) {
297281
intent {
298282
val dateChanges = pendingChangesByDate.remove(dateKey)

presentation/src/main/java/com/threegap/bitnagil/presentation/screen/recommendroutine/RecommendRoutineViewModel.kt

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package com.threegap.bitnagil.presentation.screen.recommendroutine
22

33
import androidx.lifecycle.ViewModel
4-
import androidx.lifecycle.viewModelScope
5-
import com.threegap.bitnagil.domain.emotion.usecase.GetEmotionChangeEventFlowUseCase
4+
import com.threegap.bitnagil.domain.emotion.usecase.ObserveDailyEmotionUseCase
65
import com.threegap.bitnagil.domain.recommendroutine.model.RecommendCategory
76
import com.threegap.bitnagil.domain.recommendroutine.model.RecommendLevel
87
import com.threegap.bitnagil.domain.recommendroutine.usecase.FetchRecommendRoutinesUseCase
@@ -12,25 +11,32 @@ import com.threegap.bitnagil.presentation.screen.recommendroutine.model.Recommen
1211
import com.threegap.bitnagil.presentation.screen.recommendroutine.model.RecommendRoutinesUiModel
1312
import com.threegap.bitnagil.presentation.screen.recommendroutine.model.toUiModel
1413
import dagger.hilt.android.lifecycle.HiltViewModel
15-
import kotlinx.coroutines.launch
14+
import kotlinx.coroutines.flow.distinctUntilChanged
15+
import kotlinx.coroutines.flow.map
1616
import org.orbitmvi.orbit.Container
1717
import org.orbitmvi.orbit.ContainerHost
1818
import org.orbitmvi.orbit.viewmodel.container
1919
import javax.inject.Inject
2020

2121
@HiltViewModel
2222
class RecommendRoutineViewModel @Inject constructor(
23+
private val observeDailyEmotionUseCase: ObserveDailyEmotionUseCase,
2324
private val fetchRecommendRoutinesUseCase: FetchRecommendRoutinesUseCase,
24-
private val getEmotionChangeEventFlowUseCase: GetEmotionChangeEventFlowUseCase,
2525
) : ContainerHost<RecommendRoutineState, RecommendRoutineSideEffect>, ViewModel() {
2626

2727
override val container: Container<RecommendRoutineState, RecommendRoutineSideEffect> =
28-
container(initialState = RecommendRoutineState.INIT)
29-
30-
init {
31-
loadRecommendRoutines()
32-
observeEmotionChangeEvent()
33-
}
28+
container(
29+
initialState = RecommendRoutineState.INIT,
30+
buildSettings = { repeatOnSubscribedStopTimeout = 5_000L },
31+
onCreate = {
32+
repeatOnSubscription {
33+
observeDailyEmotionUseCase()
34+
.map { it.type }
35+
.distinctUntilChanged()
36+
.collect { loadRecommendRoutines() }
37+
}
38+
},
39+
)
3440

3541
private var recommendRoutines: RecommendRoutinesUiModel = RecommendRoutinesUiModel.INIT
3642

@@ -80,14 +86,6 @@ class RecommendRoutineViewModel @Inject constructor(
8086
}
8187
}
8288

83-
private fun observeEmotionChangeEvent() {
84-
viewModelScope.launch {
85-
getEmotionChangeEventFlowUseCase().collect {
86-
loadRecommendRoutines()
87-
}
88-
}
89-
}
90-
9189
private fun loadRecommendRoutines() {
9290
intent {
9391
reduce { state.copy(isLoading = true) }

0 commit comments

Comments
 (0)