OXYGENHUB
# 29a375612f92 oxygenhub-android / app / src / main / java / io / oxygenhub / android / ui / common / Load.kt
1.6 KiB · last changed Sign-in, dashboard, repositories, issues and inbox screens, adaptive (refs #2) 11 days ago source blame history raw
1
package io.oxygenhub.android.ui.common
2
3
import androidx.lifecycle.ViewModel
4
import androidx.lifecycle.viewModelScope
5
import io.oxygenhub.android.api.userMessage
6
import kotlinx.coroutines.CancellationException
7
import kotlinx.coroutines.Job
8
import kotlinx.coroutines.flow.MutableStateFlow
9
import kotlinx.coroutines.launch
10
11
/** Something fetched from the hub: on its way, here, or failed. */
12
sealed interface Load<out T> {
13
    data object Loading : Load<Nothing>
14
15
    /** [refreshing] while a pull-to-refresh fetches it again. */
16
    data class Ready<T>(val value: T, val refreshing: Boolean = false) : Load<T>
17
18
    data class Failed(val message: String) : Load<Nothing>
19
20
    val valueOrNull: T? get() = (this as? Ready<T>)?.value
21
22
    companion object {
23
        fun failed(e: Throwable): Failed = Failed(e.userMessage())
24
    }
25
}
26
27
/** The same value, marked as being fetched again (or Loading if there is none yet). */
28
fun <T> Load<T>.refreshing(): Load<T> = when (this) {
29
    is Load.Ready -> copy(refreshing = true)
30
    else -> Load.Loading
31
}
32
33
/**
34
 * Fetches into [into], keeping what is shown while it refreshes; cancels
35
 * [previous] first, so a second pull never races the first.
36
 */
37
fun <T> ViewModel.load(into: MutableStateFlow<Load<T>>, previous: Job?, fetch: suspend () -> T): Job {
38
    previous?.cancel()
39
    into.value = into.value.refreshing()
40
    return viewModelScope.launch {
41
        into.value = try {
42
            Load.Ready(fetch())
43
        } catch (e: CancellationException) {
44
            throw e
45
        } catch (e: Exception) {
46
            Load.failed(e)
47
        }
48
    }
49
}