Skip to content

Commit

Permalink
TW-1941: Update quick actions (#1944)
Browse files Browse the repository at this point in the history
* TW-1941: Update quicks actions

* TW-1941: Separate chat list slidable actions

* TW-1941: Write test for `getSlidables`
  • Loading branch information
hieutbui authored Jul 17, 2024
1 parent 9cc656a commit 517091f
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 189 deletions.
46 changes: 46 additions & 0 deletions lib/pages/chat_list/chat_custom_slidable_action.dart
Original file line number Diff line number Diff line change
@@ -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,
),
],
),
);
}
}
58 changes: 58 additions & 0 deletions lib/pages/chat_list/chat_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -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';

Expand Down Expand Up @@ -797,6 +801,60 @@ class ChatListController extends State<ChatList>
}
}

List<Widget> 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);
Expand Down
189 changes: 6 additions & 183 deletions lib/pages/chat_list/chat_list_view_builder.dart
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -31,20 +26,20 @@ class ChatListViewBuilder extends StatelessWidget {
return ValueListenableBuilder<SelectMode>(
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],
);
Expand All @@ -53,176 +48,4 @@ class ChatListViewBuilder extends StatelessWidget {
},
);
}

List<Widget> _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<Widget> 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,
);
}
}
12 changes: 6 additions & 6 deletions lib/pages/chat_list/chat_list_view_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
}
Loading

0 comments on commit 517091f

Please sign in to comment.