ublock list management initial
This commit is contained in:
@@ -338,6 +338,16 @@ class _ManagementSection extends ConsumerWidget {
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => openAddonSettingsFlow(context, ref, addon),
|
||||
),
|
||||
if (addon.id == 'uBlock0@raymondhill.net')
|
||||
ListTile(
|
||||
leading: const Icon(Icons.filter_list),
|
||||
title: const Text('Filter Lists & Hardenings'),
|
||||
subtitle: const Text(
|
||||
'Manage filter lists and apply WebLibre hardenings',
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => UBlockFilterListsRoute().push<void>(context),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.privacy_tip_outlined),
|
||||
title: const Text('Permissions'),
|
||||
|
||||
+31
@@ -17,6 +17,8 @@
|
||||
* 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 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart' show ThemeMode;
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
@@ -26,6 +28,7 @@ import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_addon.dart';
|
||||
import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_observer.dart';
|
||||
import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
|
||||
@@ -33,6 +36,7 @@ part 'engine_settings_replication.g.dart';
|
||||
|
||||
const _safeBrowsingMalwarePref = 'browser.safebrowsing.malware.enabled';
|
||||
const _safeBrowsingPhishingPref = 'browser.safebrowsing.phishing.enabled';
|
||||
const ublockFilterListsPref = 'browser.weblibre.uBO.filterLists';
|
||||
|
||||
/// Checks if any Custom ETP setting changed between two EngineSettings instances.
|
||||
bool _customEtpSettingsChanged(
|
||||
@@ -55,6 +59,22 @@ bool _customEtpSettingsChanged(
|
||||
previous.allowListConvenience != current.allowListConvenience;
|
||||
}
|
||||
|
||||
Future<void> syncUBlockFilterLists(
|
||||
PreferenceFixator fixator,
|
||||
EngineSettings settings,
|
||||
) async {
|
||||
if (!settings.ublockFilterListSettings.enabled) {
|
||||
await fixator.unregister(ublockFilterListsPref);
|
||||
await GeckoPrefService().resetPrefs([ublockFilterListsPref]);
|
||||
return;
|
||||
}
|
||||
|
||||
await fixator.register(
|
||||
ublockFilterListsPref,
|
||||
jsonEncode(settings.ublockFilterListSettings.resolveFinalList()),
|
||||
);
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class EngineSettingsReplicationService
|
||||
extends _$EngineSettingsReplicationService {
|
||||
@@ -296,6 +316,13 @@ class EngineSettingsReplicationService
|
||||
if (previous.value?.lnaEnabled != settings.lnaEnabled) {
|
||||
await _service.lnaEnabled(settings.lnaEnabled);
|
||||
}
|
||||
if (previous.value?.ublockFilterListSettings !=
|
||||
settings.ublockFilterListSettings) {
|
||||
await syncUBlockFilterLists(
|
||||
ref.read(preferenceFixatorProvider.notifier),
|
||||
settings,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await _service.setDefaultSettings(settings);
|
||||
await ref
|
||||
@@ -319,6 +346,10 @@ class EngineSettingsReplicationService
|
||||
await ref
|
||||
.read(preferenceFixatorProvider.notifier)
|
||||
.register('intl.accept_languages', settings.locales.join(','));
|
||||
await syncUBlockFilterLists(
|
||||
ref.read(preferenceFixatorProvider.notifier),
|
||||
settings,
|
||||
);
|
||||
|
||||
// Initialize unsigned extensions fixator from Gecko pref
|
||||
await ref.read(allowUnsignedExtensionsProvider.future);
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ final class EngineSettingsReplicationServiceProvider
|
||||
}
|
||||
|
||||
String _$engineSettingsReplicationServiceHash() =>
|
||||
r'061f0b32a9c49e3bd4b76cd99993620558e0d3b2';
|
||||
r'71b611a99af83187e392d16288cec280ef44269f';
|
||||
|
||||
abstract class _$EngineSettingsReplicationService extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
+1
-2
@@ -36,7 +36,6 @@ import 'package:skeletonizer/skeletonizer.dart';
|
||||
import 'package:weblibre/core/design/app_colors.dart';
|
||||
import 'package:weblibre/core/providers/persisted_bool.dart';
|
||||
import 'package:weblibre/core/routing/routes.dart';
|
||||
import 'package:weblibre/features/addons/presentation/screens/addon_internal_settings.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/entities/states/readerable.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/entities/tab_container_selection.dart';
|
||||
@@ -1715,7 +1714,7 @@ class _ExtensionsCard extends HookConsumerWidget {
|
||||
final rootContext = Navigator.of(context, rootNavigator: true).context;
|
||||
Future<void> openExtensionSettings(String extensionId) async {
|
||||
Navigator.pop(context);
|
||||
await openAddonSettingsFlowById(rootContext, ref, extensionId);
|
||||
await AddonDetailsRoute(addonId: extensionId).push<void>(rootContext);
|
||||
}
|
||||
|
||||
return _buildMenuCard(
|
||||
|
||||
+1
@@ -169,6 +169,7 @@ class CompactAppBarTitleView extends StatelessWidget {
|
||||
Flexible(
|
||||
child: UriBreadcrumb(
|
||||
uri: tabState.url,
|
||||
showHttpScheme: false,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
|
||||
+2
-1
@@ -546,6 +546,7 @@ class ListTabPreview extends HookConsumerWidget {
|
||||
Expanded(
|
||||
child: UriBreadcrumb(
|
||||
uri: tabState.url,
|
||||
showHttpScheme: false,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: subtitleColor,
|
||||
),
|
||||
@@ -636,7 +637,7 @@ class SyncedListTabPreview extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
UriBreadcrumb(uri: url),
|
||||
UriBreadcrumb(uri: url, showHttpScheme: false),
|
||||
],
|
||||
),
|
||||
trailing: const Icon(Icons.open_in_new),
|
||||
|
||||
+4
-1
@@ -77,7 +77,10 @@ class BookmarkSearch extends HookConsumerWidget {
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: UriBreadcrumb(uri: bookmark.url),
|
||||
subtitle: UriBreadcrumb(
|
||||
uri: bookmark.url,
|
||||
showHttpScheme: false,
|
||||
),
|
||||
onTap: () => onUriSelected(bookmark.url),
|
||||
);
|
||||
},
|
||||
|
||||
+4
-1
@@ -112,7 +112,10 @@ class HistorySuggestions extends HookConsumerWidget {
|
||||
),
|
||||
subtitle:
|
||||
uri.mapNotNull(
|
||||
(uri) => UriBreadcrumb(uri: uri),
|
||||
(uri) => UriBreadcrumb(
|
||||
uri: uri,
|
||||
showHttpScheme: false,
|
||||
),
|
||||
) ??
|
||||
suggestion.description.mapNotNull(
|
||||
(description) => Text(
|
||||
|
||||
+1
-1
@@ -244,7 +244,7 @@ class TabSearch extends HookConsumerWidget {
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)
|
||||
: UriBreadcrumb(uri: result.url),
|
||||
: UriBreadcrumb(uri: result.url, showHttpScheme: false),
|
||||
onTap: () async {
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
|
||||
@@ -41,7 +41,7 @@ final class TabDataRepositoryProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$tabDataRepositoryHash() => r'c64a44c7d9de566617798a0a3bdcad51bf436658';
|
||||
String _$tabDataRepositoryHash() => r'e6482b5408aa567f626d2f0a9455b2cc908ba84f';
|
||||
|
||||
abstract class _$TabDataRepository extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
@@ -20,11 +20,17 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_addon.dart';
|
||||
import 'package:weblibre/features/onboarding/presentation/pages/abstract/i_form_page.dart';
|
||||
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/ublock_filter_list_settings.dart';
|
||||
import 'package:weblibre/features/user/data/providers/ublock_assets.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
|
||||
import 'package:weblibre/presentation/widgets/browser_page.dart';
|
||||
|
||||
class UBlockOptInPage extends HookConsumerWidget implements IFormPage {
|
||||
@@ -36,6 +42,7 @@ class UBlockOptInPage extends HookConsumerWidget implements IFormPage {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = Theme.of(context);
|
||||
final installUBlock = useState(true);
|
||||
|
||||
return BrowserPage(
|
||||
child: BrowserPageContent(
|
||||
@@ -66,6 +73,7 @@ There are many other lists available to block even more.
|
||||
FormField(
|
||||
initialValue: true,
|
||||
onSaved: (newValue) {
|
||||
installUBlock.value = newValue ?? false;
|
||||
if (newValue == true) {
|
||||
unawaited(
|
||||
ref
|
||||
@@ -80,9 +88,53 @@ There are many other lists available to block even more.
|
||||
title: const Text('Install uBlock Origin Extension'),
|
||||
onChanged: (value) {
|
||||
field.didChange(value);
|
||||
installUBlock.value = value;
|
||||
},
|
||||
),
|
||||
),
|
||||
FormField(
|
||||
initialValue: true,
|
||||
onSaved: (newValue) {
|
||||
if (newValue != true || !installUBlock.value) return;
|
||||
|
||||
Future<void> applyOptimizedDefaults() async {
|
||||
try {
|
||||
final registry = await ref.read(
|
||||
ublockAssetsRegistryProvider.future,
|
||||
);
|
||||
final optimized =
|
||||
UBlockFilterListSettings.optimizedDefaults(registry);
|
||||
await ref
|
||||
.read(engineSettingsRepositoryProvider.notifier)
|
||||
.updateSettings(
|
||||
(current) => current.copyWith
|
||||
.ublockFilterListSettings(optimized),
|
||||
);
|
||||
} catch (e, s) {
|
||||
logger.w(
|
||||
'Failed applying optimized uBlock defaults during onboarding',
|
||||
error: e,
|
||||
stackTrace: s,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unawaited(applyOptimizedDefaults());
|
||||
},
|
||||
builder: (field) => SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: field.value ?? false,
|
||||
title: const Text('Apply optimized defaults'),
|
||||
subtitle: const Text(
|
||||
'Enable WebLibre hardening filter lists.',
|
||||
),
|
||||
onChanged: installUBlock.value
|
||||
? (value) {
|
||||
field.didChange(value);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -292,24 +292,17 @@ class _ErrorLogsTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomListTile(
|
||||
title: 'Error Logs',
|
||||
subtitle: 'View and copy logs for issue reporting',
|
||||
prefix: Padding(
|
||||
padding: const EdgeInsets.only(right: 16.0),
|
||||
child: Icon(
|
||||
Icons.bug_report,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
suffix: FilledButton.icon(
|
||||
onPressed: () async {
|
||||
await ErrorLogsRoute().push(context);
|
||||
},
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
label: const Text('View'),
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
Icons.bug_report,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
title: const Text('Error Logs'),
|
||||
subtitle: const Text('View and copy logs for issue reporting'),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () async {
|
||||
await ErrorLogsRoute().push(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,25 +65,18 @@ class _ManageExtensionsTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomListTile(
|
||||
title: 'Manage Extensions',
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
MdiIcons.puzzleEdit,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
title: const Text('Manage Extensions'),
|
||||
subtitle:
|
||||
'Browse installed, disabled, available, and unsupported extensions',
|
||||
prefix: Padding(
|
||||
padding: const EdgeInsets.only(right: 16.0),
|
||||
child: Icon(
|
||||
MdiIcons.puzzleEdit,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
suffix: FilledButton.icon(
|
||||
onPressed: () async {
|
||||
await const AddonManagerRoute().push<void>(context);
|
||||
},
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
label: const Text('Open'),
|
||||
),
|
||||
const Text('Browse installed, disabled, available, and unsupported extensions'),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () async {
|
||||
await const AddonManagerRoute().push<void>(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -93,24 +86,17 @@ class _AddonCollectionTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomListTile(
|
||||
title: 'Custom Collection',
|
||||
subtitle: 'Use a custom Mozilla addon collection',
|
||||
prefix: Padding(
|
||||
padding: const EdgeInsets.only(right: 16.0),
|
||||
child: Icon(
|
||||
MdiIcons.folderMultiple,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
suffix: FilledButton.icon(
|
||||
onPressed: () async {
|
||||
await AddonCollectionRoute().push(context);
|
||||
},
|
||||
icon: const Icon(Icons.settings),
|
||||
label: const Text('Configure'),
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
MdiIcons.folderMultiple,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
title: const Text('Custom Collection'),
|
||||
subtitle: const Text('Use a custom Mozilla addon collection'),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () async {
|
||||
await AddonCollectionRoute().push(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -97,6 +97,7 @@ class _TrackingProtectionSection extends StatelessWidget {
|
||||
_BounceTrackingProtectionTile(),
|
||||
_QueryParameterStrippingSection(),
|
||||
_TrackingProtectionExceptionsTile(),
|
||||
_UBlockFilterListsTile(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -727,6 +728,27 @@ class _WebEngineHardeningTile extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _UBlockFilterListsTile extends StatelessWidget {
|
||||
const _UBlockFilterListsTile();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: const Text('uBlock Filter Lists & Hardenings'),
|
||||
subtitle: const Text('Manage filter lists and apply WebLibre hardenings'),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 8.0,
|
||||
horizontal: 16.0,
|
||||
),
|
||||
leading: const Icon(Icons.filter_list),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () async {
|
||||
await UBlockFilterListsRoute().push<void>(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FissionEnabledTile extends HookConsumerWidget {
|
||||
const _FissionEnabledTile();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -279,6 +279,7 @@ class _HistoryListItem extends ConsumerWidget {
|
||||
const SizedBox(height: 3),
|
||||
UriBreadcrumb(
|
||||
uri: visit.url,
|
||||
showHttpScheme: false,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
||||
@@ -25,6 +25,7 @@ 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';
|
||||
import 'package:weblibre/features/user/data/models/ublock_filter_list_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/entities/fingerprint_overrides.dart';
|
||||
|
||||
part 'engine_settings.g.dart';
|
||||
@@ -133,6 +134,12 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
@JsonKey(fromJson: _addonCollectionFromJson, toJson: _addonCollectionToJson)
|
||||
final AddonCollection? addonCollection;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: _ublockFilterListSettingsFromJson,
|
||||
toJson: _ublockFilterListSettingsToJson,
|
||||
)
|
||||
final UBlockFilterListSettings ublockFilterListSettings;
|
||||
|
||||
final DohSettingsMode dohSettingsMode;
|
||||
final String dohProviderUrl;
|
||||
final String dohDefaultProviderUrl;
|
||||
@@ -179,6 +186,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
required this.queryParameterStripping,
|
||||
required this.bounceTrackingProtectionMode,
|
||||
required this.addonCollection,
|
||||
required this.ublockFilterListSettings,
|
||||
required this.dohSettingsMode,
|
||||
required this.dohProviderUrl,
|
||||
required this.dohDefaultProviderUrl,
|
||||
@@ -232,6 +240,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
super.userAgent,
|
||||
bool? enterpriseRootsEnabled,
|
||||
this.addonCollection,
|
||||
UBlockFilterListSettings? ublockFilterListSettings,
|
||||
DohSettingsMode? dohSettingsMode,
|
||||
String? dohProviderUrl,
|
||||
String? dohDefaultProviderUrl,
|
||||
@@ -267,7 +276,9 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
super.lnaBlocking,
|
||||
bool? lnaBlockTrackers,
|
||||
bool? lnaEnabled,
|
||||
}) : queryParameterStripping =
|
||||
}) : ublockFilterListSettings =
|
||||
ublockFilterListSettings ?? UBlockFilterListSettings(),
|
||||
queryParameterStripping =
|
||||
queryParameterStripping ?? QueryParameterStripping.enabled,
|
||||
bounceTrackingProtectionMode =
|
||||
bounceTrackingProtectionMode ?? BounceTrackingProtectionMode.enabled,
|
||||
@@ -341,6 +352,20 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
static String? _addonCollectionToJson(AddonCollection? collection) =>
|
||||
collection.mapNotNull((collection) => jsonEncode(collection.encode()));
|
||||
|
||||
static UBlockFilterListSettings _ublockFilterListSettingsFromJson(
|
||||
String? json,
|
||||
) =>
|
||||
json.mapNotNull(
|
||||
(encoded) => UBlockFilterListSettings.fromJson(
|
||||
jsonDecode(encoded) as Map<String, dynamic>,
|
||||
),
|
||||
) ??
|
||||
UBlockFilterListSettings();
|
||||
|
||||
static String _ublockFilterListSettingsToJson(
|
||||
UBlockFilterListSettings settings,
|
||||
) => jsonEncode(settings.toJson());
|
||||
|
||||
factory EngineSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$EngineSettingsFromJson(json);
|
||||
|
||||
@@ -363,6 +388,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
queryParameterStripping,
|
||||
bounceTrackingProtectionMode,
|
||||
addonCollection,
|
||||
ublockFilterListSettings,
|
||||
dohSettingsMode,
|
||||
dohProviderUrl,
|
||||
dohDefaultProviderUrl,
|
||||
|
||||
@@ -53,6 +53,10 @@ abstract class _$EngineSettingsCWProxy {
|
||||
|
||||
EngineSettings addonCollection(AddonCollection? addonCollection);
|
||||
|
||||
EngineSettings ublockFilterListSettings(
|
||||
UBlockFilterListSettings ublockFilterListSettings,
|
||||
);
|
||||
|
||||
EngineSettings dohSettingsMode(DohSettingsMode dohSettingsMode);
|
||||
|
||||
EngineSettings dohProviderUrl(String dohProviderUrl);
|
||||
@@ -152,6 +156,7 @@ abstract class _$EngineSettingsCWProxy {
|
||||
QueryParameterStripping queryParameterStripping,
|
||||
BounceTrackingProtectionMode bounceTrackingProtectionMode,
|
||||
AddonCollection? addonCollection,
|
||||
UBlockFilterListSettings ublockFilterListSettings,
|
||||
DohSettingsMode dohSettingsMode,
|
||||
String dohProviderUrl,
|
||||
String dohDefaultProviderUrl,
|
||||
@@ -271,6 +276,11 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
EngineSettings addonCollection(AddonCollection? addonCollection) =>
|
||||
call(addonCollection: addonCollection);
|
||||
|
||||
@override
|
||||
EngineSettings ublockFilterListSettings(
|
||||
UBlockFilterListSettings ublockFilterListSettings,
|
||||
) => call(ublockFilterListSettings: ublockFilterListSettings);
|
||||
|
||||
@override
|
||||
EngineSettings dohSettingsMode(DohSettingsMode dohSettingsMode) =>
|
||||
call(dohSettingsMode: dohSettingsMode);
|
||||
@@ -442,6 +452,7 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
Object? queryParameterStripping = const $CopyWithPlaceholder(),
|
||||
Object? bounceTrackingProtectionMode = const $CopyWithPlaceholder(),
|
||||
Object? addonCollection = const $CopyWithPlaceholder(),
|
||||
Object? ublockFilterListSettings = const $CopyWithPlaceholder(),
|
||||
Object? dohSettingsMode = const $CopyWithPlaceholder(),
|
||||
Object? dohProviderUrl = const $CopyWithPlaceholder(),
|
||||
Object? dohDefaultProviderUrl = const $CopyWithPlaceholder(),
|
||||
@@ -554,6 +565,12 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
? _value.addonCollection
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: addonCollection as AddonCollection?,
|
||||
ublockFilterListSettings:
|
||||
ublockFilterListSettings == const $CopyWithPlaceholder() ||
|
||||
ublockFilterListSettings == null
|
||||
? _value.ublockFilterListSettings
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: ublockFilterListSettings as UBlockFilterListSettings,
|
||||
dohSettingsMode:
|
||||
dohSettingsMode == const $CopyWithPlaceholder() ||
|
||||
dohSettingsMode == null
|
||||
@@ -777,6 +794,9 @@ EngineSettings _$EngineSettingsFromJson(
|
||||
addonCollection: EngineSettings._addonCollectionFromJson(
|
||||
json['addonCollection'] as String?,
|
||||
),
|
||||
ublockFilterListSettings: EngineSettings._ublockFilterListSettingsFromJson(
|
||||
json['ublockFilterListSettings'] as String?,
|
||||
),
|
||||
dohSettingsMode: $enumDecodeNullable(
|
||||
_$DohSettingsModeEnumMap,
|
||||
json['dohSettingsMode'],
|
||||
@@ -893,6 +913,9 @@ Map<String, dynamic> _$EngineSettingsToJson(
|
||||
'addonCollection': EngineSettings._addonCollectionToJson(
|
||||
instance.addonCollection,
|
||||
),
|
||||
'ublockFilterListSettings': EngineSettings._ublockFilterListSettingsToJson(
|
||||
instance.ublockFilterListSettings,
|
||||
),
|
||||
'dohSettingsMode': _$DohSettingsModeEnumMap[instance.dohSettingsMode]!,
|
||||
'dohProviderUrl': instance.dohProviderUrl,
|
||||
'dohDefaultProviderUrl': instance.dohDefaultProviderUrl,
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'ublock_asset.g.dart';
|
||||
|
||||
enum UBlockAssetGroup {
|
||||
@JsonValue('default')
|
||||
$default,
|
||||
@JsonValue('ads')
|
||||
ads,
|
||||
@JsonValue('privacy')
|
||||
privacy,
|
||||
@JsonValue('malware')
|
||||
malware,
|
||||
@JsonValue('annoyances')
|
||||
annoyances,
|
||||
@JsonValue('multipurpose')
|
||||
multipurpose,
|
||||
@JsonValue('regions')
|
||||
regions;
|
||||
|
||||
String get label => switch (this) {
|
||||
$default => 'Default',
|
||||
ads => 'Ads',
|
||||
privacy => 'Privacy',
|
||||
malware => 'Malware',
|
||||
annoyances => 'Annoyances',
|
||||
multipurpose => 'Multipurpose',
|
||||
regions => 'Regions',
|
||||
};
|
||||
|
||||
static const displayOrder = UBlockAssetGroup.values;
|
||||
}
|
||||
|
||||
enum UBlockAssetSubGroup {
|
||||
@JsonValue('cookies')
|
||||
cookies,
|
||||
@JsonValue('social')
|
||||
social;
|
||||
|
||||
String get label => switch (this) {
|
||||
cookies => 'Cookie Notices',
|
||||
social => 'Social Widgets',
|
||||
};
|
||||
}
|
||||
|
||||
List<String> _contentUrlFromJson(dynamic value) {
|
||||
if (value is List) return value.cast<String>();
|
||||
if (value is String) return [value];
|
||||
return [];
|
||||
}
|
||||
|
||||
dynamic _contentUrlToJson(List<String> value) {
|
||||
if (value.length == 1) return value.first;
|
||||
return value;
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class UBlockAssetEntry {
|
||||
final String content;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final UBlockAssetGroup? group;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final UBlockAssetSubGroup? group2;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? parent;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? title;
|
||||
|
||||
@JsonKey(
|
||||
includeIfNull: false,
|
||||
fromJson: _contentUrlFromJson,
|
||||
toJson: _contentUrlToJson,
|
||||
)
|
||||
final List<String> contentURL;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final List<String>? cdnURLs;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final List<String>? patchURLs;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? supportURL;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? instructionURL;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? tags;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? lang;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? ua;
|
||||
|
||||
@JsonKey(includeIfNull: false, defaultValue: false)
|
||||
final bool off;
|
||||
|
||||
@JsonKey(includeIfNull: false, defaultValue: false)
|
||||
final bool preferred;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final int? updateAfter;
|
||||
|
||||
const UBlockAssetEntry({
|
||||
required this.content,
|
||||
this.group,
|
||||
this.group2,
|
||||
this.parent,
|
||||
this.title,
|
||||
this.contentURL = const [],
|
||||
this.cdnURLs,
|
||||
this.patchURLs,
|
||||
this.supportURL,
|
||||
this.instructionURL,
|
||||
this.tags,
|
||||
this.lang,
|
||||
this.ua,
|
||||
this.off = false,
|
||||
this.preferred = false,
|
||||
this.updateAfter,
|
||||
});
|
||||
|
||||
bool get isFilterList => content == 'filters';
|
||||
|
||||
bool get isDefaultEnabled => !off;
|
||||
|
||||
UBlockAssetGroup get effectiveGroup =>
|
||||
group2?.toGroup() ?? group ?? UBlockAssetGroup.ads;
|
||||
|
||||
factory UBlockAssetEntry.fromJson(Map<String, dynamic> json) =>
|
||||
_$UBlockAssetEntryFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$UBlockAssetEntryToJson(this);
|
||||
}
|
||||
|
||||
extension on UBlockAssetSubGroup {
|
||||
UBlockAssetGroup toGroup() => switch (this) {
|
||||
UBlockAssetSubGroup.cookies => UBlockAssetGroup.annoyances,
|
||||
UBlockAssetSubGroup.social => UBlockAssetGroup.annoyances,
|
||||
};
|
||||
}
|
||||
|
||||
class UBlockAssetsRegistry {
|
||||
final Map<String, UBlockAssetEntry> _entries;
|
||||
|
||||
UBlockAssetsRegistry(this._entries);
|
||||
|
||||
Map<String, UBlockAssetEntry> get filterEntries =>
|
||||
Map.fromEntries(_entries.entries.where((e) => e.value.isFilterList));
|
||||
|
||||
List<String> get defaultEnabledTokens => _entries.entries
|
||||
.where((e) => e.value.isFilterList && e.value.isDefaultEnabled)
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
|
||||
UBlockAssetEntry? operator [](String key) => _entries[key];
|
||||
|
||||
Map<UBlockAssetGroup, Map<String?, List<String>>> buildGroupedParentTree() {
|
||||
final result = <UBlockAssetGroup, Map<String?, List<String>>>{};
|
||||
|
||||
for (final group in UBlockAssetGroup.displayOrder) {
|
||||
final groupEntries = <String?, List<String>>{};
|
||||
final processedKeys = <String>{};
|
||||
|
||||
final groupSubGroups = UBlockAssetSubGroup.values
|
||||
.where((sg) => sg.toGroup() == group)
|
||||
.toList();
|
||||
|
||||
for (final subGroup in groupSubGroups) {
|
||||
for (final entry in _entries.entries.where(
|
||||
(e) => e.value.isFilterList && e.value.group2 == subGroup,
|
||||
)) {
|
||||
final parentKey = entry.value.parent;
|
||||
groupEntries.putIfAbsent(parentKey, () => []).add(entry.key);
|
||||
processedKeys.add(entry.key);
|
||||
}
|
||||
}
|
||||
|
||||
for (final entry in _entries.entries.where(
|
||||
(e) =>
|
||||
e.value.isFilterList &&
|
||||
e.value.group == group &&
|
||||
e.value.group2 == null &&
|
||||
!processedKeys.contains(e.key),
|
||||
)) {
|
||||
final parentKey = entry.value.parent;
|
||||
groupEntries.putIfAbsent(parentKey, () => []).add(entry.key);
|
||||
}
|
||||
|
||||
if (groupEntries.isNotEmpty) {
|
||||
result[group] = groupEntries;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
List<String> tokensMatchingLocales(Iterable<String> languageCodes) {
|
||||
final primaryCodes = languageCodes.map((code) {
|
||||
final parts = code.split('-');
|
||||
return parts.first.toLowerCase();
|
||||
}).toSet();
|
||||
|
||||
return _entries.entries
|
||||
.where((e) {
|
||||
if (!e.value.isFilterList || !e.value.off) return false;
|
||||
final lang = e.value.lang;
|
||||
if (lang == null) return false;
|
||||
final entryLangs = lang
|
||||
.split(RegExp(r'\s+'))
|
||||
.map((l) => l.toLowerCase())
|
||||
.toSet();
|
||||
return primaryCodes.intersection(entryLangs).isNotEmpty;
|
||||
})
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
}
|
||||
|
||||
int enabledCountInGroup(UBlockAssetGroup group, Set<String> enabledTokens) {
|
||||
var count = 0;
|
||||
for (final entry in _entries.entries) {
|
||||
if (!entry.value.isFilterList) continue;
|
||||
if (entry.value.effectiveGroup != group) continue;
|
||||
if (enabledTokens.contains(entry.key)) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int totalCountInGroup(UBlockAssetGroup group) {
|
||||
var count = 0;
|
||||
for (final entry in _entries.entries) {
|
||||
if (!entry.value.isFilterList) continue;
|
||||
if (entry.value.effectiveGroup != group) continue;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static UBlockAssetsRegistry fromJson(Map<String, dynamic> json) {
|
||||
final entries = <String, UBlockAssetEntry>{};
|
||||
for (final entry in json.entries) {
|
||||
if (entry.value is Map<String, dynamic>) {
|
||||
entries[entry.key] = UBlockAssetEntry.fromJson(
|
||||
entry.value as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
}
|
||||
return UBlockAssetsRegistry(entries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'ublock_asset.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
UBlockAssetEntry _$UBlockAssetEntryFromJson(Map<String, dynamic> json) =>
|
||||
UBlockAssetEntry(
|
||||
content: json['content'] as String,
|
||||
group: $enumDecodeNullable(_$UBlockAssetGroupEnumMap, json['group']),
|
||||
group2: $enumDecodeNullable(_$UBlockAssetSubGroupEnumMap, json['group2']),
|
||||
parent: json['parent'] as String?,
|
||||
title: json['title'] as String?,
|
||||
contentURL: json['contentURL'] == null
|
||||
? const []
|
||||
: _contentUrlFromJson(json['contentURL']),
|
||||
cdnURLs: (json['cdnURLs'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
patchURLs: (json['patchURLs'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
supportURL: json['supportURL'] as String?,
|
||||
instructionURL: json['instructionURL'] as String?,
|
||||
tags: json['tags'] as String?,
|
||||
lang: json['lang'] as String?,
|
||||
ua: json['ua'] as String?,
|
||||
off: json['off'] as bool? ?? false,
|
||||
preferred: json['preferred'] as bool? ?? false,
|
||||
updateAfter: (json['updateAfter'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UBlockAssetEntryToJson(UBlockAssetEntry instance) =>
|
||||
<String, dynamic>{
|
||||
'content': instance.content,
|
||||
'group': ?_$UBlockAssetGroupEnumMap[instance.group],
|
||||
'group2': ?_$UBlockAssetSubGroupEnumMap[instance.group2],
|
||||
'parent': ?instance.parent,
|
||||
'title': ?instance.title,
|
||||
'contentURL': ?_contentUrlToJson(instance.contentURL),
|
||||
'cdnURLs': ?instance.cdnURLs,
|
||||
'patchURLs': ?instance.patchURLs,
|
||||
'supportURL': ?instance.supportURL,
|
||||
'instructionURL': ?instance.instructionURL,
|
||||
'tags': ?instance.tags,
|
||||
'lang': ?instance.lang,
|
||||
'ua': ?instance.ua,
|
||||
'off': instance.off,
|
||||
'preferred': instance.preferred,
|
||||
'updateAfter': ?instance.updateAfter,
|
||||
};
|
||||
|
||||
const _$UBlockAssetGroupEnumMap = {
|
||||
UBlockAssetGroup.$default: 'default',
|
||||
UBlockAssetGroup.ads: 'ads',
|
||||
UBlockAssetGroup.privacy: 'privacy',
|
||||
UBlockAssetGroup.malware: 'malware',
|
||||
UBlockAssetGroup.annoyances: 'annoyances',
|
||||
UBlockAssetGroup.multipurpose: 'multipurpose',
|
||||
UBlockAssetGroup.regions: 'regions',
|
||||
};
|
||||
|
||||
const _$UBlockAssetSubGroupEnumMap = {
|
||||
UBlockAssetSubGroup.cookies: 'cookies',
|
||||
UBlockAssetSubGroup.social: 'social',
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:weblibre/features/user/data/models/ublock_asset.dart';
|
||||
|
||||
part 'ublock_filter_list_settings.g.dart';
|
||||
|
||||
const kUBlockMaxExternalUrls = 32;
|
||||
|
||||
const kUBlockHardeningStockTokens = <String>[
|
||||
'adguard-mobile',
|
||||
'adguard-mobile-app-banners',
|
||||
'adguard-spyware-url',
|
||||
'fanboy-cookiemonster',
|
||||
'fanboy-social',
|
||||
'ublock-annoyances',
|
||||
];
|
||||
|
||||
final kUBlockHardeningExternalLists = <UBlockExternalList>[
|
||||
UBlockExternalList(
|
||||
url:
|
||||
'https://raw.githubusercontent.com/DandelionSprout/adfilt/master/LegitimateURLShortener.txt',
|
||||
description: 'Legitimate URL Shortener Tool (DandelionSprout)',
|
||||
),
|
||||
];
|
||||
|
||||
@CopyWith()
|
||||
@JsonSerializable(includeIfNull: true)
|
||||
class UBlockExternalList with FastEquatable {
|
||||
final String url;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
final String? description;
|
||||
|
||||
UBlockExternalList({required this.url, this.description});
|
||||
|
||||
factory UBlockExternalList.fromJson(Map<String, dynamic> json) =>
|
||||
_$UBlockExternalListFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$UBlockExternalListToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [url, description];
|
||||
}
|
||||
|
||||
@CopyWith()
|
||||
@JsonSerializable(includeIfNull: true)
|
||||
class UBlockFilterListSettings with FastEquatable {
|
||||
static const String _userFiltersToken = 'user-filters';
|
||||
|
||||
final bool enabled;
|
||||
|
||||
final List<String> enabledStockListTokens;
|
||||
|
||||
final List<String> autoEnabledStockListTokens;
|
||||
|
||||
final bool autoSelectRegionalLists;
|
||||
|
||||
final List<UBlockExternalList> externalFilterLists;
|
||||
|
||||
UBlockFilterListSettings({
|
||||
this.enabled = false,
|
||||
this.enabledStockListTokens = const [],
|
||||
this.autoEnabledStockListTokens = const [],
|
||||
this.autoSelectRegionalLists = false,
|
||||
this.externalFilterLists = const [],
|
||||
});
|
||||
|
||||
factory UBlockFilterListSettings.managedDefaults(UBlockAssetsRegistry registry) {
|
||||
return UBlockFilterListSettings(
|
||||
enabled: true,
|
||||
enabledStockListTokens: registry.defaultEnabledTokens,
|
||||
);
|
||||
}
|
||||
|
||||
factory UBlockFilterListSettings.optimizedDefaults(
|
||||
UBlockAssetsRegistry registry,
|
||||
) {
|
||||
final defaultTokens = registry.defaultEnabledTokens;
|
||||
|
||||
final stockTokens = [...defaultTokens];
|
||||
for (final token in kUBlockHardeningStockTokens) {
|
||||
if (!stockTokens.contains(token)) {
|
||||
stockTokens.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
final externals = <UBlockExternalList>[];
|
||||
final seenUrls = <String>{};
|
||||
for (final entry in kUBlockHardeningExternalLists) {
|
||||
if (seenUrls.add(entry.url) &&
|
||||
externals.length < kUBlockMaxExternalUrls) {
|
||||
externals.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return UBlockFilterListSettings(
|
||||
enabled: true,
|
||||
enabledStockListTokens: stockTokens,
|
||||
externalFilterLists: externals,
|
||||
);
|
||||
}
|
||||
|
||||
factory UBlockFilterListSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$UBlockFilterListSettingsFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$UBlockFilterListSettingsToJson(this);
|
||||
|
||||
List<String> resolveFinalList() {
|
||||
if (!enabled) {
|
||||
return const [];
|
||||
}
|
||||
|
||||
final result = <String>[];
|
||||
final seen = <String>{_userFiltersToken};
|
||||
|
||||
result.add(_userFiltersToken);
|
||||
|
||||
for (final token in enabledStockListTokens) {
|
||||
if (seen.add(token)) {
|
||||
result.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
for (final token in autoEnabledStockListTokens) {
|
||||
if (seen.add(token)) {
|
||||
result.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
for (final entry in externalFilterLists) {
|
||||
final url = entry.url.trim();
|
||||
if (url.isEmpty) continue;
|
||||
final parsed = Uri.tryParse(url);
|
||||
if (parsed == null) continue;
|
||||
if (parsed.scheme != 'http' && parsed.scheme != 'https') continue;
|
||||
if (!parsed.hasAuthority) continue;
|
||||
if (seen.add(url)) {
|
||||
result.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isTokenEnabled(String token) =>
|
||||
enabledStockListTokens.contains(token) ||
|
||||
autoEnabledStockListTokens.contains(token);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
enabled,
|
||||
enabledStockListTokens,
|
||||
autoEnabledStockListTokens,
|
||||
autoSelectRegionalLists,
|
||||
externalFilterLists,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'ublock_filter_list_settings.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// CopyWithGenerator
|
||||
// **************************************************************************
|
||||
|
||||
abstract class _$UBlockExternalListCWProxy {
|
||||
UBlockExternalList url(String url);
|
||||
|
||||
UBlockExternalList description(String? description);
|
||||
|
||||
/// 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 `UBlockExternalList(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// UBlockExternalList(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
UBlockExternalList call({String url, String? description});
|
||||
}
|
||||
|
||||
/// Callable proxy for `copyWith` functionality.
|
||||
/// Use as `instanceOfUBlockExternalList.copyWith(...)` or call `instanceOfUBlockExternalList.copyWith.fieldName(value)` for a single field.
|
||||
class _$UBlockExternalListCWProxyImpl implements _$UBlockExternalListCWProxy {
|
||||
const _$UBlockExternalListCWProxyImpl(this._value);
|
||||
|
||||
final UBlockExternalList _value;
|
||||
|
||||
@override
|
||||
UBlockExternalList url(String url) => call(url: url);
|
||||
|
||||
@override
|
||||
UBlockExternalList description(String? description) =>
|
||||
call(description: description);
|
||||
|
||||
@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 `UBlockExternalList(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// UBlockExternalList(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
UBlockExternalList call({
|
||||
Object? url = const $CopyWithPlaceholder(),
|
||||
Object? description = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return UBlockExternalList(
|
||||
url: url == const $CopyWithPlaceholder() || url == null
|
||||
? _value.url
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: url as String,
|
||||
description: description == const $CopyWithPlaceholder()
|
||||
? _value.description
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: description as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension $UBlockExternalListCopyWith on UBlockExternalList {
|
||||
/// Returns a callable class used to build a new instance with modified fields.
|
||||
/// Example: `instanceOfUBlockExternalList.copyWith(...)` or `instanceOfUBlockExternalList.copyWith.fieldName(...)`.
|
||||
// ignore: library_private_types_in_public_api
|
||||
_$UBlockExternalListCWProxy get copyWith =>
|
||||
_$UBlockExternalListCWProxyImpl(this);
|
||||
}
|
||||
|
||||
abstract class _$UBlockFilterListSettingsCWProxy {
|
||||
UBlockFilterListSettings enabled(bool enabled);
|
||||
|
||||
UBlockFilterListSettings enabledStockListTokens(
|
||||
List<String> enabledStockListTokens,
|
||||
);
|
||||
|
||||
UBlockFilterListSettings autoEnabledStockListTokens(
|
||||
List<String> autoEnabledStockListTokens,
|
||||
);
|
||||
|
||||
UBlockFilterListSettings autoSelectRegionalLists(
|
||||
bool autoSelectRegionalLists,
|
||||
);
|
||||
|
||||
UBlockFilterListSettings externalFilterLists(
|
||||
List<UBlockExternalList> externalFilterLists,
|
||||
);
|
||||
|
||||
/// 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 `UBlockFilterListSettings(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// UBlockFilterListSettings(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
UBlockFilterListSettings call({
|
||||
bool enabled,
|
||||
List<String> enabledStockListTokens,
|
||||
List<String> autoEnabledStockListTokens,
|
||||
bool autoSelectRegionalLists,
|
||||
List<UBlockExternalList> externalFilterLists,
|
||||
});
|
||||
}
|
||||
|
||||
/// Callable proxy for `copyWith` functionality.
|
||||
/// Use as `instanceOfUBlockFilterListSettings.copyWith(...)` or call `instanceOfUBlockFilterListSettings.copyWith.fieldName(value)` for a single field.
|
||||
class _$UBlockFilterListSettingsCWProxyImpl
|
||||
implements _$UBlockFilterListSettingsCWProxy {
|
||||
const _$UBlockFilterListSettingsCWProxyImpl(this._value);
|
||||
|
||||
final UBlockFilterListSettings _value;
|
||||
|
||||
@override
|
||||
UBlockFilterListSettings enabled(bool enabled) => call(enabled: enabled);
|
||||
|
||||
@override
|
||||
UBlockFilterListSettings enabledStockListTokens(
|
||||
List<String> enabledStockListTokens,
|
||||
) => call(enabledStockListTokens: enabledStockListTokens);
|
||||
|
||||
@override
|
||||
UBlockFilterListSettings autoEnabledStockListTokens(
|
||||
List<String> autoEnabledStockListTokens,
|
||||
) => call(autoEnabledStockListTokens: autoEnabledStockListTokens);
|
||||
|
||||
@override
|
||||
UBlockFilterListSettings autoSelectRegionalLists(
|
||||
bool autoSelectRegionalLists,
|
||||
) => call(autoSelectRegionalLists: autoSelectRegionalLists);
|
||||
|
||||
@override
|
||||
UBlockFilterListSettings externalFilterLists(
|
||||
List<UBlockExternalList> externalFilterLists,
|
||||
) => call(externalFilterLists: externalFilterLists);
|
||||
|
||||
@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 `UBlockFilterListSettings(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// UBlockFilterListSettings(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
UBlockFilterListSettings call({
|
||||
Object? enabled = const $CopyWithPlaceholder(),
|
||||
Object? enabledStockListTokens = const $CopyWithPlaceholder(),
|
||||
Object? autoEnabledStockListTokens = const $CopyWithPlaceholder(),
|
||||
Object? autoSelectRegionalLists = const $CopyWithPlaceholder(),
|
||||
Object? externalFilterLists = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return UBlockFilterListSettings(
|
||||
enabled: enabled == const $CopyWithPlaceholder() || enabled == null
|
||||
? _value.enabled
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: enabled as bool,
|
||||
enabledStockListTokens:
|
||||
enabledStockListTokens == const $CopyWithPlaceholder() ||
|
||||
enabledStockListTokens == null
|
||||
? _value.enabledStockListTokens
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: enabledStockListTokens as List<String>,
|
||||
autoEnabledStockListTokens:
|
||||
autoEnabledStockListTokens == const $CopyWithPlaceholder() ||
|
||||
autoEnabledStockListTokens == null
|
||||
? _value.autoEnabledStockListTokens
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: autoEnabledStockListTokens as List<String>,
|
||||
autoSelectRegionalLists:
|
||||
autoSelectRegionalLists == const $CopyWithPlaceholder() ||
|
||||
autoSelectRegionalLists == null
|
||||
? _value.autoSelectRegionalLists
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: autoSelectRegionalLists as bool,
|
||||
externalFilterLists:
|
||||
externalFilterLists == const $CopyWithPlaceholder() ||
|
||||
externalFilterLists == null
|
||||
? _value.externalFilterLists
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: externalFilterLists as List<UBlockExternalList>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension $UBlockFilterListSettingsCopyWith on UBlockFilterListSettings {
|
||||
/// Returns a callable class used to build a new instance with modified fields.
|
||||
/// Example: `instanceOfUBlockFilterListSettings.copyWith(...)` or `instanceOfUBlockFilterListSettings.copyWith.fieldName(...)`.
|
||||
// ignore: library_private_types_in_public_api
|
||||
_$UBlockFilterListSettingsCWProxy get copyWith =>
|
||||
_$UBlockFilterListSettingsCWProxyImpl(this);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
UBlockExternalList _$UBlockExternalListFromJson(Map<String, dynamic> json) =>
|
||||
UBlockExternalList(
|
||||
url: json['url'] as String,
|
||||
description: json['description'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UBlockExternalListToJson(UBlockExternalList instance) =>
|
||||
<String, dynamic>{
|
||||
'url': instance.url,
|
||||
'description': ?instance.description,
|
||||
};
|
||||
|
||||
UBlockFilterListSettings _$UBlockFilterListSettingsFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => UBlockFilterListSettings(
|
||||
enabled: json['enabled'] as bool? ?? false,
|
||||
enabledStockListTokens:
|
||||
(json['enabledStockListTokens'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
autoEnabledStockListTokens:
|
||||
(json['autoEnabledStockListTokens'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
autoSelectRegionalLists: json['autoSelectRegionalLists'] as bool? ?? false,
|
||||
externalFilterLists:
|
||||
(json['externalFilterLists'] as List<dynamic>?)
|
||||
?.map((e) => UBlockExternalList.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UBlockFilterListSettingsToJson(
|
||||
UBlockFilterListSettings instance,
|
||||
) => <String, dynamic>{
|
||||
'enabled': instance.enabled,
|
||||
'enabledStockListTokens': instance.enabledStockListTokens,
|
||||
'autoEnabledStockListTokens': instance.autoEnabledStockListTokens,
|
||||
'autoSelectRegionalLists': instance.autoSelectRegionalLists,
|
||||
'externalFilterLists': instance.externalFilterLists
|
||||
.map((e) => e.toJson())
|
||||
.toList(),
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/features/user/data/models/ublock_asset.dart';
|
||||
|
||||
part 'ublock_assets.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Future<UBlockAssetsRegistry> ublockAssetsRegistry(Ref ref) async {
|
||||
final jsonStr = await rootBundle.loadString('assets/ublock/assets.json');
|
||||
|
||||
return UBlockAssetsRegistry.fromJson(
|
||||
jsonDecode(jsonStr) as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'ublock_assets.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(ublockAssetsRegistry)
|
||||
final ublockAssetsRegistryProvider = UblockAssetsRegistryProvider._();
|
||||
|
||||
final class UblockAssetsRegistryProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<UBlockAssetsRegistry>,
|
||||
UBlockAssetsRegistry,
|
||||
FutureOr<UBlockAssetsRegistry>
|
||||
>
|
||||
with
|
||||
$FutureModifier<UBlockAssetsRegistry>,
|
||||
$FutureProvider<UBlockAssetsRegistry> {
|
||||
UblockAssetsRegistryProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'ublockAssetsRegistryProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$ublockAssetsRegistryHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<UBlockAssetsRegistry> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<UBlockAssetsRegistry> create(Ref ref) {
|
||||
return ublockAssetsRegistry(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$ublockAssetsRegistryHash() =>
|
||||
r'512d2bf06d16661f5b49b4b4f02840aad5722cd3';
|
||||
@@ -100,6 +100,10 @@ class EngineSettingsRepository extends _$EngineSettingsRepository {
|
||||
DriftSqlType.string,
|
||||
db.typeMapping,
|
||||
),
|
||||
'ublockFilterListSettings': settings['ublockFilterListSettings']?.readAs(
|
||||
DriftSqlType.string,
|
||||
db.typeMapping,
|
||||
),
|
||||
'dohSettingsMode': settings['dohSettingsMode']?.readAs(
|
||||
DriftSqlType.string,
|
||||
db.typeMapping,
|
||||
|
||||
@@ -34,7 +34,7 @@ final class EngineSettingsRepositoryProvider
|
||||
}
|
||||
|
||||
String _$engineSettingsRepositoryHash() =>
|
||||
r'f3fe745e37ec89c2d403906250aea66321c3e505';
|
||||
r'03cfe93b6d4ac8cdb0b33627f4baa75240bff745';
|
||||
|
||||
abstract class _$EngineSettingsRepository
|
||||
extends $StreamNotifier<EngineSettings> {
|
||||
|
||||
Reference in New Issue
Block a user