Skip to content

Commit

Permalink
refactor: from MVI with ViewModels to MVI with the Decompose
Browse files Browse the repository at this point in the history
  • Loading branch information
y9vad9 committed Feb 22, 2024
1 parent af5f604 commit 2341311
Show file tree
Hide file tree
Showing 99 changed files with 914 additions and 928 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package org.timemates.app.authorization.dependencies.screens

import org.timemates.app.authorization.dependencies.AuthorizationDataModule
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartReducer
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine
import com.arkivanov.decompose.ComponentContext
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.timemates.app.authorization.dependencies.AuthorizationDataModule
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartReducer
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent

@Module(includes = [AuthorizationDataModule::class])
class AfterStartModule {
@Factory
fun stateMachine(
fun mviComponent(
componentContext: ComponentContext,
verificationHash: VerificationHash,
): AfterStartStateMachine {
return AfterStartStateMachine(
): AfterStartScreenComponent {
return AfterStartScreenComponent(
componentContext,
reducer = AfterStartReducer(
verificationHash = verificationHash
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package org.timemates.app.authorization.dependencies.screens

import com.arkivanov.decompose.ComponentContext
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.koin.core.annotation.Singleton
import org.timemates.app.authorization.dependencies.AuthorizationDataModule
import org.timemates.app.authorization.repositories.AuthorizationsRepository
import org.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountMiddleware
import org.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountReducer
import org.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountStateMachine
import org.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountScreenComponent
import org.timemates.app.authorization.usecases.CreateNewAccountUseCase
import org.timemates.app.authorization.validation.UserDescriptionValidator
import org.timemates.app.authorization.validation.UserNameValidator
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.koin.core.annotation.Singleton

@Module(includes = [AuthorizationDataModule::class])
class ConfigureAccountModule {
Expand All @@ -33,21 +32,23 @@ class ConfigureAccountModule {
fun userDescriptionValidator(): UserDescriptionValidator = UserDescriptionValidator()

@Factory
fun stateMachine(
fun mviComponent(
componentContext: ComponentContext,
verificationHash: VerificationHash,
configureAccountMiddleware: ConfigureAccountMiddleware,
createNewAccountUseCase: CreateNewAccountUseCase,
userNameValidator: UserNameValidator,
userDescriptionValidator: UserDescriptionValidator,
): ConfigureAccountStateMachine {
return ConfigureAccountStateMachine(
): ConfigureAccountScreenComponent {
return ConfigureAccountScreenComponent(
componentContext = componentContext,
reducer = ConfigureAccountReducer(
verificationHash = verificationHash,
createNewAccountUseCase = createNewAccountUseCase,
userNameValidator = userNameValidator,
userDescriptionValidator = userDescriptionValidator,
),
configureAccountMiddleware,
middleware = configureAccountMiddleware,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package org.timemates.app.authorization.dependencies.screens

import com.arkivanov.decompose.ComponentContext
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.koin.core.annotation.Singleton
import org.timemates.app.authorization.dependencies.AuthorizationDataModule
import org.timemates.app.authorization.repositories.AuthorizationsRepository
import org.timemates.app.authorization.ui.confirmation.mvi.ConfirmAuthorizationMiddleware
import org.timemates.app.authorization.ui.confirmation.mvi.ConfirmAuthorizationStateMachine
import org.timemates.app.authorization.ui.confirmation.mvi.ConfirmAuthorizationScreenComponent
import org.timemates.app.authorization.ui.confirmation.mvi.ConfirmAuthorizationsReducer
import org.timemates.app.authorization.usecases.ConfirmEmailAuthorizationUseCase
import org.timemates.app.authorization.validation.ConfirmationCodeValidator
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.koin.core.annotation.Singleton

@Module(includes = [AuthorizationDataModule::class])
class ConfirmAuthorizationModule {
Expand All @@ -29,13 +28,15 @@ class ConfirmAuthorizationModule {
): ConfirmEmailAuthorizationUseCase = ConfirmEmailAuthorizationUseCase(authorizationsRepository)

@Factory
fun stateMachine(
fun mviComponent(
componentContext: ComponentContext,
verificationHash: VerificationHash,
middleware: ConfirmAuthorizationMiddleware,
confirmEmailAuthorizationUseCase: ConfirmEmailAuthorizationUseCase,
confirmationCodeValidator: ConfirmationCodeValidator,
): ConfirmAuthorizationStateMachine {
return ConfirmAuthorizationStateMachine(
): ConfirmAuthorizationScreenComponent {
return ConfirmAuthorizationScreenComponent(
componentContext = componentContext,
reducer = ConfirmAuthorizationsReducer(
verificationHash,
confirmEmailAuthorizationUseCase,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package org.timemates.app.authorization.dependencies.screens

import org.timemates.app.authorization.ui.initial_authorization.mvi.InitialAuthorizationReducer
import org.timemates.app.authorization.ui.initial_authorization.mvi.InitialAuthorizationStateMachine
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.timemates.app.authorization.ui.initial_authorization.mvi.InitialAuthorizationReducer
import org.timemates.app.authorization.ui.initial_authorization.mvi.InitialAuthorizationScreenComponent

@Module
class InitialAuthorizationModule {
@Factory
fun stateMachine(): InitialAuthorizationStateMachine {
return InitialAuthorizationStateMachine(
reducer = InitialAuthorizationReducer()
fun mviComponent(componentComponent: InitialAuthorizationScreenComponent): InitialAuthorizationScreenComponent {
return InitialAuthorizationScreenComponent(
componentContext = componentComponent,
reducer = InitialAuthorizationReducer(),
)
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package org.timemates.app.authorization.dependencies.screens

import org.timemates.app.authorization.dependencies.AuthorizationDataModule
import org.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoReducer
import org.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoStateMachine
import com.arkivanov.decompose.ComponentContext
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.timemates.app.authorization.dependencies.AuthorizationDataModule
import org.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoReducer
import org.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoScreenComponent

@Module(includes = [AuthorizationDataModule::class])
class NewAccountInfoModule {
@Factory
fun stateMachine(
fun mviComponent(
componentContext: ComponentContext,
verificationHash: VerificationHash,
): NewAccountInfoStateMachine {
return NewAccountInfoStateMachine(
): NewAccountInfoScreenComponent {
return NewAccountInfoScreenComponent(
componentContext = componentContext,
reducer = NewAccountInfoReducer(
verificationHash = verificationHash
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package org.timemates.app.authorization.dependencies.screens

import com.arkivanov.decompose.ComponentContext
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.koin.core.annotation.Singleton
import org.timemates.app.authorization.dependencies.AuthorizationDataModule
import org.timemates.app.authorization.repositories.AuthorizationsRepository
import org.timemates.app.authorization.ui.start.mvi.StartAuthorizationComponent
import org.timemates.app.authorization.ui.start.mvi.StartAuthorizationMiddleware
import org.timemates.app.authorization.ui.start.mvi.StartAuthorizationReducer
import org.timemates.app.authorization.ui.start.mvi.StartAuthorizationStateMachine
import org.timemates.app.authorization.usecases.AuthorizeByEmailUseCase
import org.timemates.app.authorization.validation.EmailAddressValidator
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Module
import org.koin.core.annotation.Singleton

@Module(includes = [AuthorizationDataModule::class])
class StartAuthorizationModule {
Expand All @@ -37,11 +36,13 @@ class StartAuthorizationModule {
fun middleware(): StartAuthorizationMiddleware = StartAuthorizationMiddleware()

@Factory
fun stateMachine(
fun mviComponent(
componentContext: ComponentContext,
reducer: StartAuthorizationReducer,
middleware: StartAuthorizationMiddleware,
): StartAuthorizationStateMachine {
return StartAuthorizationStateMachine(
): StartAuthorizationComponent {
return StartAuthorizationComponent(
componentContext = componentContext,
reducer = reducer,
middleware = middleware,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.timemates.app.authorization.validation

import org.timemates.app.foundation.validation.Validator
import org.timemates.app.foundation.validation.unknownValidationFailure
import io.timemates.sdk.authorization.sessions.types.value.ConfirmationCode
import io.timemates.sdk.common.constructor.CreationFailure
import org.timemates.app.foundation.validation.Validator
import org.timemates.app.foundation.validation.unknownValidationFailure

/**
* A validator for confirmation codes.
Expand Down Expand Up @@ -32,10 +32,10 @@ class ConfirmationCodeValidator : Validator<String, ConfirmationCodeValidator.Re
* The possible validation results for a confirmation code.
*/
sealed class Result {
object SizeIsInvalid : Result()
data object SizeIsInvalid : Result()

object PatternFailure : Result()
data object PatternFailure : Result()

object Success : Result()
data object Success : Result()
}
}
1 change: 1 addition & 0 deletions feature/authorization/presentation/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id(libs.plugins.configurations.compose.multiplatform.get().pluginId)
id(libs.plugins.configurations.unit.tests.mockable.get().pluginId)
alias(libs.plugins.kotlinx.serialization)
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,32 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.github.skeptick.libres.compose.painterResource
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine.Event
import org.timemates.app.foundation.mvi.EmptyState
import org.timemates.app.foundation.mvi.StateMachine
import kotlinx.coroutines.channels.consumeEach
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.Effect
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.Event
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.State
import org.timemates.app.foundation.mvi.MVI
import org.timemates.app.localization.compose.LocalStrings
import org.timemates.app.style.system.Resources
import org.timemates.app.style.system.appbar.AppBar
import org.timemates.app.style.system.button.Button
import org.timemates.app.style.system.theme.AppTheme
import kotlinx.coroutines.channels.consumeEach

@Composable
fun AfterStartScreen(
stateMachine: StateMachine<EmptyState, Event, AfterStartStateMachine.Effect>,
mvi: MVI<State, Event, Effect>,
navigateToConfirmation: (String) -> Unit,
navigateToStart: () -> Unit,
) {
val painter: Painter = Resources.image.confirm_authorization_info_image.painterResource()

LaunchedEffect(Unit) {
stateMachine.effects.consumeEach { effect ->
mvi.effects.consumeEach { effect ->
when (effect) {
is AfterStartStateMachine.Effect.NavigateToConfirmation ->
is Effect.NavigateToConfirmation ->
navigateToConfirmation(effect.verificationHash.string)

AfterStartStateMachine.Effect.OnChangeEmailClicked ->
Effect.OnChangeEmailClicked ->
navigateToStart()
}
}
Expand All @@ -58,7 +58,7 @@ fun AfterStartScreen(
AppBar(
navigationIcon = {
IconButton(
onClick = { stateMachine.dispatchEvent(Event.OnChangeEmailClicked) },
onClick = { mvi.dispatchEvent(Event.OnChangeEmailClicked) },
) {
Icon(Icons.Rounded.ArrowBack, contentDescription = null)
}
Expand Down Expand Up @@ -106,15 +106,15 @@ fun AfterStartScreen(
Button(
modifier = Modifier.fillMaxWidth(),
primary = false,
onClick = { stateMachine.dispatchEvent(Event.OnChangeEmailClicked) },
onClick = { mvi.dispatchEvent(Event.OnChangeEmailClicked) },
) {
Text(LocalStrings.current.changeEmail)
}

Button(
modifier = Modifier.fillMaxWidth(),
primary = true,
onClick = { stateMachine.dispatchEvent(Event.NextClicked) },
onClick = { mvi.dispatchEvent(Event.NextClicked) },
) {
Text(LocalStrings.current.nextStep)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package org.timemates.app.authorization.ui.afterstart.mvi

import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine.Effect
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine.Event
import org.timemates.app.foundation.mvi.EmptyState
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.Effect
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.Event
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.State
import org.timemates.app.foundation.mvi.Reducer
import org.timemates.app.foundation.mvi.ReducerScope
import io.timemates.sdk.authorization.email.types.value.VerificationHash

class AfterStartReducer(private val verificationHash: VerificationHash) : Reducer<EmptyState, Event, Effect> {
override fun ReducerScope<Effect>.reduce(state: EmptyState, event: Event): EmptyState {
class AfterStartReducer(private val verificationHash: VerificationHash) : Reducer<State, Event, Effect> {
override fun ReducerScope<Effect>.reduce(state: State, event: Event): State {
return when (event) {
is Event.NextClicked -> {
sendEffect(Effect.NavigateToConfirmation(verificationHash))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.timemates.app.authorization.ui.afterstart.mvi

import com.arkivanov.decompose.ComponentContext
import io.timemates.sdk.authorization.email.types.value.VerificationHash
import kotlinx.serialization.Serializable
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.Effect
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.Event
import org.timemates.app.authorization.ui.afterstart.mvi.AfterStartScreenComponent.State
import org.timemates.app.foundation.mvi.MVIComponent
import org.timemates.app.foundation.mvi.UiEffect
import org.timemates.app.foundation.mvi.UiEvent
import org.timemates.app.foundation.mvi.UiState
import org.timemates.app.foundation.mvi.mviComponent

class AfterStartScreenComponent(
componentContext: ComponentContext,
reducer: AfterStartReducer,
) : MVIComponent<State, Event, Effect> by mviComponent(
componentName = "AfterStartComponent",
componentContext = componentContext,
initState = State,
reducer = reducer,
middlewares = emptyList(),
) {
@Serializable
data object State : UiState {
private fun readResolve(): Any = State

}

sealed class Effect : UiEffect {
data class NavigateToConfirmation(val verificationHash: VerificationHash) : Effect()

data object OnChangeEmailClicked : Effect()
}

sealed class Event : UiEvent {
data object NextClicked : Event()

data object OnChangeEmailClicked : Event()
}
}
Loading

0 comments on commit 2341311

Please sign in to comment.