Skip to content

Commit c2eaff6

Browse files
committed
feat: add event-driven auto backup triggers after wallet changes
Fire a debounced auto-backup when a transaction note is saved or an address book contact is created, edited, or deleted. closes #306
1 parent 51db6c7 commit c2eaff6

11 files changed

Lines changed: 85 additions & 3 deletions

File tree

lib/main.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
498498
case BackupFrequencyType.afterClosingAWallet:
499499
// ignore this case here
500500
break;
501+
case BackupFrequencyType.afterChanges:
502+
// handled by note and address book edit events
503+
break;
501504
}
502505
}
503506

lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
7373
BackupFrequencyType.everyTenMinutes,
7474
BackupFrequencyType.everyAppStart,
7575
BackupFrequencyType.afterClosingAWallet,
76+
BackupFrequencyType.afterChanges,
7677
];
7778

7879
String passwordFeedback =
@@ -572,6 +573,9 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
572573
case BackupFrequencyType.afterClosingAWallet:
573574
message = "After closing a cryptocurrency wallet";
574575
break;
576+
case BackupFrequencyType.afterChanges:
577+
message = "After editing a note or contact";
578+
break;
575579
}
576580

577581
return DropdownMenuItem(

lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/backup_frequency_type_select_sheet.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class BackupFrequencyTypeSelectSheet extends ConsumerWidget {
3030
return "Every app start";
3131
case BackupFrequencyType.afterClosingAWallet:
3232
return "After closing a cryptocurrency wallet";
33+
case BackupFrequencyType.afterChanges:
34+
return "After editing a note or contact";
3335
}
3436
}
3537

lib/pages/wallet_view/transaction_views/edit_note_view.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
1313

1414
import '../../../models/isar/models/transaction_note.dart';
1515
import '../../../providers/providers.dart';
16+
import '../../../services/auto_swb_service.dart';
1617
import '../../../themes/stack_colors.dart';
1718
import '../../../utilities/constants.dart';
1819
import '../../../utilities/text_styles.dart';
@@ -198,6 +199,10 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
198199
),
199200
);
200201

202+
AutoSWBService.requestBackupAfterChange(
203+
ref.read(prefsChangeNotifierProvider),
204+
);
205+
201206
if (mounted) {
202207
Navigator.of(context).pop();
203208
}
@@ -217,6 +222,11 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
217222
value: _noteController.text,
218223
),
219224
);
225+
226+
AutoSWBService.requestBackupAfterChange(
227+
ref.read(prefsChangeNotifierProvider),
228+
);
229+
220230
if (mounted) {
221231
Navigator.of(context).pop();
222232
}

lib/pages_desktop_specific/settings/settings_menu/backup_and_restore/create_auto_backup.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
8686
BackupFrequencyType.everyTenMinutes,
8787
BackupFrequencyType.everyAppStart,
8888
BackupFrequencyType.afterClosingAWallet,
89+
BackupFrequencyType.afterChanges,
8990
];
9091

9192
Future<void> _enableAutoBackup() async {
@@ -616,6 +617,9 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
616617
case BackupFrequencyType.afterClosingAWallet:
617618
message = "After closing a cryptocurrency wallet";
618619
break;
620+
case BackupFrequencyType.afterChanges:
621+
message = "After editing a note or contact";
622+
break;
619623
}
620624

621625
return DropdownMenuItem(

lib/providers/global/auto_swb_service_provider.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import 'secure_store_provider.dart';
1313
import '../../services/auto_swb_service.dart';
1414

1515
final autoSWBServiceProvider = ChangeNotifierProvider<AutoSWBService>(
16-
(ref) => AutoSWBService(
17-
secureStorageInterface: ref.read(secureStoreProvider),
18-
),
16+
(ref) {
17+
final service = AutoSWBService(
18+
secureStorageInterface: ref.read(secureStoreProvider),
19+
);
20+
AutoSWBService.instance = service;
21+
return service;
22+
},
1923
);

lib/services/address_book_service.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import 'package:flutter/cupertino.dart';
1212
import 'package:flutter/foundation.dart';
1313
import '../db/isar/main_db.dart';
1414
import '../models/isar/models/contact_entry.dart';
15+
import '../utilities/prefs.dart';
16+
import 'auto_swb_service.dart';
1517

1618
class AddressBookService extends ChangeNotifier {
1719
ContactEntry getContactById(String id) {
@@ -66,6 +68,7 @@ class AddressBookService extends ChangeNotifier {
6668
} else {
6769
await MainDB.instance.putContactEntry(contactEntry: contact);
6870
notifyListeners();
71+
AutoSWBService.requestBackupAfterChange(Prefs.instance);
6972
return true;
7073
}
7174
}
@@ -75,12 +78,14 @@ class AddressBookService extends ChangeNotifier {
7578
// over write the contact with edited version
7679
await MainDB.instance.putContactEntry(contactEntry: editedContact);
7780
notifyListeners();
81+
AutoSWBService.requestBackupAfterChange(Prefs.instance);
7882
return true;
7983
}
8084

8185
/// Remove address book contact entry from db if it exists
8286
Future<void> removeContact(String id) async {
8387
await MainDB.instance.deleteContactEntry(id: id);
8488
notifyListeners();
89+
AutoSWBService.requestBackupAfterChange(Prefs.instance);
8590
}
8691
}

lib/services/auto_swb_service.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:flutter/foundation.dart';
1616
import 'package:tuple/tuple.dart';
1717

1818
import '../pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
19+
import '../utilities/enums/backup_frequency_type.dart';
1920
import '../utilities/flutter_secure_storage_interface.dart';
2021
import '../utilities/fs.dart';
2122
import '../utilities/logger.dart';
@@ -24,7 +25,12 @@ import '../utilities/prefs.dart';
2425
enum AutoSWBStatus { idle, backingUp, error }
2526

2627
class AutoSWBService extends ChangeNotifier {
28+
/// Static instance reference set by the provider layer so that non-widget
29+
/// code (e.g. `Wallet`) can request a backup without Riverpod access.
30+
static AutoSWBService? instance;
31+
2732
Timer? _timer;
33+
Timer? _debounceTimer;
2834

2935
AutoSWBStatus _status = AutoSWBStatus.idle;
3036
AutoSWBStatus get status => _status;
@@ -36,6 +42,36 @@ class AutoSWBService extends ChangeNotifier {
3642

3743
AutoSWBService({required this.secureStorageInterface});
3844

45+
/// Convenience method to request a backup after an editable change such as
46+
/// a transaction note or address book contact edit. Only triggers if
47+
/// auto-backup is enabled and the frequency is set to
48+
/// [BackupFrequencyType.afterChanges].
49+
static void requestBackupAfterChange(Prefs prefs) {
50+
if (instance == null) return;
51+
if (!prefs.isAutoBackupEnabled) return;
52+
if (prefs.backupFrequencyType != BackupFrequencyType.afterChanges) return;
53+
54+
Logging.instance.d(
55+
"AutoSWBService.requestBackupAfterChange() triggered",
56+
);
57+
instance!.requestBackup();
58+
}
59+
60+
/// Request a debounced backup. Multiple calls within [debounceDuration]
61+
/// will be collapsed into a single backup. This prevents backup storms
62+
/// during wallet sync or rapid changes.
63+
void requestBackup({
64+
Duration debounceDuration = const Duration(seconds: 5),
65+
}) {
66+
_debounceTimer?.cancel();
67+
_debounceTimer = Timer(debounceDuration, () {
68+
Logging.instance.d(
69+
"AutoSWBService.requestBackup() debounce fired, running doBackup()",
70+
);
71+
doBackup();
72+
});
73+
}
74+
3975
/// Attempt a backup.
4076
Future<void> doBackup() async {
4177
if (_status == AutoSWBStatus.backingUp) {
@@ -199,6 +235,8 @@ class AutoSWBService extends ChangeNotifier {
199235

200236
@override
201237
void dispose() {
238+
_debounceTimer?.cancel();
239+
_debounceTimer = null;
202240
stopPeriodicBackupTimer(shouldNotifyListeners: false);
203241
super.dispose();
204242
}

lib/utilities/enums/backup_frequency_type.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ enum BackupFrequencyType {
1212
everyTenMinutes,
1313
everyAppStart,
1414
afterClosingAWallet,
15+
afterChanges,
1516
}

lib/utilities/format.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ abstract class Format {
141141
return "Every app start";
142142
case BackupFrequencyType.afterClosingAWallet:
143143
return "After closing a cryptocurrency wallet";
144+
case BackupFrequencyType.afterChanges:
145+
return "After editing a note or contact";
144146
}
145147
}
146148
}

0 commit comments

Comments
 (0)