Implement NIP-09 deletions

This commit is contained in:
franzap
2026-03-31 17:55:32 -03:00
parent 1d152948de
commit 038321e3f0
2 changed files with 54 additions and 0 deletions
@@ -234,6 +234,32 @@ Future<bool> _checkForUpdatesInBackground(Set<String>? appCatalogRelays) async {
}
}
// NIP-09: fetch kind-5 deletion events since last sync.
// No author filter — the AppCatalog relay is curated and the `since`
// cursor keeps incremental runs cheap. The DB layer auto-processes
// each kind-5 event (removes referenced events, records in deletions
// table). We then clean up the kind-5 events themselves as housekeeping,
// but save the cursor regardless — a cleanup failure is non-critical.
final secureStorage = SecureStorageService();
final lastDeletionSync = await secureStorage.getDeletionsSyncedUntil();
final deletionRequests = await storage.query(
RequestFilter<EventDeletionRequest>(
since: lastDeletionSync,
limit: 99,
).toRequest(subscriptionPrefix: 'app-bg-deletions'),
source: const RemoteSource(relays: 'AppCatalog', stream: false),
);
await secureStorage.setDeletionsSyncedUntil(DateTime.now());
if (deletionRequests.isNotEmpty) {
try {
await (storage as PurplebaseStorageNotifier)
.delete(deletionRequests.map((d) => d.id).toSet());
} catch (_) {
// Orphaned kind-5 rows are harmless; referenced events are
// already removed by the DB layer during save.
}
}
// Re-query apps from local to ensure relationships are loaded
final appsWithRelations = await storage.query(
RequestFilter<App>(
+28
View File
@@ -116,6 +116,34 @@ class SecureStorageService {
}
}
// =========================================================================
// Deletion Sync Timestamp (for NIP-09 incremental checks)
// =========================================================================
static const _deletionSyncedUntilKey = 'deletion_synced_until';
/// Get the timestamp of the last successful NIP-09 deletion sync.
/// Used as `since` on the next kind-5 query so checks are incremental.
Future<DateTime?> getDeletionsSyncedUntil() async {
final value = await _storage.read(key: _deletionSyncedUntilKey);
if (int.tryParse(value ?? '') case final ms?) {
return DateTime.fromMillisecondsSinceEpoch(ms);
}
return null;
}
/// Persist the deletion sync cursor after a successful kind-5 fetch.
Future<void> setDeletionsSyncedUntil(DateTime time) async {
await _storage.write(
key: _deletionSyncedUntilKey,
value: '${time.millisecondsSinceEpoch}',
);
}
// =========================================================================
// App Catalog Relays
// =========================================================================
/// Store app catalog relay URLs.
///
/// This is the local source of truth for relay configuration,