From 30db0b8bdf26f77d0c398612bbfd9a6e4ec8e056 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Thu, 24 Oct 2024 15:33:48 -0400 Subject: [PATCH 1/4] action_sheet [nfc]: Extract ActionSheetMenuItemButton Signed-off-by: Zixuan James Li --- lib/widgets/action_sheet.dart | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index f9242cf006..7fb13fa802 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -93,12 +93,8 @@ void showMessageActionSheet({required BuildContext context, required Message mes }); } -abstract class MessageActionSheetMenuItemButton extends StatelessWidget { - MessageActionSheetMenuItemButton({ - super.key, - required this.message, - required this.pageContext, - }) : assert(pageContext.findAncestorWidgetOfExactType() != null); +abstract class ActionSheetMenuItemButton extends StatelessWidget { + const ActionSheetMenuItemButton({super.key, required this.pageContext}); IconData get icon; String label(ZulipLocalizations zulipLocalizations); @@ -111,8 +107,6 @@ abstract class MessageActionSheetMenuItemButton extends StatelessWidget { /// For operations that need a [BuildContext], see [pageContext]. void onPressed(); - final Message message; - /// A context within the [MessageListPage] this action sheet was /// triggered from. final BuildContext pageContext; @@ -157,6 +151,16 @@ abstract class MessageActionSheetMenuItemButton extends StatelessWidget { } } +abstract class MessageActionSheetMenuItemButton extends ActionSheetMenuItemButton { + MessageActionSheetMenuItemButton({ + super.key, + required this.message, + required super.pageContext, + }) : assert(pageContext.findAncestorWidgetOfExactType() != null); + + final Message message; +} + class MessageActionSheetCancelButton extends StatelessWidget { const MessageActionSheetCancelButton({super.key}); From 57f239ea138f915a979dc6fa6c095483780b4967 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Thu, 24 Oct 2024 15:43:21 -0400 Subject: [PATCH 2/4] action_sheet [nfc]: Use a generalized name for the cancel button Signed-off-by: Zixuan James Li --- lib/widgets/action_sheet.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index 7fb13fa802..3a786874b6 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -88,7 +88,7 @@ void showMessageActionSheet({required BuildContext context, required Message mes borderRadius: BorderRadius.circular(7), child: Column(spacing: 1, children: optionButtons))))), - const MessageActionSheetCancelButton(), + const ActionSheetCancelButton(), ]))); }); } @@ -161,8 +161,8 @@ abstract class MessageActionSheetMenuItemButton extends ActionSheetMenuItemButto final Message message; } -class MessageActionSheetCancelButton extends StatelessWidget { - const MessageActionSheetCancelButton({super.key}); +class ActionSheetCancelButton extends StatelessWidget { + const ActionSheetCancelButton({super.key}); @override Widget build(BuildContext context) { From f580b684e5c8a2bb6b545c727cbb1da03b10423d Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Thu, 24 Oct 2024 15:36:09 -0400 Subject: [PATCH 3/4] action_sheet [nfc]: Group message action sheet classes together Signed-off-by: Zixuan James Li --- lib/widgets/action_sheet.dart | 160 +++++++++++++++++----------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index 3a786874b6..aac23d0c1f 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -23,76 +23,6 @@ import 'store.dart'; import 'text.dart'; import 'theme.dart'; -/// Show a sheet of actions you can take on a message in the message list. -/// -/// Must have a [MessageListPage] ancestor. -void showMessageActionSheet({required BuildContext context, required Message message}) { - final store = PerAccountStoreWidget.of(context); - - // The UI that's conditioned on this won't live-update during this appearance - // of the action sheet (we avoid calling composeBoxControllerOf in a build - // method; see its doc). - // So we rely on the fact that isComposeBoxOffered for any given message list - // will be constant through the page's life. - final messageListPage = MessageListPage.ancestorOf(context); - final isComposeBoxOffered = messageListPage.composeBoxController != null; - - final isMessageRead = message.flags.contains(MessageFlag.read); - final markAsUnreadSupported = store.connection.zulipFeatureLevel! >= 155; // TODO(server-6) - final showMarkAsUnreadButton = markAsUnreadSupported && isMessageRead; - - final hasThumbsUpReactionVote = message.reactions - ?.aggregated.any((reactionWithVotes) => - reactionWithVotes.reactionType == ReactionType.unicodeEmoji - && reactionWithVotes.emojiCode == '1f44d' - && reactionWithVotes.userIds.contains(store.selfUserId)) - ?? false; - - final optionButtons = [ - if (!hasThumbsUpReactionVote) - AddThumbsUpButton(message: message, pageContext: context), - StarButton(message: message, pageContext: context), - if (isComposeBoxOffered) - QuoteAndReplyButton(message: message, pageContext: context), - if (showMarkAsUnreadButton) - MarkAsUnreadButton(message: message, pageContext: context), - CopyMessageTextButton(message: message, pageContext: context), - CopyMessageLinkButton(message: message, pageContext: context), - ShareButton(message: message, pageContext: context), - ]; - - showModalBottomSheet( - context: context, - // Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect - // on my iPhone 13 Pro but is marked as "much slower": - // https://api.flutter.dev/flutter/dart-ui/Clip.html - clipBehavior: Clip.antiAlias, - useSafeArea: true, - isScrollControlled: true, - builder: (BuildContext _) { - return SafeArea( - minimum: const EdgeInsets.only(bottom: 16), - child: Padding( - padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, - children: [ - // TODO(#217): show message text - Flexible(child: InsetShadowBox( - top: 8, bottom: 8, - color: DesignVariables.of(context).bgContextMenu, - child: SingleChildScrollView( - padding: const EdgeInsets.only(top: 16, bottom: 8), - child: ClipRRect( - borderRadius: BorderRadius.circular(7), - child: Column(spacing: 1, - children: optionButtons))))), - const ActionSheetCancelButton(), - ]))); - }); -} - abstract class ActionSheetMenuItemButton extends StatelessWidget { const ActionSheetMenuItemButton({super.key, required this.pageContext}); @@ -151,16 +81,6 @@ abstract class ActionSheetMenuItemButton extends StatelessWidget { } } -abstract class MessageActionSheetMenuItemButton extends ActionSheetMenuItemButton { - MessageActionSheetMenuItemButton({ - super.key, - required this.message, - required super.pageContext, - }) : assert(pageContext.findAncestorWidgetOfExactType() != null); - - final Message message; -} - class ActionSheetCancelButton extends StatelessWidget { const ActionSheetCancelButton({super.key}); @@ -187,6 +107,86 @@ class ActionSheetCancelButton extends StatelessWidget { } } +/// Show a sheet of actions you can take on a message in the message list. +/// +/// Must have a [MessageListPage] ancestor. +void showMessageActionSheet({required BuildContext context, required Message message}) { + final store = PerAccountStoreWidget.of(context); + + // The UI that's conditioned on this won't live-update during this appearance + // of the action sheet (we avoid calling composeBoxControllerOf in a build + // method; see its doc). + // So we rely on the fact that isComposeBoxOffered for any given message list + // will be constant through the page's life. + final messageListPage = MessageListPage.ancestorOf(context); + final isComposeBoxOffered = messageListPage.composeBoxController != null; + + final isMessageRead = message.flags.contains(MessageFlag.read); + final markAsUnreadSupported = store.connection.zulipFeatureLevel! >= 155; // TODO(server-6) + final showMarkAsUnreadButton = markAsUnreadSupported && isMessageRead; + + final hasThumbsUpReactionVote = message.reactions + ?.aggregated.any((reactionWithVotes) => + reactionWithVotes.reactionType == ReactionType.unicodeEmoji + && reactionWithVotes.emojiCode == '1f44d' + && reactionWithVotes.userIds.contains(store.selfUserId)) + ?? false; + + final optionButtons = [ + if (!hasThumbsUpReactionVote) + AddThumbsUpButton(message: message, pageContext: context), + StarButton(message: message, pageContext: context), + if (isComposeBoxOffered) + QuoteAndReplyButton(message: message, pageContext: context), + if (showMarkAsUnreadButton) + MarkAsUnreadButton(message: message, pageContext: context), + CopyMessageTextButton(message: message, pageContext: context), + CopyMessageLinkButton(message: message, pageContext: context), + ShareButton(message: message, pageContext: context), + ]; + + showModalBottomSheet( + context: context, + // Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect + // on my iPhone 13 Pro but is marked as "much slower": + // https://api.flutter.dev/flutter/dart-ui/Clip.html + clipBehavior: Clip.antiAlias, + useSafeArea: true, + isScrollControlled: true, + builder: (BuildContext _) { + return SafeArea( + minimum: const EdgeInsets.only(bottom: 16), + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + // TODO(#217): show message text + Flexible(child: InsetShadowBox( + top: 8, bottom: 8, + color: DesignVariables.of(context).bgContextMenu, + child: SingleChildScrollView( + padding: const EdgeInsets.only(top: 16, bottom: 8), + child: ClipRRect( + borderRadius: BorderRadius.circular(7), + child: Column(spacing: 1, + children: optionButtons))))), + const ActionSheetCancelButton(), + ]))); + }); +} + +abstract class MessageActionSheetMenuItemButton extends ActionSheetMenuItemButton { + MessageActionSheetMenuItemButton({ + super.key, + required this.message, + required super.pageContext, + }) : assert(pageContext.findAncestorWidgetOfExactType() != null); + + final Message message; +} + // This button is very temporary, to complete #125 before we have a way to // choose an arbitrary reaction (#388). So, skipping i18n. class AddThumbsUpButton extends MessageActionSheetMenuItemButton { From cf19d68bca5d22e847b46278da88aaacf5fb42e0 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Tue, 5 Nov 2024 15:29:27 -0500 Subject: [PATCH 4/4] action_sheet [nfc]: Extract _showActionSheet Signed-off-by: Zixuan James Li --- lib/widgets/action_sheet.dart | 67 +++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index aac23d0c1f..1b6c8b2e58 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -23,6 +23,42 @@ import 'store.dart'; import 'text.dart'; import 'theme.dart'; +void _showActionSheet( + BuildContext context, { + required List optionButtons, +}) { + showModalBottomSheet( + context: context, + // Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect + // on my iPhone 13 Pro but is marked as "much slower": + // https://api.flutter.dev/flutter/dart-ui/Clip.html + clipBehavior: Clip.antiAlias, + useSafeArea: true, + isScrollControlled: true, + builder: (BuildContext _) { + return SafeArea( + minimum: const EdgeInsets.only(bottom: 16), + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + // TODO(#217): show message text + Flexible(child: InsetShadowBox( + top: 8, bottom: 8, + color: DesignVariables.of(context).bgContextMenu, + child: SingleChildScrollView( + padding: const EdgeInsets.only(top: 16, bottom: 8), + child: ClipRRect( + borderRadius: BorderRadius.circular(7), + child: Column(spacing: 1, + children: optionButtons))))), + const ActionSheetCancelButton(), + ]))); + }); +} + abstract class ActionSheetMenuItemButton extends StatelessWidget { const ActionSheetMenuItemButton({super.key, required this.pageContext}); @@ -145,36 +181,7 @@ void showMessageActionSheet({required BuildContext context, required Message mes ShareButton(message: message, pageContext: context), ]; - showModalBottomSheet( - context: context, - // Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect - // on my iPhone 13 Pro but is marked as "much slower": - // https://api.flutter.dev/flutter/dart-ui/Clip.html - clipBehavior: Clip.antiAlias, - useSafeArea: true, - isScrollControlled: true, - builder: (BuildContext _) { - return SafeArea( - minimum: const EdgeInsets.only(bottom: 16), - child: Padding( - padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, - children: [ - // TODO(#217): show message text - Flexible(child: InsetShadowBox( - top: 8, bottom: 8, - color: DesignVariables.of(context).bgContextMenu, - child: SingleChildScrollView( - padding: const EdgeInsets.only(top: 16, bottom: 8), - child: ClipRRect( - borderRadius: BorderRadius.circular(7), - child: Column(spacing: 1, - children: optionButtons))))), - const ActionSheetCancelButton(), - ]))); - }); + _showActionSheet(context, optionButtons: optionButtons); } abstract class MessageActionSheetMenuItemButton extends ActionSheetMenuItemButton {