Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -87,26 +88,30 @@ class HomeViewModel
// Refreshing if a load has already occurred and the rows haven't significantly changed
val refresh =
state.loadingState == LoadingState.Success && state.settings == settings
Timber.v("refresh=$refresh, state.loadingState=${state.loadingState}")
Timber.v(
"refresh=%s, state.loadingState=%s, %s rows",
refresh,
state.loadingState,
settings.rows.size,
)
_state.update {
it.copy(
loadingState = if (refresh) LoadingState.Success else LoadingState.Loading,
refreshState = LoadingState.Loading,
settings = settings,
homeRows =
if (refresh) {
it.homeRows
} else {
List(settings.rows.size) { HomeRowLoadingState.Pending("") }
},
)
}

val semaphore = Semaphore(4)

val watchingRowIndexes =
settings.rows
.mapIndexedNotNull { index, row ->
if (isWatchingRow(row.config)) index else null
}
val deferred =
settings.rows
// Load the watching rows first
.sortedByDescending { isWatchingRow(it.config) }
.map { row ->
viewModelScope.async(Dispatchers.IO) {
semaphore.withPermit {
Expand All @@ -123,48 +128,71 @@ class HomeViewModel
)
} catch (ex: Exception) {
Timber.e(ex, "Error on row %s", row)
HomeRowLoadingState.Error(row.title, exception = ex)
HomeRowLoadingState.Error(
row.title,
exception = ex,
)
}
}
}
}

if (refresh && state.homeRows.isNotEmpty() && watchingRowIndexes.isNotEmpty()) {
// Replace watching rows first
Timber.v("Refreshing rows: %s", watchingRowIndexes)
if (refresh) {
// Replace rows as they complete
val remaining = deferred.withIndex().toMutableList()
while (remaining.isNotEmpty()) {
val (rowIndex, rowData) =
select {
// "Return" the first remaining that is completed
remaining
.forEach { (rowIndex, deferred) ->
deferred.onAwait { rowIndex to it }
}
}
Timber.v("Got row data index=%s", rowIndex)
remaining.removeIf { it.index == rowIndex }
// Include only errors & non-empty successes
if (rowData is HomeRowLoadingState.Error ||
(rowData is HomeRowLoadingState.Success && rowData.items.isNotEmpty())
) {
_state.update { state ->
val newRows =
state.homeRows.toMutableList().apply {
set(rowIndex, rowData)
}
state.copy(
homeRows = newRows,
)
}
} else {
Timber.d("Skipping invalid row %s: %s", rowIndex, rowData)
}
}
_state.update {
it.copy(
loadingState = LoadingState.Success,
refreshState = LoadingState.Success,
)
}
} else {
val rows =
deferred
.filterIndexed { index, _ -> index in watchingRowIndexes }
.awaitAll()
_state.update {
val newRows =
it.homeRows.toMutableList().apply {
rows.forEachIndexed { index, row ->
set(watchingRowIndexes[index], row)
}
.filter {
// Include only errors & non-empty successes
it is HomeRowLoadingState.Error ||
(it is HomeRowLoadingState.Success && it.items.isNotEmpty())
}
Timber.v("Got all rows")
_state.update {
it.copy(
loadingState = LoadingState.Success,
homeRows = newRows,
refreshState = LoadingState.Success,
homeRows = rows,
)
}
}
val rows =
deferred
.awaitAll()
.filter {
// Include only errors & non-empty successes
it is HomeRowLoadingState.Error ||
(it is HomeRowLoadingState.Success && it.items.isNotEmpty())
}
Timber.v("Got all rows")
_state.update {
it.copy(
loadingState = LoadingState.Success,
refreshState = LoadingState.Success,
homeRows = rows,
)
}
Timber.d("Home page load complete")
}
} catch (ex: Exception) {
Timber.e(ex, "Exception during home page loading")
Expand Down
Loading