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

Обновление Stateholder #33

Merged
merged 2 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package ru.surfstudio.mvi.core.reducer

import ru.surfstudio.mvi.core.event.Event
import ru.surfstudio.mvi.core.state.MutableState
import ru.surfstudio.mvi.core.state.StateHolder

/**
* [Reducer] in terms of `Redux`:
Expand All @@ -26,9 +26,9 @@ import ru.surfstudio.mvi.core.state.MutableState
*
* [Reducers documentation](https://redux.js.org/basics/reducers)
*/
interface Reducer<E : Event, State> : Reactor<E, MutableState<State, *>> {
interface Reducer<E : Event, State> : Reactor<E, StateHolder<State>> {

override fun react(sh: MutableState<State, *>, event: E) {
override fun react(sh: StateHolder<State>, event: E) {
val oldState = sh.currentState
val newState = reduce(oldState, event)
if (isStateChanged(oldState, newState)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package ru.surfstudio.mvi.core.state
/**
* State that could be modified by emitting new value
*/
interface MutableState<State, ImmutableStateStream> : ImmutableState<State, ImmutableStateStream> {
interface MutableState<State> {

/**
* Emits [newState] and notifies all subscribers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2022 Surf LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ru.surfstudio.mvi.core.state

import kotlinx.coroutines.flow.Flow

/**
* State that could be observed and changed.
*/
interface StateHolder<S>: MutableState<S>, ImmutableState<S, Flow<S>>
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ package ru.surfstudio.mvi.flow

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import ru.surfstudio.mvi.core.state.MutableState
import ru.surfstudio.mvi.core.state.StateHolder

/**
* State implementing with Coroutines Flow, that could be observed and changed.
*/
class FlowState<S>(initialState: S): MutableState<S, Flow<S>> {
class FlowStateHolder<S>(initialState: S) : StateHolder<S> {

private val mutableFlow = MutableStateFlow(initialState)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import ru.surfstudio.mvi.core.reducer.Reducer
import ru.surfstudio.mvi.flow.DslFlowMiddleware
import ru.surfstudio.mvi.flow.FlowBinder
import ru.surfstudio.mvi.flow.FlowEventHub
import ru.surfstudio.mvi.flow.FlowState
import ru.surfstudio.mvi.flow.FlowStateHolder

/**
* An interface of ViewModel providing implementations of observable
Expand All @@ -45,11 +45,11 @@ abstract class MviViewModel<E : Event> : ViewModel(), FlowBinder {
*/
abstract class MviStatefulViewModel<S : Any, E : Event>: MviViewModel<E>() {

abstract val state: FlowState<S>
abstract val stateHolder: FlowStateHolder<S>
abstract val reducer: Reducer<E, S>

/** Must be called in descendant class `init` */
override fun bindFlow() {
viewModelScope.bind(hub, middleware, state, reducer)
viewModelScope.bind(hub, middleware, stateHolder, reducer)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ interface MviStatefulView<S : Any, E : Event> : MviView<E> {
*/
fun observeState(collector: suspend (S) -> Unit) {
uiScope.launch(Dispatchers.Main) {
viewModel.state
viewModel.stateHolder
.observeState()
.collect { collector(it) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import ru.surfstudio.mvi.vm.MviStatefulViewModel
infix fun <S : Any, E : Event> MviStatefulViewModel<S, E>.renders(
render: @Composable ComposedViewContext<E>.(S) -> Unit
) {
val state by state.observeState().collectAsState(initial = state.currentState)
val state by stateHolder.observeState().collectAsState(initial = stateHolder.currentState)
val scope = rememberCoroutineScope()

val composedViewContext = ComposedViewContext<E> { event ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import ru.surfstudio.mvi.core.event.Event
import ru.surfstudio.mvi.core.reducer.Reducer
import ru.surfstudio.mvi.flow.DslFlowMiddleware
import ru.surfstudio.mvi.flow.FlowEventHub
import ru.surfstudio.mvi.flow.FlowState
import ru.surfstudio.mvi.flow.FlowStateHolder
import ru.surfstudio.mvi.vm.MviStatefulViewModel
import ru.surfstudio.mvi.vm.android.MviStatefulView

Expand Down Expand Up @@ -99,7 +99,7 @@ class TestViewModel(
reducer: TestReducer
) : MviStatefulViewModel<TestState, TestEvent>() {

override val state: FlowState<TestState> = FlowState(TestState())
override val stateHolder: FlowStateHolder<TestState> = FlowStateHolder(TestState())
override val hub: FlowEventHub<TestEvent> = FlowEventHub()
override val reducer: Reducer<TestEvent, TestState> = reducer
override val middleware: DslFlowMiddleware<TestEvent> = middleware
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class MviCoreTest : BaseFlowTest() {

@Test
fun testStateChanges() = runTest {
val flow = testView?.viewModel?.state?.observeState()
val flow = testView?.viewModel?.stateHolder?.observeState()

Assert.assertEquals(flow?.firstOrNull()?.state, INITIAL_STATE_VALUE)
testView?.emit(TestEvent.Data("test"))
Expand All @@ -57,7 +57,7 @@ class MviCoreTest : BaseFlowTest() {

@Test
fun testStateUnchangedOnLogic() = runTest {
val flow = testView?.viewModel?.state?.observeState()
val flow = testView?.viewModel?.stateHolder?.observeState()

Assert.assertEquals(flow?.firstOrNull()?.state, INITIAL_STATE_VALUE)
testView?.emit(TestEvent.Logic)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ interface MVIErrorHandlerView<S : Any, E : Event> {
*/
fun observeState(collector: suspend (S) -> Unit) {
uiScope.launch(Dispatchers.Main) {
viewModel.state
viewModel.stateHolder
.observeState()
.collect { collector(it) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package ru.surfstudio.mvi.flow.app.compose.standard

import ru.surfstudio.mvi.flow.FlowState
import ru.surfstudio.mvi.flow.FlowStateHolder
import ru.surfstudio.mvi.flow.app.reused.error.ErrorHandlerImpl
import ru.surfstudio.mvi.flow.app.network.IpNetworkCreator
import ru.surfstudio.mvi.flow.app.reused.NetworkCommandEvent
Expand All @@ -29,7 +29,7 @@ import ru.surfstudio.mvi.vm.compose.emitCommand
class ComposeViewModel : MviErrorHandlerViewModel<NetworkState, NetworkEvent>(),
CommandObserver<NetworkEvent, NetworkCommandEvent> {

override val state: FlowState<NetworkState> = FlowState(NetworkState())
override val stateHolder: FlowStateHolder<NetworkState> = FlowStateHolder(NetworkState())
override val middleware: ComposeMiddleware =
ComposeMiddleware(IpNetworkCreator.repository)
override val reducer: NetworkReducer = NetworkReducer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package ru.surfstudio.mvi.flow.app.handler

import kotlinx.coroutines.CoroutineDispatcher
import ru.surfstudio.mvi.flow.FlowState
import ru.surfstudio.mvi.flow.FlowStateHolder
import ru.surfstudio.mvi.flow.app.reused.error.ErrorHandlerImpl
import ru.surfstudio.mvi.flow.app.network.IpRepository
import ru.surfstudio.mvi.flow.app.reused.NetworkCommandEvent
Expand All @@ -34,7 +34,7 @@ class HandlerViewModel(
) : MviErrorHandlerViewModel<NetworkState, NetworkEvent>(),
CommandObserver<NetworkEvent, NetworkCommandEvent> {

override val state: FlowState<NetworkState> = FlowState(NetworkState())
override val stateHolder: FlowStateHolder<NetworkState> = FlowStateHolder(NetworkState())
override val middleware: HandlerMiddleware =
HandlerMiddleware(loadOnStart, repository, dispatcher)
override val reducer: NetworkReducer = NetworkReducer(ErrorHandlerImpl(), ::emitCommand)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import ru.surfstudio.mvi.flow.app.handler.HandlerActivity
import ru.surfstudio.mvi.flow.app.simple.request.RequestState
import ru.surfstudio.mvi.vm.android.MviStatefulView

class SimpleActivity : AppCompatActivity(), MviStatefulView<SimpleState, SimpleEvent> {
internal class SimpleActivity : AppCompatActivity(), MviStatefulView<SimpleState, SimpleEvent> {

override val viewModel by viewModels<SimpleViewModel>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import ru.surfstudio.mvi.core.event.Event
import ru.surfstudio.mvi.core.event.MviLifecycleEvent
import ru.surfstudio.mvi.flow.app.simple.request.RequestState

sealed class SimpleEvent : Event {
internal sealed class SimpleEvent : Event {

data class LifecycleEvent(override var event: Lifecycle.Event) : SimpleEvent(),
MviLifecycleEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,23 @@ import androidx.lifecycle.Lifecycle
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import ru.surfstudio.mvi.flow.BaseFlowMiddleware
import ru.surfstudio.mvi.flow.FlowState
import ru.surfstudio.mvi.flow.FlowStateHolder
import ru.surfstudio.mvi.flow.app.simple.request.RequestState
import ru.surfstudio.mvi.flow.app.simple.SimpleEvent.*
import java.io.IOException

class SimpleMiddleware(
private val state: FlowState<SimpleState>
internal class SimpleMiddleware(
private val stateHolder: FlowStateHolder<SimpleState>
) : BaseFlowMiddleware<SimpleEvent> {

private val state: SimpleState
get() = stateHolder.currentState

override fun transform(eventStream: Flow<SimpleEvent>): Flow<SimpleEvent> {
return eventStream.transformations {
addAll(
StartLoadingClick::class
filter { state.currentState.request == RequestState.None }
filter { state.request == RequestState.None }
streamToStream { requestFlow(it) },
SimpleClick::class react {
println("debug react sample")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ package ru.surfstudio.mvi.flow.app.simple
import ru.surfstudio.mvi.core.reducer.Reducer
import ru.surfstudio.mvi.flow.app.simple.request.RequestState

data class SimpleState(
internal data class SimpleState(
val title: String = "Кликай меня полностью",
val counter: Int = 42,
val request: RequestState = RequestState.None
)

class SimpleReducer: Reducer<SimpleEvent, SimpleState> {
internal class SimpleReducer: Reducer<SimpleEvent, SimpleState> {

override fun reduce(state: SimpleState, event: SimpleEvent): SimpleState {
return when(event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@
*/
package ru.surfstudio.mvi.flow.app.simple

import ru.surfstudio.mvi.flow.FlowState
import ru.surfstudio.mvi.flow.FlowStateHolder
import ru.surfstudio.mvi.vm.MviStatefulViewModel

class SimpleViewModel : MviStatefulViewModel<SimpleState, SimpleEvent>() {
internal class SimpleViewModel : MviStatefulViewModel<SimpleState, SimpleEvent>() {

override val state: FlowState<SimpleState> = FlowState(SimpleState())
override val middleware: SimpleMiddleware = SimpleMiddleware(state)
override val stateHolder: FlowStateHolder<SimpleState> = FlowStateHolder(SimpleState())
override val middleware: SimpleMiddleware = SimpleMiddleware(stateHolder)
override val reducer: SimpleReducer = SimpleReducer()

init {
bindFlow()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ abstract class BaseMviScreenTest : BaseUnitTest() {
expectedStatesChecks: List<(S) -> Boolean>
) {
runTimeoutTest {
state.observeState().test {
stateHolder.observeState().test {
startEvent?.let { hub.emit(startEvent) }
(expectedStatesChecks.indices).forEach { index ->
val state = awaitItem()
Expand All @@ -111,7 +111,7 @@ abstract class BaseMviScreenTest : BaseUnitTest() {
expectedFinalStateCheck: (S) -> Boolean
) {
runTimeoutTest {
state.observeState().test {
stateHolder.observeState().test {
startEvents?.let { startEvents.forEach { hub.emit(it) } }
while (true) {
val state = awaitItem()
Expand All @@ -133,7 +133,7 @@ abstract class BaseMviScreenTest : BaseUnitTest() {
runTimeoutTest {
merge(
hub.observe().flatMapLatest { flowOf(MviData<S, E>(event = it)) },
state.observeState().flatMapLatest { flowOf(MviData(state = it)) },
stateHolder.observeState().flatMapLatest { flowOf(MviData(state = it)) },
).test {
startAction()
(expectedData.indices).forEach { index ->
Expand Down