diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 3391c46a53f..d183e7dea8c 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -596,8 +596,14 @@ class ChatController extends State // We are already setting a read marker if (_setReadMarkerFuture != null) return; - // We only set read marker if we are at the bottom - if (_scrolledUp) return; + // We only set read marker if we are at the bottom. Prefer the live + // scroll position, but fall back to the cached flag when no client is + // attached yet. + if (scrollController.hasClients + ? scrollController.position.pixels > 0 + : _scrolledUp) { + return; + } // We do not set read marker if we offer user the scroll up banner if (scrollUpBannerEventId != null) return; @@ -614,16 +620,21 @@ class ChatController extends State } final setOnLatestEvent = eventId == null; + // When we are viewing a context slice of older events, do not treat + // the newest loaded event as the room's latest event. + if (setOnLatestEvent && timeline.allowNewEvent == false) return; + // Target the latest message-like event. Do not gate this on push rule + // evaluation: read markers track what the user has seen, not what + // notifies them. eventId ??= timeline.events .firstWhereOrNull( - (event) => room.pushRuleState == PushRuleState.notify - ? room.client.pushruleEvaluator.match(event).notify - : { - EventTypes.Message, - EventTypes.Encrypted, - EventTypes.Sticker, - }.contains(event.type) && - event.eventId.isValidMatrixIdStrict(), + (event) => + { + EventTypes.Message, + EventTypes.Encrypted, + EventTypes.Sticker, + }.contains(event.type) && + event.eventId.isValidMatrixIdStrict(), ) ?.eventId; @@ -633,8 +644,10 @@ class ChatController extends State // This is a sending event, we do not set a readmarker yet if (eventId.isValidMatrixIdStrict() == false) return; - // Already set a read marker on this event - if (room.fullyRead == eventId) return; + // Already set a read marker on this event, unless the room still shows + // new messages: re-posting refreshes the receipts' timestamps, which the + // unread indicator depends on. + if (room.fullyRead == eventId && !room.hasNewMessages) return; // Set a readmarker on a specific event, not latest, but room is not unread // at all. @@ -651,7 +664,11 @@ class ChatController extends State eventId: eventId, public: AppSettings.sendPublicReadReceipts.value, ) - .then((_) { + .catchError((Object e, StackTrace s) { + Logs().w('Failed to set read marker', e, s); + }) + .whenComplete(() { + // Always release the latch, even when the request fails. _setReadMarkerFuture = null; }); }