Skip to content

Commit 10acb77

Browse files
committed
settings: Add browser preference setting ui
The translation for the label is taken from zulip-mobile. The user facing string refers to "in in-app browser" when the actual value is `UrlLaunchMode.platformDefault`. This is consistent with the behavior the user sees when the link is a HTTP URL on Android and iOS, which should cover the majority of use cases of this setting (and probably what the user would expect). The UI currently does not have a design, and is made to be as simple as possible to implement for now. Fixes: #1228 Signed-off-by: Zixuan James Li <[email protected]>
1 parent 165f9ca commit 10acb77

12 files changed

+110
-0
lines changed

assets/l10n/app_en.arb

+4
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,10 @@
803803
"@themeSettingSystem": {
804804
"description": "Label for system theme setting."
805805
},
806+
"openLinksWithInAppBrowser": "Open links with in-app browser",
807+
"@openLinksWithInAppBrowser": {
808+
"description": "Label for toggling setting to open links with in-app browser"
809+
},
806810
"pollWidgetQuestionMissing": "No question.",
807811
"@pollWidgetQuestionMissing": {
808812
"description": "Text to display for a poll when the question is missing"

lib/generated/l10n/zulip_localizations.dart

+6
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,12 @@ abstract class ZulipLocalizations {
11731173
/// **'System'**
11741174
String get themeSettingSystem;
11751175

1176+
/// Label for toggling setting to open links with in-app browser
1177+
///
1178+
/// In en, this message translates to:
1179+
/// **'Open links with in-app browser'**
1180+
String get openLinksWithInAppBrowser;
1181+
11761182
/// Text to display for a poll when the question is missing
11771183
///
11781184
/// In en, this message translates to:

lib/generated/l10n/zulip_localizations_ar.dart

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
628628
@override
629629
String get themeSettingSystem => 'System';
630630

631+
@override
632+
String get openLinksWithInAppBrowser => 'Open links with in-app browser';
633+
631634
@override
632635
String get pollWidgetQuestionMissing => 'No question.';
633636

lib/generated/l10n/zulip_localizations_en.dart

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
628628
@override
629629
String get themeSettingSystem => 'System';
630630

631+
@override
632+
String get openLinksWithInAppBrowser => 'Open links with in-app browser';
633+
631634
@override
632635
String get pollWidgetQuestionMissing => 'No question.';
633636

lib/generated/l10n/zulip_localizations_ja.dart

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
628628
@override
629629
String get themeSettingSystem => 'System';
630630

631+
@override
632+
String get openLinksWithInAppBrowser => 'Open links with in-app browser';
633+
631634
@override
632635
String get pollWidgetQuestionMissing => 'No question.';
633636

lib/generated/l10n/zulip_localizations_nb.dart

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
628628
@override
629629
String get themeSettingSystem => 'System';
630630

631+
@override
632+
String get openLinksWithInAppBrowser => 'Open links with in-app browser';
633+
631634
@override
632635
String get pollWidgetQuestionMissing => 'No question.';
633636

lib/generated/l10n/zulip_localizations_pl.dart

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
628628
@override
629629
String get themeSettingSystem => 'System';
630630

631+
@override
632+
String get openLinksWithInAppBrowser => 'Open links with in-app browser';
633+
631634
@override
632635
String get pollWidgetQuestionMissing => 'Brak pytania.';
633636

lib/generated/l10n/zulip_localizations_ru.dart

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
628628
@override
629629
String get themeSettingSystem => 'System';
630630

631+
@override
632+
String get openLinksWithInAppBrowser => 'Open links with in-app browser';
633+
631634
@override
632635
String get pollWidgetQuestionMissing => 'Нет вопроса.';
633636

lib/generated/l10n/zulip_localizations_sk.dart

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
628628
@override
629629
String get themeSettingSystem => 'System';
630630

631+
@override
632+
String get openLinksWithInAppBrowser => 'Open links with in-app browser';
633+
631634
@override
632635
String get pollWidgetQuestionMissing => 'Bez otázky.';
633636

lib/widgets/settings.dart

+30
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:drift/drift.dart' hide Column;
22
import 'package:flutter/material.dart';
33

44
import '../generated/l10n/zulip_localizations.dart';
5+
import '../model/binding.dart';
56
import '../model/database.dart';
67
import '../model/settings.dart';
78
import 'app_bar.dart';
@@ -23,6 +24,7 @@ class SettingsPage extends StatelessWidget {
2324
appBar: ZulipAppBar(
2425
title: Text(zulipLocalizations.settingsPageTitle)),
2526
body: Column(children: [
27+
const _BrowserPreferenceSetting(),
2628
const _ThemeSetting(),
2729
]));
2830
}
@@ -54,3 +56,31 @@ class _ThemeSetting extends StatelessWidget {
5456
]);
5557
}
5658
}
59+
60+
class _BrowserPreferenceSetting extends StatelessWidget {
61+
const _BrowserPreferenceSetting();
62+
63+
void _handleChange(BuildContext context, bool newOpenLinksWithInAppBrowser) {
64+
GlobalStoreWidget.of(context).updateGlobalSettings(
65+
GlobalSettingsCompanion(browserPreference: Value(
66+
newOpenLinksWithInAppBrowser ? BrowserPreference.embedded
67+
: BrowserPreference.external)));
68+
}
69+
70+
@override
71+
Widget build(BuildContext context) {
72+
final zulipLocalizations = ZulipLocalizations.of(context);
73+
// On Android and iOS, an in-app browser will be used for HTTP URLs if the
74+
// launch mode is [UrlLaunchMode.platformDefault], which is consistent with
75+
// the user-facing "Open links with in-app browser" label. In practice,
76+
// external browsers/apps can still be used for other types of links, but
77+
// those links aren't common in normal usage.
78+
final openLinksWithInAppBrowser =
79+
GlobalStoreWidget.of(context).globalSettings.urlLaunchMode
80+
== UrlLaunchMode.platformDefault;
81+
return SwitchListTile.adaptive(
82+
title: Text(zulipLocalizations.openLinksWithInAppBrowser),
83+
value: openLinksWithInAppBrowser,
84+
onChanged: (newValue) => _handleChange(context, newValue));
85+
}
86+
}

test/flutter_checks.dart

+4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ extension RadioListTileChecks<T> on Subject<RadioListTile<T>> {
8282
Subject<bool> get checked => has((x) => x.checked, 'checked');
8383
}
8484

85+
extension SwitchListTileChecks<T> on Subject<SwitchListTile> {
86+
Subject<bool> get value => has((x) => x.value, 'value');
87+
}
88+
8589
extension TextChecks on Subject<Text> {
8690
Subject<String?> get data => has((t) => t.data, 'data');
8791
Subject<TextStyle?> get style => has((t) => t.style, 'style');

test/widgets/settings_test.dart

+45
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,51 @@ void main() {
2525
await tester.pump();
2626
}
2727

28+
group('BrowserPreference', () {
29+
Finder useInAppBrowserSwitchFinder = find.ancestor(
30+
of: find.text('Open links with in-app browser'),
31+
matching: find.byType(SwitchListTile));
32+
33+
void checkSwitchAndGlobalSettings(WidgetTester tester, {
34+
required bool checked,
35+
required BrowserPreference? expectedBrowserPreference,
36+
}) {
37+
check(tester.widget<SwitchListTile>(useInAppBrowserSwitchFinder))
38+
.value.equals(checked);
39+
check(testBinding.globalStore)
40+
.globalSettings.browserPreference.equals(expectedBrowserPreference);
41+
}
42+
43+
testWidgets('smoke', (tester) async {
44+
await testBinding.globalStore.updateGlobalSettings(
45+
eg.globalSettings(
46+
browserPreference: BrowserPreference.external).toCompanion(false));
47+
await prepare(tester);
48+
checkSwitchAndGlobalSettings(tester,
49+
checked: false, expectedBrowserPreference: BrowserPreference.external);
50+
51+
await tester.tap(useInAppBrowserSwitchFinder);
52+
await tester.pump();
53+
checkSwitchAndGlobalSettings(tester,
54+
checked: true, expectedBrowserPreference: BrowserPreference.embedded);
55+
});
56+
57+
testWidgets('use platform-specific default browser preference', (tester) async {
58+
await prepare(tester);
59+
bool useInAppBrowserExpected = defaultTargetPlatform == TargetPlatform.android;
60+
checkSwitchAndGlobalSettings(tester,
61+
checked: useInAppBrowserExpected, expectedBrowserPreference: null);
62+
63+
await tester.tap(useInAppBrowserSwitchFinder);
64+
await tester.pump();
65+
useInAppBrowserExpected = !useInAppBrowserExpected;
66+
checkSwitchAndGlobalSettings(tester,
67+
checked: useInAppBrowserExpected,
68+
expectedBrowserPreference: useInAppBrowserExpected
69+
? BrowserPreference.embedded : BrowserPreference.external);
70+
}, variant: TargetPlatformVariant({TargetPlatform.android, TargetPlatform.iOS}));
71+
});
72+
2873
group('ThemeSetting', () {
2974
Finder findRadioListTileWithTitle(String title) => find.ancestor(
3075
of: find.text(title),

0 commit comments

Comments
 (0)