Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,33 @@ import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.threegap.bitnagil.presentation.common.dimension.pxToDp
import com.threegap.bitnagil.presentation.common.flow.collectAsEffect
import com.threegap.bitnagil.presentation.common.toast.GlobalBitnagilToast
import com.threegap.bitnagil.presentation.emotion.component.template.EmotionRecommendRoutineScreen
import com.threegap.bitnagil.presentation.emotion.component.template.SimpleEmotionSelectionScreen
import com.threegap.bitnagil.presentation.emotion.component.template.SwipeEmotionSelectionScreen
import com.threegap.bitnagil.presentation.emotion.model.EmotionScreenStep
import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionSideEffect
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect

@Composable
fun EmotionScreenContainer(
viewModel: EmotionViewModel = hiltViewModel(),
navigateToBack: () -> Unit,
) {
val state by viewModel.stateFlow.collectAsState()
val state by viewModel.collectAsState()

BackHandler {
viewModel.moveToPrev()
}

viewModel.sideEffectFlow.collectAsEffect { sideEffect ->
viewModel.collectSideEffect { sideEffect ->
when (sideEffect) {
EmotionSideEffect.NavigateToBack -> navigateToBack()
is EmotionSideEffect.ShowToast -> GlobalBitnagilToast.showWarning(sideEffect.message)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,179 +1,135 @@
package com.threegap.bitnagil.presentation.emotion

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.threegap.bitnagil.domain.emotion.usecase.GetEmotionsUseCase
import com.threegap.bitnagil.domain.emotion.usecase.RegisterEmotionUseCase
import com.threegap.bitnagil.domain.onboarding.usecase.RegisterRecommendOnBoardingRoutinesUseCase
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel
import com.threegap.bitnagil.presentation.emotion.model.EmotionRecommendRoutineUiModel
import com.threegap.bitnagil.presentation.emotion.model.EmotionScreenStep
import com.threegap.bitnagil.presentation.emotion.model.EmotionUiModel
import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionIntent
import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionSideEffect
import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.orbitmvi.orbit.syntax.Syntax
import org.orbitmvi.orbit.Container
import org.orbitmvi.orbit.ContainerHost
import org.orbitmvi.orbit.viewmodel.container
import javax.inject.Inject

@HiltViewModel
class EmotionViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val getEmotionsUseCase: GetEmotionsUseCase,
private val registerEmotionUseCase: RegisterEmotionUseCase,
private val registerRecommendOnBoardingRoutinesUseCase: RegisterRecommendOnBoardingRoutinesUseCase,
) : MviViewModel<EmotionState, EmotionSideEffect, EmotionIntent>(
savedStateHandle = savedStateHandle,
initState = EmotionState.Init,
) {
savedStateHandle: SavedStateHandle,
) : ContainerHost<EmotionState, EmotionSideEffect>, ViewModel() {

override val container: Container<EmotionState, EmotionSideEffect> = container(initialState = EmotionState.Init, savedStateHandle = savedStateHandle)

init {
loadEmotions()
}

private fun loadEmotions() {
viewModelScope.launch {
private fun loadEmotions() =
intent {
getEmotionsUseCase().fold(
onSuccess = { emotions ->
sendIntent(
EmotionIntent.EmotionListLoadSuccess(emotionTypeUiModels = emotions.map { EmotionUiModel.fromDomain(it) }),
)
reduce {
state.copy(
emotionTypeUiModels = emotions.map { EmotionUiModel.fromDomain(it) },
isLoading = false,
)
}
},
onFailure = {
// todo 실패 케이스 정의되면 처리
},
)
}
}

override suspend fun Syntax<EmotionState, EmotionSideEffect>.reduceState(intent: EmotionIntent, state: EmotionState): EmotionState? {
when (intent) {
is EmotionIntent.EmotionListLoadSuccess -> {
return state.copy(
emotionTypeUiModels = intent.emotionTypeUiModels,
isLoading = false,
)
}
is EmotionIntent.RegisterEmotionSuccess -> {
return state.copy(
recommendRoutines = intent.recommendRoutines,
step = EmotionScreenStep.RecommendRoutines,
isLoading = false,
showLoadingView = false,
)
}
EmotionIntent.RegisterEmotionLoading -> {
return state.copy(
fun selectEmotion(emotionType: String, minimumDelay: Long = 0) =
intent {
val isLoading = state.isLoading
if (isLoading) return@intent

reduce {
state.copy(
isLoading = true,
showLoadingView = true,
)
}
EmotionIntent.RegisterRecommendRoutinesLoading -> {
return state.copy(
isLoading = true,
)
}
EmotionIntent.RegisterRecommendRoutinesFailure -> {
return state.copy(
isLoading = false,
)
}
EmotionIntent.RegisterRecommendRoutinesSuccess -> {
sendSideEffect(EmotionSideEffect.NavigateToBack)
return null
}
EmotionIntent.BackToSelectEmotionStep -> {
return state.copy(
recommendRoutines = listOf(),
step = EmotionScreenStep.Emotion,
isLoading = false,
)
}

is EmotionIntent.SelectRecommendRoutine -> {
val selectChangedRecommendRoutines = state.recommendRoutines.map {
if (it.id == intent.recommendRoutineId) {
it.copy(selected = !it.selected)
} else {
it
}
viewModelScope.launch {
if (minimumDelay > 0) {
delay(minimumDelay)
}
return state.copy(recommendRoutines = selectChangedRecommendRoutines)
}

EmotionIntent.NavigateToBack -> {
sendSideEffect(EmotionSideEffect.NavigateToBack)
return null
}

is EmotionIntent.RegisterEmotionFailure -> {
sendSideEffect(EmotionSideEffect.ShowToast(intent.message))
sendSideEffect(EmotionSideEffect.NavigateToBack)
return null
registerEmotionUseCase(emotionType = emotionType).fold(
onSuccess = { emotionRecommendRoutines ->
val recommendRoutines = emotionRecommendRoutines.map { EmotionRecommendRoutineUiModel.fromEmotionRecommendRoutine(it) }
reduce {
state.copy(
recommendRoutines = recommendRoutines,
step = EmotionScreenStep.RecommendRoutines,
isLoading = false,
showLoadingView = false,
)
}
},
onFailure = {
postSideEffect(EmotionSideEffect.ShowToast(message = it.message ?: "에러가 발생했습니다. 잠시 후 시도해주세요."))
postSideEffect(EmotionSideEffect.NavigateToBack)
},
)
}
}
}

fun selectEmotion(emotionType: String, minimumDelay: Long = 0) {
val isLoading = stateFlow.value.isLoading
if (isLoading) return

viewModelScope.launch {
sendIntent(EmotionIntent.RegisterEmotionLoading)

if (minimumDelay > 0) {
delay(minimumDelay)
fun selectRecommendRoutine(recommendRoutineId: String) =
intent {
val selectChangedRecommendRoutines = state.recommendRoutines.map {
if (it.id == recommendRoutineId) {
it.copy(selected = !it.selected)
} else {
it
}
}

registerEmotionUseCase(emotionType = emotionType).fold(
onSuccess = { emotionRecommendRoutines ->
val recommendRoutines = emotionRecommendRoutines.map { EmotionRecommendRoutineUiModel.fromEmotionRecommendRoutine(it) }
sendIntent(EmotionIntent.RegisterEmotionSuccess(recommendRoutines))
},
onFailure = {
sendIntent(
EmotionIntent.RegisterEmotionFailure(message = it.message ?: "에러가 발생했습니다. 잠시 후 시도해주세요."),
)
},
)
}
}

fun selectRecommendRoutine(recommendRoutineId: String) {
viewModelScope.launch {
sendIntent(EmotionIntent.SelectRecommendRoutine(recommendRoutineId))
reduce { state.copy(recommendRoutines = selectChangedRecommendRoutines) }
}
}

fun moveToPrev() {
viewModelScope.launch {
val currentState = stateFlow.value

when (currentState.step) {
EmotionScreenStep.Emotion -> sendIntent(EmotionIntent.NavigateToBack)
EmotionScreenStep.RecommendRoutines -> sendIntent(EmotionIntent.BackToSelectEmotionStep)
fun moveToPrev() =
intent {
when (state.step) {
EmotionScreenStep.Emotion -> postSideEffect(EmotionSideEffect.NavigateToBack)
EmotionScreenStep.RecommendRoutines -> reduce {
state.copy(
recommendRoutines = listOf(),
step = EmotionScreenStep.Emotion,
isLoading = false,
)
}
}
}
}

fun registerRecommendRoutines() {
val isLoading = stateFlow.value.isLoading
if (isLoading) return

viewModelScope.launch {
sendIntent(EmotionIntent.RegisterRecommendRoutinesLoading)

val currentState = stateFlow.value
val selectedRecommendRoutineIds = currentState.recommendRoutines.filter { it.selected }.map { it.id }
registerRecommendOnBoardingRoutinesUseCase(selectedRecommendRoutineIds).fold(
onSuccess = {
sendIntent(EmotionIntent.RegisterRecommendRoutinesSuccess)
},
onFailure = {
sendIntent(EmotionIntent.RegisterRecommendRoutinesFailure)
},
)
fun registerRecommendRoutines() =
intent {
val isLoading = state.isLoading
if (isLoading) return@intent

viewModelScope.launch {
reduce { state.copy(isLoading = true) }

val selectedRecommendRoutineIds = state.recommendRoutines.filter { it.selected }.map { it.id }
registerRecommendOnBoardingRoutinesUseCase(selectedRecommendRoutineIds).fold(
onSuccess = {
postSideEffect(EmotionSideEffect.NavigateToBack)
},
onFailure = {
reduce { state.copy(isLoading = false) }
},
)
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.threegap.bitnagil.presentation.emotion.model.mvi

import com.threegap.bitnagil.presentation.common.mviviewmodel.MviSideEffect

sealed class EmotionSideEffect : MviSideEffect {
sealed class EmotionSideEffect {
data object NavigateToBack : EmotionSideEffect()
data class ShowToast(val message: String) : EmotionSideEffect()
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.threegap.bitnagil.presentation.emotion.model.mvi

import android.os.Parcelable
import androidx.compose.runtime.Immutable
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviState
import com.threegap.bitnagil.presentation.emotion.model.EmotionRecommendRoutineUiModel
import com.threegap.bitnagil.presentation.emotion.model.EmotionScreenStep
import com.threegap.bitnagil.presentation.emotion.model.EmotionUiModel
Expand All @@ -15,7 +15,7 @@ data class EmotionState(
val recommendRoutines: List<EmotionRecommendRoutineUiModel>,
val step: EmotionScreenStep,
val showLoadingView: Boolean,
) : MviState {
) : Parcelable {
companion object {
val Init = EmotionState(
emotionTypeUiModels = emptyList(),
Expand Down
Loading