diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index 9ff17385ec..427b602bfe 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -11,10 +11,10 @@ import '../model/narrow.dart'; import 'clipboard.dart'; import 'compose_box.dart'; import 'dialog.dart'; -import 'draggable_scrollable_modal_bottom_sheet.dart'; import 'icons.dart'; import 'message_list.dart'; import 'store.dart'; +import 'theme.dart'; /// Show a sheet of actions you can take on a message in the message list. /// @@ -36,20 +36,46 @@ void showMessageActionSheet({required BuildContext context, required Message mes && reactionWithVotes.userIds.contains(store.selfUserId)) ?? false; - showDraggableScrollableModalBottomSheet( + final designVariables = DesignVariables.of(context); + showModalBottomSheet( context: context, + clipBehavior: Clip.antiAlias, + backgroundColor: designVariables.actionSheetBackground, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20.0))), + useSafeArea: true, + isScrollControlled: true, builder: (BuildContext _) { - return Column(children: [ - if (!hasThumbsUpReactionVote) AddThumbsUpButton(message: message, messageListContext: context), - StarButton(message: message, messageListContext: context), - if (isComposeBoxOffered) QuoteAndReplyButton( - message: message, - messageListContext: context, + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + // TODO(#217): show message text + Flexible( + child: SingleChildScrollView( + child: ClipRRect( + borderRadius: BorderRadius.circular(7), + child: Column(children: [ + if (!hasThumbsUpReactionVote) AddThumbsUpButton(message: message, messageListContext: context), + StarButton(message: message, messageListContext: context), + if (isComposeBoxOffered) QuoteAndReplyButton( + message: message, + messageListContext: context, + ), + CopyMessageTextButton(message: message, messageListContext: context), + CopyMessageLinkButton(message: message, messageListContext: context), + ShareButton(message: message, messageListContext: context), + // TODO: The following line could be replaced by the [spacing] + // property when https://github.com/flutter/flutter/issues/55378 is fixed. + ].expand((item) => [const SizedBox(height: 1), item]).skip(1).toList()), + ), + ), + ), + const MessageActionSheetCancelButton(), + ], ), - CopyMessageTextButton(message: message, messageListContext: context), - CopyMessageLinkButton(message: message, messageListContext: context), - ShareButton(message: message, messageListContext: context), - ]); + ); }); } @@ -69,11 +95,16 @@ abstract class MessageActionSheetMenuItemButton extends StatelessWidget { @override Widget build(BuildContext context) { + final designVariables = DesignVariables.of(context); final zulipLocalizations = ZulipLocalizations.of(context); return MenuItemButton( - leadingIcon: Icon(icon), + trailingIcon: Icon(icon, color: designVariables.actionSheetMenuButtonForeground), + style: MenuItemButton.styleFrom( + backgroundColor: designVariables.actionSheetMenuButtonBackground, + foregroundColor: designVariables.actionSheetMenuButtonForeground, + ), onPressed: () => onPressed(context), - child: Text(label(zulipLocalizations))); + child: Text(label(zulipLocalizations), style: const TextStyle(fontSize: 16))); } } @@ -402,3 +433,24 @@ class ShareButton extends MessageActionSheetMenuItemButton { } } } + +class MessageActionSheetCancelButton extends StatelessWidget { + const MessageActionSheetCancelButton({super.key}); + + @override + Widget build(BuildContext context) { + final designVariables = DesignVariables.of(context); + return ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + backgroundColor: designVariables.actionSheetCancelButtonBackground, + foregroundColor: designVariables.actionSheetCancelButtonForeground, + shadowColor: Colors.transparent, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), + ), + onPressed: () => Navigator.pop(context), + child: Text(ZulipLocalizations.of(context).dialogCancel, + style: const TextStyle(fontSize: 16)), + ); + } +} diff --git a/lib/widgets/theme.dart b/lib/widgets/theme.dart index c155ffafe8..295232bb8a 100644 --- a/lib/widgets/theme.dart +++ b/lib/widgets/theme.dart @@ -139,6 +139,11 @@ class DesignVariables extends ThemeExtension { mainBackground: const Color(0xfff0f0f0), title: const Color(0xff1a1a1a), channelColorSwatches: ChannelColorSwatches.light, + actionSheetBackground: const HSLColor.fromAHSL(1, 0, 0, 0.94).toColor(), + actionSheetCancelButtonBackground: const HSLColor.fromAHSL(0.15, 240, 0.05, 0.50).toColor(), + actionSheetCancelButtonForeground: const HSLColor.fromAHSL(1, 0, 0, 0.13).toColor(), + actionSheetMenuButtonBackground: const HSLColor.fromAHSL(0.12, 243.53, 0.69, 0.61).toColor(), + actionSheetMenuButtonForeground: const HSLColor.fromAHSL(1, 251.74, 0.70, 0.38).toColor(), atMentionMarker: const HSLColor.fromAHSL(0.5, 0, 0, 0.2).toColor(), dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.35, 0.93).toColor(), errorBannerBackground: const HSLColor.fromAHSL(1, 4, 0.33, 0.90).toColor(), @@ -167,6 +172,11 @@ class DesignVariables extends ThemeExtension { mainBackground: const Color(0xff1d1d1d), title: const Color(0xffffffff), channelColorSwatches: ChannelColorSwatches.dark, + actionSheetBackground: const HSLColor.fromAHSL(1, 0, 0, 0.14).toColor(), + actionSheetCancelButtonBackground: const HSLColor.fromAHSL(0.15, 240, 0.05, 0.50).toColor(), + actionSheetCancelButtonForeground: const HSLColor.fromAHSL(0.75, 0, 0, 1).toColor(), + actionSheetMenuButtonBackground: const HSLColor.fromAHSL(0.12, 240.89, 0.98, 0.73).toColor(), + actionSheetMenuButtonForeground: const HSLColor.fromAHSL(1, 237.17, 0.96, 0.78).toColor(), // TODO(#95) need proper dark-theme color (this is ad hoc) atMentionMarker: const HSLColor.fromAHSL(0.4, 0, 0, 1).toColor(), dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(), @@ -201,6 +211,11 @@ class DesignVariables extends ThemeExtension { required this.mainBackground, required this.title, required this.channelColorSwatches, + required this.actionSheetBackground, + required this.actionSheetCancelButtonBackground, + required this.actionSheetCancelButtonForeground, + required this.actionSheetMenuButtonBackground, + required this.actionSheetMenuButtonForeground, required this.atMentionMarker, required this.dmHeaderBg, required this.errorBannerBackground, @@ -241,6 +256,11 @@ class DesignVariables extends ThemeExtension { final ChannelColorSwatches channelColorSwatches; // Not named variables in Figma; taken from older Figma drafts, or elsewhere. + final Color actionSheetBackground; + final Color actionSheetCancelButtonBackground; + final Color actionSheetCancelButtonForeground; + final Color actionSheetMenuButtonBackground; + final Color actionSheetMenuButtonForeground; final Color atMentionMarker; final Color dmHeaderBg; final Color errorBannerBackground; @@ -268,6 +288,11 @@ class DesignVariables extends ThemeExtension { Color? mainBackground, Color? title, ChannelColorSwatches? channelColorSwatches, + Color? actionSheetBackground, + Color? actionSheetCancelButtonBackground, + Color? actionSheetCancelButtonForeground, + Color? actionSheetMenuButtonBackground, + Color? actionSheetMenuButtonForeground, Color? atMentionMarker, Color? dmHeaderBg, Color? errorBannerBackground, @@ -294,6 +319,11 @@ class DesignVariables extends ThemeExtension { mainBackground: mainBackground ?? this.mainBackground, title: title ?? this.title, channelColorSwatches: channelColorSwatches ?? this.channelColorSwatches, + actionSheetBackground: actionSheetBackground ?? this.actionSheetBackground, + actionSheetCancelButtonBackground: actionSheetCancelButtonBackground ?? this.actionSheetCancelButtonBackground, + actionSheetCancelButtonForeground: actionSheetCancelButtonForeground ?? this.actionSheetCancelButtonForeground, + actionSheetMenuButtonBackground: actionSheetMenuButtonBackground ?? this.actionSheetMenuButtonBackground, + actionSheetMenuButtonForeground: actionSheetMenuButtonForeground ?? this.actionSheetMenuButtonBackground, atMentionMarker: atMentionMarker ?? this.atMentionMarker, dmHeaderBg: dmHeaderBg ?? this.dmHeaderBg, errorBannerBackground: errorBannerBackground ?? this.errorBannerBackground, @@ -327,6 +357,11 @@ class DesignVariables extends ThemeExtension { mainBackground: Color.lerp(mainBackground, other.mainBackground, t)!, title: Color.lerp(title, other.title, t)!, channelColorSwatches: ChannelColorSwatches.lerp(channelColorSwatches, other.channelColorSwatches, t), + actionSheetBackground: Color.lerp(actionSheetBackground, other.actionSheetBackground, t)!, + actionSheetCancelButtonBackground: Color.lerp(actionSheetCancelButtonBackground, other.actionSheetCancelButtonBackground, t)!, + actionSheetCancelButtonForeground: Color.lerp(actionSheetCancelButtonForeground, other.actionSheetCancelButtonForeground, t)!, + actionSheetMenuButtonBackground: Color.lerp(actionSheetMenuButtonBackground, other.actionSheetMenuButtonBackground, t)!, + actionSheetMenuButtonForeground: Color.lerp(actionSheetMenuButtonForeground, other.actionSheetMenuButtonBackground, t)!, atMentionMarker: Color.lerp(atMentionMarker, other.atMentionMarker, t)!, dmHeaderBg: Color.lerp(dmHeaderBg, other.dmHeaderBg, t)!, errorBannerBackground: Color.lerp(errorBannerBackground, other.errorBannerBackground, t)!, diff --git a/test/widgets/action_sheet_test.dart b/test/widgets/action_sheet_test.dart index 3b61192f0a..964de426f0 100644 --- a/test/widgets/action_sheet_test.dart +++ b/test/widgets/action_sheet_test.dart @@ -542,4 +542,37 @@ void main() { check(mockSharePlus.sharedString).isNull(); }); }); + + group('MessageActionSheetCancelButton', () { + final zulipLocalizations = GlobalLocalizations.zulipLocalizations; + + Finder cancelButtonFinder() => find.text(zulipLocalizations.dialogCancel); + + void checkActionSheet(WidgetTester tester, {required bool isShown}) { + // TODO(i18n) skip translation for now + check(find.text('React with 👍').evaluate().length).equals(isShown ? 1 : 0); + check(find.text(zulipLocalizations.actionSheetOptionStarMessage) + .evaluate().length).equals(isShown ? 1 : 0); + check(find.text(zulipLocalizations.actionSheetOptionQuoteAndReply) + .evaluate().length).equals(isShown ? 1 : 0); + check(find.text(zulipLocalizations.actionSheetOptionCopyMessageText) + .evaluate().length).equals(isShown ? 1 : 0); + check(find.text(zulipLocalizations.actionSheetOptionCopyMessageLink) + .evaluate().length).equals(isShown ? 1 : 0); + check(find.text(zulipLocalizations.actionSheetOptionShare) + .evaluate().length).equals(isShown ? 1 : 0); + + check(cancelButtonFinder().evaluate().length).equals(isShown ? 1 : 0); + } + + testWidgets('pressing the button dismisses the action sheet', (tester) async { + final message = eg.streamMessage(); + await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message)); + checkActionSheet(tester, isShown: true); + + await tester.tap(cancelButtonFinder()); + await tester.pumpAndSettle(); + checkActionSheet(tester, isShown: false); + }); + }); }