From 875cdaceaeeb63c6d54889b5b411e116ad2ab607 Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Sat, 29 Jun 2024 16:41:07 +0900 Subject: [PATCH] feat: add option to open url with internal browser --- lib/i18n/aria/aria.i18n.yaml | 1 + lib/i18n/aria/aria_ja-JP.i18n.yaml | 1 + lib/i18n/aria/aria_ja-KS.i18n.yaml | 1 + lib/i18n/strings.g.dart | 2 +- lib/i18n/strings_en_US.g.dart | 1 + lib/i18n/strings_ja_JP.g.dart | 1 + lib/i18n/strings_ja_KS.g.dart | 1 + lib/model/general_settings.dart | 2 ++ lib/model/general_settings.freezed.dart | 26 ++++++++++++-- lib/model/general_settings.g.dart | 12 +++++++ .../general_settings_notifier_provider.dart | 6 ++++ .../general_settings_notifier_provider.g.dart | 2 +- lib/util/launch_url.dart | 11 ++++++ lib/util/navigate.dart | 8 ++--- lib/view/dialog/paste_emojis_dialog.dart | 7 ++-- lib/view/page/about_aria_page.dart | 7 ++-- lib/view/page/about_misskey_page.dart | 10 +++--- lib/view/page/antenna_page.dart | 4 +-- lib/view/page/channel/channel_page.dart | 4 +-- lib/view/page/clip_page.dart | 4 +-- lib/view/page/emoji_page.dart | 7 ++-- lib/view/page/gallery/gallery_post_page.dart | 9 ++--- lib/view/page/list_page.dart | 4 +-- lib/view/page/login_page.dart | 4 +-- lib/view/page/misskey_games_page.dart | 8 ++--- lib/view/page/note_page.dart | 7 ++-- lib/view/page/page/page_page.dart | 12 ++----- lib/view/page/play/play_page.dart | 7 ++-- lib/view/page/server/server_overview.dart | 16 ++++----- lib/view/page/server/server_page.dart | 7 ++-- lib/view/page/settings/behavior_page.dart | 36 +++++++++++++++++++ lib/view/page/settings/languages_page.dart | 3 +- lib/view/page/user/user_home.dart | 13 +++---- lib/view/widget/drive_file_sheet.dart | 4 +-- lib/view/widget/media_card.dart | 4 +-- lib/view/widget/mfm/search.dart | 9 ++--- lib/view/widget/misskey_servers.dart | 4 +-- lib/view/widget/note_footer.dart | 3 +- lib/view/widget/note_sheet.dart | 7 ++-- lib/view/widget/play_widget.dart | 7 ++-- lib/view/widget/player_embed.dart | 11 +++--- lib/view/widget/skeb_status_widget.dart | 4 +-- lib/view/widget/twitter_embed.dart | 12 ++++--- lib/view/widget/user_sheet.dart | 6 ++-- 44 files changed, 194 insertions(+), 121 deletions(-) create mode 100644 lib/util/launch_url.dart diff --git a/lib/i18n/aria/aria.i18n.yaml b/lib/i18n/aria/aria.i18n.yaml index 50dbb0a2..6bf70941 100644 --- a/lib/i18n/aria/aria.i18n.yaml +++ b/lib/i18n/aria/aria.i18n.yaml @@ -125,3 +125,4 @@ userSaysSomething(rich): "{name} said something" userSaysSomethingSensitive(rich): "Post by {name} contains sensitive content" vibrateNote: "Vibrate on new note" vibrateNotification: "Vibrate on new notification" +webBrowser: "Web browser" diff --git a/lib/i18n/aria/aria_ja-JP.i18n.yaml b/lib/i18n/aria/aria_ja-JP.i18n.yaml index 4fba1d7c..8287a393 100644 --- a/lib/i18n/aria/aria_ja-JP.i18n.yaml +++ b/lib/i18n/aria/aria_ja-JP.i18n.yaml @@ -121,3 +121,4 @@ userSaysSomething(rich): "{name}が何かを言いました" userSaysSomethingSensitive(rich): "{name}のセンシティブなファイルを含む投稿" vibrateNote: "ノートを受信したときに振動する" vibrateNotification: "通知を受信したときに振動する" +webBrowser: "ウェブブラウザ" diff --git a/lib/i18n/aria/aria_ja-KS.i18n.yaml b/lib/i18n/aria/aria_ja-KS.i18n.yaml index d800de33..21622ea8 100644 --- a/lib/i18n/aria/aria_ja-KS.i18n.yaml +++ b/lib/i18n/aria/aria_ja-KS.i18n.yaml @@ -121,3 +121,4 @@ userSaysSomething(rich): "{name}が何か言うとるわ" userSaysSomethingSensitive(rich): "{name}のセンシティブなファイルを含む投稿" vibrateNote: "ノートを受信したときに振動する" vibrateNotification: "通知を受信したときに振動する" +webBrowser: "ウェブブラウザ" diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 74538e6c..8ede16ea 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,7 +4,7 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 31 -/// Strings: 51591 (1664 per locale) +/// Strings: 51594 (1664 per locale) // coverage:ignore-file // ignore_for_file: type=lint diff --git a/lib/i18n/strings_en_US.g.dart b/lib/i18n/strings_en_US.g.dart index 4a32c871..8ba4bbc6 100644 --- a/lib/i18n/strings_en_US.g.dart +++ b/lib/i18n/strings_en_US.g.dart @@ -219,6 +219,7 @@ class _StringsAriaEnUs { ]); String get vibrateNote => 'Vibrate on new note'; String get vibrateNotification => 'Vibrate on new notification'; + String get webBrowser => 'Web browser'; } // Path: misskey diff --git a/lib/i18n/strings_ja_JP.g.dart b/lib/i18n/strings_ja_JP.g.dart index 8a794795..6b7267f3 100644 --- a/lib/i18n/strings_ja_JP.g.dart +++ b/lib/i18n/strings_ja_JP.g.dart @@ -205,6 +205,7 @@ class _StringsAriaJaJp extends _StringsAriaEnUs { ]); @override String get vibrateNote => 'ノートを受信したときに振動する'; @override String get vibrateNotification => '通知を受信したときに振動する'; + @override String get webBrowser => 'ウェブブラウザ'; } // Path: misskey diff --git a/lib/i18n/strings_ja_KS.g.dart b/lib/i18n/strings_ja_KS.g.dart index ff90252f..8ed130bf 100644 --- a/lib/i18n/strings_ja_KS.g.dart +++ b/lib/i18n/strings_ja_KS.g.dart @@ -205,6 +205,7 @@ class _StringsAriaJaKs extends _StringsAriaEnUs { ]); @override String get vibrateNote => 'ノートを受信したときに振動する'; @override String get vibrateNotification => '通知を受信したときに振動する'; + @override String get webBrowser => 'ウェブブラウザ'; } // Path: misskey diff --git a/lib/model/general_settings.dart b/lib/model/general_settings.dart index 5894e2ba..41ca520a 100644 --- a/lib/model/general_settings.dart +++ b/lib/model/general_settings.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:url_launcher/url_launcher.dart'; import '../i18n/strings.g.dart'; @@ -104,6 +105,7 @@ class GeneralSettings with _$GeneralSettings { @Default(true) bool confirmBeforePost, @Default(true) bool confirmBeforeReact, @Default(true) bool confirmBeforeFollow, + @Default(LaunchMode.externalApplication) LaunchMode launchMode, // Theme @Default(ThemeMode.system) ThemeMode themeMode, diff --git a/lib/model/general_settings.freezed.dart b/lib/model/general_settings.freezed.dart index 8372e6ab..3b931830 100644 --- a/lib/model/general_settings.freezed.dart +++ b/lib/model/general_settings.freezed.dart @@ -93,7 +93,8 @@ mixin _$GeneralSettings { NoteActionType get noteLongPressAction => throw _privateConstructorUsedError; bool get confirmBeforePost => throw _privateConstructorUsedError; bool get confirmBeforeReact => throw _privateConstructorUsedError; - bool get confirmBeforeFollow => throw _privateConstructorUsedError; // Theme + bool get confirmBeforeFollow => throw _privateConstructorUsedError; + LaunchMode get launchMode => throw _privateConstructorUsedError; // Theme ThemeMode get themeMode => throw _privateConstructorUsedError; String get lightThemeId => throw _privateConstructorUsedError; String get darkThemeId => throw _privateConstructorUsedError; @@ -175,6 +176,7 @@ abstract class $GeneralSettingsCopyWith<$Res> { bool confirmBeforePost, bool confirmBeforeReact, bool confirmBeforeFollow, + LaunchMode launchMode, ThemeMode themeMode, String lightThemeId, String darkThemeId}); @@ -256,6 +258,7 @@ class _$GeneralSettingsCopyWithImpl<$Res, $Val extends GeneralSettings> Object? confirmBeforePost = null, Object? confirmBeforeReact = null, Object? confirmBeforeFollow = null, + Object? launchMode = null, Object? themeMode = null, Object? lightThemeId = null, Object? darkThemeId = null, @@ -513,6 +516,10 @@ class _$GeneralSettingsCopyWithImpl<$Res, $Val extends GeneralSettings> ? _value.confirmBeforeFollow : confirmBeforeFollow // ignore: cast_nullable_to_non_nullable as bool, + launchMode: null == launchMode + ? _value.launchMode + : launchMode // ignore: cast_nullable_to_non_nullable + as LaunchMode, themeMode: null == themeMode ? _value.themeMode : themeMode // ignore: cast_nullable_to_non_nullable @@ -602,6 +609,7 @@ abstract class _$$GeneralSettingsImplCopyWith<$Res> bool confirmBeforePost, bool confirmBeforeReact, bool confirmBeforeFollow, + LaunchMode launchMode, ThemeMode themeMode, String lightThemeId, String darkThemeId}); @@ -681,6 +689,7 @@ class __$$GeneralSettingsImplCopyWithImpl<$Res> Object? confirmBeforePost = null, Object? confirmBeforeReact = null, Object? confirmBeforeFollow = null, + Object? launchMode = null, Object? themeMode = null, Object? lightThemeId = null, Object? darkThemeId = null, @@ -938,6 +947,10 @@ class __$$GeneralSettingsImplCopyWithImpl<$Res> ? _value.confirmBeforeFollow : confirmBeforeFollow // ignore: cast_nullable_to_non_nullable as bool, + launchMode: null == launchMode + ? _value.launchMode + : launchMode // ignore: cast_nullable_to_non_nullable + as LaunchMode, themeMode: null == themeMode ? _value.themeMode : themeMode // ignore: cast_nullable_to_non_nullable @@ -1022,6 +1035,7 @@ class _$GeneralSettingsImpl implements _GeneralSettings { this.confirmBeforePost = true, this.confirmBeforeReact = true, this.confirmBeforeFollow = true, + this.launchMode = LaunchMode.externalApplication, this.themeMode = ThemeMode.system, this.lightThemeId = 'a58a0abb-ff8c-476a-8dec-0ad7837e7e96', this.darkThemeId = '66e7e5a9-cd43-42cd-837d-12f47841fa34'}); @@ -1223,6 +1237,9 @@ class _$GeneralSettingsImpl implements _GeneralSettings { @override @JsonKey() final bool confirmBeforeFollow; + @override + @JsonKey() + final LaunchMode launchMode; // Theme @override @JsonKey() @@ -1236,7 +1253,7 @@ class _$GeneralSettingsImpl implements _GeneralSettings { @override String toString() { - return 'GeneralSettings(locale: $locale, collapseRenotes: $collapseRenotes, sensitive: $sensitive, highlightSensitiveMedia: $highlightSensitiveMedia, animatedMfm: $animatedMfm, advancedMfm: $advancedMfm, showReactionsCount: $showReactionsCount, loadRawImages: $loadRawImages, instanceTicker: $instanceTicker, showNoteCreatedAt: $showNoteCreatedAt, showAvatarsInNote: $showAvatarsInNote, showAvatarsInSubNote: $showAvatarsInSubNote, squareAvatars: $squareAvatars, showAvatarDecorations: $showAvatarDecorations, showQuoteButtonInNoteFooter: $showQuoteButtonInNoteFooter, showLikeButtonInNoteFooter: $showLikeButtonInNoteFooter, showClipButtonInNoteFooter: $showClipButtonInNoteFooter, showTranslateButtonInNoteFooter: $showTranslateButtonInNoteFooter, showNoteReactionsViewer: $showNoteReactionsViewer, showSubNoteReactionsViewer: $showSubNoteReactionsViewer, showNoteFooter: $showNoteFooter, showSubNoteFooter: $showSubNoteFooter, emojiStyle: $emojiStyle, fontFamily: $fontFamily, fontSize: $fontSize, lineHeight: $lineHeight, avatarScale: $avatarScale, reactionsDisplayScale: $reactionsDisplayScale, limitWidthOfReaction: $limitWidthOfReaction, noteFooterScale: $noteFooterScale, noteVerticalPadding: $noteVerticalPadding, noteHorizontalPadding: $noteHorizontalPadding, publicNoteBackgroundColor: $publicNoteBackgroundColor, homeNoteBackgroundColor: $homeNoteBackgroundColor, followersNoteBackgroundColor: $followersNoteBackgroundColor, specifiedNoteBackgroundColor: $specifiedNoteBackgroundColor, emojiPickerUseDialog: $emojiPickerUseDialog, emojiPickerScale: $emojiPickerScale, emojiPickerAutofocus: $emojiPickerAutofocus, emojiPickerKeepOpen: $emojiPickerKeepOpen, dataSaverMedia: $dataSaverMedia, dataSaverAvatar: $dataSaverAvatar, dataSaverUrlPreview: $dataSaverUrlPreview, disableDataSaverWhenOnWifi: $disableDataSaverWhenOnWifi, disableShowingAnimatedImages: $disableShowingAnimatedImages, forceShowAds: $forceShowAds, useGroupedNotifications: $useGroupedNotifications, showTimelineTabBarAtBottom: $showTimelineTabBarAtBottom, showMenuButtonInTabBar: $showMenuButtonInTabBar, alwaysShowTabHeader: $alwaysShowTabHeader, showTimelineLastViewedAt: $showTimelineLastViewedAt, vibrateNote: $vibrateNote, vibrateNotification: $vibrateNotification, enableInfiniteScroll: $enableInfiniteScroll, keepScreenOn: $keepScreenOn, enableHorizontalSwipe: $enableHorizontalSwipe, openSensitiveMediaOnDoubleTap: $openSensitiveMediaOnDoubleTap, noteTapAction: $noteTapAction, noteDoubleTapAction: $noteDoubleTapAction, noteLongPressAction: $noteLongPressAction, confirmBeforePost: $confirmBeforePost, confirmBeforeReact: $confirmBeforeReact, confirmBeforeFollow: $confirmBeforeFollow, themeMode: $themeMode, lightThemeId: $lightThemeId, darkThemeId: $darkThemeId)'; + return 'GeneralSettings(locale: $locale, collapseRenotes: $collapseRenotes, sensitive: $sensitive, highlightSensitiveMedia: $highlightSensitiveMedia, animatedMfm: $animatedMfm, advancedMfm: $advancedMfm, showReactionsCount: $showReactionsCount, loadRawImages: $loadRawImages, instanceTicker: $instanceTicker, showNoteCreatedAt: $showNoteCreatedAt, showAvatarsInNote: $showAvatarsInNote, showAvatarsInSubNote: $showAvatarsInSubNote, squareAvatars: $squareAvatars, showAvatarDecorations: $showAvatarDecorations, showQuoteButtonInNoteFooter: $showQuoteButtonInNoteFooter, showLikeButtonInNoteFooter: $showLikeButtonInNoteFooter, showClipButtonInNoteFooter: $showClipButtonInNoteFooter, showTranslateButtonInNoteFooter: $showTranslateButtonInNoteFooter, showNoteReactionsViewer: $showNoteReactionsViewer, showSubNoteReactionsViewer: $showSubNoteReactionsViewer, showNoteFooter: $showNoteFooter, showSubNoteFooter: $showSubNoteFooter, emojiStyle: $emojiStyle, fontFamily: $fontFamily, fontSize: $fontSize, lineHeight: $lineHeight, avatarScale: $avatarScale, reactionsDisplayScale: $reactionsDisplayScale, limitWidthOfReaction: $limitWidthOfReaction, noteFooterScale: $noteFooterScale, noteVerticalPadding: $noteVerticalPadding, noteHorizontalPadding: $noteHorizontalPadding, publicNoteBackgroundColor: $publicNoteBackgroundColor, homeNoteBackgroundColor: $homeNoteBackgroundColor, followersNoteBackgroundColor: $followersNoteBackgroundColor, specifiedNoteBackgroundColor: $specifiedNoteBackgroundColor, emojiPickerUseDialog: $emojiPickerUseDialog, emojiPickerScale: $emojiPickerScale, emojiPickerAutofocus: $emojiPickerAutofocus, emojiPickerKeepOpen: $emojiPickerKeepOpen, dataSaverMedia: $dataSaverMedia, dataSaverAvatar: $dataSaverAvatar, dataSaverUrlPreview: $dataSaverUrlPreview, disableDataSaverWhenOnWifi: $disableDataSaverWhenOnWifi, disableShowingAnimatedImages: $disableShowingAnimatedImages, forceShowAds: $forceShowAds, useGroupedNotifications: $useGroupedNotifications, showTimelineTabBarAtBottom: $showTimelineTabBarAtBottom, showMenuButtonInTabBar: $showMenuButtonInTabBar, alwaysShowTabHeader: $alwaysShowTabHeader, showTimelineLastViewedAt: $showTimelineLastViewedAt, vibrateNote: $vibrateNote, vibrateNotification: $vibrateNotification, enableInfiniteScroll: $enableInfiniteScroll, keepScreenOn: $keepScreenOn, enableHorizontalSwipe: $enableHorizontalSwipe, openSensitiveMediaOnDoubleTap: $openSensitiveMediaOnDoubleTap, noteTapAction: $noteTapAction, noteDoubleTapAction: $noteDoubleTapAction, noteLongPressAction: $noteLongPressAction, confirmBeforePost: $confirmBeforePost, confirmBeforeReact: $confirmBeforeReact, confirmBeforeFollow: $confirmBeforeFollow, launchMode: $launchMode, themeMode: $themeMode, lightThemeId: $lightThemeId, darkThemeId: $darkThemeId)'; } @override @@ -1337,6 +1354,7 @@ class _$GeneralSettingsImpl implements _GeneralSettings { (identical(other.confirmBeforePost, confirmBeforePost) || other.confirmBeforePost == confirmBeforePost) && (identical(other.confirmBeforeReact, confirmBeforeReact) || other.confirmBeforeReact == confirmBeforeReact) && (identical(other.confirmBeforeFollow, confirmBeforeFollow) || other.confirmBeforeFollow == confirmBeforeFollow) && + (identical(other.launchMode, launchMode) || other.launchMode == launchMode) && (identical(other.themeMode, themeMode) || other.themeMode == themeMode) && (identical(other.lightThemeId, lightThemeId) || other.lightThemeId == lightThemeId) && (identical(other.darkThemeId, darkThemeId) || other.darkThemeId == darkThemeId)); @@ -1409,6 +1427,7 @@ class _$GeneralSettingsImpl implements _GeneralSettings { confirmBeforePost, confirmBeforeReact, confirmBeforeFollow, + launchMode, themeMode, lightThemeId, darkThemeId @@ -1495,6 +1514,7 @@ abstract class _GeneralSettings implements GeneralSettings { final bool confirmBeforePost, final bool confirmBeforeReact, final bool confirmBeforeFollow, + final LaunchMode launchMode, final ThemeMode themeMode, final String lightThemeId, final String darkThemeId}) = _$GeneralSettingsImpl; @@ -1634,6 +1654,8 @@ abstract class _GeneralSettings implements GeneralSettings { bool get confirmBeforeReact; @override bool get confirmBeforeFollow; + @override + LaunchMode get launchMode; @override // Theme ThemeMode get themeMode; @override diff --git a/lib/model/general_settings.g.dart b/lib/model/general_settings.g.dart index 7d6faadc..58791ca9 100644 --- a/lib/model/general_settings.g.dart +++ b/lib/model/general_settings.g.dart @@ -108,6 +108,9 @@ _$GeneralSettingsImpl _$$GeneralSettingsImplFromJson( confirmBeforePost: json['confirmBeforePost'] as bool? ?? true, confirmBeforeReact: json['confirmBeforeReact'] as bool? ?? true, confirmBeforeFollow: json['confirmBeforeFollow'] as bool? ?? true, + launchMode: + $enumDecodeNullable(_$LaunchModeEnumMap, json['launchMode']) ?? + LaunchMode.externalApplication, themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ?? ThemeMode.system, lightThemeId: json['lightThemeId'] as String? ?? @@ -204,6 +207,7 @@ Map _$$GeneralSettingsImplToJson( val['confirmBeforePost'] = instance.confirmBeforePost; val['confirmBeforeReact'] = instance.confirmBeforeReact; val['confirmBeforeFollow'] = instance.confirmBeforeFollow; + val['launchMode'] = _$LaunchModeEnumMap[instance.launchMode]!; val['themeMode'] = _$ThemeModeEnumMap[instance.themeMode]!; val['lightThemeId'] = instance.lightThemeId; val['darkThemeId'] = instance.darkThemeId; @@ -274,6 +278,14 @@ const _$NoteActionTypeEnumMap = { NoteActionType.reaction: 'reaction', }; +const _$LaunchModeEnumMap = { + LaunchMode.platformDefault: 'platformDefault', + LaunchMode.inAppWebView: 'inAppWebView', + LaunchMode.inAppBrowserView: 'inAppBrowserView', + LaunchMode.externalApplication: 'externalApplication', + LaunchMode.externalNonBrowserApplication: 'externalNonBrowserApplication', +}; + const _$ThemeModeEnumMap = { ThemeMode.system: 'system', ThemeMode.light: 'light', diff --git a/lib/provider/general_settings_notifier_provider.dart b/lib/provider/general_settings_notifier_provider.dart index bec36ed8..69f15dfe 100644 --- a/lib/provider/general_settings_notifier_provider.dart +++ b/lib/provider/general_settings_notifier_provider.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:url_launcher/url_launcher.dart'; import '../i18n/strings.g.dart'; import '../model/general_settings.dart'; @@ -403,6 +404,11 @@ class GeneralSettingsNotifier extends _$GeneralSettingsNotifier { await _save(); } + Future setLaunchMode(LaunchMode launchMode) async { + state = state.copyWith(launchMode: launchMode); + await _save(); + } + Future setThemeMode(ThemeMode themeMode) async { state = state.copyWith(themeMode: themeMode); await _save(); diff --git a/lib/provider/general_settings_notifier_provider.g.dart b/lib/provider/general_settings_notifier_provider.g.dart index e97cf7e0..682bd95a 100644 --- a/lib/provider/general_settings_notifier_provider.g.dart +++ b/lib/provider/general_settings_notifier_provider.g.dart @@ -7,7 +7,7 @@ part of 'general_settings_notifier_provider.dart'; // ************************************************************************** String _$generalSettingsNotifierHash() => - r'802aaca9f3c19688d30c0b1756359663f66d177a'; + r'd8c0109cc8d8a7174c946a99dd83dd5cd0a9f686'; /// See also [GeneralSettingsNotifier]. @ProviderFor(GeneralSettingsNotifier) diff --git a/lib/util/launch_url.dart b/lib/util/launch_url.dart new file mode 100644 index 00000000..88d04373 --- /dev/null +++ b/lib/util/launch_url.dart @@ -0,0 +1,11 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:url_launcher/url_launcher.dart' as launcher; + +import '../provider/general_settings_notifier_provider.dart'; + +Future launchUrl(WidgetRef ref, Uri url) { + return launcher.launchUrl( + url, + mode: ref.read(generalSettingsNotifierProvider).launchMode, + ); +} diff --git a/lib/util/navigate.dart b/lib/util/navigate.dart index 4dfd66c4..22c96ee1 100644 --- a/lib/util/navigate.dart +++ b/lib/util/navigate.dart @@ -1,9 +1,9 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../model/account.dart'; import '../provider/emojis_notifier_provider.dart'; +import 'launch_url.dart'; Future navigate(WidgetRef ref, Account account, String link) async { final url = Uri.tryParse(link); @@ -25,7 +25,7 @@ Future navigate(WidgetRef ref, Account account, String link) async { if (!ref.context.mounted) return; await ref.context.push('/${url.host}${url.path}'); } catch (_) { - await launchUrl(url, mode: LaunchMode.externalApplication); + await launchUrl(ref, url); } } else { await ref.context.push('/$account${url.path}'); @@ -40,13 +40,13 @@ Future navigate(WidgetRef ref, Account account, String link) async { if (!ref.context.mounted) return; await ref.context.push('/${url.host}${url.path}'); } catch (_) { - await launchUrl(url, mode: LaunchMode.externalApplication); + await launchUrl(ref, url); } } else { await ref.context.push('/$account${url.path}'); } } else { - await launchUrl(url, mode: LaunchMode.externalApplication); + await launchUrl(ref, url); } } } diff --git a/lib/view/dialog/paste_emojis_dialog.dart b/lib/view/dialog/paste_emojis_dialog.dart index 89994e6b..7ffc6806 100644 --- a/lib/view/dialog/paste_emojis_dialog.dart +++ b/lib/view/dialog/paste_emojis_dialog.dart @@ -3,7 +3,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:json5/json5.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../constant/shortcuts.dart'; import '../../extension/string_extension.dart'; @@ -12,6 +11,7 @@ import '../../i18n/strings.g.dart'; import '../../model/account.dart'; import '../../provider/api/endpoints_provider.dart'; import '../../provider/misskey_colors_provider.dart'; +import '../../util/launch_url.dart'; import 'message_dialog.dart'; const _sampleEmojis = """ @@ -64,10 +64,7 @@ class PasteEmojisDialog extends HookConsumerWidget { text: registryUrl.toString().breakAll, style: TextStyle(color: colors.link), recognizer: recognizer - ..onTap = () => launchUrl( - registryUrl, - mode: LaunchMode.externalApplication, - ), + ..onTap = () => launchUrl(ref, registryUrl), ), ), ), diff --git a/lib/view/page/about_aria_page.dart b/lib/view/page/about_aria_page.dart index bd401904..2c91c173 100644 --- a/lib/view/page/about_aria_page.dart +++ b/lib/view/page/about_aria_page.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../gen/assets.gen.dart'; import '../../i18n/strings.g.dart'; import '../../provider/misskey_colors_provider.dart'; +import '../../util/launch_url.dart'; import '../widget/url_sheet.dart'; class AboutAriaPage extends HookConsumerWidget { @@ -61,6 +61,7 @@ class AboutAriaPage extends HookConsumerWidget { baseline: TextBaseline.alphabetic, child: InkWell( onTap: () => launchUrl( + ref, Uri.https( 'github.com', 'shiosyakeyakini-info/miria', @@ -85,6 +86,7 @@ class AboutAriaPage extends HookConsumerWidget { baseline: TextBaseline.alphabetic, child: InkWell( onTap: () => launchUrl( + ref, Uri.https('github.com', 'misskey-dev/misskey'), ), onLongPress: () => showModalBottomSheet( @@ -128,6 +130,7 @@ class AboutAriaPage extends HookConsumerWidget { baseline: TextBaseline.alphabetic, child: InkWell( onTap: () => launchUrl( + ref, Uri.https( 'creativecommons.org', 'licenses/by/4.0', @@ -158,8 +161,8 @@ class AboutAriaPage extends HookConsumerWidget { leading: const Icon(Icons.code), title: Text(t.misskey.aboutMisskey_.source), onTap: () => launchUrl( + ref, Uri.https('github.com', 'poppingmoon/aria'), - mode: LaunchMode.externalApplication, ), ), ListTile( diff --git a/lib/view/page/about_misskey_page.dart b/lib/view/page/about_misskey_page.dart index 43267fbb..3a64b530 100644 --- a/lib/view/page/about_misskey_page.dart +++ b/lib/view/page/about_misskey_page.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../gen/assets.gen.dart'; import '../../i18n/strings.g.dart'; +import '../../util/launch_url.dart'; class AboutMisskeyPage extends ConsumerWidget { const AboutMisskeyPage({super.key}); @@ -37,8 +37,8 @@ class AboutMisskeyPage extends ConsumerWidget { Text(t.misskey.aboutMisskey_.about), TextButton( onPressed: () => launchUrl( + ref, Uri.https('misskey-hub.net', 'docs/about-misskey'), - mode: LaunchMode.externalApplication, ), child: Text(t.misskey.learnMore), ), @@ -52,24 +52,24 @@ class AboutMisskeyPage extends ConsumerWidget { '${t.misskey.aboutMisskey_.source} (${t.misskey.aboutMisskey_.original})', ), onTap: () => launchUrl( + ref, Uri.https('github.com', 'misskey-dev/misskey'), - mode: LaunchMode.externalApplication, ), ), ListTile( leading: const Icon(Icons.translate), title: Text(t.misskey.aboutMisskey_.translation), onTap: () => launchUrl( + ref, Uri.https('crowdin.com', 'project/misskey'), - mode: LaunchMode.externalApplication, ), ), ListTile( leading: const Icon(Icons.savings), title: Text(t.misskey.aboutMisskey_.donate), onTap: () => launchUrl( + ref, Uri.https('www.patreon.com', 'syuilo'), - mode: LaunchMode.externalApplication, ), ), ], diff --git a/lib/view/page/antenna_page.dart b/lib/view/page/antenna_page.dart index b22c40d4..6046b008 100644 --- a/lib/view/page/antenna_page.dart +++ b/lib/view/page/antenna_page.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; @@ -13,6 +12,7 @@ import '../../provider/api/antennas_notifier_provider.dart'; import '../../provider/api/timeline_notes_notifier_provider.dart'; import '../../util/copy_text.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../dialog/antenna_settings_dialog.dart'; import '../dialog/confirmation_dialog.dart'; import '../widget/note_widget.dart'; @@ -74,8 +74,8 @@ class AntennaPage extends ConsumerWidget { itemBuilder: (context) => [ PopupMenuItem( onTap: () => launchUrl( + ref, Uri.https(account.host, 'my/antennas/$antennaId'), - mode: LaunchMode.externalApplication, ), child: Text(t.aria.openInBrowser), ), diff --git a/lib/view/page/channel/channel_page.dart b/lib/view/page/channel/channel_page.dart index c848cd87..87cd9e66 100644 --- a/lib/view/page/channel/channel_page.dart +++ b/lib/view/page/channel/channel_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../../i18n/strings.g.dart'; import '../../../model/account.dart'; @@ -9,6 +8,7 @@ import '../../../model/tab_settings.dart'; import '../../../provider/api/channel_notifier_provider.dart'; import '../../../provider/api/post_notifier_provider.dart'; import '../../../util/copy_text.dart'; +import '../../../util/launch_url.dart'; import '../../widget/timeline_list_view.dart'; import 'channel_featured.dart'; import 'channel_home.dart'; @@ -49,8 +49,8 @@ class ChannelPage extends ConsumerWidget { ), PopupMenuItem( onTap: () => launchUrl( + ref, Uri.https(account.host, 'channels/$channelId'), - mode: LaunchMode.externalApplication, ), child: Text(t.aria.openInBrowser), ), diff --git a/lib/view/page/clip_page.dart b/lib/view/page/clip_page.dart index b65f333b..a4809a47 100644 --- a/lib/view/page/clip_page.dart +++ b/lib/view/page/clip_page.dart @@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; @@ -14,6 +13,7 @@ import '../../provider/api/clip_notifier_provider.dart'; import '../../provider/api/clips_notifier_provider.dart'; import '../../util/copy_text.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../dialog/clip_settings_dialog.dart'; import '../dialog/confirmation_dialog.dart'; import '../widget/like_button.dart'; @@ -72,8 +72,8 @@ class ClipPage extends HookConsumerWidget { itemBuilder: (context) => [ PopupMenuItem( onTap: () => launchUrl( + ref, Uri.https(account.host, 'clips/$clipId'), - mode: LaunchMode.externalApplication, ), child: Text(t.aria.openInBrowser), ), diff --git a/lib/view/page/emoji_page.dart b/lib/view/page/emoji_page.dart index 0489b191..72df43cf 100644 --- a/lib/view/page/emoji_page.dart +++ b/lib/view/page/emoji_page.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; import '../../provider/api/emoji_response_provider.dart'; import '../../provider/misskey_colors_provider.dart'; import '../../util/copy_text.dart'; +import '../../util/launch_url.dart'; import '../dialog/image_dialog.dart'; import '../widget/error_message.dart'; import '../widget/image_widget.dart'; @@ -138,10 +138,7 @@ class EmojiPage extends ConsumerWidget { label: t.misskey.emojiUrl, child: UrlWidget( url: emoji.url.toString(), - onTap: () => launchUrl( - emoji.url!, - mode: LaunchMode.externalApplication, - ), + onTap: () => launchUrl(ref, emoji.url!), style: TextStyle(color: colors.link), ), ), diff --git a/lib/view/page/gallery/gallery_post_page.dart b/lib/view/page/gallery/gallery_post_page.dart index b481eced..eef20d2a 100644 --- a/lib/view/page/gallery/gallery_post_page.dart +++ b/lib/view/page/gallery/gallery_post_page.dart @@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../../i18n/strings.g.dart'; import '../../../model/account.dart'; @@ -13,6 +12,7 @@ import '../../../provider/api/post_notifier_provider.dart'; import '../../../provider/api/user_gallery_posts_notifier_provider.dart'; import '../../../util/copy_text.dart'; import '../../../util/future_with_dialog.dart'; +import '../../../util/launch_url.dart'; import '../../dialog/image_gallery_dialog.dart'; import '../../widget/ad_widget.dart'; import '../../widget/error_message.dart'; @@ -68,10 +68,7 @@ class GalleryPostPage extends ConsumerWidget { child: Text(t.misskey.copyLink), ), PopupMenuItem( - onTap: () => launchUrl( - url, - mode: LaunchMode.externalApplication, - ), + onTap: () => launchUrl(ref, url), child: Text(t.aria.openInBrowser), ), PopupMenuItem( @@ -180,8 +177,8 @@ class GalleryPostPage extends ConsumerWidget { IconButton( tooltip: t.aria.openInBrowser, onPressed: () => launchUrl( + ref, url, - mode: LaunchMode.externalApplication, ), icon: const Icon(Icons.open_in_browser), ), diff --git a/lib/view/page/list_page.dart b/lib/view/page/list_page.dart index cf0cf949..b17d1b3f 100644 --- a/lib/view/page/list_page.dart +++ b/lib/view/page/list_page.dart @@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; @@ -14,6 +13,7 @@ import '../../provider/api/lists_notifier_provider.dart'; import '../../provider/api/timeline_notes_notifier_provider.dart'; import '../../util/copy_text.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../dialog/confirmation_dialog.dart'; import '../dialog/list_settings_dialog.dart'; import '../widget/note_widget.dart'; @@ -66,13 +66,13 @@ class ListPage extends HookConsumerWidget { itemBuilder: (context) => [ PopupMenuItem( onTap: () => launchUrl( + ref, Uri.https( account.host, list?.isPublic ?? false ? 'lists/$listId' : 'my/lists/$listId', ), - mode: LaunchMode.externalApplication, ), child: Text(t.aria.openInBrowser), ), diff --git a/lib/view/page/login_page.dart b/lib/view/page/login_page.dart index c7ccb25b..24b4254d 100644 --- a/lib/view/page/login_page.dart +++ b/lib/view/page/login_page.dart @@ -5,11 +5,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../provider/miauth_notifier_provider.dart'; import '../../provider/misskey_servers_provider.dart'; +import '../../util/launch_url.dart'; import '../../util/punycode.dart'; import '../dialog/misskey_server_list_dialog.dart'; import '../widget/image_widget.dart'; @@ -25,7 +25,7 @@ class LoginPage extends HookConsumerWidget { .toLowerCase(); final url = ref.read(miAuthNotifierProvider.notifier).buildMiAuthUrl(trimmed); - launchUrl(url, mode: LaunchMode.externalApplication); + launchUrl(ref, url); ref.context.push('/login/authenticate'); } diff --git a/lib/view/page/misskey_games_page.dart b/lib/view/page/misskey_games_page.dart index fc22a48b..a10d6895 100644 --- a/lib/view/page/misskey_games_page.dart +++ b/lib/view/page/misskey_games_page.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../extension/user_extension.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; import '../../provider/api/reversi_invitations_provider.dart'; +import '../../util/launch_url.dart'; class MisskeyGamesPage extends ConsumerWidget { const MisskeyGamesPage({super.key, required this.account}); @@ -24,12 +24,12 @@ class MisskeyGamesPage extends ConsumerWidget { ListTile( title: const Text('🍪👈'), trailing: const Icon(Icons.navigate_next), - onTap: () => launchUrl(Uri.https(account.host, 'clicker')), + onTap: () => launchUrl(ref, Uri.https(account.host, 'clicker')), ), ListTile( title: Text(t.misskey.bubbleGame), trailing: const Icon(Icons.navigate_next), - onTap: () => launchUrl(Uri.https(account.host, 'bubble-game')), + onTap: () => launchUrl(ref, Uri.https(account.host, 'bubble-game')), ), ListTile( title: Text(t.misskey.reversi_.reversi), @@ -39,7 +39,7 @@ class MisskeyGamesPage extends ConsumerWidget { ) : null, trailing: const Icon(Icons.navigate_next), - onTap: () => launchUrl(Uri.https(account.host, 'reversi')), + onTap: () => launchUrl(ref, Uri.https(account.host, 'reversi')), ), ], ), diff --git a/lib/view/page/note_page.dart b/lib/view/page/note_page.dart index 11e85cf6..1b7e9eec 100644 --- a/lib/view/page/note_page.dart +++ b/lib/view/page/note_page.dart @@ -3,7 +3,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; @@ -19,6 +18,7 @@ import '../../provider/misskey_colors_provider.dart'; import '../../provider/note_provider.dart'; import '../../provider/notes_notifier_provider.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../widget/note_detailed_widget.dart'; import '../widget/note_fallback_widget.dart'; import '../widget/note_widget.dart'; @@ -289,10 +289,7 @@ class NotePage extends HookConsumerWidget { Wrap( children: [ TextButton( - onPressed: () => launchUrl( - remoteUrl, - mode: LaunchMode.externalApplication, - ), + onPressed: () => launchUrl(ref, remoteUrl), child: Text(t.misskey.showOnRemote), ), if (remoteNoteId != null) diff --git a/lib/view/page/page/page_page.dart b/lib/view/page/page/page_page.dart index d4fa2778..78aa6d81 100644 --- a/lib/view/page/page/page_page.dart +++ b/lib/view/page/page/page_page.dart @@ -4,7 +4,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:mfm_parser/mfm_parser.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; import 'package:share_plus/share_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../../i18n/strings.g.dart'; import '../../../model/account.dart'; @@ -14,6 +13,7 @@ import '../../../provider/api/user_pages_notifier_provider.dart'; import '../../../util/copy_text.dart'; import '../../../util/extract_url.dart'; import '../../../util/future_with_dialog.dart'; +import '../../../util/launch_url.dart'; import '../../dialog/image_dialog.dart'; import '../../widget/ad_widget.dart'; import '../../widget/error_message.dart'; @@ -166,10 +166,7 @@ class PagePage extends ConsumerWidget { child: Text(t.misskey.copyLink), ), PopupMenuItem( - onTap: () => launchUrl( - url, - mode: LaunchMode.externalApplication, - ), + onTap: () => launchUrl(ref, url), child: Text(t.aria.openInBrowser), ), PopupMenuItem( @@ -329,10 +326,7 @@ class PagePage extends ConsumerWidget { ), IconButton( tooltip: t.aria.openInBrowser, - onPressed: () => launchUrl( - url, - mode: LaunchMode.externalApplication, - ), + onPressed: () => launchUrl(ref, url), icon: const Icon(Icons.open_in_browser), ), IconButton( diff --git a/lib/view/page/play/play_page.dart b/lib/view/page/play/play_page.dart index 31759694..b1711081 100644 --- a/lib/view/page/play/play_page.dart +++ b/lib/view/page/play/play_page.dart @@ -3,7 +3,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../../i18n/strings.g.dart'; import '../../../model/account.dart'; @@ -11,6 +10,7 @@ import '../../../provider/api/play_notifier_provider.dart'; import '../../../provider/api/post_notifier_provider.dart'; import '../../../provider/misskey_colors_provider.dart'; import '../../../util/copy_text.dart'; +import '../../../util/launch_url.dart'; import '../../widget/ad_widget.dart'; import '../../widget/error_message.dart'; import '../../widget/follow_button.dart'; @@ -60,10 +60,7 @@ class PlayPage extends HookConsumerWidget { child: Text(t.misskey.copyLink), ), PopupMenuItem( - onTap: () => launchUrl( - url, - mode: LaunchMode.externalApplication, - ), + onTap: () => launchUrl(ref, url), child: Text(t.aria.openInBrowser), ), PopupMenuItem( diff --git a/lib/view/page/server/server_overview.dart b/lib/view/page/server/server_overview.dart index cb27d1f5..14ca0156 100644 --- a/lib/view/page/server/server_overview.dart +++ b/lib/view/page/server/server_overview.dart @@ -5,7 +5,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:misskey_dart/misskey_dart.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../../constant/colors.dart'; import '../../../i18n/strings.g.dart'; @@ -15,6 +14,7 @@ import '../../../provider/api/meta_notifier_provider.dart'; import '../../../provider/api/stats_provider.dart'; import '../../../provider/misskey_colors_provider.dart'; import '../../../provider/node_info_provider.dart'; +import '../../../util/launch_url.dart'; import '../../widget/error_message.dart'; import '../../widget/image_widget.dart'; import '../../widget/key_value_widget.dart'; @@ -118,7 +118,7 @@ class ServerOverview extends ConsumerWidget { child: Html( data: meta?.description ?? instance?.description ?? '', onLinkTap: (url, _, __) => - url != null ? launchUrl(Uri.parse(url)) : null, + url != null ? launchUrl(ref, Uri.parse(url)) : null, style: { 'body': Style(margin: Margins.all(0)), 'a': Style( @@ -159,12 +159,12 @@ class ServerOverview extends ConsumerWidget { leading: const Icon(Icons.code), title: Text(t.misskey.sourceCode), onTap: () => launchUrl( + ref, meta.repositoryUrl ?? Uri.https( host, 'tarball/misskey-${meta.version}.tar.gz', ), - mode: LaunchMode.externalApplication, ), ), ], @@ -194,8 +194,8 @@ class ServerOverview extends ConsumerWidget { leading: const Icon(Icons.shield), title: Text(t.misskey.impressum), onTap: () => launchUrl( + ref, impressumUrl, - mode: LaunchMode.externalApplication, ), ), if (meta != null && meta.serverRules.isNotEmpty) @@ -225,7 +225,7 @@ class ServerOverview extends ConsumerWidget { title: Html( data: rule, onLinkTap: (url, _, __) => - url != null ? launchUrl(Uri.parse(url)) : null, + url != null ? launchUrl(ref, Uri.parse(url)) : null, style: { 'body': Style(margin: Margins.all(0)), 'a': Style( @@ -243,8 +243,8 @@ class ServerOverview extends ConsumerWidget { leading: const Icon(Icons.verified), title: Text(t.misskey.termsOfService), onTap: () => launchUrl( + ref, tosUrl, - mode: LaunchMode.externalApplication, ), ), if (meta case MetaResponse(:final privacyPolicyUrl?)) @@ -252,8 +252,8 @@ class ServerOverview extends ConsumerWidget { leading: const Icon(Icons.policy), title: Text(t.misskey.privacyPolicy), onTap: () => launchUrl( + ref, privacyPolicyUrl, - mode: LaunchMode.externalApplication, ), ), if (meta case MetaResponse(:final feedbackUrl?)) @@ -261,8 +261,8 @@ class ServerOverview extends ConsumerWidget { leading: const Icon(Icons.message), title: Text(t.misskey.feedback), onTap: () => launchUrl( + ref, Uri.parse(feedbackUrl), - mode: LaunchMode.externalApplication, ), ), if (stats != null) ...[ diff --git a/lib/view/page/server/server_page.dart b/lib/view/page/server/server_page.dart index 9984dca0..c29b21a9 100644 --- a/lib/view/page/server/server_page.dart +++ b/lib/view/page/server/server_page.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../../i18n/strings.g.dart'; import '../../../model/account.dart'; import '../../../provider/api/federation_instance_provider.dart'; import '../../../provider/api/meta_notifier_provider.dart'; import '../../../util/copy_text.dart'; +import '../../../util/launch_url.dart'; import 'server_ads.dart'; import 'server_emojis.dart'; import 'server_overview.dart'; @@ -122,10 +122,7 @@ class ServerPage extends HookConsumerWidget { ListTile( leading: const Icon(Icons.open_in_browser), title: Text(t.aria.openInBrowser), - onTap: () => launchUrl( - Uri.https(host), - mode: LaunchMode.externalApplication, - ), + onTap: () => launchUrl(ref, Uri.https(host)), ), ], ), diff --git a/lib/view/page/settings/behavior_page.dart b/lib/view/page/settings/behavior_page.dart index 11f02a57..da9a3f0d 100644 --- a/lib/view/page/settings/behavior_page.dart +++ b/lib/view/page/settings/behavior_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import '../../../i18n/strings.g.dart'; @@ -170,6 +171,41 @@ class BehaviorPage extends ConsumerWidget { .read(generalSettingsNotifierProvider.notifier) .setConfirmBeforeFollow(value), ), + ListTile( + title: Text(t.aria.webBrowser), + subtitle: Text( + switch (settings.launchMode) { + LaunchMode.inAppBrowserView => t.aria.openInInternalBrowser, + LaunchMode.externalApplication => t.aria.openInExternalBrowser, + _ => '', + }, + ), + trailing: const Icon(Icons.navigate_next), + onTap: () async { + final result = await showRadioDialog( + context, + title: Text(t.aria.webBrowser), + values: [ + LaunchMode.inAppBrowserView, + LaunchMode.externalApplication, + ], + initialValue: settings.launchMode, + itemBuilder: (context, value) => Text( + switch (value) { + LaunchMode.inAppBrowserView => t.aria.openInInternalBrowser, + LaunchMode.externalApplication => + t.aria.openInExternalBrowser, + _ => '', + }, + ), + ); + if (result != null) { + await ref + .read(generalSettingsNotifierProvider.notifier) + .setLaunchMode(result); + } + }, + ), ], ), selectedDestination: GeneralSettingsDestination.behavior, diff --git a/lib/view/page/settings/languages_page.dart b/lib/view/page/settings/languages_page.dart index 5591cc02..ff6e6316 100644 --- a/lib/view/page/settings/languages_page.dart +++ b/lib/view/page/settings/languages_page.dart @@ -1,12 +1,12 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../../i18n/strings.g.dart'; import '../../../model/account.dart'; import '../../../provider/general_settings_notifier_provider.dart'; import '../../../provider/misskey_colors_provider.dart'; +import '../../../util/launch_url.dart'; import '../../widget/general_settings_scaffold.dart'; import '../../widget/mfm.dart'; @@ -48,6 +48,7 @@ class LanguagesPage extends ConsumerWidget { baseline: TextBaseline.alphabetic, child: InkWell( onTap: () => launchUrl( + ref, Uri.https('github.com', 'poppingmoon/aria'), ), child: Text.rich( diff --git a/lib/view/page/user/user_home.dart b/lib/view/page/user/user_home.dart index 3495c333..0bc035e2 100644 --- a/lib/view/page/user/user_home.dart +++ b/lib/view/page/user/user_home.dart @@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; -import 'package:url_launcher/url_launcher.dart'; import '../../../constant/colors.dart'; import '../../../extension/user_detailed_extension.dart'; @@ -17,6 +16,7 @@ import '../../../provider/general_settings_notifier_provider.dart'; import '../../../provider/misskey_colors_provider.dart'; import '../../../util/copy_text.dart'; import '../../../util/future_with_dialog.dart'; +import '../../../util/launch_url.dart'; import '../../../util/navigate.dart'; import '../../../util/open_as_guest.dart'; import '../../../util/punycode.dart'; @@ -274,10 +274,7 @@ class _UserHome extends ConsumerWidget { Wrap( children: [ TextButton( - onPressed: () => launchUrl( - remoteUrl, - mode: LaunchMode.externalApplication, - ), + onPressed: () => launchUrl(ref, remoteUrl), child: Text(t.misskey.showOnRemote), ), TextButton( @@ -581,10 +578,8 @@ class _UserHome extends ConsumerWidget { padding: const EdgeInsets.all(4.0), child: Align( child: InkWell( - onTap: () => launchUrl( - Uri.https('skeb.jp'), - mode: LaunchMode.externalApplication, - ), + onTap: () => + launchUrl(ref, Uri.https('skeb.jp')), child: DefaultTextStyle.merge( style: const TextStyle( fontWeight: FontWeight.bold, diff --git a/lib/view/widget/drive_file_sheet.dart b/lib/view/widget/drive_file_sheet.dart index e07d6754..bbe22db6 100644 --- a/lib/view/widget/drive_file_sheet.dart +++ b/lib/view/widget/drive_file_sheet.dart @@ -5,7 +5,6 @@ import 'package:gal/gal.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; @@ -16,6 +15,7 @@ import '../../provider/cache_manager_provider.dart'; import '../../provider/selected_drive_files_notifier_provider.dart'; import '../../util/copy_text.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../dialog/confirmation_dialog.dart'; import '../dialog/gallery_dialog.dart'; import '../dialog/message_dialog.dart'; @@ -97,7 +97,7 @@ class DriveFileSheet extends ConsumerWidget { final isImage = file.type.startsWith('image/'); final isVideo = file.type.startsWith('image/'); if (!isImage && !isVideo) { - await launchUrl(Uri.parse(file.url)); + await launchUrl(ref, Uri.parse(file.url)); return; } if (!await Gal.requestAccess()) { diff --git a/lib/view/widget/media_card.dart b/lib/view/widget/media_card.dart index 022603a4..256f4999 100644 --- a/lib/view/widget/media_card.dart +++ b/lib/view/widget/media_card.dart @@ -6,7 +6,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; import 'package:pretty_bytes/pretty_bytes.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../extension/text_style_extension.dart'; import '../../i18n/strings.g.dart'; @@ -16,6 +15,7 @@ import '../../provider/data_saver_provider.dart'; import '../../provider/general_settings_notifier_provider.dart'; import '../../provider/misskey_colors_provider.dart'; import '../../provider/static_image_url_provider.dart'; +import '../../util/launch_url.dart'; import '../dialog/audio_dialog.dart'; import '../dialog/image_gallery_dialog.dart'; import '../dialog/video_dialog.dart'; @@ -144,7 +144,7 @@ class MediaCard extends HookConsumerWidget { } } return InkWell( - onTap: () => launchUrl(Uri.parse(file.url)), + onTap: () => launchUrl(ref, Uri.parse(file.url)), child: Stack( alignment: Alignment.center, children: [ diff --git a/lib/view/widget/mfm/search.dart b/lib/view/widget/mfm/search.dart index 46d15e3e..8cfb6097 100644 --- a/lib/view/widget/mfm/search.dart +++ b/lib/view/widget/mfm/search.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../../i18n/strings.g.dart'; +import '../../../util/launch_url.dart'; import '../url_sheet.dart'; -class Search extends HookWidget { +class Search extends HookConsumerWidget { const Search({ super.key, required this.query, @@ -16,7 +16,7 @@ class Search extends HookWidget { final TextScaler? textScaler; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { return Container( margin: const EdgeInsets.symmetric(vertical: 8.0), decoration: BoxDecoration( @@ -46,6 +46,7 @@ class Search extends HookWidget { clipBehavior: Clip.hardEdge, child: InkWell( onTap: () => launchUrl( + ref, Uri.https('google.com', 'search', {'q': query}), ), onLongPress: () => showModalBottomSheet( diff --git a/lib/view/widget/misskey_servers.dart b/lib/view/widget/misskey_servers.dart index 9a4a1451..89012897 100644 --- a/lib/view/widget/misskey_servers.dart +++ b/lib/view/widget/misskey_servers.dart @@ -4,7 +4,6 @@ import 'package:flutter_html/flutter_html.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; -import 'package:url_launcher/url_launcher.dart'; import '../../constant/colors.dart'; import '../../constant/shortcuts.dart'; @@ -12,6 +11,7 @@ import '../../i18n/strings.g.dart'; import '../../provider/misskey_colors_provider.dart'; import '../../provider/misskey_servers_provider.dart'; import '../../provider/search_misskey_servers_provider.dart'; +import '../../util/launch_url.dart'; import 'error_message.dart'; import 'image_widget.dart'; @@ -176,7 +176,7 @@ class _ServerPreview extends ConsumerWidget { Html( data: description, onLinkTap: (url, _, __) => - url != null ? launchUrl(Uri.parse(url)) : null, + url != null ? launchUrl(ref, Uri.parse(url)) : null, style: { 'a': Style( color: colors.link, diff --git a/lib/view/widget/note_footer.dart b/lib/view/widget/note_footer.dart index c1f74ada..6d2fb612 100644 --- a/lib/view/widget/note_footer.dart +++ b/lib/view/widget/note_footer.dart @@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; -import 'package:url_launcher/url_launcher.dart'; import '../../constant/colors.dart'; import '../../extension/text_style_extension.dart'; @@ -18,6 +17,7 @@ import '../../provider/general_settings_notifier_provider.dart'; import '../../provider/note_provider.dart'; import '../../provider/notes_notifier_provider.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../dialog/clip_dialog.dart'; import '../dialog/confirmation_dialog.dart'; import '../dialog/reaction_confirmation_dialog.dart'; @@ -450,6 +450,7 @@ class NoteFooter extends ConsumerWidget { ); } else { launchUrl( + ref, Uri.https( 'translate.google.com', '', diff --git a/lib/view/widget/note_sheet.dart b/lib/view/widget/note_sheet.dart index 12ce5f70..d6b7559b 100644 --- a/lib/view/widget/note_sheet.dart +++ b/lib/view/widget/note_sheet.dart @@ -3,7 +3,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; import 'package:share_plus/share_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../extension/note_extension.dart'; import '../../extension/user_extension.dart'; @@ -20,6 +19,7 @@ import '../../provider/note_provider.dart'; import '../../provider/notes_notifier_provider.dart'; import '../../util/copy_text.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../dialog/clip_dialog.dart'; import '../dialog/confirmation_dialog.dart'; import '../dialog/delete_and_edit_dialog.dart'; @@ -114,7 +114,7 @@ class NoteSheet extends ConsumerWidget { leading: const Icon(Icons.open_in_browser), title: Text(t.aria.openInBrowser), onTap: () { - launchUrl(url, mode: LaunchMode.externalApplication); + launchUrl(ref, url); context.pop(); }, ), @@ -123,7 +123,7 @@ class NoteSheet extends ConsumerWidget { leading: const Icon(Icons.rocket_launch), title: Text(t.misskey.showOnRemote), onTap: () { - launchUrl(remoteUrl, mode: LaunchMode.externalApplication); + launchUrl(ref, remoteUrl); context.pop(); }, ), @@ -239,6 +239,7 @@ class NoteSheet extends ConsumerWidget { ); } else { launchUrl( + ref, Uri.https( 'translate.google.com', '', diff --git a/lib/view/widget/play_widget.dart b/lib/view/widget/play_widget.dart index d64011a2..0dad999b 100644 --- a/lib/view/widget/play_widget.dart +++ b/lib/view/widget/play_widget.dart @@ -7,7 +7,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:misskey_dart/misskey_dart.dart' hide Clip; import 'package:share_plus/share_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; @@ -26,6 +25,7 @@ import '../../rust/api/aiscript/play.dart'; import '../../rust/api/aiscript/ui.dart'; import '../../util/copy_text.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../../util/nyaize.dart'; import '../dialog/error_message_dialog.dart'; import '../dialog/text_field_dialog.dart'; @@ -148,10 +148,7 @@ class PlayWidget extends HookConsumerWidget { ), IconButton( tooltip: t.aria.openInBrowser, - onPressed: () => launchUrl( - url, - mode: LaunchMode.externalApplication, - ), + onPressed: () => launchUrl(ref, url), icon: const Icon(Icons.open_in_browser), ), IconButton( diff --git a/lib/view/widget/player_embed.dart b/lib/view/widget/player_embed.dart index 03f6e2e7..b46cd206 100644 --- a/lib/view/widget/player_embed.dart +++ b/lib/view/widget/player_embed.dart @@ -1,16 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../model/summaly_result.dart'; +import '../../util/launch_url.dart'; -class PlayerEmbed extends StatelessWidget { +class PlayerEmbed extends ConsumerWidget { const PlayerEmbed({super.key, required this.player}); final Player player; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final width = player.width; final height = player.height; final url = player.url != null ? Uri.tryParse(player.url!) : null; @@ -33,8 +34,8 @@ class PlayerEmbed extends StatelessWidget { shouldOverrideUrlLoading: (controller, navigationAction) async { if (navigationAction.hasGesture ?? false) { final url = navigationAction.request.url; - if (url != null && await canLaunchUrl(url)) { - await launchUrl(url, mode: LaunchMode.externalApplication); + if (url != null) { + await launchUrl(ref, url); return NavigationActionPolicy.CANCEL; } } diff --git a/lib/view/widget/skeb_status_widget.dart b/lib/view/widget/skeb_status_widget.dart index 45a45fe8..8a4b1f1c 100644 --- a/lib/view/widget/skeb_status_widget.dart +++ b/lib/view/widget/skeb_status_widget.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:misskey_dart/misskey_dart.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../i18n/strings.g.dart'; import '../../model/account.dart'; +import '../../util/launch_url.dart'; import 'mfm.dart'; class SkebStatusWidget extends ConsumerWidget { @@ -50,11 +50,11 @@ class SkebStatusWidget extends ConsumerWidget { .join(' | '), child: InkWell( onTap: () => launchUrl( + ref, Uri.https( 'skeb.jp', '@${skebStatus.screenName}', ), - mode: LaunchMode.externalApplication, ), child: Text.rich( TextSpan( diff --git a/lib/view/widget/twitter_embed.dart b/lib/view/widget/twitter_embed.dart index d5066120..cf9a1435 100644 --- a/lib/view/widget/twitter_embed.dart +++ b/lib/view/widget/twitter_embed.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; -class TwitterEmbed extends HookWidget { +import '../../util/launch_url.dart'; + +class TwitterEmbed extends HookConsumerWidget { const TwitterEmbed({ super.key, required this.tweetId, @@ -17,7 +19,7 @@ class TwitterEmbed extends HookWidget { final String lang; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final height = useState(200.0); // https://developer.twitter.com/en/docs/twitter-for-websites/embedded-tweets/guides/embedded-tweet-javascript-factory-function final content = """ @@ -82,8 +84,8 @@ class TwitterEmbed extends HookWidget { shouldOverrideUrlLoading: (controller, navigationAction) async { if (navigationAction.hasGesture ?? false) { final url = navigationAction.request.url; - if (url != null && await canLaunchUrl(url)) { - await launchUrl(url, mode: LaunchMode.externalApplication); + if (url != null) { + await launchUrl(ref, url); return NavigationActionPolicy.CANCEL; } } diff --git a/lib/view/widget/user_sheet.dart b/lib/view/widget/user_sheet.dart index 94fc522d..57209ebb 100644 --- a/lib/view/widget/user_sheet.dart +++ b/lib/view/widget/user_sheet.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:misskey_dart/misskey_dart.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../../extension/user_extension.dart'; import '../../i18n/strings.g.dart'; @@ -15,6 +14,7 @@ import '../../provider/api/post_notifier_provider.dart'; import '../../provider/api/user_notifier_provider.dart'; import '../../util/copy_text.dart'; import '../../util/future_with_dialog.dart'; +import '../../util/launch_url.dart'; import '../../util/open_as_guest.dart'; import '../dialog/antenna_dialog.dart'; import '../dialog/confirmation_dialog.dart'; @@ -94,7 +94,7 @@ class UserSheet extends ConsumerWidget { leading: const Icon(Icons.open_in_browser), title: Text(t.aria.openInBrowser), onTap: () { - launchUrl(profileUrl, mode: LaunchMode.externalApplication); + launchUrl(ref, profileUrl); context.pop(); }, ), @@ -103,7 +103,7 @@ class UserSheet extends ConsumerWidget { leading: const Icon(Icons.rocket_launch), title: Text(t.misskey.showOnRemote), onTap: () { - launchUrl(remoteUrl, mode: LaunchMode.externalApplication); + launchUrl(ref, remoteUrl); context.pop(); }, ),