ublock list management initial

This commit is contained in:
Fabian Freund
2026-04-30 10:44:49 +02:00
parent 28dc4e93f1
commit d2bd36b1ed
43 changed files with 2894 additions and 65 deletions
@@ -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> {