Meldingen-hardening: verwerp-optie bij sluiten, classificatie-gate, presentatietimer #7

Merged
brenno merged 3 commits from feature/meldingen-hardening into main 2026-06-13 05:54:43 +00:00
2 changed files with 47 additions and 16 deletions
Showing only changes of commit 483264b652 - Show all commits

View file

@ -783,6 +783,7 @@ const _dutchSourceStrings = {
'Verwijderen': 'Delete',
'Herstellen': 'Restore',
'Opslaan en sluiten': 'Save and close',
'Niet opslaan': "Don't save",
'Niet-opgeslagen werk herstellen?': 'Restore unsaved work?',
'Niet-opgeslagen wijzigingen': 'Unsaved changes',
'Er is een presentatie met niet-opgeslagen wijzigingen gevonden van een vorige sessie:':
@ -1161,6 +1162,7 @@ const _dutchSourceStrings = {
'Verwijderen': 'Elimina',
'Herstellen': 'Ripristina',
'Opslaan en sluiten': 'Salva e chiudi',
'Niet opslaan': 'Non salvare',
'Importeren via URL': 'Importa da URL',
'Ophalen': 'Recupera',
'Titelpagina': 'Slide titolo',
@ -1372,6 +1374,7 @@ const _dutchSourceStrings = {
'Verwijderen': 'Löschen',
'Herstellen': 'Wiederherstellen',
'Opslaan en sluiten': 'Speichern und schließen',
'Niet opslaan': 'Nicht speichern',
'Importeren via URL': 'Von URL importieren',
'Ophalen': 'Abrufen',
'Titelpagina': 'Titelfolie',
@ -1584,6 +1587,7 @@ const _dutchSourceStrings = {
'Verwijderen': 'Supprimer',
'Herstellen': 'Restaurer',
'Opslaan en sluiten': 'Enregistrer et fermer',
'Niet opslaan': 'Ne pas enregistrer',
'Importeren via URL': 'Importer depuis une URL',
'Ophalen': 'Récupérer',
'Titelpagina': 'Diapositive de titre',
@ -1795,6 +1799,7 @@ const _dutchSourceStrings = {
'Verwijderen': 'Eliminar',
'Herstellen': 'Restaurar',
'Opslaan en sluiten': 'Guardar y cerrar',
'Niet opslaan': 'No guardar',
'Importeren via URL': 'Importar desde URL',
'Ophalen': 'Obtener',
'Titelpagina': 'Diapositiva de título',
@ -2007,6 +2012,7 @@ const _dutchSourceStrings = {
'Verwijderen': 'Fuortsmite',
'Herstellen': 'Weromsette',
'Opslaan en sluiten': 'Bewarje en slute',
'Niet opslaan': 'Net bewarje',
'Importeren via URL': 'Ymportearje fan URL',
'Ophalen': 'Ophelje',
'Titelpagina': 'Titelslide',
@ -2219,6 +2225,7 @@ const _dutchSourceStrings = {
'Verwijderen': 'Kita',
'Herstellen': 'Restorá',
'Opslaan en sluiten': 'Warda i sera',
'Niet opslaan': 'No warda',
'Importeren via URL': 'Importá for di URL',
'Ophalen': 'Tuma',
'Titelpagina': 'Slide di título',

View file

@ -39,6 +39,9 @@ part 'shell/welcome_screen.dart';
part 'shell/status_bar.dart';
part 'shell/shell_overlays.dart';
/// Keuze uit de "niet-opgeslagen wijzigingen"-dialoog bij het sluiten.
enum _CloseChoice { cancel, discard, save }
class AppShell extends ConsumerStatefulWidget {
const AppShell({super.key});
@ -128,14 +131,21 @@ class _AppShellState extends ConsumerState<AppShell> with WindowListener {
@override
void onWindowClose() async {
if (ref.read(tabsProvider).anyDirty) {
final shouldSave = await _confirmSaveBeforeClose(
final choice = await _confirmSaveBeforeClose(
context.l10n.d(
'Er zijn presentaties met niet-opgeslagen wijzigingen. Sla ze op voordat de app sluit.',
),
);
if (!shouldSave) return;
switch (choice) {
case _CloseChoice.cancel:
return;
case _CloseChoice.discard:
// Wijzigingen verwerpen: herstelbestanden weg, niets opslaan.
await _destroy();
case _CloseChoice.save:
final saved = await _saveAllDirtyTabs();
if (saved) await _destroy();
}
} else {
await _destroy();
}
@ -147,9 +157,9 @@ class _AppShellState extends ConsumerState<AppShell> with WindowListener {
await windowManager.destroy();
}
Future<bool> _confirmSaveBeforeClose(String message) async {
if (!mounted) return false;
return await showDialog<bool>(
Future<_CloseChoice> _confirmSaveBeforeClose(String message) async {
if (!mounted) return _CloseChoice.cancel;
return await showDialog<_CloseChoice>(
context: context,
barrierDismissible: false,
builder: (ctx) {
@ -159,18 +169,25 @@ class _AppShellState extends ConsumerState<AppShell> with WindowListener {
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
onPressed: () => Navigator.pop(ctx, _CloseChoice.cancel),
child: Text(l10n.t('cancel')),
),
TextButton(
onPressed: () => Navigator.pop(ctx, _CloseChoice.discard),
style: TextButton.styleFrom(
foregroundColor: Theme.of(ctx).colorScheme.error,
),
child: Text(l10n.d('Niet opslaan')),
),
ElevatedButton(
onPressed: () => Navigator.pop(ctx, true),
onPressed: () => Navigator.pop(ctx, _CloseChoice.save),
child: Text(l10n.d('Opslaan en sluiten')),
),
],
);
},
) ??
false;
_CloseChoice.cancel;
}
Future<bool> _saveAllDirtyTabs() async {
@ -186,17 +203,24 @@ class _AppShellState extends ConsumerState<AppShell> with WindowListener {
Future<void> _onCloseTab(int index) async {
final tab = ref.read(tabsProvider).tabs[index];
if (tab.isDirty) {
final shouldSave = await _confirmSaveBeforeClose(
final choice = await _confirmSaveBeforeClose(
context.l10n.d(
'Deze presentatie heeft niet-opgeslagen wijzigingen. Sla de presentatie op voordat het tabblad sluit.',
),
);
if (!shouldSave) return;
switch (choice) {
case _CloseChoice.cancel:
return;
case _CloseChoice.discard:
// Wijzigingen verwerpen: closeTab() ruimt ook het herstelbestand op.
break;
case _CloseChoice.save:
final saved = await tab.deckNotifier.save(
initialDirectory: ref.read(settingsProvider).homeDirectory,
);
if (!saved) return;
}
}
ref.read(tabsProvider.notifier).closeTab(index);
}