added support for overriding locales
This commit is contained in:
+18
@@ -17,6 +17,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart' show ThemeMode;
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
@@ -141,6 +142,18 @@ class EngineSettingsReplicationService
|
||||
.read(preferenceFixatorProvider.notifier)
|
||||
.register('pdfjs.disabled', !settings.enablePdfJs);
|
||||
}
|
||||
if (!const DeepCollectionEquality.unordered().equals(
|
||||
previous.value?.locales,
|
||||
settings.locales,
|
||||
)) {
|
||||
// ignore: only_use_keep_alive_inside_keep_alive
|
||||
await ref
|
||||
.read(preferenceFixatorProvider.notifier)
|
||||
.register(
|
||||
'intl.accept_languages',
|
||||
settings.locales.join(','),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await _service.setDefaultSettings(settings);
|
||||
|
||||
@@ -149,6 +162,11 @@ class EngineSettingsReplicationService
|
||||
.read(preferenceFixatorProvider.notifier)
|
||||
.register('pdfjs.disabled', !settings.enablePdfJs);
|
||||
|
||||
// ignore: only_use_keep_alive_inside_keep_alive
|
||||
await ref
|
||||
.read(preferenceFixatorProvider.notifier)
|
||||
.register('intl.accept_languages', settings.locales.join(','));
|
||||
|
||||
initialSettingsSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ final class EngineSettingsReplicationServiceProvider
|
||||
}
|
||||
|
||||
String _$engineSettingsReplicationServiceHash() =>
|
||||
r'd28cc58a94f45008a478bf52a9c40d61811b591d';
|
||||
r'7583ebc8ca9c11377486ff5a3e763493dcdc903a';
|
||||
|
||||
abstract class _$EngineSettingsReplicationService extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
+2
-2
@@ -25,8 +25,6 @@ class PreferenceChangeListener extends _$PreferenceChangeListener {
|
||||
@Riverpod()
|
||||
class PreferenceFixator extends _$PreferenceFixator {
|
||||
Future<void> register(String name, Object value) async {
|
||||
await GeckoPrefService().applyPrefs({name: value});
|
||||
|
||||
state = {...state}
|
||||
..update(
|
||||
name,
|
||||
@@ -36,6 +34,8 @@ class PreferenceFixator extends _$PreferenceFixator {
|
||||
return value;
|
||||
},
|
||||
);
|
||||
|
||||
await GeckoPrefService().applyPrefs({name: value});
|
||||
}
|
||||
|
||||
Future<void> unregister(String name) async {
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ final class PreferenceFixatorProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$preferenceFixatorHash() => r'dfa323859c660d3db74dba7297e2a33b758ed78a';
|
||||
String _$preferenceFixatorHash() => r'c76245d5c420e2e3f6c026f23c017c939b69ef59';
|
||||
|
||||
abstract class _$PreferenceFixator extends $Notifier<Map<String, Object>> {
|
||||
Map<String, Object> build();
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import 'package:country_flags/country_flags.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:intl/locale.dart';
|
||||
import 'package:nullability/nullability.dart';
|
||||
import 'package:weblibre/domain/repositories/locale_resolver.dart';
|
||||
import 'package:weblibre/extensions/locale.dart';
|
||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
|
||||
|
||||
class LocaleSettingsScreen extends HookConsumerWidget {
|
||||
const LocaleSettingsScreen();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final formKey = useMemoized(() => GlobalKey<FormState>());
|
||||
|
||||
final systemLocales = useMemoized(
|
||||
() => WidgetsBinding.instance.platformDispatcher.locales
|
||||
.map((locale) => locale.toIntlLocale())
|
||||
.toList(),
|
||||
);
|
||||
|
||||
final userLocales = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select(
|
||||
(settings) => EquatableValue(
|
||||
settings.locales.map(Locale.tryParse).nonNulls.toSet(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final availableLocales = {
|
||||
...systemLocales,
|
||||
...userLocales.value,
|
||||
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
|
||||
};
|
||||
|
||||
final customLocaleController = useTextEditingController();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Browser Languages')),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
children: [
|
||||
...availableLocales.map((locale) {
|
||||
return CheckboxListTile.adaptive(
|
||||
value: userLocales.value.contains(locale),
|
||||
onChanged: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(currentSettings) => currentSettings.copyWith.locales(
|
||||
value
|
||||
? {
|
||||
...currentSettings.locales,
|
||||
locale.toLanguageTag(),
|
||||
}.toList()
|
||||
: ([
|
||||
...currentSettings.locales,
|
||||
]..remove(locale.toLanguageTag())).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
title: Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final resolvedAsync = ref.watch(
|
||||
resolveLocaleProvider(locale),
|
||||
);
|
||||
|
||||
return Text(
|
||||
resolvedAsync.maybeWhen(
|
||||
data: (data) =>
|
||||
data.mapNotNull(
|
||||
(data) =>
|
||||
'${data.languageName} ${data.countryName.mapNotNull((country) => '($country)') ?? ''}'
|
||||
.trim(),
|
||||
) ??
|
||||
locale.toLanguageTag(),
|
||||
orElse: () => locale.toLanguageTag(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
subtitle: Text(locale.toLanguageTag()),
|
||||
secondary: CountryFlag.fromLanguageCode(
|
||||
locale.languageCode,
|
||||
theme: const ImageTheme(shape: RoundedRectangle(8.0)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, right: 20),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: TextFormField(
|
||||
controller: customLocaleController,
|
||||
decoration: InputDecoration(
|
||||
label: const Text('Custom Locale'),
|
||||
hint: const Text('en-US'),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
if (formKey.currentState?.validate() == true) {
|
||||
formKey.currentState?.save();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value != null && Locale.tryParse(value) == null) {
|
||||
return 'Invalid locale identifier';
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
onSaved: (newValue) async {
|
||||
if (newValue != null) {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(currentSettings) =>
|
||||
currentSettings.copyWith.locales(
|
||||
{
|
||||
...currentSettings.locales,
|
||||
Locale.parse(newValue).toLanguageTag(),
|
||||
}.toList(),
|
||||
),
|
||||
);
|
||||
|
||||
customLocaleController.clear();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -203,6 +203,18 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Browser Languages'),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 8.0,
|
||||
horizontal: 16.0,
|
||||
),
|
||||
leading: const Icon(MdiIcons.translate),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () async {
|
||||
await LocaleSettingsRoute().push(context);
|
||||
},
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Global Privacy Control (GPC)'),
|
||||
secondary: const Icon(MdiIcons.incognitoCircleOff),
|
||||
|
||||
@@ -21,6 +21,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:nullability/nullability.dart';
|
||||
@@ -75,6 +76,9 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
@override
|
||||
bool get enterpriseRootsEnabled => super.enterpriseRootsEnabled!;
|
||||
|
||||
@override
|
||||
List<String> get locales => super.locales!;
|
||||
|
||||
final QueryParameterStripping queryParameterStripping;
|
||||
|
||||
final BounceTrackingProtectionMode bounceTrackingProtectionMode;
|
||||
@@ -130,6 +134,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
required this.dohExceptionsList,
|
||||
required super.fingerprintingProtectionOverrides,
|
||||
required this.enablePdfJs,
|
||||
required super.locales,
|
||||
});
|
||||
|
||||
EngineSettings.withDefaults({
|
||||
@@ -154,6 +159,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
List<String>? dohExceptionsList,
|
||||
String? fingerprintingProtectionOverrides,
|
||||
bool? enablePdfJs,
|
||||
List<String>? locales,
|
||||
}) : queryParameterStripping =
|
||||
queryParameterStripping ?? QueryParameterStripping.disabled,
|
||||
bounceTrackingProtectionMode =
|
||||
@@ -188,6 +194,11 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
fingerprintingProtectionOverrides:
|
||||
fingerprintingProtectionOverrides ??
|
||||
FingerprintOverrides.defaults().toString(),
|
||||
locales:
|
||||
locales ??
|
||||
WidgetsBinding.instance.platformDispatcher.locales
|
||||
.map((x) => x.toLanguageTag())
|
||||
.toList(),
|
||||
);
|
||||
|
||||
static AddonCollection? _addonCollectionFromJson(String? json) =>
|
||||
@@ -226,5 +237,6 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
dohExceptionsList,
|
||||
fingerprintingProtectionOverrides,
|
||||
enablePdfJs,
|
||||
locales,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ abstract class _$EngineSettingsCWProxy {
|
||||
|
||||
EngineSettings enablePdfJs(bool enablePdfJs);
|
||||
|
||||
EngineSettings locales(List<String>? locales);
|
||||
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
@@ -96,6 +98,7 @@ abstract class _$EngineSettingsCWProxy {
|
||||
List<String> dohExceptionsList,
|
||||
String? fingerprintingProtectionOverrides,
|
||||
bool enablePdfJs,
|
||||
List<String>? locales,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -207,6 +210,9 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
EngineSettings enablePdfJs(bool enablePdfJs) =>
|
||||
call(enablePdfJs: enablePdfJs);
|
||||
|
||||
@override
|
||||
EngineSettings locales(List<String>? locales) => call(locales: locales);
|
||||
|
||||
@override
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`.
|
||||
@@ -239,6 +245,7 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
Object? dohExceptionsList = const $CopyWithPlaceholder(),
|
||||
Object? fingerprintingProtectionOverrides = const $CopyWithPlaceholder(),
|
||||
Object? enablePdfJs = const $CopyWithPlaceholder(),
|
||||
Object? locales = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return EngineSettings(
|
||||
javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder()
|
||||
@@ -350,6 +357,10 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
? _value.enablePdfJs
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: enablePdfJs as bool,
|
||||
locales: locales == const $CopyWithPlaceholder()
|
||||
? _value.locales
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: locales as List<String>?,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -422,6 +433,9 @@ EngineSettings _$EngineSettingsFromJson(Map<String, dynamic> json) =>
|
||||
fingerprintingProtectionOverrides:
|
||||
json['fingerprintingProtectionOverrides'] as String?,
|
||||
enablePdfJs: json['enablePdfJs'] as bool?,
|
||||
locales: (json['locales'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$EngineSettingsToJson(
|
||||
@@ -448,6 +462,7 @@ Map<String, dynamic> _$EngineSettingsToJson(
|
||||
_$WebContentIsolationStrategyEnumMap[instance
|
||||
.webContentIsolationStrategy]!,
|
||||
'enterpriseRootsEnabled': instance.enterpriseRootsEnabled,
|
||||
'locales': instance.locales,
|
||||
'queryParameterStripping':
|
||||
_$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!,
|
||||
'bounceTrackingProtectionMode':
|
||||
|
||||
@@ -124,6 +124,9 @@ class EngineSettingsRepository extends _$EngineSettingsRepository {
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'locales': settings['locales']
|
||||
?.readAs(DriftSqlType.string, db.typeMapping)
|
||||
.mapNotNull(jsonDecode),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ final class EngineSettingsRepositoryProvider
|
||||
}
|
||||
|
||||
String _$engineSettingsRepositoryHash() =>
|
||||
r'cd087c5631faaddf1fced31569b0fd577d6030a7';
|
||||
r'916abcd932fc3b3b1e060bc93a46c1a3a38bb984';
|
||||
|
||||
abstract class _$EngineSettingsRepository
|
||||
extends $StreamNotifier<EngineSettings> {
|
||||
|
||||
Reference in New Issue
Block a user