Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#308 [feat] 대표 룰 수정 api 및 presentation 로직 #310

Merged
merged 10 commits into from
Sep 9, 2023
11 changes: 11 additions & 0 deletions app/src/main/java/hous/release/android/di/ReducerModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import hous.release.android.presentation.our_rules.event.AddRuleReducer
import hous.release.android.presentation.our_rules.event.MainRuleReducer
import hous.release.android.presentation.our_rules.event.RepresentRulesReducer
import hous.release.android.presentation.our_rules.event.UpdateRuleReducer
import hous.release.android.presentation.our_rules.viewmodel.AddRuleEvent
import hous.release.android.presentation.our_rules.viewmodel.AddRuleState
import hous.release.android.presentation.our_rules.viewmodel.MainRulesEvent
import hous.release.android.presentation.our_rules.viewmodel.MainRulesState
import hous.release.android.presentation.our_rules.viewmodel.RepresentRulesEvent
import hous.release.android.presentation.our_rules.viewmodel.RepresentRulesState
import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleEvent
import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleState
import hous.release.android.util.event.Reducer
Expand All @@ -30,8 +33,16 @@ abstract class ReducerModule {
@UpdateRule
@Binds
abstract fun bindUpdateRuleReducer(updateRuleReducer: UpdateRuleReducer): Reducer<UpdateRuleState, UpdateRuleEvent>

@RepresentRules
@Binds
abstract fun bindRepresentRulesReducer(representRulesReducer: RepresentRulesReducer): Reducer<RepresentRulesState, RepresentRulesEvent>
}

@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class RepresentRules

@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class MainRules
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package hous.release.android.presentation.our_rules.event

import hous.release.android.presentation.our_rules.model.RepresentRuleUiModel
import hous.release.android.presentation.our_rules.viewmodel.RepresentRulesEvent
import hous.release.android.presentation.our_rules.viewmodel.RepresentRulesState
import hous.release.android.util.event.Reducer
import javax.inject.Inject

class RepresentRulesReducer @Inject constructor() :
Reducer<RepresentRulesState, RepresentRulesEvent> {
override fun dispatch(
state: RepresentRulesState,
event: RepresentRulesEvent
): RepresentRulesState {
return when (event) {
is RepresentRulesEvent.FetchRules -> {
state.copy(
originRules = event.rules.map { RepresentRuleUiModel.from(it) },
rules = event.rules.map { RepresentRuleUiModel.from(it) }
)
}

is RepresentRulesEvent.UpdateRule -> {
state.copy(
rules = state.rules.map { rule ->
if (rule.id == event.id) rule.copy(isRepresent = rule.isRepresent.not())
else rule
}
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package hous.release.android.presentation.our_rules.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import hous.release.android.di.RepresentRules
import hous.release.android.presentation.our_rules.model.RepresentRuleUiModel
import hous.release.android.util.event.Reducer
import hous.release.domain.entity.rule.Rule
import hous.release.domain.usecase.rule.GetRulesUseCase
import hous.release.domain.usecase.rule.UpdateRepresentRulesUseCase
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.runningFold
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import retrofit2.HttpException
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class RepresentRuleViewModel @Inject constructor(
private val updateRepresentRulesUseCase: UpdateRepresentRulesUseCase,
private val getRulesUseCase: GetRulesUseCase,
@RepresentRules private val reducer: Reducer<RepresentRulesState, RepresentRulesEvent>
) : ViewModel() {

private val uiEvents = Channel<RepresentRulesEvent>()
val uiState: StateFlow<RepresentRulesState> = uiEvents
.receiveAsFlow()
.runningFold(RepresentRulesState(), reducer::dispatch)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), RepresentRulesState())

private val _sideEffect: Channel<RepresentRulesSideEffect> = Channel()
val sideEffect = _sideEffect.receiveAsFlow()

init {
viewModelScope.launch {
val rules = getRulesUseCase()
uiEvents.send(RepresentRulesEvent.FetchRules(rules))
}
}

val isSavable get() = uiState.value.rules == uiState.value.originRules

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오.. 항상 combine 사용했었는데 이런 식으로 짧게도 가능하네요... 👍

fun updateRuleBy(id: Int) {
viewModelScope.launch {
if (canAddRepresentRule()) {
uiEvents.send(RepresentRulesEvent.UpdateRule(id))
return@launch
}
_sideEffect.send(RepresentRulesSideEffect.ShowLimitRulesToast)
}
}

fun saveRules() {
viewModelScope.launch {
_sideEffect.send(RepresentRulesSideEffect.LoadingBar(true))
runCatching {
updateRepresentRulesUseCase(
uiState.value.rules.filter { it.isRepresent }
.map { it.id }
)
}.onSuccess {
_sideEffect.send(RepresentRulesSideEffect.LoadingBar(false))
_sideEffect.send(RepresentRulesSideEffect.PopBackStack)
}.onFailure { e ->
Timber.e("updateRule() - onFailure() - e: ${e.stackTraceToString()}")
if (e is HttpException) {
when (e.code()) {
LIMIT_RULES_CODE -> _sideEffect.send(RepresentRulesSideEffect.ShowLimitRulesToast)
}
}
_sideEffect.send(RepresentRulesSideEffect.LoadingBar(false))
}
}
}

private fun canAddRepresentRule(): Boolean {
return uiState.value.rules.filter { it.isRepresent }.size < 3
}

private companion object {
const val LIMIT_RULES_CODE = 403
}
}

sealed class RepresentRulesSideEffect {
object IDLE : RepresentRulesSideEffect()
data class LoadingBar(val isLoading: Boolean) : RepresentRulesSideEffect()
object ShowLimitRulesToast : RepresentRulesSideEffect()
object PopBackStack : RepresentRulesSideEffect()
}

data class RepresentRulesState(
val originRules: List<RepresentRuleUiModel> = emptyList(),
val rules: List<RepresentRuleUiModel> = emptyList()
)

sealed class RepresentRulesEvent {
data class FetchRules(val rules: List<Rule>) : RepresentRulesEvent()
data class UpdateRule(val id: Int) : RepresentRulesEvent()
}