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.defaultMinSize
11import androidx.compose.foundation.layout.fillMaxWidth
12import androidx.compose.foundation.layout.padding
13import androidx.compose.foundation.layout.size
14import androidx.compose.foundation.layout.widthIn
15import androidx.compose.foundation.selection.selectable
16import androidx.compose.foundation.selection.toggleable
17import androidx.compose.material.icons.Icons
18import androidx.compose.material.icons.automirrored.filled.ArrowBack
19import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
20import androidx.compose.material.icons.filled.BugReport
21import androidx.compose.material.icons.filled.Dns
22import androidx.compose.material3.ExperimentalMaterial3Api
23import androidx.compose.material3.HorizontalDivider
24import androidx.compose.material3.Icon
25import androidx.compose.material3.IconButton
26import androidx.compose.material3.MaterialTheme
27import androidx.compose.material3.RadioButton
28import androidx.compose.material3.Scaffold
29import androidx.compose.material3.Switch
30import androidx.compose.material3.Text
31import androidx.compose.material3.TextButton
32import androidx.compose.material3.TopAppBar
33import androidx.compose.runtime.Composable
34import androidx.compose.runtime.getValue
35import androidx.compose.runtime.remember
36import androidx.compose.runtime.rememberCoroutineScope
37import androidx.compose.ui.Alignment
38import androidx.compose.ui.Modifier
39import androidx.compose.ui.platform.LocalContext
40import androidx.compose.ui.res.stringResource
41import androidx.compose.ui.semantics.Role
42import androidx.compose.ui.semantics.heading
43import androidx.compose.ui.semantics.role
44import androidx.compose.ui.semantics.semantics
45import androidx.compose.ui.unit.dp
46import androidx.lifecycle.compose.collectAsStateWithLifecycle
47import kotlinx.coroutines.launch
48import soy.iko.opencode.data.repo.ThemeMode
49import soy.iko.opencode.di.AppContainer
50import soy.iko.opencode.R
51import soy.iko.opencode.util.runCatchingCancellable
53@OptIn(ExperimentalMaterial3Api::class)
55fun SettingsScreen(container: AppContainer, onBack: () -> Unit, onManageServers: () -> Unit, onOpenDiagnostics: () -> Unit) {
56 val scope = rememberCoroutineScope()
57 val themeMode by container.settingsStore.themeMode.collectAsStateWithLifecycle(initialValue = ThemeMode.SYSTEM)
58 val dynamicColor by container.settingsStore.dynamicColor.collectAsStateWithLifecycle(initialValue = true)
59 val activeProfile = container.activeConnection.collectAsStateWithLifecycle().value?.profile
60 val context = LocalContext.current
61 val unknownVersion = stringResource(R.string.unknown_version)
62 val versionName = remember {
64 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
65 context.packageManager.getPackageInfo(
67 PackageManager.PackageInfoFlags.of(0),
70 @Suppress("DEPRECATION")
71 context.packageManager.getPackageInfo(context.packageName, 0).versionName
73 }.getOrNull() ?: unknownVersion
79 title = { Text(stringResource(R.string.settings)) },
81 IconButton(onClick = onBack) {
82 Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back))
88 Column(modifier = Modifier.padding(padding).widthIn(max = 600.dp).padding(16.dp)) {
90 stringResource(R.string.appearance),
91 style = MaterialTheme.typography.titleSmall,
92 color = MaterialTheme.colorScheme.primary,
93 modifier = Modifier.semantics { heading() },
95 ThemeMode.entries.forEach { mode ->
98 selected = themeMode == mode,
99 onSelect = { scope.launch { runCatchingCancellable { container.settingsStore.setThemeMode(mode) } } },
103 Spacer(Modifier.size(8.dp))
108 value = dynamicColor,
109 onValueChange = { scope.launch { runCatchingCancellable { container.settingsStore.setDynamicColor(it) } } },
112 .padding(vertical = 8.dp),
113 verticalAlignment = Alignment.CenterVertically,
115 Column(modifier = Modifier.weight(1f)) {
116 Text(stringResource(R.string.dynamic_color), style = MaterialTheme.typography.bodyLarge)
118 stringResource(R.string.dynamic_color_desc),
119 style = MaterialTheme.typography.bodySmall,
120 color = MaterialTheme.colorScheme.onSurfaceVariant,
124 checked = dynamicColor,
125 onCheckedChange = null,
129 HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
132 stringResource(R.string.connection),
133 style = MaterialTheme.typography.titleSmall,
134 color = MaterialTheme.colorScheme.primary,
135 modifier = Modifier.semantics { heading() },
137 if (activeProfile != null) {
138 Text(activeProfile.displayLabel, modifier = Modifier.padding(top = 8.dp))
140 activeProfile.baseUrl + if (activeProfile.hasAuth) stringResource(R.string.server_auth_basic) else "",
141 style = MaterialTheme.typography.bodySmall,
142 color = MaterialTheme.colorScheme.onSurfaceVariant,
146 stringResource(R.string.not_connected),
147 modifier = Modifier.padding(top = 8.dp),
148 color = MaterialTheme.colorScheme.onSurfaceVariant,
154 .defaultMinSize(minHeight = 48.dp)
156 .clickable(role = Role.Button) { onManageServers() }
157 .padding(vertical = 8.dp),
158 verticalAlignment = Alignment.CenterVertically,
160 Icon(Icons.Filled.Dns, contentDescription = null, tint = MaterialTheme.colorScheme.primary)
161 Text(stringResource(R.string.manage_servers), modifier = Modifier.weight(1f).padding(start = 8.dp))
162 Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
165 HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
168 stringResource(R.string.about),
169 style = MaterialTheme.typography.titleSmall,
170 color = MaterialTheme.colorScheme.primary,
171 modifier = Modifier.semantics { heading() },
174 stringResource(R.string.about_version, versionName),
175 style = MaterialTheme.typography.bodyMedium,
176 modifier = Modifier.padding(top = 8.dp),
179 stringResource(R.string.about_desc),
180 style = MaterialTheme.typography.bodySmall,
181 color = MaterialTheme.colorScheme.onSurfaceVariant,
182 modifier = Modifier.padding(top = 4.dp),
185 Spacer(Modifier.size(16.dp))
189 .defaultMinSize(minHeight = 48.dp)
190 .clickable(role = Role.Button) { onOpenDiagnostics() }
191 .padding(vertical = 8.dp),
192 verticalAlignment = Alignment.CenterVertically,
194 Icon(Icons.Filled.BugReport, contentDescription = null, tint = MaterialTheme.colorScheme.error)
195 Text(stringResource(R.string.diagnostics), modifier = Modifier.weight(1f).padding(start = 8.dp))
196 Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
203private fun ThemeRow(mode: ThemeMode, selected: Boolean, onSelect: () -> Unit) {
207 .selectable(selected = selected, onClick = onSelect)
208 .padding(vertical = 8.dp),
209 horizontalArrangement = Arrangement.spacedBy(8.dp),
210 verticalAlignment = Alignment.CenterVertically,
212 RadioButton(selected = selected, onClick = null)
215 ThemeMode.SYSTEM -> stringResource(R.string.theme_system)
216 ThemeMode.LIGHT -> stringResource(R.string.theme_light)
217 ThemeMode.DARK -> stringResource(R.string.theme_dark)
219 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