| 1 |
package io.oxygenhub.android.ui.proposals |
| 2 |
|
| 3 |
import androidx.compose.foundation.lazy.LazyColumn |
| 4 |
import androidx.compose.material.icons.Icons |
| 5 |
import androidx.compose.material.icons.outlined.Description |
| 6 |
import androidx.compose.runtime.Composable |
| 7 |
import androidx.compose.runtime.getValue |
| 8 |
import androidx.compose.runtime.mutableStateOf |
| 9 |
import androidx.compose.runtime.saveable.rememberSaveable |
| 10 |
import androidx.compose.runtime.setValue |
| 11 |
import androidx.lifecycle.ViewModel |
| 12 |
import androidx.lifecycle.compose.collectAsStateWithLifecycle |
| 13 |
import io.oxygenhub.android.api.FileChange |
| 14 |
import io.oxygenhub.android.api.HubApi |
| 15 |
import io.oxygenhub.android.ui.code.fileDiffs |
| 16 |
import io.oxygenhub.android.ui.code.openByDefault |
| 17 |
import io.oxygenhub.android.ui.common.AppScreen |
| 18 |
import io.oxygenhub.android.ui.common.EmptyState |
| 19 |
import io.oxygenhub.android.ui.common.Load |
| 20 |
import io.oxygenhub.android.ui.common.LoadContent |
| 21 |
import io.oxygenhub.android.ui.common.load |
| 22 |
import io.oxygenhub.android.ui.theme.LocalStatusColors |
| 23 |
import kotlinx.coroutines.Job |
| 24 |
import kotlinx.coroutines.flow.MutableStateFlow |
| 25 |
import kotlinx.coroutines.flow.StateFlow |
| 26 |
import kotlinx.coroutines.flow.asStateFlow |
| 27 |
|
| 28 |
/** The files a proposal changes, each with its diff (#4). */ |
| 29 |
class ProposalFilesViewModel(private val api: HubApi, val owner: String, val name: String, val number: Long) : ViewModel() { |
| 30 |
private val _state = MutableStateFlow<Load<List<FileChange>>>(Load.Loading) |
| 31 |
val state: StateFlow<Load<List<FileChange>>> = _state.asStateFlow() |
| 32 |
private var job: Job? = null |
| 33 |
|
| 34 |
init { |
| 35 |
refresh() |
| 36 |
} |
| 37 |
|
| 38 |
fun refresh() { |
| 39 |
job = load(_state, job) { api.proposalDiff(owner, name, number) } |
| 40 |
} |
| 41 |
} |
| 42 |
|
| 43 |
/** A proposal's files, as its page on the hub shows them: each folds open to its diff. */ |
| 44 |
@Composable |
| 45 |
fun ProposalFilesScreen(vm: ProposalFilesViewModel, onBack: () -> Unit) { |
| 46 |
val state by vm.state.collectAsStateWithLifecycle() |
| 47 |
val status = LocalStatusColors.current |
| 48 |
AppScreen(title = "Files · #${vm.number}", subtitle = "${vm.owner}/${vm.name}", onBack = onBack) { |
| 49 |
LoadContent(state, onRefresh = vm::refresh) { files -> |
| 50 |
if (files.isEmpty()) { |
| 51 |
EmptyState(Icons.Outlined.Description, "No files changed", "This proposal's changes are already on its target, or change nothing.") |
| 52 |
return@LoadContent |
| 53 |
} |
| 54 |
var open by rememberSaveable(vm.number) { mutableStateOf(openByDefault(files).toList()) } |
| 55 |
LazyColumn { |
| 56 |
fileDiffs( |
| 57 |
files, |
| 58 |
open, |
| 59 |
status.ok, |
| 60 |
status.bad, |
| 61 |
onToggle = { path -> open = if (path in open) open - path else open + path }, |
| 62 |
onOpen = { null }, |
| 63 |
) |
| 64 |
} |
| 65 |
} |
| 66 |
} |
| 67 |
} |