-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
345 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
plugins { | ||
id("kmm-library-convention") | ||
id("kmp-library-convention") | ||
} | ||
|
||
version = libs.versions.mvm.get() | ||
|
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
plugins { | ||
id("kmm-library-convention") | ||
id("kmp-library-convention") | ||
} | ||
|
||
version = libs.versions.mvm.get() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
|
||
public interface Intent<TState, out TEffect> { | ||
|
||
public fun flowOf(state: TState): Flow<Update<TState, TEffect>> | ||
|
||
public sealed interface Update<out TState, out TEffect> { | ||
|
||
public data class State<TState>(public val state: TState): Update<TState, Nothing> | ||
|
||
public data class Effect<TEffect>(public val effect: TEffect): Update<Nothing, TEffect> | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
mvi/src/commonMain/kotlin/app/meetacy/vm/mvi/IntentBuilder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.flow.FlowCollector | ||
import kotlin.jvm.JvmName | ||
|
||
@DslMarker | ||
public annotation class IntentBuilderDsl | ||
|
||
public class IntentBuilder<TState, TEffect>( | ||
initial: TState, | ||
public val scope: CoroutineScope, | ||
private val collector: FlowCollector<Intent.Update<TState, TEffect>>, | ||
) { | ||
private var _state = initial | ||
|
||
@IntentBuilderDsl | ||
public val currentState: TState get() = _state | ||
|
||
@IntentBuilderDsl | ||
public suspend fun reduce(transform: suspend TState.() -> TState) { | ||
_state = currentState.transform() | ||
collector.emit(Intent.Update.State(currentState)) | ||
} | ||
|
||
@JvmName("performEffect") | ||
@IntentBuilderDsl | ||
public suspend fun perform(effect: TEffect) { | ||
collector.emit(Intent.Update.Effect(effect)) | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
mvi/src/commonMain/kotlin/app/meetacy/vm/mvi/IntentHost.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.channelFlow | ||
|
||
public interface IntentHost<TState, TEffect> | ||
|
||
@IntentBuilderDsl | ||
public inline fun <TState, TEffect> IntentHost<TState, TEffect>.intent( | ||
crossinline builder: suspend IntentBuilder<TState, TEffect>.() -> Unit | ||
): Intent<TState, TEffect> = buildIntent(builder) | ||
|
||
public inline fun <TState, TEffect> buildIntent( | ||
crossinline builder: suspend IntentBuilder<TState, TEffect>.() -> Unit | ||
): Intent<TState, TEffect> = object : Intent<TState, TEffect> { | ||
override fun flowOf(state: TState): Flow<Intent.Update<TState, TEffect>> = channelFlow { | ||
val intent = IntentBuilder( | ||
state, | ||
scope = this, | ||
collector = { this.send(it) } | ||
) | ||
intent.run { builder() } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
mvi/src/commonMain/kotlin/app/meetacy/vm/mvi/StateHolder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import app.meetacy.vm.flow.CFlow | ||
import app.meetacy.vm.flow.CStateFlow | ||
|
||
public abstract class StateHolder<TState, TEffect> { | ||
|
||
public abstract val effects: CFlow<TEffect> | ||
public abstract val states: CStateFlow<TState> | ||
|
||
public abstract suspend fun accept(intent: Intent<TState, TEffect>) | ||
public abstract suspend fun accept(effect: TEffect) | ||
public abstract fun accept(newState: TState) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import app.meetacy.vm.flow.CFlow | ||
import app.meetacy.vm.flow.CStateFlow | ||
import kotlinx.coroutines.channels.Channel | ||
import kotlinx.coroutines.flow.* | ||
|
||
public interface StateHost<TState, TEffect> { | ||
|
||
public val holder: StateHolder<TState, TEffect> | ||
} | ||
|
||
public fun <TState, TEffect> StateHost<TState, TEffect>.holder( | ||
initial: TState | ||
): StateHolder<TState, TEffect> = object : StateHolder<TState, TEffect>() { | ||
private val _effects: Channel<TEffect> = Channel(Channel.BUFFERED) | ||
private val _states: MutableStateFlow<TState> = MutableStateFlow(initial) | ||
|
||
override val effects: CFlow<TEffect> = CFlow(_effects.receiveAsFlow()) | ||
override val states: CStateFlow<TState> = CStateFlow(_states.asStateFlow()) | ||
|
||
private val collector: FlowCollector<Intent.Update<TState, TEffect>> = FlowCollector { value -> | ||
when (value) { | ||
is Intent.Update.State -> _states.emit(value.state) | ||
is Intent.Update.Effect -> _effects.send(value.effect) | ||
} | ||
} | ||
|
||
override suspend fun accept(effect: TEffect) { | ||
_effects.send(effect) | ||
} | ||
|
||
override fun accept(newState: TState) { | ||
_states.update { newState } | ||
} | ||
|
||
override suspend fun accept(intent: Intent<TState, TEffect>) { | ||
intent.flowOf(states.value).collect(collector) | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
mvi/src/commonMain/kotlin/app/meetacy/vm/mvi/StateHostedViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import app.meetacy.vm.ViewModel | ||
import app.meetacy.vm.extension.launchIn | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Job | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.launch | ||
|
||
public abstract class StateHostedViewModel<TState, TEffect>: ViewModel(), StateHost<TState, TEffect> { | ||
|
||
protected fun viewModelScopeLaunch(block: suspend CoroutineScope.() -> Unit) { | ||
viewModelScope.launch(block = block) | ||
} | ||
|
||
protected fun <T> Flow<T>.observe(block: suspend (T) -> Unit): Job = launchIn(viewModelScope, block) | ||
|
||
protected fun accept(intent: Intent<TState, TEffect>) { | ||
viewModelScope.launch { holder.accept(intent) } | ||
} | ||
|
||
protected fun mutateState(transform: TState.() -> TState) { | ||
holder.accept(holder.states.value.transform()) | ||
} | ||
|
||
protected fun accept(effect: TEffect) { | ||
viewModelScope.launch { holder.accept(effect) } | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
mvi/src/commonTest/kotlin/app/meetacy/vm/mvi/SignUpHost.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import kotlinx.coroutines.CoroutineScope | ||
|
||
object SignUpHost: IntentHost<SignUpHost.State, SignUpHost.SideEffect > { | ||
interface RegisterUseCase { | ||
|
||
suspend fun register(userName: String): Result<Unit> | ||
} | ||
|
||
sealed interface SideEffect { | ||
object RouteMain : SideEffect | ||
object ShowError : SideEffect | ||
} | ||
|
||
data class State( | ||
val userName: String, | ||
val isLoading: Boolean | ||
) { | ||
companion object { | ||
val Initial = State( | ||
userName = "", | ||
isLoading = true | ||
) | ||
} | ||
} | ||
|
||
fun signUpIntent( | ||
text: String, | ||
useCase: RegisterUseCase | ||
) = intent { | ||
reduce { | ||
copy( | ||
isLoading = true, | ||
userName = text | ||
) | ||
} | ||
|
||
useCase.register(currentState.userName).onSuccess { | ||
perform(SideEffect.RouteMain) | ||
}.onFailure { | ||
perform(SideEffect.ShowError) | ||
} | ||
|
||
reduce { copy(isLoading = false) } | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
|
||
interface SomeUseCase { | ||
|
||
fun getFlow(): Flow<Int> | ||
} |
20 changes: 20 additions & 0 deletions
20
mvi/src/commonTest/kotlin/app/meetacy/vm/mvi/SomeViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package app.meetacy.vm.mvi | ||
|
||
import app.meetacy.vm.extension.launchIn | ||
|
||
class SomeViewModel: StateHostedViewModel<SomeViewModel.State, SomeViewModel.Effect>() { | ||
|
||
override val holder: StateHolder<State, Effect> = holder(State()) | ||
data class State(val isLoading: Boolean = true) | ||
|
||
sealed interface Effect | ||
|
||
companion object : IntentHost<State, Effect> { | ||
|
||
fun subscription(useCase: SomeUseCase) = intent { | ||
useCase.getFlow().launchIn(scope) { value -> | ||
reduce { copy(isLoading = value % 3 == 0) } | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.