feat(v1.8.0): IMPL_021 Sync Status Legend
- New SyncStatusLegendDialog.kt showing all 5 sync status icons with descriptions - Help button (?) in MainScreen TopAppBar (only visible when sync available) - Localized strings (English + German) for all 5 status explanations - Material You design with consistent colors matching NoteCard icons - Dialog shows: Synced, Pending, Conflict, Local only, Deleted on server IMPL_021_SYNC_STATUS_LEGEND.md
This commit is contained in:
@@ -17,6 +17,7 @@ import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.SelectAll
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
// FabPosition nicht mehr benötigt - FAB wird manuell platziert
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -53,6 +54,7 @@ import dev.dettmer.simplenotes.ui.main.components.NoteTypeFAB
|
||||
import dev.dettmer.simplenotes.ui.main.components.NotesList
|
||||
import dev.dettmer.simplenotes.ui.main.components.NotesStaggeredGrid
|
||||
import dev.dettmer.simplenotes.ui.main.components.SyncStatusBanner
|
||||
import dev.dettmer.simplenotes.ui.main.components.SyncStatusLegendDialog
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val TIMESTAMP_UPDATE_INTERVAL_MS = 30_000L
|
||||
@@ -92,6 +94,9 @@ fun MainScreen(
|
||||
// Delete confirmation dialog state
|
||||
var showBatchDeleteDialog by remember { mutableStateOf(false) }
|
||||
|
||||
// 🆕 v1.8.0: Sync status legend dialog
|
||||
var showSyncLegend by remember { mutableStateOf(false) }
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val scope = rememberCoroutineScope()
|
||||
val listState = rememberLazyListState()
|
||||
@@ -170,6 +175,8 @@ fun MainScreen(
|
||||
) {
|
||||
MainTopBar(
|
||||
syncEnabled = canSync,
|
||||
showSyncLegend = isSyncAvailable, // 🆕 v1.8.0: Nur wenn Sync verfügbar
|
||||
onSyncLegendClick = { showSyncLegend = true }, // 🆕 v1.8.0
|
||||
onSyncClick = { viewModel.triggerManualSync("toolbar") },
|
||||
onSettingsClick = onOpenSettings
|
||||
)
|
||||
@@ -276,6 +283,13 @@ fun MainScreen(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 🆕 v1.8.0: Sync Status Legend Dialog
|
||||
if (showSyncLegend) {
|
||||
SyncStatusLegendDialog(
|
||||
onDismiss = { showSyncLegend = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +297,8 @@ fun MainScreen(
|
||||
@Composable
|
||||
private fun MainTopBar(
|
||||
syncEnabled: Boolean,
|
||||
showSyncLegend: Boolean, // 🆕 v1.8.0: Ob der Hilfe-Button sichtbar sein soll
|
||||
onSyncLegendClick: () -> Unit, // 🆕 v1.8.0
|
||||
onSyncClick: () -> Unit,
|
||||
onSettingsClick: () -> Unit
|
||||
) {
|
||||
@@ -294,6 +310,15 @@ private fun MainTopBar(
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
// 🆕 v1.8.0: Sync Status Legend Button (nur wenn Sync verfügbar)
|
||||
if (showSyncLegend) {
|
||||
IconButton(onClick = onSyncLegendClick) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
|
||||
contentDescription = stringResource(R.string.sync_legend_button)
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = onSyncClick,
|
||||
enabled = syncEnabled
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package dev.dettmer.simplenotes.ui.main.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material.icons.outlined.CloudDone
|
||||
import androidx.compose.material.icons.outlined.CloudOff
|
||||
import androidx.compose.material.icons.outlined.CloudSync
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.dettmer.simplenotes.R
|
||||
|
||||
/**
|
||||
* 🆕 v1.8.0: Dialog showing the sync status icon legend
|
||||
*
|
||||
* Displays all 5 SyncStatus values with their icons, colors,
|
||||
* and descriptions. Helps users understand what each icon means.
|
||||
*/
|
||||
@Composable
|
||||
fun SyncStatusLegendDialog(
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.sync_legend_title),
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// Optional: Kurze Einleitung
|
||||
Text(
|
||||
text = stringResource(R.string.sync_legend_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
// ☁️✓ SYNCED
|
||||
LegendRow(
|
||||
icon = Icons.Outlined.CloudDone,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
label = stringResource(R.string.sync_legend_synced_label),
|
||||
description = stringResource(R.string.sync_legend_synced_desc)
|
||||
)
|
||||
|
||||
// ☁️↻ PENDING
|
||||
LegendRow(
|
||||
icon = Icons.Outlined.CloudSync,
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
label = stringResource(R.string.sync_legend_pending_label),
|
||||
description = stringResource(R.string.sync_legend_pending_desc)
|
||||
)
|
||||
|
||||
// ⚠️ CONFLICT
|
||||
LegendRow(
|
||||
icon = Icons.Default.Warning,
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
label = stringResource(R.string.sync_legend_conflict_label),
|
||||
description = stringResource(R.string.sync_legend_conflict_desc)
|
||||
)
|
||||
|
||||
// ☁️✗ LOCAL_ONLY
|
||||
LegendRow(
|
||||
icon = Icons.Outlined.CloudOff,
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
label = stringResource(R.string.sync_legend_local_only_label),
|
||||
description = stringResource(R.string.sync_legend_local_only_desc)
|
||||
)
|
||||
|
||||
// ☁️✗ DELETED_ON_SERVER
|
||||
LegendRow(
|
||||
icon = Icons.Outlined.CloudOff,
|
||||
tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f),
|
||||
label = stringResource(R.string.sync_legend_deleted_label),
|
||||
description = stringResource(R.string.sync_legend_deleted_desc)
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.ok))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Single row in the sync status legend
|
||||
* Shows icon + label + description
|
||||
*/
|
||||
@Composable
|
||||
private fun LegendRow(
|
||||
icon: ImageVector,
|
||||
tint: Color,
|
||||
label: String,
|
||||
description: String
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.Top,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null, // Dekorativ, Label reicht
|
||||
tint = tint,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.padding(top = 2.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,21 @@
|
||||
<string name="sync_status_error">Synchronisierung fehlgeschlagen</string>
|
||||
<string name="sync_already_running">Synchronisierung läuft bereits</string>
|
||||
|
||||
<!-- 🆕 v1.8.0: Sync-Status Legende Dialog -->
|
||||
<string name="sync_legend_button">Sync-Status Hilfe</string>
|
||||
<string name="sync_legend_title">Sync-Status Icons</string>
|
||||
<string name="sync_legend_description">Jede Notiz zeigt ein kleines Icon, das den Sync-Status anzeigt:</string>
|
||||
<string name="sync_legend_synced_label">Synchronisiert</string>
|
||||
<string name="sync_legend_synced_desc">Diese Notiz ist auf allen Geräten aktuell.</string>
|
||||
<string name="sync_legend_pending_label">Ausstehend</string>
|
||||
<string name="sync_legend_pending_desc">Diese Notiz hat lokale Änderungen, die noch synchronisiert werden müssen.</string>
|
||||
<string name="sync_legend_conflict_label">Konflikt</string>
|
||||
<string name="sync_legend_conflict_desc">Diese Notiz wurde auf mehreren Geräten gleichzeitig geändert. Die neueste Version wurde beibehalten.</string>
|
||||
<string name="sync_legend_local_only_label">Nur lokal</string>
|
||||
<string name="sync_legend_local_only_desc">Diese Notiz wurde noch nie mit dem Server synchronisiert.</string>
|
||||
<string name="sync_legend_deleted_label">Auf Server gelöscht</string>
|
||||
<string name="sync_legend_deleted_desc">Diese Notiz wurde auf einem anderen Gerät oder direkt auf dem Server gelöscht. Sie existiert noch lokal.</string>
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- DELETE DIALOGS -->
|
||||
<!-- ============================= -->
|
||||
|
||||
@@ -65,6 +65,21 @@
|
||||
<string name="sync_status_local_only">Not yet synced</string>
|
||||
<string name="sync_status_deleted_on_server">Deleted on server</string>
|
||||
|
||||
<!-- 🆕 v1.8.0: Sync Status Legend Dialog -->
|
||||
<string name="sync_legend_button">Sync status help</string>
|
||||
<string name="sync_legend_title">Sync Status Icons</string>
|
||||
<string name="sync_legend_description">Each note shows a small icon indicating its sync status:</string>
|
||||
<string name="sync_legend_synced_label">Synced</string>
|
||||
<string name="sync_legend_synced_desc">This note is up to date on all devices.</string>
|
||||
<string name="sync_legend_pending_label">Pending</string>
|
||||
<string name="sync_legend_pending_desc">This note has local changes waiting to be synced.</string>
|
||||
<string name="sync_legend_conflict_label">Conflict</string>
|
||||
<string name="sync_legend_conflict_desc">This note was changed on multiple devices simultaneously. The latest version was kept.</string>
|
||||
<string name="sync_legend_local_only_label">Local only</string>
|
||||
<string name="sync_legend_local_only_desc">This note has never been synced to the server yet.</string>
|
||||
<string name="sync_legend_deleted_label">Deleted on server</string>
|
||||
<string name="sync_legend_deleted_desc">This note was deleted on another device or directly on the server. It still exists locally.</string>
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- DELETE DIALOGS -->
|
||||
<!-- ============================= -->
|
||||
|
||||
Reference in New Issue
Block a user