OXYGENHUB
# 3f3508bd5f16 oxygenhub-android / app / src / main / java / io / oxygenhub / android / ui / dashboard / DashboardScreen.kt
5.5 KiB · last changed Screenshots from Compose previews, and what rendering them showed (refs #2) 11 days ago source blame history raw
1
package io.oxygenhub.android.ui.dashboard
2
3
import androidx.compose.foundation.clickable
4
import androidx.compose.foundation.layout.Arrangement
5
import androidx.compose.foundation.layout.BoxWithConstraints
6
import androidx.compose.foundation.layout.Column
7
import androidx.compose.foundation.layout.Row
8
import androidx.compose.foundation.layout.Spacer
9
import androidx.compose.foundation.layout.fillMaxWidth
10
import androidx.compose.foundation.layout.height
11
import androidx.compose.foundation.layout.padding
12
import androidx.compose.foundation.rememberScrollState
13
import androidx.compose.foundation.verticalScroll
14
import androidx.compose.material.icons.Icons
15
import androidx.compose.material.icons.outlined.Inbox
16
import androidx.compose.material.icons.outlined.MarkEmailUnread
17
import androidx.compose.material3.Card
18
import androidx.compose.material3.HorizontalDivider
19
import androidx.compose.material3.Icon
20
import androidx.compose.material3.ListItem
21
import androidx.compose.material3.ListItemDefaults
22
import androidx.compose.material3.MaterialTheme
23
import androidx.compose.material3.Text
24
import androidx.compose.material3.TextButton
25
import androidx.compose.runtime.Composable
26
import androidx.compose.runtime.getValue
27
import androidx.compose.ui.Modifier
28
import androidx.compose.ui.graphics.Color
29
import androidx.compose.ui.text.font.FontWeight
30
import androidx.compose.ui.unit.dp
31
import androidx.lifecycle.compose.collectAsStateWithLifecycle
32
import io.oxygenhub.android.api.Inbox
33
import io.oxygenhub.android.api.Repository
34
import io.oxygenhub.android.ui.common.AppScreen
35
import io.oxygenhub.android.ui.common.Load
36
import io.oxygenhub.android.ui.common.LoadContent
37
import io.oxygenhub.android.ui.repos.RepositoryRow
38
39
@Composable
40
fun DashboardScreen(
41
    vm: DashboardViewModel,
42
    hub: String,
43
    inbox: Load<Inbox>,
44
    onRefreshInbox: () -> Unit,
45
    onOpenRepository: (Repository) -> Unit,
46
    onAllRepositories: () -> Unit,
47
    onInbox: () -> Unit,
48
) {
49
    val state by vm.state.collectAsStateWithLifecycle()
50
    AppScreen(title = "Dashboard", subtitle = hub.substringAfter("://")) {
51
        LoadContent(state, onRefresh = { vm.refresh(); onRefreshInbox() }) { d ->
52
            DashboardContent(d, inbox, onOpenRepository, onAllRepositories, onInbox)
53
        }
54
    }
55
}
56
57
@Composable
58
fun DashboardContent(
59
    d: Dashboard,
60
    inbox: Load<Inbox>,
61
    onOpenRepository: (Repository) -> Unit,
62
    onAllRepositories: () -> Unit,
63
    onInbox: () -> Unit,
64
) {
65
    BoxWithConstraints {
66
        // Side by side once there is room (a tablet, or a foldable open).
67
        val wide = maxWidth >= 720.dp
68
        Column(
69
            Modifier
70
                .fillMaxWidth()
71
                .verticalScroll(rememberScrollState())
72
                .padding(16.dp),
73
        ) {
74
            Text(d.greeting, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)
75
            Spacer(Modifier.height(16.dp))
76
            if (wide) {
77
                Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
78
                    Column(Modifier.weight(2f)) { Recent(d, onOpenRepository, onAllRepositories) }
79
                    Column(Modifier.weight(1f)) { Unread(inbox, onInbox) }
80
                }
81
            } else {
82
                Unread(inbox, onInbox)
83
                Spacer(Modifier.height(16.dp))
84
                Recent(d, onOpenRepository, onAllRepositories)
85
            }
86
        }
87
    }
88
}
89
90
@Composable
91
private fun Unread(inbox: Load<Inbox>, onInbox: () -> Unit) {
92
    Card(Modifier.fillMaxWidth().clickable(onClick = onInbox)) {
93
        ListItem(
94
            colors = ListItemDefaults.colors(containerColor = Color.Transparent),
95
            leadingContent = {
96
                Icon(if ((inbox.valueOrNull?.unread ?: 0) > 0) Icons.Outlined.MarkEmailUnread else Icons.Outlined.Inbox, contentDescription = null)
97
            },
98
            headlineContent = {
99
                Text(
100
                    when (inbox) {
101
                        Load.Loading -> "Inbox"
102
                        is Load.Failed -> "Inbox unavailable"
103
                        is Load.Ready -> when (val n = inbox.value.unread) {
104
                            0L -> "Nothing unread"
105
                            1L -> "1 unread notification"
106
                            else -> "$n unread notifications"
107
                        }
108
                    },
109
                )
110
            },
111
            supportingContent = (inbox as? Load.Failed)?.let { f -> { Text(f.message) } },
112
        )
113
    }
114
}
115
116
@Composable
117
private fun Recent(d: Dashboard, onOpenRepository: (Repository) -> Unit, onAllRepositories: () -> Unit) {
118
    Card(Modifier.fillMaxWidth()) {
119
        Text(
120
            if (d.recentAreYours) "Your recent repositories" else "Recently updated on the hub",
121
            style = MaterialTheme.typography.titleMedium,
122
            modifier = Modifier.padding(start = 16.dp, top = 16.dp, end = 16.dp),
123
        )
124
        if (!d.recentAreYours) {
125
            Text(
126
                "You have no repositories of your own yet.",
127
                style = MaterialTheme.typography.bodySmall,
128
                color = MaterialTheme.colorScheme.onSurfaceVariant,
129
                modifier = Modifier.padding(horizontal = 16.dp),
130
            )
131
        }
132
        if (d.recent.isEmpty()) {
133
            Text("No repositories to show.", modifier = Modifier.padding(16.dp))
134
        }
135
        d.recent.forEachIndexed { i, repo ->
136
            if (i > 0) HorizontalDivider()
137
            RepositoryRow(repo, onClick = { onOpenRepository(repo) })
138
        }
139
        TextButton(onClick = onAllRepositories, modifier = Modifier.padding(8.dp)) { Text("All repositories") }
140
    }
141
}