Stop calendar queries emitting an empty list before they have run
`queryCalendarEvents` forwarded the `MutableStateFlow` it accumulates the provider results in, so the initial empty value was the first thing collectors saw - before any provider had reported. Callers in the launcher take that first result (`CalendarPartProvider` uses `.first()`) and cannot tell it apart from "no events", so the agenda stayed empty and its part never showed up. `CalendarRepository.findMany`'s 500 ms debounce used to hide this: the placeholder was superseded by the real list within the debounce window, which is exactly why the agenda only ever appeared half a second late. Keep the accumulator null until the first provider reports, and let `findMany` - a one-shot query for the clock and calendar widgets - wait for all providers (`awaitAllProviders`) so it never hands out a partial list. The search path keeps streaming results as providers report, now without an empty flash.
This commit is contained in:
@@ -22,10 +22,12 @@ import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.combineTransform
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlin.time.Duration.Companion.days
|
||||
@@ -126,6 +128,7 @@ internal class CalendarRepositoryImpl(
|
||||
excludeAllDayEvents = excludeAllDayEvents,
|
||||
excludeCalendars = excludeCalendars,
|
||||
providers = providers,
|
||||
awaitAllProviders = true,
|
||||
allowNetwork = false,
|
||||
)
|
||||
emitAll(if (debounceMillis > 0L) events.debounce(debounceMillis) else events)
|
||||
@@ -139,12 +142,17 @@ internal class CalendarRepositoryImpl(
|
||||
excludeAllDayEvents: Boolean = false,
|
||||
excludeCalendars: List<String> = emptyList(),
|
||||
allowNetwork: Boolean = false,
|
||||
awaitAllProviders: Boolean = false,
|
||||
providers: List<CalendarProvider>,
|
||||
): Flow<ImmutableList<CalendarEvent>> = flow {
|
||||
supervisorScope {
|
||||
val result = MutableStateFlow(persistentListOf<CalendarEvent>())
|
||||
// Null until the first provider has reported. Emitting the (empty) accumulator right
|
||||
// away would hand collectors an empty list before any query had run, which callers
|
||||
// that take the first result - like the agenda in the clock widget - cannot tell
|
||||
// apart from "no events".
|
||||
val result = MutableStateFlow<ImmutableList<CalendarEvent>?>(null)
|
||||
|
||||
for (provider in providers) {
|
||||
val jobs = providers.map { provider ->
|
||||
launch {
|
||||
val r = provider.search(
|
||||
query,
|
||||
@@ -158,11 +166,19 @@ internal class CalendarRepositoryImpl(
|
||||
allowNetwork = allowNetwork,
|
||||
)
|
||||
result.update {
|
||||
(it + r).toPersistentList()
|
||||
((it ?: persistentListOf()) + r).toPersistentList()
|
||||
}
|
||||
}
|
||||
}
|
||||
emitAll(result)
|
||||
|
||||
if (awaitAllProviders) {
|
||||
// One-shot callers must not receive a partial list, so wait until every
|
||||
// provider has reported; if there is none, report an empty result.
|
||||
jobs.joinAll()
|
||||
emit(result.value ?: persistentListOf())
|
||||
} else {
|
||||
emitAll(result.filterNotNull())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user