top site feature backend implementation
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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:drift/drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/daos/seed_state.drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/definitions.drift.dart';
|
||||
|
||||
@DriftAccessor()
|
||||
class TopSiteSeedStateDao extends DatabaseAccessor<TopSiteDatabase>
|
||||
with $TopSiteSeedStateDaoMixin {
|
||||
TopSiteSeedStateDao(super.db);
|
||||
|
||||
Future<bool> hasSeed(String seedId) async {
|
||||
final row =
|
||||
await (db.topSiteSeedState.select()
|
||||
..where((t) => t.seedId.equals(seedId)))
|
||||
.getSingleOrNull();
|
||||
return row != null;
|
||||
}
|
||||
|
||||
Future<void> markSeedApplied(String seedId) {
|
||||
return db.topSiteSeedState.insertOne(
|
||||
TopSiteSeedStateCompanion.insert(
|
||||
seedId: seedId,
|
||||
appliedAt: DateTime.now(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.dart'
|
||||
as i1;
|
||||
|
||||
mixin $TopSiteSeedStateDaoMixin on i0.DatabaseAccessor<i1.TopSiteDatabase> {
|
||||
TopSiteSeedStateDaoManager get managers => TopSiteSeedStateDaoManager(this);
|
||||
}
|
||||
|
||||
class TopSiteSeedStateDaoManager {
|
||||
final $TopSiteSeedStateDaoMixin _db;
|
||||
TopSiteSeedStateDaoManager(this._db);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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:drift/drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/daos/top_site.drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/definitions.drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/entities/stored_top_site_source.dart';
|
||||
|
||||
@DriftAccessor()
|
||||
class TopSiteDao extends DatabaseAccessor<TopSiteDatabase>
|
||||
with $TopSiteDaoMixin {
|
||||
TopSiteDao(super.db);
|
||||
|
||||
Selectable<TopSiteData> selectPersistedTopSites() {
|
||||
return db.topSite.select()..orderBy([(t) => OrderingTerm.asc(t.orderKey)]);
|
||||
}
|
||||
|
||||
Future<List<TopSiteData>> getPersistedTopSites() {
|
||||
return selectPersistedTopSites().get();
|
||||
}
|
||||
|
||||
Future<TopSiteData?> getPersistedTopSiteById(String id) {
|
||||
return (db.topSite.select()..where((t) => t.id.equals(id)))
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<TopSiteData?> getPersistedTopSiteByUrl(Uri url) {
|
||||
return (db.topSite.select()..where((t) => t.url.equalsValue(url)))
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<int> countPersistedSites() async {
|
||||
final count = db.topSite.id.count();
|
||||
final query = db.selectOnly(db.topSite)..addColumns([count]);
|
||||
final result = await query.getSingle();
|
||||
return result.read(count)!;
|
||||
}
|
||||
|
||||
Future<int> insertPinnedSite({
|
||||
required String id,
|
||||
required String title,
|
||||
required Uri url,
|
||||
required String orderKey,
|
||||
}) {
|
||||
return db.topSite.insertOne(
|
||||
TopSiteCompanion.insert(
|
||||
id: id,
|
||||
title: title,
|
||||
url: url,
|
||||
source: StoredTopSiteSource.pinned,
|
||||
orderKey: orderKey,
|
||||
createdAt: DateTime.now(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> insertSeededSites(List<TopSiteCompanion> rows) {
|
||||
return db.batch((batch) {
|
||||
batch.insertAll(db.topSite, rows);
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> updatePersistedSite(
|
||||
String id, {
|
||||
required String title,
|
||||
required Uri url,
|
||||
}) {
|
||||
return (db.topSite.update()..where((t) => t.id.equals(id))).write(
|
||||
TopSiteCompanion(title: Value(title), url: Value(url)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> deletePersistedSite(String id) {
|
||||
return (db.topSite.delete()..where((t) => t.id.equals(id))).go();
|
||||
}
|
||||
|
||||
Future<void> assignOrderKey(String id, {required String orderKey}) {
|
||||
return (db.topSite.update()..where((t) => t.id.equals(id))).write(
|
||||
TopSiteCompanion(orderKey: Value(orderKey)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> promoteToSource(
|
||||
String id, {
|
||||
required StoredTopSiteSource source,
|
||||
required String title,
|
||||
required String orderKey,
|
||||
}) {
|
||||
return (db.topSite.update()..where((t) => t.id.equals(id))).write(
|
||||
TopSiteCompanion(
|
||||
source: Value(source),
|
||||
title: Value(title),
|
||||
orderKey: Value(orderKey),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SingleSelectable<String> generateLeadingOrderKey({int bucket = 0}) {
|
||||
return db.definitionsDrift.leadingOrderKey(bucket: bucket);
|
||||
}
|
||||
|
||||
SingleSelectable<String> generateTrailingOrderKey({int bucket = 0}) {
|
||||
return db.definitionsDrift.trailingOrderKey(bucket: bucket);
|
||||
}
|
||||
|
||||
SingleOrNullSelectable<String> generateOrderKeyAfterSiteId(String id) {
|
||||
return db.definitionsDrift.orderKeyAfterSite(siteId: id);
|
||||
}
|
||||
|
||||
SingleSelectable<String> generateOrderKeyBeforeSiteId(String id) {
|
||||
return db.definitionsDrift.orderKeyBeforeSite(siteId: id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.dart'
|
||||
as i1;
|
||||
|
||||
mixin $TopSiteDaoMixin on i0.DatabaseAccessor<i1.TopSiteDatabase> {
|
||||
TopSiteDaoManager get managers => TopSiteDaoManager(this);
|
||||
}
|
||||
|
||||
class TopSiteDaoManager {
|
||||
final $TopSiteDaoMixin _db;
|
||||
TopSiteDaoManager(this._db);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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:drift/drift.dart';
|
||||
import 'package:drift_dev/api/migrations_native.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/daos/seed_state.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/daos/top_site.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.drift.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
include: {'definitions.drift'},
|
||||
daos: [TopSiteDao, TopSiteSeedStateDao],
|
||||
)
|
||||
class TopSiteDatabase extends $TopSiteDatabase {
|
||||
@override
|
||||
final int schemaVersion = 1;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
beforeOpen: (details) async {
|
||||
if (kDebugMode) {
|
||||
await validateDatabaseSchema();
|
||||
}
|
||||
|
||||
await customStatement('PRAGMA foreign_keys = ON;');
|
||||
},
|
||||
);
|
||||
|
||||
TopSiteDatabase(super.e);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/definitions.drift.dart'
|
||||
as i1;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/daos/top_site.dart'
|
||||
as i2;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.dart'
|
||||
as i3;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/daos/seed_state.dart'
|
||||
as i4;
|
||||
import 'package:drift/internal/modular.dart' as i5;
|
||||
import 'package:sqlite3/common.dart' as i6;
|
||||
|
||||
abstract class $TopSiteDatabase extends i0.GeneratedDatabase {
|
||||
$TopSiteDatabase(i0.QueryExecutor e) : super(e);
|
||||
$TopSiteDatabaseManager get managers => $TopSiteDatabaseManager(this);
|
||||
late final i1.TopSite topSite = i1.TopSite(this);
|
||||
late final i1.TopSiteSeedState topSiteSeedState = i1.TopSiteSeedState(this);
|
||||
late final i2.TopSiteDao topSiteDao = i2.TopSiteDao(
|
||||
this as i3.TopSiteDatabase,
|
||||
);
|
||||
late final i4.TopSiteSeedStateDao topSiteSeedStateDao =
|
||||
i4.TopSiteSeedStateDao(this as i3.TopSiteDatabase);
|
||||
i1.DefinitionsDrift get definitionsDrift => i5.ReadDatabaseContainer(
|
||||
this,
|
||||
).accessor<i1.DefinitionsDrift>(i1.DefinitionsDrift.new);
|
||||
@override
|
||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||
@override
|
||||
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [
|
||||
topSite,
|
||||
i1.idxTopSiteOrderKey,
|
||||
topSiteSeedState,
|
||||
];
|
||||
}
|
||||
|
||||
class $TopSiteDatabaseManager {
|
||||
final $TopSiteDatabase _db;
|
||||
$TopSiteDatabaseManager(this._db);
|
||||
i1.$TopSiteTableManager get topSite =>
|
||||
i1.$TopSiteTableManager(_db, _db.topSite);
|
||||
i1.$TopSiteSeedStateTableManager get topSiteSeedState =>
|
||||
i1.$TopSiteSeedStateTableManager(_db, _db.topSiteSeedState);
|
||||
}
|
||||
|
||||
extension DefineFunctions on i6.CommonDatabase {
|
||||
void defineFunctions({
|
||||
required String Function(int, String?) lexoRankNext,
|
||||
required String Function(int, String?) lexoRankPrevious,
|
||||
required String Function(String?, String?) lexoRankReorderAfter,
|
||||
required String Function(String?, String?) lexoRankReorderBefore,
|
||||
}) {
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_next',
|
||||
argumentCount: const i6.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as int;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankNext(arg0, arg1);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_previous',
|
||||
argumentCount: const i6.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as int;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankPrevious(arg0, arg1);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_reorder_after',
|
||||
argumentCount: const i6.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankReorderAfter(arg0, arg1);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_reorder_before',
|
||||
argumentCount: const i6.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankReorderBefore(arg0, arg1);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:weblibre/data/database/converters/uri.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/entities/stored_top_site_source.dart';
|
||||
|
||||
CREATE TABLE top_site (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
url TEXT NOT NULL MAPPED BY `const UriConverter()`,
|
||||
source ENUM(StoredTopSiteSource) NOT NULL,
|
||||
order_key TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
UNIQUE(url)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_top_site_order_key ON top_site(order_key);
|
||||
|
||||
CREATE TABLE top_site_seed_state (
|
||||
seed_id TEXT PRIMARY KEY NOT NULL,
|
||||
applied_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
leadingOrderKey(:bucket AS INTEGER):
|
||||
SELECT lexo_rank_previous(
|
||||
:bucket,
|
||||
(
|
||||
SELECT order_key
|
||||
FROM top_site
|
||||
ORDER BY order_key
|
||||
LIMIT 1
|
||||
)
|
||||
);
|
||||
|
||||
trailingOrderKey(:bucket AS INTEGER):
|
||||
SELECT lexo_rank_next(
|
||||
:bucket,
|
||||
(
|
||||
SELECT order_key
|
||||
FROM top_site
|
||||
ORDER BY order_key DESC
|
||||
LIMIT 1
|
||||
)
|
||||
);
|
||||
|
||||
orderKeyAfterSite(:site_id AS TEXT):
|
||||
WITH ordered_table AS (
|
||||
SELECT
|
||||
id,
|
||||
order_key,
|
||||
LEAD(order_key) OVER (ORDER BY order_key) AS next_order_key
|
||||
FROM top_site
|
||||
)
|
||||
SELECT lexo_rank_reorder_after(order_key, next_order_key)
|
||||
FROM ordered_table
|
||||
WHERE id = :site_id;
|
||||
|
||||
orderKeyBeforeSite(:site_id AS TEXT):
|
||||
WITH ordered_table AS (
|
||||
SELECT
|
||||
id,
|
||||
order_key,
|
||||
LAG(order_key) OVER (ORDER BY order_key) AS prev_order_key
|
||||
FROM top_site
|
||||
)
|
||||
SELECT lexo_rank_reorder_before(order_key, prev_order_key)
|
||||
FROM ordered_table
|
||||
WHERE id = :site_id;
|
||||
@@ -0,0 +1,983 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/definitions.drift.dart'
|
||||
as i1;
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/entities/stored_top_site_source.dart'
|
||||
as i2;
|
||||
import 'package:weblibre/data/database/converters/uri.dart' as i3;
|
||||
import 'package:drift/internal/modular.dart' as i4;
|
||||
|
||||
typedef $TopSiteCreateCompanionBuilder =
|
||||
i1.TopSiteCompanion Function({
|
||||
required String id,
|
||||
required String title,
|
||||
required Uri url,
|
||||
required i2.StoredTopSiteSource source,
|
||||
required String orderKey,
|
||||
required DateTime createdAt,
|
||||
i0.Value<int> rowid,
|
||||
});
|
||||
typedef $TopSiteUpdateCompanionBuilder =
|
||||
i1.TopSiteCompanion Function({
|
||||
i0.Value<String> id,
|
||||
i0.Value<String> title,
|
||||
i0.Value<Uri> url,
|
||||
i0.Value<i2.StoredTopSiteSource> source,
|
||||
i0.Value<String> orderKey,
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<int> rowid,
|
||||
});
|
||||
|
||||
class $TopSiteFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.TopSite> {
|
||||
$TopSiteFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<String> get title => $composableBuilder(
|
||||
column: $table.title,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnWithTypeConverterFilters<Uri, Uri, String> get url =>
|
||||
$composableBuilder(
|
||||
column: $table.url,
|
||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnWithTypeConverterFilters<
|
||||
i2.StoredTopSiteSource,
|
||||
i2.StoredTopSiteSource,
|
||||
int
|
||||
>
|
||||
get source => $composableBuilder(
|
||||
column: $table.source,
|
||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<String> get orderKey => $composableBuilder(
|
||||
column: $table.orderKey,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $TopSiteOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.TopSite> {
|
||||
$TopSiteOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<String> get title => $composableBuilder(
|
||||
column: $table.title,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<String> get url => $composableBuilder(
|
||||
column: $table.url,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<int> get source => $composableBuilder(
|
||||
column: $table.source,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<String> get orderKey => $composableBuilder(
|
||||
column: $table.orderKey,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $TopSiteAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.TopSite> {
|
||||
$TopSiteAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get title =>
|
||||
$composableBuilder(column: $table.title, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumnWithTypeConverter<Uri, String> get url =>
|
||||
$composableBuilder(column: $table.url, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumnWithTypeConverter<i2.StoredTopSiteSource, int> get source =>
|
||||
$composableBuilder(column: $table.source, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get orderKey =>
|
||||
$composableBuilder(column: $table.orderKey, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get createdAt =>
|
||||
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $TopSiteTableManager
|
||||
extends
|
||||
i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.TopSite,
|
||||
i1.TopSiteData,
|
||||
i1.$TopSiteFilterComposer,
|
||||
i1.$TopSiteOrderingComposer,
|
||||
i1.$TopSiteAnnotationComposer,
|
||||
$TopSiteCreateCompanionBuilder,
|
||||
$TopSiteUpdateCompanionBuilder,
|
||||
(
|
||||
i1.TopSiteData,
|
||||
i0.BaseReferences<i0.GeneratedDatabase, i1.TopSite, i1.TopSiteData>,
|
||||
),
|
||||
i1.TopSiteData,
|
||||
i0.PrefetchHooks Function()
|
||||
> {
|
||||
$TopSiteTableManager(i0.GeneratedDatabase db, i1.TopSite table)
|
||||
: super(
|
||||
i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$TopSiteFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
i1.$TopSiteOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$TopSiteAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
i0.Value<String> id = const i0.Value.absent(),
|
||||
i0.Value<String> title = const i0.Value.absent(),
|
||||
i0.Value<Uri> url = const i0.Value.absent(),
|
||||
i0.Value<i2.StoredTopSiteSource> source =
|
||||
const i0.Value.absent(),
|
||||
i0.Value<String> orderKey = const i0.Value.absent(),
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<int> rowid = const i0.Value.absent(),
|
||||
}) => i1.TopSiteCompanion(
|
||||
id: id,
|
||||
title: title,
|
||||
url: url,
|
||||
source: source,
|
||||
orderKey: orderKey,
|
||||
createdAt: createdAt,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback:
|
||||
({
|
||||
required String id,
|
||||
required String title,
|
||||
required Uri url,
|
||||
required i2.StoredTopSiteSource source,
|
||||
required String orderKey,
|
||||
required DateTime createdAt,
|
||||
i0.Value<int> rowid = const i0.Value.absent(),
|
||||
}) => i1.TopSiteCompanion.insert(
|
||||
id: id,
|
||||
title: title,
|
||||
url: url,
|
||||
source: source,
|
||||
orderKey: orderKey,
|
||||
createdAt: createdAt,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $TopSiteProcessedTableManager =
|
||||
i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.TopSite,
|
||||
i1.TopSiteData,
|
||||
i1.$TopSiteFilterComposer,
|
||||
i1.$TopSiteOrderingComposer,
|
||||
i1.$TopSiteAnnotationComposer,
|
||||
$TopSiteCreateCompanionBuilder,
|
||||
$TopSiteUpdateCompanionBuilder,
|
||||
(
|
||||
i1.TopSiteData,
|
||||
i0.BaseReferences<i0.GeneratedDatabase, i1.TopSite, i1.TopSiteData>,
|
||||
),
|
||||
i1.TopSiteData,
|
||||
i0.PrefetchHooks Function()
|
||||
>;
|
||||
typedef $TopSiteSeedStateCreateCompanionBuilder =
|
||||
i1.TopSiteSeedStateCompanion Function({
|
||||
required String seedId,
|
||||
required DateTime appliedAt,
|
||||
i0.Value<int> rowid,
|
||||
});
|
||||
typedef $TopSiteSeedStateUpdateCompanionBuilder =
|
||||
i1.TopSiteSeedStateCompanion Function({
|
||||
i0.Value<String> seedId,
|
||||
i0.Value<DateTime> appliedAt,
|
||||
i0.Value<int> rowid,
|
||||
});
|
||||
|
||||
class $TopSiteSeedStateFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.TopSiteSeedState> {
|
||||
$TopSiteSeedStateFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<String> get seedId => $composableBuilder(
|
||||
column: $table.seedId,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<DateTime> get appliedAt => $composableBuilder(
|
||||
column: $table.appliedAt,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $TopSiteSeedStateOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.TopSiteSeedState> {
|
||||
$TopSiteSeedStateOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<String> get seedId => $composableBuilder(
|
||||
column: $table.seedId,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<DateTime> get appliedAt => $composableBuilder(
|
||||
column: $table.appliedAt,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $TopSiteSeedStateAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.TopSiteSeedState> {
|
||||
$TopSiteSeedStateAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<String> get seedId =>
|
||||
$composableBuilder(column: $table.seedId, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get appliedAt =>
|
||||
$composableBuilder(column: $table.appliedAt, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $TopSiteSeedStateTableManager
|
||||
extends
|
||||
i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.TopSiteSeedState,
|
||||
i1.TopSiteSeedStateData,
|
||||
i1.$TopSiteSeedStateFilterComposer,
|
||||
i1.$TopSiteSeedStateOrderingComposer,
|
||||
i1.$TopSiteSeedStateAnnotationComposer,
|
||||
$TopSiteSeedStateCreateCompanionBuilder,
|
||||
$TopSiteSeedStateUpdateCompanionBuilder,
|
||||
(
|
||||
i1.TopSiteSeedStateData,
|
||||
i0.BaseReferences<
|
||||
i0.GeneratedDatabase,
|
||||
i1.TopSiteSeedState,
|
||||
i1.TopSiteSeedStateData
|
||||
>,
|
||||
),
|
||||
i1.TopSiteSeedStateData,
|
||||
i0.PrefetchHooks Function()
|
||||
> {
|
||||
$TopSiteSeedStateTableManager(
|
||||
i0.GeneratedDatabase db,
|
||||
i1.TopSiteSeedState table,
|
||||
) : super(
|
||||
i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$TopSiteSeedStateFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
i1.$TopSiteSeedStateOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$TopSiteSeedStateAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
i0.Value<String> seedId = const i0.Value.absent(),
|
||||
i0.Value<DateTime> appliedAt = const i0.Value.absent(),
|
||||
i0.Value<int> rowid = const i0.Value.absent(),
|
||||
}) => i1.TopSiteSeedStateCompanion(
|
||||
seedId: seedId,
|
||||
appliedAt: appliedAt,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback:
|
||||
({
|
||||
required String seedId,
|
||||
required DateTime appliedAt,
|
||||
i0.Value<int> rowid = const i0.Value.absent(),
|
||||
}) => i1.TopSiteSeedStateCompanion.insert(
|
||||
seedId: seedId,
|
||||
appliedAt: appliedAt,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $TopSiteSeedStateProcessedTableManager =
|
||||
i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.TopSiteSeedState,
|
||||
i1.TopSiteSeedStateData,
|
||||
i1.$TopSiteSeedStateFilterComposer,
|
||||
i1.$TopSiteSeedStateOrderingComposer,
|
||||
i1.$TopSiteSeedStateAnnotationComposer,
|
||||
$TopSiteSeedStateCreateCompanionBuilder,
|
||||
$TopSiteSeedStateUpdateCompanionBuilder,
|
||||
(
|
||||
i1.TopSiteSeedStateData,
|
||||
i0.BaseReferences<
|
||||
i0.GeneratedDatabase,
|
||||
i1.TopSiteSeedState,
|
||||
i1.TopSiteSeedStateData
|
||||
>,
|
||||
),
|
||||
i1.TopSiteSeedStateData,
|
||||
i0.PrefetchHooks Function()
|
||||
>;
|
||||
|
||||
class TopSite extends i0.Table with i0.TableInfo<TopSite, i1.TopSiteData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
TopSite(this.attachedDatabase, [this._alias]);
|
||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'PRIMARY KEY NOT NULL',
|
||||
);
|
||||
late final i0.GeneratedColumn<String> title = i0.GeneratedColumn<String>(
|
||||
'title',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
late final i0.GeneratedColumnWithTypeConverter<Uri, String> url =
|
||||
i0.GeneratedColumn<String>(
|
||||
'url',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
).withConverter<Uri>(i1.TopSite.$converterurl);
|
||||
late final i0.GeneratedColumnWithTypeConverter<i2.StoredTopSiteSource, int>
|
||||
source = i0.GeneratedColumn<int>(
|
||||
'source',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
).withConverter<i2.StoredTopSiteSource>(i1.TopSite.$convertersource);
|
||||
late final i0.GeneratedColumn<String> orderKey = i0.GeneratedColumn<String>(
|
||||
'order_key',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
late final i0.GeneratedColumn<DateTime> createdAt =
|
||||
i0.GeneratedColumn<DateTime>(
|
||||
'created_at',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [
|
||||
id,
|
||||
title,
|
||||
url,
|
||||
source,
|
||||
orderKey,
|
||||
createdAt,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'top_site';
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
List<Set<i0.GeneratedColumn>> get uniqueKeys => [
|
||||
{url},
|
||||
];
|
||||
@override
|
||||
i1.TopSiteData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.TopSiteData(
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
title: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}title'],
|
||||
)!,
|
||||
url: i1.TopSite.$converterurl.fromSql(
|
||||
attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}url'],
|
||||
)!,
|
||||
),
|
||||
source: i1.TopSite.$convertersource.fromSql(
|
||||
attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.int,
|
||||
data['${effectivePrefix}source'],
|
||||
)!,
|
||||
),
|
||||
orderKey: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}order_key'],
|
||||
)!,
|
||||
createdAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}created_at'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TopSite createAlias(String alias) {
|
||||
return TopSite(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
static i0.TypeConverter<Uri, String> $converterurl = const i3.UriConverter();
|
||||
static i0.JsonTypeConverter2<i2.StoredTopSiteSource, int, int>
|
||||
$convertersource = const i0.EnumIndexConverter<i2.StoredTopSiteSource>(
|
||||
i2.StoredTopSiteSource.values,
|
||||
);
|
||||
@override
|
||||
List<String> get customConstraints => const ['UNIQUE(url)'];
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class TopSiteData extends i0.DataClass
|
||||
implements i0.Insertable<i1.TopSiteData> {
|
||||
final String id;
|
||||
final String title;
|
||||
final Uri url;
|
||||
final i2.StoredTopSiteSource source;
|
||||
final String orderKey;
|
||||
final DateTime createdAt;
|
||||
const TopSiteData({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.url,
|
||||
required this.source,
|
||||
required this.orderKey,
|
||||
required this.createdAt,
|
||||
});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['id'] = i0.Variable<String>(id);
|
||||
map['title'] = i0.Variable<String>(title);
|
||||
{
|
||||
map['url'] = i0.Variable<String>(i1.TopSite.$converterurl.toSql(url));
|
||||
}
|
||||
{
|
||||
map['source'] = i0.Variable<int>(
|
||||
i1.TopSite.$convertersource.toSql(source),
|
||||
);
|
||||
}
|
||||
map['order_key'] = i0.Variable<String>(orderKey);
|
||||
map['created_at'] = i0.Variable<DateTime>(createdAt);
|
||||
return map;
|
||||
}
|
||||
|
||||
factory TopSiteData.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
i0.ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return TopSiteData(
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
title: serializer.fromJson<String>(json['title']),
|
||||
url: serializer.fromJson<Uri>(json['url']),
|
||||
source: i1.TopSite.$convertersource.fromJson(
|
||||
serializer.fromJson<int>(json['source']),
|
||||
),
|
||||
orderKey: serializer.fromJson<String>(json['order_key']),
|
||||
createdAt: serializer.fromJson<DateTime>(json['created_at']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<String>(id),
|
||||
'title': serializer.toJson<String>(title),
|
||||
'url': serializer.toJson<Uri>(url),
|
||||
'source': serializer.toJson<int>(
|
||||
i1.TopSite.$convertersource.toJson(source),
|
||||
),
|
||||
'order_key': serializer.toJson<String>(orderKey),
|
||||
'created_at': serializer.toJson<DateTime>(createdAt),
|
||||
};
|
||||
}
|
||||
|
||||
i1.TopSiteData copyWith({
|
||||
String? id,
|
||||
String? title,
|
||||
Uri? url,
|
||||
i2.StoredTopSiteSource? source,
|
||||
String? orderKey,
|
||||
DateTime? createdAt,
|
||||
}) => i1.TopSiteData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
url: url ?? this.url,
|
||||
source: source ?? this.source,
|
||||
orderKey: orderKey ?? this.orderKey,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
);
|
||||
TopSiteData copyWithCompanion(i1.TopSiteCompanion data) {
|
||||
return TopSiteData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
title: data.title.present ? data.title.value : this.title,
|
||||
url: data.url.present ? data.url.value : this.url,
|
||||
source: data.source.present ? data.source.value : this.source,
|
||||
orderKey: data.orderKey.present ? data.orderKey.value : this.orderKey,
|
||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('TopSiteData(')
|
||||
..write('id: $id, ')
|
||||
..write('title: $title, ')
|
||||
..write('url: $url, ')
|
||||
..write('source: $source, ')
|
||||
..write('orderKey: $orderKey, ')
|
||||
..write('createdAt: $createdAt')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, title, url, source, orderKey, createdAt);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.TopSiteData &&
|
||||
other.id == this.id &&
|
||||
other.title == this.title &&
|
||||
other.url == this.url &&
|
||||
other.source == this.source &&
|
||||
other.orderKey == this.orderKey &&
|
||||
other.createdAt == this.createdAt);
|
||||
}
|
||||
|
||||
class TopSiteCompanion extends i0.UpdateCompanion<i1.TopSiteData> {
|
||||
final i0.Value<String> id;
|
||||
final i0.Value<String> title;
|
||||
final i0.Value<Uri> url;
|
||||
final i0.Value<i2.StoredTopSiteSource> source;
|
||||
final i0.Value<String> orderKey;
|
||||
final i0.Value<DateTime> createdAt;
|
||||
final i0.Value<int> rowid;
|
||||
const TopSiteCompanion({
|
||||
this.id = const i0.Value.absent(),
|
||||
this.title = const i0.Value.absent(),
|
||||
this.url = const i0.Value.absent(),
|
||||
this.source = const i0.Value.absent(),
|
||||
this.orderKey = const i0.Value.absent(),
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.rowid = const i0.Value.absent(),
|
||||
});
|
||||
TopSiteCompanion.insert({
|
||||
required String id,
|
||||
required String title,
|
||||
required Uri url,
|
||||
required i2.StoredTopSiteSource source,
|
||||
required String orderKey,
|
||||
required DateTime createdAt,
|
||||
this.rowid = const i0.Value.absent(),
|
||||
}) : id = i0.Value(id),
|
||||
title = i0.Value(title),
|
||||
url = i0.Value(url),
|
||||
source = i0.Value(source),
|
||||
orderKey = i0.Value(orderKey),
|
||||
createdAt = i0.Value(createdAt);
|
||||
static i0.Insertable<i1.TopSiteData> custom({
|
||||
i0.Expression<String>? id,
|
||||
i0.Expression<String>? title,
|
||||
i0.Expression<String>? url,
|
||||
i0.Expression<int>? source,
|
||||
i0.Expression<String>? orderKey,
|
||||
i0.Expression<DateTime>? createdAt,
|
||||
i0.Expression<int>? rowid,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (title != null) 'title': title,
|
||||
if (url != null) 'url': url,
|
||||
if (source != null) 'source': source,
|
||||
if (orderKey != null) 'order_key': orderKey,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
||||
i1.TopSiteCompanion copyWith({
|
||||
i0.Value<String>? id,
|
||||
i0.Value<String>? title,
|
||||
i0.Value<Uri>? url,
|
||||
i0.Value<i2.StoredTopSiteSource>? source,
|
||||
i0.Value<String>? orderKey,
|
||||
i0.Value<DateTime>? createdAt,
|
||||
i0.Value<int>? rowid,
|
||||
}) {
|
||||
return i1.TopSiteCompanion(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
url: url ?? this.url,
|
||||
source: source ?? this.source,
|
||||
orderKey: orderKey ?? this.orderKey,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<String>(id.value);
|
||||
}
|
||||
if (title.present) {
|
||||
map['title'] = i0.Variable<String>(title.value);
|
||||
}
|
||||
if (url.present) {
|
||||
map['url'] = i0.Variable<String>(
|
||||
i1.TopSite.$converterurl.toSql(url.value),
|
||||
);
|
||||
}
|
||||
if (source.present) {
|
||||
map['source'] = i0.Variable<int>(
|
||||
i1.TopSite.$convertersource.toSql(source.value),
|
||||
);
|
||||
}
|
||||
if (orderKey.present) {
|
||||
map['order_key'] = i0.Variable<String>(orderKey.value);
|
||||
}
|
||||
if (createdAt.present) {
|
||||
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = i0.Variable<int>(rowid.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('TopSiteCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('title: $title, ')
|
||||
..write('url: $url, ')
|
||||
..write('source: $source, ')
|
||||
..write('orderKey: $orderKey, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
i0.Index get idxTopSiteOrderKey => i0.Index(
|
||||
'idx_top_site_order_key',
|
||||
'CREATE INDEX idx_top_site_order_key ON top_site (order_key)',
|
||||
);
|
||||
|
||||
class TopSiteSeedState extends i0.Table
|
||||
with i0.TableInfo<TopSiteSeedState, i1.TopSiteSeedStateData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
TopSiteSeedState(this.attachedDatabase, [this._alias]);
|
||||
late final i0.GeneratedColumn<String> seedId = i0.GeneratedColumn<String>(
|
||||
'seed_id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'PRIMARY KEY NOT NULL',
|
||||
);
|
||||
late final i0.GeneratedColumn<DateTime> appliedAt =
|
||||
i0.GeneratedColumn<DateTime>(
|
||||
'applied_at',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [seedId, appliedAt];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'top_site_seed_state';
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {seedId};
|
||||
@override
|
||||
i1.TopSiteSeedStateData map(
|
||||
Map<String, dynamic> data, {
|
||||
String? tablePrefix,
|
||||
}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.TopSiteSeedStateData(
|
||||
seedId: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}seed_id'],
|
||||
)!,
|
||||
appliedAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}applied_at'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TopSiteSeedState createAlias(String alias) {
|
||||
return TopSiteSeedState(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class TopSiteSeedStateData extends i0.DataClass
|
||||
implements i0.Insertable<i1.TopSiteSeedStateData> {
|
||||
final String seedId;
|
||||
final DateTime appliedAt;
|
||||
const TopSiteSeedStateData({required this.seedId, required this.appliedAt});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['seed_id'] = i0.Variable<String>(seedId);
|
||||
map['applied_at'] = i0.Variable<DateTime>(appliedAt);
|
||||
return map;
|
||||
}
|
||||
|
||||
factory TopSiteSeedStateData.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
i0.ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return TopSiteSeedStateData(
|
||||
seedId: serializer.fromJson<String>(json['seed_id']),
|
||||
appliedAt: serializer.fromJson<DateTime>(json['applied_at']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'seed_id': serializer.toJson<String>(seedId),
|
||||
'applied_at': serializer.toJson<DateTime>(appliedAt),
|
||||
};
|
||||
}
|
||||
|
||||
i1.TopSiteSeedStateData copyWith({String? seedId, DateTime? appliedAt}) =>
|
||||
i1.TopSiteSeedStateData(
|
||||
seedId: seedId ?? this.seedId,
|
||||
appliedAt: appliedAt ?? this.appliedAt,
|
||||
);
|
||||
TopSiteSeedStateData copyWithCompanion(i1.TopSiteSeedStateCompanion data) {
|
||||
return TopSiteSeedStateData(
|
||||
seedId: data.seedId.present ? data.seedId.value : this.seedId,
|
||||
appliedAt: data.appliedAt.present ? data.appliedAt.value : this.appliedAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('TopSiteSeedStateData(')
|
||||
..write('seedId: $seedId, ')
|
||||
..write('appliedAt: $appliedAt')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(seedId, appliedAt);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.TopSiteSeedStateData &&
|
||||
other.seedId == this.seedId &&
|
||||
other.appliedAt == this.appliedAt);
|
||||
}
|
||||
|
||||
class TopSiteSeedStateCompanion
|
||||
extends i0.UpdateCompanion<i1.TopSiteSeedStateData> {
|
||||
final i0.Value<String> seedId;
|
||||
final i0.Value<DateTime> appliedAt;
|
||||
final i0.Value<int> rowid;
|
||||
const TopSiteSeedStateCompanion({
|
||||
this.seedId = const i0.Value.absent(),
|
||||
this.appliedAt = const i0.Value.absent(),
|
||||
this.rowid = const i0.Value.absent(),
|
||||
});
|
||||
TopSiteSeedStateCompanion.insert({
|
||||
required String seedId,
|
||||
required DateTime appliedAt,
|
||||
this.rowid = const i0.Value.absent(),
|
||||
}) : seedId = i0.Value(seedId),
|
||||
appliedAt = i0.Value(appliedAt);
|
||||
static i0.Insertable<i1.TopSiteSeedStateData> custom({
|
||||
i0.Expression<String>? seedId,
|
||||
i0.Expression<DateTime>? appliedAt,
|
||||
i0.Expression<int>? rowid,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (seedId != null) 'seed_id': seedId,
|
||||
if (appliedAt != null) 'applied_at': appliedAt,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
||||
i1.TopSiteSeedStateCompanion copyWith({
|
||||
i0.Value<String>? seedId,
|
||||
i0.Value<DateTime>? appliedAt,
|
||||
i0.Value<int>? rowid,
|
||||
}) {
|
||||
return i1.TopSiteSeedStateCompanion(
|
||||
seedId: seedId ?? this.seedId,
|
||||
appliedAt: appliedAt ?? this.appliedAt,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (seedId.present) {
|
||||
map['seed_id'] = i0.Variable<String>(seedId.value);
|
||||
}
|
||||
if (appliedAt.present) {
|
||||
map['applied_at'] = i0.Variable<DateTime>(appliedAt.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = i0.Variable<int>(rowid.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('TopSiteSeedStateCompanion(')
|
||||
..write('seedId: $seedId, ')
|
||||
..write('appliedAt: $appliedAt, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class DefinitionsDrift extends i4.ModularAccessor {
|
||||
DefinitionsDrift(i0.GeneratedDatabase db) : super(db);
|
||||
i0.Selectable<String> leadingOrderKey({required int bucket}) {
|
||||
return customSelect(
|
||||
'SELECT lexo_rank_previous(?1, (SELECT order_key FROM top_site ORDER BY order_key LIMIT 1)) AS _c0',
|
||||
variables: [i0.Variable<int>(bucket)],
|
||||
readsFrom: {topSite},
|
||||
).map((i0.QueryRow row) => row.read<String>('_c0'));
|
||||
}
|
||||
|
||||
i0.Selectable<String> trailingOrderKey({required int bucket}) {
|
||||
return customSelect(
|
||||
'SELECT lexo_rank_next(?1, (SELECT order_key FROM top_site ORDER BY order_key DESC LIMIT 1)) AS _c0',
|
||||
variables: [i0.Variable<int>(bucket)],
|
||||
readsFrom: {topSite},
|
||||
).map((i0.QueryRow row) => row.read<String>('_c0'));
|
||||
}
|
||||
|
||||
i0.Selectable<String> orderKeyAfterSite({required String siteId}) {
|
||||
return customSelect(
|
||||
'WITH ordered_table AS (SELECT id, order_key, LEAD(order_key)OVER (ORDER BY order_key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS next_order_key FROM top_site) SELECT lexo_rank_reorder_after(order_key, next_order_key) AS _c0 FROM ordered_table WHERE id = ?1',
|
||||
variables: [i0.Variable<String>(siteId)],
|
||||
readsFrom: {topSite},
|
||||
).map((i0.QueryRow row) => row.read<String>('_c0'));
|
||||
}
|
||||
|
||||
i0.Selectable<String> orderKeyBeforeSite({required String siteId}) {
|
||||
return customSelect(
|
||||
'WITH ordered_table AS (SELECT id, order_key, LAG(order_key)OVER (ORDER BY order_key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS prev_order_key FROM top_site) SELECT lexo_rank_reorder_before(order_key, prev_order_key) AS _c0 FROM ordered_table WHERE id = ?1',
|
||||
variables: [i0.Variable<String>(siteId)],
|
||||
readsFrom: {topSite},
|
||||
).map((i0.QueryRow row) => row.read<String>('_c0'));
|
||||
}
|
||||
|
||||
i1.TopSite get topSite => i4.ReadDatabaseContainer(
|
||||
attachedDatabase,
|
||||
).resultSet<i1.TopSite>('top_site');
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
enum StoredTopSiteSource { seeded, pinned }
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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:io';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
|
||||
|
||||
import 'package:weblibre/core/filesystem.dart';
|
||||
import 'package:weblibre/data/database/functions/lexo_rank_functions.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.dart';
|
||||
|
||||
part 'providers.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
TopSiteDatabase topSiteDatabase(Ref ref) {
|
||||
final db = TopSiteDatabase(
|
||||
LazyDatabase(() async {
|
||||
final file = File(
|
||||
p.join(filesystem.profileDatabasesDir.path, 'top_site.db'),
|
||||
);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
|
||||
}
|
||||
|
||||
return NativeDatabase.createInBackground(
|
||||
file,
|
||||
setup: (database) {
|
||||
registerLexorankFunctions(database);
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
ref.onDispose(() async {
|
||||
await db.close();
|
||||
});
|
||||
|
||||
return db;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'providers.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(topSiteDatabase)
|
||||
final topSiteDatabaseProvider = TopSiteDatabaseProvider._();
|
||||
|
||||
final class TopSiteDatabaseProvider
|
||||
extends
|
||||
$FunctionalProvider<TopSiteDatabase, TopSiteDatabase, TopSiteDatabase>
|
||||
with $Provider<TopSiteDatabase> {
|
||||
TopSiteDatabaseProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'topSiteDatabaseProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$topSiteDatabaseHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<TopSiteDatabase> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
TopSiteDatabase create(Ref ref) {
|
||||
return topSiteDatabase(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(TopSiteDatabase value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<TopSiteDatabase>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$topSiteDatabaseHash() => r'6371e1784bd1272bdfc656caaa56e786e7503c78';
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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:fast_equatable/fast_equatable.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/entities/top_site_source.dart';
|
||||
|
||||
class TopSiteItem with FastEquatable {
|
||||
final String? id;
|
||||
final String title;
|
||||
final Uri url;
|
||||
final TopSiteSource source;
|
||||
final String? orderKey;
|
||||
final DateTime? createdAt;
|
||||
final String? previewImageUrl;
|
||||
final double? historyScore;
|
||||
final int? historyPlaceId;
|
||||
|
||||
TopSiteItem({
|
||||
this.id,
|
||||
required this.title,
|
||||
required this.url,
|
||||
required this.source,
|
||||
this.orderKey,
|
||||
this.createdAt,
|
||||
this.previewImageUrl,
|
||||
this.historyScore,
|
||||
this.historyPlaceId,
|
||||
});
|
||||
|
||||
bool get isPersisted =>
|
||||
source == TopSiteSource.seeded || source == TopSiteSource.pinned;
|
||||
|
||||
bool get isReorderable => isPersisted;
|
||||
|
||||
bool get isEditable => isPersisted;
|
||||
|
||||
bool get isRemovable => isPersisted;
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
id,
|
||||
title,
|
||||
url,
|
||||
source,
|
||||
orderKey,
|
||||
createdAt,
|
||||
previewImageUrl,
|
||||
historyScore,
|
||||
historyPlaceId,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
enum TopSiteSource { seeded, pinned, history }
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/entities/top_site_item.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/repositories/top_site_repository.dart';
|
||||
|
||||
part 'providers.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
List<({String title, Uri url})> topSiteDefaultSeeds(Ref ref) {
|
||||
return [
|
||||
(title: 'Wikipedia', url: Uri.parse('https://wikipedia.org')),
|
||||
(title: 'OpenStreetMap', url: Uri.parse('https://www.openstreetmap.org')),
|
||||
(title: 'Project Gutenberg', url: Uri.parse('https://www.gutenberg.org/')),
|
||||
];
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
Stream<List<TopSiteItem>> topSiteList(Ref ref, {int limit = 8}) {
|
||||
return ref
|
||||
.watch(topSiteRepositoryProvider.notifier)
|
||||
.watchTopSites(limit: limit);
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
Stream<List<TopSiteItem>> persistedTopSiteList(Ref ref) {
|
||||
return ref.watch(topSiteRepositoryProvider.notifier).watchPersistedTopSites();
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'providers.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(topSiteDefaultSeeds)
|
||||
final topSiteDefaultSeedsProvider = TopSiteDefaultSeedsProvider._();
|
||||
|
||||
final class TopSiteDefaultSeedsProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
List<({String title, Uri url})>,
|
||||
List<({String title, Uri url})>,
|
||||
List<({String title, Uri url})>
|
||||
>
|
||||
with $Provider<List<({String title, Uri url})>> {
|
||||
TopSiteDefaultSeedsProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'topSiteDefaultSeedsProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$topSiteDefaultSeedsHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<List<({String title, Uri url})>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
List<({String title, Uri url})> create(Ref ref) {
|
||||
return topSiteDefaultSeeds(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(List<({String title, Uri url})> value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<List<({String title, Uri url})>>(
|
||||
value,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$topSiteDefaultSeedsHash() =>
|
||||
r'd15156e1ebe1896a11dc2d983db9fb90a2222380';
|
||||
|
||||
@ProviderFor(topSiteList)
|
||||
final topSiteListProvider = TopSiteListFamily._();
|
||||
|
||||
final class TopSiteListProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<TopSiteItem>>,
|
||||
List<TopSiteItem>,
|
||||
Stream<List<TopSiteItem>>
|
||||
>
|
||||
with
|
||||
$FutureModifier<List<TopSiteItem>>,
|
||||
$StreamProvider<List<TopSiteItem>> {
|
||||
TopSiteListProvider._({
|
||||
required TopSiteListFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'topSiteListProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$topSiteListHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'topSiteListProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$StreamProviderElement<List<TopSiteItem>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $StreamProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Stream<List<TopSiteItem>> create(Ref ref) {
|
||||
final argument = this.argument as int;
|
||||
return topSiteList(ref, limit: argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is TopSiteListProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$topSiteListHash() => r'c9cb226f7a4368b907b85230ef2e8a4e0c73a199';
|
||||
|
||||
final class TopSiteListFamily extends $Family
|
||||
with $FunctionalFamilyOverride<Stream<List<TopSiteItem>>, int> {
|
||||
TopSiteListFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'topSiteListProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
TopSiteListProvider call({int limit = 8}) =>
|
||||
TopSiteListProvider._(argument: limit, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'topSiteListProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(persistedTopSiteList)
|
||||
final persistedTopSiteListProvider = PersistedTopSiteListProvider._();
|
||||
|
||||
final class PersistedTopSiteListProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<TopSiteItem>>,
|
||||
List<TopSiteItem>,
|
||||
Stream<List<TopSiteItem>>
|
||||
>
|
||||
with
|
||||
$FutureModifier<List<TopSiteItem>>,
|
||||
$StreamProvider<List<TopSiteItem>> {
|
||||
PersistedTopSiteListProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'persistedTopSiteListProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$persistedTopSiteListHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$StreamProviderElement<List<TopSiteItem>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $StreamProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Stream<List<TopSiteItem>> create(Ref ref) {
|
||||
return persistedTopSiteList(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$persistedTopSiteListHash() =>
|
||||
r'f0f7f087dccc0cea8498811ea2ad2af5e6d3fbb8';
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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:async';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/uuid.dart';
|
||||
import 'package:weblibre/features/geckoview/features/history/domain/repositories/history.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/definitions.drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/entities/stored_top_site_source.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/providers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/entities/top_site_item.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/entities/top_site_source.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/domain/providers.dart';
|
||||
|
||||
part 'top_site_repository.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class TopSiteRepository extends _$TopSiteRepository {
|
||||
Future<void> ensureSeeded() async {
|
||||
final db = ref.read(topSiteDatabaseProvider);
|
||||
final seeds = ref.read(topSiteDefaultSeedsProvider);
|
||||
|
||||
await db.transaction(() async {
|
||||
final alreadySeeded = await db.topSiteSeedStateDao.hasSeed(
|
||||
'initial-defaults-v1',
|
||||
);
|
||||
if (alreadySeeded) return;
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
for (var i = 0; i < seeds.length; i++) {
|
||||
final orderKey = await db.topSiteDao
|
||||
.generateTrailingOrderKey()
|
||||
.getSingle();
|
||||
// Insert one-by-one so trailing key advances
|
||||
await db.topSite.insertOne(
|
||||
TopSiteCompanion.insert(
|
||||
id: uuid.v7(),
|
||||
title: seeds[i].title,
|
||||
url: seeds[i].url,
|
||||
source: StoredTopSiteSource.seeded,
|
||||
orderKey: orderKey,
|
||||
createdAt: now,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await db.topSiteSeedStateDao.markSeedApplied('initial-defaults-v1');
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<TopSiteItem>> getTopSites({int limit = 8}) async {
|
||||
final persisted = await _getPersistedItems();
|
||||
|
||||
if (persisted.length >= limit) {
|
||||
return persisted.take(limit).toList();
|
||||
}
|
||||
|
||||
final remaining = limit - persisted.length;
|
||||
final historyItems = await _getHistoryItems(
|
||||
limit: remaining,
|
||||
excludeUrls: persisted.map((s) => s.url.toString()).toSet(),
|
||||
);
|
||||
|
||||
return [...persisted, ...historyItems];
|
||||
}
|
||||
|
||||
Stream<List<TopSiteItem>> watchTopSites({int limit = 8}) {
|
||||
final db = ref.read(topSiteDatabaseProvider);
|
||||
return db.topSiteDao.selectPersistedTopSites().watch().asyncMap((
|
||||
persistedRows,
|
||||
) async {
|
||||
final persistedItems = persistedRows.map(_mapPersistedRow).toList();
|
||||
|
||||
if (persistedItems.length >= limit) {
|
||||
return persistedItems.take(limit).toList();
|
||||
}
|
||||
|
||||
final remaining = limit - persistedItems.length;
|
||||
final historyItems = await _getHistoryItems(
|
||||
limit: remaining,
|
||||
excludeUrls: persistedItems.map((s) => s.url.toString()).toSet(),
|
||||
);
|
||||
|
||||
return [...persistedItems, ...historyItems];
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<TopSiteItem>> getPersistedTopSites() {
|
||||
return _getPersistedItems();
|
||||
}
|
||||
|
||||
Stream<List<TopSiteItem>> watchPersistedTopSites() {
|
||||
final db = ref.read(topSiteDatabaseProvider);
|
||||
return db.topSiteDao.selectPersistedTopSites().watch().map(
|
||||
(rows) => rows.map(_mapPersistedRow).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> addPinnedSite({
|
||||
required String title,
|
||||
required Uri url,
|
||||
}) async {
|
||||
final db = ref.read(topSiteDatabaseProvider);
|
||||
|
||||
// Check if URL already exists as a persisted site
|
||||
final existing = await db.topSiteDao.getPersistedTopSiteByUrl(url);
|
||||
if (existing != null) {
|
||||
final leadingKey = await db.topSiteDao
|
||||
.generateLeadingOrderKey()
|
||||
.getSingle();
|
||||
await db.topSiteDao.promoteToSource(
|
||||
existing.id,
|
||||
source: StoredTopSiteSource.pinned,
|
||||
title: title,
|
||||
orderKey: leadingKey,
|
||||
);
|
||||
return existing.id;
|
||||
}
|
||||
|
||||
final id = uuid.v7();
|
||||
final orderKey = await db.topSiteDao.generateLeadingOrderKey().getSingle();
|
||||
await db.topSiteDao.insertPinnedSite(
|
||||
id: id,
|
||||
title: title,
|
||||
url: url,
|
||||
orderKey: orderKey,
|
||||
);
|
||||
return id;
|
||||
}
|
||||
|
||||
Future<void> updatePersistedSite({
|
||||
required String id,
|
||||
required String title,
|
||||
required Uri url,
|
||||
}) {
|
||||
return ref
|
||||
.read(topSiteDatabaseProvider)
|
||||
.topSiteDao
|
||||
.updatePersistedSite(id, title: title, url: url);
|
||||
}
|
||||
|
||||
Future<void> removePersistedSite(String id) {
|
||||
return ref.read(topSiteDatabaseProvider).topSiteDao.deletePersistedSite(id);
|
||||
}
|
||||
|
||||
Future<void> assignOrderKey(String id, String orderKey) {
|
||||
return ref
|
||||
.read(topSiteDatabaseProvider)
|
||||
.topSiteDao
|
||||
.assignOrderKey(id, orderKey: orderKey);
|
||||
}
|
||||
|
||||
Future<String> getLeadingOrderKey() {
|
||||
return ref
|
||||
.read(topSiteDatabaseProvider)
|
||||
.topSiteDao
|
||||
.generateLeadingOrderKey()
|
||||
.getSingle();
|
||||
}
|
||||
|
||||
Future<String> getTrailingOrderKey() {
|
||||
return ref
|
||||
.read(topSiteDatabaseProvider)
|
||||
.topSiteDao
|
||||
.generateTrailingOrderKey()
|
||||
.getSingle();
|
||||
}
|
||||
|
||||
Future<String?> getOrderKeyAfterSite(String id) {
|
||||
return ref
|
||||
.read(topSiteDatabaseProvider)
|
||||
.topSiteDao
|
||||
.generateOrderKeyAfterSiteId(id)
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<String> getOrderKeyBeforeSite(String id) {
|
||||
return ref
|
||||
.read(topSiteDatabaseProvider)
|
||||
.topSiteDao
|
||||
.generateOrderKeyBeforeSiteId(id)
|
||||
.getSingle();
|
||||
}
|
||||
|
||||
Future<List<TopSiteItem>> _getPersistedItems() async {
|
||||
final rows = await ref
|
||||
.read(topSiteDatabaseProvider)
|
||||
.topSiteDao
|
||||
.getPersistedTopSites();
|
||||
return rows.map(_mapPersistedRow).toList();
|
||||
}
|
||||
|
||||
Future<List<TopSiteItem>> _getHistoryItems({
|
||||
required int limit,
|
||||
required Set<String> excludeUrls,
|
||||
}) async {
|
||||
final highlights = await ref
|
||||
.read(historyRepositoryProvider.notifier)
|
||||
.getHistoryHighlights(limit: limit + excludeUrls.length);
|
||||
|
||||
final items = <TopSiteItem>[];
|
||||
for (final h in highlights) {
|
||||
if (items.length >= limit) break;
|
||||
|
||||
final uri = Uri.tryParse(h.url);
|
||||
if (uri == null) continue;
|
||||
|
||||
if (excludeUrls.contains(uri.toString())) continue;
|
||||
|
||||
final title = (h.title?.trim().isNotEmpty == true)
|
||||
? h.title!.trim()
|
||||
: uri.host;
|
||||
|
||||
items.add(
|
||||
TopSiteItem(
|
||||
title: title,
|
||||
url: uri,
|
||||
source: TopSiteSource.history,
|
||||
previewImageUrl: h.previewImageUrl,
|
||||
historyScore: h.score,
|
||||
historyPlaceId: h.placeId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
TopSiteItem _mapPersistedRow(TopSiteData row) {
|
||||
return TopSiteItem(
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
url: row.url,
|
||||
source: row.source == StoredTopSiteSource.seeded
|
||||
? TopSiteSource.seeded
|
||||
: TopSiteSource.pinned,
|
||||
orderKey: row.orderKey,
|
||||
createdAt: row.createdAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void build() {
|
||||
unawaited(ensureSeeded());
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'top_site_repository.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(TopSiteRepository)
|
||||
final topSiteRepositoryProvider = TopSiteRepositoryProvider._();
|
||||
|
||||
final class TopSiteRepositoryProvider
|
||||
extends $NotifierProvider<TopSiteRepository, void> {
|
||||
TopSiteRepositoryProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'topSiteRepositoryProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$topSiteRepositoryHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
TopSiteRepository create() => TopSiteRepository();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(void value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<void>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$topSiteRepositoryHash() => r'b70c50a60411cbafb5367cd303d822c2eeb11add';
|
||||
|
||||
abstract class _$TopSiteRepository extends $Notifier<void> {
|
||||
void build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref = this.ref as $Ref<void, void>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<void, void>,
|
||||
void,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
@@ -72,9 +72,7 @@ class UrlListTile extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
leading ??
|
||||
RepaintBoundary(
|
||||
child: UrlIcon([uri], iconSize: iconSize),
|
||||
),
|
||||
RepaintBoundary(child: UrlIcon([uri], iconSize: iconSize)),
|
||||
const SizedBox(width: 14.0),
|
||||
Expanded(
|
||||
child: Column(
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "top_site",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverter()",
|
||||
"dart_type_name": "Uri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"getter_name": "source",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<StoredTopSiteSource>(StoredTopSiteSource.values)",
|
||||
"dart_type_name": "StoredTopSiteSource"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"getter_name": "createdAt",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"UNIQUE(url)"
|
||||
],
|
||||
"unique_keys": [
|
||||
[
|
||||
"url"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "index",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"name": "idx_top_site_order_key",
|
||||
"sql": "CREATE INDEX idx_top_site_order_key ON top_site(order_key);",
|
||||
"unique": false,
|
||||
"columns": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "top_site_seed_state",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "seed_id",
|
||||
"getter_name": "seedId",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "applied_at",
|
||||
"getter_name": "appliedAt",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "top_site",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"top_site\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"title\" TEXT NOT NULL, \"url\" TEXT NOT NULL, \"source\" INTEGER NOT NULL, \"order_key\" TEXT NOT NULL, \"created_at\" INTEGER NOT NULL, UNIQUE(url));"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_top_site_order_key",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE INDEX idx_top_site_order_key ON top_site (order_key)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "top_site_seed_state",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"top_site_seed_state\" (\"seed_id\" TEXT PRIMARY KEY NOT NULL, \"applied_at\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// dart format width=80
|
||||
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||
// ignore_for_file: type=lint,unused_import
|
||||
//
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/internal/migrations.dart';
|
||||
import 'schema_v1.dart' as v1;
|
||||
|
||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
@override
|
||||
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
||||
switch (version) {
|
||||
case 1:
|
||||
return v1.DatabaseAtV1(db);
|
||||
default:
|
||||
throw MissingSchemaException(version, versions);
|
||||
}
|
||||
}
|
||||
|
||||
static const versions = const [1];
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// dart format width=80
|
||||
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||
// ignore_for_file: type=lint,unused_import
|
||||
//
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
class TopSite extends Table with TableInfo {
|
||||
@override
|
||||
final GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
TopSite(this.attachedDatabase, [this._alias]);
|
||||
late final GeneratedColumn<String> id = GeneratedColumn<String>(
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'PRIMARY KEY NOT NULL',
|
||||
);
|
||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
||||
'title',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
late final GeneratedColumn<String> url = GeneratedColumn<String>(
|
||||
'url',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
late final GeneratedColumn<int> source = GeneratedColumn<int>(
|
||||
'source',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
late final GeneratedColumn<String> orderKey = GeneratedColumn<String>(
|
||||
'order_key',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
late final GeneratedColumn<int> createdAt = GeneratedColumn<int>(
|
||||
'created_at',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [
|
||||
id,
|
||||
title,
|
||||
url,
|
||||
source,
|
||||
orderKey,
|
||||
createdAt,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'top_site';
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
List<Set<GeneratedColumn>> get uniqueKeys => [
|
||||
{url},
|
||||
];
|
||||
@override
|
||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
||||
}
|
||||
|
||||
@override
|
||||
TopSite createAlias(String alias) {
|
||||
return TopSite(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => const ['UNIQUE(url)'];
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class TopSiteSeedState extends Table with TableInfo {
|
||||
@override
|
||||
final GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
TopSiteSeedState(this.attachedDatabase, [this._alias]);
|
||||
late final GeneratedColumn<String> seedId = GeneratedColumn<String>(
|
||||
'seed_id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'PRIMARY KEY NOT NULL',
|
||||
);
|
||||
late final GeneratedColumn<int> appliedAt = GeneratedColumn<int>(
|
||||
'applied_at',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [seedId, appliedAt];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'top_site_seed_state';
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {seedId};
|
||||
@override
|
||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
||||
}
|
||||
|
||||
@override
|
||||
TopSiteSeedState createAlias(String alias) {
|
||||
return TopSiteSeedState(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class DatabaseAtV1 extends GeneratedDatabase {
|
||||
DatabaseAtV1(QueryExecutor e) : super(e);
|
||||
late final TopSite topSite = TopSite(this);
|
||||
late final Index idxTopSiteOrderKey = Index(
|
||||
'idx_top_site_order_key',
|
||||
'CREATE INDEX idx_top_site_order_key ON top_site (order_key)',
|
||||
);
|
||||
late final TopSiteSeedState topSiteSeedState = TopSiteSeedState(this);
|
||||
@override
|
||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||
@override
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities => [
|
||||
topSite,
|
||||
idxTopSiteOrderKey,
|
||||
topSiteSeedState,
|
||||
];
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// dart format width=80
|
||||
// ignore_for_file: unused_local_variable, unused_import
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_dev/api/migrations_native.dart';
|
||||
import 'package:weblibre/features/geckoview/features/top_sites/data/database/database.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'generated/schema.dart';
|
||||
|
||||
import 'generated/schema_v1.dart' as v1;
|
||||
|
||||
void main() {
|
||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||
late SchemaVerifier verifier;
|
||||
|
||||
setUpAll(() {
|
||||
verifier = SchemaVerifier(GeneratedHelper());
|
||||
});
|
||||
|
||||
group('simple database migrations', () {
|
||||
const versions = GeneratedHelper.versions;
|
||||
for (final (i, fromVersion) in versions.indexed) {
|
||||
group('from $fromVersion', () {
|
||||
for (final toVersion in versions.skip(i + 1)) {
|
||||
test('to $toVersion', () async {
|
||||
final schema = await verifier.schemaAt(fromVersion);
|
||||
final db = TopSiteDatabase(schema.newConnection());
|
||||
await verifier.migrateAndValidate(db, toVersion);
|
||||
await db.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('v1 schema creation works', () async {
|
||||
final schema = await verifier.schemaAt(1);
|
||||
final db = TopSiteDatabase(schema.newConnection());
|
||||
await verifier.migrateAndValidate(db, 1);
|
||||
await db.close();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user