mirror of
https://github.com/block/buzz.git
synced 2026-10-29 05:21:31 +00:00
fix(mobile): fail a fast DM open that leaves the DM hidden
Reopening a hidden DM unhides it, but the relay accepts the open even when publishing that visibility snapshot fails. The fast open path now reads the caller's visibility snapshot alongside the DM's metadata and roster and fails if the DM is still hidden, so the sheet shows an error and Done can retry instead of opening a DM the sidebar keeps hidden. Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
@@ -255,21 +255,27 @@ Future<Set<String>> _fetchHiddenDmIds(
|
||||
String myPk,
|
||||
) async {
|
||||
try {
|
||||
final events = await session.fetchHistory(NostrFilters.hiddenDms(myPk));
|
||||
if (events.isEmpty) return const {};
|
||||
NostrEvent latest = events.first;
|
||||
for (final event in events.skip(1)) {
|
||||
if (event.createdAt > latest.createdAt) latest = event;
|
||||
}
|
||||
return {
|
||||
for (final tag in latest.tags)
|
||||
if (tag.length >= 2 && tag[0] == 'h') tag[1],
|
||||
};
|
||||
return hiddenDmIdsFromEvents(
|
||||
await session.fetchHistory(NostrFilters.hiddenDms(myPk)),
|
||||
);
|
||||
} catch (_) {
|
||||
return const {};
|
||||
}
|
||||
}
|
||||
|
||||
/// The DM ids hidden by the newest of the caller's NIP-DV snapshots.
|
||||
Set<String> hiddenDmIdsFromEvents(List<NostrEvent> events) {
|
||||
if (events.isEmpty) return const {};
|
||||
NostrEvent latest = events.first;
|
||||
for (final event in events.skip(1)) {
|
||||
if (event.createdAt > latest.createdAt) latest = event;
|
||||
}
|
||||
return {
|
||||
for (final tag in latest.tags)
|
||||
if (tag.length >= 2 && tag[0] == 'h') tag[1],
|
||||
};
|
||||
}
|
||||
|
||||
Future<List<NostrEvent>> _fetchHuddleStarts(
|
||||
RelaySessionNotifier session,
|
||||
List<String> parentChannelIds,
|
||||
|
||||
@@ -145,7 +145,20 @@ class ChannelActions {
|
||||
}
|
||||
if (waitForChannelList) return _refreshChannelsAndRead(channelId);
|
||||
unawaited(_ref.read(channelsProvider.notifier).refresh());
|
||||
return await findJoinedChannel(channelId) ??
|
||||
// Reopening a hidden DM unhides it, but the relay still accepts the open
|
||||
// when that visibility snapshot fails. Check it here so the failure shows
|
||||
// and Done can retry, rather than leaving the DM hidden from the sidebar.
|
||||
final pk = _currentPubkey;
|
||||
final hiddenRequest = pk == null
|
||||
? Future.value(const <NostrEvent>[])
|
||||
: _session.fetchHistory(NostrFilters.hiddenDms(pk));
|
||||
final channelRequest = findJoinedChannel(channelId);
|
||||
await Future.wait(eagerError: true, [hiddenRequest, channelRequest]);
|
||||
_ensureCommunityValid();
|
||||
if (hiddenDmIdsFromEvents(await hiddenRequest).contains(channelId)) {
|
||||
throw StateError('DM was opened but is still hidden');
|
||||
}
|
||||
return await channelRequest ??
|
||||
(throw StateError('DM was opened but is not visible yet'));
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,16 @@ void main() {
|
||||
throwsStateError,
|
||||
);
|
||||
|
||||
// A reopen whose unhide snapshot the relay failed to publish fails
|
||||
// visibly instead of opening a DM the sidebar still hides.
|
||||
session.member = true;
|
||||
session.hidden = {_dmId};
|
||||
await expectLater(
|
||||
actions.openDm(pubkeys: [_peer], waitForChannelList: false),
|
||||
throwsStateError,
|
||||
);
|
||||
session.hidden = {};
|
||||
|
||||
// Hidden-DM resurfacing keeps waiting for the list that clears its
|
||||
// hidden state, so it cannot outrun its own retry coalescing.
|
||||
session.member = true;
|
||||
@@ -95,6 +105,7 @@ class _DmSession extends RelaySessionNotifier {
|
||||
_DmSession(this.self);
|
||||
final String self;
|
||||
bool member = true;
|
||||
Set<String> hidden = {};
|
||||
void Function()? onRead;
|
||||
final published = <NostrEvent>[];
|
||||
Completer<List<NostrEvent>> stalledList = Completer();
|
||||
@@ -140,6 +151,25 @@ class _DmSession extends RelaySessionNotifier {
|
||||
NostrFilter filter, {
|
||||
Duration timeout = const Duration(seconds: 8),
|
||||
}) async {
|
||||
if (filter.kinds.single == EventKind.dmVisibility) {
|
||||
expect(filter.tags, {
|
||||
'#p': [self],
|
||||
});
|
||||
return [
|
||||
NostrEvent(
|
||||
id: 'visibility',
|
||||
pubkey: 'relay',
|
||||
createdAt: 1,
|
||||
kind: EventKind.dmVisibility,
|
||||
tags: [
|
||||
['p', self],
|
||||
for (final id in hidden) ['h', id],
|
||||
],
|
||||
content: '',
|
||||
sig: '',
|
||||
),
|
||||
];
|
||||
}
|
||||
expect(filter.tags, {
|
||||
'#d': [_dmId],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user