Skip to content

Commit 191f4e3

Browse files
sm-sayedignprice
authored andcommitted
action-sheet: Move the "Share" option to the bottom of options
This is because in the next commit(s) the "Copy link to message" button will be added and we want to make it next to the "Share" button at the bottom of the options.
1 parent aeb6a94 commit 191f4e3

File tree

2 files changed

+122
-122
lines changed

2 files changed

+122
-122
lines changed

lib/widgets/action_sheet.dart

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ void showMessageActionSheet({required BuildContext context, required Message mes
3939
return Column(children: [
4040
if (!hasThumbsUpReactionVote) AddThumbsUpButton(message: message, messageListContext: context),
4141
StarButton(message: message, messageListContext: context),
42-
ShareButton(message: message, messageListContext: context),
4342
if (isComposeBoxOffered) QuoteAndReplyButton(
4443
message: message,
4544
messageListContext: context,
4645
),
4746
CopyMessageTextButton(message: message, messageListContext: context),
47+
ShareButton(message: message, messageListContext: context),
4848
]);
4949
});
5050
}
@@ -164,63 +164,6 @@ class StarButton extends MessageActionSheetMenuItemButton {
164164
}
165165
}
166166

167-
class ShareButton extends MessageActionSheetMenuItemButton {
168-
ShareButton({
169-
super.key,
170-
required super.message,
171-
required super.messageListContext,
172-
});
173-
174-
@override IconData get icon => Icons.adaptive.share;
175-
176-
@override
177-
String label(ZulipLocalizations zulipLocalizations) {
178-
return zulipLocalizations.actionSheetOptionShare;
179-
}
180-
181-
@override void onPressed(BuildContext context) async {
182-
// Close the message action sheet; we're about to show the share
183-
// sheet. (We could do this after the sharing Future settles
184-
// with [ShareResultStatus.success], but on iOS I get impatient with
185-
// how slowly our action sheet dismisses in that case.)
186-
// TODO(#24): Fix iOS bug where this call causes the keyboard to
187-
// reopen (if it was open at the time of this
188-
// `showMessageActionSheet` call) and cover a large part of the
189-
// share sheet.
190-
Navigator.of(context).pop();
191-
final zulipLocalizations = ZulipLocalizations.of(messageListContext);
192-
193-
final rawContent = await fetchRawContentWithFeedback(
194-
context: messageListContext,
195-
messageId: message.id,
196-
errorDialogTitle: zulipLocalizations.errorSharingFailed,
197-
);
198-
199-
if (rawContent == null) return;
200-
201-
if (!messageListContext.mounted) return;
202-
203-
// TODO: to support iPads, we're asked to give a
204-
// `sharePositionOrigin` param, or risk crashing / hanging:
205-
// https://pub.dev/packages/share_plus#ipad
206-
// Perhaps a wart in the API; discussion:
207-
// https://github.com/zulip/zulip-flutter/pull/12#discussion_r1130146231
208-
final result = await Share.share(rawContent);
209-
210-
switch (result.status) {
211-
// The plugin isn't very helpful: "The status can not be determined".
212-
// Until we learn otherwise, assume something wrong happened.
213-
case ShareResultStatus.unavailable:
214-
if (!messageListContext.mounted) return;
215-
await showErrorDialog(context: messageListContext,
216-
title: zulipLocalizations.errorSharingFailed);
217-
case ShareResultStatus.success:
218-
case ShareResultStatus.dismissed:
219-
// nothing to do
220-
}
221-
}
222-
}
223-
224167
/// Fetch and return the raw Markdown content for [messageId],
225168
/// showing an error dialog on failure.
226169
Future<String?> fetchRawContentWithFeedback({
@@ -366,3 +309,60 @@ class CopyMessageTextButton extends MessageActionSheetMenuItemButton {
366309
data: ClipboardData(text: rawContent));
367310
}
368311
}
312+
313+
class ShareButton extends MessageActionSheetMenuItemButton {
314+
ShareButton({
315+
super.key,
316+
required super.message,
317+
required super.messageListContext,
318+
});
319+
320+
@override IconData get icon => Icons.adaptive.share;
321+
322+
@override
323+
String label(ZulipLocalizations zulipLocalizations) {
324+
return zulipLocalizations.actionSheetOptionShare;
325+
}
326+
327+
@override void onPressed(BuildContext context) async {
328+
// Close the message action sheet; we're about to show the share
329+
// sheet. (We could do this after the sharing Future settles
330+
// with [ShareResultStatus.success], but on iOS I get impatient with
331+
// how slowly our action sheet dismisses in that case.)
332+
// TODO(#24): Fix iOS bug where this call causes the keyboard to
333+
// reopen (if it was open at the time of this
334+
// `showMessageActionSheet` call) and cover a large part of the
335+
// share sheet.
336+
Navigator.of(context).pop();
337+
final zulipLocalizations = ZulipLocalizations.of(messageListContext);
338+
339+
final rawContent = await fetchRawContentWithFeedback(
340+
context: messageListContext,
341+
messageId: message.id,
342+
errorDialogTitle: zulipLocalizations.errorSharingFailed,
343+
);
344+
345+
if (rawContent == null) return;
346+
347+
if (!messageListContext.mounted) return;
348+
349+
// TODO: to support iPads, we're asked to give a
350+
// `sharePositionOrigin` param, or risk crashing / hanging:
351+
// https://pub.dev/packages/share_plus#ipad
352+
// Perhaps a wart in the API; discussion:
353+
// https://github.com/zulip/zulip-flutter/pull/12#discussion_r1130146231
354+
final result = await Share.share(rawContent);
355+
356+
switch (result.status) {
357+
// The plugin isn't very helpful: "The status can not be determined".
358+
// Until we learn otherwise, assume something wrong happened.
359+
case ShareResultStatus.unavailable:
360+
if (!messageListContext.mounted) return;
361+
await showErrorDialog(context: messageListContext,
362+
title: zulipLocalizations.errorSharingFailed);
363+
case ShareResultStatus.success:
364+
case ShareResultStatus.dismissed:
365+
// nothing to do
366+
}
367+
}
368+
}

test/widgets/action_sheet_test.dart

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -247,70 +247,6 @@ void main() {
247247
});
248248
});
249249

250-
group('ShareButton', () {
251-
// Tests should call this.
252-
MockSharePlus setupMockSharePlus() {
253-
final mock = MockSharePlus();
254-
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
255-
MethodChannelShare.channel,
256-
mock.handleMethodCall,
257-
);
258-
return mock;
259-
}
260-
261-
Future<void> tapShareButton(WidgetTester tester) async {
262-
await tester.ensureVisible(find.byIcon(Icons.adaptive.share, skipOffstage: false));
263-
await tester.tap(find.byIcon(Icons.adaptive.share));
264-
await tester.pump(); // [MenuItemButton.onPressed] called in a post-frame callback: flutter/flutter@e4a39fa2e
265-
}
266-
267-
testWidgets('request succeeds; sharing succeeds', (WidgetTester tester) async {
268-
final mockSharePlus = setupMockSharePlus();
269-
final message = eg.streamMessage();
270-
await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message));
271-
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
272-
273-
prepareRawContentResponseSuccess(store, message: message, rawContent: 'Hello world');
274-
await tapShareButton(tester);
275-
await tester.pump(Duration.zero);
276-
check(mockSharePlus.sharedString).equals('Hello world');
277-
});
278-
279-
testWidgets('request succeeds; sharing fails', (WidgetTester tester) async {
280-
final mockSharePlus = setupMockSharePlus();
281-
final message = eg.streamMessage();
282-
await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message));
283-
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
284-
285-
prepareRawContentResponseSuccess(store, message: message, rawContent: 'Hello world');
286-
mockSharePlus.resultString = 'dev.fluttercommunity.plus/share/unavailable';
287-
await tapShareButton(tester);
288-
await tester.pump(Duration.zero);
289-
check(mockSharePlus.sharedString).equals('Hello world');
290-
await tester.pump();
291-
await tester.tap(find.byWidget(checkErrorDialog(tester,
292-
expectedTitle: 'Sharing failed')));
293-
});
294-
295-
testWidgets('request has an error', (WidgetTester tester) async {
296-
final mockSharePlus = setupMockSharePlus();
297-
final message = eg.streamMessage();
298-
await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message));
299-
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
300-
301-
prepareRawContentResponseError(store);
302-
await tapShareButton(tester);
303-
await tester.pump(Duration.zero); // error arrives; error dialog shows
304-
305-
await tester.tap(find.byWidget(checkErrorDialog(tester,
306-
expectedTitle: 'Sharing failed',
307-
expectedMessage: 'That message does not seem to exist.',
308-
)));
309-
310-
check(mockSharePlus.sharedString).isNull();
311-
});
312-
});
313-
314250
group('QuoteAndReplyButton', () {
315251
ComposeBoxController? findComposeBoxController(WidgetTester tester) {
316252
return tester.widget<ComposeBox>(find.byType(ComposeBox))
@@ -517,4 +453,68 @@ void main() {
517453
check(await Clipboard.getData('text/plain')).isNull();
518454
});
519455
});
456+
457+
group('ShareButton', () {
458+
// Tests should call this.
459+
MockSharePlus setupMockSharePlus() {
460+
final mock = MockSharePlus();
461+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
462+
MethodChannelShare.channel,
463+
mock.handleMethodCall,
464+
);
465+
return mock;
466+
}
467+
468+
Future<void> tapShareButton(WidgetTester tester) async {
469+
await tester.ensureVisible(find.byIcon(Icons.adaptive.share, skipOffstage: false));
470+
await tester.tap(find.byIcon(Icons.adaptive.share));
471+
await tester.pump(); // [MenuItemButton.onPressed] called in a post-frame callback: flutter/flutter@e4a39fa2e
472+
}
473+
474+
testWidgets('request succeeds; sharing succeeds', (WidgetTester tester) async {
475+
final mockSharePlus = setupMockSharePlus();
476+
final message = eg.streamMessage();
477+
await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message));
478+
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
479+
480+
prepareRawContentResponseSuccess(store, message: message, rawContent: 'Hello world');
481+
await tapShareButton(tester);
482+
await tester.pump(Duration.zero);
483+
check(mockSharePlus.sharedString).equals('Hello world');
484+
});
485+
486+
testWidgets('request succeeds; sharing fails', (WidgetTester tester) async {
487+
final mockSharePlus = setupMockSharePlus();
488+
final message = eg.streamMessage();
489+
await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message));
490+
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
491+
492+
prepareRawContentResponseSuccess(store, message: message, rawContent: 'Hello world');
493+
mockSharePlus.resultString = 'dev.fluttercommunity.plus/share/unavailable';
494+
await tapShareButton(tester);
495+
await tester.pump(Duration.zero);
496+
check(mockSharePlus.sharedString).equals('Hello world');
497+
await tester.pump();
498+
await tester.tap(find.byWidget(checkErrorDialog(tester,
499+
expectedTitle: 'Sharing failed')));
500+
});
501+
502+
testWidgets('request has an error', (WidgetTester tester) async {
503+
final mockSharePlus = setupMockSharePlus();
504+
final message = eg.streamMessage();
505+
await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message));
506+
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
507+
508+
prepareRawContentResponseError(store);
509+
await tapShareButton(tester);
510+
await tester.pump(Duration.zero); // error arrives; error dialog shows
511+
512+
await tester.tap(find.byWidget(checkErrorDialog(tester,
513+
expectedTitle: 'Sharing failed',
514+
expectedMessage: 'That message does not seem to exist.',
515+
)));
516+
517+
check(mockSharePlus.sharedString).isNull();
518+
});
519+
});
520520
}

0 commit comments

Comments
 (0)