remove deprecated content blocking

This commit is contained in:
Fabian Freund
2024-10-10 06:12:46 +02:00
parent 2c71eeb670
commit 10d6a3de1d
24 changed files with 0 additions and 1526 deletions
@@ -4,8 +4,6 @@ import 'package:exceptions/exceptions.dart';
import 'package:lensai/features/about/data/repositories/package_info_repository.dart'; import 'package:lensai/features/about/data/repositories/package_info_repository.dart';
import 'package:lensai/features/bangs/data/models/bang.dart'; import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/domain/repositories/sync.dart'; import 'package:lensai/features/bangs/domain/repositories/sync.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/content_block/domain/repositories/sync.dart';
import 'package:lensai/features/kagi/data/services/session.dart'; import 'package:lensai/features/kagi/data/services/session.dart';
import 'package:lensai/features/settings/data/models/settings.dart'; import 'package:lensai/features/settings/data/models/settings.dart';
import 'package:lensai/features/settings/data/repositories/settings_repository.dart'; import 'package:lensai/features/settings/data/repositories/settings_repository.dart';
@@ -48,25 +46,6 @@ class AppInitializationService extends _$AppInitializationService {
.syncBangGroups(syncInterval: const Duration(days: 7)); .syncBangGroups(syncInterval: const Duration(days: 7));
} }
FutureOr<Map<HostSource, Result<void>>> _initHosts(Settings settings) {
if (settings.enableContentBlocking) {
state = Result.success(
(
initialized: false,
stage: 'Synchronizing Ad & Content Blocking Lists...',
errors: List.empty(),
),
);
return ref.read(hostSyncRepositoryProvider.notifier).syncHostSources(
sources: settings.enableHostList,
syncInterval: const Duration(days: 7),
);
}
return {};
}
Future<void> _initIncognito(Settings settings) async { Future<void> _initIncognito(Settings settings) async {
state = Result.success( state = Result.success(
( (
@@ -97,11 +76,6 @@ class AppInitializationService extends _$AppInitializationService {
result.onFailure(errors.add); result.onFailure(errors.add);
} }
final hostSyncResults = await _initHosts(settings);
for (final MapEntry(value: result) in hostSyncResults.entries) {
result.onFailure(errors.add);
}
await _initIncognito(settings); await _initIncognito(settings);
return (initialized: true, stage: null, errors: errors); return (initialized: true, stage: null, errors: errors);
@@ -1,25 +0,0 @@
import 'package:drift/drift.dart';
import 'package:lensai/features/content_block/data/database/database.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
part 'host.g.dart';
@DriftAccessor()
class HostDao extends DatabaseAccessor<HostDatabase> with _$HostDaoMixin {
HostDao(super.db);
Selectable<HostData> getHostList({Iterable<HostSource>? sources}) {
final selectable = select(db.host);
if (sources != null) {
selectable.where((t) => t.source.isInValues(sources));
}
return selectable;
}
SingleSelectable<int> getHostCount({Iterable<HostSource>? sources}) {
return db.host.count(
where: (sources != null) ? (t) => t.source.isInValues(sources) : null,
);
}
}
@@ -1,6 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'host.dart';
// ignore_for_file: type=lint
mixin _$HostDaoMixin on DatabaseAccessor<HostDatabase> {}
@@ -1,66 +0,0 @@
import 'package:drift/drift.dart';
import 'package:lensai/features/content_block/data/database/database.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
part 'sync.g.dart';
@DriftAccessor()
class SyncDao extends DatabaseAccessor<HostDatabase> with _$SyncDaoMixin {
SyncDao(super.db);
SingleOrNullSelectable<DateTime?> lastSyncOfSource(HostSource source) {
final query = selectOnly(db.hostSync)
..addColumns([db.hostSync.lastSync])
..where(db.hostSync.source.equalsValue(source));
return query.map((row) => row.read(db.hostSync.lastSync));
}
Future<void> upsertLastSyncOfSource(HostSource source, DateTime lastSync) {
return db.hostSync.insertOne(
HostSyncCompanion.insert(
source: Value(source),
lastSync: lastSync,
),
onConflict: DoUpdate(
(old) => HostSyncCompanion.custom(lastSync: Variable(lastSync)),
),
);
}
Future<void> insertHosts(HostSource source, Iterable<String> hosts) {
return db.host.insertAll(
hosts.map((host) => HostCompanion.insert(hostname: host, source: source)),
// It is possible that we get conflicts, in case the host is added in
// multiple lists. In this case we just gonna igore it, and make sure,
// the unified list is inserted first.
mode: InsertMode.insertOrIgnore,
);
}
Future<int> deleteHosts(Iterable<String> hosts) {
return db.host.deleteWhere((t) => t.hostname.isIn(hosts));
}
Future<void> syncHosts({
required HostSource source,
required Set<String> remoteHosts,
required DateTime syncTime,
}) async {
final localHosts = await db.hostDao
.getHostList(sources: [source])
.get()
.then((hosts) => hosts.map((host) => host.hostname).toSet());
final removedHosts = localHosts.difference(remoteHosts);
final addedHosts = remoteHosts.difference(localHosts);
await db.transaction(
() async {
await deleteHosts(removedHosts);
await insertHosts(source, addedHosts);
await upsertLastSyncOfSource(source, syncTime);
},
);
}
}
@@ -1,6 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sync.dart';
// ignore_for_file: type=lint
mixin _$SyncDaoMixin on DatabaseAccessor<HostDatabase> {}
@@ -1,17 +0,0 @@
import 'package:drift/drift.dart';
import 'package:lensai/features/content_block/data/database/daos/host.dart';
import 'package:lensai/features/content_block/data/database/daos/sync.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
part 'database.g.dart';
@DriftDatabase(
include: {'database.drift'},
daos: [HostDao, SyncDao],
)
class HostDatabase extends _$HostDatabase {
@override
final int schemaVersion = 1;
HostDatabase(super.e);
}
@@ -1,11 +0,0 @@
import 'package:lensai/features/content_block/data/models/host.dart';
CREATE TABLE host (
hostname TEXT PRIMARY KEY NOT NULL,
source ENUM(HostSource) NOT NULL
);
CREATE TABLE host_sync (
source ENUM(HostSource) PRIMARY KEY NOT NULL,
last_sync DATETIME NOT NULL
);
@@ -1,544 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'database.dart';
// ignore_for_file: type=lint
class Host extends Table with TableInfo<Host, HostData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
Host(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<String> hostname = GeneratedColumn<String>(
'hostname', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'PRIMARY KEY NOT NULL');
late final GeneratedColumnWithTypeConverter<HostSource, int> source =
GeneratedColumn<int>('source', aliasedName, false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL')
.withConverter<HostSource>(Host.$convertersource);
@override
List<GeneratedColumn> get $columns => [hostname, source];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'host';
@override
Set<GeneratedColumn> get $primaryKey => {hostname};
@override
HostData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return HostData(
hostname: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}hostname'])!,
source: Host.$convertersource.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}source'])!),
);
}
@override
Host createAlias(String alias) {
return Host(attachedDatabase, alias);
}
static JsonTypeConverter2<HostSource, int, int> $convertersource =
const EnumIndexConverter<HostSource>(HostSource.values);
@override
bool get dontWriteConstraints => true;
}
class HostData extends DataClass implements Insertable<HostData> {
final String hostname;
final HostSource source;
const HostData({required this.hostname, required this.source});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['hostname'] = Variable<String>(hostname);
{
map['source'] = Variable<int>(Host.$convertersource.toSql(source));
}
return map;
}
factory HostData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return HostData(
hostname: serializer.fromJson<String>(json['hostname']),
source: Host.$convertersource
.fromJson(serializer.fromJson<int>(json['source'])),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'hostname': serializer.toJson<String>(hostname),
'source': serializer.toJson<int>(Host.$convertersource.toJson(source)),
};
}
HostData copyWith({String? hostname, HostSource? source}) => HostData(
hostname: hostname ?? this.hostname,
source: source ?? this.source,
);
HostData copyWithCompanion(HostCompanion data) {
return HostData(
hostname: data.hostname.present ? data.hostname.value : this.hostname,
source: data.source.present ? data.source.value : this.source,
);
}
@override
String toString() {
return (StringBuffer('HostData(')
..write('hostname: $hostname, ')
..write('source: $source')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(hostname, source);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is HostData &&
other.hostname == this.hostname &&
other.source == this.source);
}
class HostCompanion extends UpdateCompanion<HostData> {
final Value<String> hostname;
final Value<HostSource> source;
final Value<int> rowid;
const HostCompanion({
this.hostname = const Value.absent(),
this.source = const Value.absent(),
this.rowid = const Value.absent(),
});
HostCompanion.insert({
required String hostname,
required HostSource source,
this.rowid = const Value.absent(),
}) : hostname = Value(hostname),
source = Value(source);
static Insertable<HostData> custom({
Expression<String>? hostname,
Expression<int>? source,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (hostname != null) 'hostname': hostname,
if (source != null) 'source': source,
if (rowid != null) 'rowid': rowid,
});
}
HostCompanion copyWith(
{Value<String>? hostname, Value<HostSource>? source, Value<int>? rowid}) {
return HostCompanion(
hostname: hostname ?? this.hostname,
source: source ?? this.source,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (hostname.present) {
map['hostname'] = Variable<String>(hostname.value);
}
if (source.present) {
map['source'] = Variable<int>(Host.$convertersource.toSql(source.value));
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('HostCompanion(')
..write('hostname: $hostname, ')
..write('source: $source, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
class HostSync extends Table with TableInfo<HostSync, HostSyncData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
HostSync(this.attachedDatabase, [this._alias]);
late final GeneratedColumnWithTypeConverter<HostSource, int> source =
GeneratedColumn<int>('source', aliasedName, false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'PRIMARY KEY NOT NULL')
.withConverter<HostSource>(HostSync.$convertersource);
late final GeneratedColumn<DateTime> lastSync = GeneratedColumn<DateTime>(
'last_sync', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL');
@override
List<GeneratedColumn> get $columns => [source, lastSync];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'host_sync';
@override
Set<GeneratedColumn> get $primaryKey => {source};
@override
HostSyncData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return HostSyncData(
source: HostSync.$convertersource.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}source'])!),
lastSync: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}last_sync'])!,
);
}
@override
HostSync createAlias(String alias) {
return HostSync(attachedDatabase, alias);
}
static JsonTypeConverter2<HostSource, int, int> $convertersource =
const EnumIndexConverter<HostSource>(HostSource.values);
@override
bool get dontWriteConstraints => true;
}
class HostSyncData extends DataClass implements Insertable<HostSyncData> {
final HostSource source;
final DateTime lastSync;
const HostSyncData({required this.source, required this.lastSync});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
{
map['source'] = Variable<int>(HostSync.$convertersource.toSql(source));
}
map['last_sync'] = Variable<DateTime>(lastSync);
return map;
}
factory HostSyncData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return HostSyncData(
source: HostSync.$convertersource
.fromJson(serializer.fromJson<int>(json['source'])),
lastSync: serializer.fromJson<DateTime>(json['last_sync']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'source':
serializer.toJson<int>(HostSync.$convertersource.toJson(source)),
'last_sync': serializer.toJson<DateTime>(lastSync),
};
}
HostSyncData copyWith({HostSource? source, DateTime? lastSync}) =>
HostSyncData(
source: source ?? this.source,
lastSync: lastSync ?? this.lastSync,
);
HostSyncData copyWithCompanion(HostSyncCompanion data) {
return HostSyncData(
source: data.source.present ? data.source.value : this.source,
lastSync: data.lastSync.present ? data.lastSync.value : this.lastSync,
);
}
@override
String toString() {
return (StringBuffer('HostSyncData(')
..write('source: $source, ')
..write('lastSync: $lastSync')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(source, lastSync);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is HostSyncData &&
other.source == this.source &&
other.lastSync == this.lastSync);
}
class HostSyncCompanion extends UpdateCompanion<HostSyncData> {
final Value<HostSource> source;
final Value<DateTime> lastSync;
const HostSyncCompanion({
this.source = const Value.absent(),
this.lastSync = const Value.absent(),
});
HostSyncCompanion.insert({
this.source = const Value.absent(),
required DateTime lastSync,
}) : lastSync = Value(lastSync);
static Insertable<HostSyncData> custom({
Expression<int>? source,
Expression<DateTime>? lastSync,
}) {
return RawValuesInsertable({
if (source != null) 'source': source,
if (lastSync != null) 'last_sync': lastSync,
});
}
HostSyncCompanion copyWith(
{Value<HostSource>? source, Value<DateTime>? lastSync}) {
return HostSyncCompanion(
source: source ?? this.source,
lastSync: lastSync ?? this.lastSync,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (source.present) {
map['source'] =
Variable<int>(HostSync.$convertersource.toSql(source.value));
}
if (lastSync.present) {
map['last_sync'] = Variable<DateTime>(lastSync.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('HostSyncCompanion(')
..write('source: $source, ')
..write('lastSync: $lastSync')
..write(')'))
.toString();
}
}
abstract class _$HostDatabase extends GeneratedDatabase {
_$HostDatabase(QueryExecutor e) : super(e);
$HostDatabaseManager get managers => $HostDatabaseManager(this);
late final Host host = Host(this);
late final HostSync hostSync = HostSync(this);
late final HostDao hostDao = HostDao(this as HostDatabase);
late final SyncDao syncDao = SyncDao(this as HostDatabase);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [host, hostSync];
}
typedef $HostCreateCompanionBuilder = HostCompanion Function({
required String hostname,
required HostSource source,
Value<int> rowid,
});
typedef $HostUpdateCompanionBuilder = HostCompanion Function({
Value<String> hostname,
Value<HostSource> source,
Value<int> rowid,
});
class $HostFilterComposer extends FilterComposer<_$HostDatabase, Host> {
$HostFilterComposer(super.$state);
ColumnFilters<String> get hostname => $state.composableBuilder(
column: $state.table.hostname,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnWithTypeConverterFilters<HostSource, HostSource, int> get source =>
$state.composableBuilder(
column: $state.table.source,
builder: (column, joinBuilders) => ColumnWithTypeConverterFilters(
column,
joinBuilders: joinBuilders));
}
class $HostOrderingComposer extends OrderingComposer<_$HostDatabase, Host> {
$HostOrderingComposer(super.$state);
ColumnOrderings<String> get hostname => $state.composableBuilder(
column: $state.table.hostname,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<int> get source => $state.composableBuilder(
column: $state.table.source,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
}
class $HostTableManager extends RootTableManager<
_$HostDatabase,
Host,
HostData,
$HostFilterComposer,
$HostOrderingComposer,
$HostCreateCompanionBuilder,
$HostUpdateCompanionBuilder,
(HostData, BaseReferences<_$HostDatabase, Host, HostData>),
HostData,
PrefetchHooks Function()> {
$HostTableManager(_$HostDatabase db, Host table)
: super(TableManagerState(
db: db,
table: table,
filteringComposer: $HostFilterComposer(ComposerState(db, table)),
orderingComposer: $HostOrderingComposer(ComposerState(db, table)),
updateCompanionCallback: ({
Value<String> hostname = const Value.absent(),
Value<HostSource> source = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) =>
HostCompanion(
hostname: hostname,
source: source,
rowid: rowid,
),
createCompanionCallback: ({
required String hostname,
required HostSource source,
Value<int> rowid = const Value.absent(),
}) =>
HostCompanion.insert(
hostname: hostname,
source: source,
rowid: rowid,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
));
}
typedef $HostProcessedTableManager = ProcessedTableManager<
_$HostDatabase,
Host,
HostData,
$HostFilterComposer,
$HostOrderingComposer,
$HostCreateCompanionBuilder,
$HostUpdateCompanionBuilder,
(HostData, BaseReferences<_$HostDatabase, Host, HostData>),
HostData,
PrefetchHooks Function()>;
typedef $HostSyncCreateCompanionBuilder = HostSyncCompanion Function({
Value<HostSource> source,
required DateTime lastSync,
});
typedef $HostSyncUpdateCompanionBuilder = HostSyncCompanion Function({
Value<HostSource> source,
Value<DateTime> lastSync,
});
class $HostSyncFilterComposer extends FilterComposer<_$HostDatabase, HostSync> {
$HostSyncFilterComposer(super.$state);
ColumnWithTypeConverterFilters<HostSource, HostSource, int> get source =>
$state.composableBuilder(
column: $state.table.source,
builder: (column, joinBuilders) => ColumnWithTypeConverterFilters(
column,
joinBuilders: joinBuilders));
ColumnFilters<DateTime> get lastSync => $state.composableBuilder(
column: $state.table.lastSync,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
}
class $HostSyncOrderingComposer
extends OrderingComposer<_$HostDatabase, HostSync> {
$HostSyncOrderingComposer(super.$state);
ColumnOrderings<int> get source => $state.composableBuilder(
column: $state.table.source,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<DateTime> get lastSync => $state.composableBuilder(
column: $state.table.lastSync,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
}
class $HostSyncTableManager extends RootTableManager<
_$HostDatabase,
HostSync,
HostSyncData,
$HostSyncFilterComposer,
$HostSyncOrderingComposer,
$HostSyncCreateCompanionBuilder,
$HostSyncUpdateCompanionBuilder,
(HostSyncData, BaseReferences<_$HostDatabase, HostSync, HostSyncData>),
HostSyncData,
PrefetchHooks Function()> {
$HostSyncTableManager(_$HostDatabase db, HostSync table)
: super(TableManagerState(
db: db,
table: table,
filteringComposer: $HostSyncFilterComposer(ComposerState(db, table)),
orderingComposer: $HostSyncOrderingComposer(ComposerState(db, table)),
updateCompanionCallback: ({
Value<HostSource> source = const Value.absent(),
Value<DateTime> lastSync = const Value.absent(),
}) =>
HostSyncCompanion(
source: source,
lastSync: lastSync,
),
createCompanionCallback: ({
Value<HostSource> source = const Value.absent(),
required DateTime lastSync,
}) =>
HostSyncCompanion.insert(
source: source,
lastSync: lastSync,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
));
}
typedef $HostSyncProcessedTableManager = ProcessedTableManager<
_$HostDatabase,
HostSync,
HostSyncData,
$HostSyncFilterComposer,
$HostSyncOrderingComposer,
$HostSyncCreateCompanionBuilder,
$HostSyncUpdateCompanionBuilder,
(HostSyncData, BaseReferences<_$HostDatabase, HostSync, HostSyncData>),
HostSyncData,
PrefetchHooks Function()>;
class $HostDatabaseManager {
final _$HostDatabase _db;
$HostDatabaseManager(this._db);
$HostTableManager get host => $HostTableManager(_db, _db.host);
$HostSyncTableManager get hostSync =>
$HostSyncTableManager(_db, _db.hostSync);
}
@@ -1,21 +0,0 @@
enum HostSource {
stevenBlackUnified(
'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts',
),
stevenBlackFakeNews(
'https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-only/hosts',
),
stevenBlackSocial(
'https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/social-only/hosts',
),
stevenBlackGambling(
'https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/gambling-only/hosts',
),
stevenBlackPorn(
'https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/porn-only/hosts',
);
final String url;
const HostSource(this.url);
}
@@ -1,37 +0,0 @@
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:lensai/features/content_block/data/database/database.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sqlite3/sqlite3.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
import 'package:universal_io/io.dart';
part 'providers.g.dart';
@Riverpod(keepAlive: true)
HostDatabase hostDatabase(HostDatabaseRef ref) {
return HostDatabase(
LazyDatabase(() async {
// put the database file, called db.sqlite here, into the documents folder
// for your app.
final dbFolder = await path_provider.getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'host.db'));
// Also work around limitations on old Android versions
if (Platform.isAndroid) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
// Make sqlite3 pick a more suitable location for temporary files - the
// one from the system may be inaccessible due to sandboxing.
final cachebase = (await path_provider.getTemporaryDirectory()).path;
// We can't access /tmp on Android, which sqlite3 would try by default.
// Explicitly tell it about the correct temporary directory.
sqlite3.tempDirectory = cachebase;
return NativeDatabase.createInBackground(file);
}),
);
}
@@ -1,24 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'providers.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$hostDatabaseHash() => r'6fb4a1b259c9a049d8ab9b688ea5fb1cc227c117';
/// See also [hostDatabase].
@ProviderFor(hostDatabase)
final hostDatabaseProvider = Provider<HostDatabase>.internal(
hostDatabase,
name: r'hostDatabaseProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$hostDatabaseHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef HostDatabaseRef = ProviderRef<HostDatabase>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -1,40 +0,0 @@
import 'dart:convert';
import 'package:exceptions/exceptions.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:lensai/core/http_error_handler.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'source.g.dart';
@Riverpod()
class HostSourceService extends _$HostSourceService {
late http.Client _client;
@override
void build() {
_client = http.Client();
}
Future<Result<Set<String>>> getHosts(Uri url) async {
return Result.fromAsync(
() async {
final response = await _client.get(url);
return await compute(
(args) {
final hostRegex = RegExp(
r'^\s*([0-9a-fA-F:.]+)\s+(\S+)',
multiLine: true,
);
final matches = hostRegex.allMatches(utf8.decode(args[0]));
return matches.map((match) => match.group(2)).nonNulls.toSet();
},
[response.bodyBytes],
);
},
exceptionHandler: handleHttpError,
);
}
}
@@ -1,26 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'source.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$hostSourceServiceHash() => r'6c1ee7bfde02cc542a4f55db666d49037fda187e';
/// See also [HostSourceService].
@ProviderFor(HostSourceService)
final hostSourceServiceProvider =
AutoDisposeNotifierProvider<HostSourceService, void>.internal(
HostSourceService.new,
name: r'hostSourceServiceProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$hostSourceServiceHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$HostSourceService = AutoDisposeNotifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -1,24 +0,0 @@
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/content_block/domain/repositories/host.dart';
import 'package:lensai/features/content_block/domain/repositories/sync.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'providers.g.dart';
@Riverpod()
Stream<DateTime?> lastSyncOfSource(
LastSyncOfSourceRef ref,
HostSource source,
) {
final repository = ref.watch(hostSyncRepositoryProvider.notifier);
return repository.watchLastSyncOfSource(source);
}
@Riverpod()
Stream<int> hostCountOfSource(
HostCountOfSourceRef ref,
HostSource source,
) {
final repository = ref.watch(hostRepositoryProvider.notifier);
return repository.watchHostCount(source);
}
@@ -1,287 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'providers.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$lastSyncOfSourceHash() => r'0f96dc6c5ffc44654734bd4be187fd0c4d34a310';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [lastSyncOfSource].
@ProviderFor(lastSyncOfSource)
const lastSyncOfSourceProvider = LastSyncOfSourceFamily();
/// See also [lastSyncOfSource].
class LastSyncOfSourceFamily extends Family<AsyncValue<DateTime?>> {
/// See also [lastSyncOfSource].
const LastSyncOfSourceFamily();
/// See also [lastSyncOfSource].
LastSyncOfSourceProvider call(
HostSource source,
) {
return LastSyncOfSourceProvider(
source,
);
}
@override
LastSyncOfSourceProvider getProviderOverride(
covariant LastSyncOfSourceProvider provider,
) {
return call(
provider.source,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'lastSyncOfSourceProvider';
}
/// See also [lastSyncOfSource].
class LastSyncOfSourceProvider extends AutoDisposeStreamProvider<DateTime?> {
/// See also [lastSyncOfSource].
LastSyncOfSourceProvider(
HostSource source,
) : this._internal(
(ref) => lastSyncOfSource(
ref as LastSyncOfSourceRef,
source,
),
from: lastSyncOfSourceProvider,
name: r'lastSyncOfSourceProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$lastSyncOfSourceHash,
dependencies: LastSyncOfSourceFamily._dependencies,
allTransitiveDependencies:
LastSyncOfSourceFamily._allTransitiveDependencies,
source: source,
);
LastSyncOfSourceProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.source,
}) : super.internal();
final HostSource source;
@override
Override overrideWith(
Stream<DateTime?> Function(LastSyncOfSourceRef provider) create,
) {
return ProviderOverride(
origin: this,
override: LastSyncOfSourceProvider._internal(
(ref) => create(ref as LastSyncOfSourceRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
source: source,
),
);
}
@override
AutoDisposeStreamProviderElement<DateTime?> createElement() {
return _LastSyncOfSourceProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is LastSyncOfSourceProvider && other.source == source;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, source.hashCode);
return _SystemHash.finish(hash);
}
}
mixin LastSyncOfSourceRef on AutoDisposeStreamProviderRef<DateTime?> {
/// The parameter `source` of this provider.
HostSource get source;
}
class _LastSyncOfSourceProviderElement
extends AutoDisposeStreamProviderElement<DateTime?>
with LastSyncOfSourceRef {
_LastSyncOfSourceProviderElement(super.provider);
@override
HostSource get source => (origin as LastSyncOfSourceProvider).source;
}
String _$hostCountOfSourceHash() => r'3bd118037e5762fd5134838ac12ed6d0d2283e76';
/// See also [hostCountOfSource].
@ProviderFor(hostCountOfSource)
const hostCountOfSourceProvider = HostCountOfSourceFamily();
/// See also [hostCountOfSource].
class HostCountOfSourceFamily extends Family<AsyncValue<int>> {
/// See also [hostCountOfSource].
const HostCountOfSourceFamily();
/// See also [hostCountOfSource].
HostCountOfSourceProvider call(
HostSource source,
) {
return HostCountOfSourceProvider(
source,
);
}
@override
HostCountOfSourceProvider getProviderOverride(
covariant HostCountOfSourceProvider provider,
) {
return call(
provider.source,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'hostCountOfSourceProvider';
}
/// See also [hostCountOfSource].
class HostCountOfSourceProvider extends AutoDisposeStreamProvider<int> {
/// See also [hostCountOfSource].
HostCountOfSourceProvider(
HostSource source,
) : this._internal(
(ref) => hostCountOfSource(
ref as HostCountOfSourceRef,
source,
),
from: hostCountOfSourceProvider,
name: r'hostCountOfSourceProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$hostCountOfSourceHash,
dependencies: HostCountOfSourceFamily._dependencies,
allTransitiveDependencies:
HostCountOfSourceFamily._allTransitiveDependencies,
source: source,
);
HostCountOfSourceProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.source,
}) : super.internal();
final HostSource source;
@override
Override overrideWith(
Stream<int> Function(HostCountOfSourceRef provider) create,
) {
return ProviderOverride(
origin: this,
override: HostCountOfSourceProvider._internal(
(ref) => create(ref as HostCountOfSourceRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
source: source,
),
);
}
@override
AutoDisposeStreamProviderElement<int> createElement() {
return _HostCountOfSourceProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is HostCountOfSourceProvider && other.source == source;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, source.hashCode);
return _SystemHash.finish(hash);
}
}
mixin HostCountOfSourceRef on AutoDisposeStreamProviderRef<int> {
/// The parameter `source` of this provider.
HostSource get source;
}
class _HostCountOfSourceProviderElement
extends AutoDisposeStreamProviderElement<int> with HostCountOfSourceRef {
_HostCountOfSourceProviderElement(super.provider);
@override
HostSource get source => (origin as HostCountOfSourceProvider).source;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -1,29 +0,0 @@
import 'package:lensai/features/content_block/data/database/database.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/content_block/data/providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'host.g.dart';
@Riverpod(keepAlive: true)
class HostRepository extends _$HostRepository {
late HostDatabase _db;
@override
void build() {
_db = ref.watch(hostDatabaseProvider);
}
Stream<List<String>> watchHosts({Iterable<HostSource>? sources}) {
return _db.hostDao
.getHostList(sources: sources)
.map(
(hostData) => hostData.hostname,
)
.watch();
}
Stream<int> watchHostCount(HostSource source) {
return _db.hostDao.getHostCount(sources: [source]).watchSingle();
}
}
@@ -1,25 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'host.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$hostRepositoryHash() => r'b28362146b75391f55fb353fc6fde72dc1d2fcb9';
/// See also [HostRepository].
@ProviderFor(HostRepository)
final hostRepositoryProvider = NotifierProvider<HostRepository, void>.internal(
HostRepository.new,
name: r'hostRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$hostRepositoryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$HostRepository = Notifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -1,105 +0,0 @@
import 'package:drift/isolate.dart';
import 'package:exceptions/exceptions.dart';
import 'package:lensai/features/content_block/data/database/database.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/content_block/data/providers.dart';
import 'package:lensai/features/content_block/data/services/source.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'sync.g.dart';
@Riverpod(keepAlive: true)
class HostSyncRepository extends _$HostSyncRepository {
late HostDatabase _db;
HostSyncRepository();
static Future<Result<void>> _fetchAndSync({
required HostSourceService sourceService,
required HostDatabase db,
required Uri url,
required HostSource source,
required Duration? syncInterval,
}) async {
if (syncInterval != null) {
final lastSync =
await db.syncDao.lastSyncOfSource(source).getSingleOrNull();
if (lastSync != null &&
DateTime.now().difference(lastSync) < syncInterval) {
return Result.success(null);
}
}
final result = await sourceService.getHosts(url);
return result.flatMapAsync(
(remoteHosts) async {
await db.syncDao.syncHosts(
source: source,
remoteHosts: remoteHosts,
syncTime: DateTime.now(),
);
},
);
}
Future<Result<void>> syncHostSource(
HostSource source,
Duration? syncInterval,
) async {
try {
return Result.success(
await _db.computeWithDatabase(
connect: HostDatabase.new,
computation: (db) async {
final ref = ProviderContainer();
final result = await _fetchAndSync(
sourceService: ref.read(hostSourceServiceProvider.notifier),
db: db,
url: Uri.parse(source.url),
source: source,
syncInterval: syncInterval,
);
//Throw if necessary
return result.value;
},
),
);
} catch (e) {
return Result.failure(
ErrorMessage(
message: "Failed to sync Hosts (${source.name})",
source: 'HostSync',
details: e,
),
);
}
}
Stream<DateTime?> watchLastSyncOfSource(HostSource source) {
return _db.syncDao.lastSyncOfSource(source).watchSingleOrNull();
}
Future<Map<HostSource, Result<void>>> syncHostSources({
Set<HostSource>? sources,
Duration? syncInterval,
}) async {
//Default to all sources
sources ??= HostSource.values.toSet();
//Run isolated operations
final futures = sources.map(
(source) => syncHostSource(source, syncInterval)
.then((result) => MapEntry(source, result)),
);
return Map.fromEntries(await Future.wait(futures));
}
@override
void build() {
_db = ref.watch(hostDatabaseProvider);
}
}
@@ -1,27 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sync.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$hostSyncRepositoryHash() =>
r'5eed0c51e1b1e4dd1241634e297aff283eef01c5';
/// See also [HostSyncRepository].
@ProviderFor(HostSyncRepository)
final hostSyncRepositoryProvider =
NotifierProvider<HostSyncRepository, void>.internal(
HostSyncRepository.new,
name: r'hostSyncRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$hostSyncRepositoryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$HostSyncRepository = Notifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -1,7 +1,6 @@
import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:fast_equatable/fast_equatable.dart'; import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart'; import 'package:lensai/features/kagi/data/entities/modes.dart';
part 'settings.g.dart'; part 'settings.g.dart';
@@ -13,9 +12,7 @@ class Settings with FastEquatable {
final bool incognitoMode; final bool incognitoMode;
final bool enableJavascript; final bool enableJavascript;
final bool launchUrlExternal; final bool launchUrlExternal;
final bool enableContentBlocking;
final bool blockHttpProtocol; final bool blockHttpProtocol;
final Set<HostSource> enableHostList;
final ThemeMode themeMode; final ThemeMode themeMode;
final KagiTool? quickAction; final KagiTool? quickAction;
final bool quickActionVoiceInput; final bool quickActionVoiceInput;
@@ -27,9 +24,7 @@ class Settings with FastEquatable {
required this.incognitoMode, required this.incognitoMode,
required this.enableJavascript, required this.enableJavascript,
required this.launchUrlExternal, required this.launchUrlExternal,
required this.enableContentBlocking,
required this.blockHttpProtocol, required this.blockHttpProtocol,
required this.enableHostList,
required this.themeMode, required this.themeMode,
required this.quickAction, required this.quickAction,
required this.quickActionVoiceInput, required this.quickActionVoiceInput,
@@ -44,7 +39,6 @@ class Settings with FastEquatable {
bool? launchUrlExternal, bool? launchUrlExternal,
bool? enableContentBlocking, bool? enableContentBlocking,
bool? blockHttpProtocol, bool? blockHttpProtocol,
Set<HostSource>? enableHostList,
ThemeMode? themeMode, ThemeMode? themeMode,
this.quickAction, this.quickAction,
bool? quickActionVoiceInput, bool? quickActionVoiceInput,
@@ -53,9 +47,7 @@ class Settings with FastEquatable {
incognitoMode = incognitoMode ?? true, incognitoMode = incognitoMode ?? true,
enableJavascript = enableJavascript ?? true, enableJavascript = enableJavascript ?? true,
launchUrlExternal = launchUrlExternal ?? false, launchUrlExternal = launchUrlExternal ?? false,
enableContentBlocking = enableContentBlocking ?? true,
blockHttpProtocol = blockHttpProtocol ?? false, blockHttpProtocol = blockHttpProtocol ?? false,
enableHostList = enableHostList ?? {HostSource.stevenBlackUnified},
themeMode = themeMode ?? ThemeMode.dark, themeMode = themeMode ?? ThemeMode.dark,
quickActionVoiceInput = quickActionVoiceInput ?? false, quickActionVoiceInput = quickActionVoiceInput ?? false,
enableReadability = enableReadability ?? true; enableReadability = enableReadability ?? true;
@@ -70,9 +62,7 @@ class Settings with FastEquatable {
incognitoMode, incognitoMode,
enableJavascript, enableJavascript,
launchUrlExternal, launchUrlExternal,
enableContentBlocking,
blockHttpProtocol, blockHttpProtocol,
enableHostList,
themeMode, themeMode,
quickAction, quickAction,
quickActionVoiceInput, quickActionVoiceInput,
@@ -1,4 +1,3 @@
import 'package:collection/collection.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:lensai/features/settings/data/models/settings.dart'; import 'package:lensai/features/settings/data/models/settings.dart';
import 'package:lensai/features/settings/utils/preference_parser.dart'; import 'package:lensai/features/settings/utils/preference_parser.dart';
@@ -15,9 +14,7 @@ enum _StorageKeys {
incognito('enabe_incognito'), incognito('enabe_incognito'),
javascript('enable_js'), javascript('enable_js'),
launchExternal('enable_launch_external'), launchExternal('enable_launch_external'),
contentBlocking('enable_content_blocking'),
blockHttpProtocol('block_http'), blockHttpProtocol('block_http'),
enableHostList('enable_host_lists'),
themeMode('theme_mode'), themeMode('theme_mode'),
quickAction('enable_quick_action'), quickAction('enable_quick_action'),
quickActionVoiceInput('enable_quick_action_voice_input'), quickActionVoiceInput('enable_quick_action_voice_input'),
@@ -81,14 +78,6 @@ class SettingsRepository extends _$SettingsRepository {
); );
} }
if (newSettings.enableContentBlocking !=
oldSettings.enableContentBlocking) {
await sharedPreferences.setBool(
_StorageKeys.contentBlocking.key,
newSettings.enableContentBlocking,
);
}
if (newSettings.blockHttpProtocol != oldSettings.blockHttpProtocol) { if (newSettings.blockHttpProtocol != oldSettings.blockHttpProtocol) {
await sharedPreferences.setBool( await sharedPreferences.setBool(
_StorageKeys.blockHttpProtocol.key, _StorageKeys.blockHttpProtocol.key,
@@ -96,16 +85,6 @@ class SettingsRepository extends _$SettingsRepository {
); );
} }
if (!const DeepCollectionEquality.unordered().equals(
newSettings.enableHostList,
oldSettings.enableHostList,
)) {
await sharedPreferences.setStringList(
_StorageKeys.enableHostList.key,
newSettings.enableHostList.map((list) => list.name).toList(),
);
}
if (newSettings.themeMode != oldSettings.themeMode) { if (newSettings.themeMode != oldSettings.themeMode) {
await sharedPreferences.setInt( await sharedPreferences.setInt(
_StorageKeys.themeMode.key, _StorageKeys.themeMode.key,
@@ -157,13 +136,8 @@ class SettingsRepository extends _$SettingsRepository {
enableJavascript: sharedPreferences.getBool(_StorageKeys.javascript.key), enableJavascript: sharedPreferences.getBool(_StorageKeys.javascript.key),
launchUrlExternal: launchUrlExternal:
sharedPreferences.getBool(_StorageKeys.launchExternal.key), sharedPreferences.getBool(_StorageKeys.launchExternal.key),
enableContentBlocking:
sharedPreferences.getBool(_StorageKeys.contentBlocking.key),
blockHttpProtocol: blockHttpProtocol:
sharedPreferences.getBool(_StorageKeys.blockHttpProtocol.key), sharedPreferences.getBool(_StorageKeys.blockHttpProtocol.key),
enableHostList: parseHostSources(
sharedPreferences.getStringList(_StorageKeys.enableHostList.key),
),
themeMode: themeMode:
parseThemeMode(sharedPreferences.getInt(_StorageKeys.themeMode.key)), parseThemeMode(sharedPreferences.getInt(_StorageKeys.themeMode.key)),
quickAction: quickAction:
@@ -7,14 +7,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/bangs/data/models/bang.dart'; import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/domain/providers.dart'; import 'package:lensai/features/bangs/domain/providers.dart';
import 'package:lensai/features/bangs/domain/repositories/data.dart'; import 'package:lensai/features/bangs/domain/repositories/data.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart'; import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/settings/data/models/settings.dart'; import 'package:lensai/features/settings/data/models/settings.dart';
import 'package:lensai/features/settings/data/repositories/settings_repository.dart'; import 'package:lensai/features/settings/data/repositories/settings_repository.dart';
import 'package:lensai/features/settings/presentation/controllers/save_settings.dart'; import 'package:lensai/features/settings/presentation/controllers/save_settings.dart';
import 'package:lensai/features/settings/presentation/widgets/bang_group_list_tile.dart'; import 'package:lensai/features/settings/presentation/widgets/bang_group_list_tile.dart';
import 'package:lensai/features/settings/presentation/widgets/custom_list_tile.dart'; import 'package:lensai/features/settings/presentation/widgets/custom_list_tile.dart';
import 'package:lensai/features/settings/presentation/widgets/host_list_tile.dart';
import 'package:lensai/features/settings/utils/session_link_extractor.dart'; import 'package:lensai/features/settings/utils/session_link_extractor.dart';
import 'package:lensai/presentation/hooks/listenable_callback.dart'; import 'package:lensai/presentation/hooks/listenable_callback.dart';
import 'package:lensai/utils/ui_helper.dart' as ui_helper; import 'package:lensai/utils/ui_helper.dart' as ui_helper;
@@ -335,56 +333,7 @@ class SettingsScreen extends HookConsumerWidget {
: null, : null,
), ),
_buildSection(theme, 'Content Blocking'), _buildSection(theme, 'Content Blocking'),
SwitchListTile.adaptive(
title: const Text('Enable Content Blocking'),
subtitle: const Text(
'Prevents access to unwanted websites and ads, as defined in the selected lists below.',
),
value: settings.enableContentBlocking,
onChanged: (value) async {
await ref.read(saveSettingsControllerProvider.notifier).save(
(currentSettings) => currentSettings.copyWith
.enableContentBlocking(value),
);
},
),
_buildSubSection(theme, 'Lists'), _buildSubSection(theme, 'Lists'),
HostListTile(
enableContentBlocking: settings.enableContentBlocking,
enableHostLists: settings.enableHostList,
source: HostSource.stevenBlackUnified,
title: 'StevenBlack: Unified',
subtitle:
'Blocks domains containing adware, malware and trackers.',
),
HostListTile(
enableContentBlocking: settings.enableContentBlocking,
enableHostLists: settings.enableHostList,
source: HostSource.stevenBlackFakeNews,
title: 'StevenBlack: Fake News',
subtitle: 'Blocks domains known for spreading fake news.',
),
HostListTile(
enableContentBlocking: settings.enableContentBlocking,
enableHostLists: settings.enableHostList,
source: HostSource.stevenBlackGambling,
title: 'StevenBlack: Gambling',
subtitle: 'Blocks domains related to gambling.',
),
HostListTile(
enableContentBlocking: settings.enableContentBlocking,
enableHostLists: settings.enableHostList,
source: HostSource.stevenBlackPorn,
title: 'StevenBlack: Porn',
subtitle: 'Blocks adult content domains.',
),
HostListTile(
enableContentBlocking: settings.enableContentBlocking,
enableHostLists: settings.enableHostList,
source: HostSource.stevenBlackSocial,
title: 'StevenBlack: Social',
subtitle: 'Blocks social media domains.',
),
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
@@ -1,83 +0,0 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/content_block/domain/providers.dart';
import 'package:lensai/features/content_block/domain/repositories/sync.dart';
import 'package:lensai/features/settings/data/models/settings.dart';
import 'package:lensai/features/settings/presentation/controllers/save_settings.dart';
import 'package:lensai/features/settings/presentation/widgets/custom_list_tile.dart';
import 'package:lensai/features/settings/presentation/widgets/sync_details_table.dart';
class HostListTile extends HookConsumerWidget {
final bool enabled;
final bool enableContentBlocking;
final Set<HostSource> enableHostLists;
final HostSource source;
final String title;
final String subtitle;
const HostListTile({
required this.enableContentBlocking,
required this.enableHostLists,
required this.source,
required this.title,
required this.subtitle,
this.enabled = true,
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final enabled = enableHostLists.contains(source);
final lastSync = ref.watch(
lastSyncOfSourceProvider(source).select((value) => value.valueOrNull),
);
final count = ref.watch(
hostCountOfSourceProvider(source).select((value) => value.valueOrNull),
);
Future<void> toggleHostLists(HostSource source) async {
final lists = enabled
? ({...enableHostLists}..remove(source))
: {...enableHostLists, source};
await ref.read(saveSettingsControllerProvider.notifier).save(
(currentSettings) => currentSettings.copyWith.enableHostList(lists),
);
}
return CustomListTile(
enabled: enableContentBlocking,
title: title,
subtitle: subtitle,
prefix: Checkbox.adaptive(
value: enableHostLists.contains(source),
onChanged: enableContentBlocking
? (_) async {
await toggleHostLists(source);
}
: null,
),
suffix: FilledButton.icon(
onPressed: (enabled && enableContentBlocking)
? () async {
await ref
.read(hostSyncRepositoryProvider.notifier)
.syncHostSource(source, null);
}
: null,
icon: const Icon(Icons.sync),
label: const Text('Sync'),
),
content: (enabled && enableContentBlocking)
? Padding(
padding: const EdgeInsets.only(top: 8.0),
child: SyncDetailsTable(count, lastSync),
)
: null,
);
}
}
@@ -1,16 +1,6 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lensai/features/content_block/data/models/host.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart'; import 'package:lensai/features/kagi/data/entities/modes.dart';
Set<HostSource>? parseHostSources(List<String>? input) => input
?.map(
(list) =>
HostSource.values.firstWhereOrNull((source) => source.name == list),
)
.nonNulls
.toSet();
ThemeMode? parseThemeMode(int? index) { ThemeMode? parseThemeMode(int? index) {
if (index != null && index < ThemeMode.values.length) { if (index != null && index < ThemeMode.values.length) {
return ThemeMode.values[index]; return ThemeMode.values[index];