Skip to content

Commit

Permalink
Bring back in-app browser
Browse files Browse the repository at this point in the history
  • Loading branch information
micahmo committed Jan 12, 2024
1 parent 2495369 commit 6ae8e98
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 55 deletions.
5 changes: 5 additions & 0 deletions lib/core/enums/browser_mode.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
enum BrowserMode {
inApp,
customTabs,
external,
}
2 changes: 2 additions & 0 deletions lib/core/enums/local_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ enum LocalSettings {

// General Settings
scrapeMissingPreviews(name: 'setting_general_scrape_missing_previews', label: 'Scrape Missing External Link Previews'),
// Deprecated, use browserMode
openLinksInExternalBrowser(name: 'setting_links_open_in_external_browser', label: 'Open Links in External Browser'),
browserMode(name: 'setting_browser_mode', label: ''),
openLinksInReaderMode(name: 'setting_links_open_in_reader_mode', label: 'Open Links in Reader Mode when available'),
useDisplayNamesForUsers(name: 'setting_use_display_names_for_users', label: 'Show User Display Names'),
markPostAsReadOnMediaView(name: 'setting_general_mark_post_read_on_media_view', label: 'Mark Read After Viewing Media'),
Expand Down
32 changes: 32 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@
"@floatingActionButtonSinglePressDescription": {
"description": "Description of the FAB's single-press action"
},
"forward": "Forward",
"@forward": {
"description": "Label for navigating forward (e.g., in a browser)"
},
"fullScreenNavigationSwipeDescription": "Swipe anywhere to go back when left-to-right gestures are disabled",
"@fullScreenNavigationSwipeDescription": {},
"general": "General",
Expand Down Expand Up @@ -479,6 +483,34 @@
"@link": {},
"linkActions": "Link Actions",
"@linkActions": {},
"linkHandling": "Link handling",
"@linkHandling": {
"description": "Title for link handling setting"
},
"linkHandlingCustomTabs": "Open in system browser embedded in-app",
"@linkHandlingCustomTabs": {
"description": "Description for custom tabs link handling"
},
"linkHandlingCustomTabsShort": "In-app embedded",
"@linkHandlingCustomTabsShort": {
"description": "Short description for custom tabs link handling"
},
"linkHandlingExternal": "Open in system browser externally",
"@linkHandlingExternal": {
"description": "Description for external link handling"
},
"linkHandlingExternalShort": "External",
"@linkHandlingExternalShort": {
"description": "Short description for external link handling"
},
"linkHandlingInApp": "Use Thunder's built-in browser",
"@linkHandlingInApp": {
"description": "Description for in-app link handling"
},
"linkHandlingInAppShort": "In-app",
"@linkHandlingInAppShort": {
"description": "Short description for in-app link handling"
},
"linksBehaviourSettings": "Links",
"@linksBehaviourSettings": {
"description": "Subcategory in Setting -> General"
Expand Down
45 changes: 33 additions & 12 deletions lib/settings/pages/general_settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:android_intent_plus/android_intent.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:thunder/core/enums/browser_mode.dart';
import 'package:thunder/core/enums/full_name_separator.dart';

import 'package:thunder/core/enums/local_settings.dart';
Expand Down Expand Up @@ -51,8 +52,8 @@ class _GeneralSettingsPageState extends State<GeneralSettingsPage> with SingleTi
/// When enabled, missing link previews will be scraped
bool scrapeMissingPreviews = false;

/// When enabled, links will be opened in the external browser
bool openInExternalBrowser = false;
/// Determines how links are handled
BrowserMode browserMode = BrowserMode.customTabs;

/// When enabled, links will be opened in the reader mode. This is only available on iOS
bool openInReaderMode = false;
Expand Down Expand Up @@ -152,9 +153,9 @@ class _GeneralSettingsPageState extends State<GeneralSettingsPage> with SingleTi
setState(() => combineNavAndFab = value);
break;

case LocalSettings.openLinksInExternalBrowser:
await prefs.setBool(LocalSettings.openLinksInExternalBrowser.name, value);
setState(() => openInExternalBrowser = value);
case LocalSettings.browserMode:
await prefs.setString(LocalSettings.browserMode.name, value);
setState(() => browserMode = BrowserMode.values.byName(value ?? BrowserMode.customTabs));
break;
case LocalSettings.openLinksInReaderMode:
await prefs.setBool(LocalSettings.openLinksInReaderMode.name, value);
Expand Down Expand Up @@ -213,7 +214,15 @@ class _GeneralSettingsPageState extends State<GeneralSettingsPage> with SingleTi
enableCommentNavigation = prefs.getBool(LocalSettings.enableCommentNavigation.name) ?? true;
combineNavAndFab = prefs.getBool(LocalSettings.combineNavAndFab.name) ?? true;

openInExternalBrowser = prefs.getBool(LocalSettings.openLinksInExternalBrowser.name) ?? false;
browserMode = BrowserMode.values.byName(prefs.getString(LocalSettings.browserMode.name) ?? BrowserMode.customTabs.name);
// Migrate the openInExternalBrowser setting, if found.
bool? legacyOpenInExternalBrowser = prefs.getBool(LocalSettings.openLinksInExternalBrowser.name);
if (legacyOpenInExternalBrowser != null) {
browserMode = legacyOpenInExternalBrowser ? BrowserMode.external : BrowserMode.customTabs;
prefs.remove(LocalSettings.openLinksInExternalBrowser.name);
prefs.setString(LocalSettings.browserMode.name, browserMode.toString());
}

openInReaderMode = prefs.getBool(LocalSettings.openLinksInReaderMode.name) ?? false;
scrapeMissingPreviews = prefs.getBool(LocalSettings.scrapeMissingPreviews.name) ?? false;

Expand Down Expand Up @@ -499,12 +508,24 @@ class _GeneralSettingsPageState extends State<GeneralSettingsPage> with SingleTi
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ToggleOption(
description: LocalSettings.openLinksInExternalBrowser.label,
value: openInExternalBrowser,
iconEnabled: Icons.add_link_rounded,
iconDisabled: Icons.link_rounded,
onToggle: (bool value) => setPreferences(LocalSettings.openLinksInExternalBrowser, value),
child: ListOption(
description: l10n.linkHandling,
value: ListPickerItem(
label: switch (browserMode) {
BrowserMode.inApp => l10n.linkHandlingInAppShort,
BrowserMode.customTabs => l10n.linkHandlingCustomTabsShort,
BrowserMode.external => l10n.linkHandlingExternalShort,
},
payload: browserMode,
capitalizeLabel: false,
),
options: [
ListPickerItem(label: l10n.linkHandlingInApp, icon: Icons.dataset_linked_rounded, payload: BrowserMode.inApp),
ListPickerItem(label: l10n.linkHandlingCustomTabs, icon: Icons.language_rounded, payload: BrowserMode.customTabs),
ListPickerItem(label: l10n.linkHandlingExternal, icon: Icons.open_in_browser_rounded, payload: BrowserMode.external),
],
icon: Icons.link_rounded,
onChanged: (value) => setPreferences(LocalSettings.browserMode, value.payload.name),
),
),
),
Expand Down
114 changes: 80 additions & 34 deletions lib/shared/webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class WebView extends StatefulWidget {
final String url;
Expand Down Expand Up @@ -37,7 +38,10 @@ class _WebViewState extends State<WebView> {
controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate())
..loadRequest(Uri.parse(widget.url));
..loadRequest(Uri.parse(widget.url))
..setNavigationDelegate(NavigationDelegate(
onUrlChange: (_) => setState(() {}),
));

if (controller.platform is AndroidWebViewController) {
(controller.platform as AndroidWebViewController).setMediaPlaybackRequiresUserGesture(false);
Expand All @@ -47,14 +51,25 @@ class _WebViewState extends State<WebView> {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(toolbarHeight: 70.0, actions: <Widget>[
NavigationControls(
webViewController: _controller,
url: widget.url,
)
]),
body: WebViewWidget(controller: _controller),
return FutureBuilder(
future: Future.wait([_controller.getTitle(), _controller.currentUrl()]),
builder: (context, snapshot) => Scaffold(
appBar: AppBar(
toolbarHeight: 70.0,
titleSpacing: 0,
title: ListTile(
title: Text(snapshot.data?[0] ?? '', maxLines: 1, overflow: TextOverflow.ellipsis),
subtitle: Text(snapshot.data?[1]?.replaceFirst('https://', '').replaceFirst('www.', '') ?? '', maxLines: 1, overflow: TextOverflow.ellipsis),
),
actions: <Widget>[
NavigationControls(
webViewController: _controller,
url: widget.url,
)
],
),
body: WebViewWidget(controller: _controller),
),
);
}
}
Expand All @@ -67,31 +82,62 @@ class NavigationControls extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
IconButton(
icon: const Icon(
Icons.replay_rounded,
semanticLabel: 'Refresh',
),
onPressed: () async => await webViewController.reload(),
),
IconButton(
icon: const Icon(
Icons.open_in_browser_rounded,
semanticLabel: 'Open in Browser',
),
onPressed: () => launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication),
),
IconButton(
icon: const Icon(
Icons.share_rounded,
semanticLabel: 'Share',
),
onPressed: () => Share.share(url),
),
const SizedBox(width: 8.0),
],
final AppLocalizations l10n = AppLocalizations.of(context)!;

return FutureBuilder(
future: Future.wait([webViewController.canGoBack(), webViewController.canGoForward()]),
builder: (context, snapshot) {
return Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.arrow_back_rounded,
semanticLabel: l10n.back,
),
onPressed: snapshot.hasData && snapshot.data![0] == true ? () async => await webViewController.goBack() : null,
),
IconButton(
icon: Icon(
Icons.arrow_forward_rounded,
semanticLabel: l10n.forward,
),
onPressed: snapshot.hasData && snapshot.data![1] == true ? () async => await webViewController.goForward() : null,
),
PopupMenuButton(
itemBuilder: (BuildContext context) => [
PopupMenuItem(
onTap: () async => await webViewController.reload(),
child: ListTile(
dense: true,
horizontalTitleGap: 5,
leading: const Icon(Icons.replay_rounded, size: 20),
title: Text(l10n.refresh),
),
),
PopupMenuItem(
onTap: () => launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication),
child: ListTile(
dense: true,
horizontalTitleGap: 5,
leading: const Icon(Icons.open_in_browser_rounded, size: 20),
title: Text(l10n.openInBrowser),
),
),
PopupMenuItem(
onTap: () => Share.share(url),
child: ListTile(
dense: true,
horizontalTitleGap: 5,
leading: const Icon(Icons.share_rounded, size: 20),
title: Text(l10n.share),
),
),
],
),
const SizedBox(width: 8.0),
],
);
},
);
}
}
13 changes: 11 additions & 2 deletions lib/thunder/bloc/thunder_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:lemmy_api_client/v3.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stream_transform/stream_transform.dart';
import 'package:thunder/account/models/account.dart';
import 'package:thunder/core/enums/browser_mode.dart';

import 'package:thunder/core/enums/custom_theme_type.dart';
import 'package:thunder/core/enums/fab_action.dart';
Expand Down Expand Up @@ -106,7 +107,6 @@ class ThunderBloc extends Bloc<ThunderEvent, ThunderState> {

// General Settings
bool scrapeMissingPreviews = prefs.getBool(LocalSettings.scrapeMissingPreviews.name) ?? false;
bool openInExternalBrowser = prefs.getBool(LocalSettings.openLinksInExternalBrowser.name) ?? false;
bool openInReaderMode = prefs.getBool(LocalSettings.openLinksInReaderMode.name) ?? false;
bool useDisplayNames = prefs.getBool(LocalSettings.useDisplayNamesForUsers.name) ?? true;
bool markPostReadOnMediaView = prefs.getBool(LocalSettings.markPostAsReadOnMediaView.name) ?? false;
Expand All @@ -116,6 +116,15 @@ class ThunderBloc extends Bloc<ThunderEvent, ThunderState> {
FullNameSeparator communitySeparator = FullNameSeparator.values.byName(prefs.getString(LocalSettings.communityFormat.name) ?? FullNameSeparator.dot.name);
bool hideTopBarOnScroll = prefs.getBool(LocalSettings.hideTopBarOnScroll.name) ?? false;

BrowserMode browserMode = BrowserMode.values.byName(prefs.getString(LocalSettings.browserMode.name) ?? BrowserMode.customTabs.name);
// Migrate the openInExternalBrowser setting, if found.
bool? legacyOpenInExternalBrowser = prefs.getBool(LocalSettings.openLinksInExternalBrowser.name);
if (legacyOpenInExternalBrowser != null) {
browserMode = legacyOpenInExternalBrowser ? BrowserMode.external : BrowserMode.customTabs;
prefs.remove(LocalSettings.openLinksInExternalBrowser.name);
prefs.setString(LocalSettings.browserMode.name, browserMode.toString());
}

/// -------------------------- Feed Post Related Settings --------------------------
// Compact Related Settings
bool useCompactView = prefs.getBool(LocalSettings.useCompactView.name) ?? false;
Expand Down Expand Up @@ -235,7 +244,7 @@ class ThunderBloc extends Bloc<ThunderEvent, ThunderState> {

// General Settings
scrapeMissingPreviews: scrapeMissingPreviews,
openInExternalBrowser: openInExternalBrowser,
browserMode: browserMode,
openInReaderMode: openInReaderMode,
useDisplayNames: useDisplayNames,
markPostReadOnMediaView: markPostReadOnMediaView,
Expand Down
10 changes: 5 additions & 5 deletions lib/thunder/bloc/thunder_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ThunderState extends Equatable {

// General Settings
this.scrapeMissingPreviews = false,
this.openInExternalBrowser = false,
this.browserMode = BrowserMode.customTabs,
this.openInReaderMode = false,
this.useDisplayNames = true,
this.markPostReadOnMediaView = false,
Expand Down Expand Up @@ -151,7 +151,7 @@ class ThunderState extends Equatable {

// General Settings
final bool scrapeMissingPreviews;
final bool openInExternalBrowser;
final BrowserMode browserMode;
final bool openInReaderMode;
final bool useDisplayNames;
final bool markPostReadOnMediaView;
Expand Down Expand Up @@ -287,7 +287,7 @@ class ThunderState extends Equatable {

// General Settings
bool? scrapeMissingPreviews,
bool? openInExternalBrowser,
BrowserMode? browserMode,
bool? openInReaderMode,
bool? useDisplayNames,
bool? markPostReadOnMediaView,
Expand Down Expand Up @@ -413,7 +413,7 @@ class ThunderState extends Equatable {

// General Settings
scrapeMissingPreviews: scrapeMissingPreviews ?? this.scrapeMissingPreviews,
openInExternalBrowser: openInExternalBrowser ?? this.openInExternalBrowser,
browserMode: browserMode ?? this.browserMode,
openInReaderMode: openInReaderMode ?? this.openInReaderMode,
useDisplayNames: useDisplayNames ?? this.useDisplayNames,
markPostReadOnMediaView: markPostReadOnMediaView ?? this.markPostReadOnMediaView,
Expand Down Expand Up @@ -549,7 +549,7 @@ class ThunderState extends Equatable {

// General Settings
scrapeMissingPreviews,
openInExternalBrowser,
browserMode,
useDisplayNames,
markPostReadOnMediaView,
disableFeedFab,
Expand Down
Loading

0 comments on commit 6ae8e98

Please sign in to comment.