Skip to content

Commit a3a5f5a

Browse files
committed
msglist: Support adding a thumbs-up reaction
Fixes: zulip#125
1 parent 001d8d0 commit a3a5f5a

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

lib/widgets/action_sheet.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,26 @@ import 'store.dart';
1717
///
1818
/// Must have a [MessageListPage] ancestor.
1919
void showMessageActionSheet({required BuildContext context, required Message message}) {
20+
final store = PerAccountStoreWidget.of(context);
21+
2022
// The UI that's conditioned on this won't live-update during this appearance
2123
// of the action sheet (we avoid calling composeBoxControllerOf in a build
2224
// method; see its doc). But currently it will be constant through the life of
2325
// any message list, so that's fine.
2426
final isComposeBoxOffered = MessageListPage.composeBoxControllerOf(context) != null;
27+
28+
final selfUserId = store.account.userId;
29+
final hasThumbsUpReactionVote = message.reactions
30+
?.aggregated.any((reactionWithVotes) =>
31+
reactionWithVotes.emojiCode == '1f44d'
32+
&& reactionWithVotes.userIds.contains(selfUserId))
33+
?? false;
34+
2535
showDraggableScrollableModalBottomSheet(
2636
context: context,
2737
builder: (BuildContext _) {
2838
return Column(children: [
39+
if (!hasThumbsUpReactionVote) AddThumbsUpButton(message: message, messageListContext: context),
2940
ShareButton(message: message, messageListContext: context),
3041
if (isComposeBoxOffered) QuoteAndReplyButton(
3142
message: message,
@@ -60,6 +71,49 @@ abstract class MessageActionSheetMenuItemButton extends StatelessWidget {
6071
}
6172
}
6273

74+
// This button is very temporary, to complete #125 before we have a way to
75+
// choose an arbitrary reaction (#388). So, skipping tests and i18n.
76+
class AddThumbsUpButton extends MessageActionSheetMenuItemButton {
77+
AddThumbsUpButton({
78+
super.key,
79+
required super.message,
80+
required super.messageListContext,
81+
});
82+
83+
@override get icon => Icons.add_reaction_outlined;
84+
85+
@override
86+
String label(ZulipLocalizations zulipLocalizations) {
87+
return 'React with 👍'; // skip translation for now
88+
}
89+
90+
@override get onPressed => (BuildContext context) async {
91+
Navigator.of(context).pop();
92+
String? errorMessage;
93+
try {
94+
await addReaction(PerAccountStoreWidget.of(messageListContext).connection,
95+
messageId: message.id,
96+
reactionType: ReactionType.unicodeEmoji,
97+
emojiCode: '1f44d',
98+
emojiName: '+1',
99+
);
100+
} catch (e) {
101+
if (!messageListContext.mounted) return;
102+
103+
switch (e) {
104+
case ZulipApiException():
105+
errorMessage = e.message;
106+
// TODO specific messages for common errors, like network errors
107+
// (support with reusable code)
108+
default:
109+
}
110+
111+
await showErrorDialog(context: context,
112+
title: 'Adding reaction failed', message: errorMessage);
113+
}
114+
};
115+
}
116+
63117
class ShareButton extends MessageActionSheetMenuItemButton {
64118
ShareButton({
65119
super.key,

test/widgets/action_sheet_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ void main() {
177177
///
178178
/// Checks that there is a quote-and-reply button.
179179
Future<void> tapQuoteAndReplyButton(WidgetTester tester) async {
180+
await tester.ensureVisible(find.byIcon(Icons.format_quote_outlined, skipOffstage: false));
180181
final quoteAndReplyButton = findQuoteAndReplyButton(tester);
181182
check(quoteAndReplyButton).isNotNull();
182183
await tester.tap(find.byWidget(quoteAndReplyButton!));

0 commit comments

Comments
 (0)