mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(mobile): paginate channel discovery
Co-authored-by: Tom Brow <tomb@block.xyz> Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
co-authored by
Tom Brow
parent
1aa9844dc7
commit
4e7cefc4e6
@@ -160,9 +160,29 @@ class ChannelsNotifier extends AsyncNotifier<List<Channel>> {
|
||||
// Step 3: discover relay-visible open channels without fabricating
|
||||
// membership. The relay withholds private and DM metadata from this query,
|
||||
// while the client-side checks below keep that trust boundary explicit.
|
||||
final discoverableMetas = await session.fetchHistory(
|
||||
const NostrFilter(kinds: [39000], limit: 500),
|
||||
);
|
||||
// Page with the relay's composite cursor so tied timestamps cannot skip
|
||||
// channels and communities larger than one relay page remain complete.
|
||||
final discoverableMetas = <NostrEvent>[];
|
||||
{
|
||||
int? until;
|
||||
String? beforeId;
|
||||
const pageSize = 500;
|
||||
while (true) {
|
||||
final page = await session.fetchHistory(
|
||||
NostrFilter(
|
||||
kinds: const [39000],
|
||||
limit: pageSize,
|
||||
until: until,
|
||||
extensions: {'before_id': ?beforeId},
|
||||
),
|
||||
);
|
||||
discoverableMetas.addAll(page);
|
||||
if (page.length < pageSize) break;
|
||||
final last = page.last;
|
||||
until = last.createdAt;
|
||||
beforeId = last.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge and dedupe by `d` tag (channel id). Kind:39000 is
|
||||
// parameterized-replaceable, but stale revisions from before the relay's
|
||||
|
||||
@@ -86,6 +86,52 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test('paginates open-channel discovery with a composite cursor', () async {
|
||||
final firstPage = List.generate(
|
||||
500,
|
||||
(index) => _meta(
|
||||
id: '${index.toString().padLeft(8, '0')}-0000-4000-8000-000000000000',
|
||||
name: 'channel-$index',
|
||||
createdAt: 10,
|
||||
),
|
||||
);
|
||||
final lastPageEvent = _meta(
|
||||
id: '99999999-9999-4999-8999-999999999999',
|
||||
name: 'last-page',
|
||||
createdAt: 9,
|
||||
);
|
||||
final session = _FakeRelaySession(
|
||||
memberships: const [],
|
||||
metadataPages: [
|
||||
firstPage,
|
||||
[lastPageEvent],
|
||||
],
|
||||
);
|
||||
final container = _buildContainer(session: session);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final channels = await container.read(channelsProvider.future);
|
||||
|
||||
expect(channels, hasLength(501));
|
||||
expect(
|
||||
channels.map((channel) => channel.id),
|
||||
contains(lastPageEvent.getTagValue('d')),
|
||||
);
|
||||
final discoveryFilters = session.historyFilters
|
||||
.where(
|
||||
(filter) =>
|
||||
filter.kinds.length == 1 &&
|
||||
filter.kinds.single == 39000 &&
|
||||
!filter.tags.containsKey('#d'),
|
||||
)
|
||||
.toList();
|
||||
expect(discoveryFilters, hasLength(2));
|
||||
expect(discoveryFilters.first.until, isNull);
|
||||
expect(discoveryFilters.first.extensions, isEmpty);
|
||||
expect(discoveryFilters.last.until, firstPage.last.createdAt);
|
||||
expect(discoveryFilters.last.extensions['before_id'], firstPage.last.id);
|
||||
});
|
||||
|
||||
test('deduplicates joined channels from open-channel discovery', () async {
|
||||
final session = _FakeRelaySession(
|
||||
memberships: [_membership(_channelA, myPk)],
|
||||
@@ -488,15 +534,18 @@ ProviderContainer _buildContainer({required _FakeRelaySession session}) {
|
||||
class _FakeRelaySession extends RelaySessionNotifier {
|
||||
_FakeRelaySession({
|
||||
required this.memberships,
|
||||
required this.metadata,
|
||||
this.metadata = const [],
|
||||
this.metadataPages,
|
||||
this.hiddenDmEvents = const [],
|
||||
this.membershipFailures = 0,
|
||||
});
|
||||
|
||||
List<NostrEvent> memberships;
|
||||
List<NostrEvent> metadata;
|
||||
final List<List<NostrEvent>>? metadataPages;
|
||||
final List<NostrEvent> hiddenDmEvents;
|
||||
int membershipFailures;
|
||||
int _metadataPageIndex = 0;
|
||||
|
||||
final List<NostrFilter> historyFilters = [];
|
||||
final List<NostrFilter> subscribeFilters = [];
|
||||
@@ -535,7 +584,14 @@ class _FakeRelaySession extends RelaySessionNotifier {
|
||||
// rows so tests verify the provider rejects them rather than trusting the
|
||||
// fake to pre-filter them.
|
||||
final ids = filter.tags['#d']?.toSet();
|
||||
if (ids == null) return List.of(metadata);
|
||||
if (ids == null) {
|
||||
final pages = metadataPages;
|
||||
if (pages != null) {
|
||||
if (_metadataPageIndex >= pages.length) return const [];
|
||||
return List.of(pages[_metadataPageIndex++]);
|
||||
}
|
||||
return List.of(metadata);
|
||||
}
|
||||
return metadata.where((e) => ids.contains(e.getTagValue('d'))).toList();
|
||||
}
|
||||
return const [];
|
||||
|
||||
Reference in New Issue
Block a user