vicky7230
03/16/2024, 10:45 AMvicky7230
03/16/2024, 1:06 PM@HiltViewModel
class HomeViewModel @Inject constructor(
private val repository: Repository
) : ViewModel(), ContainerHost<HomeUiState, Nothing> {
override val container =
container<HomeUiState, Nothing>(HomeUiState())
private val stateMachine = StateMachine.create<HomeState, HomeEvent, HomeSideEffect> {
initialState(HomeState.Idle)
state<HomeState.Idle> {
on<HomeEvent.OnLoading> {
transitionTo(HomeState.Loading, HomeSideEffect.Loading)
}
}
state<HomeState.Loading> {
on<HomeEvent.OnError> {
transitionTo(HomeState.Error, HomeSideEffect.Error(it.error))
}
on<HomeEvent.OnSuccess> {
transitionTo(HomeState.Success, HomeSideEffect.Success(it.list))
}
}
state<HomeState.Success> {}
state<HomeState.Error> {
on<HomeEvent.OnLoading> {
transitionTo(HomeState.Loading, HomeSideEffect.Loading)
}
}
onTransition {
val validTransition = it as? StateMachine.Transition.Valid ?: return@onTransition
Timber.tag("FSM").e("\n=================================================")
Timber.tag("FSM").e("From State: ${it.fromState}")
Timber.tag("FSM").e("Event Fired = ${it.event::class.simpleName}")
Timber.tag("FSM").e("State Transitioned to: ${it.toState}")
when (val effect = validTransition.sideEffect as HomeSideEffect) {
is HomeSideEffect.Loading -> intent { reduce { state.copy(loading = true) } }
is HomeSideEffect.Error -> intent {
reduce {
state.copy(
loading = false,
error = effect.error
)
}
}
is HomeSideEffect.Success -> intent {
reduce {
state.copy(
error = "null",
loading = false,
todos = effect.list
)
}
}
}
}
}
init {
getTodos()
}
fun getTodos() {
viewModelScope.launch {
repository.getTodos().collect {
updateState(it)
}
}
}
private suspend fun updateState(networkResult: NetworkResult<JsonElement>) {
when (networkResult) {
is NetworkResult.Loading -> {
stateMachine.transition(HomeEvent.OnLoading)
}
is NetworkResult.Success -> {
val listJson = networkResult.data
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
val deserializedList = parseTodoList(listJson)
stateMachine.transition(HomeEvent.OnSuccess(deserializedList))
}
}
is NetworkResult.Error -> {
var errorMessage = networkResult.message
stateMachine.transition(HomeEvent.OnError(errorMessage!!))
}
is NetworkResult.Exception -> {
var exceptionMessage = networkResult.throwable.localizedMessage
stateMachine.transition(HomeEvent.OnError(exceptionMessage!!))
}
}
}
}
vicky7230
03/16/2024, 1:06 PMsealed class HomeState {
data object Idle : HomeState()
data object Loading : HomeState()
data object Error : HomeState()
data object Success : HomeState()
}
sealed class HomeEvent {
data object OnLoading : HomeEvent()
data class OnError(val error: String) : HomeEvent()
data class OnSuccess(val list: List<Todo>) : HomeEvent()
}
sealed class HomeSideEffect {
data object Loading : HomeSideEffect()
data class Error(val error: String) : HomeSideEffect()
data class Success(val list: List<Todo>) : HomeSideEffect()
}
data class HomeUiState(
val loading: Boolean = false,
val error: String = "null",
val todos: List<Todo> = emptyList()
)
vicky7230
03/16/2024, 1:07 PMvicky7230
03/16/2024, 1:07 PMGuilherme Delgado
04/01/2024, 8:56 AMvicky7230
04/01/2024, 3:25 PMGuilherme Delgado
04/02/2024, 10:23 AM...
state<LandingState.State.Idle> {
on<LandingState.Event.OnCheckedIn> {
logState(this, LandingState.Event.OnCheckedIn)
transitionTo(LandingState.State.Finish, LandingState.SideEffect.NavigateHome)
}
state<LandingState.State.Finish> {}
private val stateMachine = LandingStateMachine { effect ->
when (effect) {
is LandingState.SideEffect.ShowAbout -> containerHost.intent { postSideEffect(ShowAbout) }
is LandingState.SideEffect.NavigateHome -> containerHost.intent { postSideEffect(NavigateToHome) }
is LandingState.SideEffect.CheckIn -> checkIn(effect.tokens.token)
is LandingState.SideEffect.Error -> effect.error?.let { containerHost.intent { parseError(it) } }
...
}
}
private fun checkIn(token: String) {
containerHost.intent {
with(stateMachine) {
when (val result = sessionManager.checkIn(token)) {
is SessionManager.CheckInResult.Success -> {
reduce { state.copy(hasSession = true) }
transitionOn(LandingState.Event.OnCheckedIn)
}
is SessionManager.CheckInResult.Error -> {
if (result.exception.isTimeoutException()) {
transitionOn(LandingState.Event.OnCheckInTimeout())
} else {
transitionOn(LandingState.Event.OnError(result.exception))
}
}
}
}
}
}
This way my “screen” is fully protected by the FSM, be it UI changes or logic changesGuilherme Delgado
04/02/2024, 10:25 AMGuilherme Delgado
04/02/2024, 10:26 AM