feat: update navigation logic in MainApp and enhance ListDetailScreen interactions; externalize strings in SettingsScreen and strings.xml
This commit is contained in:
@@ -74,11 +74,16 @@ fun MainApp() {
|
|||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
selected = currentRoute == Routes.TABLES || currentRoute?.startsWith("list/") == true,
|
selected = currentRoute == Routes.TABLES || currentRoute?.startsWith("list/") == true,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
if (currentRoute?.startsWith("list/") == true) {
|
||||||
|
// If we're in a list detail, pop back to the tables root instead of creating a new entry
|
||||||
|
navController.popBackStack(Routes.TABLES, inclusive = false)
|
||||||
|
} else if (currentRoute != Routes.TABLES) {
|
||||||
navController.navigate(Routes.TABLES) {
|
navController.navigate(Routes.TABLES) {
|
||||||
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
|
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
restoreState = true
|
restoreState = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
icon = { androidx.compose.material3.Icon(Icons.Default.Home, contentDescription = null) },
|
icon = { androidx.compose.material3.Icon(Icons.Default.Home, contentDescription = null) },
|
||||||
label = { Text("Tables") },
|
label = { Text("Tables") },
|
||||||
@@ -87,11 +92,16 @@ fun MainApp() {
|
|||||||
// Highlight settings tab for both Settings screen and Logs screen
|
// Highlight settings tab for both Settings screen and Logs screen
|
||||||
selected = currentRoute == Routes.SETTINGS || currentRoute == Routes.LOGS,
|
selected = currentRoute == Routes.SETTINGS || currentRoute == Routes.LOGS,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
if (currentRoute == Routes.LOGS) {
|
||||||
|
// Return to Settings without rebuilding entire graph
|
||||||
|
navController.popBackStack(Routes.SETTINGS, inclusive = false)
|
||||||
|
} else if (currentRoute != Routes.SETTINGS) {
|
||||||
navController.navigate(Routes.SETTINGS) {
|
navController.navigate(Routes.SETTINGS) {
|
||||||
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
|
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
restoreState = true
|
restoreState = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
icon = { androidx.compose.material3.Icon(Icons.Default.Settings, contentDescription = null) },
|
icon = { androidx.compose.material3.Icon(Icons.Default.Settings, contentDescription = null) },
|
||||||
label = { Text("Settings") },
|
label = { Text("Settings") },
|
||||||
|
|||||||
+49
-50
@@ -202,7 +202,13 @@ fun ListDetailScreen(
|
|||||||
val localCtx = LocalContext.current
|
val localCtx = LocalContext.current
|
||||||
val localPrefs = remember { PreferencesManager.getInstance(localCtx) }
|
val localPrefs = remember { PreferencesManager.getInstance(localCtx) }
|
||||||
ConnectionStatusAction(prefs = localPrefs)
|
ConnectionStatusAction(prefs = localPrefs)
|
||||||
IconButton(onClick = { showAddItemDialog = true }) {
|
IconButton(onClick = {
|
||||||
|
if (stableFields.isEmpty()) {
|
||||||
|
showManageColumnsDialog = true
|
||||||
|
} else {
|
||||||
|
showAddItemDialog = true
|
||||||
|
}
|
||||||
|
}) {
|
||||||
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.add_item))
|
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.add_item))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -253,7 +259,7 @@ fun ListDetailScreen(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "No fields yet. Add fields to get started!",
|
text = "No columns yet. Add columns to get started!",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -463,7 +469,7 @@ fun ListDetailScreen(
|
|||||||
}
|
}
|
||||||
prefs.setColumnWidths(listId, fieldWidths.mapValues { it.value.value })
|
prefs.setColumnWidths(listId, fieldWidths.mapValues { it.value.value })
|
||||||
},
|
},
|
||||||
label = { Text("Auto-fit") },
|
label = { Text(stringResource(R.string.auto_fit)) },
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.FitScreen,
|
Icons.Default.FitScreen,
|
||||||
@@ -476,44 +482,10 @@ fun ListDetailScreen(
|
|||||||
|
|
||||||
// Header will be rendered as a stickyHeader inside the LazyColumn below
|
// Header will be rendered as a stickyHeader inside the LazyColumn below
|
||||||
|
|
||||||
// Items list with synchronized scrolling
|
// Unified layout: always render headers, then content state/messages
|
||||||
if (stableItems.isEmpty()) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.no_items),
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (processedItems.isEmpty()) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
|
||||||
Text(
|
|
||||||
text = "No items match the current filter",
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
TextButton(onClick = {
|
|
||||||
filterField = null
|
|
||||||
filterValue = ""
|
|
||||||
}) {
|
|
||||||
Text("Clear Filter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
// If a new item was just added, scroll to bottom after composition
|
|
||||||
LaunchedEffect(processedItems.size, pendingScrollToBottom) {
|
LaunchedEffect(processedItems.size, pendingScrollToBottom) {
|
||||||
if (pendingScrollToBottom) {
|
if (pendingScrollToBottom && processedItems.isNotEmpty()) {
|
||||||
// Scroll to the last real item index
|
|
||||||
val lastIndex = (listState.layoutInfo.totalItemsCount - 1).coerceAtLeast(0)
|
val lastIndex = (listState.layoutInfo.totalItemsCount - 1).coerceAtLeast(0)
|
||||||
try {
|
try {
|
||||||
listState.animateScrollToItem(lastIndex)
|
listState.animateScrollToItem(lastIndex)
|
||||||
@@ -529,7 +501,7 @@ fun ListDetailScreen(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.horizontalScroll(horizontalScrollState),
|
.horizontalScroll(horizontalScrollState),
|
||||||
) {
|
) {
|
||||||
// Fixed header (does not participate in vertical scroll)
|
// Fixed header (always visible even with zero items)
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
@@ -556,7 +528,6 @@ fun ListDetailScreen(
|
|||||||
onHeaderClick = { showManageColumnsDialog = true },
|
onHeaderClick = { showManageColumnsDialog = true },
|
||||||
alignment = columnAlignments[field.id] ?: "start",
|
alignment = columnAlignments[field.id] ?: "start",
|
||||||
onAlignmentChange = { newAlign ->
|
onAlignmentChange = { newAlign ->
|
||||||
// Update local state and persist via ViewModel + cache to prefs for backward-compat
|
|
||||||
columnAlignments[field.id] = newAlign
|
columnAlignments[field.id] = newAlign
|
||||||
viewModel.updateFieldAlignment(field.id, newAlign)
|
viewModel.updateFieldAlignment(field.id, newAlign)
|
||||||
prefs.setColumnAlignments(listId, columnAlignments.toMap())
|
prefs.setColumnAlignments(listId, columnAlignments.toMap())
|
||||||
@@ -564,28 +535,55 @@ fun ListDetailScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Loading overlay only while nothing is available to render yet
|
when {
|
||||||
if (isLoading && stableFields.isEmpty() && stableItems.isEmpty()) {
|
isLoading && stableItems.isEmpty() -> {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) { androidx.compose.material3.CircularProgressIndicator() }
|
||||||
|
}
|
||||||
|
stableItems.isEmpty() -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
androidx.compose.material3.CircularProgressIndicator()
|
Text(
|
||||||
|
text = stringResource(R.string.no_items),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processedItems.isEmpty() -> {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Text(
|
||||||
|
text = "No items match the current filter",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
TextButton(onClick = {
|
||||||
|
filterField = null
|
||||||
|
filterValue = ""
|
||||||
|
}) { Text(stringResource(R.string.clear_filter)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
// Items list below the fixed header
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier =
|
modifier = Modifier.fillMaxSize(),
|
||||||
Modifier
|
|
||||||
.fillMaxSize(),
|
|
||||||
state = listState,
|
state = listState,
|
||||||
contentPadding = PaddingValues(0.dp),
|
contentPadding = PaddingValues(0.dp),
|
||||||
) {
|
) {
|
||||||
groupedItems.entries.forEach { (_, groupItems) ->
|
groupedItems.entries.forEach { (_, groupItems) ->
|
||||||
// Show items in the group
|
|
||||||
items(
|
items(
|
||||||
items = groupItems,
|
items = groupItems,
|
||||||
key = { it.item.id },
|
key = { it.item.id },
|
||||||
@@ -606,6 +604,7 @@ fun ListDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (showManageColumnsDialog) {
|
if (showManageColumnsDialog) {
|
||||||
ManageColumnsDialog(
|
ManageColumnsDialog(
|
||||||
|
|||||||
+37
-40
@@ -153,7 +153,7 @@ fun SettingsScreen(
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Server Connection",
|
text = stringResource(R.string.server_connection),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ fun SettingsScreen(
|
|||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Connected to",
|
text = stringResource(R.string.connected_to),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -186,7 +186,7 @@ fun SettingsScreen(
|
|||||||
// Authentication section removed
|
// Authentication section removed
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "Appearance",
|
text = stringResource(R.string.appearance),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -198,19 +198,19 @@ fun SettingsScreen(
|
|||||||
FilterChip(
|
FilterChip(
|
||||||
selected = themeMode == PreferencesManager.THEME_MODE_SYSTEM,
|
selected = themeMode == PreferencesManager.THEME_MODE_SYSTEM,
|
||||||
onClick = { preferencesManager.setThemeMode(PreferencesManager.THEME_MODE_SYSTEM) },
|
onClick = { preferencesManager.setThemeMode(PreferencesManager.THEME_MODE_SYSTEM) },
|
||||||
label = { Text("System") },
|
label = { Text(stringResource(R.string.theme_system)) },
|
||||||
leadingIcon = { Icon(Icons.Filled.SettingsBrightness, contentDescription = null) },
|
leadingIcon = { Icon(Icons.Filled.SettingsBrightness, contentDescription = null) },
|
||||||
)
|
)
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = themeMode == PreferencesManager.THEME_MODE_LIGHT,
|
selected = themeMode == PreferencesManager.THEME_MODE_LIGHT,
|
||||||
onClick = { preferencesManager.setThemeMode(PreferencesManager.THEME_MODE_LIGHT) },
|
onClick = { preferencesManager.setThemeMode(PreferencesManager.THEME_MODE_LIGHT) },
|
||||||
label = { Text("Light") },
|
label = { Text(stringResource(R.string.theme_light)) },
|
||||||
leadingIcon = { Icon(Icons.Filled.Brightness7, contentDescription = null) },
|
leadingIcon = { Icon(Icons.Filled.Brightness7, contentDescription = null) },
|
||||||
)
|
)
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = themeMode == PreferencesManager.THEME_MODE_DARK,
|
selected = themeMode == PreferencesManager.THEME_MODE_DARK,
|
||||||
onClick = { preferencesManager.setThemeMode(PreferencesManager.THEME_MODE_DARK) },
|
onClick = { preferencesManager.setThemeMode(PreferencesManager.THEME_MODE_DARK) },
|
||||||
label = { Text("Dark") },
|
label = { Text(stringResource(R.string.theme_dark)) },
|
||||||
leadingIcon = { Icon(Icons.Filled.Brightness4, contentDescription = null) },
|
leadingIcon = { Icon(Icons.Filled.Brightness4, contentDescription = null) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -222,9 +222,9 @@ fun SettingsScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text("Use Material 3 colors")
|
Text(stringResource(R.string.use_material3_colors))
|
||||||
Text(
|
Text(
|
||||||
text = "Dynamic colors on supported devices",
|
text = stringResource(R.string.dynamic_colors_desc),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -239,9 +239,9 @@ fun SettingsScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text("AMOLED dark")
|
Text(stringResource(R.string.amoled_dark))
|
||||||
Text(
|
Text(
|
||||||
text = "Pure black background in dark mode",
|
text = stringResource(R.string.amoled_dark_desc),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -251,7 +251,7 @@ fun SettingsScreen(
|
|||||||
|
|
||||||
// Sync settings
|
// Sync settings
|
||||||
Text(
|
Text(
|
||||||
text = "Sync",
|
text = stringResource(R.string.sync),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ fun SettingsScreen(
|
|||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "HTTP polling interval (ms)",
|
text = stringResource(R.string.http_polling_interval_ms),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -296,7 +296,7 @@ fun SettingsScreen(
|
|||||||
syncIntervalError = null
|
syncIntervalError = null
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Text("Apply")
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (syncIntervalError != null) {
|
if (syncIntervalError != null) {
|
||||||
@@ -307,7 +307,7 @@ fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Text(
|
Text(
|
||||||
text = "Current: $syncIntervalMs ms (min 250, max 600000)",
|
text = stringResource(R.string.current_interval, syncIntervalMs),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -317,7 +317,7 @@ fun SettingsScreen(
|
|||||||
|
|
||||||
// Notifications section (moved inside the main scroll column so it doesn't overlay other content)
|
// Notifications section (moved inside the main scroll column so it doesn't overlay other content)
|
||||||
Text(
|
Text(
|
||||||
text = "Notifications",
|
text = stringResource(R.string.notifications),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
Card(
|
Card(
|
||||||
@@ -334,9 +334,9 @@ fun SettingsScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text("List added")
|
Text(stringResource(R.string.notify_list_added_title))
|
||||||
Text(
|
Text(
|
||||||
text = "Notify when a new list/table is created",
|
text = stringResource(R.string.notify_list_added_desc),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -360,9 +360,9 @@ fun SettingsScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text("List edited")
|
Text(stringResource(R.string.notify_list_edited_title))
|
||||||
Text(
|
Text(
|
||||||
text = "Notify when a list/table name changes",
|
text = stringResource(R.string.notify_list_edited_desc),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -386,9 +386,9 @@ fun SettingsScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text("List content updated")
|
Text(stringResource(R.string.notify_list_content_title))
|
||||||
Text(
|
Text(
|
||||||
text = "Notify when fields or items change (add/edit/delete)",
|
text = stringResource(R.string.notify_list_content_desc),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -412,9 +412,9 @@ fun SettingsScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text("List removed")
|
Text(stringResource(R.string.notify_list_removed_title))
|
||||||
Text(
|
Text(
|
||||||
text = "Notify when a list/table is deleted",
|
text = stringResource(R.string.notify_list_removed_desc),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -433,7 +433,7 @@ fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = "Notifications fire only when the app is in background or not visible.",
|
text = stringResource(R.string.notifications_footer),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -461,14 +461,14 @@ fun SettingsScreen(
|
|||||||
Button(onClick = onNavigateToLogs, modifier = Modifier.fillMaxWidth()) {
|
Button(onClick = onNavigateToLogs, modifier = Modifier.fillMaxWidth()) {
|
||||||
Icon(Icons.AutoMirrored.Filled.List, contentDescription = null)
|
Icon(Icons.AutoMirrored.Filled.List, contentDescription = null)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Text("Open Logs")
|
Text(stringResource(R.string.open_logs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leave Server section
|
// Leave Server section
|
||||||
Text(
|
Text(
|
||||||
text = "Leave Server",
|
text = stringResource(R.string.leave_server_section),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
Card(
|
Card(
|
||||||
@@ -481,27 +481,27 @@ fun SettingsScreen(
|
|||||||
) {
|
) {
|
||||||
// Warning/info text appears above the action button
|
// Warning/info text appears above the action button
|
||||||
Text(
|
Text(
|
||||||
text = "Leaving removes local data",
|
text = stringResource(R.string.leaving_removes_local_data),
|
||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Disconnects and clears local database",
|
text = stringResource(R.string.disconnects_and_clears_db),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Clears stored server URL and password",
|
text = stringResource(R.string.clears_server_settings),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Deletes all local data (tables, fields, items)",
|
text = stringResource(R.string.deletes_all_local),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Returns to server setup screen",
|
text = stringResource(R.string.returns_to_setup),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
@@ -524,9 +524,9 @@ fun SettingsScreen(
|
|||||||
strokeWidth = 2.dp,
|
strokeWidth = 2.dp,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Text("Leaving...")
|
Text(stringResource(R.string.leaving_progress))
|
||||||
} else {
|
} else {
|
||||||
Text("Leave Server")
|
Text(stringResource(R.string.leave_server))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,12 +536,9 @@ fun SettingsScreen(
|
|||||||
if (showLeaveDialog) {
|
if (showLeaveDialog) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { showLeaveDialog = false },
|
onDismissRequest = { showLeaveDialog = false },
|
||||||
title = { Text("Leave Server?") },
|
title = { Text(stringResource(R.string.leave_server_dialog_title)) },
|
||||||
text = {
|
text = {
|
||||||
Text(
|
Text(stringResource(R.string.leave_server_dialog_body),
|
||||||
"All your data will be synced to the server before disconnecting. " +
|
|
||||||
"After leaving, all local data will be deleted and you'll need to set up a new connection. " +
|
|
||||||
"This action cannot be undone.",
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
@@ -589,12 +586,12 @@ fun SettingsScreen(
|
|||||||
contentColor = MaterialTheme.colorScheme.error,
|
contentColor = MaterialTheme.colorScheme.error,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Text("Leave Server")
|
Text(stringResource(R.string.leave_server))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
TextButton(onClick = { showLeaveDialog = false }) {
|
TextButton(onClick = { showLeaveDialog = false }) {
|
||||||
Text("Cancel")
|
Text(stringResource(R.string.cancel))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
<string name="lists">Tables</string>
|
<string name="lists">Tables</string>
|
||||||
<string name="create_list">Create Table</string>
|
<string name="create_list">Create Table</string>
|
||||||
<string name="list_name">Table Name</string>
|
<string name="list_name">Table Name</string>
|
||||||
<string name="add_field">Add Field</string>
|
<string name="add_field">Add Column</string>
|
||||||
<string name="field_name">Field Name</string>
|
<string name="field_name">Column Name</string>
|
||||||
<string name="add_item">Add Item</string>
|
<string name="add_item">Add Item</string>
|
||||||
<string name="add">Add</string>
|
<string name="add">Add</string>
|
||||||
<string name="delete">Delete</string>
|
<string name="delete">Delete</string>
|
||||||
@@ -20,7 +20,67 @@
|
|||||||
<string name="syncing">Syncing…</string>
|
<string name="syncing">Syncing…</string>
|
||||||
<string name="sync_error">Sync error</string>
|
<string name="sync_error">Sync error</string>
|
||||||
<string name="delete_list">Delete Table</string>
|
<string name="delete_list">Delete Table</string>
|
||||||
<string name="delete_field">Delete Field</string>
|
<string name="delete_field">Delete Column</string>
|
||||||
<string name="delete_item">Delete Item</string>
|
<string name="delete_item">Delete Item</string>
|
||||||
<string name="confirm_delete">Are you sure you want to delete this?</string>
|
<string name="confirm_delete">Are you sure you want to delete this?</string>
|
||||||
|
<!-- Newly externalized strings -->
|
||||||
|
<string name="theme_system">System</string>
|
||||||
|
<string name="theme_light">Light</string>
|
||||||
|
<string name="theme_dark">Dark</string>
|
||||||
|
<string name="appearance">Appearance</string>
|
||||||
|
<string name="use_material3_colors">Use Material 3 colors</string>
|
||||||
|
<string name="amoled_dark">AMOLED dark</string>
|
||||||
|
<string name="dynamic_colors_desc">Dynamic colors on supported devices</string>
|
||||||
|
<string name="amoled_dark_desc">Pure black background in dark mode</string>
|
||||||
|
<string name="server_connection">Server Connection</string>
|
||||||
|
<string name="connected_to">Connected to</string>
|
||||||
|
<string name="http_polling_interval_ms">HTTP polling interval (ms)</string>
|
||||||
|
<string name="apply">Apply</string>
|
||||||
|
<string name="current_interval">Current: %1$d ms (min 250, max 600000)</string>
|
||||||
|
<string name="notifications">Notifications</string>
|
||||||
|
<string name="notify_list_added_title">List added</string>
|
||||||
|
<string name="notify_list_added_desc">Notify when a new list/table is created</string>
|
||||||
|
<string name="notify_list_edited_title">List edited</string>
|
||||||
|
<string name="notify_list_edited_desc">Notify when a list/table name changes</string>
|
||||||
|
<string name="notify_list_content_title">List content updated</string>
|
||||||
|
<string name="notify_list_content_desc">Notify when columns or items change (add/edit/delete)</string>
|
||||||
|
<string name="notify_list_removed_title">List removed</string>
|
||||||
|
<string name="notify_list_removed_desc">Notify when a list/table is deleted</string>
|
||||||
|
<string name="notifications_footer">Notifications fire only when the app is in background or not visible.</string>
|
||||||
|
<string name="open_logs">Open Logs</string>
|
||||||
|
<string name="leave_server_section">Leave Server</string>
|
||||||
|
<string name="leaving_removes_local_data">Leaving removes local data</string>
|
||||||
|
<string name="disconnects_and_clears_db">Disconnects and clears local database</string>
|
||||||
|
<string name="clears_server_settings">Clears stored server URL and password</string>
|
||||||
|
<string name="deletes_all_local">Deletes all local data (tables, columns, items)</string>
|
||||||
|
<string name="returns_to_setup">Returns to server setup screen</string>
|
||||||
|
<string name="leave_server">Leave Server</string>
|
||||||
|
<string name="leaving_progress">Leaving...</string>
|
||||||
|
<string name="leave_server_dialog_title">Leave Server?</string>
|
||||||
|
<string name="leave_server_dialog_body">All your data will sync to the server before disconnecting. After leaving, all local data will be deleted and you must set up a new connection. This action cannot be undone.</string>
|
||||||
|
<string name="clear_filter">Clear Filter</string>
|
||||||
|
<string name="no_items_match_filter">No items match the current filter</string>
|
||||||
|
<string name="auto_fit">Auto-fit</string>
|
||||||
|
<string name="resize">Resize</string>
|
||||||
|
<string name="field_type">Field Type</string>
|
||||||
|
<!-- Field type labels -->
|
||||||
|
<string name="ft_text">Text</string>
|
||||||
|
<string name="ft_multiline_text">Multi-line Text</string>
|
||||||
|
<string name="ft_number">Number</string>
|
||||||
|
<string name="ft_currency">Currency</string>
|
||||||
|
<string name="ft_percentage">Percentage</string>
|
||||||
|
<string name="ft_dropdown">Dropdown</string>
|
||||||
|
<string name="ft_autocomplete">Autocomplete</string>
|
||||||
|
<string name="ft_checkbox">Checkbox</string>
|
||||||
|
<string name="ft_switch">Switch</string>
|
||||||
|
<string name="ft_url">URL</string>
|
||||||
|
<string name="ft_email">Email</string>
|
||||||
|
<string name="ft_phone">Phone</string>
|
||||||
|
<string name="ft_date">Date</string>
|
||||||
|
<string name="ft_time">Time</string>
|
||||||
|
<string name="ft_datetime">Date & Time</string>
|
||||||
|
<string name="ft_duration">Duration</string>
|
||||||
|
<string name="ft_image">Image</string>
|
||||||
|
<string name="ft_file">File</string>
|
||||||
|
<!-- Additional existing types could be added similarly -->
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user