From 94eee15aafd12f77653b5cd9e7ad269a078bcfee Mon Sep 17 00:00:00 2001 From: stslex Date: Sun, 8 Dec 2024 20:34:32 +0300 Subject: [PATCH 1/4] refactor mvi arch --- .../wizard/core/ui/mvi/handler/BaseHandler.kt | 17 +++++++++++++++++ .../wizard/core/ui/mvi/handler/Handler.kt | 11 +++++++++++ 2 files changed, 28 insertions(+) create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt new file mode 100644 index 0000000..d7370ea --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt @@ -0,0 +1,17 @@ +package com.stslex.wizard.core.ui.mvi.handler + +import com.stslex.wizard.core.ui.mvi.Store +import kotlinx.coroutines.flow.StateFlow + +abstract class BaseHandler( + private val store: Store +) : Handler { + + val state: StateFlow + get() = store.state + + protected fun sendAction(action: A) { + store.sendAction(action) + } + +} diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt new file mode 100644 index 0000000..9783b90 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt @@ -0,0 +1,11 @@ +package com.stslex.wizard.core.ui.mvi.handler + +import com.stslex.wizard.core.ui.mvi.Store +import com.stslex.wizard.core.ui.mvi.handler.Handler.Event + +fun interface Handler { + + operator fun invoke(event: E) + + interface Event : Store.Event +} From bd4f3d2ba6d01d99b77a3ca17243901881aff6ef Mon Sep 17 00:00:00 2001 From: stslex Date: Mon, 9 Dec 2024 23:02:58 +0300 Subject: [PATCH 2/4] refactor mvi arch --- .../stslex/wizard/core/ui/mvi/BaseStore.kt | 5 +- .../com/stslex/wizard/core/ui/mvi/Store.kt | 5 +- .../wizard/core/ui/mvi/handler/BaseHandler.kt | 17 -- .../wizard/core/ui/mvi/handler/Handler.kt | 11 -- .../core/ui/mvi/store_di/StoreBeanV2.kt | 36 +++++ .../stslex/wizard/core/ui/mvi/v2/BaseStore.kt | 115 ++++++++++++++ .../wizard/core/ui/mvi/v2/BaseStoreImpl.kt | 13 ++ .../stslex/wizard/core/ui/mvi/v2/Handler.kt | 28 ++++ .../wizard/core/ui/mvi/v2/HandlerStore.kt | 58 +++++++ .../stslex/wizard/core/ui/mvi/v2/StoreExt.kt | 30 ++++ .../profile/di/ModuleFeatureProfile.kt | 3 - .../feature/profile/mvi/ClickersHandler.kt | 21 +++ .../feature/profile/mvi/InitStorageHandler.kt | 60 +++++++ .../feature/profile/mvi/LogoutHandler.kt | 44 ++++++ .../feature/profile/mvi/NavigationHandler.kt | 29 ++++ .../profile/mvi/RepeatLastActionHandler.kt | 26 ++++ .../profile/navigation/GraphProfile.kt | 4 +- .../profile/navigation/ProfileRouter.kt | 6 - .../profile/navigation/ProfileRouterImpl.kt | 39 ----- .../feature/profile/ui/ProfileScreen.kt | 5 +- .../ui/components/ProfileScreenContent.kt | 10 +- .../feature/profile/ui/store/ProfileStore.kt | 15 +- .../profile/ui/store/ProfileStoreImpl.kt | 146 +++--------------- 23 files changed, 507 insertions(+), 219 deletions(-) delete mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt delete mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerStore.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt create mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt create mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt create mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt create mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt create mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt delete mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouter.kt delete mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouterImpl.kt diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/BaseStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/BaseStore.kt index 9ed385a..e15a796 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/BaseStore.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/BaseStore.kt @@ -22,8 +22,9 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -abstract class BaseStore(initialState: S) : ViewModel(), - Store { +abstract class BaseStore( + initialState: S +) : ViewModel(), Store { private val _event: MutableSharedFlow = MutableSharedFlow() override val event: SharedFlow = _event.asSharedFlow() diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt index 1496c78..3148cc6 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt @@ -24,7 +24,10 @@ interface Store { interface State - interface Event + interface Event { + + data class Handler(val action: Action) : Event + } interface Action { diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt deleted file mode 100644 index d7370ea..0000000 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/BaseHandler.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.stslex.wizard.core.ui.mvi.handler - -import com.stslex.wizard.core.ui.mvi.Store -import kotlinx.coroutines.flow.StateFlow - -abstract class BaseHandler( - private val store: Store -) : Handler { - - val state: StateFlow - get() = store.state - - protected fun sendAction(action: A) { - store.sendAction(action) - } - -} diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt deleted file mode 100644 index 9783b90..0000000 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/handler/Handler.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.stslex.wizard.core.ui.mvi.handler - -import com.stslex.wizard.core.ui.mvi.Store -import com.stslex.wizard.core.ui.mvi.handler.Handler.Event - -fun interface Handler { - - operator fun invoke(event: E) - - interface Event : Store.Event -} diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt new file mode 100644 index 0000000..b082850 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt @@ -0,0 +1,36 @@ +package com.stslex.wizard.core.ui.mvi.store_di + +import com.stslex.wizard.core.ui.mvi.v2.BaseStore +import org.koin.core.definition.BeanDefinition +import org.koin.core.definition.KoinDefinition +import org.koin.core.module.Module + +inline fun > Module.storeOf( + crossinline constructor: () -> R, + noinline options: (BeanDefinition.() -> Unit)? = null, +): KoinDefinition = viewModelOf(constructor, options) + +inline fun , reified T1> Module.storeOf( + crossinline constructor: (T1) -> R, + noinline options: (BeanDefinition.() -> Unit)? = null, +): KoinDefinition = viewModelOf(constructor, options) + +inline fun , reified T1, reified T2> Module.storeOf( + crossinline constructor: (T1, T2) -> R, + noinline options: (BeanDefinition.() -> Unit)? = null, +): KoinDefinition = viewModelOf(constructor, options) + +inline fun , reified T1, reified T2, reified T3> Module.storeOf( + crossinline constructor: (T1, T2, T3) -> R, + noinline options: (BeanDefinition.() -> Unit)? = null, +): KoinDefinition = viewModelOf(constructor, options) + +inline fun , reified T1, reified T2, reified T3, reified T4> Module.storeOf( + crossinline constructor: (T1, T2, T3, T4) -> R, + noinline options: (BeanDefinition.() -> Unit)? = null, +): KoinDefinition = viewModelOf(constructor, options) + +inline fun , reified T1, reified T2, reified T3, reified T4, reified T5> Module.storeOf( + crossinline constructor: (T1, T2, T3, T4, T5) -> R, + noinline options: (BeanDefinition.() -> Unit)? = null, +): KoinDefinition = viewModelOf(constructor, options) \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt new file mode 100644 index 0000000..a36f35a --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt @@ -0,0 +1,115 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.stslex.wizard.core.core.AppDispatcher +import com.stslex.wizard.core.core.coroutine.AppCoroutineScope +import com.stslex.wizard.core.core.coroutine.AppCoroutineScopeImpl +import com.stslex.wizard.core.ui.mvi.Store +import com.stslex.wizard.core.ui.mvi.Store.Action +import com.stslex.wizard.core.ui.mvi.Store.Event +import com.stslex.wizard.core.ui.mvi.Store.State +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +open class BaseStore( + initialState: S, + private val handlers: Set> +) : ViewModel(), Store, HandlerStore { + + private val _event: MutableSharedFlow = MutableSharedFlow() + override val event: SharedFlow = _event.asSharedFlow() + + private val _state: MutableStateFlow = MutableStateFlow(initialState) + override val state: StateFlow = _state.asStateFlow() + + protected val scope: AppCoroutineScope = AppCoroutineScopeImpl(viewModelScope) + + private var _lastAction: A? = null + override val lastAction: A? + get() = _lastAction + + @Suppress("UNCHECKED_CAST") + override fun sendAction(action: A) { + if (lastAction != action && action !is Action.RepeatLast) { + _lastAction = action + } + val handler = handlers.firstOrNull { it.checkAction(action) } as? Handler + ?: throw IllegalStateException("Handler not found for action: ${action::class.simpleName}") + handler.invoke(this, action) + } + + /** + * Updates the state of the screen. + * @param update - function that updates the state + * */ + override fun updateState(update: (S) -> S) { + _state.update(update) + } + + /** + * Sends an event to the screen. The event is sent on the default dispatcher of the AppDispatcher. + * @param event - event to be sent + * @see AppDispatcher + * */ + override fun sendEvent(event: E) { + viewModelScope.launch { _event.emit(event) } + } + + /** + * Launches a coroutine and catches exceptions. The coroutine is launched on the default dispatcher of the AppDispatcher. + * @param onError - error handler + * @param onSuccess - success handler + * @param action - action to be executed + * @return Job + * @see Job + * @see AppDispatcher + * */ + override fun launch( + onError: suspend (Throwable) -> Unit, + onSuccess: suspend CoroutineScope.(T) -> Unit, + workDispatcher: CoroutineDispatcher, + eachDispatcher: CoroutineDispatcher, + action: suspend CoroutineScope.() -> T, + ) = scope.launch( + onError = onError, + workDispatcher = workDispatcher, + eachDispatcher = eachDispatcher, + onSuccess = onSuccess, + action = action + ) + + /** + * Launches a flow and collects it in the screenModelScope. The flow is collected on the default dispatcher. of the AppDispatcher. + * @param onError - error handler + * @param each - action for each element of the flow + * @return Job + * @see Flow + * @see Job + * @see AppDispatcher + * */ + override fun Flow.launch( + onError: suspend (cause: Throwable) -> Unit, + workDispatcher: CoroutineDispatcher, + eachDispatcher: CoroutineDispatcher, + each: suspend (T) -> Unit + ): Job = scope.launch( + flow = this, + workDispatcher = workDispatcher, + eachDispatcher = eachDispatcher, + onError = onError, + each = each, + ) + + +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt new file mode 100644 index 0000000..6d175c6 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt @@ -0,0 +1,13 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import com.stslex.wizard.core.ui.mvi.Store.Action +import com.stslex.wizard.core.ui.mvi.Store.Event +import com.stslex.wizard.core.ui.mvi.Store.State + +internal class BaseStoreImpl( + initialState: S, + handlers: Set> +) : BaseStore( + initialState = initialState, + handlers = handlers +) \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt new file mode 100644 index 0000000..3bf7837 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt @@ -0,0 +1,28 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import com.stslex.wizard.core.ui.mvi.Store.Action +import com.stslex.wizard.core.ui.mvi.Store.Event +import com.stslex.wizard.core.ui.mvi.Store.State +import kotlin.reflect.KClass + +abstract class Handler(val actionKClass: KClass<*>) { + + val handlerName: String = requireNotNull(actionKClass.simpleName) { + "Action class name is null" + } + + inline fun checkAction(action: StoreAction): Boolean = actionKClass.isInstance(action) + + abstract fun HandlerStore.invoke(action: A) + + override fun toString(): String = handlerName + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Handler<*, *, *, *>) return false + return handlerName == other.handlerName + } + + override fun hashCode(): Int = handlerName.hashCode() + +} diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerStore.kt new file mode 100644 index 0000000..cb29e00 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerStore.kt @@ -0,0 +1,58 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import com.stslex.wizard.core.core.AppDispatcher +import com.stslex.wizard.core.core.AppDispatcherImpl +import com.stslex.wizard.core.ui.mvi.Store +import com.stslex.wizard.core.ui.mvi.Store.Event +import com.stslex.wizard.core.ui.mvi.Store.State +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +interface HandlerStore { + + val state: StateFlow + + val lastAction: A? + + fun sendEvent(event: E) + + fun sendAction(action: A) + + fun updateState(update: (S) -> S) + + /** + * Launches a coroutine and catches exceptions. The coroutine is launched on the default dispatcher of the AppDispatcher. + * @param onError - error handler + * @param onSuccess - success handler + * @param action - action to be executed + * @return Job + * @see Job + * @see AppDispatcher + * */ + fun launch( + onError: suspend (Throwable) -> Unit = {}, + onSuccess: suspend CoroutineScope.(T) -> Unit = {}, + workDispatcher: CoroutineDispatcher = AppDispatcherImpl.default, + eachDispatcher: CoroutineDispatcher = AppDispatcherImpl.main.immediate, + action: suspend CoroutineScope.() -> T, + ): Job + + /** + * Launches a flow and collects it in the screenModelScope. The flow is collected on the default dispatcher. of the AppDispatcher. + * @param onError - error handler + * @param each - action for each element of the flow + * @return Job + * @see Flow + * @see Job + * @see AppDispatcher + * */ + fun Flow.launch( + onError: suspend (cause: Throwable) -> Unit = {}, + workDispatcher: CoroutineDispatcher = AppDispatcherImpl.default, + eachDispatcher: CoroutineDispatcher = AppDispatcherImpl.main.immediate, + each: suspend (T) -> Unit + ): Job +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt new file mode 100644 index 0000000..c6040c4 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt @@ -0,0 +1,30 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import com.stslex.wizard.core.ui.mvi.Store +import com.stslex.wizard.core.ui.mvi.Store.Action +import com.stslex.wizard.core.ui.mvi.Store.Event +import com.stslex.wizard.core.ui.mvi.Store.State + +@Suppress("UNCHECKED_CAST") +fun > store( + initialState: S, + handlers: Set> +): TStore = BaseStoreImpl(initialState, handlers) as TStore + +fun Handler.invoke( + store: HandlerStore, + action: A +) { + with(store) { + this.invoke(action) + } +} + +inline fun handler( + crossinline block: HandlerStore.(action: A) -> Unit +) = object : Handler(A::class) { + + override fun HandlerStore.invoke(action: A) { + block(action) + } +} diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt index db98cf3..78fc82e 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt @@ -6,8 +6,6 @@ import com.stslex.wizard.feature.profile.data.repository.ProfileRepository import com.stslex.wizard.feature.profile.data.repository.ProfileRepositoryImpl import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractorImpl -import com.stslex.wizard.feature.profile.navigation.ProfileRouter -import com.stslex.wizard.feature.profile.navigation.ProfileRouterImpl import com.stslex.wizard.feature.profile.ui.store.ProfileStore import com.stslex.wizard.feature.profile.ui.store.ProfileStoreImpl import org.koin.core.annotation.Module @@ -20,7 +18,6 @@ class ModuleFeatureProfile : AppModule() { override fun declaration(): ModuleDeclaration = { storeOf(::ProfileStoreImpl) { bind() } - factoryOf(::ProfileRouterImpl) { bind() } factoryOf(::ProfileInteractorImpl) { bind() } factoryOf(::ProfileRepositoryImpl) { bind() } } diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt new file mode 100644 index 0000000..3fc9911 --- /dev/null +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt @@ -0,0 +1,21 @@ +package com.stslex.wizard.feature.profile.mvi + +import com.stslex.wizard.core.ui.mvi.v2.Handler +import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Click +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State + +class ClickersHandler : Handler(Click::class) { + + override fun HandlerStore.invoke(action: Click) { + when (action) { + Click.BackButtonClick -> sendAction(Action.Navigation.Back) + Click.FavouriteClick -> sendAction(Action.Navigation.Favourite(state.value.uuid)) + Click.FollowersClick -> sendAction(Action.Navigation.Followers(state.value.uuid)) + Click.FollowingClick -> sendAction(Action.Navigation.Following(state.value.uuid)) + Click.SettingsClick -> sendAction(Action.Navigation.Settings) + } + } +} \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt new file mode 100644 index 0000000..7a70763 --- /dev/null +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt @@ -0,0 +1,60 @@ +package com.stslex.wizard.feature.profile.mvi + +import androidx.compose.ui.graphics.Color +import com.stslex.wizard.core.database.store.UserStore +import com.stslex.wizard.core.navigation.Screen +import com.stslex.wizard.core.ui.mvi.v2.Handler +import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor +import com.stslex.wizard.feature.profile.ui.model.ProfileAvatarModel +import com.stslex.wizard.feature.profile.ui.model.toUi +import com.stslex.wizard.feature.profile.ui.store.ProfileScreenState +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Init +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State + +class InitStorageHandler( + private val interactor: ProfileInteractor, + private val userStore: UserStore, +) : Handler(Init::class) { + + override fun HandlerStore.invoke(action: Init) { + val uuid = action.uuid.ifBlank { userStore.uuid } + + updateState { currentState -> + currentState.copy( + isSelf = action.type == Screen.Profile.Type.SELF, + uuid = uuid, + ) + } + + interactor.getProfile(uuid) + .launch( + onError = { error -> + updateState { currentState -> + currentState.copy( + screen = ProfileScreenState.Error(error) + ) + } + } + ) { profile -> + val avatar = if (profile.avatarUrl.isBlank()) { + ProfileAvatarModel.Empty( + color = Color.Gray, // TODO replace with random color + symbol = profile.username.firstOrNull()?.lowercase().orEmpty() + ) + } else { + ProfileAvatarModel.Content(profile.avatarUrl) + } + val profileUi = profile.toUi( + avatarModel = avatar + ) + updateState { currentState -> + currentState.copy( + screen = ProfileScreenState.Content.NotLoading(profileUi) + ) + } + } + } +} \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt new file mode 100644 index 0000000..3a6d81e --- /dev/null +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt @@ -0,0 +1,44 @@ +package com.stslex.wizard.feature.profile.mvi + +import com.stslex.wizard.core.ui.mvi.CommonEvents.Snackbar +import com.stslex.wizard.core.ui.mvi.v2.Handler +import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor +import com.stslex.wizard.feature.profile.ui.store.ProfileScreenState +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State + +class LogoutHandler( + private val interactor: ProfileInteractor, +) : Handler(Action.Logout::class) { + + override fun HandlerStore.invoke(action: Action.Logout) { + val currentScreen = state.value.screen + + if ( + currentScreen is ProfileScreenState.Content.Loading || + currentScreen is ProfileScreenState.Shimmer + ) { + return + } + + updateState { currentState -> + currentState.copy( + screen = ProfileScreenState.Shimmer + ) + } + + launch( + action = { + interactor.logOut() + }, + onSuccess = { + sendAction(Action.Navigation.LogIn) + }, + onError = { error -> + sendEvent(Event.ShowSnackbar(Snackbar.Error(error.message ?: "error logout"))) + } + ) + } +} \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt new file mode 100644 index 0000000..231be6c --- /dev/null +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt @@ -0,0 +1,29 @@ +package com.stslex.wizard.feature.profile.mvi + +import com.stslex.wizard.core.navigation.Screen +import com.stslex.wizard.core.navigation.Screen.Follower.FollowerType.FOLLOWER +import com.stslex.wizard.core.navigation.Screen.Follower.FollowerType.FOLLOWING +import com.stslex.wizard.core.navigation.navigator.Navigator +import com.stslex.wizard.core.ui.mvi.v2.Handler +import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Navigation +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State + +class NavigationHandler( + private val navigator: Navigator +) : Handler(Navigation::class) { + + override fun HandlerStore.invoke(action: Navigation) { + when (action) { + Navigation.LogIn -> navigator.navTo(Screen.Auth) + Navigation.Back -> navigator.popBack() + Navigation.Settings -> navigator.navTo(Screen.Settings) + is Navigation.Favourite -> navigator.navTo(Screen.Favourite(action.uuid)) + is Navigation.Following -> navigator.navTo(Screen.Follower(FOLLOWING, action.uuid)) + is Navigation.Followers -> navigator.navTo(Screen.Follower(FOLLOWER, action.uuid)) + } + } + +} \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt new file mode 100644 index 0000000..54e9dc4 --- /dev/null +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt @@ -0,0 +1,26 @@ +package com.stslex.wizard.feature.profile.mvi + + +import com.stslex.wizard.core.ui.mvi.v2.Handler +import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.feature.profile.ui.store.ProfileScreenState +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.RepeatLastAction +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State + +class RepeatLastActionHandler : + Handler(RepeatLastAction::class) { + + override fun HandlerStore.invoke(action: RepeatLastAction) { + val lastAction = lastAction ?: return + updateState { currentState -> + val screen = when (val screen = currentState.screen) { + is ProfileScreenState.Content -> ProfileScreenState.Content.Loading(screen.data) + is ProfileScreenState.Error, is ProfileScreenState.Shimmer -> ProfileScreenState.Shimmer + } + currentState.copy(screen = screen) + } + sendAction(lastAction) + } +} \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/GraphProfile.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/GraphProfile.kt index 296e7ac..26c200b 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/GraphProfile.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/GraphProfile.kt @@ -10,13 +10,13 @@ import com.stslex.wizard.core.navigation.Screen import com.stslex.wizard.core.navigation.navScreen import com.stslex.wizard.core.ui.mvi.store_di.getStore import com.stslex.wizard.feature.profile.ui.ProfileScreen -import com.stslex.wizard.feature.profile.ui.store.ProfileStoreImpl +import com.stslex.wizard.feature.profile.ui.store.ProfileStore import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event fun NavGraphBuilder.graphProfile() { navScreen { screen -> - val store = getStore() + val store = getStore() LaunchedEffect(Unit) { store.sendAction( Action.Init( diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouter.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouter.kt deleted file mode 100644 index 8f7d899..0000000 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouter.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.stslex.wizard.feature.profile.navigation - -import com.stslex.wizard.core.ui.mvi.Router -import com.stslex.wizard.feature.profile.ui.store.ProfileStore - -interface ProfileRouter : Router \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouterImpl.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouterImpl.kt deleted file mode 100644 index 21fc5ef..0000000 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/navigation/ProfileRouterImpl.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.stslex.wizard.feature.profile.navigation - -import com.stslex.wizard.core.navigation.Screen -import com.stslex.wizard.core.navigation.navigator.Navigator -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Navigation - -class ProfileRouterImpl( - private val navigator: Navigator -) : ProfileRouter { - - override fun invoke(event: Navigation) { - when (event) { - Navigation.LogIn -> navigator.navTo(Screen.Auth) - Navigation.Back -> navigator.popBack() - Navigation.Settings -> navigator.navTo(Screen.Settings) - is Navigation.Favourite -> navigator.navTo(Screen.Favourite(uuid = event.uuid)) - is Navigation.Following -> navToFollowing(event) - is Navigation.Followers -> navToFollower(event) - } - } - - private fun navToFollower(event: Navigation.Followers) { - navigator.navTo( - Screen.Follower( - type = Screen.Follower.FollowerType.FOLLOWER, - uuid = event.uuid - ) - ) - } - - private fun navToFollowing(event: Navigation.Following) { - navigator.navTo( - Screen.Follower( - type = Screen.Follower.FollowerType.FOLLOWING, - uuid = event.uuid - ) - ) - } -} \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/ProfileScreen.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/ProfileScreen.kt index c3b7828..05e69ed 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/ProfileScreen.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/ProfileScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.SnackbarHostState @@ -31,7 +32,9 @@ internal fun ProfileScreen( modifier: Modifier = Modifier, ) { BoxWithConstraints( - modifier = modifier.fillMaxSize(), + modifier = modifier + .fillMaxSize() + .systemBarsPadding(), ) { when (val screen = state.screen) { is ProfileScreenState.Content -> ProfileScreenContent( diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/components/ProfileScreenContent.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/components/ProfileScreenContent.kt index 1a8961f..65c713c 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/components/ProfileScreenContent.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/components/ProfileScreenContent.kt @@ -19,8 +19,8 @@ internal fun ProfileScreenContent( ProfileScreenToolbar( nickname = state.data.username, isCurrentUser = state.data.isCurrentUser, - onSettingsClick = { onAction(Action.SettingsClick) }, - onBackClick = { onAction(Action.BackButtonClick) } + onSettingsClick = { onAction(Action.Click.SettingsClick) }, + onBackClick = { onAction(Action.Click.BackButtonClick) } ) ProfileAvatar(avatar = state.data.avatar) @@ -30,9 +30,9 @@ internal fun ProfileScreenContent( favouriteCount = state.data.favouriteCount, followingCount = state.data.following, followersCount = state.data.followers, - onFavouriteClick = { onAction(Action.FavouriteClick) }, - onFollowingClick = { onAction(Action.FollowingClick) }, - onFollowersClick = { onAction(Action.FollowersClick) }, + onFavouriteClick = { onAction(Action.Click.FavouriteClick) }, + onFollowingClick = { onAction(Action.Click.FollowingClick) }, + onFollowersClick = { onAction(Action.Click.FollowersClick) }, ) } diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStore.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStore.kt index fe7e868..d7c88b7 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStore.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStore.kt @@ -40,18 +40,20 @@ interface ProfileStore : Store { data object RepeatLastAction : Action, Store.Action.RepeatLast - data object FavouriteClick : Action + sealed interface Click : Action { - data object FollowingClick : Action + data object FavouriteClick : Click - data object FollowersClick : Action + data object FollowingClick : Click - data object SettingsClick : Action + data object FollowersClick : Click - data object BackButtonClick : Action + data object SettingsClick : Click + data object BackButtonClick : Click + } - sealed interface Navigation : Action, Store.Action.Navigation { + sealed interface Navigation : Action { data object LogIn : Navigation @@ -78,5 +80,6 @@ interface ProfileStore : Store { @Stable data class ShowSnackbar(val snackbar: CommonEvents.Snackbar) : Event + } } \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt index 09d69e2..0e522a4 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt @@ -1,135 +1,29 @@ package com.stslex.wizard.feature.profile.ui.store -import androidx.compose.ui.graphics.Color import com.stslex.wizard.core.database.store.UserStore -import com.stslex.wizard.core.navigation.Screen -import com.stslex.wizard.core.ui.mvi.BaseStore -import com.stslex.wizard.core.ui.mvi.CommonEvents.Snackbar +import com.stslex.wizard.core.navigation.navigator.Navigator +import com.stslex.wizard.core.ui.mvi.v2.BaseStore import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor -import com.stslex.wizard.feature.profile.navigation.ProfileRouter -import com.stslex.wizard.feature.profile.ui.model.ProfileAvatarModel -import com.stslex.wizard.feature.profile.ui.model.toUi +import com.stslex.wizard.feature.profile.mvi.ClickersHandler +import com.stslex.wizard.feature.profile.mvi.InitStorageHandler +import com.stslex.wizard.feature.profile.mvi.LogoutHandler +import com.stslex.wizard.feature.profile.mvi.NavigationHandler +import com.stslex.wizard.feature.profile.mvi.RepeatLastActionHandler import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State class ProfileStoreImpl( - private val interactor: ProfileInteractor, - private val userStore: UserStore, - private val router: ProfileRouter, -) : ProfileStore, BaseStore(State.INITIAL) { - - override fun process(action: Action) { - when (action) { - is Action.Init -> actionInit(action) - is Action.Logout -> actionLogout() - is Action.RepeatLastAction -> actionRepeatLastAction() - is Action.FavouriteClick -> actionFavouriteClick() - is Action.FollowingClick -> actionFollowingClick() - is Action.FollowersClick -> actionFollowersClick() - is Action.SettingsClick -> actionSettingsClick() - is Action.BackButtonClick -> actionBackClick() - is Action.Navigation -> router(action) - } - } - - private fun actionInit(action: Action.Init) { - val uuid = action.uuid.ifBlank { userStore.uuid } - - updateState { currentState -> - currentState.copy( - isSelf = action.type == Screen.Profile.Type.SELF, - uuid = uuid, - ) - } - - interactor.getProfile(uuid) - .launch( - onError = { error -> - updateState { currentState -> - currentState.copy( - screen = ProfileScreenState.Error(error) - ) - } - } - ) { profile -> - val avatar = if (profile.avatarUrl.isBlank()) { - ProfileAvatarModel.Empty( - color = Color.Gray, // TODO replace with random color - symbol = profile.username.firstOrNull()?.lowercase().orEmpty() - ) - } else { - ProfileAvatarModel.Content(profile.avatarUrl) - } - val profileUi = profile.toUi( - avatarModel = avatar - ) - updateState { currentState -> - currentState.copy( - screen = ProfileScreenState.Content.NotLoading(profileUi) - ) - } - } - } - - private fun actionFavouriteClick() { - sendAction(Action.Navigation.Favourite(state.value.uuid)) - } - - private fun actionFollowingClick() { - sendAction(Action.Navigation.Following(state.value.uuid)) - } - - private fun actionFollowersClick() { - sendAction(Action.Navigation.Followers(state.value.uuid)) - } - - private fun actionRepeatLastAction() { - val lastAction = lastAction ?: return - updateState { currentState -> - val screen = when (val screen = currentState.screen) { - is ProfileScreenState.Content -> ProfileScreenState.Content.Loading(screen.data) - is ProfileScreenState.Error, is ProfileScreenState.Shimmer -> ProfileScreenState.Shimmer - } - currentState.copy(screen = screen) - } - process(lastAction) - } - - private fun actionLogout() { - val currentScreen = state.value.screen - - if ( - currentScreen is ProfileScreenState.Content.Loading || - currentScreen is ProfileScreenState.Shimmer - ) { - return - } - - updateState { currentState -> - currentState.copy( - screen = ProfileScreenState.Shimmer - ) - } - - launch( - action = { - interactor.logOut() - }, - onSuccess = { - sendAction(Action.Navigation.LogIn) - }, - onError = { error -> - sendEvent(Event.ShowSnackbar(Snackbar.Error(error.message ?: "error logout"))) - } - ) - } - - private fun actionSettingsClick() { - sendAction(Action.Navigation.Settings) - } - - private fun actionBackClick() { - sendAction(Action.Navigation.Back) - } -} + interactor: ProfileInteractor, + userStore: UserStore, + navigator: Navigator +) : ProfileStore, BaseStore( + initialState = State.INITIAL, + handlers = setOf( + InitStorageHandler(interactor, userStore), + LogoutHandler(interactor), + RepeatLastActionHandler(), + ClickersHandler(), + NavigationHandler(navigator) + ) +) From 1bfba2276b3061e53c8dfae3d4f70fceb3a9f372 Mon Sep 17 00:00:00 2001 From: stslex Date: Mon, 9 Dec 2024 23:11:06 +0300 Subject: [PATCH 3/4] fun test function handlers --- .../com/stslex/wizard/core/ui/mvi/Store.kt | 5 +---- .../stslex/wizard/core/ui/mvi/v2/Handler.kt | 14 ------------- .../feature/profile/mvi/ClickersHandler.kt | 21 ++++++++----------- .../profile/ui/store/ProfileStoreImpl.kt | 5 +++-- 4 files changed, 13 insertions(+), 32 deletions(-) diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt index 3148cc6..1496c78 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/Store.kt @@ -24,10 +24,7 @@ interface Store { interface State - interface Event { - - data class Handler(val action: Action) : Event - } + interface Event interface Action { diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt index 3bf7837..06c2eb9 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt @@ -7,22 +7,8 @@ import kotlin.reflect.KClass abstract class Handler(val actionKClass: KClass<*>) { - val handlerName: String = requireNotNull(actionKClass.simpleName) { - "Action class name is null" - } - inline fun checkAction(action: StoreAction): Boolean = actionKClass.isInstance(action) abstract fun HandlerStore.invoke(action: A) - override fun toString(): String = handlerName - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Handler<*, *, *, *>) return false - return handlerName == other.handlerName - } - - override fun hashCode(): Int = handlerName.hashCode() - } diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt index 3fc9911..bbc76dd 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt @@ -1,21 +1,18 @@ package com.stslex.wizard.feature.profile.mvi import com.stslex.wizard.core.ui.mvi.v2.Handler -import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.core.ui.mvi.v2.handler import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Click import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State -class ClickersHandler : Handler(Click::class) { - - override fun HandlerStore.invoke(action: Click) { - when (action) { - Click.BackButtonClick -> sendAction(Action.Navigation.Back) - Click.FavouriteClick -> sendAction(Action.Navigation.Favourite(state.value.uuid)) - Click.FollowersClick -> sendAction(Action.Navigation.Followers(state.value.uuid)) - Click.FollowingClick -> sendAction(Action.Navigation.Following(state.value.uuid)) - Click.SettingsClick -> sendAction(Action.Navigation.Settings) - } +fun clickersHandler(): Handler = handler { action -> + when (action) { + Click.BackButtonClick -> sendAction(Action.Navigation.Back) + Click.FavouriteClick -> sendAction(Action.Navigation.Favourite(state.value.uuid)) + Click.FollowersClick -> sendAction(Action.Navigation.Followers(state.value.uuid)) + Click.FollowingClick -> sendAction(Action.Navigation.Following(state.value.uuid)) + Click.SettingsClick -> sendAction(Action.Navigation.Settings) } -} \ No newline at end of file +} diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt index 0e522a4..193ab4c 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt @@ -4,11 +4,11 @@ import com.stslex.wizard.core.database.store.UserStore import com.stslex.wizard.core.navigation.navigator.Navigator import com.stslex.wizard.core.ui.mvi.v2.BaseStore import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor -import com.stslex.wizard.feature.profile.mvi.ClickersHandler import com.stslex.wizard.feature.profile.mvi.InitStorageHandler import com.stslex.wizard.feature.profile.mvi.LogoutHandler import com.stslex.wizard.feature.profile.mvi.NavigationHandler import com.stslex.wizard.feature.profile.mvi.RepeatLastActionHandler +import com.stslex.wizard.feature.profile.mvi.clickersHandler import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State @@ -23,7 +23,8 @@ class ProfileStoreImpl( InitStorageHandler(interactor, userStore), LogoutHandler(interactor), RepeatLastActionHandler(), - ClickersHandler(), + clickersHandler(), NavigationHandler(navigator) ) ) + From 5ea63d95af08312a65687e2fec10c6d5416f8241 Mon Sep 17 00:00:00 2001 From: stslex Date: Sat, 14 Dec 2024 17:59:40 +0300 Subject: [PATCH 4/4] refactor handlers to match option --- .../core/ui/mvi/store_di/StoreBeanV2.kt | 12 +++---- .../stslex/wizard/core/ui/mvi/v2/BaseStore.kt | 9 +++--- .../wizard/core/ui/mvi/v2/BaseStoreImpl.kt | 8 ++--- .../stslex/wizard/core/ui/mvi/v2/Handler.kt | 9 ++---- .../wizard/core/ui/mvi/v2/HandlerCreator.kt | 10 ++++++ .../stslex/wizard/core/ui/mvi/v2/StoreExt.kt | 21 +++++-------- .../profile/di/ModuleFeatureProfile.kt | 12 +++++++ .../feature/profile/mvi/ClickersHandler.kt | 22 ++++++------- .../feature/profile/mvi/InitStorageHandler.kt | 9 ++---- .../feature/profile/mvi/LogoutHandler.kt | 7 ++--- .../feature/profile/mvi/NavigationHandler.kt | 9 ++---- .../profile/mvi/RepeatLastActionHandler.kt | 10 ++---- .../profile/ui/store/ProfileHandlerStore.kt | 8 +++++ .../profile/ui/store/ProfileStoreImpl.kt | 31 ++++++++++--------- 14 files changed, 93 insertions(+), 84 deletions(-) create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerCreator.kt create mode 100644 feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileHandlerStore.kt diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt index b082850..cce83b0 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/store_di/StoreBeanV2.kt @@ -5,32 +5,32 @@ import org.koin.core.definition.BeanDefinition import org.koin.core.definition.KoinDefinition import org.koin.core.module.Module -inline fun > Module.storeOf( +inline fun > Module.storeOf( crossinline constructor: () -> R, noinline options: (BeanDefinition.() -> Unit)? = null, ): KoinDefinition = viewModelOf(constructor, options) -inline fun , reified T1> Module.storeOf( +inline fun , reified T1> Module.storeOf( crossinline constructor: (T1) -> R, noinline options: (BeanDefinition.() -> Unit)? = null, ): KoinDefinition = viewModelOf(constructor, options) -inline fun , reified T1, reified T2> Module.storeOf( +inline fun , reified T1, reified T2> Module.storeOf( crossinline constructor: (T1, T2) -> R, noinline options: (BeanDefinition.() -> Unit)? = null, ): KoinDefinition = viewModelOf(constructor, options) -inline fun , reified T1, reified T2, reified T3> Module.storeOf( +inline fun , reified T1, reified T2, reified T3> Module.storeOf( crossinline constructor: (T1, T2, T3) -> R, noinline options: (BeanDefinition.() -> Unit)? = null, ): KoinDefinition = viewModelOf(constructor, options) -inline fun , reified T1, reified T2, reified T3, reified T4> Module.storeOf( +inline fun , reified T1, reified T2, reified T3, reified T4> Module.storeOf( crossinline constructor: (T1, T2, T3, T4) -> R, noinline options: (BeanDefinition.() -> Unit)? = null, ): KoinDefinition = viewModelOf(constructor, options) -inline fun , reified T1, reified T2, reified T3, reified T4, reified T5> Module.storeOf( +inline fun , reified T1, reified T2, reified T3, reified T4, reified T5> Module.storeOf( crossinline constructor: (T1, T2, T3, T4, T5) -> R, noinline options: (BeanDefinition.() -> Unit)? = null, ): KoinDefinition = viewModelOf(constructor, options) \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt index a36f35a..e38deec 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStore.kt @@ -22,9 +22,9 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -open class BaseStore( +open class BaseStore>( initialState: S, - private val handlers: Set> + private val handlerCreator: HandlerCreator, ) : ViewModel(), Store, HandlerStore { private val _event: MutableSharedFlow = MutableSharedFlow() @@ -44,9 +44,8 @@ open class BaseStore( if (lastAction != action && action !is Action.RepeatLast) { _lastAction = action } - val handler = handlers.firstOrNull { it.checkAction(action) } as? Handler - ?: throw IllegalStateException("Handler not found for action: ${action::class.simpleName}") - handler.invoke(this, action) + val handler = handlerCreator(action) as Handler + handler.invoke(this as HStore, action) } /** diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt index 6d175c6..374cdb1 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/BaseStoreImpl.kt @@ -4,10 +4,10 @@ import com.stslex.wizard.core.ui.mvi.Store.Action import com.stslex.wizard.core.ui.mvi.Store.Event import com.stslex.wizard.core.ui.mvi.Store.State -internal class BaseStoreImpl( +internal class BaseStoreImpl>( initialState: S, - handlers: Set> -) : BaseStore( + handlerCreator: HandlerCreator, +) : BaseStore( initialState = initialState, - handlers = handlers + handlerCreator = handlerCreator ) \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt index 06c2eb9..54eadda 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/Handler.kt @@ -1,14 +1,9 @@ package com.stslex.wizard.core.ui.mvi.v2 import com.stslex.wizard.core.ui.mvi.Store.Action -import com.stslex.wizard.core.ui.mvi.Store.Event -import com.stslex.wizard.core.ui.mvi.Store.State -import kotlin.reflect.KClass -abstract class Handler(val actionKClass: KClass<*>) { +fun interface Handler> { - inline fun checkAction(action: StoreAction): Boolean = actionKClass.isInstance(action) - - abstract fun HandlerStore.invoke(action: A) + fun TStore.invoke(action: A) } diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerCreator.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerCreator.kt new file mode 100644 index 0000000..41979d0 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/HandlerCreator.kt @@ -0,0 +1,10 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import com.stslex.wizard.core.ui.mvi.Store.Action +import com.stslex.wizard.core.ui.mvi.Store.Event +import com.stslex.wizard.core.ui.mvi.Store.State + +fun interface HandlerCreator> { + + operator fun invoke(action: A): Handler<*, HStore> +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt index c6040c4..0a939c1 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/wizard/core/ui/mvi/v2/StoreExt.kt @@ -6,13 +6,13 @@ import com.stslex.wizard.core.ui.mvi.Store.Event import com.stslex.wizard.core.ui.mvi.Store.State @Suppress("UNCHECKED_CAST") -fun > store( +fun , HStore : HandlerStore> store( initialState: S, - handlers: Set> -): TStore = BaseStoreImpl(initialState, handlers) as TStore + handlerCreator: HandlerCreator +): TStore = BaseStoreImpl(initialState, handlerCreator) as TStore -fun Handler.invoke( - store: HandlerStore, +fun > Handler.invoke( + store: HStore, action: A ) { with(store) { @@ -20,11 +20,6 @@ fun Handler handler( - crossinline block: HandlerStore.(action: A) -> Unit -) = object : Handler(A::class) { - - override fun HandlerStore.invoke(action: A) { - block(action) - } -} +inline fun > handler( + crossinline block: HandlerStore.(action: A) -> Unit +) = Handler { block(it) } diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt index 78fc82e..e2ac331 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/di/ModuleFeatureProfile.kt @@ -6,6 +6,11 @@ import com.stslex.wizard.feature.profile.data.repository.ProfileRepository import com.stslex.wizard.feature.profile.data.repository.ProfileRepositoryImpl import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractorImpl +import com.stslex.wizard.feature.profile.mvi.ClickersHandler +import com.stslex.wizard.feature.profile.mvi.InitStorageHandler +import com.stslex.wizard.feature.profile.mvi.LogoutHandler +import com.stslex.wizard.feature.profile.mvi.NavigationHandler +import com.stslex.wizard.feature.profile.mvi.RepeatLastActionHandler import com.stslex.wizard.feature.profile.ui.store.ProfileStore import com.stslex.wizard.feature.profile.ui.store.ProfileStoreImpl import org.koin.core.annotation.Module @@ -17,7 +22,14 @@ import org.koin.dsl.ModuleDeclaration class ModuleFeatureProfile : AppModule() { override fun declaration(): ModuleDeclaration = { + factoryOf(::InitStorageHandler) + factoryOf(::LogoutHandler) + factoryOf(::RepeatLastActionHandler) + factoryOf(::ClickersHandler) + factoryOf(::NavigationHandler) + storeOf(::ProfileStoreImpl) { bind() } + factoryOf(::ProfileInteractorImpl) { bind() } factoryOf(::ProfileRepositoryImpl) { bind() } } diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt index bbc76dd..72b8906 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/ClickersHandler.kt @@ -1,18 +1,18 @@ package com.stslex.wizard.feature.profile.mvi import com.stslex.wizard.core.ui.mvi.v2.Handler -import com.stslex.wizard.core.ui.mvi.v2.handler +import com.stslex.wizard.feature.profile.ui.store.ProfileHandlerStore import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Click -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State -fun clickersHandler(): Handler = handler { action -> - when (action) { - Click.BackButtonClick -> sendAction(Action.Navigation.Back) - Click.FavouriteClick -> sendAction(Action.Navigation.Favourite(state.value.uuid)) - Click.FollowersClick -> sendAction(Action.Navigation.Followers(state.value.uuid)) - Click.FollowingClick -> sendAction(Action.Navigation.Following(state.value.uuid)) - Click.SettingsClick -> sendAction(Action.Navigation.Settings) +class ClickersHandler : Handler { + + override fun ProfileHandlerStore.invoke(action: Action.Click) { + when (action) { + Action.Click.BackButtonClick -> sendAction(Action.Navigation.Back) + Action.Click.FavouriteClick -> sendAction(Action.Navigation.Favourite(state.value.uuid)) + Action.Click.FollowersClick -> sendAction(Action.Navigation.Followers(state.value.uuid)) + Action.Click.FollowingClick -> sendAction(Action.Navigation.Following(state.value.uuid)) + Action.Click.SettingsClick -> sendAction(Action.Navigation.Settings) + } } } diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt index 7a70763..c61fcd2 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/InitStorageHandler.kt @@ -4,22 +4,19 @@ import androidx.compose.ui.graphics.Color import com.stslex.wizard.core.database.store.UserStore import com.stslex.wizard.core.navigation.Screen import com.stslex.wizard.core.ui.mvi.v2.Handler -import com.stslex.wizard.core.ui.mvi.v2.HandlerStore import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor import com.stslex.wizard.feature.profile.ui.model.ProfileAvatarModel import com.stslex.wizard.feature.profile.ui.model.toUi +import com.stslex.wizard.feature.profile.ui.store.ProfileHandlerStore import com.stslex.wizard.feature.profile.ui.store.ProfileScreenState import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Init -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State class InitStorageHandler( private val interactor: ProfileInteractor, private val userStore: UserStore, -) : Handler(Init::class) { +) : Handler { - override fun HandlerStore.invoke(action: Init) { + override fun ProfileHandlerStore.invoke(action: Action.Init) { val uuid = action.uuid.ifBlank { userStore.uuid } updateState { currentState -> diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt index 3a6d81e..9e3bd54 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/LogoutHandler.kt @@ -2,18 +2,17 @@ package com.stslex.wizard.feature.profile.mvi import com.stslex.wizard.core.ui.mvi.CommonEvents.Snackbar import com.stslex.wizard.core.ui.mvi.v2.Handler -import com.stslex.wizard.core.ui.mvi.v2.HandlerStore import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor +import com.stslex.wizard.feature.profile.ui.store.ProfileHandlerStore import com.stslex.wizard.feature.profile.ui.store.ProfileScreenState import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State class LogoutHandler( private val interactor: ProfileInteractor, -) : Handler(Action.Logout::class) { +) : Handler { - override fun HandlerStore.invoke(action: Action.Logout) { + override fun ProfileHandlerStore.invoke(action: Action.Logout) { val currentScreen = state.value.screen if ( diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt index 231be6c..47bec5e 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/NavigationHandler.kt @@ -5,17 +5,14 @@ import com.stslex.wizard.core.navigation.Screen.Follower.FollowerType.FOLLOWER import com.stslex.wizard.core.navigation.Screen.Follower.FollowerType.FOLLOWING import com.stslex.wizard.core.navigation.navigator.Navigator import com.stslex.wizard.core.ui.mvi.v2.Handler -import com.stslex.wizard.core.ui.mvi.v2.HandlerStore -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action +import com.stslex.wizard.feature.profile.ui.store.ProfileHandlerStore import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.Navigation -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State class NavigationHandler( private val navigator: Navigator -) : Handler(Navigation::class) { +) : Handler { - override fun HandlerStore.invoke(action: Navigation) { + override fun ProfileHandlerStore.invoke(action: Navigation) { when (action) { Navigation.LogIn -> navigator.navTo(Screen.Auth) Navigation.Back -> navigator.popBack() diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt index 54e9dc4..3045e6c 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/mvi/RepeatLastActionHandler.kt @@ -2,17 +2,13 @@ package com.stslex.wizard.feature.profile.mvi import com.stslex.wizard.core.ui.mvi.v2.Handler -import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.feature.profile.ui.store.ProfileHandlerStore import com.stslex.wizard.feature.profile.ui.store.ProfileScreenState import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action.RepeatLastAction -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event -import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State -class RepeatLastActionHandler : - Handler(RepeatLastAction::class) { +class RepeatLastActionHandler : Handler { - override fun HandlerStore.invoke(action: RepeatLastAction) { + override fun ProfileHandlerStore.invoke(action: Action.RepeatLastAction) { val lastAction = lastAction ?: return updateState { currentState -> val screen = when (val screen = currentState.screen) { diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileHandlerStore.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileHandlerStore.kt new file mode 100644 index 0000000..c6e607f --- /dev/null +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileHandlerStore.kt @@ -0,0 +1,8 @@ +package com.stslex.wizard.feature.profile.ui.store + +import com.stslex.wizard.core.ui.mvi.v2.HandlerStore +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event +import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State + +interface ProfileHandlerStore : ProfileStore, HandlerStore \ No newline at end of file diff --git a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt index 193ab4c..c7e5c9e 100644 --- a/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt +++ b/feature/profile/src/commonMain/kotlin/com/stslex/wizard/feature/profile/ui/store/ProfileStoreImpl.kt @@ -1,30 +1,31 @@ package com.stslex.wizard.feature.profile.ui.store -import com.stslex.wizard.core.database.store.UserStore -import com.stslex.wizard.core.navigation.navigator.Navigator import com.stslex.wizard.core.ui.mvi.v2.BaseStore -import com.stslex.wizard.feature.profile.domain.interactor.ProfileInteractor +import com.stslex.wizard.feature.profile.mvi.ClickersHandler import com.stslex.wizard.feature.profile.mvi.InitStorageHandler import com.stslex.wizard.feature.profile.mvi.LogoutHandler import com.stslex.wizard.feature.profile.mvi.NavigationHandler import com.stslex.wizard.feature.profile.mvi.RepeatLastActionHandler -import com.stslex.wizard.feature.profile.mvi.clickersHandler import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Action import com.stslex.wizard.feature.profile.ui.store.ProfileStore.Event import com.stslex.wizard.feature.profile.ui.store.ProfileStore.State class ProfileStoreImpl( - interactor: ProfileInteractor, - userStore: UserStore, - navigator: Navigator -) : ProfileStore, BaseStore( + initStorageHandler: InitStorageHandler, + logoutHandler: LogoutHandler, + repeatLastActionHandler: RepeatLastActionHandler, + clickersHandler: ClickersHandler, + navigationHandler: NavigationHandler +) : ProfileHandlerStore, BaseStore( initialState = State.INITIAL, - handlers = setOf( - InitStorageHandler(interactor, userStore), - LogoutHandler(interactor), - RepeatLastActionHandler(), - clickersHandler(), - NavigationHandler(navigator) - ) + handlerCreator = { action -> + when (action) { + is Action.Init -> initStorageHandler + is Action.Logout -> logoutHandler + is Action.RepeatLastAction -> repeatLastActionHandler + is Action.Click -> clickersHandler + is Action.Navigation -> navigationHandler + } + } )