Skip to content

Commit

Permalink
TW-1283: Back to chat when refresh pinned message page
Browse files Browse the repository at this point in the history
  • Loading branch information
nqhhdev authored and hoangdat committed Jan 17, 2024
1 parent 2ba2378 commit 892aa23
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 40 deletions.
8 changes: 8 additions & 0 deletions lib/di/global/get_it_initializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -94,6 +95,7 @@ class GetItInitializer {
bindingDatasourceImpl();
bindingRepositories();
bindingInteractor();
_bindingControllers();
}

void bindingGlobal() {
Expand Down Expand Up @@ -273,4 +275,10 @@ class GetItInitializer {
() => SendFilesOnWebWithCaptionInteractor(),
);
}

void _bindingControllers() {
getIt.registerFactory<PinnedEventsController>(
() => PinnedEventsController(),
);
}
}
5 changes: 5 additions & 0 deletions lib/domain/app_state/room/chat_get_pinned_events_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ class ChatGetPinnedEventsNoResult extends Failure {
List<Object?> get props => [];
}

class CannotGetPinnedMessages extends Failure {
@override
List<Object?> get props => [];
}

class ChatGetPinnedEventsFailure extends Failure {
final dynamic exception;

Expand Down
24 changes: 21 additions & 3 deletions lib/domain/usecase/room/chat_get_pinned_events_interactor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,32 @@ import 'package:matrix/matrix.dart';

class ChatGetPinnedEventsInteractor {
Stream<Either<Failure, Success>> 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),
))
Expand Down
47 changes: 37 additions & 10 deletions lib/pages/chat/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class ChatController extends State<Chat>

final getPinnedMessageInteractor = getIt.get<ChatGetPinnedEventsInteractor>();

PinnedEventsController pinnedEventsController = PinnedEventsController();
final pinnedEventsController = getIt.get<PinnedEventsController>();

final ValueKey chatComposerTypeAheadKey =
const ValueKey('chatComposerTypeAheadKey');
Expand Down Expand Up @@ -159,6 +159,9 @@ class ChatController extends State<Chat>
final FocusSuggestionController _focusSuggestionController =
FocusSuggestionController();

final AutoScrollController pinnedMessageScrollController =
AutoScrollController();

final TextEditingController _captionsController = TextEditingController();

FocusNode inputFocus = FocusNode();
Expand Down Expand Up @@ -361,9 +364,7 @@ class ChatController extends State<Chat>
void _tryLoadTimeline() async {
loadTimelineFuture = _getTimeline();
try {
await loadTimelineFuture?.then((_) {
_initializePinnedEvents();
});
await loadTimelineFuture;
final fullyRead = room?.fullyRead;
if (fullyRead == null || fullyRead.isEmpty || fullyRead == '') {
setReadMarker();
Expand Down Expand Up @@ -1114,9 +1115,10 @@ class ChatController extends State<Chat>
await TwakeDialog.showFutureLoadingDialogFullScreen(
future: () => room.setPinnedEvents(pinnedEventIds),
);
pinnedEventsController.getPinnedMessageAction(
isInitial: unpin,
room: room,
_getPinnedEvents(
isUnpin: unpin,
roomId: room.id,
client: room.client,
eventId: selectedEventIds,
);
}
Expand Down Expand Up @@ -1498,19 +1500,44 @@ class ChatController extends State<Chat>
if (popResult is Event) {
scrollToEventIdAndHighlight(popResult.eventId);
} else if (popResult is List<Event?>) {
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);
Expand Down
57 changes: 38 additions & 19 deletions lib/pages/chat/chat_pinned_events/pinned_events_controller.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<ChatGetPinnedEventsInteractor>();

final AutoScrollController pinnedMessageScrollController =
AutoScrollController();

final ValueNotifier<Event?> currentPinnedEventNotifier = ValueNotifier(null);

final ValueNotifier<Either<Failure, Success>> getPinnedMessageNotifier =
Expand All @@ -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,
);
}
}
Expand All @@ -71,7 +79,6 @@ class PinnedEventsController {
);
if (event != null) {
currentPinnedEventNotifier.value = event;
pinnedMessageScrollController.scrollToIndex(nextIndex);
if (scrollToEventId != null) {
scrollToEventId.call(event.eventId);
}
Expand All @@ -95,47 +102,59 @@ class PinnedEventsController {
return index;
}

void initialPinnedMessage(List<Event?> pinnedEvents) {
void updatePinnedMessage(
List<Event?> 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<Event?>) {
if (popResult.isEmpty) {
return;
}
final room = popResult.first?.room;
if (room != null) {
getPinnedMessageAction(
room: room,
isInitial: true,
roomId: room.id,
client: client,
);
}
}
}

void jumpToCurrentMessage(
List<Event?> 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();
}
}
6 changes: 0 additions & 6 deletions lib/pages/chat/chat_pinned_events/pinned_events_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -76,7 +72,6 @@ class PinnedEventsView extends StatelessWidget {
.copyWith(scrollbars: false),
child: ListView.separated(
controller: controller
.pinnedEventsController
.pinnedMessageScrollController,
shrinkWrap: true,
physics:
Expand All @@ -95,7 +90,6 @@ class PinnedEventsView extends StatelessWidget {
return _PinnedEventsIndicator(
currentEvent: currentEvent,
scrollController: controller
.pinnedEventsController
.pinnedMessageScrollController,
color:
LinagoraSysColors.material()
Expand Down
23 changes: 22 additions & 1 deletion lib/pages/chat/chat_pinned_events/pinned_messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -200,10 +202,29 @@ class PinnedMessagesController extends State<PinnedMessages>
}
}

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down

0 comments on commit 892aa23

Please sign in to comment.