diff --git a/core/src/jsMain/kotlin/dev/fritz2/tracking/tracking.kt b/core/src/jsMain/kotlin/dev/fritz2/tracking/tracking.kt new file mode 100644 index 000000000..0673d51b7 --- /dev/null +++ b/core/src/jsMain/kotlin/dev/fritz2/tracking/tracking.kt @@ -0,0 +1,38 @@ +package dev.fritz2.tracking + +import dev.fritz2.binding.Store +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.debounce + +/** + * convenience method for creating a [Tracker] + */ +fun tracker(defaultTransaction: String = "...", debounceTimeout: Long = 100): Tracker = + Tracker(defaultTransaction, debounceTimeout) + +/** + * tracks running transactions (e.g. inside a [Store]) + * + * @param defaultTransaction default transactions text (used if not specified when [track] is called) + * @param debounceTimeout denounces values in the [Flow] of running transaction by this value + * @param state stores the actual running transaction or null + */ +class Tracker( + private val defaultTransaction: String, + debounceTimeout: Long, + private val state: MutableStateFlow = MutableStateFlow(null) +) : Flow by state.debounce(debounceTimeout) { + + /** + * tracks a given operation + * + * @param transaction text describing the transaction + * @param operation function to track + */ + suspend fun track(transaction: String = defaultTransaction, operation: suspend () -> T): T { + state.value = transaction + return operation().also { state.value = null } + } + +} \ No newline at end of file diff --git a/core/src/jsTest/kotlin/dev/fritz2/tracking/tracking.kt b/core/src/jsTest/kotlin/dev/fritz2/tracking/tracking.kt new file mode 100644 index 000000000..850357f4c --- /dev/null +++ b/core/src/jsTest/kotlin/dev/fritz2/tracking/tracking.kt @@ -0,0 +1,73 @@ +package dev.fritz2.tracking + +import dev.fritz2.binding.RootStore +import dev.fritz2.binding.action +import dev.fritz2.binding.handledBy +import dev.fritz2.dom.html.render +import dev.fritz2.dom.mount +import dev.fritz2.identification.uniqueId +import dev.fritz2.test.initDocument +import dev.fritz2.test.runTest +import dev.fritz2.test.targetId +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.map +import kotlin.browser.document +import kotlin.test.Test +import kotlin.test.assertEquals + +class TrackingTests { + + + @Test + fun testTracking() = runTest { + initDocument() + + val transactionText = "long running" + val transactionId = "transaction-${uniqueId()}" + + val startValue = "start" + val endValue = "end" + val valueId = "value-${uniqueId()}" + + val store = object : RootStore(startValue) { + val running = tracker() + + val longRunningHandler = handle { + running.track(transactionText) { + delay(600) + endValue + } + } + } + + render { + div { + span(id = transactionId) { store.running.map { it.orEmpty() }.bind() } + span(id = valueId) { store.data.bind() } + } + }.mount(targetId) + delay(200) + + action() handledBy store.longRunningHandler + + val valueBeforeTransaction = document.getElementById(valueId)?.textContent + assertEquals(startValue, valueBeforeTransaction) + + delay(300) + + val transactionDuringHandler = document.getElementById(transactionId)?.textContent + assertEquals(transactionText, transactionDuringHandler) + + val valueDuringTransaction = document.getElementById(valueId)?.textContent + assertEquals(startValue, valueDuringTransaction) + + delay(450) + + val transactionAfterHandler = document.getElementById(transactionId)?.textContent + assertEquals("", transactionAfterHandler) + + val valueAfterTransaction = document.getElementById(valueId)?.textContent + assertEquals(endValue, valueAfterTransaction) + + } +} \ No newline at end of file