chore: update screenshots for fdroid metadata in both de-DE and en-US
@@ -1,315 +0,0 @@
|
|||||||
# 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<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
|
|
||||||
|
|
||||||
```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
|
|
||||||
<!-- 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
|
|
||||||
|
|
||||||
```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<Boolean> = _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<Boolean>` |
|
|
||||||
| `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)
|
|
||||||
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 100 KiB |