Skip to content

Commit

Permalink
TW-898: fix error when unpin message in current selected pin
Browse files Browse the repository at this point in the history
  • Loading branch information
sherlockvn authored and hoangdat committed Dec 20, 2023
1 parent d4c2ffa commit 28d28a1
Show file tree
Hide file tree
Showing 15 changed files with 218 additions and 83 deletions.
2 changes: 1 addition & 1 deletion assets/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -2970,7 +2970,7 @@
"description": "Description",
"groupName": "Group name",
"descriptionHelper": "You can provide an optional description for your group.",
"groupNameCannotBeEmpty": "Group name cannot be empty"
"groupNameCannotBeEmpty": "Group name cannot be empty",
"sharedMediaAndFiles": "Shared media and files",
"unpinAllMessages": "Unpin all messages",
"pinnedMessagesTooltip": "Pinned messages",
Expand Down
19 changes: 19 additions & 0 deletions lib/config/go_routes/go_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,25 @@ abstract class AppRoutes {
),
redirect: loggedOutRedirect,
),
GoRoute(
path: 'pinnedmessages',
pageBuilder: (context, state) {
if (state.extra is PinnedEventsArgument) {
final arg = state.extra as PinnedEventsArgument;
return MaterialPage(
fullscreenDialog: true,
child: PinnedMessages(
pinnedEvents: arg.pinnedEvents,
timeline: arg.timeline,
),
);
}

return const CupertinoPage(
child: PinnedMessages(pinnedEvents: []),
);
},
),
],
),
],
Expand Down
4 changes: 4 additions & 0 deletions lib/di/global/get_it_initializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import 'package:fluffychat/domain/usecase/room/chat_room_search_interactor.dart'
import 'package:fluffychat/domain/usecase/room/create_new_group_chat_interactor.dart';
import 'package:fluffychat/domain/usecase/room/timeline_search_event_interactor.dart';
import 'package:fluffychat/domain/usecase/room/update_group_chat_interactor.dart';
import 'package:fluffychat/domain/usecase/room/update_pinned_messages_interactor.dart';
import 'package:fluffychat/domain/usecase/room/upload_content_interactor.dart';
import 'package:fluffychat/domain/usecase/room/upload_content_for_web_interactor.dart';
import 'package:fluffychat/domain/usecase/search/pre_search_recent_contacts_interactor.dart';
Expand Down Expand Up @@ -264,5 +265,8 @@ class GetItInitializer {
getIt.registerSingleton<UpdateGroupChatInteractor>(
UpdateGroupChatInteractor(),
);
getIt.registerLazySingleton<UpdatePinnedMessagesInteractor>(
() => UpdatePinnedMessagesInteractor(),
);
}
}
43 changes: 43 additions & 0 deletions lib/domain/app_state/room/update_pinned_events_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:fluffychat/app_state/failure.dart';
import 'package:fluffychat/app_state/success.dart';

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

class UpdatePinnedEventsSuccess extends Success {
final String eventId;

const UpdatePinnedEventsSuccess(this.eventId);

@override
List<Object?> get props => [eventId];
}

class UpdatePinnedEventsFailure extends Failure {
final Exception exception;

const UpdatePinnedEventsFailure(this.exception);

@override
List<Object?> get props => [exception];
}

class PinEventsFailure extends Failure {
final Object error;

const PinEventsFailure(this.error);

@override
List<Object?> get props => [error];
}

class UnpinEventsFailure extends Failure {
final Object error;

const UnpinEventsFailure(this.error);

@override
List<Object?> get props => [error];
}
4 changes: 4 additions & 0 deletions lib/domain/enums/pinned_messages_action_enum.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
enum PinnedMessagesActionEnum {
pin,
unpin,
}
39 changes: 39 additions & 0 deletions lib/domain/usecase/room/update_pinned_messages_interactor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:dartz/dartz.dart';
import 'package:fluffychat/app_state/failure.dart';
import 'package:fluffychat/app_state/success.dart';
import 'package:fluffychat/domain/app_state/room/update_pinned_events_state.dart';
import 'package:fluffychat/domain/enums/pinned_messages_action_enum.dart';
import 'package:matrix/matrix.dart';

class UpdatePinnedMessagesInteractor {
Stream<Either<Failure, Success>> execute({
required Room room,
required List<String> eventIds,
PinnedMessagesActionEnum action = PinnedMessagesActionEnum.unpin,
}) async* {
try {
yield Right(UpdatePinnedEventsInitial());
final currentPinnedEvents = room.pinnedEventIds.toSet();
if (action == PinnedMessagesActionEnum.pin) {
currentPinnedEvents.addAll(eventIds);
} else {
currentPinnedEvents.removeAll(eventIds);
}
final result = await room.setPinnedEvents(currentPinnedEvents.toList());
yield Right(UpdatePinnedEventsSuccess(result));
} on MatrixException catch (exception) {
Logs().e(
'UpdatePinnedMessagesInteractor::execute(): ErrorCode ${exception.errcode}: ${exception.errorMessage}',
);
yield Left(
UpdatePinnedEventsFailure(exception),
);
} catch (e) {
if (action == PinnedMessagesActionEnum.pin) {
yield Left(PinEventsFailure(e));
} else {
yield Left(UnpinEventsFailure(e));
}
}
}
}
13 changes: 9 additions & 4 deletions lib/pages/chat/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,8 @@ class ChatController extends State<Chat>
inputFocus.requestFocus();
}

Future<void> scrollToEventIdAndHighlight(String eventId) {
return scrollToEventId(eventId, highlight: true);
Future<void> scrollToEventIdAndHighlight(String eventId) async {
return await scrollToEventId(eventId, highlight: true);
}

Future<void>? loadTimelineFuture;
Expand Down Expand Up @@ -868,6 +868,11 @@ class ChatController extends State<Chat>
}
}

int getDisplayEventIndex(int eventIndex) {
const addedHeadItemsInChat = 1;
return eventIndex + addedHeadItemsInChat;
}

Future<void> scrollToEventId(String eventId, {bool highlight = false}) async {
final eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
if (eventIndex == -1) {
Expand All @@ -884,7 +889,7 @@ class ChatController extends State<Chat>
setState(() {});
return;
}
await scrollToIndex(eventIndex, highlight: highlight);
await scrollToIndex(getDisplayEventIndex(eventIndex), highlight: highlight);
_updateScrollController();
}

Expand All @@ -895,7 +900,7 @@ class ChatController extends State<Chat>
);
if (highlight) {
await scrollController.highlight(
index + 1,
index,
);
}
setState(() {});
Expand Down
26 changes: 16 additions & 10 deletions lib/pages/chat/chat_pinned_events/pinned_events_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ class PinnedEventsController {
final AutoScrollController pinnedMessageScrollController =
AutoScrollController();

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

final ValueNotifier<Either<Failure, Success>> getPinnedMessageNotifier =
ValueNotifier<Either<Failure, Success>>(
Expand All @@ -29,7 +28,7 @@ class PinnedEventsController {
StreamSubscription? _pinnedEventsSubscription;

bool isCurrentPinnedEvent(Event event) {
return isCurrentPinnedEventNotifier.value?.eventId == event.eventId;
return currentPinnedEventNotifier.value?.eventId == event.eventId;
}

void getPinnedMessageAction({
Expand Down Expand Up @@ -71,7 +70,7 @@ class PinnedEventsController {
"PinnedEventsController()::jumpToPinnedMessage(): eventID: ${event?.eventId}",
);
if (event != null) {
isCurrentPinnedEventNotifier.value = event;
currentPinnedEventNotifier.value = event;
pinnedMessageScrollController.scrollToIndex(nextIndex);
if (scrollToEventId != null) {
scrollToEventId.call(event.eventId);
Expand All @@ -80,9 +79,13 @@ class PinnedEventsController {
}

int currentIndexOfPinnedMessage(List<Event?> pinnedEvents) {
return pinnedEvents.indexWhere(
(event) => event?.eventId == isCurrentPinnedEventNotifier.value?.eventId,
final index = pinnedEvents.indexWhere(
(event) => event?.eventId == currentPinnedEventNotifier.value?.eventId,
);
if (index < 0) {
currentPinnedEventNotifier.value = pinnedEvents.first;
}
return index;
}

int _nextIndexOfPinnedMessage(List<Event?> pinnedEvents) {
Expand All @@ -93,7 +96,7 @@ class PinnedEventsController {
}

void initialPinnedMessage(List<Event?> pinnedEvents) {
isCurrentPinnedEventNotifier.value = pinnedEvents.last;
currentPinnedEventNotifier.value = pinnedEvents.last;
pinnedMessageScrollController.scrollToIndex(
pinnedEvents.length - 1,
);
Expand Down Expand Up @@ -121,13 +124,16 @@ class PinnedEventsController {
final currentEvent = pinnedEvents.firstWhere(
(event) => event?.eventId == eventId,
);
final index = pinnedEvents.indexOf(currentEvent);
isCurrentPinnedEventNotifier.value = currentEvent;
int index = pinnedEvents.indexOf(currentEvent);
if (index == -1) {
index = 0;
}
currentPinnedEventNotifier.value = currentEvent;
pinnedMessageScrollController.scrollToIndex(index);
}

void dispose() {
isCurrentPinnedEventNotifier.dispose();
currentPinnedEventNotifier.dispose();
getPinnedMessageNotifier.dispose();
pinnedMessageScrollController.dispose();
_pinnedEventsSubscription?.cancel();
Expand Down
17 changes: 9 additions & 8 deletions lib/pages/chat/chat_pinned_events/pinned_events_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class PinnedEventsView extends StatelessWidget {
children: [
ValueListenableBuilder(
valueListenable: controller.pinnedEventsController
.isCurrentPinnedEventNotifier,
.currentPinnedEventNotifier,
builder: (context, currentEvent, child) {
if (currentEvent == null) return child!;
return Expanded(
Expand Down Expand Up @@ -115,14 +115,15 @@ class PinnedEventsView extends StatelessWidget {
),
),
),
_PinnedEventsContentWidget(
countPinnedEvents: controller
.pinnedEventsController
.currentIndexOfPinnedMessage(
data.pinnedEvents.reversed.toList(),
if (data.pinnedEvents.isNotEmpty)
_PinnedEventsContentWidget(
countPinnedEvents: controller
.pinnedEventsController
.currentIndexOfPinnedMessage(
data.pinnedEvents.reversed.toList(),
),
currentEvent: currentEvent,
),
currentEvent: currentEvent,
),
TwakeIconButton(
tooltip: L10n.of(context)!
.pinnedMessagesTooltip,
Expand Down
56 changes: 44 additions & 12 deletions lib/pages/chat/chat_pinned_events/pinned_messages.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import 'package:fluffychat/di/global/get_it_initializer.dart';
import 'package:fluffychat/domain/app_state/room/update_pinned_events_state.dart';
import 'package:fluffychat/domain/usecase/room/update_pinned_messages_interactor.dart';
import 'package:fluffychat/pages/chat/chat_pinned_events/pinned_messages_screen.dart';
import 'package:fluffychat/presentation/model/chat/pop_up_menu_item_model.dart';
import 'package:fluffychat/resource/image_paths.dart';
import 'package:fluffychat/utils/extension/build_context_extension.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/twake_snackbar.dart';
import 'package:fluffychat/widgets/mixins/popup_context_menu_action_mixin.dart';
Expand Down Expand Up @@ -29,23 +33,19 @@ class PinnedMessagesController extends State<PinnedMessages>

final ScrollController scrollController = ScrollController();

final updatePinnedMessagesInteractor =
getIt.get<UpdatePinnedMessagesInteractor>();

Room? get room => widget.pinnedEvents.first?.room;

List<ContextMenuItemModel> get pinnedMessagesActionsList => [
ContextMenuItemModel(
text: L10n.of(context)!.unpin,
imagePath: ImagePaths.icUnpin,
color: Theme.of(context).colorScheme.onSurface,
onTap: ({extra}) async {
if (extra is Event) {
final result = await extra.unpin();
if (result) {
eventsNotifier.value.remove(extra);
eventsNotifier.value = List.from(eventsNotifier.value);
if (eventsNotifier.value.isEmpty) {
Navigator.of(context).pop(eventsNotifier.value);
}
} else {
TwakeSnackBar.show(context, L10n.of(context)!.failedToUnpin);
}
unpin(extra.eventId);
}
},
),
Expand All @@ -71,9 +71,41 @@ class PinnedMessagesController extends State<PinnedMessages>
),
];

void unpin(String eventId) {
updatePinnedMessagesInteractor
.execute(room: room!, eventIds: [eventId]).listen((event) {
event.fold((failure) {
if (failure is UnpinEventsFailure) {
TwakeSnackBar.show(context, L10n.of(context)!.failedToUnpin);
} else if (failure is UpdatePinnedEventsFailure) {
TwakeSnackBar.show(
context,
failure.exception.toLocalizedString(context),
);
}
}, (success) {
if (success is UpdatePinnedEventsSuccess) {
eventsNotifier.value
.removeWhere((element) => element?.eventId == eventId);
eventsNotifier.value = List.from(eventsNotifier.value);
if (eventsNotifier.value.isEmpty) {
Navigator.of(context).pop(eventsNotifier.value);
}
}
});
});
}

void unpinAll() {
widget.pinnedEvents.first?.room.setPinnedEvents([]);
Navigator.of(context).pop();
updatePinnedMessagesInteractor
.execute(room: room!, eventIds: []).listen((event) {
if (event is UpdatePinnedEventsSuccess) {
eventsNotifier.value = [];
Navigator.of(context).pop();
} else if (event is UnpinEventsFailure) {
TwakeSnackBar.show(context, L10n.of(context)!.failedToUnpin);
}
});
}

void handleContextMenuActionInMore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/responsive/responsive_utils.dart';
import 'package:fluffychat/widgets/context_menu/context_menu_area.dart';
import 'package:fluffychat/widgets/context_menu/twake_context_menu_area.dart';
import 'package:fluffychat/widgets/twake_components/twake_icon_button.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
Expand Down Expand Up @@ -73,7 +73,7 @@ class MessageContentWithTimestampBuilder extends StatelessWidget {
children: [
if (event.isOwnMessage)
_menuActionsRowBuilder(context, event.isOwnMessage),
ContextMenuArea(
TwakeContextMenuArea(
width: MessageStyle.contextMenuWidth,
builder: menuChildren != null
? (context) => menuChildren!.call(context)
Expand Down
2 changes: 1 addition & 1 deletion lib/pages/chat/events/message/message_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,5 @@ class MessageStyle {

static const double paddingAllPushpin = 0;

static const double contextMenuWidth = 250;
static const double contextMenuWidth = 200;
}
Loading

0 comments on commit 28d28a1

Please sign in to comment.