package cash.z.ecc.android.sdk.demoapp.ui.common import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import kotlin.time.Duration import kotlin.time.ExperimentalTime import kotlin.time.TimeSource @OptIn(ExperimentalTime::class) fun Flow.throttle( duration: Duration, timeSource: TimeSource = TimeSource.Monotonic ): Flow = flow { coroutineScope { val context = coroutineContext val mutex = Mutex() var timeMark = timeSource.markNow() var delayEmit: Deferred? = null var firstValue = true var valueToEmit: T collect { value -> if (firstValue) { firstValue = false emit(value) timeMark = timeSource.markNow() return@collect } delayEmit?.cancel() valueToEmit = value if (timeMark.elapsedNow() >= duration) { mutex.withLock { emit(valueToEmit) timeMark = timeSource.markNow() } } else { delayEmit = async(Dispatchers.Default) { mutex.withLock { delay(duration) withContext(context) { emit(valueToEmit) } timeMark = timeSource.markNow() } } } } } }