OXYGENHUB
# 144cbf7bf2b5 oxygenhub-android / app / src / main / java / io / oxygenhub / android / ui / dashboard / DashboardViewModel.kt
2.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.dashboard
2
3
import androidx.lifecycle.ViewModel
4
import io.oxygenhub.android.api.HubApi
5
import io.oxygenhub.android.api.Repository
6
import io.oxygenhub.android.api.User
7
import io.oxygenhub.android.auth.Session
8
import io.oxygenhub.android.ui.common.Load
9
import io.oxygenhub.android.ui.common.load
10
import kotlinx.coroutines.CancellationException
11
import kotlinx.coroutines.Job
12
import kotlinx.coroutines.async
13
import kotlinx.coroutines.coroutineScope
14
import kotlinx.coroutines.flow.MutableStateFlow
15
import kotlinx.coroutines.flow.StateFlow
16
import kotlinx.coroutines.flow.asStateFlow
17
import java.util.Calendar
18
19
data class Dashboard(
20
    val greeting: String,
21
    /** The account, or null when the token may not read it (no `user:read`). */
22
    val user: User?,
23
    /** The newest-updated repositories: the user's own, or everyone's when they own none. */
24
    val recent: List<Repository>,
25
    val recentAreYours: Boolean,
26
    val repositoryCount: Int,
27
)
28
29
class DashboardViewModel(
30
    private val api: HubApi,
31
    private val session: Session,
32
    private val hourOfDay: () -> Int = { Calendar.getInstance().get(Calendar.HOUR_OF_DAY) },
33
) : ViewModel() {
34
    private val _state = MutableStateFlow<Load<Dashboard>>(Load.Loading)
35
    val state: StateFlow<Load<Dashboard>> = _state.asStateFlow()
36
37
    private var job: Job? = null
38
39
    init {
40
        refresh()
41
    }
42
43
    fun refresh() {
44
        job = load(_state, job) { fetch() }
45
    }
46
47
    private suspend fun fetch(): Dashboard = coroutineScope {
48
        // The account is a nicety (a display name); the repositories are the page.
49
        val user = async {
50
            try {
51
                api.user()
52
            } catch (e: CancellationException) {
53
                throw e
54
            } catch (_: Exception) {
55
                null
56
            }
57
        }
58
        val repos = api.repositories()
59
        val me = user.await()
60
        val username = me?.username ?: session.username
61
        val mine = repos.filter { it.owner == username }
62
        val pool = mine.ifEmpty { repos }
63
        Dashboard(
64
            greeting = greeting(hourOfDay(), me?.shownName ?: session.username),
65
            user = me,
66
            recent = pool.sortedByDescending { it.updatedMs }.take(RECENT),
67
            recentAreYours = mine.isNotEmpty(),
68
            repositoryCount = mine.size,
69
        )
70
    }
71
72
    companion object {
73
        const val RECENT = 5
74
75
        fun greeting(hour: Int, name: String): String {
76
            val part = when (hour) {
77
                in 5..11 -> "Good morning"
78
                in 12..17 -> "Good afternoon"
79
                else -> "Good evening"
80
            }
81
            return "$part, $name"
82
        }
83
    }
84
}