v2.53.1 — LOW-1: Willkommen-zurück-Dialog nur bei >24h Offline (24h-Check)
This commit is contained in:
@@ -267,6 +267,10 @@ class _CloudScreenState extends State<CloudScreen>
|
|||||||
/// „Willkommen zurück! Seit letztem Sync: +8 neue · −3 gelöscht · ⭐5“
|
/// „Willkommen zurück! Seit letztem Sync: +8 neue · −3 gelöscht · ⭐5“
|
||||||
void _zeigeKonfliktReport(SyncBericht bericht) {
|
void _zeigeKonfliktReport(SyncBericht bericht) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
// LOW-1: „Willkommen zurück!“ nur zeigen, wenn der letzte Sync mehr als
|
||||||
|
// 24h zurückliegt (oder noch nie gesynct) — sonst Dialog-Flut bei jedem
|
||||||
|
// Sync mit Änderungen. Basis ist der letzterSync-Stand VOR diesem Lauf.
|
||||||
|
if (!bericht.nachLangerPause()) return;
|
||||||
final titel = 'Willkommen zurück!';
|
final titel = 'Willkommen zurück!';
|
||||||
final zeilen = <String>[
|
final zeilen = <String>[
|
||||||
'Seit deinem letzten Sync:',
|
'Seit deinem letzten Sync:',
|
||||||
|
|||||||
@@ -35,11 +35,17 @@ class SyncBericht {
|
|||||||
/// Menschlich lesbare Änderungszeilen („+ Neuer Song: …“, „− Gelöscht: …“).
|
/// Menschlich lesbare Änderungszeilen („+ Neuer Song: …“, „− Gelöscht: …“).
|
||||||
final List<String> details;
|
final List<String> details;
|
||||||
|
|
||||||
|
/// Letzter Sync-ZEITPUNKT VOR diesem Lauf (Snapshot vor listSongs,
|
||||||
|
/// MED-1) — Basis für den 24h-Check des „Willkommen zurück!“-Dialogs
|
||||||
|
/// (LOW-1). null = noch nie gesynct (Erst-Sync) → Dialog zeigen.
|
||||||
|
final DateTime? letzterSyncVorher;
|
||||||
|
|
||||||
const SyncBericht({
|
const SyncBericht({
|
||||||
required this.neueSongs,
|
required this.neueSongs,
|
||||||
required this.geloeschteSongs,
|
required this.geloeschteSongs,
|
||||||
required this.favoritenGeaendert,
|
required this.favoritenGeaendert,
|
||||||
this.details = const [],
|
this.details = const [],
|
||||||
|
this.letzterSyncVorher,
|
||||||
});
|
});
|
||||||
|
|
||||||
bool get hatAenderungen =>
|
bool get hatAenderungen =>
|
||||||
@@ -54,6 +60,16 @@ class SyncBericht {
|
|||||||
];
|
];
|
||||||
return teile.join(' · ');
|
return teile.join(' · ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// LOW-1: „Willkommen zurück!“-Dialog nur nach längerer Pause zeigen —
|
||||||
|
/// letzter Sync mehr als 24h zurück (oder noch nie gesynct). [jetzt]
|
||||||
|
/// injizierbar für Tests.
|
||||||
|
bool nachLangerPause({DateTime? jetzt}) {
|
||||||
|
final vorher = letzterSyncVorher;
|
||||||
|
if (vorher == null) return true;
|
||||||
|
final j = jetzt ?? DateTime.now();
|
||||||
|
return j.difference(vorher) > const Duration(hours: 24);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Zentraler Cloud-Sync (Sprint D, v2.52.2).
|
/// Zentraler Cloud-Sync (Sprint D, v2.52.2).
|
||||||
@@ -487,6 +503,10 @@ class SyncService {
|
|||||||
geloeschteSongs: geloeschteSongs,
|
geloeschteSongs: geloeschteSongs,
|
||||||
favoritenGeaendert: favoritenGeaendert,
|
favoritenGeaendert: favoritenGeaendert,
|
||||||
details: [...neueDetails, ...loeschDetails, ...favoritenDetails],
|
details: [...neueDetails, ...loeschDetails, ...favoritenDetails],
|
||||||
|
// LOW-1: 24h-Check des „Willkommen zurück!“-Dialogs gegen den
|
||||||
|
// letzterSync-Stand VOR diesem Sync (MED-1-Snapshot) — nicht gegen
|
||||||
|
// den frisch persistierten, sonst wäre die Pause immer 0.
|
||||||
|
letzterSyncVorher: letzterSync,
|
||||||
);
|
);
|
||||||
if (bericht.hatAenderungen) {
|
if (bericht.hatAenderungen) {
|
||||||
onBericht?.call(bericht);
|
onBericht?.call(bericht);
|
||||||
|
|||||||
@@ -103,6 +103,32 @@ void main() {
|
|||||||
expect(bericht.details, hasLength(2));
|
expect(bericht.details, hasLength(2));
|
||||||
expect(bericht.details.first, '+ Neuer Song: A');
|
expect(bericht.details.first, '+ Neuer Song: A');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('nachLangerPause: letzterSyncVorher null (Erst-Sync) → true', () {
|
||||||
|
const bericht = SyncBericht(
|
||||||
|
neueSongs: 1, geloeschteSongs: 0, favoritenGeaendert: 0);
|
||||||
|
expect(bericht.nachLangerPause(jetzt: DateTime(2026, 8, 5, 12)), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('nachLangerPause: < 24h seit letztem Sync → false', () {
|
||||||
|
final bericht = SyncBericht(
|
||||||
|
neueSongs: 1,
|
||||||
|
geloeschteSongs: 0,
|
||||||
|
favoritenGeaendert: 0,
|
||||||
|
letzterSyncVorher: DateTime(2026, 8, 5, 11, 0),
|
||||||
|
);
|
||||||
|
expect(bericht.nachLangerPause(jetzt: DateTime(2026, 8, 5, 12)), isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('nachLangerPause: > 24h seit letztem Sync → true', () {
|
||||||
|
final bericht = SyncBericht(
|
||||||
|
neueSongs: 1,
|
||||||
|
geloeschteSongs: 0,
|
||||||
|
favoritenGeaendert: 0,
|
||||||
|
letzterSyncVorher: DateTime(2026, 8, 3, 10, 0),
|
||||||
|
);
|
||||||
|
expect(bericht.nachLangerPause(jetzt: DateTime(2026, 8, 5, 12)), isTrue);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('songAusServerMap (MED-2: Server-Listeneintrag → lokaler Song)', () {
|
group('songAusServerMap (MED-2: Server-Listeneintrag → lokaler Song)', () {
|
||||||
|
|||||||
Reference in New Issue
Block a user