Skip to content

Commit db90fdb

Browse files
launch_url [nfc]: Move launchUrl logic to a new file
- Move the existing _launchUrl logic from content.dart to a new file named launch_url.dart. - Split function into pieces to handle realm-based and non-realm URLs. - Refactor error handling into a private function.
1 parent ba74abd commit db90fdb

File tree

2 files changed

+63
-53
lines changed

2 files changed

+63
-53
lines changed

lib/widgets/content.dart

+2-53
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1-
import 'package:flutter/foundation.dart';
21
import 'package:flutter/gestures.dart';
32
import 'package:flutter/material.dart';
4-
import 'package:flutter/services.dart';
53
import 'package:html/dom.dart' as dom;
64
import 'package:intl/intl.dart';
75
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
86

97
import '../api/core.dart';
108
import '../api/model/model.dart';
119
import '../model/avatar_url.dart';
12-
import '../model/binding.dart';
1310
import '../model/content.dart';
14-
import '../model/internal_link.dart';
1511
import 'code_block.dart';
16-
import 'dialog.dart';
1712
import 'icons.dart';
13+
import 'launch_url.dart';
1814
import 'lightbox.dart';
19-
import 'message_list.dart';
2015
import 'store.dart';
2116
import 'text.dart';
2217

@@ -525,7 +520,7 @@ class _BlockInlineContainerState extends State<_BlockInlineContainer> {
525520

526521
void _prepareRecognizers() {
527522
_recognizers.addEntries(widget.links.map((node) => MapEntry(node,
528-
TapGestureRecognizer()..onTap = () => _launchUrl(context, node.url))));
523+
TapGestureRecognizer()..onTap = () => launchUrlWithRealm(context, node.url))));
529524
}
530525

531526
void _disposeRecognizers() {
@@ -891,52 +886,6 @@ class GlobalTime extends StatelessWidget {
891886
}
892887
}
893888

894-
void _launchUrl(BuildContext context, String urlString) async {
895-
Future<void> showError(BuildContext context, String? message) {
896-
return showErrorDialog(context: context,
897-
title: 'Unable to open link',
898-
message: [
899-
'Link could not be opened: $urlString',
900-
if (message != null) message,
901-
].join("\n\n"));
902-
}
903-
904-
final store = PerAccountStoreWidget.of(context);
905-
final url = store.tryResolveUrl(urlString);
906-
if (url == null) { // TODO(log)
907-
await showError(context, null);
908-
return;
909-
}
910-
911-
final internalNarrow = parseInternalLink(url, store);
912-
if (internalNarrow != null) {
913-
Navigator.push(context,
914-
MessageListPage.buildRoute(context: context,
915-
narrow: internalNarrow));
916-
return;
917-
}
918-
919-
bool launched = false;
920-
String? errorMessage;
921-
try {
922-
launched = await ZulipBinding.instance.launchUrl(url,
923-
mode: switch (defaultTargetPlatform) {
924-
// On iOS we prefer LaunchMode.externalApplication because (for
925-
// HTTP URLs) LaunchMode.platformDefault uses SFSafariViewController,
926-
// which gives an awkward UX as described here:
927-
// https://chat.zulip.org/#narrow/stream/48-mobile/topic/in-app.20browser/near/1169118
928-
TargetPlatform.iOS => UrlLaunchMode.externalApplication,
929-
_ => UrlLaunchMode.platformDefault,
930-
});
931-
} on PlatformException catch (e) {
932-
errorMessage = e.message;
933-
}
934-
if (!launched) { // TODO(log)
935-
if (!context.mounted) return;
936-
await showError(context, errorMessage);
937-
}
938-
}
939-
940889
/// Like [Image.network], but includes [authHeader] if [src] is on-realm.
941890
///
942891
/// Use this to present image content in the ambient realm: avatars, images in

lib/widgets/launch_url.dart

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter/services.dart';
4+
5+
import '../model/binding.dart';
6+
import '../model/internal_link.dart';
7+
import 'dialog.dart';
8+
import 'message_list.dart';
9+
import 'store.dart';
10+
11+
/// Handles showing an error dialog with a customizable message.
12+
Future<void> _showError(BuildContext context, String? message, String urlString) {
13+
return showErrorDialog(
14+
context: context,
15+
title: 'Unable to open link',
16+
message: [
17+
'Link could not be opened: $urlString',
18+
if (message != null) message,
19+
].join("\n\n"));
20+
}
21+
22+
/// Launches a URL without considering a realm base URL.
23+
void launchUrlWithoutRealm(BuildContext context, Uri url) async {
24+
bool launched = false;
25+
String? errorMessage;
26+
try {
27+
launched = await ZulipBinding.instance.launchUrl(url,
28+
mode: switch (defaultTargetPlatform) {
29+
// On iOS we prefer LaunchMode.externalApplication because (for
30+
// HTTP URLs) LaunchMode.platformDefault uses SFSafariViewController,
31+
// which gives an awkward UX as described here:
32+
// https://chat.zulip.org/#narrow/stream/48-mobile/topic/in-app.20browser/near/1169118
33+
TargetPlatform.iOS => UrlLaunchMode.externalApplication,
34+
_ => UrlLaunchMode.platformDefault,
35+
});
36+
} on PlatformException catch (e) {
37+
errorMessage = e.message;
38+
}
39+
if (!launched) {
40+
if (!context.mounted) return;
41+
await _showError(context, errorMessage, url.toString());
42+
}
43+
}
44+
45+
/// Launches a URL considering a realm base URL.
46+
void launchUrlWithRealm(BuildContext context, String urlString) async {
47+
final store = PerAccountStoreWidget.of(context);
48+
final url = store.tryResolveUrl(urlString);
49+
if (url == null) { // TODO(log)
50+
await _showError(context, null, urlString);
51+
return;
52+
}
53+
54+
final internalNarrow = parseInternalLink(url, store);
55+
if (internalNarrow != null) {
56+
Navigator.push(context, MessageListPage.buildRoute(context: context, narrow: internalNarrow));
57+
return;
58+
}
59+
60+
launchUrlWithoutRealm(context, url);
61+
}

0 commit comments

Comments
 (0)