@@ -145,6 +145,32 @@ 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 is used as the anchor for fetching the next batch of older messages
151+ /// and will be `null` if no messages of this narrow have been fetched yet.
152+ /// A non-null value for this doesn't always mean [haveOldest] is `true` .
153+ ///
154+ /// The related message may not appear in [messages] because it
155+ /// is muted in one way or another. Also, the related message may not stay
156+ /// in the narrow for reasons such as message moves or deletions,
157+ /// but that's fine for what this is used for.
158+ int ? get oldMessageId => _oldMessageId;
159+ int ? _oldMessageId;
160+
161+ /// The ID of the newest known message so far in this narrow.
162+ ///
163+ /// This is used as the anchor for fetching the next batch of newer messages
164+ /// and will be `null` if no messages of this narrow have been fetched yet.
165+ /// A non-null value for this doesn't always mean [haveNewest] is `true` .
166+ ///
167+ /// The related message may not appear in [messages] because it
168+ /// is muted in one way or another. Also, the related message may not stay
169+ /// in the narrow for reasons such as message moves or deletions,
170+ /// but that's fine for what this is used for.
171+ int ? get newMessageId => _newMessageId;
172+ int ? _newMessageId;
173+
148174 /// Whether [messages] and [items] represent the results of a fetch.
149175 ///
150176 /// This allows the UI to distinguish "still working on fetching messages"
@@ -409,6 +435,8 @@ mixin _MessageSequence {
409435 generation += 1 ;
410436 messages.clear ();
411437 middleMessage = 0 ;
438+ _oldMessageId = null ;
439+ _newMessageId = null ;
412440 outboxMessages.clear ();
413441 _haveOldest = false ;
414442 _haveNewest = false ;
@@ -818,6 +846,7 @@ class MessageListView with ChangeNotifier, _MessageSequence {
818846 Future <void > fetchInitial () async {
819847 assert (! fetched && ! haveOldest && ! haveNewest && ! busyFetchingMore);
820848 assert (messages.isEmpty && contents.isEmpty);
849+ assert (oldMessageId == null && newMessageId == null );
821850
822851 if (narrow case KeywordSearchNarrow (keyword: '' )) {
823852 // The server would reject an empty keyword search; skip the request.
@@ -841,6 +870,9 @@ class MessageListView with ChangeNotifier, _MessageSequence {
841870 );
842871 if (this .generation > generation) return ;
843872
873+ _oldMessageId = result.messages.firstOrNull? .id;
874+ _newMessageId = result.messages.lastOrNull? .id;
875+
844876 _adjustNarrowForTopicPermalink (result.messages.firstOrNull);
845877
846878 store.reconcileMessages (result.messages);
@@ -868,6 +900,12 @@ class MessageListView with ChangeNotifier, _MessageSequence {
868900 _syncOutboxMessagesFromStore ();
869901 }
870902
903+ while (messages.isEmpty && ! (haveOldest && haveNewest)) {
904+ await fetchOlder (partOfInitialFetch: true );
905+ if (messages.isNotEmpty) break ;
906+ await fetchNewer (partOfInitialFetch: true );
907+ }
908+
871909 _setStatus (FetchingStatus .idle, was: FetchingStatus .fetchInitial);
872910 }
873911
@@ -911,16 +949,18 @@ class MessageListView with ChangeNotifier, _MessageSequence {
911949 /// then this method does nothing and immediately returns.
912950 /// That makes this method suitable to call frequently, e.g. every frame,
913951 /// whenever it looks likely to be useful to have more messages.
914- Future <void > fetchOlder () async {
952+ Future <void > fetchOlder ({ bool partOfInitialFetch = false } ) async {
915953 if (haveOldest) return ;
916954 if (busyFetchingMore) return ;
917- assert (fetched);
918- assert (messages.isNotEmpty );
955+ assert (partOfInitialFetch || fetched);
956+ assert (oldMessageId != null );
919957 await _fetchMore (
920- anchor: NumericAnchor (messages[ 0 ].id ),
958+ anchor: NumericAnchor (oldMessageId ! ),
921959 numBefore: kMessageListFetchBatchSize,
922960 numAfter: 0 ,
961+ partOfInitialFetch: partOfInitialFetch,
923962 processResult: (result) {
963+ _oldMessageId = result.messages.firstOrNull? .id ?? oldMessageId;
924964 store.reconcileMessages (result.messages);
925965 store.recentSenders.handleMessages (result.messages); // TODO(#824)
926966
@@ -941,16 +981,18 @@ class MessageListView with ChangeNotifier, _MessageSequence {
941981 /// then this method does nothing and immediately returns.
942982 /// That makes this method suitable to call frequently, e.g. every frame,
943983 /// whenever it looks likely to be useful to have more messages.
944- Future <void > fetchNewer () async {
984+ Future <void > fetchNewer ({ bool partOfInitialFetch = false } ) async {
945985 if (haveNewest) return ;
946986 if (busyFetchingMore) return ;
947- assert (fetched);
948- assert (messages.isNotEmpty );
987+ assert (partOfInitialFetch || fetched);
988+ assert (newMessageId != null );
949989 await _fetchMore (
950- anchor: NumericAnchor (messages.last.id ),
990+ anchor: NumericAnchor (newMessageId ! ),
951991 numBefore: 0 ,
952992 numAfter: kMessageListFetchBatchSize,
993+ partOfInitialFetch: partOfInitialFetch,
953994 processResult: (result) {
995+ _newMessageId = result.messages.lastOrNull? .id ?? newMessageId;
954996 store.reconcileMessages (result.messages);
955997 store.recentSenders.handleMessages (result.messages); // TODO(#824)
956998
@@ -971,12 +1013,15 @@ class MessageListView with ChangeNotifier, _MessageSequence {
9711013 required Anchor anchor,
9721014 required int numBefore,
9731015 required int numAfter,
1016+ required bool partOfInitialFetch,
9741017 required void Function (GetMessagesResult ) processResult,
9751018 }) async {
9761019 assert (narrow is ! TopicNarrow
9771020 // We only intend to send "with" in [fetchInitial]; see there.
9781021 || (narrow as TopicNarrow ).with_ == null );
979- _setStatus (FetchingStatus .fetchingMore, was: FetchingStatus .idle);
1022+ if (! partOfInitialFetch) {
1023+ _setStatus (FetchingStatus .fetchingMore, was: FetchingStatus .idle);
1024+ }
9801025 final generation = this .generation;
9811026 bool hasFetchError = false ;
9821027 try {
@@ -998,7 +1043,7 @@ class MessageListView with ChangeNotifier, _MessageSequence {
9981043
9991044 processResult (result);
10001045 } finally {
1001- if (this .generation == generation) {
1046+ if (this .generation == generation && ! partOfInitialFetch ) {
10021047 if (hasFetchError) {
10031048 _setStatus (FetchingStatus .backoff, was: FetchingStatus .fetchingMore);
10041049 unawaited ((_fetchBackoffMachine ?? = BackoffMachine ())
0 commit comments