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
49import soy.iko.opencode.util.runCatchingCancellable
51@OptIn(ExperimentalMaterial3Api::class)
53fun SettingsScreen(container: AppContainer, onBack: () -> Unit, onManageServers: () -> Unit, onOpenDiagnostics: () -> Unit) {
54 val scope = rememberCoroutineScope()
55 val themeMode by container.settingsStore.themeMode.collectAsStateWithLifecycle(initialValue = ThemeMode.SYSTEM)
56 val dynamicColor by container.settingsStore.dynamicColor.collectAsStateWithLifecycle(initialValue = true)
57 val activeProfile = container.activeConnection.collectAsStateWithLifecycle().value?.profile
58 val context = LocalContext.current
59 val unknownVersion = stringResource(R.string.unknown_version)
60 val versionName = remember {
62 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
63 context.packageManager.getPackageInfo(
65 PackageManager.PackageInfoFlags.of(0),
68 @Suppress("DEPRECATION")
69 context.packageManager.getPackageInfo(context.packageName, 0).versionName
71 }.getOrNull() ?: unknownVersion
77 title = { Text(stringResource(R.string.settings)) },
79 IconButton(onClick = onBack) {
80 Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back))
86 Column(modifier = Modifier.padding(padding).widthIn(max = 600.dp).padding(16.dp)) {
88 stringResource(R.string.appearance),
89 style = MaterialTheme.typography.titleSmall,
90 color = MaterialTheme.colorScheme.primary,
91 modifier = Modifier.semantics { heading() },
93 ThemeMode.entries.forEach { mode ->
96 selected = themeMode == mode,
97 onSelect = { scope.launch { runCatchingCancellable { container.settingsStore.setThemeMode(mode) } } },
101 Spacer(Modifier.size(8.dp))
103 modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
104 verticalAlignment = Alignment.CenterVertically,
106 Column(modifier = Modifier.weight(1f)) {
107 Text(stringResource(R.string.dynamic_color), style = MaterialTheme.typography.bodyLarge)
109 stringResource(R.string.dynamic_color_desc),
110 style = MaterialTheme.typography.bodySmall,
111 color = MaterialTheme.colorScheme.onSurfaceVariant,
115 checked = dynamicColor,
116 onCheckedChange = { scope.launch { runCatchingCancellable { container.settingsStore.setDynamicColor(it) } } },
120 HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
123 stringResource(R.string.connection),
124 style = MaterialTheme.typography.titleSmall,
125 color = MaterialTheme.colorScheme.primary,
126 modifier = Modifier.semantics { heading() },
128 if (activeProfile != null) {
129 Text(activeProfile.displayLabel, modifier = Modifier.padding(top = 8.dp))
131 activeProfile.baseUrl + if (activeProfile.hasAuth) stringResource(R.string.server_auth_basic) else "",
132 style = MaterialTheme.typography.bodySmall,
133 color = MaterialTheme.colorScheme.onSurfaceVariant,
137 stringResource(R.string.not_connected),
138 modifier = Modifier.padding(top = 8.dp),
139 color = MaterialTheme.colorScheme.onSurfaceVariant,
146 .clickable(role = Role.Button) { onManageServers() }
147 .padding(vertical = 8.dp),
148 verticalAlignment = Alignment.CenterVertically,
150 Icon(Icons.Filled.Dns, contentDescription = null, tint = MaterialTheme.colorScheme.primary)
151 Text(stringResource(R.string.manage_servers), modifier = Modifier.weight(1f).padding(start = 8.dp))
152 Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
155 HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
158 stringResource(R.string.about),
159 style = MaterialTheme.typography.titleSmall,
160 color = MaterialTheme.colorScheme.primary,
161 modifier = Modifier.semantics { heading() },
164 stringResource(R.string.about_version, versionName),
165 style = MaterialTheme.typography.bodyMedium,
166 modifier = Modifier.padding(top = 8.dp),
169 stringResource(R.string.about_desc),
170 style = MaterialTheme.typography.bodySmall,
171 color = MaterialTheme.colorScheme.onSurfaceVariant,
172 modifier = Modifier.padding(top = 4.dp),
175 Spacer(Modifier.size(16.dp))
179 .clickable(role = Role.Button) { onOpenDiagnostics() }
180 .padding(vertical = 8.dp),
181 verticalAlignment = Alignment.CenterVertically,
183 Icon(Icons.Filled.BugReport, contentDescription = null, tint = MaterialTheme.colorScheme.error)
184 Text(stringResource(R.string.diagnostics), modifier = Modifier.weight(1f).padding(start = 8.dp))
185 Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
192private fun ThemeRow(mode: ThemeMode, selected: Boolean, onSelect: () -> Unit) {
196 .selectable(selected = selected, onClick = onSelect)
197 .padding(vertical = 8.dp),
198 horizontalArrangement = Arrangement.spacedBy(8.dp),
199 verticalAlignment = Alignment.CenterVertically,
201 RadioButton(selected = selected, onClick = onSelect)
204 ThemeMode.SYSTEM -> stringResource(R.string.theme_system)
205 ThemeMode.LIGHT -> stringResource(R.string.theme_light)
206 ThemeMode.DARK -> stringResource(R.string.theme_dark)
208 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