diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 3a2ade5821..06324909e1 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -44,7 +44,7 @@ abstract class AppConfig { static bool showDirectChatsInSpaces = true; static bool separateChatTypes = false; static bool autoplayImages = true; - static bool sendOnEnter = PlatformInfos.isMobile ? false : true; + static bool sendOnEnter = !PlatformInfos.isMobile; static bool experimentalVoip = false; static const bool hideTypingUsernames = false; static const bool hideAllStateEvents = false; diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 8cca366dcb..8cf9f577a8 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/domain/model/download_file/download_file_for_preview_ import 'package:fluffychat/domain/model/preview_file/document_uti.dart'; import 'package:fluffychat/domain/model/preview_file/supported_preview_file_types.dart'; import 'package:fluffychat/domain/usecase/download_file_for_preview_interactor.dart'; +import 'package:fluffychat/pages/chat/chat_context_menu_actions.dart'; import 'package:fluffychat/domain/usecase/send_file_interactor.dart'; import 'package:fluffychat/pages/chat/chat_horizontal_action_menu.dart'; import 'package:fluffychat/pages/chat/chat_view.dart'; @@ -105,6 +106,7 @@ class ChatController extends State final AutoScrollController scrollController = AutoScrollController(); final AutoScrollController forwardListController = AutoScrollController(); final ValueNotifier focusHover = ValueNotifier(null); + final ValueNotifier openingPopupMenu = ValueNotifier(false); FocusNode inputFocus = FocusNode(); @@ -1316,13 +1318,19 @@ class ChatController extends State } void onHover(bool isHovered, int index, Event event) { - if (timeline!.events[index - 1].eventId == event.eventId && - !responsive.isMobile(context) && - !selectMode) { + if (index > 0 && + timeline!.events[index - 1].eventId == event.eventId && + responsive.isDesktop(context) && + !selectMode && + !openingPopupMenu.value) { focusHover.value = isHovered ? event.eventId : null; } } + void _handleStateContextMenu() { + openingPopupMenu.value = !openingPopupMenu.value; + } + List listHorizontalActionMenuBuilder() { final listAction = [ ChatHorizontalActionMenu.reply, @@ -1358,97 +1366,67 @@ class ChatController extends State } } - List _popupMenuActionTile( + List _popupMenuActionTile( BuildContext context, Event event, ) { - return [ - _buildSelectPopupMenuItem(context, event), - _buildCopyMessagePopupMenuItem(context, event), - _buildPinMessagePopupMenuItem(context, event), - _buildForwardPopupMenuItem(context, event), + final listAction = [ + ChatContextMenuActions.select, + ChatContextMenuActions.copyMessage, + ChatContextMenuActions.pinMessage, + ChatContextMenuActions.forward, ]; + return listAction.map((action) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: popupItem( + context, + action.getTitle(context), + iconAction: action.getIcon(), + onCallbackAction: () => _handleClickOnContextMenuItem( + action, + event, + ), + ), + ); + }).toList(); } - PopupMenuEntry _buildSelectPopupMenuItem( - BuildContext context, - Event event, - ) { - return PopupMenuItem( - padding: EdgeInsets.zero, - child: popupItem( - context, - L10n.of(context)!.select, - iconAction: Icons.check_circle_outline, - onCallbackAction: () { - onSelectMessage(event); - }, - ), - ); - } - - PopupMenuEntry _buildCopyMessagePopupMenuItem( - BuildContext context, - Event event, - ) { - return PopupMenuItem( - padding: EdgeInsets.zero, - child: popupItem( - context, - L10n.of(context)!.copyMessageText, - iconAction: Icons.content_copy, - onCallbackAction: () { - onSelectMessage(event); - copyEventsAction(); - }, - ), - ); - } - - PopupMenuEntry _buildPinMessagePopupMenuItem( - BuildContext context, - Event event, - ) { - return PopupMenuItem( - padding: EdgeInsets.zero, - child: popupItem( - context, - L10n.of(context)!.pinMessage, - iconAction: Icons.push_pin, - onCallbackAction: () { - onSelectMessage(event); - pinEvent(); - }, - ), - ); - } - - PopupMenuEntry _buildForwardPopupMenuItem( - BuildContext context, + void _handleClickOnContextMenuItem( + ChatContextMenuActions action, Event event, ) { - return PopupMenuItem( - padding: EdgeInsets.zero, - child: popupItem( - context, - L10n.of(context)!.forward, - iconAction: Icons.shortcut, - onCallbackAction: () { - onSelectMessage(event); - forwardEventsAction(); - }, - ), - ); + switch (action) { + case ChatContextMenuActions.select: + onSelectMessage(event); + break; + case ChatContextMenuActions.copyMessage: + onSelectMessage(event); + copyEventsAction(); + break; + case ChatContextMenuActions.pinMessage: + onSelectMessage(event); + pinEvent(); + break; + case ChatContextMenuActions.forward: + onSelectMessage(event); + forwardEventsAction(); + break; + } } void handleContextMenuAction( BuildContext context, Event event, ) { + _handleStateContextMenu(); openPopupMenuAction( context, context.getCurrentRelativeRectOfWidget(), _popupMenuActionTile(context, event), + onClose: () { + _handleStateContextMenu(); + }, ); } diff --git a/lib/pages/chat/chat_context_menu_actions.dart b/lib/pages/chat/chat_context_menu_actions.dart new file mode 100644 index 0000000000..9ba636e464 --- /dev/null +++ b/lib/pages/chat/chat_context_menu_actions.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +enum ChatContextMenuActions { + select, + copyMessage, + pinMessage, + forward; + + String getTitle(BuildContext context) { + switch (this) { + case ChatContextMenuActions.select: + return L10n.of(context)!.select; + case ChatContextMenuActions.copyMessage: + return L10n.of(context)!.copyMessageText; + case ChatContextMenuActions.pinMessage: + return L10n.of(context)!.pinMessage; + case ChatContextMenuActions.forward: + return L10n.of(context)!.forward; + } + } + + IconData getIcon() { + switch (this) { + case ChatContextMenuActions.select: + return Icons.check_circle_outline; + case ChatContextMenuActions.copyMessage: + return Icons.content_copy; + case ChatContextMenuActions.pinMessage: + return Icons.push_pin; + case ChatContextMenuActions.forward: + return Icons.shortcut; + } + } +} diff --git a/lib/pages/chat/events/message/message_style.dart b/lib/pages/chat/events/message/message_style.dart index 91def97e0f..e17ab1fc3b 100644 --- a/lib/pages/chat/events/message/message_style.dart +++ b/lib/pages/chat/events/message/message_style.dart @@ -15,6 +15,7 @@ class MessageStyle { static const notSameSenderPadding = EdgeInsets.only(left: 8.0, bottom: 4); static const double buttonHeight = 66; + static List boxShadow(BuildContext context) { return Theme.of(context).brightness == Brightness.light ? const [ @@ -39,6 +40,7 @@ class MessageStyle { } static int get messageFlexMobile => 7; + static int get replyIconFlexMobile => 2; static TextStyle? displayTime(BuildContext context) => @@ -50,6 +52,7 @@ class MessageStyle { ); static double get forwardContainerSize => 40.0; + static Color? forwardColorBackground(context) => Theme.of(context).colorScheme.surfaceTint.withOpacity(0.08); @@ -58,12 +61,11 @@ class MessageStyle { static const double messageBubbleTabletRatioMaxWidth = 0.50; static double messageBubbleWidth(BuildContext context) { - if (responsiveUtils.isDesktop(context)) { - return messageBubbleDesktopMaxWidth; - } else if (responsiveUtils.isTablet(context)) { - return context.width * messageBubbleTabletRatioMaxWidth; - } else { - return context.width * messageBubbleMobileRatioMaxWidth; - } + return context.responsiveValue( + desktop: messageBubbleDesktopMaxWidth, + tablet: context.width * messageBubbleTabletRatioMaxWidth, + mobile: + MediaQuery.of(context).size.width * messageBubbleMobileRatioMaxWidth, + ); } } diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 6b056ebb93..960a7979ff 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -82,7 +82,8 @@ class MessageContent extends StatelessWidget { leading: CloseButton(onPressed: Navigator.of(context).pop), title: Text( l10n.whyIsThisMessageEncrypted, - style: TextStyle(fontSize: MessageContentStyle.appBarFontSize), + style: + const TextStyle(fontSize: MessageContentStyle.appBarFontSize), ), ), body: SafeArea( @@ -426,7 +427,7 @@ class _MessageImageBuilder extends StatelessWidget { return ImageBubble( event, width: MessageContentStyle.imageBubbleWidth(context), - height: MessageContentStyle.imageBubbleHeight, + height: MessageContentStyle.imageBubbleHeightForWeb, fit: BoxFit.cover, onTapSelectMode: onTapSelectMode, onTapPreview: onTapPreview, diff --git a/lib/pages/chat/events/message_content_style.dart b/lib/pages/chat/events/message_content_style.dart index a0b6ba72ca..7a7762a761 100644 --- a/lib/pages/chat/events/message_content_style.dart +++ b/lib/pages/chat/events/message_content_style.dart @@ -5,17 +5,19 @@ import 'package:flutter/material.dart'; class MessageContentStyle { static ResponsiveUtils responsiveUtils = getIt.get(); - static int get maxLengthTextInline => 180; - static double get appBarFontSize => 16.0; + static const int maxLengthTextInline = 180; + static const double appBarFontSize = 16.0; + static double imageBubbleWidth(BuildContext context) { if (responsiveUtils.isDesktop(context)) { - return 500; + return imageBubbleHeightForWeb; } - return 256; + return imageBubbleHeightForMobileAndTablet; } - static double get imageBubbleHeight => 500; - static Color get backgroundColorButton => Colors.white.withAlpha(64); + static const double imageBubbleHeightForWeb = 500; + static const double imageBubbleHeightForMobileAndTablet = 256; + static Color backgroundColorButton = Colors.white.withAlpha(64); - static double get letterSpacingMessageContent => -0.15; + static const double letterSpacingMessageContent = -0.15; } diff --git a/lib/pages/chat/events/sending_image_info_widget.dart b/lib/pages/chat/events/sending_image_info_widget.dart index 939c01c4d5..3c0951a73f 100644 --- a/lib/pages/chat/events/sending_image_info_widget.dart +++ b/lib/pages/chat/events/sending_image_info_widget.dart @@ -71,8 +71,8 @@ class SendingImageInfoWidget extends StatelessWidget { child: Image.file( File(matrixFile.filePath!), width: MessageContentStyle.imageBubbleWidth(context), - height: MessageContentStyle.imageBubbleHeight, - cacheHeight: MessageContentStyle.imageBubbleHeight.toInt(), + height: MessageContentStyle.imageBubbleHeightForWeb, + cacheHeight: MessageContentStyle.imageBubbleHeightForWeb.toInt(), cacheWidth: MessageContentStyle.imageBubbleWidth(context).toInt(), fit: BoxFit.cover, filterQuality: FilterQuality.medium, diff --git a/lib/pages/chat/events/sending_video_widget.dart b/lib/pages/chat/events/sending_video_widget.dart index 7f6493e0f5..f5024f864c 100644 --- a/lib/pages/chat/events/sending_video_widget.dart +++ b/lib/pages/chat/events/sending_video_widget.dart @@ -131,7 +131,7 @@ class _SendingVideoWidgetState extends State if (imageWidth == null || imageHeight == null) { return ( MessageContentStyle.imageBubbleWidth(context), - MessageContentStyle.imageBubbleHeight + MessageContentStyle.imageBubbleHeightForWeb ); } @@ -140,7 +140,7 @@ class _SendingVideoWidgetState extends State if (imageWidth <= imageHeight) { return ( MessageContentStyle.imageBubbleWidth(context), - MessageContentStyle.imageBubbleHeight + MessageContentStyle.imageBubbleHeightForWeb ); } else { return ( diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index 96c200405b..b9f1fe10d0 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -111,7 +111,7 @@ class EventVideoPlayerState extends State { color: Colors.black, child: SizedBox( width: MessageContentStyle.imageBubbleWidth(context), - height: MessageContentStyle.imageBubbleHeight, + height: MessageContentStyle.imageBubbleHeightForWeb, child: chewieManager != null ? FittedBox( fit: BoxFit.contain, diff --git a/lib/pages/forward/forward_view.dart b/lib/pages/forward/forward_view.dart index 6c45e3a5e4..6351e6ebae 100644 --- a/lib/pages/forward/forward_view.dart +++ b/lib/pages/forward/forward_view.dart @@ -136,7 +136,7 @@ class _ForwardButton extends StatelessWidget { alignment: Alignment.centerRight, child: TwakeIconButton( size: ForwardViewStyle.iconSendSize, - onPressed: forwardAction, + onTap: forwardAction, tooltip: L10n.of(context)!.send, imagePath: ImagePaths.icSend, ), @@ -168,7 +168,7 @@ class _ForwardAppBar extends StatelessWidget { TwakeIconButton( tooltip: L10n.of(context)!.back, icon: Icons.arrow_back, - onPressed: () { + onTap: () { Matrix.of(context).shareContent = null; if (sendFromRoomId != null) { context.go('/rooms/$sendFromRoomId'); @@ -227,7 +227,7 @@ class _ForwardAppBar extends StatelessWidget { actions: [ TwakeIconButton( icon: Icons.search, - onPressed: () => isSearchBarShowNotifier.value = true, + onTap: () => isSearchBarShowNotifier.value = true, tooltip: L10n.of(context)!.search, ), ], diff --git a/lib/pages/forward/recent_chat_title.dart b/lib/pages/forward/recent_chat_title.dart index 460a661024..89ac078d71 100644 --- a/lib/pages/forward/recent_chat_title.dart +++ b/lib/pages/forward/recent_chat_title.dart @@ -34,7 +34,7 @@ class RecentChatsTitle extends StatelessWidget { ), icon: isShowRecentlyChats ? Icons.expand_less : Icons.expand_more, - onPressed: toggleRecentChat, + onTap: toggleRecentChat, tooltip: isShowRecentlyChats ? L10n.of(context)!.shrink : L10n.of(context)!.expand, diff --git a/lib/pages/share/share_view.dart b/lib/pages/share/share_view.dart index ea982049b4..7a0c896516 100644 --- a/lib/pages/share/share_view.dart +++ b/lib/pages/share/share_view.dart @@ -25,7 +25,7 @@ class ShareView extends StatelessWidget { leading: TwakeIconButton( tooltip: L10n.of(context)!.cancel, icon: Icons.close, - onPressed: () => context.pop(), + onTap: () => context.pop(), ), ), body: Padding( diff --git a/lib/utils/extension/build_context_extension.dart b/lib/utils/extension/build_context_extension.dart index 1ad15d506e..868d7d90c1 100644 --- a/lib/utils/extension/build_context_extension.dart +++ b/lib/utils/extension/build_context_extension.dart @@ -128,17 +128,25 @@ extension ContextExtensionss on BuildContext { if (PlatformInfos.isDesktop) { deviceWidth = mediaQuerySize.width; } - if (deviceWidth >= 1200 && desktop != null) { + if (deviceWidth >= minDesktopWidth && desktop != null) { return desktop; - } else if (deviceWidth >= 600 && tablet != null) { + } else if (deviceWidth >= minTabletWidth && + deviceWidth < minDesktopWidth && + tablet != null) { return tablet; - } else if (deviceWidth < 300 && watch != null) { - return watch; + } else if (deviceWidth < maxMobileWidth && mobile != null) { + return mobile; } else { - return mobile!; + return watch!; } } + static const double minDesktopWidth = 1239; + + static const double minTabletWidth = 905; + + static const double maxMobileWidth = 904; + void goChild(String path) => go('${GoRouterState.of(this).uri.path}/$path'); void pushChild(String path) => diff --git a/lib/widgets/mixins/popup_context_menu_action_mixin.dart b/lib/widgets/mixins/popup_context_menu_action_mixin.dart index 07614ebc27..4666cf29cc 100644 --- a/lib/widgets/mixins/popup_context_menu_action_mixin.dart +++ b/lib/widgets/mixins/popup_context_menu_action_mixin.dart @@ -5,8 +5,9 @@ mixin PopupContextMenuActionMixin { void openPopupMenuAction( BuildContext context, RelativeRect? position, - List popupMenuItems, - ) async { + List popupMenuItems, { + VoidCallback? onClose, + }) async { await showMenu( context: context, position: position ?? const RelativeRect.fromLTRB(16, 40, 16, 16), @@ -14,6 +15,8 @@ mixin PopupContextMenuActionMixin { elevation: 5, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), items: popupMenuItems, - ); + ).then((value) { + onClose?.call(); + }); } } diff --git a/lib/widgets/mixins/popup_menu_widget_mixin.dart b/lib/widgets/mixins/popup_menu_widget_mixin.dart index 12ad082016..ee36721e74 100644 --- a/lib/widgets/mixins/popup_menu_widget_mixin.dart +++ b/lib/widgets/mixins/popup_menu_widget_mixin.dart @@ -1,10 +1,9 @@ import 'package:fluffychat/widgets/fluffy_chat_app.dart'; +import 'package:fluffychat/widgets/twake_components/twake_icon_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:linagora_design_flutter/colors/linagora_sys_colors.dart'; -typedef OnTapIconButtonCallbackAction = void Function(); - mixin PopupMenuWidgetMixin { Widget popupItem( BuildContext context,