From 517091faf6358f2d37f80f229585ae51d0864983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=B9i=20Trung=20Hi=E1=BA=BFu?= Date: Wed, 17 Jul 2024 15:27:12 +0700 Subject: [PATCH] TW-1941: Update quick actions (#1944) * TW-1941: Update quicks actions * TW-1941: Separate chat list slidable actions * TW-1941: Write test for `getSlidables` --- .../chat_custom_slidable_action.dart | 46 +++ lib/pages/chat_list/chat_list.dart | 58 ++++ .../chat_list/chat_list_view_builder.dart | 189 +---------- lib/pages/chat_list/chat_list_view_style.dart | 12 +- .../chat_list/common_chat_list_item.dart | 56 ++++ .../chat_list/slidable_chat_list_item.dart | 31 ++ test/pages/chat_list/chat_list_test.dart | 294 ++++++++++++++++++ 7 files changed, 497 insertions(+), 189 deletions(-) create mode 100644 lib/pages/chat_list/chat_custom_slidable_action.dart create mode 100644 lib/pages/chat_list/common_chat_list_item.dart create mode 100644 lib/pages/chat_list/slidable_chat_list_item.dart create mode 100644 test/pages/chat_list/chat_list_test.dart diff --git a/lib/pages/chat_list/chat_custom_slidable_action.dart b/lib/pages/chat_list/chat_custom_slidable_action.dart new file mode 100644 index 0000000000..c19adb2192 --- /dev/null +++ b/lib/pages/chat_list/chat_custom_slidable_action.dart @@ -0,0 +1,46 @@ +import 'package:fluffychat/pages/chat_list/chat_list_view_style.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +class ChatCustomSlidableAction extends StatelessWidget { + const ChatCustomSlidableAction({ + super.key, + required this.icon, + required this.label, + required this.onPressed, + required this.backgroundColor, + required this.foregroundColor, + }); + + final Widget icon; + final String label; + final SlidableActionCallback? onPressed; + final Color backgroundColor; + final Color foregroundColor; + + @override + Widget build(BuildContext context) { + return CustomSlidableAction( + autoClose: true, + padding: ChatListViewStyle.slidablePadding, + onPressed: onPressed, + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + const SizedBox(height: ChatListViewStyle.slidableIconTextGap), + Text( + label, + style: Theme.of(context).textTheme.labelMedium?.copyWith( + color: foregroundColor, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ); + } +} diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 2f2905aed1..b3511394dc 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -8,6 +8,8 @@ import 'package:fluffychat/di/global/dio_cache_interceptor_for_client.dart'; import 'package:fluffychat/di/global/get_it_initializer.dart'; import 'package:fluffychat/domain/model/room/room_extension.dart'; import 'package:fluffychat/pages/bootstrap/bootstrap_dialog.dart'; +import 'package:fluffychat/pages/chat_list/chat_custom_slidable_action.dart'; +import 'package:fluffychat/pages/chat_list/chat_list_view_style.dart'; import 'package:fluffychat/presentation/mixins/comparable_presentation_contact_mixin.dart'; import 'package:fluffychat/pages/bootstrap/tom_bootstrap_dialog.dart'; import 'package:fluffychat/pages/chat_list/chat_list_view.dart'; @@ -16,6 +18,7 @@ import 'package:fluffychat/presentation/enum/chat_list/chat_list_enum.dart'; import 'package:fluffychat/presentation/extensions/client_extension.dart'; import 'package:fluffychat/presentation/mixins/go_to_group_chat_mixin.dart'; import 'package:fluffychat/presentation/model/chat_list/chat_selection_actions.dart'; +import 'package:fluffychat/resource/image_paths.dart'; import 'package:fluffychat/utils/dialog/twake_dialog.dart'; import 'package:fluffychat/utils/extension/build_context_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -34,6 +37,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; @@ -797,6 +801,60 @@ class ChatListController extends State } } + List getSlidables(BuildContext context, Room room) { + return [ + if (!room.isInvitation) + ChatCustomSlidableAction( + label: + room.isUnread ? L10n.of(context)!.read : L10n.of(context)!.unread, + icon: Icon( + room.isUnread + ? Icons.mark_chat_read_outlined + : Icons.mark_chat_unread_outlined, + size: ChatListViewStyle.slidableIconSize, + ), + onPressed: (_) => toggleRead(room), + foregroundColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: ChatListViewStyle.readSlidableColor(room.isUnread)!, + ), + ChatCustomSlidableAction( + label: room.isMuted ? L10n.of(context)!.unmute : L10n.of(context)!.mute, + icon: Icon( + room.isMuted + ? Icons.notifications_on_outlined + : Icons.notifications_off_outlined, + size: ChatListViewStyle.slidableIconSize, + ), + onPressed: (_) => toggleMuteRoom(room), + foregroundColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: ChatListViewStyle.muteSlidableColor(room.isMuted)!, + ), + if (!room.isInvitation) + ChatCustomSlidableAction( + label: room.isFavourite + ? L10n.of(context)!.unpin + : L10n.of(context)!.pin, + icon: room.isFavourite + ? SvgPicture.asset( + ImagePaths.icUnpin, + width: ChatListViewStyle.slidableIconSize, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onPrimary, + BlendMode.srcIn, + ), + ) + : const Icon( + Icons.push_pin_outlined, + size: ChatListViewStyle.slidableIconSize, + ), + onPressed: (_) => togglePin(room), + foregroundColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: + ChatListViewStyle.pinSlidableColor(room.isFavourite)!, + ), + ]; + } + @override void dispose() { scrollController.removeListener(_onScroll); diff --git a/lib/pages/chat_list/chat_list_view_builder.dart b/lib/pages/chat_list/chat_list_view_builder.dart index 214548e645..eee9f8b109 100644 --- a/lib/pages/chat_list/chat_list_view_builder.dart +++ b/lib/pages/chat_list/chat_list_view_builder.dart @@ -1,15 +1,10 @@ -import 'package:fluffychat/domain/model/room/room_extension.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; -import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; -import 'package:collection/collection.dart'; import 'package:fluffychat/pages/chat_list/chat_list_view_style.dart'; +import 'package:fluffychat/pages/chat_list/common_chat_list_item.dart'; +import 'package:fluffychat/pages/chat_list/slidable_chat_list_item.dart'; import 'package:fluffychat/presentation/enum/chat_list/chat_list_enum.dart'; -import 'package:fluffychat/resource/image_paths.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:matrix/matrix.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; class ChatListViewBuilder extends StatelessWidget { final ChatListController controller; @@ -31,20 +26,20 @@ class ChatListViewBuilder extends StatelessWidget { return ValueListenableBuilder( valueListenable: controller.selectModeNotifier, builder: (context, selectMode, _) { - final slidables = _getSlidables(context, rooms[index]); + final slidables = controller.getSlidables(context, rooms[index]); if (ChatListViewStyle.responsiveUtils.isMobileOrTablet(context) && !selectMode.isSelectMode && slidables.isNotEmpty) { - return _SlidableChatListItem( + return SlidableChatListItem( controller: controller, slidables: slidables, - chatListItem: _CommonChatListItem( + chatListItem: CommonChatListItem( controller: controller, room: rooms[index], ), ); } - return _CommonChatListItem( + return CommonChatListItem( controller: controller, room: rooms[index], ); @@ -53,176 +48,4 @@ class ChatListViewBuilder extends StatelessWidget { }, ); } - - List _getSlidables(BuildContext context, Room room) { - return [ - if (!room.isInvitation) - _ChatCustomSlidableAction( - label: - room.isUnread ? L10n.of(context)!.read : L10n.of(context)!.unread, - icon: Icon( - room.isUnread - ? Icons.mark_chat_read_outlined - : Icons.mark_chat_unread_outlined, - size: ChatListViewStyle.slidableIconSize, - ), - onPressed: (_) => controller.toggleRead(room), - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: ChatListViewStyle.readSlidableColor(room.isUnread)!, - ), - _ChatCustomSlidableAction( - label: room.isMuted ? L10n.of(context)!.unmute : L10n.of(context)!.mute, - icon: Icon( - room.isMuted - ? Icons.notifications_off_outlined - : Icons.notifications_on_outlined, - size: ChatListViewStyle.slidableIconSize, - ), - onPressed: (_) => controller.toggleMuteRoom(room), - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: ChatListViewStyle.muteSlidableColor(room.isMuted)!, - ), - if (!room.isInvitation) - _ChatCustomSlidableAction( - label: room.isFavourite - ? L10n.of(context)!.unpin - : L10n.of(context)!.pin, - icon: room.isFavourite - ? SvgPicture.asset( - ImagePaths.icUnpin, - width: ChatListViewStyle.slidableIconSize, - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.onPrimary, - BlendMode.srcIn, - ), - ) - : const Icon( - Icons.push_pin_outlined, - size: ChatListViewStyle.slidableIconSize, - ), - onPressed: (_) => controller.togglePin(room), - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: - ChatListViewStyle.pinSlidableColor(room.isFavourite)!, - ), - ]; - } -} - -class _ChatCustomSlidableAction extends StatelessWidget { - const _ChatCustomSlidableAction({ - required this.icon, - required this.label, - required this.onPressed, - required this.backgroundColor, - required this.foregroundColor, - }); - - final Widget icon; - final String label; - final SlidableActionCallback? onPressed; - final Color backgroundColor; - final Color foregroundColor; - - @override - Widget build(BuildContext context) { - return CustomSlidableAction( - autoClose: true, - padding: ChatListViewStyle.slidablePadding, - onPressed: onPressed, - backgroundColor: backgroundColor, - foregroundColor: foregroundColor, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - icon, - const SizedBox(height: ChatListViewStyle.slidableIconTextGap), - Text( - label, - style: Theme.of(context).textTheme.labelMedium?.copyWith( - color: foregroundColor, - ), - overflow: TextOverflow.ellipsis, - ), - ], - ), - ); - } -} - -class _CommonChatListItem extends StatelessWidget { - const _CommonChatListItem({ - required this.controller, - required this.room, - }); - - final ChatListController controller; - final Room room; - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: controller.widget.activeRoomIdNotifier, - builder: (context, activeRoomId, child) { - return ChatListItem( - room, - key: Key('chat_list_item_${room.id}'), - isEnableSelectMode: controller.isSelectMode, - onTap: controller.isSelectMode - ? () => controller.toggleSelection(room.id) - : null, - onSecondaryTapDown: (detail) => controller.handleContextMenuAction( - context, - room, - detail, - ), - onLongPress: () => controller.onLongPressChatListItem( - room, - ), - checkBoxWidget: ValueListenableBuilder( - valueListenable: controller.conversationSelectionNotifier, - builder: (context, conversationSelection, __) { - final conversation = conversationSelection.firstWhereOrNull( - (conversation) => conversation.roomId.contains(room.id), - ); - return Checkbox( - value: conversation?.isSelected == true, - onChanged: (_) { - controller.toggleSelection(room.id); - }, - ); - }, - ), - activeChat: activeRoomId == room.id, - ); - }, - ); - } -} - -class _SlidableChatListItem extends StatelessWidget { - const _SlidableChatListItem({ - required this.controller, - required this.slidables, - required this.chatListItem, - }); - - final ChatListController controller; - final List slidables; - final Widget chatListItem; - - @override - Widget build(BuildContext context) { - return Slidable( - // Slidables must have the same groupTag for SlidableAutoCloseBehavior to work properly - groupTag: 'slidable_list', - endActionPane: ActionPane( - motion: const ScrollMotion(), - extentRatio: ChatListViewStyle.slidableExtentRatio(slidables.length), - children: slidables, - ), - child: chatListItem, - ); - } } diff --git a/lib/pages/chat_list/chat_list_view_style.dart b/lib/pages/chat_list/chat_list_view_style.dart index a104a66b7b..6289134c67 100644 --- a/lib/pages/chat_list/chat_list_view_style.dart +++ b/lib/pages/chat_list/chat_list_view_style.dart @@ -23,19 +23,19 @@ class ChatListViewStyle { static const double slidableIconSize = 24.0; static Color? pinSlidableColor(bool isFavourite) { return isFavourite - ? LinagoraRefColors.material().neutral[70] - : Colors.greenAccent[700]; + ? LinagoraRefColors.material().tertiary[40] + : Colors.tealAccent[700]; } static Color? readSlidableColor(bool isUnread) { return isUnread - ? LinagoraRefColors.material().neutral[70] - : LinagoraRefColors.material().primary[40]; + ? LinagoraRefColors.material().tertiary[40] + : Colors.deepPurpleAccent[200]; } static Color? muteSlidableColor(bool isMuted) { return isMuted - ? LinagoraRefColors.material().primary[20] - : Colors.amber[700]; + ? LinagoraRefColors.material().primary[50] + : LinagoraRefColors.material().primary[40]; } } diff --git a/lib/pages/chat_list/common_chat_list_item.dart b/lib/pages/chat_list/common_chat_list_item.dart new file mode 100644 index 0000000000..d74cb7f1fc --- /dev/null +++ b/lib/pages/chat_list/common_chat_list_item.dart @@ -0,0 +1,56 @@ +import 'package:collection/collection.dart'; +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; + +class CommonChatListItem extends StatelessWidget { + final ChatListController controller; + final Room room; + + const CommonChatListItem({ + super.key, + required this.controller, + required this.room, + }); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: controller.widget.activeRoomIdNotifier, + builder: (context, activeRoomId, child) { + return ChatListItem( + room, + key: Key('chat_list_item_${room.id}'), + isEnableSelectMode: controller.isSelectMode, + onTap: controller.isSelectMode + ? () => controller.toggleSelection(room.id) + : null, + onSecondaryTapDown: (detail) => controller.handleContextMenuAction( + context, + room, + detail, + ), + onLongPress: () => controller.onLongPressChatListItem( + room, + ), + checkBoxWidget: ValueListenableBuilder( + valueListenable: controller.conversationSelectionNotifier, + builder: (context, conversationSelection, __) { + final conversation = conversationSelection.firstWhereOrNull( + (conversation) => conversation.roomId.contains(room.id), + ); + return Checkbox( + value: conversation?.isSelected == true, + onChanged: (_) { + controller.toggleSelection(room.id); + }, + ); + }, + ), + activeChat: activeRoomId == room.id, + ); + }, + ); + } +} diff --git a/lib/pages/chat_list/slidable_chat_list_item.dart b/lib/pages/chat_list/slidable_chat_list_item.dart new file mode 100644 index 0000000000..8a22e86308 --- /dev/null +++ b/lib/pages/chat_list/slidable_chat_list_item.dart @@ -0,0 +1,31 @@ +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pages/chat_list/chat_list_view_style.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +class SlidableChatListItem extends StatelessWidget { + const SlidableChatListItem({ + super.key, + required this.controller, + required this.slidables, + required this.chatListItem, + }); + + final ChatListController controller; + final List slidables; + final Widget chatListItem; + + @override + Widget build(BuildContext context) { + return Slidable( + // Slidables must have the same groupTag for SlidableAutoCloseBehavior to work properly + groupTag: 'slidable_list', + endActionPane: ActionPane( + motion: const ScrollMotion(), + extentRatio: ChatListViewStyle.slidableExtentRatio(slidables.length), + children: slidables, + ), + child: chatListItem, + ); + } +} diff --git a/test/pages/chat_list/chat_list_test.dart b/test/pages/chat_list/chat_list_test.dart new file mode 100644 index 0000000000..458afbffdc --- /dev/null +++ b/test/pages/chat_list/chat_list_test.dart @@ -0,0 +1,294 @@ +import 'package:fluffychat/config/localizations/localization_service.dart'; +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pages/chat_list/chat_custom_slidable_action.dart'; +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/utils/custom_scroll_behaviour.dart'; +import 'package:fluffychat/utils/responsive/responsive_utils.dart'; +import 'package:fluffychat/widgets/theme_builder.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_localized_locales/flutter_localized_locales.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:linagora_design_flutter/colors/linagora_ref_colors.dart'; +import 'package:matrix/matrix.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +import 'chat_list_test.mocks.dart'; + +@GenerateNiceMocks([ + MockSpec(), +]) +void main() { + const double iconSize = 24.0; + late final Room room; + setUpAll(() { + final getIt = GetIt.instance; + getIt.registerSingleton(ResponsiveUtils()); + room = MockRoom(); + }); + + Future> makeTestable(WidgetTester tester) async { + late List slideActions; + await tester.pumpWidget( + ThemeBuilder( + builder: (context, themeMode, primaryColor) => MaterialApp( + locale: const Locale('en'), + scrollBehavior: CustomScrollBehavior(), + localizationsDelegates: const [ + LocaleNamesLocalizationsDelegate(), + L10n.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: LocalizationService.supportedLocales, + theme: TwakeThemes.buildTheme( + context, + Brightness.light, + primaryColor, + ), + home: Scaffold( + body: Builder( + builder: (context) { + final controller = ChatListController(); + slideActions = controller.getSlidables(context, room); + return const SizedBox(); + }, + ), + ), + ), + ), + ); + + await tester.pump(); + return slideActions; + } + + group('[ChatList] TEST', () { + group('[getSlidables] TEST', () { + group('GIVEN room is invitation', () { + testWidgets( + 'GIVEN room is muted\n' + 'THEN return unmute action ', + (WidgetTester tester) async { + when(room.membership).thenReturn(Membership.invite); + when(room.pushRuleState).thenReturn(PushRuleState.dontNotify); + + final List slideActions = await makeTestable(tester); + + expect(slideActions, isNotNull); + expect(slideActions.length, 1); + expect(slideActions[0], isA()); + + final unmuteAction = slideActions[0] as ChatCustomSlidableAction; + expect(unmuteAction.label, isA()); + expect(unmuteAction.label, 'Unmute'); + expect(unmuteAction.icon, isA()); + expect( + (unmuteAction.icon as Icon).icon, + equals(Icons.notifications_on_outlined), + ); + expect( + (unmuteAction.icon as Icon).size, + equals(iconSize), + ); + expect(unmuteAction.backgroundColor, isA()); + expect( + unmuteAction.backgroundColor, + equals(LinagoraRefColors.material().primary[50]), + ); + }, + ); + + testWidgets( + 'GIVEN room is not muted\n' + 'THEN return mute action ', + (WidgetTester tester) async { + when(room.membership).thenReturn(Membership.invite); + when(room.pushRuleState).thenReturn(PushRuleState.notify); + + final List slideActions = await makeTestable(tester); + + expect(slideActions, isNotNull); + expect(slideActions.length, 1); + expect(slideActions[0], isA()); + + final unmuteAction = slideActions[0] as ChatCustomSlidableAction; + expect(unmuteAction.label, isA()); + expect(unmuteAction.label, 'Mute'); + expect(unmuteAction.icon, isA()); + expect( + (unmuteAction.icon as Icon).icon, + equals(Icons.notifications_off_outlined), + ); + expect( + (unmuteAction.icon as Icon).size, + equals(iconSize), + ); + expect(unmuteAction.backgroundColor, isA()); + expect( + unmuteAction.backgroundColor, + equals(LinagoraRefColors.material().primary[40]), + ); + }, + ); + }); + + group('GIVEN room is not invitation', () { + testWidgets( + 'GIVEN room is unread\n' + 'AND room is muted\n' + 'AND room is favourite\n' + 'THEN return read, unmute, and unpin actions\n', + (WidgetTester tester) async { + when(room.membership).thenReturn(Membership.join); + when(room.isUnread).thenReturn(true); + when(room.pushRuleState).thenReturn(PushRuleState.dontNotify); + when(room.isFavourite).thenReturn(true); + + final List slideActions = await makeTestable(tester); + + expect(slideActions, isNotNull); + expect(slideActions.length, 3); + expect(slideActions[0], isA()); + expect(slideActions[1], isA()); + expect(slideActions[2], isA()); + + final readAction = slideActions[0] as ChatCustomSlidableAction; + expect(readAction.label, isA()); + expect(readAction.label, 'Read'); + expect(readAction.icon, isA()); + expect( + (readAction.icon as Icon).icon, + equals(Icons.mark_chat_read_outlined), + ); + expect( + (readAction.icon as Icon).size, + equals(iconSize), + ); + expect(readAction.backgroundColor, isA()); + expect( + readAction.backgroundColor, + equals(LinagoraRefColors.material().tertiary[40]), + ); + + final unmuteAction = slideActions[1] as ChatCustomSlidableAction; + expect(unmuteAction.label, isA()); + expect(unmuteAction.label, 'Unmute'); + expect(unmuteAction.icon, isA()); + expect( + (unmuteAction.icon as Icon).icon, + equals(Icons.notifications_on_outlined), + ); + expect( + (unmuteAction.icon as Icon).size, + equals(iconSize), + ); + expect(unmuteAction.backgroundColor, isA()); + expect( + unmuteAction.backgroundColor, + equals(LinagoraRefColors.material().primary[50]), + ); + + final unpinAction = slideActions[2] as ChatCustomSlidableAction; + expect(unpinAction.label, isA()); + expect(unpinAction.label, 'Unpin'); + expect(unpinAction.icon, isA()); + expect( + (unpinAction.icon as SvgPicture).toString(), + contains('assets/images/ic_unpin.svg'), + ); + expect( + (unpinAction.icon as SvgPicture).width, + equals(iconSize), + ); + expect(unpinAction.backgroundColor, isA()); + expect( + unpinAction.backgroundColor, + equals(LinagoraRefColors.material().tertiary[40]), + ); + }, + ); + + testWidgets( + 'GIVEN room is not unread\n' + 'AND room is not muted\n' + 'AND room is not favourite\n' + 'THEN return unread, mute and pin actions\n', + (WidgetTester tester) async { + when(room.membership).thenReturn(Membership.join); + when(room.isUnread).thenReturn(false); + when(room.pushRuleState).thenReturn(PushRuleState.notify); + when(room.isFavourite).thenReturn(false); + + final List slideActions = await makeTestable(tester); + + expect(slideActions, isNotNull); + expect(slideActions.length, 3); + expect(slideActions[0], isA()); + expect(slideActions[1], isA()); + expect(slideActions[2], isA()); + + final unreadAction = slideActions[0] as ChatCustomSlidableAction; + expect(unreadAction.label, isA()); + expect(unreadAction.label, 'Unread'); + expect(unreadAction.icon, isA()); + expect( + (unreadAction.icon as Icon).icon, + equals(Icons.mark_chat_unread_outlined), + ); + expect( + (unreadAction.icon as Icon).size, + equals(iconSize), + ); + expect(unreadAction.backgroundColor, isA()); + expect( + unreadAction.backgroundColor, + equals(Colors.deepPurpleAccent[200]), + ); + + final muteAction = slideActions[1] as ChatCustomSlidableAction; + expect(muteAction.label, isA()); + expect(muteAction.label, 'Mute'); + expect(muteAction.icon, isA()); + expect( + (muteAction.icon as Icon).icon, + equals(Icons.notifications_off_outlined), + ); + expect( + (muteAction.icon as Icon).size, + equals(iconSize), + ); + expect(muteAction.backgroundColor, isA()); + expect( + muteAction.backgroundColor, + equals(LinagoraRefColors.material().primary[40]), + ); + + final pinAction = slideActions[2] as ChatCustomSlidableAction; + expect(pinAction.label, isA()); + expect(pinAction.label, 'Pin'); + expect(pinAction.icon, isA()); + expect( + (pinAction.icon as Icon).icon, + equals(Icons.push_pin_outlined), + ); + expect( + (pinAction.icon as Icon).size, + equals(iconSize), + ); + expect(pinAction.backgroundColor, isA()); + expect( + pinAction.backgroundColor, + equals(Colors.tealAccent[700]), + ); + }, + ); + }); + }); + }); +}