Skip to content

Commit

Permalink
Updated libraries to the latest milestone (#24)
Browse files Browse the repository at this point in the history
* Updated libraries to the latest milestone

* Bumped ktlint to the latest milestone

* Fixed warnings from the new kotlin language features
  • Loading branch information
extmkv authored Mar 6, 2024
1 parent e6940de commit 555fafa
Show file tree
Hide file tree
Showing 40 changed files with 261 additions and 190 deletions.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img style='width: 500px' src="assets/mvi_logo.png" alt="adidas mvi Logo"/>
</div>

[![Kotlin](https://img.shields.io/badge/Kotlin-1.8.20-blue.svg?style=flat&logo=kotlin)](https://kotlinlang.org)
[![Kotlin](https://img.shields.io/badge/Kotlin-1.9.22-blue.svg?style=flat&logo=kotlin)](https://kotlinlang.org)
![Test workflow](https://github.com/adidas/mvi/actions/workflows/deploy_docs.yml/badge.svg)
[![adidas official](https://img.shields.io/badge/adidas-official-000000)](https://github.com/adidas)

Expand Down
23 changes: 13 additions & 10 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
[versions]
kotlin = "1.8.21"
coroutines = "1.7.1"
kotest = "5.6.2"
ktlint = "0.45.2"
mvi = "1.5.0"
activity = "1.7.1"
lifecycle = "2.6.1"
kotlin = "1.9.22"
coroutines = "1.8.0"
kotest = "5.8.0"
ktlint-lib = "1.2.1"
mvi = "1.6.0"
activity = "1.8.2"
lifecycle = "2.7.0"
ktlint-gradle = "12.1.0"
android-library = "8.3.0"
maven-publish = "0.25.2"

[libraries]
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
Expand All @@ -18,7 +21,7 @@ lifecycleRuntimeCompose = { module = "androidx.lifecycle:lifecycle-runtime-compo

[plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "10.3.0" }
mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.25.2" }
android-library = { id = "com.android.library", version = "8.0.1" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-gradle" }
mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "maven-publish" }
android-library = { id = "com.android.library", version.ref = "android-library" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
7 changes: 2 additions & 5 deletions mvi-compose/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import com.vanniktech.maven.publish.SonatypeHost

@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
Expand All @@ -13,7 +10,7 @@ kotlin {

android {
namespace = "com.adidas.mvi.compose"
compileSdk = 33
compileSdk = 34

defaultConfig {
minSdk = 24
Expand Down Expand Up @@ -41,7 +38,7 @@ android {
}

composeOptions {
kotlinCompilerExtensionVersion = "1.4.7"
kotlinCompilerExtensionVersion = "1.5.10"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.StateFlow

/**
* The [MviContainer] is a composable function for implementing the MVI in Jetpack Compose, providing a convenient way to observe the state of your ViewModel.
* The MVIContainer should be used in non-screen composable with a [com.adidas.confirmed.mvi.MviViewModel] attached, such as a ViewPager page that will be
* The MVIContainer should be used in non-screen composable with a ViewModel attached, such as a ViewPager page that will be
* included inside of a parent screen.
* @param state The current state of the MVI ViewModel. This parameter must be of type [StateFlow]<[State]<TViewState, TSideEffect>>, where TViewState is the type
* of the view state and TSideEffect is the type of the side effect.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.StateFlow
/**
* The [MviScreen] is a composable function for implementing the MVI Jetpack Compose, providing a convenient way to observe the state of your ViewModel
* inside a screen composable.
* @param state The current state of the [MviViewModel]. This parameter must be of type [StateFlow]<[State]<TViewState, TSideEffect>>, where TViewState is the type
* @param state The current state of the ViewModel or similar component. This parameter must be of type [StateFlow]<[State]<TViewState, TSideEffect>>, where TViewState is the type
* of the view state and TSideEffect is the type of the side effect.
* @param onSideEffect A lambda function that is called when a side effect is emitted by the MVI architecture. The TSideEffect parameter of the function
* is the emitted side effect.
Expand Down
4 changes: 1 addition & 3 deletions mvi/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import org.jlleitschuh.gradle.ktlint.KtlintExtension

@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed

plugins {
kotlin("jvm") version libs.versions.kotlin.get()
alias(libs.plugins.ktlint)
Expand All @@ -18,7 +16,7 @@ compileTestKotlin.kotlinOptions {
}

configure<KtlintExtension> {
version.set(libs.versions.ktlint.get())
version.set(libs.versions.ktlint.lib.get())
}

tasks.getByName<Test>("test") {
Expand Down
20 changes: 17 additions & 3 deletions mvi/src/main/kotlin/com/adidas/mvi/Logger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@ package com.adidas.mvi

public interface Logger {
public fun logIntent(intent: Loggable)
public fun logFailedIntent(intent: Loggable, throwable: Throwable)
public fun logTransformedNewState(transform: Loggable, previousState: Loggable, newState: Loggable)
public fun logFailedTransformNewState(transform: Loggable, state: Loggable, throwable: Throwable)

public fun logFailedIntent(
intent: Loggable,
throwable: Throwable,
)

public fun logTransformedNewState(
transform: Loggable,
previousState: Loggable,
newState: Loggable,
)

public fun logFailedTransformNewState(
transform: Loggable,
state: Loggable,
throwable: Throwable,
)
}
17 changes: 12 additions & 5 deletions mvi/src/main/kotlin/com/adidas/mvi/Multimap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ internal class Multimap<TKey : Any, TValue> {
val keys: Collection<TKey>
get() = Collections.unmodifiableSet(innerMap.keys)

fun put(key: TKey, value: TValue): MultimapEntry<TKey, TValue> {
fun put(
key: TKey,
value: TValue,
): MultimapEntry<TKey, TValue> {
val entry = MultimapEntry(key, value)

innerMap.getOrPut(key) {
Expand All @@ -19,15 +22,19 @@ internal class Multimap<TKey : Any, TValue> {
return entry
}

private fun putAllReplacing(key: TKey, values: List<MultimapEntry<TKey, TValue>>) {
private fun putAllReplacing(
key: TKey,
values: List<MultimapEntry<TKey, TValue>>,
) {
innerMap[key] = MutableList(values.size) { values[it] }
}

operator fun get(key: TKey): List<MultimapEntry<TKey, TValue>> = innerMap[key]?.let(Collections::unmodifiableList) ?: listOf()

operator fun <T : TKey> get(keyClass: KClass<T>): List<MultimapEntry<TKey, TValue>> = keys
.filter { keyClass.isInstance(it) }
.flatMap { get(it) }
operator fun <T : TKey> get(keyClass: KClass<T>): List<MultimapEntry<TKey, TValue>> =
keys
.filter { keyClass.isInstance(it) }
.flatMap { get(it) }

fun remove(multimapEntry: MultimapEntry<TKey, TValue>) {
val key = multimapEntry.key
Expand Down
1 change: 0 additions & 1 deletion mvi/src/main/kotlin/com/adidas/mvi/MviHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import kotlinx.coroutines.flow.Flow
*
*/
public interface MviHost<in TIntent : Intent, out TState : LoggableState> {

public val state: Flow<TState>

public fun execute(intent: TIntent)
Expand Down
41 changes: 22 additions & 19 deletions mvi/src/main/kotlin/com/adidas/mvi/Reducer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,34 @@ public class Reducer<TIntent, TState>(
initialState: TState,
private val logger: Logger? = null,
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
private val intentExecutor: IntentExecutor<TIntent, TState>
private val intentExecutor: IntentExecutor<TIntent, TState>,
) where TIntent : Intent,
TState : LoggableState {

private val _transforms = MutableSharedFlow<StateTransform<TState>>()
TState : LoggableState {
private val transforms = MutableSharedFlow<StateTransform<TState>>()
private val multimap = Multimap<TIntent, Job>()

public val state: StateFlow<TState> = _transforms
.scan(initialState, this::reduce)
.stateIn(coroutineScope, SharingStarted.Eagerly, initialState)
public val state: StateFlow<TState> =
transforms
.scan(initialState, this::reduce)
.stateIn(coroutineScope, SharingStarted.Eagerly, initialState)

public fun executeIntent(intent: TIntent) {
logger?.logIntent(intent)

val job = coroutineScope.launch {
if (intent is UniqueIntent) {
cleanIntentJobsOfType(intent::class)
}
val job =
coroutineScope.launch {
if (intent is UniqueIntent) {
cleanIntentJobsOfType(intent::class)
}

try {
_transforms.emitAll(intentExecutor.executeIntent(intent))
} catch (throwable: Throwable) {
if (coroutineScope.isActive && throwable !is TerminatedIntentException) {
logger?.logFailedIntent(intent, throwable)
try {
transforms.emitAll(intentExecutor.executeIntent(intent))
} catch (throwable: Throwable) {
if (coroutineScope.isActive && throwable !is TerminatedIntentException) {
logger?.logFailedIntent(intent, throwable)
}
}
}
}

val entry = multimap.put(intent, job)

Expand All @@ -65,8 +66,10 @@ public class Reducer<TIntent, TState>(

public inline fun <reified T : TState> requireState(): T = state.value as T

@Suppress("FunctionName")
private suspend fun reduce(previousState: TState, transform: StateTransform<TState>): TState {
private suspend fun reduce(
previousState: TState,
transform: StateTransform<TState>,
): TState {
return try {
transform.reduce(previousState, defaultDispatcher).also { newState ->
logger?.logTransformedNewState(transform, previousState, newState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import kotlin.reflect.KClass

public class StateRequiredNotFulfilledException(
expectedState: KClass<*>,
realState: KClass<*>
realState: KClass<*>,
) : Exception() {

override val message: String = "Unexpected state! State was $realState, but the expected was $expectedState"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public fun <TIntent : Intent, TInnerState : LoggableState, TAction> Reducer(
initialInnerState: TInnerState,
intentExecutor: IntentExecutor<TIntent, State<TInnerState, TAction>>,
logger: Logger? = null,
defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
): Reducer<TIntent, State<TInnerState, TAction>> {
return Reducer(
coroutineScope = coroutineScope,
initialState = State(initialInnerState, SideEffects()),
intentExecutor = intentExecutor,
logger = logger,
defaultDispatcher = defaultDispatcher
defaultDispatcher = defaultDispatcher,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ package com.adidas.mvi.requirements

internal class DoubleReduceRequirement<TState>(
private val leftReduceRequirement: ReduceRequirement<TState>,
private val rightReduceRequirement: ReduceRequirement<TState>
private val rightReduceRequirement: ReduceRequirement<TState>,
) : ReduceRequirement<TState> {

override fun reduce(state: TState): TState {
return try {
leftReduceRequirement.reduce(state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import com.adidas.mvi.LoggableState

public inline fun <TState : LoggableState, reified TRequiredState : TState> requireAndReduceState(
state: TState,
noinline reduce: (TRequiredState) -> TState
noinline reduce: (TRequiredState) -> TState,
): TState {
return StateReduceRequirement(TRequiredState::class, reduce).reduce(state)
}

public inline fun <TState : LoggableState, reified TRequiredState : TState> requireState(
noinline reduce: (TRequiredState) -> TState
noinline reduce: (TRequiredState) -> TState,
): ReduceRequirement<TState> {
return StateReduceRequirement(TRequiredState::class, reduce)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import kotlin.reflect.cast

public class StateReduceRequirement<TState, TRequiredState>(
private val expectedState: KClass<out TRequiredState>,
private val reduceFunction: (TRequiredState) -> TState
private val reduceFunction: (TRequiredState) -> TState,
) : ReduceRequirement<TState> where TState : LoggableState, TRequiredState : TState {

public override fun reduce(state: TState): TState {
if (expectedState.isInstance(state)) {
return reduceFunction(expectedState.cast(state))
Expand Down
14 changes: 7 additions & 7 deletions mvi/src/main/kotlin/com/adidas/mvi/sideeffects/SideEffects.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import java.util.Queue
* It locks itself, so you can't add and read at the same time, also it's not possible to read it at the same time from different threads, being completely thread-safe.
*/
public class SideEffects<T>() : Iterable<T> {

private val sideEffects: Queue<T> = LinkedList()

private constructor(sideEffects: Iterable<T>) : this() {
Expand All @@ -24,10 +23,11 @@ public class SideEffects<T>() : Iterable<T> {
return SideEffects()
}

override fun iterator(): Iterator<T> = iterator<T> {
do {
val nextSideEffect: T? = sideEffects.poll()
nextSideEffect?.let { yield(it) }
} while (nextSideEffect != null)
}
override fun iterator(): Iterator<T> =
iterator {
do {
val nextSideEffect: T? = sideEffects.poll()
nextSideEffect?.let { yield(it) }
} while (nextSideEffect != null)
}
}
10 changes: 8 additions & 2 deletions mvi/src/main/kotlin/com/adidas/mvi/transform/Operators.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ public fun <TOwner> notOperator(function: (TOwner) -> Boolean): (TOwner) -> Bool
}
}

public fun <TOwner, TValue> equalsOperator(retriever: (TOwner) -> TValue, valueToCompare: TValue): (TOwner) -> Boolean {
public fun <TOwner, TValue> equalsOperator(
retriever: (TOwner) -> TValue,
valueToCompare: TValue,
): (TOwner) -> Boolean {
return {
retriever(it) == valueToCompare
}
}

public fun <TOwner, TValue> notEqualsOperator(retriever: (TOwner) -> TValue, valueToCompare: TValue): (TOwner) -> Boolean {
public fun <TOwner, TValue> notEqualsOperator(
retriever: (TOwner) -> TValue,
valueToCompare: TValue,
): (TOwner) -> Boolean {
return notOperator(equalsOperator(retriever, valueToCompare))
}
7 changes: 5 additions & 2 deletions mvi/src/main/kotlin/com/adidas/mvi/transform/Replace.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.adidas.mvi.transform

public fun <T> List<T>.replaceIf(evaluation: (T) -> Boolean, producer: (T) -> T): List<T> {
public fun <T> List<T>.replaceIf(
evaluation: (T) -> Boolean,
producer: (T) -> T,
): List<T> {
return map {
if (evaluation(it)) {
return@map producer(it)
Expand All @@ -12,7 +15,7 @@ public fun <T> List<T>.replaceIf(evaluation: (T) -> Boolean, producer: (T) -> T)

public inline fun <TOld, reified TNew : TOld> List<TOld>.replaceIfIs(
evaluation: (TNew) -> Boolean = { true },
producer: (TNew) -> TOld
producer: (TNew) -> TOld,
): List<TOld> {
return map {
if (it is TNew && evaluation(it)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.adidas.mvi.sideeffects.SideEffects

public abstract class SideEffectTransform<TView, TSideEffect> :
StateTransform<State<TView, TSideEffect>> {

protected abstract fun mutate(sideEffects: SideEffects<TSideEffect>): SideEffects<TSideEffect>

final override fun reduce(currentState: State<TView, TSideEffect>): State<TView, TSideEffect> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import kotlinx.coroutines.CoroutineDispatcher
public interface StateTransform<TState> : Loggable {
public suspend fun reduce(
currentState: TState,
defaultDispatcher: CoroutineDispatcher
defaultDispatcher: CoroutineDispatcher,
): TState {
return this.reduce(currentState)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.adidas.mvi.transform
import com.adidas.mvi.State

public abstract class ViewTransform<TView, TSideEffect> : StateTransform<State<TView, TSideEffect>> {

protected abstract fun mutate(currentState: TView): TView

final override fun reduce(currentState: State<TView, TSideEffect>): State<TView, TSideEffect> {
Expand Down
Loading

0 comments on commit 555fafa

Please sign in to comment.