- NEW: Configurable sync triggers (onSave, onResume, WiFi, Periodic, Boot) - NEW: Offline mode toggle to disable all network features - Various fixes and UI improvements - Version bumped to 1.6.0 (code 14)
11 KiB
v1.6.0 Feature: Server-Lösch-Einschränkung im Offline-Modus
📋 Übersicht
Problem: Im Offline-Modus kann der Benutzer immer noch "Überall löschen (auch Server)" auswählen, was zu Netzwerkverkehr führt (auch wenn die Anfrage fehlschlägt).
Ziel: Die "Überall löschen"-Option im Offline-Modus subtil aber intuitiv deaktivieren, um echte Offline-Nutzung zu gewährleisten.
🔍 Analyse der betroffenen Komponenten
1. DeleteConfirmationDialog
Datei: DeleteConfirmationDialog.kt
Aktueller Zustand:
- Zeigt zwei Optionen: "Überall löschen" und "Nur lokal löschen"
- Keine Berücksichtigung des Offline-Modus
- Verwendet in:
MainScreen.kt(Batch-Delete) undNoteEditorScreen.kt(Einzelne Notiz)
Änderungen:
- Neuer Parameter:
isOfflineMode: Boolean = false - "Überall löschen" Button:
enabled = !isOfflineMode - Subtile visuelle Kennzeichnung wenn deaktiviert
2. Verwendungsstellen
| Datei | Verwendung | ViewModel-Zugriff |
|---|---|---|
MainScreen.kt |
Batch-Löschung | MainViewModel.isOfflineMode ✅ bereits vorhanden |
NoteEditorScreen.kt |
Einzelne Notiz | NoteEditorViewModel ❌ benötigt Erweiterung |
3. NoteEditorViewModel
Datei: NoteEditorViewModel.kt
Aktueller Zustand:
- Prüft Offline-Status nur für
triggerOnSaveSync()inline viaprefs.getString(KEY_SERVER_URL, null) - Kein reaktiver State für Offline-Modus
Änderungen:
- Neuer StateFlow:
isOfflineMode: StateFlow<Boolean>
📐 Technische Design-Entscheidungen
UI/UX Design für deaktivierte Option
Option A: Grayed-out mit Tooltip-Hinweis ✅ EMPFOHLEN
┌─────────────────────────────────────────┐
│ Notiz löschen? │
│ │
│ Wie möchtest du diese Notiz löschen? │
│ │
│ ┌─────────────────────────────────────┐│
│ │ Überall löschen (auch Server) ││ ← Grau, nicht anklickbar
│ │ 📴 Nicht verfügbar im Offline-Modus ││ ← Subtiler Hinweis
│ └─────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────┐│
│ │ ✓ Nur lokal löschen ││ ← Normal, anklickbar
│ └─────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────┐│
│ │ Abbrechen ││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
Vorteile:
- Konsistent mit bestehendem v1.6.0 Pattern (BackupSettingsScreen, MarkdownSettingsScreen)
- Benutzer sieht sofort warum Option nicht verfügbar
- Keine Verwirrung - klare Ursache angegeben
Option B: Button komplett ausblenden ❌
Nachteile:
- Verwirrend für Benutzer die den Button sonst sehen
- Inkonsistent mit v1.6.0 Design-Pattern
Option C: Button mit Toast-Feedback ❌
Nachteile:
- Schlechte UX - warum klickbar wenn nicht möglich?
- Frustierend für Benutzer
📝 Implementierungs-Plan
Phase 1: DeleteConfirmationDialog erweitern
Schritt 1.1: Neuer Parameter und String-Ressourcen
// DeleteConfirmationDialog.kt
@Composable
fun DeleteConfirmationDialog(
noteCount: Int = 1,
isOfflineMode: Boolean = false, // 🌟 v1.6.0: NEU
onDismiss: () -> Unit,
onDeleteLocal: () -> Unit,
onDeleteEverywhere: () -> Unit
)
Neue Strings:
<!-- values/strings.xml -->
<string name="delete_everywhere_offline_hint">Not available in offline mode</string>
<!-- values-de/strings.xml -->
<string name="delete_everywhere_offline_hint">Nicht verfügbar im Offline-Modus</string>
Schritt 1.2: UI-Anpassung für deaktivierten Button
// Delete everywhere (server + local) - primary action
// 🌟 v1.6.0: Disabled in offline mode
Column {
TextButton(
onClick = onDeleteEverywhere,
modifier = Modifier.fillMaxWidth(),
enabled = !isOfflineMode, // 🌟 NEU
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.error,
disabledContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
)
) {
Text(stringResource(R.string.delete_everywhere))
}
// 🌟 v1.6.0: Show hint when offline
if (isOfflineMode) {
Text(
text = stringResource(R.string.delete_everywhere_offline_hint),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.tertiary,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
)
}
}
Phase 2: MainScreen anpassen (Batch-Löschung)
Datei: MainScreen.kt
Aktuelle Verwendung (Zeile ~218):
DeleteConfirmationDialog(
noteCount = selectedNotes.size,
onDismiss = { showBatchDeleteDialog = false },
onDeleteLocal = { ... },
onDeleteEverywhere = { ... }
)
Änderung:
DeleteConfirmationDialog(
noteCount = selectedNotes.size,
isOfflineMode = isOfflineMode, // 🌟 v1.6.0: NEU - bereits als State vorhanden
onDismiss = { showBatchDeleteDialog = false },
onDeleteLocal = { ... },
onDeleteEverywhere = { ... }
)
Phase 3: NoteEditorScreen + NoteEditorViewModel anpassen
Schritt 3.1: NoteEditorViewModel erweitern
// NoteEditorViewModel.kt
// 🌟 v1.6.0: Offline Mode State
private val _isOfflineMode = MutableStateFlow(
prefs.getBoolean(Constants.KEY_OFFLINE_MODE, true)
)
val isOfflineMode: StateFlow<Boolean> = _isOfflineMode.asStateFlow()
Schritt 3.2: NoteEditorScreen anpassen
// NoteEditorScreen.kt
// State abrufen
val isOfflineMode by viewModel.isOfflineMode.collectAsState() // 🌟 NEU
// Dialog anpassen
if (showDeleteDialog) {
DeleteConfirmationDialog(
noteCount = 1,
isOfflineMode = isOfflineMode, // 🌟 v1.6.0: NEU
onDismiss = { showDeleteDialog = false },
onDeleteLocal = { ... },
onDeleteEverywhere = { ... }
)
}
🔧 Detaillierte Änderungs-Matrix
| Datei | Änderungstyp | Beschreibung |
|---|---|---|
strings.xml |
String hinzufügen | delete_everywhere_offline_hint (EN) |
strings.xml (de) |
String hinzufügen | delete_everywhere_offline_hint (DE) |
DeleteConfirmationDialog.kt |
Parameter + UI | isOfflineMode Parameter, grayed-out Button + Hint |
MainScreen.kt |
Parameter übergeben | isOfflineMode = isOfflineMode an Dialog |
NoteEditorViewModel.kt |
StateFlow hinzufügen | isOfflineMode: StateFlow<Boolean> |
NoteEditorScreen.kt |
State abrufen + übergeben | collectAsState + an Dialog übergeben |
✅ Akzeptanzkriterien
-
Offline-Modus aktiv:
- "Überall löschen" Button ist grau/deaktiviert
- Subtiler Hinweis-Text erscheint unter dem Button
- Button ist nicht anklickbar
- "Nur lokal löschen" funktioniert normal
- Kein Netzwerkverkehr bei Lösch-Aktionen
-
Online-Modus (Offline-Modus deaktiviert):
- Beide Buttons funktionieren normal
- Kein Hinweis-Text
- Verhalten unverändert
-
Konsistenz:
- UI-Pattern konsistent mit anderen v1.6.0 Offline-Einschränkungen
- Farbgebung nutzt
MaterialTheme.colorScheme.tertiaryfür Hints
-
Stellen:
- MainScreen (Batch-Löschung mit Multi-Select)
- NoteEditorScreen (Einzelne Notiz löschen)
📊 Geschätzter Aufwand
| Phase | Aufwand | Dateien |
|---|---|---|
| Phase 1: Dialog | ~30 Min | 3 Dateien |
| Phase 2: MainScreen | ~10 Min | 1 Datei |
| Phase 3: NoteEditor | ~20 Min | 2 Dateien |
| Gesamt | ~1 Stunde | 6 Dateien |
🧪 Test-Szenarien
Szenario 1: Einzelne Notiz löschen (Editor)
- Offline-Modus aktivieren
- Bestehende Notiz öffnen
- Löschen-Button klicken
- Erwartung: "Überall löschen" grau, Hint sichtbar
- "Nur lokal löschen" funktioniert
Szenario 2: Batch-Löschung (Main Screen)
- Offline-Modus aktivieren
- Mehrere Notizen auswählen (Long-Press + Tap)
- Papierkorb-Icon klicken
- Erwartung: "Überall löschen" grau, Hint sichtbar
- "Nur lokal löschen" funktioniert
Szenario 3: Wechsel zwischen Modi
- Im Offline-Modus Dialog öffnen → Button deaktiviert
- Abbrechen, in Einstellungen Offline-Modus deaktivieren
- Zurück, Dialog erneut öffnen → Button aktiviert
📌 Implementierungs-Reihenfolge
1. ┌─────────────────────────────────┐
│ String-Ressourcen hinzufügen │ ← Start hier
└───────────────┬─────────────────┘
▼
2. ┌─────────────────────────────────┐
│ DeleteConfirmationDialog.kt │ ← Kern-Änderung
└───────────────┬─────────────────┘
▼
3. ┌─────────────────────────────────┐
│ MainScreen.kt │ ← Einfach (State vorhanden)
└───────────────┬─────────────────┘
▼
4. ┌─────────────────────────────────┐
│ NoteEditorViewModel.kt │ ← StateFlow hinzufügen
└───────────────┬─────────────────┘
▼
5. ┌─────────────────────────────────┐
│ NoteEditorScreen.kt │ ← State abrufen + übergeben
└─────────────────────────────────┘
🔗 Abhängigkeiten
- Keine externen Abhängigkeiten
- Nutzt bestehende v1.6.0 Offline-Mode Infrastruktur
- Konsistent mit Design-Pattern aus
MarkdownSettingsScreen.ktundBackupSettingsScreen.kt
📝 Hinweise
- Der
isOfflineModeState inMainViewModelwird bereits reaktiv viaStateFlowverwaltet refreshOfflineModeState()wird inComposeMainActivity.onResume()aufgerufen- Das gleiche Pattern wird in
NoteEditorViewModelrepliziert (einmalige Initialisierung ausreichend, da Editor-Lebenszyklus kurz ist)