1
+ import 'package:flutter/foundation.dart' ;
1
2
import 'package:flutter/material.dart' ;
2
3
import 'package:flutter/services.dart' ;
3
4
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart' ;
@@ -12,16 +13,18 @@ import 'actions.dart';
12
13
import 'clipboard.dart' ;
13
14
import 'compose_box.dart' ;
14
15
import 'dialog.dart' ;
15
- import 'draggable_scrollable_modal_bottom_sheet.dart' ;
16
16
import 'icons.dart' ;
17
17
import 'message_list.dart' ;
18
18
import 'store.dart' ;
19
+ import 'text.dart' ;
20
+ import 'theme.dart' ;
19
21
20
22
/// Show a sheet of actions you can take on a message in the message list.
21
23
///
22
24
/// Must have a [MessageListPage] ancestor.
23
25
void showMessageActionSheet ({required BuildContext context, required Message message}) {
24
26
final store = PerAccountStoreWidget .of (context);
27
+ final designVariables = DesignVariables .of (context);
25
28
26
29
// The UI that's conditioned on this won't live-update during this appearance
27
30
// of the action sheet (we avoid calling composeBoxControllerOf in a build
@@ -41,21 +44,76 @@ void showMessageActionSheet({required BuildContext context, required Message mes
41
44
&& reactionWithVotes.userIds.contains (store.selfUserId))
42
45
?? false ;
43
46
44
- showDraggableScrollableModalBottomSheet <void >(
47
+ final optionButtons = [
48
+ if (! hasThumbsUpReactionVote)
49
+ AddThumbsUpButton (message: message, messageListContext: context),
50
+ StarButton (message: message, messageListContext: context),
51
+ if (isComposeBoxOffered)
52
+ QuoteAndReplyButton (message: message, messageListContext: context),
53
+ if (showMarkAsUnreadButton)
54
+ MarkAsUnreadButton (message: message, messageListContext: context, narrow: narrow),
55
+ CopyMessageTextButton (message: message, messageListContext: context),
56
+ CopyMessageLinkButton (message: message, messageListContext: context),
57
+ ShareButton (message: message, messageListContext: context),
58
+ ];
59
+
60
+ showModalBottomSheet <void >(
45
61
context: context,
62
+ useSafeArea: true ,
63
+ isScrollControlled: true ,
46
64
builder: (BuildContext _) {
47
- return Column (children: [
48
- if (! hasThumbsUpReactionVote)
49
- AddThumbsUpButton (message: message, messageListContext: context),
50
- StarButton (message: message, messageListContext: context),
51
- if (isComposeBoxOffered)
52
- QuoteAndReplyButton (message: message, messageListContext: context),
53
- if (showMarkAsUnreadButton)
54
- MarkAsUnreadButton (message: message, messageListContext: context, narrow: narrow),
55
- CopyMessageTextButton (message: message, messageListContext: context),
56
- CopyMessageLinkButton (message: message, messageListContext: context),
57
- ShareButton (message: message, messageListContext: context),
58
- ]);
65
+ return SafeArea (
66
+ minimum: const EdgeInsets .only (bottom: 16 ),
67
+ child: Padding (
68
+ padding: const EdgeInsets .fromLTRB (16 , 0 , 16 , 0 ),
69
+ child: Column (
70
+ crossAxisAlignment: CrossAxisAlignment .stretch,
71
+ mainAxisSize: MainAxisSize .min,
72
+ children: [
73
+ // TODO(#217): show message text
74
+ Flexible (
75
+ child: Stack (
76
+ children: [
77
+ SingleChildScrollView (
78
+ padding: const EdgeInsets .only (top: 16 , bottom: 8 ),
79
+ child: ClipRRect (
80
+ borderRadius: BorderRadius .circular (7 ),
81
+ child: Column (spacing: 1 , children: optionButtons),
82
+ )),
83
+ // top shadow
84
+ Positioned (
85
+ top: 0 , right: 0 , left: 0 ,
86
+ child: Container (
87
+ height: 8 ,
88
+ decoration: BoxDecoration (
89
+ gradient: LinearGradient (
90
+ begin: Alignment .topCenter,
91
+ end: Alignment .bottomCenter,
92
+ colors: [
93
+ designVariables.bgContextMenu,
94
+ designVariables.bgContextMenu.withValues (alpha: 0 ),
95
+ ])))),
96
+ // bottom shadow
97
+ Positioned (
98
+ bottom: 0 , right: 0 , left: 0 ,
99
+ child: Container (
100
+ height: 8 ,
101
+ decoration: BoxDecoration (
102
+ gradient: LinearGradient (
103
+ begin: Alignment .topCenter,
104
+ end: Alignment .bottomCenter,
105
+ colors: [
106
+ designVariables.bgContextMenu.withValues (alpha: 0 ),
107
+ designVariables.bgContextMenu,
108
+ ])))),
109
+ ],
110
+ ),
111
+ ),
112
+ const MessageActionSheetCancelButton (),
113
+ ],
114
+ ),
115
+ ),
116
+ );
59
117
});
60
118
}
61
119
@@ -75,11 +133,22 @@ abstract class MessageActionSheetMenuItemButton extends StatelessWidget {
75
133
76
134
@override
77
135
Widget build (BuildContext context) {
136
+ final designVariables = DesignVariables .of (context);
78
137
final zulipLocalizations = ZulipLocalizations .of (context);
79
138
return MenuItemButton (
80
- leadingIcon: Icon (icon),
139
+ trailingIcon: Icon (icon, color: designVariables.contextMenuItemText),
140
+ style: MenuItemButton .styleFrom (
141
+ padding: const EdgeInsets .symmetric (vertical: 12 , horizontal: 16 ),
142
+ foregroundColor: designVariables.contextMenuItemText,
143
+ splashFactory: NoSplash .splashFactory,
144
+ ).copyWith (backgroundColor: WidgetStateColor .resolveWith ((states) =>
145
+ designVariables.contextMenuItemBg.withValues (
146
+ alpha: states.contains (WidgetState .pressed) ? 0.20 : 0.12 ))),
81
147
onPressed: () => onPressed (context),
82
- child: Text (label (zulipLocalizations)));
148
+ child: Text (label (zulipLocalizations),
149
+ style: const TextStyle (fontSize: 20 , height: 24 / 20 )
150
+ .merge (weightVariableTextStyle (context, wght: 600 )),
151
+ ));
83
152
}
84
153
}
85
154
@@ -92,7 +161,7 @@ class AddThumbsUpButton extends MessageActionSheetMenuItemButton {
92
161
required super .messageListContext,
93
162
});
94
163
95
- @override IconData get icon => Icons .add_reaction_outlined ;
164
+ @override IconData get icon => ZulipIcons .smile ;
96
165
97
166
@override
98
167
String label (ZulipLocalizations zulipLocalizations) {
@@ -133,11 +202,13 @@ class StarButton extends MessageActionSheetMenuItemButton {
133
202
required super .messageListContext,
134
203
});
135
204
136
- @override IconData get icon => ZulipIcons .star_filled;
205
+ @override IconData get icon => _isStarred ? ZulipIcons .star_filled : ZulipIcons .star;
206
+
207
+ bool get _isStarred => message.flags.contains (MessageFlag .starred);
137
208
138
209
@override
139
210
String label (ZulipLocalizations zulipLocalizations) {
140
- return message.flags. contains ( MessageFlag .starred)
211
+ return _isStarred
141
212
? zulipLocalizations.actionSheetOptionUnstarMessage
142
213
: zulipLocalizations.actionSheetOptionStarMessage;
143
214
}
@@ -229,7 +300,7 @@ class QuoteAndReplyButton extends MessageActionSheetMenuItemButton {
229
300
required super .messageListContext,
230
301
});
231
302
232
- @override IconData get icon => Icons .format_quote_outlined ;
303
+ @override IconData get icon => ZulipIcons .format_quote ;
233
304
234
305
@override
235
306
String label (ZulipLocalizations zulipLocalizations) {
@@ -314,7 +385,7 @@ class CopyMessageTextButton extends MessageActionSheetMenuItemButton {
314
385
required super .messageListContext,
315
386
});
316
387
317
- @override IconData get icon => Icons .copy;
388
+ @override IconData get icon => ZulipIcons .copy;
318
389
319
390
@override
320
391
String label (ZulipLocalizations zulipLocalizations) {
@@ -382,7 +453,10 @@ class ShareButton extends MessageActionSheetMenuItemButton {
382
453
required super .messageListContext,
383
454
});
384
455
385
- @override IconData get icon => Icons .adaptive.share;
456
+ @override
457
+ IconData get icon => defaultTargetPlatform == TargetPlatform .iOS
458
+ ? ZulipIcons .share_ios
459
+ : ZulipIcons .share;
386
460
387
461
@override
388
462
String label (ZulipLocalizations zulipLocalizations) {
@@ -431,3 +505,28 @@ class ShareButton extends MessageActionSheetMenuItemButton {
431
505
}
432
506
}
433
507
}
508
+
509
+ class MessageActionSheetCancelButton extends StatelessWidget {
510
+ const MessageActionSheetCancelButton ({super .key});
511
+
512
+ @override
513
+ Widget build (BuildContext context) {
514
+ final designVariables = DesignVariables .of (context);
515
+ return TextButton (
516
+ style: TextButton .styleFrom (
517
+ padding: const EdgeInsets .all (10 ),
518
+ foregroundColor: designVariables.contextMenuCancelText,
519
+ shape: RoundedRectangleBorder (borderRadius: BorderRadius .circular (7 )),
520
+ splashFactory: NoSplash .splashFactory,
521
+ ).copyWith (backgroundColor: WidgetStateColor .resolveWith ((states) =>
522
+ designVariables.contextMenuCancelBg.withValues (
523
+ alpha: states.contains (WidgetState .pressed) ? 0.20 : 0.15 ))),
524
+ onPressed: () {
525
+ Navigator .pop (context);
526
+ },
527
+ child: Text (ZulipLocalizations .of (context).dialogCancel,
528
+ style: const TextStyle (fontSize: 20 , height: 24 / 20 )
529
+ .merge (weightVariableTextStyle (context, wght: 600 ))),
530
+ );
531
+ }
532
+ }
0 commit comments