mirror of
https://github.com/block/buzz.git
synced 2026-10-27 20:41:57 +00:00
fix(mobile): dispatch durable push opt-outs without waiting for snapshots
Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
@@ -481,7 +481,12 @@ class CommunityListNotifier extends AsyncNotifier<List<Community>> {
|
||||
await storage.save(updated);
|
||||
final updatedList = [...current]..[index] = updated;
|
||||
state = AsyncData(updatedList);
|
||||
await syncCommunitySnapshot(ref, updatedList);
|
||||
if (enabled) {
|
||||
await syncCommunitySnapshot(ref, updatedList);
|
||||
} else {
|
||||
// Presentation export must not hold up the durably journaled opt-out.
|
||||
unawaited(syncCommunitySnapshot(ref, updatedList));
|
||||
}
|
||||
shouldDeactivate =
|
||||
!enabled && pushState.pendingTombstoneGeneration != null;
|
||||
});
|
||||
@@ -548,7 +553,7 @@ class CommunityListNotifier extends AsyncNotifier<List<Community>> {
|
||||
await storage.save(updated);
|
||||
final updatedList = [...current]..[index] = updated;
|
||||
state = AsyncData(updatedList);
|
||||
await syncCommunitySnapshot(ref, updatedList);
|
||||
unawaited(syncCommunitySnapshot(ref, updatedList));
|
||||
}
|
||||
pendingCommunity = updated;
|
||||
pendingGeneration = pushState.pendingTombstoneGeneration;
|
||||
@@ -583,7 +588,7 @@ class CommunityListNotifier extends AsyncNotifier<List<Community>> {
|
||||
await storage.save(updated);
|
||||
final updatedList = [...current]..[index] = updated;
|
||||
state = AsyncData(updatedList);
|
||||
await syncCommunitySnapshot(ref, updatedList);
|
||||
unawaited(syncCommunitySnapshot(ref, updatedList));
|
||||
});
|
||||
|
||||
Future<void> renameCommunity(String id, String name) async {
|
||||
|
||||
@@ -42,7 +42,7 @@ void main() {
|
||||
|
||||
tearDown(() => container.dispose());
|
||||
|
||||
ProviderContainer createContainer() {
|
||||
ProviderContainer createContainer({CommunitySnapshotWriter? snapshotWriter}) {
|
||||
Future<void> writeSnapshot(List<Community> communities) async {
|
||||
snapshots.add(List.of(communities));
|
||||
}
|
||||
@@ -50,7 +50,9 @@ void main() {
|
||||
return ProviderContainer(
|
||||
overrides: [
|
||||
communityStorageProvider.overrideWithValue(communityStorage),
|
||||
communitySnapshotWriterProvider.overrideWithValue(writeSnapshot),
|
||||
communitySnapshotWriterProvider.overrideWithValue(
|
||||
snapshotWriter ?? writeSnapshot,
|
||||
),
|
||||
communityPushLeaseDeactivatorProvider.overrideWithValue(deactivator),
|
||||
communityPushLeaseRevocationEnqueuerProvider.overrideWithValue((
|
||||
community,
|
||||
@@ -307,6 +309,99 @@ void main() {
|
||||
expect(deactivationGenerations, [2]);
|
||||
});
|
||||
|
||||
test('opt-out dispatch does not wait for presentation export', () async {
|
||||
final exportStarted = Completer<void>();
|
||||
final finishExport = Completer<void>();
|
||||
final subscription = BuzzPushSubscription(
|
||||
filter: BuzzPushFilter(kinds: const [9], pTags: ['a' * 64]),
|
||||
notificationClass: 'default',
|
||||
);
|
||||
final community =
|
||||
Community.create(
|
||||
name: 'Test',
|
||||
relayUrl: 'https://test.example.com',
|
||||
).copyWith(
|
||||
pushNotificationsEnabled: true,
|
||||
pushSubscriptionState: BuzzPushLeaseSubscriptionState.desired(
|
||||
desired: [subscription],
|
||||
).withAccepted(subscriptions: [subscription], generation: 7),
|
||||
);
|
||||
deactivator = (community, {generation}) async {
|
||||
final stored = (await communityStorage.loadAll()).single;
|
||||
expect(stored.pushNotificationsEnabled, isFalse);
|
||||
expect(stored.pushSubscriptionState.pendingTombstoneGeneration, 8);
|
||||
deactivatedCommunityIds.add(community.id);
|
||||
deactivationGenerations.add(generation);
|
||||
};
|
||||
container = createContainer(
|
||||
snapshotWriter: (communities) async {
|
||||
if (communities.isNotEmpty &&
|
||||
!communities.single.pushNotificationsEnabled) {
|
||||
if (!exportStarted.isCompleted) exportStarted.complete();
|
||||
await finishExport.future;
|
||||
}
|
||||
},
|
||||
);
|
||||
await container.read(communityListProvider.future);
|
||||
final notifier = container.read(communityListProvider.notifier);
|
||||
await notifier.addCommunity(community);
|
||||
final disable = notifier.setPushNotificationsEnabled(community.id, false);
|
||||
await exportStarted.future;
|
||||
try {
|
||||
final stored = (await communityStorage.loadAll()).single;
|
||||
expect(stored.pushNotificationsEnabled, isFalse);
|
||||
// Presentation I/O is still blocked, but the durable opt-out must
|
||||
// already be allowed to reach its relay-publication seam.
|
||||
await container.pump();
|
||||
expect(deactivatedCommunityIds, [community.id]);
|
||||
expect(deactivationGenerations, [8]);
|
||||
await disable.timeout(const Duration(seconds: 1));
|
||||
final accepted = (await communityStorage.loadAll()).single;
|
||||
expect(
|
||||
accepted.pushSubscriptionState.pendingTombstoneGeneration,
|
||||
isNull,
|
||||
);
|
||||
} finally {
|
||||
finishExport.complete();
|
||||
await disable;
|
||||
}
|
||||
});
|
||||
|
||||
test('opt-in still waits for presentation export', () async {
|
||||
final exportStarted = Completer<void>();
|
||||
final finishExport = Completer<void>();
|
||||
final community = Community.create(
|
||||
name: 'Test',
|
||||
relayUrl: 'https://test.example.com',
|
||||
).copyWith(pushNotificationsEnabled: false);
|
||||
container = createContainer(
|
||||
snapshotWriter: (communities) async {
|
||||
if (communities.isNotEmpty &&
|
||||
communities.single.pushNotificationsEnabled) {
|
||||
exportStarted.complete();
|
||||
await finishExport.future;
|
||||
}
|
||||
},
|
||||
);
|
||||
await container.read(communityListProvider.future);
|
||||
final notifier = container.read(communityListProvider.notifier);
|
||||
await notifier.addCommunity(community);
|
||||
var completed = false;
|
||||
final enable = notifier
|
||||
.setPushNotificationsEnabled(community.id, true)
|
||||
.then((_) => completed = true);
|
||||
await exportStarted.future;
|
||||
await container.pump();
|
||||
try {
|
||||
expect(completed, isFalse);
|
||||
expect(deactivatedCommunityIds, isEmpty);
|
||||
} finally {
|
||||
finishExport.complete();
|
||||
await enable;
|
||||
}
|
||||
expect(completed, isTrue);
|
||||
});
|
||||
|
||||
test('opt-out persists first and publishes a higher tombstone', () async {
|
||||
container = createContainer();
|
||||
await container.read(communityListProvider.future);
|
||||
|
||||
Reference in New Issue
Block a user