git.iko.soy/opencode-android
on 8865a77 help sign in
93 lines 3.9 kB history raw
1package soy.iko.opencode.ui.file
3import androidx.compose.foundation.horizontalScroll
4import androidx.compose.foundation.layout.Box
5import androidx.compose.foundation.layout.fillMaxSize
6import androidx.compose.foundation.layout.padding
7import androidx.compose.foundation.rememberScrollState
8import androidx.compose.foundation.verticalScroll
9import androidx.compose.material.icons.Icons
10import androidx.compose.material.icons.automirrored.filled.ArrowBack
11import androidx.compose.material3.CircularProgressIndicator
12import androidx.compose.material3.ExperimentalMaterial3Api
13import androidx.compose.material3.Icon
14import androidx.compose.material3.IconButton
15import androidx.compose.material3.MaterialTheme
16import androidx.compose.material3.Scaffold
17import androidx.compose.material3.Text
18import androidx.compose.material3.TopAppBar
19import androidx.compose.runtime.Composable
20import androidx.compose.runtime.getValue
21import androidx.compose.ui.Alignment
22import androidx.compose.ui.Modifier
23import androidx.compose.ui.text.font.FontFamily
24import androidx.compose.ui.text.style.TextOverflow
25import androidx.compose.ui.unit.dp
26import androidx.lifecycle.compose.collectAsStateWithLifecycle
27import androidx.lifecycle.viewmodel.compose.viewModel
28import soy.iko.opencode.di.AppContainer
29import soy.iko.opencode.ui.components.DiffView
30import soy.iko.opencode.ui.vmFactory
32@OptIn(ExperimentalMaterial3Api::class)
33@Composable
34fun FileViewScreen(
35 container: AppContainer,
36 path: String,
37 onBack: () -> Unit,
38) {
39 val vm: FileViewModel = viewModel(factory = vmFactory { FileViewModel(container, path) })
40 val state by vm.state.collectAsStateWithLifecycle()
42 Scaffold(
43 topBar = {
44 TopAppBar(
45 title = { Text(path.substringAfterLast('/'), maxLines = 1, overflow = TextOverflow.Ellipsis) },
46 navigationIcon = {
47 IconButton(onClick = onBack) {
48 Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
49 }
50 },
51 )
52 },
53 ) { padding ->
54 Box(modifier = Modifier.fillMaxSize().padding(padding)) {
55 when {
56 state.loading -> CircularProgressIndicator(Modifier.align(Alignment.Center))
57 state.error != null -> Text(
58 state.error!!,
59 modifier = Modifier.align(Alignment.Center).padding(24.dp),
60 color = MaterialTheme.colorScheme.error,
61 )
62 state.content?.isBinary == true -> Text(
63 "Binary file — preview not supported.",
64 modifier = Modifier.align(Alignment.Center).padding(24.dp),
65 color = MaterialTheme.colorScheme.onSurfaceVariant,
66 )
67 else -> {
68 val content = state.content
69 val scrollState = rememberScrollState()
70 if (content?.diff != null && content.diff.isNotBlank()) {
71 DiffView(
72 diff = content.diff,
73 modifier = Modifier
74 .fillMaxSize()
75 .padding(padding),
76 )
77 } else {
78 Text(
79 text = content?.content.orEmpty(),
80 modifier = Modifier
81 .fillMaxSize()
82 .verticalScroll(scrollState)
83 .horizontalScroll(rememberScrollState())
84 .padding(12.dp),
85 style = MaterialTheme.typography.bodySmall,
86 fontFamily = FontFamily.Monospace,
87 )
88 }
89 }
90 }
91 }
92 }