Skip to content

Commit 22c0f3e

Browse files
sm-sayedignprice
authored andcommitted
action-sheet: Add "Copy message link" button
Fixes: #673
1 parent 191f4e3 commit 22c0f3e

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

assets/l10n/app_en.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
"@actionSheetOptionCopyMessageText": {
4848
"description": "Label for copy message text button on action sheet."
4949
},
50+
"actionSheetOptionCopyMessageLink": "Copy link to message",
51+
"@actionSheetOptionCopyMessageLink": {
52+
"description": "Label for copy message link button on action sheet."
53+
},
5054
"actionSheetOptionShare": "Share",
5155
"@actionSheetOptionShare": {
5256
"description": "Label for share button on action sheet."
@@ -172,6 +176,10 @@
172176
"@successMessageTextCopied": {
173177
"description": "Message when content of a message was copied to the user's system clipboard."
174178
},
179+
"successMessageLinkCopied": "Message link copied",
180+
"@successMessageLinkCopied": {
181+
"description": "Message when link of a message was copied to the user's system clipboard."
182+
},
175183
"composeBoxAttachFilesTooltip": "Attach files",
176184
"@composeBoxAttachFilesTooltip": {
177185
"description": "Tooltip for compose box icon to attach a file to the message."

lib/widgets/action_sheet.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import 'package:share_plus/share_plus.dart';
66
import '../api/exception.dart';
77
import '../api/model/model.dart';
88
import '../api/route/messages.dart';
9+
import '../model/internal_link.dart';
10+
import '../model/narrow.dart';
911
import 'clipboard.dart';
1012
import 'compose_box.dart';
1113
import 'dialog.dart';
@@ -44,6 +46,7 @@ void showMessageActionSheet({required BuildContext context, required Message mes
4446
messageListContext: context,
4547
),
4648
CopyMessageTextButton(message: message, messageListContext: context),
49+
CopyMessageLinkButton(message: message, messageListContext: context),
4750
ShareButton(message: message, messageListContext: context),
4851
]);
4952
});
@@ -310,6 +313,37 @@ class CopyMessageTextButton extends MessageActionSheetMenuItemButton {
310313
}
311314
}
312315

316+
class CopyMessageLinkButton extends MessageActionSheetMenuItemButton {
317+
CopyMessageLinkButton({
318+
super.key,
319+
required super.message,
320+
required super.messageListContext,
321+
});
322+
323+
@override IconData get icon => Icons.link;
324+
325+
@override
326+
String label(ZulipLocalizations zulipLocalizations) {
327+
return zulipLocalizations.actionSheetOptionCopyMessageLink;
328+
}
329+
330+
@override void onPressed(BuildContext context) {
331+
Navigator.of(context).pop();
332+
final zulipLocalizations = ZulipLocalizations.of(messageListContext);
333+
334+
final store = PerAccountStoreWidget.of(messageListContext);
335+
final messageLink = narrowLink(
336+
store,
337+
SendableNarrow.ofMessage(message, selfUserId: store.selfUserId),
338+
nearMessageId: message.id,
339+
);
340+
341+
copyWithPopup(context: messageListContext,
342+
successContent: Text(zulipLocalizations.successMessageLinkCopied),
343+
data: ClipboardData(text: messageLink.toString()));
344+
}
345+
}
346+
313347
class ShareButton extends MessageActionSheetMenuItemButton {
314348
ShareButton({
315349
super.key,

test/widgets/action_sheet_test.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:zulip/api/model/model.dart';
1010
import 'package:zulip/api/route/messages.dart';
1111
import 'package:zulip/model/binding.dart';
1212
import 'package:zulip/model/compose.dart';
13+
import 'package:zulip/model/internal_link.dart';
1314
import 'package:zulip/model/localizations.dart';
1415
import 'package:zulip/model/narrow.dart';
1516
import 'package:zulip/model/store.dart';
@@ -454,6 +455,33 @@ void main() {
454455
});
455456
});
456457

458+
group('CopyMessageLinkButton', () {
459+
setUp(() async {
460+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
461+
SystemChannels.platform,
462+
MockClipboard().handleMethodCall,
463+
);
464+
});
465+
466+
Future<void> tapCopyMessageLinkButton(WidgetTester tester) async {
467+
await tester.ensureVisible(find.byIcon(Icons.link, skipOffstage: false));
468+
await tester.tap(find.byIcon(Icons.link));
469+
await tester.pump(); // [MenuItemButton.onPressed] called in a post-frame callback: flutter/flutter@e4a39fa2e
470+
}
471+
472+
testWidgets('copies message link to clipboard', (tester) async {
473+
final message = eg.streamMessage();
474+
final narrow = TopicNarrow.ofMessage(message);
475+
await setupToMessageActionSheet(tester, message: message, narrow: narrow);
476+
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
477+
478+
await tapCopyMessageLinkButton(tester);
479+
await tester.pump(Duration.zero);
480+
final expectedLink = narrowLink(store, narrow, nearMessageId: message.id).toString();
481+
check(await Clipboard.getData('text/plain')).isNotNull().text.equals(expectedLink);
482+
});
483+
});
484+
457485
group('ShareButton', () {
458486
// Tests should call this.
459487
MockSharePlus setupMockSharePlus() {

0 commit comments

Comments
 (0)