diff --git a/lib/main.dart b/lib/main.dart index 696311e..27eeae6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -30,6 +30,7 @@ import 'services/sync_service.dart'; import 'services/yt_download_service.dart'; import 'services/yt_search_service.dart'; import 'settings/app_settings.dart'; +import 'shared/sync_bericht_dialog.dart'; import 'shared/theme.dart'; late final MeloAudioHandler _handler; @@ -177,7 +178,7 @@ class _HomeShellState extends State with WidgetsBindingObserver { WidgetsBinding.instance.addObserver(this); // Beim Start einmal mit dem Server abgleichen — neue Titel von anderen // Geräten sind dann sofort da. - context.read().automatisch(); + unawaited(_gleicheAbUndZeigeBericht()); unawaited(context.read().ensureStartupPermissions()); unawaited(_pruefeGeteilteAdresse()); } @@ -205,13 +206,29 @@ class _HomeShellState extends State with WidgetsBindingObserver { // auf dem Gerät wie am Server. if (state == AppLifecycleState.resumed) { if (Platform.isAndroid) _autoScan.pruefe(); - context.read().automatisch(); + unawaited(_gleicheAbUndZeigeBericht()); // Ein zweites Teilen startet die App nicht neu (singleTop), sondern // bringt sie nur nach vorn — deshalb auch hier nachsehen. unawaited(_pruefeGeteilteAdresse()); } } + /// Gleicht ab und zeigt danach höchstens einmal den „Was ist neu"-Bericht. + Future _gleicheAbUndZeigeBericht() async { + final sync = context.read(); + await sync.automatisch(); + if (!mounted) return; + final bericht = sync.bericht; + if (bericht == null) return; + // Zuerst quittieren: ein zweites Zurückkehren in die App soll denselben + // Bericht nicht erneut zeigen. + sync.berichtGesehen(); + await showDialog( + context: context, + builder: (_) => SyncBerichtDialog(bericht: bericht), + ); + } + @override Widget build(BuildContext context) { final tabs = [ diff --git a/lib/shared/sync_bericht_dialog.dart b/lib/shared/sync_bericht_dialog.dart new file mode 100644 index 0000000..78bcc75 --- /dev/null +++ b/lib/shared/sync_bericht_dialog.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +import '../services/sync_service.dart'; + +/// „Willkommen zurück" — was sich seit dem letzten erfolgreichen Abgleich +/// getan hat. Rein in-App, ohne Benachrichtigungs-Kanal und ohne neue +/// Abhängigkeit. +class SyncBerichtDialog extends StatelessWidget { + const SyncBerichtDialog({super.key, required this.bericht}); + + final SyncBericht bericht; + + static String textFuer(SyncBericht b) { + if (b.istLeer) return 'Nichts Neues seit dem letzten Abgleich.'; + final teile = []; + if (b.neueSongs > 0) teile.add('${b.neueSongs} neue Titel'); + if (b.geloeschte > 0) teile.add('${b.geloeschte} entfernt'); + if (b.favoriten > 0) teile.add('${b.favoriten} Favoriten geändert'); + return '${teile.join(' · ')}.'; + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Willkommen zurück!'), + content: Text(textFuer(bericht)), + actions: [ + FilledButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Alles klar'), + ), + ], + ); + } +} diff --git a/test/shared/sync_bericht_dialog_test.dart b/test/shared/sync_bericht_dialog_test.dart new file mode 100644 index 0000000..07d71de --- /dev/null +++ b/test/shared/sync_bericht_dialog_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:melo/services/sync_service.dart'; +import 'package:melo/shared/sync_bericht_dialog.dart'; + +void main() { + Future zeige(WidgetTester tester, SyncBericht bericht) async { + await tester.pumpWidget(MaterialApp( + home: Scaffold(body: SyncBerichtDialog(bericht: bericht)), + )); + await tester.pump(); + } + + testWidgets('nennt neue und entfernte Titel', (tester) async { + await zeige( + tester, + const SyncBericht(neueSongs: 3, geloeschte: 1, favoriten: 0), + ); + + expect(find.text('Willkommen zurück!'), findsOneWidget); + expect(find.textContaining('3 neue Titel'), findsOneWidget); + expect(find.textContaining('1 entfernt'), findsOneWidget); + }); + + testWidgets('ohne Änderungen sagt er das auch', (tester) async { + await zeige( + tester, + const SyncBericht(neueSongs: 0, geloeschte: 0, favoriten: 0), + ); + + expect(find.textContaining('Nichts Neues'), findsOneWidget); + }); +}