tane/apps/app_seeds/lib/ui/auto_backup_gate.dart
vjrj 9c449964fd feat(backup): add automatic weekly encrypted backups
A silent safety net against in-app data loss or DB corruption, with zero
friction. Driven off the app lifecycle (startup + when hidden), it writes a
sealed .tanemaki copy into the app's private storage once a week, keeping the
newest 3 (rotating). Reuses the manual backup's encryption via the extracted
ExportImportService.buildSealedBackup(); never throws, so a failed copy can't
crash startup or backgrounding. Not registered on web (no file storage).

Settings shows a quiet reassurance line with the date of the last automatic
copy (localised via intl), hidden where no automatic backup exists.
2026-07-10 10:17:36 +02:00

42 lines
1.3 KiB
Dart

import 'package:flutter/widgets.dart';
import '../services/auto_backup_service.dart';
/// Drives the automatic backup off the app lifecycle: once on startup and again
/// each time the app becomes hidden (backgrounded on mobile, window hidden on
/// desktop). The [AutoBackupService] decides whether a copy is actually due, so
/// these are cheap no-ops most of the time.
///
/// When [service] is null (widget tests, or platforms without file storage such
/// as web) it wraps [child] untouched and does nothing.
class AutoBackupGate extends StatefulWidget {
const AutoBackupGate({required this.child, this.service, super.key});
final Widget child;
final AutoBackupService? service;
@override
State<AutoBackupGate> createState() => _AutoBackupGateState();
}
class _AutoBackupGateState extends State<AutoBackupGate> {
AppLifecycleListener? _listener;
@override
void initState() {
super.initState();
final service = widget.service;
if (service == null) return;
_listener = AppLifecycleListener(onHide: () => service.runIfDue());
WidgetsBinding.instance.addPostFrameCallback((_) => service.runIfDue());
}
@override
void dispose() {
_listener?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => widget.child;
}