git.iko.soy/parseable-android
on 0da331f help sign in
245 lines 11 kB history raw
1package com.parseable.android.ui.screens.settings
3import androidx.compose.foundation.layout.*
4import androidx.compose.foundation.rememberScrollState
5import androidx.compose.foundation.verticalScroll
6import androidx.compose.material.icons.Icons
7import androidx.compose.material.icons.automirrored.filled.ArrowBack
8import androidx.compose.material.icons.filled.*
9import androidx.compose.material3.*
10import androidx.compose.material3.pulltorefresh.PullToRefreshBox
11import androidx.compose.runtime.*
12import androidx.compose.ui.Alignment
13import androidx.compose.ui.Modifier
14import androidx.compose.ui.text.font.FontWeight
15import androidx.compose.ui.unit.dp
16import androidx.hilt.navigation.compose.hiltViewModel
17import androidx.lifecycle.compose.collectAsStateWithLifecycle
18import com.parseable.android.BuildConfig
19import kotlinx.serialization.json.contentOrNull
20import kotlinx.serialization.json.jsonPrimitive
22@OptIn(ExperimentalMaterial3Api::class)
23@Composable
24fun SettingsScreen(
25 onBack: () -> Unit,
26 viewModel: SettingsViewModel = hiltViewModel(),
27) {
28 val state by viewModel.state.collectAsStateWithLifecycle()
30 Scaffold(
31 topBar = {
32 TopAppBar(
33 title = { Text("Settings") },
34 navigationIcon = {
35 IconButton(onClick = onBack) {
36 Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
37 }
38 },
39 )
40 },
41 ) { padding ->
42 PullToRefreshBox(
43 isRefreshing = state.isLoading,
44 onRefresh = viewModel::load,
45 modifier = Modifier
46 .fillMaxSize()
47 .padding(padding),
48 ) {
49 Column(
50 modifier = Modifier
51 .fillMaxSize()
52 .verticalScroll(rememberScrollState())
53 .padding(16.dp),
54 verticalArrangement = Arrangement.spacedBy(16.dp),
55 ) {
56 // Server info
57 Card(
58 modifier = Modifier.fillMaxWidth(),
59 elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
60 ) {
61 Column(modifier = Modifier.padding(16.dp)) {
62 Row(verticalAlignment = Alignment.CenterVertically) {
63 Icon(
64 Icons.Filled.Dns,
65 contentDescription = "Server connection",
66 tint = MaterialTheme.colorScheme.primary,
67 )
68 Spacer(modifier = Modifier.width(8.dp))
69 Text(
70 text = "Server Connection",
71 style = MaterialTheme.typography.titleMedium,
72 fontWeight = FontWeight.Bold,
73 )
74 }
75 Spacer(modifier = Modifier.height(12.dp))
76 SettingsRow("URL", state.serverUrl)
77 SettingsRow("Username", state.username)
78 SettingsRow("TLS", if (state.useTls) "Enabled" else "Disabled")
79 }
80 }
82 // Loading indicator for server data
83 if (state.isLoading) {
84 Box(
85 modifier = Modifier
86 .fillMaxWidth()
87 .padding(vertical = 8.dp),
88 contentAlignment = Alignment.Center,
89 ) {
90 Row(
91 verticalAlignment = Alignment.CenterVertically,
92 horizontalArrangement = Arrangement.spacedBy(8.dp),
93 ) {
94 CircularProgressIndicator(
95 modifier = Modifier.size(16.dp),
96 strokeWidth = 2.dp,
97 )
98 Text(
99 text = "Loading server info...",
100 style = MaterialTheme.typography.bodySmall,
101 color = MaterialTheme.colorScheme.onSurfaceVariant,
102 )
103 }
104 }
105 }
107 // Error from loading server data
108 state.error?.let { errorText ->
109 Card(
110 colors = CardDefaults.cardColors(
111 containerColor = MaterialTheme.colorScheme.errorContainer,
112 ),
113 modifier = Modifier.fillMaxWidth(),
114 ) {
115 Row(
116 modifier = Modifier.padding(16.dp),
117 verticalAlignment = Alignment.CenterVertically,
118 ) {
119 Icon(
120 Icons.Filled.ErrorOutline,
121 contentDescription = "Error",
122 tint = MaterialTheme.colorScheme.onErrorContainer,
123 )
124 Spacer(modifier = Modifier.width(8.dp))
125 Text(
126 text = errorText,
127 color = MaterialTheme.colorScheme.onErrorContainer,
128 style = MaterialTheme.typography.bodyMedium,
129 )
130 }
131 }
132 }
134 // About info
135 state.aboutInfo?.let { about ->
136 Card(
137 modifier = Modifier.fillMaxWidth(),
138 elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
139 ) {
140 Column(modifier = Modifier.padding(16.dp)) {
141 Row(verticalAlignment = Alignment.CenterVertically) {
142 Icon(
143 Icons.Filled.Info,
144 contentDescription = "Server info",
145 tint = MaterialTheme.colorScheme.primary,
146 )
147 Spacer(modifier = Modifier.width(8.dp))
148 Text(
149 text = "Server Info",
150 style = MaterialTheme.typography.titleMedium,
151 fontWeight = FontWeight.Bold,
152 )
153 }
154 Spacer(modifier = Modifier.height(12.dp))
155 about.version?.let { SettingsRow("Version", it) }
156 about.commit?.let { SettingsRow("Commit", it.take(12)) }
157 about.mode?.let { SettingsRow("Mode", it) }
158 about.license?.get("name")?.jsonPrimitive?.contentOrNull?.let { SettingsRow("License", it) }
159 about.deploymentId?.let { SettingsRow("Deployment ID", it.take(12)) }
160 about.queryEngine?.let { SettingsRow("Query Engine", it) }
161 }
162 }
163 }
165 // Users section
166 if (state.users.isNotEmpty()) {
167 Card(
168 modifier = Modifier.fillMaxWidth(),
169 elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
170 ) {
171 Column(modifier = Modifier.padding(16.dp)) {
172 Row(verticalAlignment = Alignment.CenterVertically) {
173 Icon(
174 Icons.Filled.People,
175 contentDescription = "Users",
176 tint = MaterialTheme.colorScheme.primary,
177 )
178 Spacer(modifier = Modifier.width(8.dp))
179 Text(
180 text = "Users (${state.users.size})",
181 style = MaterialTheme.typography.titleMedium,
182 fontWeight = FontWeight.Bold,
183 )
184 }
185 Spacer(modifier = Modifier.height(12.dp))
186 state.users.forEach { user ->
187 Text(
188 text = user,
189 style = MaterialTheme.typography.bodyMedium,
190 modifier = Modifier.padding(vertical = 4.dp),
191 )
192 }
193 }
194 }
195 }
197 // App info
198 Card(
199 modifier = Modifier.fillMaxWidth(),
200 elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
201 ) {
202 Column(modifier = Modifier.padding(16.dp)) {
203 Row(verticalAlignment = Alignment.CenterVertically) {
204 Icon(
205 Icons.Filled.PhoneAndroid,
206 contentDescription = "App info",
207 tint = MaterialTheme.colorScheme.primary,
208 )
209 Spacer(modifier = Modifier.width(8.dp))
210 Text(
211 text = "App",
212 style = MaterialTheme.typography.titleMedium,
213 fontWeight = FontWeight.Bold,
214 )
215 }
216 Spacer(modifier = Modifier.height(12.dp))
217 SettingsRow("App Version", BuildConfig.VERSION_NAME)
218 SettingsRow("Platform", "Android")
219 }
220 }
221 }
222 }
223 }
226@Composable
227private fun SettingsRow(label: String, value: String) {
228 Row(
229 modifier = Modifier
230 .fillMaxWidth()
231 .padding(vertical = 4.dp),
232 horizontalArrangement = Arrangement.SpaceBetween,
233 ) {
234 Text(
235 text = label,
236 style = MaterialTheme.typography.bodyMedium,
237 color = MaterialTheme.colorScheme.onSurfaceVariant,
238 )
239 Text(
240 text = value,
241 style = MaterialTheme.typography.bodyMedium,
242 fontWeight = FontWeight.Medium,
243 )
244 }