1package soy.iko.opencode.ui.settings
3import android.content.pm.PackageManager
5import androidx.compose.foundation.clickable
6import androidx.compose.foundation.layout.Arrangement
7import androidx.compose.foundation.layout.Column
8import androidx.compose.foundation.layout.Row
9import androidx.compose.foundation.layout.Spacer
10import androidx.compose.foundation.layout.fillMaxWidth
11import androidx.compose.foundation.layout.padding
12import androidx.compose.foundation.layout.size
13import androidx.compose.foundation.layout.widthIn
14import androidx.compose.foundation.selection.selectable
15import androidx.compose.material.icons.Icons
16import androidx.compose.material.icons.automirrored.filled.ArrowBack
17import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
18import androidx.compose.material.icons.filled.BugReport
19import androidx.compose.material.icons.filled.Dns
20import androidx.compose.material3.ExperimentalMaterial3Api
21import androidx.compose.material3.HorizontalDivider
22import androidx.compose.material3.Icon
23import androidx.compose.material3.IconButton
24import androidx.compose.material3.MaterialTheme
25import androidx.compose.material3.RadioButton
26import androidx.compose.material3.Scaffold
27import androidx.compose.material3.Switch
28import androidx.compose.material3.Text
29import androidx.compose.material3.TextButton
30import androidx.compose.material3.TopAppBar
31import androidx.compose.runtime.Composable
32import androidx.compose.runtime.getValue
33import androidx.compose.runtime.remember
34import androidx.compose.runtime.rememberCoroutineScope
35import androidx.compose.ui.Alignment
36import androidx.compose.ui.Modifier
37import androidx.compose.ui.platform.LocalContext
38import androidx.compose.ui.res.stringResource
39import androidx.compose.ui.semantics.Role
40import androidx.compose.ui.semantics.heading
41import androidx.compose.ui.semantics.role
42import androidx.compose.ui.semantics.semantics
43import androidx.compose.ui.unit.dp
44import androidx.lifecycle.compose.collectAsStateWithLifecycle
45import kotlinx.coroutines.launch
46import soy.iko.opencode.data.repo.ThemeMode
47import soy.iko.opencode.di.AppContainer
48import soy.iko.opencode.R
50@OptIn(ExperimentalMaterial3Api::class)
52fun SettingsScreen(container: AppContainer, onBack: () -> Unit, onManageServers: () -> Unit, onOpenDiagnostics: () -> Unit) {
53 val scope = rememberCoroutineScope()
54 val themeMode by container.settingsStore.themeMode.collectAsStateWithLifecycle(initialValue = ThemeMode.SYSTEM)
55 val dynamicColor by container.settingsStore.dynamicColor.collectAsStateWithLifecycle(initialValue = true)
56 val activeProfile = container.activeConnection.collectAsStateWithLifecycle().value?.profile
57 val context = LocalContext.current
58 val unknownVersion = stringResource(R.string.unknown_version)
59 val versionName = remember {
61 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
62 context.packageManager.getPackageInfo(
64 PackageManager.PackageInfoFlags.of(0),
67 @Suppress("DEPRECATION")
68 context.packageManager.getPackageInfo(context.packageName, 0).versionName
70 }.getOrNull() ?: unknownVersion
76 title = { Text(stringResource(R.string.settings)) },
78 IconButton(onClick = onBack) {
79 Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back))
85 Column(modifier = Modifier.padding(padding).widthIn(max = 600.dp).padding(16.dp)) {
87 stringResource(R.string.appearance),
88 style = MaterialTheme.typography.titleSmall,
89 color = MaterialTheme.colorScheme.primary,
90 modifier = Modifier.semantics { heading() },
92 ThemeMode.entries.forEach { mode ->
95 selected = themeMode == mode,
96 onSelect = { scope.launch { container.settingsStore.setThemeMode(mode) } },
100 Spacer(Modifier.size(8.dp))
102 modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
103 verticalAlignment = Alignment.CenterVertically,
105 Column(modifier = Modifier.weight(1f)) {
106 Text(stringResource(R.string.dynamic_color), style = MaterialTheme.typography.bodyLarge)
108 stringResource(R.string.dynamic_color_desc),
109 style = MaterialTheme.typography.bodySmall,
110 color = MaterialTheme.colorScheme.onSurfaceVariant,
114 checked = dynamicColor,
115 onCheckedChange = { scope.launch { container.settingsStore.setDynamicColor(it) } },
119 HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
122 stringResource(R.string.connection),
123 style = MaterialTheme.typography.titleSmall,
124 color = MaterialTheme.colorScheme.primary,
125 modifier = Modifier.semantics { heading() },
127 if (activeProfile != null) {
128 Text(activeProfile.displayLabel, modifier = Modifier.padding(top = 8.dp))
130 activeProfile.baseUrl + if (activeProfile.hasAuth) stringResource(R.string.server_auth_basic) else "",
131 style = MaterialTheme.typography.bodySmall,
132 color = MaterialTheme.colorScheme.onSurfaceVariant,
136 stringResource(R.string.not_connected),
137 modifier = Modifier.padding(top = 8.dp),
138 color = MaterialTheme.colorScheme.onSurfaceVariant,
145 .clickable(role = Role.Button) { onManageServers() }
146 .padding(vertical = 8.dp),
147 verticalAlignment = Alignment.CenterVertically,
149 Icon(Icons.Filled.Dns, contentDescription = null, tint = MaterialTheme.colorScheme.primary)
150 Text(stringResource(R.string.manage_servers), modifier = Modifier.weight(1f).padding(start = 8.dp))
151 Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
154 HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
157 stringResource(R.string.about),
158 style = MaterialTheme.typography.titleSmall,
159 color = MaterialTheme.colorScheme.primary,
160 modifier = Modifier.semantics { heading() },
163 stringResource(R.string.about_version, versionName),
164 style = MaterialTheme.typography.bodyMedium,
165 modifier = Modifier.padding(top = 8.dp),
168 stringResource(R.string.about_desc),
169 style = MaterialTheme.typography.bodySmall,
170 color = MaterialTheme.colorScheme.onSurfaceVariant,
171 modifier = Modifier.padding(top = 4.dp),
174 Spacer(Modifier.size(16.dp))
178 .clickable(role = Role.Button) { onOpenDiagnostics() }
179 .padding(vertical = 8.dp),
180 verticalAlignment = Alignment.CenterVertically,
182 Icon(Icons.Filled.BugReport, contentDescription = null, tint = MaterialTheme.colorScheme.error)
183 Text(stringResource(R.string.diagnostics), modifier = Modifier.weight(1f).padding(start = 8.dp))
184 Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
191private fun ThemeRow(mode: ThemeMode, selected: Boolean, onSelect: () -> Unit) {
195 .selectable(selected = selected, onClick = onSelect)
196 .padding(vertical = 8.dp),
197 horizontalArrangement = Arrangement.spacedBy(8.dp),
198 verticalAlignment = Alignment.CenterVertically,
200 RadioButton(selected = selected, onClick = onSelect)
203 ThemeMode.SYSTEM -> stringResource(R.string.theme_system)
204 ThemeMode.LIGHT -> stringResource(R.string.theme_light)
205 ThemeMode.DARK -> stringResource(R.string.theme_dark)
207 style = MaterialTheme.typography.bodyLarge,
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list