commonMain.pro.respawn.flowmvi.plugins.AwaitSubscribersPlugin.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-jvm Show documentation
Show all versions of core-jvm Show documentation
A Kotlin Multiplatform MVI library based on plugins that is simple, fast, powerful & flexible
package pro.respawn.flowmvi.plugins
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
import pro.respawn.flowmvi.api.FlowMVIDSL
import pro.respawn.flowmvi.api.MVIAction
import pro.respawn.flowmvi.api.MVIIntent
import pro.respawn.flowmvi.api.MVIState
import pro.respawn.flowmvi.api.StorePlugin
import pro.respawn.flowmvi.dsl.StoreBuilder
import pro.respawn.flowmvi.dsl.plugin
import kotlin.time.Duration
/**
* A class that manages the job that waits for subscribers to appear.
* This job is set by the [awaitSubscribersPlugin] when the store is launched.
* Use [await] or [complete] to manage the job.
* You can only complete the job once per one start of the store.
*/
public class SubscriberManager {
internal companion object {
internal const val Name = "AwaitSubscribersPlugin"
}
private var subscriber by atomic(null)
/**
* Start waiting for subscribers, suspending until the given number of subscribers arrive.
* After the subscribers arrived, this call will return immediately
*/
public suspend fun await() {
subscriber?.join()
}
/**
* Complete the wait period, freeing the store and coroutines that called [await] to continue.
*/
public fun complete() {
subscriber?.cancel()
}
/**
* Same as [complete], but suspends until completion
*/
public suspend fun completeAndWait(): Unit? = subscriber?.cancelAndJoin()
/**
* Starts waiting for the subscribers until either [this] [CoroutineScope] is cancelled or [complete] is called.
* Usually not called manually but rather launched by the [awaitSubscribersPlugin].
*/
public fun CoroutineScope.launch(timeout: Duration) {
subscriber = launch {
subscriber?.cancelAndJoin()
withTimeout(timeout) {
awaitCancellation()
}
}.apply {
invokeOnCompletion {
subscriber = null
}
}
}
}
/**
* Installs a new [awaitSubscribersPlugin]
* @see awaitSubscribersPlugin
*/
@FlowMVIDSL
public fun StoreBuilder.awaitSubscribers(
manager: SubscriberManager,
minSubs: Int = 1,
suspendStore: Boolean,
timeout: Duration = Duration.INFINITE,
name: String = SubscriberManager.Name,
): Unit = install(awaitSubscribersPlugin(manager, minSubs, suspendStore, timeout, name))
/**
* Installs a new plugin using [manager] that will start waiting for new subscribers when the store launches.
* The plugin will wait for [minSubs] subscribers for a maximum duration of [timeout].
* If [suspendStore] is true, then the store will not process any [MVIIntent]s while it waits for subscribers.
* This plugin starts waiting **after** plugins installed before have finished their [StorePlugin.onStart] block.
* By default, cannot be installed multiple times, but [name] can be overridden to allow that,
* provided that you do not reuse the [manager].
*/
@FlowMVIDSL
public fun awaitSubscribersPlugin(
manager: SubscriberManager,
minSubs: Int = 1,
suspendStore: Boolean = true,
timeout: Duration = Duration.INFINITE,
name: String = SubscriberManager.Name,
): StorePlugin = plugin {
this.name = name
onStart {
with(manager) {
launch(timeout)
if (suspendStore) await()
}
}
onStop {
manager.complete()
}
onSubscribe { _, subscriberCount ->
if (minSubs == subscriberCount + 1) manager.complete()
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy