diff --git a/lib/di/global/get_it_initializer.dart b/lib/di/global/get_it_initializer.dart index 30fa593cfc..690bae9a11 100644 --- a/lib/di/global/get_it_initializer.dart +++ b/lib/di/global/get_it_initializer.dart @@ -71,6 +71,7 @@ import 'package:fluffychat/domain/usecase/send_media_on_web_with_caption_interac import 'package:fluffychat/domain/usecase/settings/save_language_interactor.dart'; import 'package:fluffychat/domain/usecase/settings/update_profile_interactor.dart'; import 'package:fluffychat/event/twake_event_dispatcher.dart'; +import 'package:fluffychat/pages/chat/chat_pinned_events/pinned_events_controller.dart'; import 'package:fluffychat/utils/famedlysdk_store.dart'; import 'package:fluffychat/utils/responsive/responsive_utils.dart'; import 'package:get_it/get_it.dart'; @@ -94,6 +95,7 @@ class GetItInitializer { bindingDatasourceImpl(); bindingRepositories(); bindingInteractor(); + _bindingControllers(); } void bindingGlobal() { @@ -273,4 +275,10 @@ class GetItInitializer { () => SendFilesOnWebWithCaptionInteractor(), ); } + + void _bindingControllers() { + getIt.registerFactory( + () => PinnedEventsController(), + ); + } } diff --git a/lib/domain/app_state/room/chat_get_pinned_events_state.dart b/lib/domain/app_state/room/chat_get_pinned_events_state.dart index 897481c3ab..daa6fa4498 100644 --- a/lib/domain/app_state/room/chat_get_pinned_events_state.dart +++ b/lib/domain/app_state/room/chat_get_pinned_events_state.dart @@ -29,6 +29,11 @@ class ChatGetPinnedEventsNoResult extends Failure { List get props => []; } +class CannotGetPinnedMessages extends Failure { + @override + List get props => []; +} + class ChatGetPinnedEventsFailure extends Failure { final dynamic exception; diff --git a/lib/domain/usecase/room/chat_get_pinned_events_interactor.dart b/lib/domain/usecase/room/chat_get_pinned_events_interactor.dart index dc4967748c..02c82feee2 100644 --- a/lib/domain/usecase/room/chat_get_pinned_events_interactor.dart +++ b/lib/domain/usecase/room/chat_get_pinned_events_interactor.dart @@ -8,14 +8,32 @@ import 'package:matrix/matrix.dart'; class ChatGetPinnedEventsInteractor { Stream> execute({ - required Room room, + required String roomId, + required Client client, + bool isInitial = false, }) async* { Logs().d( - "ChatGetPinnedEventsInteractor()::execute()::roomId: ${room.id}", + "ChatGetPinnedEventsInteractor()::execute()::roomId: $roomId", ); - yield Right(ChatGetPinnedEventsLoading()); + if (isInitial) { + yield Right(ChatGetPinnedEventsLoading()); + } try { + final room = client.getRoomById(roomId); + if (room == null) { + Logs().d( + "ChatGetPinnedEventsInteractor()::execute(): Room is Null", + ); + yield Left(CannotGetPinnedMessages()); + return; + } + if (isInitial) { + await room.getTimeline(); + } final pinnedEvents = room.pinnedEventIds; + Logs().d( + "ChatGetPinnedEventsInteractor()::execute()::pinnedEvents: $pinnedEvents", + ); final result = (await Future.wait( pinnedEvents.map(room.getEventById), )) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index d481438abd..c471bc88b6 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -102,7 +102,7 @@ class ChatController extends State final getPinnedMessageInteractor = getIt.get(); - PinnedEventsController pinnedEventsController = PinnedEventsController(); + final pinnedEventsController = getIt.get(); final ValueKey chatComposerTypeAheadKey = const ValueKey('chatComposerTypeAheadKey'); @@ -159,6 +159,9 @@ class ChatController extends State final FocusSuggestionController _focusSuggestionController = FocusSuggestionController(); + final AutoScrollController pinnedMessageScrollController = + AutoScrollController(); + final TextEditingController _captionsController = TextEditingController(); FocusNode inputFocus = FocusNode(); @@ -361,9 +364,7 @@ class ChatController extends State void _tryLoadTimeline() async { loadTimelineFuture = _getTimeline(); try { - await loadTimelineFuture?.then((_) { - _initializePinnedEvents(); - }); + await loadTimelineFuture; final fullyRead = room?.fullyRead; if (fullyRead == null || fullyRead.isEmpty || fullyRead == '') { setReadMarker(); @@ -1114,9 +1115,10 @@ class ChatController extends State await TwakeDialog.showFutureLoadingDialogFullScreen( future: () => room.setPinnedEvents(pinnedEventIds), ); - pinnedEventsController.getPinnedMessageAction( - isInitial: unpin, - room: room, + _getPinnedEvents( + isUnpin: unpin, + roomId: room.id, + client: room.client, eventId: selectedEventIds, ); } @@ -1498,19 +1500,44 @@ class ChatController extends State if (popResult is Event) { scrollToEventIdAndHighlight(popResult.eventId); } else if (popResult is List) { - pinnedEventsController.handlePopBack(popResult); + pinnedEventsController.handlePopBack( + popResult: popResult, + client: client, + ); } } void _initializePinnedEvents() { - pinnedEventsController.getPinnedMessageAction( - room: room!, + if (roomId == null) return; + _getPinnedEvents( + client: client, + roomId: roomId!, isInitial: true, ); } + void _getPinnedEvents({ + required Client client, + required String roomId, + bool isInitial = false, + bool isUnpin = false, + String? eventId, + }) { + pinnedEventsController.getPinnedMessageAction( + client: client, + roomId: roomId, + isInitial: isInitial, + isUnpin: isUnpin, + eventId: eventId, + jumpToPinnedMessageCallback: (index) { + pinnedMessageScrollController.scrollToIndex(index); + }, + ); + } + @override void initState() { + _initializePinnedEvents(); registerPasteShortcutListeners(); keyboardVisibilityController.onChange.listen(_keyboardListener); scrollController.addListener(_updateScrollController); diff --git a/lib/pages/chat/chat_pinned_events/pinned_events_controller.dart b/lib/pages/chat/chat_pinned_events/pinned_events_controller.dart index 7c1e306746..daee63af39 100644 --- a/lib/pages/chat/chat_pinned_events/pinned_events_controller.dart +++ b/lib/pages/chat/chat_pinned_events/pinned_events_controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:collection/collection.dart'; import 'package:dartz/dartz.dart' hide State; import 'package:fluffychat/app_state/failure.dart'; import 'package:fluffychat/app_state/success.dart'; @@ -8,16 +9,14 @@ import 'package:fluffychat/domain/app_state/room/chat_get_pinned_events_state.da import 'package:fluffychat/domain/usecase/room/chat_get_pinned_events_interactor.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; -import 'package:scroll_to_index/scroll_to_index.dart'; + +typedef JumpToPinnedMessageCallback = void Function(int index); class PinnedEventsController { static const _timeDelayGetPinnedMessage = Duration(seconds: 1); final getPinnedMessageInteractor = getIt.get(); - final AutoScrollController pinnedMessageScrollController = - AutoScrollController(); - final ValueNotifier currentPinnedEventNotifier = ValueNotifier(null); final ValueNotifier> getPinnedMessageNotifier = @@ -32,26 +31,35 @@ class PinnedEventsController { } void getPinnedMessageAction({ - required Room room, + required String roomId, + required Client client, bool isInitial = false, + bool isUnpin = false, String? eventId, + JumpToPinnedMessageCallback? jumpToPinnedMessageCallback, }) async { await Future.delayed(_timeDelayGetPinnedMessage); _pinnedEventsSubscription = getPinnedMessageInteractor .execute( - room: room, + roomId: roomId, + client: client, + isInitial: isInitial, ) .listen((event) { getPinnedMessageNotifier.value = event; event.fold((_) => null, (success) { if (success is ChatGetPinnedEventsSuccess) { if (success.pinnedEvents.isNotEmpty) { - if (isInitial) { - initialPinnedMessage(success.pinnedEvents); + if (isInitial || isUnpin) { + updatePinnedMessage( + success.pinnedEvents, + jumpToPinnedMessageCallback: jumpToPinnedMessageCallback, + ); } else { jumpToCurrentMessage( success.pinnedEvents, eventId: eventId, + jumpToPinnedMessageCallback: jumpToPinnedMessageCallback, ); } } @@ -71,7 +79,6 @@ class PinnedEventsController { ); if (event != null) { currentPinnedEventNotifier.value = event; - pinnedMessageScrollController.scrollToIndex(nextIndex); if (scrollToEventId != null) { scrollToEventId.call(event.eventId); } @@ -95,23 +102,34 @@ class PinnedEventsController { return index; } - void initialPinnedMessage(List pinnedEvents) { + void updatePinnedMessage( + List pinnedEvents, { + JumpToPinnedMessageCallback? jumpToPinnedMessageCallback, + }) { currentPinnedEventNotifier.value = pinnedEvents.last; - pinnedMessageScrollController.scrollToIndex( - pinnedEvents.length - 1, - ); + if (pinnedEvents.isNotEmpty) { + jumpToPinnedMessageCallback?.call( + pinnedEvents.length - 1, + ); + } } - void handlePopBack(Object? popResult) { + void handlePopBack({ + required Client client, + Object? popResult, + }) { Logs().d( "PinnedEventsController()::handlePopBack(): popResult: $popResult", ); if (popResult is List) { + if (popResult.isEmpty) { + return; + } final room = popResult.first?.room; if (room != null) { getPinnedMessageAction( - room: room, - isInitial: true, + roomId: room.id, + client: client, ); } } @@ -119,23 +137,24 @@ class PinnedEventsController { void jumpToCurrentMessage( List pinnedEvents, { + JumpToPinnedMessageCallback? jumpToPinnedMessageCallback, String? eventId, }) async { - final currentEvent = pinnedEvents.firstWhere( + final currentEvent = pinnedEvents.firstWhereOrNull( (event) => event?.eventId == eventId, ); + if (currentEvent == null) return; int index = pinnedEvents.indexOf(currentEvent); if (index == -1) { index = 0; } currentPinnedEventNotifier.value = currentEvent; - pinnedMessageScrollController.scrollToIndex(index); + jumpToPinnedMessageCallback?.call(index); } void dispose() { currentPinnedEventNotifier.dispose(); getPinnedMessageNotifier.dispose(); - pinnedMessageScrollController.dispose(); _pinnedEventsSubscription?.cancel(); } } diff --git a/lib/pages/chat/chat_pinned_events/pinned_events_view.dart b/lib/pages/chat/chat_pinned_events/pinned_events_view.dart index b0cb11bc16..20aab7ca7b 100644 --- a/lib/pages/chat/chat_pinned_events/pinned_events_view.dart +++ b/lib/pages/chat/chat_pinned_events/pinned_events_view.dart @@ -38,10 +38,6 @@ class PinnedEventsView extends StatelessWidget { switch (success.runtimeType) { case ChatGetPinnedEventsSuccess: final data = success as ChatGetPinnedEventsSuccess; - if (data.pinnedEvents.isEmpty) { - return child!; - } - return Material( color: LinagoraSysColors.material().onPrimary, child: InkWell( @@ -76,7 +72,6 @@ class PinnedEventsView extends StatelessWidget { .copyWith(scrollbars: false), child: ListView.separated( controller: controller - .pinnedEventsController .pinnedMessageScrollController, shrinkWrap: true, physics: @@ -95,7 +90,6 @@ class PinnedEventsView extends StatelessWidget { return _PinnedEventsIndicator( currentEvent: currentEvent, scrollController: controller - .pinnedEventsController .pinnedMessageScrollController, color: LinagoraSysColors.material() diff --git a/lib/pages/chat/chat_pinned_events/pinned_messages.dart b/lib/pages/chat/chat_pinned_events/pinned_messages.dart index 93415b62a0..c95806bf85 100644 --- a/lib/pages/chat/chat_pinned_events/pinned_messages.dart +++ b/lib/pages/chat/chat_pinned_events/pinned_messages.dart @@ -13,6 +13,8 @@ import 'package:fluffychat/utils/twake_snackbar.dart'; import 'package:fluffychat/widgets/mixins/popup_context_menu_action_mixin.dart'; import 'package:fluffychat/widgets/mixins/popup_menu_widget_mixin.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -200,10 +202,29 @@ class PinnedMessagesController extends State } } + void _initPinnedEvents() { + if (widget.pinnedEvents.isNotEmpty) { + eventsNotifier.value = widget.pinnedEvents; + } else { + final currentRoomId = GoRouterState.of(context).pathParameters['roomid']; + if (currentRoomId != null) { + context.go('/rooms/$currentRoomId'); + } + } + } + + void onClickBackButton() { + Navigator.of(context).pop( + eventsNotifier.value != widget.pinnedEvents ? eventsNotifier.value : null, + ); + } + @override void initState() { super.initState(); - eventsNotifier.value = widget.pinnedEvents; + SchedulerBinding.instance.addPostFrameCallback((_) async { + _initPinnedEvents(); + }); } @override diff --git a/lib/pages/chat/chat_pinned_events/pinned_messages_screen.dart b/lib/pages/chat/chat_pinned_events/pinned_messages_screen.dart index 9388d18343..d0b19f1ad8 100644 --- a/lib/pages/chat/chat_pinned_events/pinned_messages_screen.dart +++ b/lib/pages/chat/chat_pinned_events/pinned_messages_screen.dart @@ -39,7 +39,7 @@ class PinnedMessagesScreen extends StatelessWidget { leading: TwakeIconButton( tooltip: L10n.of(context)!.back, icon: Icons.arrow_back, - onTap: () => Navigator.of(context).pop(), + onTap: controller.onClickBackButton, ), actions: [ if (!responsiveUtils.isMobile(context))