diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 864bc3ab..da7d6cf1 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2528,5 +2528,6 @@ } }, "jumpToLastReadMessage": "Jump to last read message", - "readUpToHere": "Read up to here" + "readUpToHere": "Read up to here", + "jump": "Jump" } diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 43fb8233..109bbfa2 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -163,11 +163,6 @@ class ChatController extends State { bool showEmojiPicker = false; - bool get lastReadEventVisible => - timeline == null || - room.fullyRead.isEmpty || - timeline!.events.any((event) => event.eventId == room.fullyRead); - void recreateChat() async { final room = this.room; final userId = room.directChatMatrixID; @@ -287,14 +282,36 @@ class ChatController extends State { Future? loadTimelineFuture; - Future _getTimeline([String? eventContextId]) async { + Future _getTimeline({ + String? eventContextId, + Duration timeout = const Duration(seconds: 5), + }) async { await Matrix.of(context).client.roomsLoading; await Matrix.of(context).client.accountDataLoading; - final timeline = this.timeline = await room.getTimeline( - onUpdate: updateView, - eventContextId: eventContextId, - ); - if (timeline.events.isNotEmpty) { + eventContextId ??= room.fullyRead; + try { + timeline = await room + .getTimeline( + onUpdate: updateView, + eventContextId: eventContextId, + ) + .timeout(timeout); + } on TimeoutException catch (_) { + if (!mounted) return; + timeline = await room.getTimeline(onUpdate: updateView); + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(L10n.of(context)!.jumpToLastReadMessage), + action: SnackBarAction( + label: L10n.of(context)!.jump, + onPressed: () => scrollToEventId(eventContextId!), + ), + ), + ); + } + timeline!.requestKeys(onlineKeyBackupOnly: false); + if (timeline!.events.isNotEmpty) { if (room.markedUnread) room.markUnread(false); setReadMarker(); } @@ -311,7 +328,6 @@ class ChatController extends State { } }); - timeline.requestKeys(onlineKeyBackupOnly: false); return; } @@ -320,7 +336,6 @@ class ChatController extends State { void setReadMarker({String? eventId}) { if (_setReadMarkerFuture != null) return; if (eventId == null && - lastReadEventVisible && !room.hasNewMessages && room.notificationCount == 0) { return; @@ -330,10 +345,6 @@ class ChatController extends State { final timeline = this.timeline; if (timeline == null || timeline.events.isEmpty) return; - if (eventId == null && !lastReadEventVisible) { - return; - } - eventId ??= timeline.events.first.eventId; Logs().v('Set read marker...', eventId); // ignore: unawaited_futures @@ -787,7 +798,10 @@ class ChatController extends State { if (eventIndex == -1) { setState(() { timeline = null; - loadTimelineFuture = _getTimeline(eventId); + loadTimelineFuture = _getTimeline( + eventContextId: eventId, + timeout: const Duration(seconds: 30), + ); }); await loadTimelineFuture; WidgetsBinding.instance.addPostFrameCallback((timeStamp) { diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 27d58d81..66448648 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -336,33 +336,6 @@ class ChatView extends StatelessWidget { ], ), ), - if (!controller.lastReadEventVisible && - controller.timeline!.allowNewEvent) - Positioned( - top: 16, - left: 0, - right: 0, - child: Center( - child: FloatingActionButton.extended( - icon: const Icon(Icons.arrow_upward_outlined), - onPressed: () => controller.scrollToEventId( - controller.room.fullyRead, - ), - label: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(L10n.of(context)!.jumpToLastReadMessage), - IconButton( - onPressed: () => controller.setReadMarker( - eventId: controller.room.fullyRead, - ), - icon: const Icon(Icons.close), - ), - ], - ), - ), - ), - ), if (controller.dragging) Container( color: Theme.of(context)