mirror of
https://github.com/block/buzz.git
synced 2026-11-15 13:51:12 +00:00
fix(mobile): retire directory-only loads on a scope switch
A directory-only search load kept its ownership marker after a community or identity switch retired it. On returning to the first community, the list refresh treated the abandoned loading status as owned by a live load and left it in place, so search never loaded the directory again. A scope switch now retires directory-only loads too: it advances the directory generation and clears the marker. Tests cover returning both while the old request is in flight and after it ended. Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz> Signed-off-by: Tom Brow <tomb@squareup.com>
This commit is contained in:
@@ -459,6 +459,16 @@ class _ChannelRefreshCoordinator {
|
|||||||
/// wire cannot be written into the new scope.
|
/// wire cannot be written into the new scope.
|
||||||
void retireInFlight() => _generation++;
|
void retireInFlight() => _generation++;
|
||||||
|
|
||||||
|
/// Retires every in-flight request, directory-only loads included, for a
|
||||||
|
/// relay or identity change. A directory-only load survives ordinary list
|
||||||
|
/// refreshes, but not a scope change: its loading status must not look
|
||||||
|
/// owned when the user returns to the old scope.
|
||||||
|
void retireScope() {
|
||||||
|
_generation++;
|
||||||
|
_directoryGeneration++;
|
||||||
|
_directoryOnlyGeneration = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts a fenced refresh without issuing the directory query.
|
/// Starts a fenced refresh without issuing the directory query.
|
||||||
///
|
///
|
||||||
/// Used by callers that must carry the scope across later awaits even when
|
/// Used by callers that must carry the scope across later awaits even when
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class ChannelsNotifier extends AsyncNotifier<List<Channel>> {
|
|||||||
_confirmedJoins.clear();
|
_confirmedJoins.clear();
|
||||||
// Retire any in-flight directory request: its response describes the
|
// Retire any in-flight directory request: its response describes the
|
||||||
// previous relay or identity and must not reach this scope's state.
|
// previous relay or identity and must not reach this scope's state.
|
||||||
_refreshCoordinator.retireInFlight();
|
_refreshCoordinator.retireScope();
|
||||||
}
|
}
|
||||||
final snapshot =
|
final snapshot =
|
||||||
(_hasLoaded ? state.value : null) ??
|
(_hasLoaded ? state.value : null) ??
|
||||||
|
|||||||
@@ -1963,6 +1963,107 @@ void main() {
|
|||||||
isNot(communityB),
|
isNot(communityB),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'returning to a community during a discarded load loads again',
|
||||||
|
() async {
|
||||||
|
final session = _FakeRelaySession(
|
||||||
|
memberships: [_membership(_channelA, myPk)],
|
||||||
|
metadata: [
|
||||||
|
_meta(id: _channelA, name: 'general'),
|
||||||
|
_meta(id: _channelB, name: 'community-a-open'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
final container = await loaded(session);
|
||||||
|
final communityA = container.read(relayConfigProvider).baseUrl;
|
||||||
|
|
||||||
|
session.pauseNextDirectoryQuery();
|
||||||
|
final directory = container
|
||||||
|
.read(channelsProvider.notifier)
|
||||||
|
.ensureDirectoryLoaded(maxAge: Duration.zero);
|
||||||
|
await session.nextDirectoryQueryStarted;
|
||||||
|
|
||||||
|
container
|
||||||
|
.read(relayConfigProvider.notifier)
|
||||||
|
.update(baseUrl: 'https://community-b.example');
|
||||||
|
await container.read(channelsProvider.future);
|
||||||
|
container
|
||||||
|
.read(relayConfigProvider.notifier)
|
||||||
|
.update(baseUrl: communityA);
|
||||||
|
await container.read(channelsProvider.future);
|
||||||
|
await _settle();
|
||||||
|
final directoryQueries = session.directoryQueryFilters.length;
|
||||||
|
await container
|
||||||
|
.read(channelsProvider.notifier)
|
||||||
|
.ensureDirectoryLoaded(maxAge: Duration.zero);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
session.directoryQueryFilters.length,
|
||||||
|
greaterThan(directoryQueries),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
container.read(channelDirectoryLoadStatusProvider).status,
|
||||||
|
ChannelDirectoryLoadStatus.loaded,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The old request ending later leaves the newer load's status alone.
|
||||||
|
session.resumePausedDirectoryQuery();
|
||||||
|
await directory;
|
||||||
|
await _settle();
|
||||||
|
expect(
|
||||||
|
container.read(channelDirectoryLoadStatusProvider).status,
|
||||||
|
ChannelDirectoryLoadStatus.loaded,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'returning to a community after a discarded load loads again',
|
||||||
|
() async {
|
||||||
|
final session = _FakeRelaySession(
|
||||||
|
memberships: [_membership(_channelA, myPk)],
|
||||||
|
metadata: [
|
||||||
|
_meta(id: _channelA, name: 'general'),
|
||||||
|
_meta(id: _channelB, name: 'community-a-open'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
final container = await loaded(session);
|
||||||
|
final communityA = container.read(relayConfigProvider).baseUrl;
|
||||||
|
|
||||||
|
session.pauseNextDirectoryQuery();
|
||||||
|
final directory = container
|
||||||
|
.read(channelsProvider.notifier)
|
||||||
|
.ensureDirectoryLoaded(maxAge: Duration.zero);
|
||||||
|
await session.nextDirectoryQueryStarted;
|
||||||
|
|
||||||
|
container
|
||||||
|
.read(relayConfigProvider.notifier)
|
||||||
|
.update(baseUrl: 'https://community-b.example');
|
||||||
|
await container.read(channelsProvider.future);
|
||||||
|
session.resumePausedDirectoryQuery();
|
||||||
|
await directory;
|
||||||
|
await _settle();
|
||||||
|
|
||||||
|
container
|
||||||
|
.read(relayConfigProvider.notifier)
|
||||||
|
.update(baseUrl: communityA);
|
||||||
|
await container.read(channelsProvider.future);
|
||||||
|
await _settle();
|
||||||
|
final directoryQueries = session.directoryQueryFilters.length;
|
||||||
|
await container
|
||||||
|
.read(channelsProvider.notifier)
|
||||||
|
.ensureDirectoryLoaded(maxAge: Duration.zero);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
session.directoryQueryFilters.length,
|
||||||
|
greaterThan(directoryQueries),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
container.read(channelDirectoryLoadStatusProvider).status,
|
||||||
|
ChannelDirectoryLoadStatus.loaded,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('membership refresh does not refetch a loaded directory', () async {
|
test('membership refresh does not refetch a loaded directory', () async {
|
||||||
@@ -3490,6 +3591,7 @@ class _FakeRelaySession extends RelaySessionNotifier {
|
|||||||
Completer<void>? _pausedSubscribe;
|
Completer<void>? _pausedSubscribe;
|
||||||
Completer<void>? _subscribeStarted;
|
Completer<void>? _subscribeStarted;
|
||||||
Completer<void>? _pausedDirectory;
|
Completer<void>? _pausedDirectory;
|
||||||
|
bool _pauseClaimed = false;
|
||||||
Completer<void>? _directoryStarted;
|
Completer<void>? _directoryStarted;
|
||||||
Completer<void>? _pausedHiddenDm;
|
Completer<void>? _pausedHiddenDm;
|
||||||
Completer<void>? _hiddenDmStarted;
|
Completer<void>? _hiddenDmStarted;
|
||||||
@@ -3812,11 +3914,15 @@ class _FakeRelaySession extends RelaySessionNotifier {
|
|||||||
// active when the response is released.
|
// active when the response is released.
|
||||||
final directorySnapshot = List.of(metadata);
|
final directorySnapshot = List.of(metadata);
|
||||||
final paused = _pausedDirectory;
|
final paused = _pausedDirectory;
|
||||||
if (paused != null) {
|
// Only the first query after the pause waits; a later one meanwhile
|
||||||
|
// (a new scope's load) answers at once.
|
||||||
|
if (paused != null && !_pauseClaimed) {
|
||||||
|
_pauseClaimed = true;
|
||||||
_directoryStarted!.complete();
|
_directoryStarted!.complete();
|
||||||
await paused.future;
|
await paused.future;
|
||||||
_pausedDirectory = null;
|
_pausedDirectory = null;
|
||||||
_directoryStarted = null;
|
_directoryStarted = null;
|
||||||
|
_pauseClaimed = false;
|
||||||
}
|
}
|
||||||
if (directoryFailures > 0) {
|
if (directoryFailures > 0) {
|
||||||
directoryFailures--;
|
directoryFailures--;
|
||||||
|
|||||||
Reference in New Issue
Block a user