diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 990557ee5..a373b87ea 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -102,7 +102,7 @@ class ChatController extends State Timeline? timeline; - String? readMarkerEventId; + late final String readMarkerEventId; String get roomId => widget.room.id; @@ -270,6 +270,7 @@ class ChatController extends State ); sendingClient = Matrix.of(context).client; + readMarkerEventId = room.hasNewMessages ? room.fullyRead : ''; WidgetsBinding.instance.addObserver(this); _tryLoadTimeline(); if (kIsWeb) { @@ -284,19 +285,22 @@ class ChatController extends State await loadTimelineFuture; if (initialEventId != null) scrollToEventId(initialEventId); - final fullyRead = room.fullyRead; - if (fullyRead.isEmpty) { - setReadMarker(); - return; - } - if (timeline?.events.any((event) => event.eventId == fullyRead) ?? - false) { - Logs().v('Scroll up to visible event', fullyRead); - scrollToEventId(fullyRead, highlightEvent: false); + final readMarkerEventIndex = readMarkerEventId.isEmpty + ? -1 + : timeline!.events + .where((e) => e.isVisibleInGui) + .toList() + .indexWhere((e) => e.eventId == readMarkerEventId); + + if (readMarkerEventIndex > 1) { + Logs().v('Scroll up to visible event', readMarkerEventId); + scrollToEventId(readMarkerEventId, highlightEvent: false); return; + } else if (readMarkerEventId.isNotEmpty && readMarkerEventIndex == -1) { + _showScrollUpMaterialBanner(readMarkerEventId); } + if (!mounted) return; - _showScrollUpMaterialBanner(fullyRead); } catch (e, s) { ErrorReporter(context, 'Unable to load timeline').onErrorCallback(e, s); rethrow; @@ -323,13 +327,16 @@ class ChatController extends State int? animateInEventIndex; void onInsert(int i) { + onChange(i); + // setState will be called by updateView() anyway + animateInEventIndex = i; + } + + void onChange(int i) { if (timeline?.events[i].status == EventStatus.synced) { final index = timeline!.events.firstIndexWhereNotError; if (i == index) setReadMarker(eventId: timeline?.events[i].eventId); } - - // setState will be called by updateView() anyway - animateInEventIndex = i; } Future _getTimeline({ @@ -347,6 +354,7 @@ class ChatController extends State onUpdate: updateView, eventContextId: eventContextId, onInsert: onInsert, + onChange: onChange, ); } catch (e, s) { Logs().w('Unable to load timeline on event ID $eventContextId', e, s); @@ -354,6 +362,7 @@ class ChatController extends State timeline = await room.getTimeline( onUpdate: updateView, onInsert: onInsert, + onChange: onChange, ); if (!mounted) return; if (e is TimeoutException || e is IOException) { @@ -380,6 +389,7 @@ class ChatController extends State if (_setReadMarkerFuture != null) return; if (_scrolledUp) return; if (scrollUpBannerEventId != null) return; + if (eventId == null && !room.hasNewMessages && room.notificationCount == 0) { diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 2d56aa5c0..65c920360 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -141,8 +141,7 @@ class ChatEventList extends StatelessWidget { .any((e) => e.eventId == event.eventId), timeline: controller.timeline!, displayReadMarker: - controller.readMarkerEventId == event.eventId && - controller.timeline?.allowNewEvent == false, + i > 0 && controller.readMarkerEventId == event.eventId, nextEvent: i + 1 < events.length ? events[i + 1] : null, previousEvent: i > 0 ? events[i - 1] : null, avatarPresenceBackgroundColor: