# 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](../android/app/src/main/java/dev/dettmer/simplenotes/ui/main/components/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) und `NoteEditorScreen.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](../android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/NoteEditorViewModel.kt) **Aktueller Zustand:** - PrΓΌft Offline-Status nur fΓΌr `triggerOnSaveSync()` inline via `prefs.getString(KEY_SERVER_URL, null)` - Kein reaktiver State fΓΌr Offline-Modus **Γ„nderungen:** - Neuer StateFlow: `isOfflineMode: StateFlow` --- ## πŸ“ 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 ```kotlin // DeleteConfirmationDialog.kt @Composable fun DeleteConfirmationDialog( noteCount: Int = 1, isOfflineMode: Boolean = false, // 🌟 v1.6.0: NEU onDismiss: () -> Unit, onDeleteLocal: () -> Unit, onDeleteEverywhere: () -> Unit ) ``` **Neue Strings:** ```xml Not available in offline mode Nicht verfΓΌgbar im Offline-Modus ``` **Schritt 1.2:** UI-Anpassung fΓΌr deaktivierten Button ```kotlin // 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):** ```kotlin DeleteConfirmationDialog( noteCount = selectedNotes.size, onDismiss = { showBatchDeleteDialog = false }, onDeleteLocal = { ... }, onDeleteEverywhere = { ... } ) ``` **Γ„nderung:** ```kotlin 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 ```kotlin // NoteEditorViewModel.kt // 🌟 v1.6.0: Offline Mode State private val _isOfflineMode = MutableStateFlow( prefs.getBoolean(Constants.KEY_OFFLINE_MODE, true) ) val isOfflineMode: StateFlow = _isOfflineMode.asStateFlow() ``` **Schritt 3.2:** NoteEditorScreen anpassen ```kotlin // 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` | | `NoteEditorScreen.kt` | State abrufen + ΓΌbergeben | collectAsState + an Dialog ΓΌbergeben | --- ## βœ… Akzeptanzkriterien 1. **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 2. **Online-Modus (Offline-Modus deaktiviert):** - [ ] Beide Buttons funktionieren normal - [ ] Kein Hinweis-Text - [ ] Verhalten unverΓ€ndert 3. **Konsistenz:** - [ ] UI-Pattern konsistent mit anderen v1.6.0 Offline-EinschrΓ€nkungen - [ ] Farbgebung nutzt `MaterialTheme.colorScheme.tertiary` fΓΌr Hints 4. **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) 1. Offline-Modus aktivieren 2. Bestehende Notiz ΓΆffnen 3. LΓΆschen-Button klicken 4. **Erwartung:** "Überall lΓΆschen" grau, Hint sichtbar 5. "Nur lokal lΓΆschen" funktioniert ### Szenario 2: Batch-LΓΆschung (Main Screen) 1. Offline-Modus aktivieren 2. Mehrere Notizen auswΓ€hlen (Long-Press + Tap) 3. Papierkorb-Icon klicken 4. **Erwartung:** "Überall lΓΆschen" grau, Hint sichtbar 5. "Nur lokal lΓΆschen" funktioniert ### Szenario 3: Wechsel zwischen Modi 1. Im Offline-Modus Dialog ΓΆffnen β†’ Button deaktiviert 2. Abbrechen, in Einstellungen Offline-Modus deaktivieren 3. 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.kt` und `BackupSettingsScreen.kt` --- ## πŸ“ Hinweise - Der `isOfflineMode` State in `MainViewModel` wird bereits reaktiv via `StateFlow` verwaltet - `refreshOfflineModeState()` wird in `ComposeMainActivity.onResume()` aufgerufen - Das gleiche Pattern wird in `NoteEditorViewModel` repliziert (einmalige Initialisierung ausreichend, da Editor-Lebenszyklus kurz ist)