diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 986c70f1df..7dc2bb4625 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pages/chat/chat_actions.dart'; import 'package:fluffychat/pages/chat/chat_view_style.dart'; import 'package:fluffychat/presentation/mixins/handle_clipboard_action_mixin.dart'; import 'package:fluffychat/presentation/mixins/paste_image_mixin.dart'; +import 'package:fluffychat/presentation/model/chat/view_event_list_ui_state.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:fluffychat/utils/extension/global_key_extension.dart'; @@ -145,16 +146,16 @@ class ChatController extends State final composerDebouncer = Debouncer(const Duration(milliseconds: 100), initialValue: ''); - bool get isEmptyChat => + bool get hasNoMessageEvents => timeline != null && - !timeline!.events.any( - (event) => { - EventTypes.Message, - EventTypes.Sticker, - EventTypes.Encrypted, - EventTypes.CallInvite, - }.contains(event.type), - ); + !timeline!.events.where((event) => event.isVisibleInGui).any( + (event) => { + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + EventTypes.CallInvite, + }.contains(event.type), + ); final AutoScrollController scrollController = AutoScrollController(); @@ -171,6 +172,9 @@ class ChatController extends State final ValueNotifier stickyTimestampNotifier = ValueNotifier(null); + final ValueNotifier openingChatViewStateNotifier = + ValueNotifier(ViewEventListInitial()); + final FocusSuggestionController _focusSuggestionController = FocusSuggestionController(); @@ -310,11 +314,15 @@ class ChatController extends State EmojiPickerType emojiPickerType = EmojiPickerType.keyboard; - void requestHistory() async { + Future requestHistory({ + int? historyCount, + }) async { if (!timeline!.canRequestHistory) return; Logs().v('Chat::requestHistory(): Requesting history...'); try { - await timeline!.requestHistory(historyCount: _loadHistoryCount); + return timeline!.requestHistory( + historyCount: historyCount ?? _loadHistoryCount, + ); } catch (err) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -327,7 +335,7 @@ class ChatController extends State } } - void _updateScrollController() { + void _updateScrollController() async { if (!mounted) { return; } @@ -345,7 +353,7 @@ class ChatController extends State scrollController.position.maxScrollExtent || scrollController.position.pixels + _isPortionAvailableToScroll == scrollController.position.maxScrollExtent) { - requestHistory(); + await requestHistory(); } } @@ -389,10 +397,11 @@ class ChatController extends State } void _tryLoadTimeline() async { + _updateOpeningChatViewStateNotifier(ViewEventListLoading()); loadTimelineFuture = _getTimeline(); try { await loadTimelineFuture; - _listenRequestHistory(); + await _tryRequestHistory(); final fullyRead = room?.fullyRead; if (fullyRead == null || fullyRead.isEmpty || fullyRead == '') { setReadMarker(); @@ -1636,7 +1645,7 @@ class ChatController extends State _currentChatScrollState = ChatScrollState.scrolling; } - void _listenRequestHistory() { + Future _tryRequestHistory() async { if (timeline == null) return; final allMembershipEvents = timeline!.events.every( @@ -1650,7 +1659,24 @@ class ChatController extends State _defaultEventCountDisplay; if (allMembershipEvents || canRequestHistory) { - requestHistory(); + try { + await requestHistory(historyCount: _defaultEventCountDisplay); + } catch (e) { + Logs().e( + 'Chat::_tryRequestHistory():: Error - $e', + ); + } + _updateOpeningChatViewStateNotifier(ViewEventListSuccess()); + } + } + + void _updateOpeningChatViewStateNotifier(ViewEventListUIState state) { + try { + openingChatViewStateNotifier.value = state; + } on FlutterError catch (e) { + Logs().e( + 'Chat::_updateViewEventListNotifier():: FlutterError - $e', + ); } } @@ -1706,6 +1732,7 @@ class ChatController extends State sendController.dispose(); suggestionsController.dispose(); stickyTimestampNotifier.dispose(); + openingChatViewStateNotifier.dispose(); super.dispose(); } diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 24dc9d132d..718add73ca 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -37,7 +37,7 @@ class ChatEventList extends StatelessWidget { thisEventsKeyMap[events[i].eventId] = i; } - if (controller.isEmptyChat) { + if (controller.hasNoMessageEvents) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/pages/chat/chat_view_body.dart b/lib/pages/chat/chat_view_body.dart index 5dca15d98e..7dd46dec9b 100644 --- a/lib/pages/chat/chat_view_body.dart +++ b/lib/pages/chat/chat_view_body.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/pages/chat/events/message_content_mixin.dart'; import 'package:fluffychat/pages/chat/chat_pinned_events/pinned_events_view.dart'; import 'package:fluffychat/pages/chat/sticky_timestamp_widget.dart'; import 'package:fluffychat/pages/chat/tombstone_display.dart'; +import 'package:fluffychat/presentation/model/chat/view_event_list_ui_state.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/widgets/connection_status_header.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -51,14 +52,21 @@ class ChatViewBody extends StatelessWidget with MessageContentMixin { Expanded( child: GestureDetector( onTap: controller.clearSingleSelectedEvent, - child: Builder( - builder: (context) { - if (controller.timeline == null) { + child: ValueListenableBuilder( + valueListenable: + controller.openingChatViewStateNotifier, + builder: (context, viewState, __) { + if (viewState is ViewEventListLoading) { return const ChatLoadingView(); } - return ChatEventList( - controller: controller, - ); + + if (viewState is ViewEventListSuccess) { + return ChatEventList( + controller: controller, + ); + } + + return const SizedBox.shrink(); }, ), ), diff --git a/lib/presentation/model/chat/view_event_list_ui_state.dart b/lib/presentation/model/chat/view_event_list_ui_state.dart new file mode 100644 index 0000000000..d6c01c1eca --- /dev/null +++ b/lib/presentation/model/chat/view_event_list_ui_state.dart @@ -0,0 +1,21 @@ +import 'package:equatable/equatable.dart'; + +abstract class ViewEventListUIState with EquatableMixin { + @override + List get props => []; +} + +class ViewEventListInitial extends ViewEventListUIState { + @override + List get props => []; +} + +class ViewEventListLoading extends ViewEventListUIState { + @override + List get props => []; +} + +class ViewEventListSuccess extends ViewEventListUIState { + @override + List get props => []; +}