From 3491eba246e2c9bd0ebfef52a873742878a9cffc Mon Sep 17 00:00:00 2001 From: Julian KOUNE Date: Wed, 24 Jan 2024 11:15:53 +0100 Subject: [PATCH] TW-1358: Add Slidable Chat List Item - Pin/Unpin --- .../chat_list/chat_list_view_builder.dart | 144 +++++++++++++----- lib/pages/chat_list/chat_list_view_style.dart | 11 +- .../enum/chat_list/chat_list_enum.dart | 4 +- pubspec.lock | 8 + pubspec.yaml | 1 + 5 files changed, 127 insertions(+), 41 deletions(-) diff --git a/lib/pages/chat_list/chat_list_view_builder.dart b/lib/pages/chat_list/chat_list_view_builder.dart index 30bc07e943..9afb46ca04 100644 --- a/lib/pages/chat_list/chat_list_view_builder.dart +++ b/lib/pages/chat_list/chat_list_view_builder.dart @@ -1,8 +1,12 @@ 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/presentation/enum/chat_list/chat_list_enum.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:matrix/matrix.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; class ChatListViewBuilder extends StatelessWidget { final ChatListController controller; @@ -21,49 +25,111 @@ class ChatListViewBuilder extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), itemCount: rooms.length, itemBuilder: (BuildContext context, int index) { - return ValueListenableBuilder( + return ValueListenableBuilder( valueListenable: controller.selectModeNotifier, - builder: (context, _, __) { - return ValueListenableBuilder( - valueListenable: controller.widget.activeRoomIdNotifier, - builder: (context, activeRoomId, child) { - return ChatListItem( - rooms[index], - key: Key('chat_list_item_${rooms[index].id}'), - isEnableSelectMode: controller.isSelectMode, - onTap: controller.isSelectMode - ? () => controller.toggleSelection(rooms[index].id) - : null, - onSecondaryTap: () => controller.handleContextMenuAction( - context, - rooms[index], - ), - onLongPress: () => controller.onLongPressChatListItem( - rooms[index], - ), - checkBoxWidget: ValueListenableBuilder( - valueListenable: controller.conversationSelectionNotifier, - builder: (context, conversationSelection, __) { - final conversation = - conversationSelection.firstWhereOrNull( - (conversation) => - conversation.roomId.contains(rooms[index].id), - ); - return Checkbox( - value: conversation?.isSelected == true, - onChanged: (_) { - controller.toggleSelection(rooms[index].id); - }, - ); - }, - ), - activeChat: activeRoomId == rooms[index].id, - ); - }, - ); + builder: (context, selectMode, child) { + if (ChatListViewStyle.responsiveUtils.isMobileOrTablet(context) && + !selectMode.isSelectMode) { + return _SlidableChatListItem( + controller: controller, + room: rooms[index], + chatListItem: child!, + ); + } + + return child!; }, + child: _CommonChatListItem( + controller: controller, + room: rooms[index], + ), ); }, ); } } + +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, + onSecondaryTap: () => controller.handleContextMenuAction( + context, + room, + ), + 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.room, + required this.chatListItem, + }); + + final ChatListController controller; + final Room room; + final Widget chatListItem; + + @override + Widget build(BuildContext context) { + return Slidable( + endActionPane: ActionPane( + motion: const ScrollMotion(), + extentRatio: ChatListViewStyle.slidableExtentRatio, + children: [ + SlidableAction( + autoClose: true, + label: room.isFavourite + ? L10n.of(context)!.unpin + : L10n.of(context)!.pin, + icon: room.isFavourite ? Icons.push_pin_outlined : Icons.push_pin, + onPressed: (_) => controller.togglePin(room), + foregroundColor: ChatListViewStyle.slidableForegroundColorDefault, + backgroundColor: Colors.greenAccent[700] ?? + ChatListViewStyle.pinSlidableColorRaw, + ), + ], + ), + 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 052671c371..cf97274daa 100644 --- a/lib/pages/chat_list/chat_list_view_style.dart +++ b/lib/pages/chat_list/chat_list_view_style.dart @@ -1,8 +1,17 @@ -import 'package:flutter/cupertino.dart'; +import 'package:fluffychat/di/global/get_it_initializer.dart'; +import 'package:fluffychat/utils/responsive/responsive_utils.dart'; +import 'package:flutter/material.dart'; class ChatListViewStyle { + static final responsiveUtils = getIt.get(); + static const editIconSize = 18.0; static Size preferredSizeAppBar(BuildContext context) => const Size.fromHeight(120); + + // Between 0 and 1, scale on actions length + static const double slidableExtentRatio = 0.25; + static const Color slidableForegroundColorDefault = Colors.white; + static const Color pinSlidableColorRaw = Color(0xFF00C853); } diff --git a/lib/presentation/enum/chat_list/chat_list_enum.dart b/lib/presentation/enum/chat_list/chat_list_enum.dart index 3667f2e6ec..5420004565 100644 --- a/lib/presentation/enum/chat_list/chat_list_enum.dart +++ b/lib/presentation/enum/chat_list/chat_list_enum.dart @@ -5,7 +5,9 @@ import 'package:matrix/matrix.dart'; enum SelectMode { normal, - select, + select; + + bool get isSelectMode => this == SelectMode.select; } enum PopupMenuAction { diff --git a/pubspec.lock b/pubspec.lock index bf24f13570..c439c1f33e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1019,6 +1019,14 @@ packages: url: "https://gitlab.com/TheOneWithTheBraid/flutter_secure_storage_windows.git" source: git version: "1.1.2" + flutter_slidable: + dependency: "direct main" + description: + name: flutter_slidable + sha256: "19ed4813003a6ff4e9c6bcce37e792a2a358919d7603b2b31ff200229191e44c" + url: "https://pub.dev" + source: hosted + version: "3.0.1" flutter_svg: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4923c82e32..11bf9cb041 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -160,6 +160,7 @@ dependencies: flutter_localized_locales: ^2.0.5 after_layout: ^1.2.0 photo_manager_image_provider: ^2.1.0 + flutter_slidable: ^3.0.1 dev_dependencies: build_runner: ^2.3.3