Skip to content

Commit 03d61ca

Browse files
committed
msglist: Fetch newer messages despite previous muted batch
1 parent 3c10bed commit 03d61ca

File tree

3 files changed

+400
-65
lines changed

3 files changed

+400
-65
lines changed

lib/model/message_list.dart

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,26 @@ mixin _MessageSequence {
145145
/// The corresponding item index is [middleItem].
146146
int middleMessage = 0;
147147

148+
/// The ID of the oldest known message so far in this narrow.
149+
///
150+
/// This will be `null` if no messages of this narrow are fetched yet.
151+
/// Having a non-null value for this doesn't always mean [haveOldest] is `true`.
152+
///
153+
/// The related message may not appear in [messages] because it
154+
/// is muted in one way or another.
155+
int? get oldMessageId => _oldMessageId;
156+
int? _oldMessageId;
157+
158+
/// The ID of the newest known message so far in this narrow.
159+
///
160+
/// This will be `null` if no messages of this narrow are fetched yet.
161+
/// Having a non-null value for this doesn't always mean [haveNewest] is `true`.
162+
///
163+
/// The related message may not appear in [messages] because it
164+
/// is muted in one way or another.
165+
int? get newMessageId => _newMessageId;
166+
int? _newMessageId;
167+
148168
/// Whether [messages] and [items] represent the results of a fetch.
149169
///
150170
/// This allows the UI to distinguish "still working on fetching messages"
@@ -409,6 +429,8 @@ mixin _MessageSequence {
409429
generation += 1;
410430
messages.clear();
411431
middleMessage = 0;
432+
_oldMessageId = null;
433+
_newMessageId = null;
412434
outboxMessages.clear();
413435
_haveOldest = false;
414436
_haveNewest = false;
@@ -775,6 +797,7 @@ class MessageListView with ChangeNotifier, _MessageSequence {
775797
Future<void> fetchInitial() async {
776798
assert(!fetched && !haveOldest && !haveNewest && !busyFetchingMore);
777799
assert(messages.isEmpty && contents.isEmpty);
800+
assert(oldMessageId == null && newMessageId == null);
778801

779802
if (narrow case KeywordSearchNarrow(keyword: '')) {
780803
// The server would reject an empty keyword search; skip the request.
@@ -798,6 +821,9 @@ class MessageListView with ChangeNotifier, _MessageSequence {
798821
);
799822
if (this.generation > generation) return;
800823

824+
_oldMessageId = result.messages.firstOrNull?.id;
825+
_newMessageId = result.messages.lastOrNull?.id;
826+
801827
_adjustNarrowForTopicPermalink(result.messages.firstOrNull);
802828

803829
store.reconcileMessages(result.messages);
@@ -869,25 +895,32 @@ class MessageListView with ChangeNotifier, _MessageSequence {
869895
/// That makes this method suitable to call frequently, e.g. every frame,
870896
/// whenever it looks likely to be useful to have more messages.
871897
Future<void> fetchOlder() async {
872-
if (haveOldest) return;
873-
if (busyFetchingMore) return;
874-
assert(fetched);
875-
assert(messages.isNotEmpty);
876-
await _fetchMore(
877-
anchor: NumericAnchor(messages[0].id),
878-
numBefore: kMessageListFetchBatchSize,
879-
numAfter: 0,
880-
processResult: (result) {
881-
store.reconcileMessages(result.messages);
882-
store.recentSenders.handleMessages(result.messages); // TODO(#824)
883-
884-
final fetchedMessages = _allMessagesVisible
885-
? result.messages // Avoid unnecessarily copying the list.
886-
: result.messages.where(_messageVisible);
887-
888-
_insertAllMessages(0, fetchedMessages);
889-
_haveOldest = result.foundOldest;
890-
});
898+
final generation = this.generation;
899+
int visibleMessageCount = 0;
900+
do {
901+
if (haveOldest) return;
902+
if (busyFetchingMore) return;
903+
assert(fetched);
904+
assert(oldMessageId != null);
905+
await _fetchMore(
906+
anchor: NumericAnchor(oldMessageId!),
907+
numBefore: kMessageListFetchBatchSize,
908+
numAfter: 0,
909+
processResult: (result) {
910+
_oldMessageId = result.messages.firstOrNull?.id ?? oldMessageId;
911+
store.reconcileMessages(result.messages);
912+
store.recentSenders.handleMessages(result.messages); // TODO(#824)
913+
914+
final fetchedMessages = _allMessagesVisible
915+
? result.messages // Avoid unnecessarily copying the list.
916+
: result.messages.where(_messageVisible);
917+
918+
_insertAllMessages(0, fetchedMessages);
919+
_haveOldest = result.foundOldest;
920+
visibleMessageCount += fetchedMessages.length;
921+
});
922+
} while (visibleMessageCount < kMessageListFetchBatchSize / 2
923+
&& this.generation == generation);
891924
}
892925

893926
/// Fetch the next batch of newer messages, if applicable.
@@ -899,29 +932,36 @@ class MessageListView with ChangeNotifier, _MessageSequence {
899932
/// That makes this method suitable to call frequently, e.g. every frame,
900933
/// whenever it looks likely to be useful to have more messages.
901934
Future<void> fetchNewer() async {
902-
if (haveNewest) return;
903-
if (busyFetchingMore) return;
904-
assert(fetched);
905-
assert(messages.isNotEmpty);
906-
await _fetchMore(
907-
anchor: NumericAnchor(messages.last.id),
908-
numBefore: 0,
909-
numAfter: kMessageListFetchBatchSize,
910-
processResult: (result) {
911-
store.reconcileMessages(result.messages);
912-
store.recentSenders.handleMessages(result.messages); // TODO(#824)
913-
914-
for (final message in result.messages) {
915-
if (_messageVisible(message)) {
916-
_addMessage(message);
935+
final generation = this.generation;
936+
int visibleMessageCount = 0;
937+
do {
938+
if (haveNewest) return;
939+
if (busyFetchingMore) return;
940+
assert(fetched);
941+
assert(newMessageId != null);
942+
await _fetchMore(
943+
anchor: NumericAnchor(newMessageId!),
944+
numBefore: 0,
945+
numAfter: kMessageListFetchBatchSize,
946+
processResult: (result) {
947+
_newMessageId = result.messages.lastOrNull?.id ?? newMessageId;
948+
store.reconcileMessages(result.messages);
949+
store.recentSenders.handleMessages(result.messages); // TODO(#824)
950+
951+
for (final message in result.messages) {
952+
if (_messageVisible(message)) {
953+
_addMessage(message);
954+
visibleMessageCount++;
955+
}
917956
}
918-
}
919-
_haveNewest = result.foundNewest;
957+
_haveNewest = result.foundNewest;
920958

921-
if (haveNewest) {
922-
_syncOutboxMessagesFromStore();
923-
}
924-
});
959+
if (haveNewest) {
960+
_syncOutboxMessagesFromStore();
961+
}
962+
});
963+
} while (visibleMessageCount < kMessageListFetchBatchSize / 2
964+
&& this.generation == generation);
925965
}
926966

927967
Future<void> _fetchMore({

lib/widgets/message_list.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,6 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
838838
model.fetchInitial();
839839
}
840840

841-
bool _prevFetched = false;
842-
843841
void _modelChanged() {
844842
// When you're scrolling quickly, our mark-as-read requests include the
845843
// messages *between* _messagesRecentlyInViewport and the messages currently
@@ -866,14 +864,13 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
866864
// This method was called because that just changed.
867865
});
868866

869-
if (!_prevFetched && model.fetched && model.messages.isEmpty) {
867+
if (model.messages.isEmpty && model.haveOldest && model.haveNewest) {
870868
// If the fetch came up empty, there's nothing to read,
871869
// so opening the keyboard won't be bothersome and could be helpful.
872870
// It's definitely helpful if we got here from the new-DM page.
873871
MessageListPage.ancestorOf(context)
874872
.composeBoxState?.controller.requestFocusIfUnfocused();
875873
}
876-
_prevFetched = model.fetched;
877874
}
878875

879876
/// Find the range of message IDs on screen, as a (first, last) tuple,
@@ -1222,7 +1219,8 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
12221219
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
12231220
TypingStatusWidget(narrow: widget.narrow),
12241221
// TODO perhaps offer mark-as-read even when not done fetching?
1225-
MarkAsReadWidget(narrow: widget.narrow),
1222+
if (model.messages.isNotEmpty)
1223+
MarkAsReadWidget(narrow: widget.narrow),
12261224
// To reinforce that the end of the feed has been reached:
12271225
// https://chat.zulip.org/#narrow/channel/48-mobile/topic/space.20at.20end.20of.20thread/near/2203391
12281226
const SizedBox(height: 12),

0 commit comments

Comments
 (0)