track is_private for tabs; add clsoe all private tabs button; close private tabs before exit;
This commit is contained in:
@@ -0,0 +1,427 @@
|
||||
{
|
||||
"_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": "container",
|
||||
"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": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"getter_name": "color",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ColorConverter()",
|
||||
"dart_type_name": "Color"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"getter_name": "metadata",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ContainerMetadataConverter()",
|
||||
"dart_type_name": "ContainerMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab",
|
||||
"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": "parent_id",
|
||||
"getter_name": "parentId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES tab(id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "container_id",
|
||||
"getter_name": "containerId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES container(id)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "container",
|
||||
"column": "id"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"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": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_private",
|
||||
"getter_name": "isPrivate",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_probably_readerable",
|
||||
"getter_name": "isProbablyReaderable",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_markdown",
|
||||
"getter_name": "extractedContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_markdown",
|
||||
"getter_name": "fullContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "timestamp",
|
||||
"getter_name": "timestamp",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1
|
||||
],
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN\n -- Update all children of the deleted row to point to its parent\n UPDATE tab \n SET parent_id = OLD.parent_id \n WHERE parent_id = OLD.id;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_insert",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_delete",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_update",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "container",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"container\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"name\" TEXT, \"color\" INTEGER NOT NULL, \"metadata\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"tab\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"parent_id\" TEXT REFERENCES tab(id)ON DELETE SET NULL, \"container_id\" TEXT REFERENCES container(id)ON DELETE CASCADE, \"order_key\" TEXT NOT NULL, \"url\" TEXT, \"title\" TEXT, \"is_private\" INTEGER, \"is_probably_readerable\" INTEGER, \"extracted_content_markdown\" TEXT, \"extracted_content_plain\" TEXT, \"full_content_markdown\" TEXT, \"full_content_plain\" TEXT, \"timestamp\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = OLD.parent_id WHERE parent_id = OLD.id;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -92,6 +92,7 @@ class TabRepository extends _$TabRepository {
|
||||
},
|
||||
parentId: Value(parentId),
|
||||
containerId: Value(assingedContainer.value?.id),
|
||||
isPrivate: Value(private),
|
||||
);
|
||||
|
||||
if (launchedFromIntent) {
|
||||
@@ -102,7 +103,7 @@ class TabRepository extends _$TabRepository {
|
||||
}
|
||||
|
||||
Future<String> duplicateTab({
|
||||
required String? selectTabId,
|
||||
required String selectTabId,
|
||||
String? containerId,
|
||||
bool selectTab = true,
|
||||
}) async {
|
||||
@@ -124,6 +125,9 @@ class TabRepository extends _$TabRepository {
|
||||
},
|
||||
parentId: const Value.absent(),
|
||||
containerId: Value(containerData?.id),
|
||||
isPrivate: Value(
|
||||
await tabDao.getTabIsPrivate(selectTabId).getSingleOrNull(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -378,6 +382,7 @@ class TabRepository extends _$TabRepository {
|
||||
tabId,
|
||||
parentId: const Value.absent(),
|
||||
containerId: Value(containerId),
|
||||
isPrivate: const Value.absent(),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$tabRepositoryHash() => r'7af24e759044d1b1b471cdf90a3eb38eef06f61b';
|
||||
String _$tabRepositoryHash() => r'0411e8b3fa9c3e344106f8fa8178bd6eb613309a';
|
||||
|
||||
abstract class _$TabRepository extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
+58
-3
@@ -219,8 +219,8 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(MdiIcons.closeBoxMultiple),
|
||||
child: const Text('Close All'),
|
||||
leadingIcon: const Icon(MdiIcons.closeCircle),
|
||||
child: const Text('Close All Tabs'),
|
||||
onPressed: () async {
|
||||
final result = await showDialog<bool?>(
|
||||
context: context,
|
||||
@@ -256,7 +256,62 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
|
||||
final count = await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.closeAllTabsByContainer(container);
|
||||
.closeContainerTabs(container);
|
||||
|
||||
if (context.mounted) {
|
||||
ui_helper.showTabUndoClose(
|
||||
context,
|
||||
ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.undoClose,
|
||||
count: count,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(MdiIcons.incognitoCircleOff),
|
||||
child: const Text('Close Private Tabs'),
|
||||
onPressed: () async {
|
||||
final result = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
icon: const Icon(Icons.warning),
|
||||
title: const Text('Close All Private Tabs'),
|
||||
content: const Text(
|
||||
'Are you sure you want to close all displayed private tabs?',
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: const Text('Close'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
final container = ref.read(
|
||||
selectedContainerProvider,
|
||||
);
|
||||
|
||||
final count = await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.closeContainerTabs(
|
||||
container,
|
||||
includeRegular: false,
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
ui_helper.showTabUndoClose(
|
||||
|
||||
@@ -72,7 +72,31 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
|
||||
);
|
||||
}
|
||||
|
||||
Selectable<String> getContainerTabIds(String? containerId) {
|
||||
Selectable<String> getAllTabIds({
|
||||
bool includeRegular = true,
|
||||
bool includePrivate = true,
|
||||
}) {
|
||||
final query = selectOnly(db.tab)..addColumns([db.tab.id]);
|
||||
|
||||
if (!includeRegular) {
|
||||
query.where(
|
||||
db.tab.isPrivate.isNotNull() & db.tab.isPrivate.isNotValue(false),
|
||||
);
|
||||
}
|
||||
if (!includePrivate) {
|
||||
query.where(
|
||||
db.tab.isPrivate.isNotNull() & db.tab.isPrivate.isNotValue(true),
|
||||
);
|
||||
}
|
||||
|
||||
return query.map((row) => row.read(db.tab.id)!);
|
||||
}
|
||||
|
||||
Selectable<String> getContainerTabIds(
|
||||
String? containerId, {
|
||||
bool includeRegular = true,
|
||||
bool includePrivate = true,
|
||||
}) {
|
||||
final query = selectOnly(db.tab)
|
||||
..addColumns([db.tab.id])
|
||||
..where(
|
||||
@@ -82,6 +106,17 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
|
||||
)
|
||||
..orderBy([OrderingTerm.asc(db.tab.orderKey)]);
|
||||
|
||||
if (!includeRegular) {
|
||||
query.where(
|
||||
db.tab.isPrivate.isNotNull() & db.tab.isPrivate.isNotValue(false),
|
||||
);
|
||||
}
|
||||
if (!includePrivate) {
|
||||
query.where(
|
||||
db.tab.isPrivate.isNotNull() & db.tab.isPrivate.isNotValue(true),
|
||||
);
|
||||
}
|
||||
|
||||
return query.map((row) => row.read(db.tab.id)!);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,14 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
|
||||
SingleOrNullSelectable<TabData> getTabDataById(String id) =>
|
||||
db.tab.select()..where((t) => t.id.equals(id));
|
||||
|
||||
SingleOrNullSelectable<bool?> getTabIsPrivate(String tabId) {
|
||||
final query = selectOnly(db.tab)
|
||||
..addColumns([db.tab.isPrivate])
|
||||
..where(db.tab.id.equals(tabId));
|
||||
|
||||
return query.map((row) => row.read(db.tab.isPrivate));
|
||||
}
|
||||
|
||||
Selectable<String> getAllTabIds() {
|
||||
final query = selectOnly(db.tab)
|
||||
..addColumns([db.tab.id])
|
||||
@@ -97,6 +105,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
|
||||
|
||||
Future<String> upsertContainerTabTransactional(
|
||||
Future<String> Function() createTab, {
|
||||
required Value<bool?> isPrivate,
|
||||
required Value<String?> parentId,
|
||||
Value<String?> containerId = const Value.absent(),
|
||||
Value<String?> orderKey = const Value.absent(),
|
||||
@@ -113,6 +122,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
|
||||
parentId: parentId,
|
||||
timestamp: DateTime.now(),
|
||||
containerId: containerId,
|
||||
isPrivate: isPrivate,
|
||||
orderKey: currentOrderKey,
|
||||
),
|
||||
onConflict: DoUpdate(
|
||||
@@ -131,6 +141,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
|
||||
//Upsert an tab only if there is no container assigned yet
|
||||
Future<String> upsertUnassignedTab(
|
||||
String tabId, {
|
||||
required Value<bool?> isPrivate,
|
||||
required Value<String?> parentId,
|
||||
Value<String?> containerId = const Value.absent(),
|
||||
Value<String?> orderKey = const Value.absent(),
|
||||
@@ -147,6 +158,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
|
||||
timestamp: DateTime.now(),
|
||||
containerId: containerId,
|
||||
orderKey: currentOrderKey,
|
||||
isPrivate: isPrivate,
|
||||
),
|
||||
mode: InsertMode.insertOrIgnore,
|
||||
);
|
||||
|
||||
@@ -18,17 +18,19 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/internal/versioned_schema.dart';
|
||||
import 'package:drift_dev/api/migrations_native.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/daos/container.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/daos/tab.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/database.drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/database.steps.dart';
|
||||
import 'package:weblibre/features/search/domain/fts_tokenizer.dart';
|
||||
|
||||
@DriftDatabase(include: {'definitions.drift'}, daos: [ContainerDao, TabDao])
|
||||
class TabDatabase extends $TabDatabase with TrigramQueryBuilderMixin {
|
||||
@override
|
||||
final int schemaVersion = 2;
|
||||
final int schemaVersion = 3;
|
||||
|
||||
@override
|
||||
final int ftsTokenLimit = 10;
|
||||
@@ -47,7 +49,39 @@ class TabDatabase extends $TabDatabase with TrigramQueryBuilderMixin {
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
await definitionsDrift.optimizeFtsIndex();
|
||||
},
|
||||
onUpgrade: (m, from, to) async {
|
||||
// Following the advice from https://drift.simonbinder.eu/Migrations/api/#general-tips
|
||||
await customStatement('PRAGMA foreign_keys = OFF');
|
||||
|
||||
await transaction(
|
||||
() => VersionedSchema.runMigrationSteps(
|
||||
migrator: m,
|
||||
from: from,
|
||||
to: to,
|
||||
steps: _upgrade,
|
||||
),
|
||||
);
|
||||
|
||||
if (kDebugMode) {
|
||||
final wrongForeignKeys = await customSelect(
|
||||
'PRAGMA foreign_key_check',
|
||||
).get();
|
||||
assert(
|
||||
wrongForeignKeys.isEmpty,
|
||||
'${wrongForeignKeys.map((e) => e.data)}',
|
||||
);
|
||||
}
|
||||
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
},
|
||||
);
|
||||
|
||||
TabDatabase(super.e);
|
||||
|
||||
static final _upgrade = migrationSteps(
|
||||
from2To3: (m, schema) async {
|
||||
final tabAtV3 = schema.tab;
|
||||
await m.addColumn(tabAtV3, tabAtV3.isPrivate);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
// dart format width=80
|
||||
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||
import 'package:drift/drift.dart' as i1;
|
||||
import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
|
||||
|
||||
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||
final class Schema3 extends i0.VersionedSchema {
|
||||
Schema3({required super.database}) : super(version: 3);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
container,
|
||||
tab,
|
||||
tabFts,
|
||||
tabMaintainParentChainOnDelete,
|
||||
tabAfterInsert,
|
||||
tabAfterDelete,
|
||||
tabAfterUpdate,
|
||||
];
|
||||
late final Shape0 container = Shape0(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'container',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_0, _column_1, _column_2, _column_3],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape1 tab = Shape1(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'tab',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_0,
|
||||
_column_4,
|
||||
_column_5,
|
||||
_column_6,
|
||||
_column_7,
|
||||
_column_8,
|
||||
_column_9,
|
||||
_column_10,
|
||||
_column_11,
|
||||
_column_12,
|
||||
_column_13,
|
||||
_column_14,
|
||||
_column_15,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape2 tabFts = Shape2(
|
||||
source: i0.VersionedVirtualTable(
|
||||
entityName: 'tab_fts',
|
||||
moduleAndArgs:
|
||||
'fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize="trigram")',
|
||||
columns: [_column_8, _column_7, _column_12, _column_14],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
final i1.Trigger tabMaintainParentChainOnDelete = i1.Trigger(
|
||||
'CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = OLD.parent_id WHERE parent_id = OLD.id;END',
|
||||
'tab_maintain_parent_chain_on_delete',
|
||||
);
|
||||
final i1.Trigger tabAfterInsert = i1.Trigger(
|
||||
'CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts ("rowid", title, url, extracted_content_plain, full_content_plain) VALUES (new."rowid", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END',
|
||||
'tab_after_insert',
|
||||
);
|
||||
final i1.Trigger tabAfterDelete = i1.Trigger(
|
||||
'CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, "rowid", title, url, extracted_content_plain, full_content_plain) VALUES (\'delete\', old."rowid", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END',
|
||||
'tab_after_delete',
|
||||
);
|
||||
final i1.Trigger tabAfterUpdate = i1.Trigger(
|
||||
'CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, "rowid", title, url, extracted_content_plain, full_content_plain) VALUES (\'delete\', old."rowid", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts ("rowid", title, url, extracted_content_plain, full_content_plain) VALUES (new."rowid", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END',
|
||||
'tab_after_update',
|
||||
);
|
||||
}
|
||||
|
||||
class Shape0 extends i0.VersionedTable {
|
||||
Shape0({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get id =>
|
||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get name =>
|
||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<int> get color =>
|
||||
columnsByName['color']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get metadata =>
|
||||
columnsByName['metadata']! as i1.GeneratedColumn<String>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<String> _column_0(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: 'PRIMARY KEY NOT NULL',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'name',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_2(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'color',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'metadata',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
|
||||
class Shape1 extends i0.VersionedTable {
|
||||
Shape1({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get id =>
|
||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get parentId =>
|
||||
columnsByName['parent_id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get containerId =>
|
||||
columnsByName['container_id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get orderKey =>
|
||||
columnsByName['order_key']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get url =>
|
||||
columnsByName['url']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get title =>
|
||||
columnsByName['title']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<int> get isPrivate =>
|
||||
columnsByName['is_private']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get isProbablyReaderable =>
|
||||
columnsByName['is_probably_readerable']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get extractedContentMarkdown =>
|
||||
columnsByName['extracted_content_markdown']!
|
||||
as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get extractedContentPlain =>
|
||||
columnsByName['extracted_content_plain']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get fullContentMarkdown =>
|
||||
columnsByName['full_content_markdown']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get fullContentPlain =>
|
||||
columnsByName['full_content_plain']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<int> get timestamp =>
|
||||
columnsByName['timestamp']! as i1.GeneratedColumn<int>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<String> _column_4(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'parent_id',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: 'REFERENCES tab(id)ON DELETE SET NULL',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_5(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'container_id',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: 'REFERENCES container(id)ON DELETE CASCADE',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_6(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'order_key',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_7(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'url',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_8(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'title',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_9(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'is_private',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_10(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'is_probably_readerable',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_11(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'extracted_content_markdown',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_12(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'extracted_content_plain',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_13(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'full_content_markdown',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_14(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'full_content_plain',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: '',
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_15(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'timestamp',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
|
||||
class Shape2 extends i0.VersionedVirtualTable {
|
||||
Shape2({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get title =>
|
||||
columnsByName['title']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get url =>
|
||||
columnsByName['url']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get extractedContentPlain =>
|
||||
columnsByName['extracted_content_plain']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get fullContentPlain =>
|
||||
columnsByName['full_content_plain']! as i1.GeneratedColumn<String>;
|
||||
}
|
||||
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
}) {
|
||||
return (currentVersion, database) async {
|
||||
switch (currentVersion) {
|
||||
case 2:
|
||||
final schema = Schema3(database: database);
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from2To3(migrator, schema);
|
||||
return 3;
|
||||
default:
|
||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
i1.OnUpgrade stepByStep({
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
}) => i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(from2To3: from2To3),
|
||||
);
|
||||
@@ -19,6 +19,7 @@ CREATE TABLE tab(
|
||||
order_key TEXT NOT NULL,
|
||||
url TEXT MAPPED BY `const UriConverterNullable()`,
|
||||
title TEXT,
|
||||
is_private BOOL,
|
||||
is_probably_readerable BOOL,
|
||||
extracted_content_markdown TEXT,
|
||||
extracted_content_plain TEXT,
|
||||
@@ -141,6 +142,7 @@ queryTabsBasic WITH TabQueryResult:
|
||||
SELECT
|
||||
t.id,
|
||||
t.container_id,
|
||||
t.is_private,
|
||||
t.title,
|
||||
CAST(t.url AS TEXT) AS url,
|
||||
t.url AS clean_url,
|
||||
@@ -168,6 +170,7 @@ queryTabsFullContent WITH TabQueryResult:
|
||||
SELECT
|
||||
t.id,
|
||||
t.container_id,
|
||||
t.is_private,
|
||||
highlight(tab_fts, 0, :beforeMatch, :afterMatch) AS title,
|
||||
highlight(tab_fts, 1, :beforeMatch, :afterMatch) AS url,
|
||||
snippet(tab_fts, 2, :beforeMatch, :afterMatch, :ellipsis, :snippetLength) AS extracted_content,
|
||||
|
||||
@@ -326,6 +326,7 @@ typedef $TabCreateCompanionBuilder =
|
||||
required String orderKey,
|
||||
i0.Value<Uri?> url,
|
||||
i0.Value<String?> title,
|
||||
i0.Value<bool?> isPrivate,
|
||||
i0.Value<bool?> isProbablyReaderable,
|
||||
i0.Value<String?> extractedContentMarkdown,
|
||||
i0.Value<String?> extractedContentPlain,
|
||||
@@ -342,6 +343,7 @@ typedef $TabUpdateCompanionBuilder =
|
||||
i0.Value<String> orderKey,
|
||||
i0.Value<Uri?> url,
|
||||
i0.Value<String?> title,
|
||||
i0.Value<bool?> isPrivate,
|
||||
i0.Value<bool?> isProbablyReaderable,
|
||||
i0.Value<String?> extractedContentMarkdown,
|
||||
i0.Value<String?> extractedContentPlain,
|
||||
@@ -418,6 +420,11 @@ class $TabFilterComposer extends i0.Composer<i0.GeneratedDatabase, i3.Tab> {
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<bool> get isPrivate => $composableBuilder(
|
||||
column: $table.isPrivate,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<bool> get isProbablyReaderable => $composableBuilder(
|
||||
column: $table.isProbablyReaderable,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
@@ -509,6 +516,11 @@ class $TabOrderingComposer extends i0.Composer<i0.GeneratedDatabase, i3.Tab> {
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<bool> get isPrivate => $composableBuilder(
|
||||
column: $table.isPrivate,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<bool> get isProbablyReaderable => $composableBuilder(
|
||||
column: $table.isProbablyReaderable,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
@@ -590,6 +602,9 @@ class $TabAnnotationComposer extends i0.Composer<i0.GeneratedDatabase, i3.Tab> {
|
||||
i0.GeneratedColumn<String> get title =>
|
||||
$composableBuilder(column: $table.title, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<bool> get isPrivate =>
|
||||
$composableBuilder(column: $table.isPrivate, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<bool> get isProbablyReaderable => $composableBuilder(
|
||||
column: $table.isProbablyReaderable,
|
||||
builder: (column) => column,
|
||||
@@ -680,6 +695,7 @@ class $TabTableManager
|
||||
i0.Value<String> orderKey = const i0.Value.absent(),
|
||||
i0.Value<Uri?> url = const i0.Value.absent(),
|
||||
i0.Value<String?> title = const i0.Value.absent(),
|
||||
i0.Value<bool?> isPrivate = const i0.Value.absent(),
|
||||
i0.Value<bool?> isProbablyReaderable = const i0.Value.absent(),
|
||||
i0.Value<String?> extractedContentMarkdown =
|
||||
const i0.Value.absent(),
|
||||
@@ -696,6 +712,7 @@ class $TabTableManager
|
||||
orderKey: orderKey,
|
||||
url: url,
|
||||
title: title,
|
||||
isPrivate: isPrivate,
|
||||
isProbablyReaderable: isProbablyReaderable,
|
||||
extractedContentMarkdown: extractedContentMarkdown,
|
||||
extractedContentPlain: extractedContentPlain,
|
||||
@@ -712,6 +729,7 @@ class $TabTableManager
|
||||
required String orderKey,
|
||||
i0.Value<Uri?> url = const i0.Value.absent(),
|
||||
i0.Value<String?> title = const i0.Value.absent(),
|
||||
i0.Value<bool?> isPrivate = const i0.Value.absent(),
|
||||
i0.Value<bool?> isProbablyReaderable = const i0.Value.absent(),
|
||||
i0.Value<String?> extractedContentMarkdown =
|
||||
const i0.Value.absent(),
|
||||
@@ -728,6 +746,7 @@ class $TabTableManager
|
||||
orderKey: orderKey,
|
||||
url: url,
|
||||
title: title,
|
||||
isPrivate: isPrivate,
|
||||
isProbablyReaderable: isProbablyReaderable,
|
||||
extractedContentMarkdown: extractedContentMarkdown,
|
||||
extractedContentPlain: extractedContentPlain,
|
||||
@@ -1219,6 +1238,14 @@ class Tab extends i0.Table with i0.TableInfo<Tab, i3.TabData> {
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '',
|
||||
);
|
||||
late final i0.GeneratedColumn<bool> isPrivate = i0.GeneratedColumn<bool>(
|
||||
'is_private',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i0.DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '',
|
||||
);
|
||||
late final i0.GeneratedColumn<bool> isProbablyReaderable =
|
||||
i0.GeneratedColumn<bool>(
|
||||
'is_probably_readerable',
|
||||
@@ -1281,6 +1308,7 @@ class Tab extends i0.Table with i0.TableInfo<Tab, i3.TabData> {
|
||||
orderKey,
|
||||
url,
|
||||
title,
|
||||
isPrivate,
|
||||
isProbablyReaderable,
|
||||
extractedContentMarkdown,
|
||||
extractedContentPlain,
|
||||
@@ -1325,6 +1353,10 @@ class Tab extends i0.Table with i0.TableInfo<Tab, i3.TabData> {
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}title'],
|
||||
),
|
||||
isPrivate: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.bool,
|
||||
data['${effectivePrefix}is_private'],
|
||||
),
|
||||
isProbablyReaderable: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.bool,
|
||||
data['${effectivePrefix}is_probably_readerable'],
|
||||
@@ -1370,6 +1402,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
final String orderKey;
|
||||
final Uri? url;
|
||||
final String? title;
|
||||
final bool? isPrivate;
|
||||
final bool? isProbablyReaderable;
|
||||
final String? extractedContentMarkdown;
|
||||
final String? extractedContentPlain;
|
||||
@@ -1383,6 +1416,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
required this.orderKey,
|
||||
this.url,
|
||||
this.title,
|
||||
this.isPrivate,
|
||||
this.isProbablyReaderable,
|
||||
this.extractedContentMarkdown,
|
||||
this.extractedContentPlain,
|
||||
@@ -1407,6 +1441,9 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
if (!nullToAbsent || title != null) {
|
||||
map['title'] = i0.Variable<String>(title);
|
||||
}
|
||||
if (!nullToAbsent || isPrivate != null) {
|
||||
map['is_private'] = i0.Variable<bool>(isPrivate);
|
||||
}
|
||||
if (!nullToAbsent || isProbablyReaderable != null) {
|
||||
map['is_probably_readerable'] = i0.Variable<bool>(isProbablyReaderable);
|
||||
}
|
||||
@@ -1442,6 +1479,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
orderKey: serializer.fromJson<String>(json['order_key']),
|
||||
url: serializer.fromJson<Uri?>(json['url']),
|
||||
title: serializer.fromJson<String?>(json['title']),
|
||||
isPrivate: serializer.fromJson<bool?>(json['is_private']),
|
||||
isProbablyReaderable: serializer.fromJson<bool?>(
|
||||
json['is_probably_readerable'],
|
||||
),
|
||||
@@ -1470,6 +1508,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
'order_key': serializer.toJson<String>(orderKey),
|
||||
'url': serializer.toJson<Uri?>(url),
|
||||
'title': serializer.toJson<String?>(title),
|
||||
'is_private': serializer.toJson<bool?>(isPrivate),
|
||||
'is_probably_readerable': serializer.toJson<bool?>(isProbablyReaderable),
|
||||
'extracted_content_markdown': serializer.toJson<String?>(
|
||||
extractedContentMarkdown,
|
||||
@@ -1490,6 +1529,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
String? orderKey,
|
||||
i0.Value<Uri?> url = const i0.Value.absent(),
|
||||
i0.Value<String?> title = const i0.Value.absent(),
|
||||
i0.Value<bool?> isPrivate = const i0.Value.absent(),
|
||||
i0.Value<bool?> isProbablyReaderable = const i0.Value.absent(),
|
||||
i0.Value<String?> extractedContentMarkdown = const i0.Value.absent(),
|
||||
i0.Value<String?> extractedContentPlain = const i0.Value.absent(),
|
||||
@@ -1503,6 +1543,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
orderKey: orderKey ?? this.orderKey,
|
||||
url: url.present ? url.value : this.url,
|
||||
title: title.present ? title.value : this.title,
|
||||
isPrivate: isPrivate.present ? isPrivate.value : this.isPrivate,
|
||||
isProbablyReaderable: isProbablyReaderable.present
|
||||
? isProbablyReaderable.value
|
||||
: this.isProbablyReaderable,
|
||||
@@ -1530,6 +1571,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
orderKey: data.orderKey.present ? data.orderKey.value : this.orderKey,
|
||||
url: data.url.present ? data.url.value : this.url,
|
||||
title: data.title.present ? data.title.value : this.title,
|
||||
isPrivate: data.isPrivate.present ? data.isPrivate.value : this.isPrivate,
|
||||
isProbablyReaderable: data.isProbablyReaderable.present
|
||||
? data.isProbablyReaderable.value
|
||||
: this.isProbablyReaderable,
|
||||
@@ -1558,6 +1600,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
..write('orderKey: $orderKey, ')
|
||||
..write('url: $url, ')
|
||||
..write('title: $title, ')
|
||||
..write('isPrivate: $isPrivate, ')
|
||||
..write('isProbablyReaderable: $isProbablyReaderable, ')
|
||||
..write('extractedContentMarkdown: $extractedContentMarkdown, ')
|
||||
..write('extractedContentPlain: $extractedContentPlain, ')
|
||||
@@ -1576,6 +1619,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
orderKey,
|
||||
url,
|
||||
title,
|
||||
isPrivate,
|
||||
isProbablyReaderable,
|
||||
extractedContentMarkdown,
|
||||
extractedContentPlain,
|
||||
@@ -1593,6 +1637,7 @@ class TabData extends i0.DataClass implements i0.Insertable<i3.TabData> {
|
||||
other.orderKey == this.orderKey &&
|
||||
other.url == this.url &&
|
||||
other.title == this.title &&
|
||||
other.isPrivate == this.isPrivate &&
|
||||
other.isProbablyReaderable == this.isProbablyReaderable &&
|
||||
other.extractedContentMarkdown == this.extractedContentMarkdown &&
|
||||
other.extractedContentPlain == this.extractedContentPlain &&
|
||||
@@ -1608,6 +1653,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
final i0.Value<String> orderKey;
|
||||
final i0.Value<Uri?> url;
|
||||
final i0.Value<String?> title;
|
||||
final i0.Value<bool?> isPrivate;
|
||||
final i0.Value<bool?> isProbablyReaderable;
|
||||
final i0.Value<String?> extractedContentMarkdown;
|
||||
final i0.Value<String?> extractedContentPlain;
|
||||
@@ -1622,6 +1668,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
this.orderKey = const i0.Value.absent(),
|
||||
this.url = const i0.Value.absent(),
|
||||
this.title = const i0.Value.absent(),
|
||||
this.isPrivate = const i0.Value.absent(),
|
||||
this.isProbablyReaderable = const i0.Value.absent(),
|
||||
this.extractedContentMarkdown = const i0.Value.absent(),
|
||||
this.extractedContentPlain = const i0.Value.absent(),
|
||||
@@ -1637,6 +1684,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
required String orderKey,
|
||||
this.url = const i0.Value.absent(),
|
||||
this.title = const i0.Value.absent(),
|
||||
this.isPrivate = const i0.Value.absent(),
|
||||
this.isProbablyReaderable = const i0.Value.absent(),
|
||||
this.extractedContentMarkdown = const i0.Value.absent(),
|
||||
this.extractedContentPlain = const i0.Value.absent(),
|
||||
@@ -1654,6 +1702,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
i0.Expression<String>? orderKey,
|
||||
i0.Expression<String>? url,
|
||||
i0.Expression<String>? title,
|
||||
i0.Expression<bool>? isPrivate,
|
||||
i0.Expression<bool>? isProbablyReaderable,
|
||||
i0.Expression<String>? extractedContentMarkdown,
|
||||
i0.Expression<String>? extractedContentPlain,
|
||||
@@ -1669,6 +1718,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
if (orderKey != null) 'order_key': orderKey,
|
||||
if (url != null) 'url': url,
|
||||
if (title != null) 'title': title,
|
||||
if (isPrivate != null) 'is_private': isPrivate,
|
||||
if (isProbablyReaderable != null)
|
||||
'is_probably_readerable': isProbablyReaderable,
|
||||
if (extractedContentMarkdown != null)
|
||||
@@ -1690,6 +1740,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
i0.Value<String>? orderKey,
|
||||
i0.Value<Uri?>? url,
|
||||
i0.Value<String?>? title,
|
||||
i0.Value<bool?>? isPrivate,
|
||||
i0.Value<bool?>? isProbablyReaderable,
|
||||
i0.Value<String?>? extractedContentMarkdown,
|
||||
i0.Value<String?>? extractedContentPlain,
|
||||
@@ -1705,6 +1756,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
orderKey: orderKey ?? this.orderKey,
|
||||
url: url ?? this.url,
|
||||
title: title ?? this.title,
|
||||
isPrivate: isPrivate ?? this.isPrivate,
|
||||
isProbablyReaderable: isProbablyReaderable ?? this.isProbablyReaderable,
|
||||
extractedContentMarkdown:
|
||||
extractedContentMarkdown ?? this.extractedContentMarkdown,
|
||||
@@ -1738,6 +1790,9 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
if (title.present) {
|
||||
map['title'] = i0.Variable<String>(title.value);
|
||||
}
|
||||
if (isPrivate.present) {
|
||||
map['is_private'] = i0.Variable<bool>(isPrivate.value);
|
||||
}
|
||||
if (isProbablyReaderable.present) {
|
||||
map['is_probably_readerable'] = i0.Variable<bool>(
|
||||
isProbablyReaderable.value,
|
||||
@@ -1779,6 +1834,7 @@ class TabCompanion extends i0.UpdateCompanion<i3.TabData> {
|
||||
..write('orderKey: $orderKey, ')
|
||||
..write('url: $url, ')
|
||||
..write('title: $title, ')
|
||||
..write('isPrivate: $isPrivate, ')
|
||||
..write('isProbablyReaderable: $isProbablyReaderable, ')
|
||||
..write('extractedContentMarkdown: $extractedContentMarkdown, ')
|
||||
..write('extractedContentPlain: $extractedContentPlain, ')
|
||||
@@ -2161,13 +2217,14 @@ class DefinitionsDrift extends i7.ModularAccessor {
|
||||
|
||||
i0.Selectable<i8.TabQueryResult> queryTabsBasic({required String query}) {
|
||||
return customSelect(
|
||||
'WITH weights AS (SELECT 10.0 AS title_weight, 5.0 AS url_weight) SELECT t.id, t.container_id, t.title, CAST(t.url AS TEXT) AS url, t.url AS clean_url, bm25(tab_fts, weights.title_weight, weights.url_weight) AS weighted_rank FROM tab_fts AS fts INNER JOIN tab AS t ON t."rowid" = fts."rowid" CROSS JOIN weights WHERE fts.title LIKE ?1 OR fts.url LIKE ?1 ORDER BY weighted_rank ASC, t.timestamp DESC',
|
||||
'WITH weights AS (SELECT 10.0 AS title_weight, 5.0 AS url_weight) SELECT t.id, t.container_id, t.is_private, t.title, CAST(t.url AS TEXT) AS url, t.url AS clean_url, bm25(tab_fts, weights.title_weight, weights.url_weight) AS weighted_rank FROM tab_fts AS fts INNER JOIN tab AS t ON t."rowid" = fts."rowid" CROSS JOIN weights WHERE fts.title LIKE ?1 OR fts.url LIKE ?1 ORDER BY weighted_rank ASC, t.timestamp DESC',
|
||||
variables: [i0.Variable<String>(query)],
|
||||
readsFrom: {tab, tabFts},
|
||||
).map(
|
||||
(i0.QueryRow row) => i8.TabQueryResult(
|
||||
id: row.read<String>('id'),
|
||||
containerId: row.readNullable<String>('container_id'),
|
||||
isPrivate: row.readNullable<bool>('is_private'),
|
||||
title: row.readNullable<String>('title'),
|
||||
url: row.readNullable<String>('url'),
|
||||
cleanUrl: i3.Tab.$converterurl.fromSql(
|
||||
@@ -2186,7 +2243,7 @@ class DefinitionsDrift extends i7.ModularAccessor {
|
||||
required String query,
|
||||
}) {
|
||||
return customSelect(
|
||||
'WITH weights AS (SELECT 10.0 AS title_weight, 5.0 AS url_weight, 3.0 AS extracted_weight, 1.0 AS full_weight) SELECT t.id, t.container_id, highlight(tab_fts, 0, ?1, ?2) AS title, highlight(tab_fts, 1, ?1, ?2) AS url, snippet(tab_fts, 2, ?1, ?2, ?3, ?4) AS extracted_content, snippet(tab_fts, 3, ?1, ?2, ?3, ?4) AS full_content, t.url AS clean_url,(bm25(tab_fts, weights.title_weight, weights.url_weight, weights.extracted_weight, weights.full_weight))AS weighted_rank FROM tab_fts(?5)AS fts INNER JOIN tab AS t ON t."rowid" = fts."rowid" CROSS JOIN weights ORDER BY weighted_rank ASC, t.timestamp DESC',
|
||||
'WITH weights AS (SELECT 10.0 AS title_weight, 5.0 AS url_weight, 3.0 AS extracted_weight, 1.0 AS full_weight) SELECT t.id, t.container_id, t.is_private, highlight(tab_fts, 0, ?1, ?2) AS title, highlight(tab_fts, 1, ?1, ?2) AS url, snippet(tab_fts, 2, ?1, ?2, ?3, ?4) AS extracted_content, snippet(tab_fts, 3, ?1, ?2, ?3, ?4) AS full_content, t.url AS clean_url,(bm25(tab_fts, weights.title_weight, weights.url_weight, weights.extracted_weight, weights.full_weight))AS weighted_rank FROM tab_fts(?5)AS fts INNER JOIN tab AS t ON t."rowid" = fts."rowid" CROSS JOIN weights ORDER BY weighted_rank ASC, t.timestamp DESC',
|
||||
variables: [
|
||||
i0.Variable<String>(beforeMatch),
|
||||
i0.Variable<String>(afterMatch),
|
||||
@@ -2199,6 +2256,7 @@ class DefinitionsDrift extends i7.ModularAccessor {
|
||||
(i0.QueryRow row) => i8.TabQueryResult(
|
||||
id: row.read<String>('id'),
|
||||
containerId: row.readNullable<String>('container_id'),
|
||||
isPrivate: row.readNullable<bool>('is_private'),
|
||||
title: row.readNullable<String>('title'),
|
||||
url: row.readNullable<String>('url'),
|
||||
cleanUrl: i3.Tab.$converterurl.fromSql(
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'package:fast_equatable/fast_equatable.dart';
|
||||
class TabQueryResult with FastEquatable {
|
||||
final String id;
|
||||
final String? containerId;
|
||||
final bool? isPrivate;
|
||||
|
||||
final String? title;
|
||||
final Uri? cleanUrl;
|
||||
@@ -35,6 +36,7 @@ class TabQueryResult with FastEquatable {
|
||||
TabQueryResult({
|
||||
required this.id,
|
||||
required this.containerId,
|
||||
required this.isPrivate,
|
||||
required this.title,
|
||||
required this.url,
|
||||
required this.cleanUrl,
|
||||
@@ -47,6 +49,7 @@ class TabQueryResult with FastEquatable {
|
||||
List<Object?> get hashParameters => [
|
||||
id,
|
||||
containerId,
|
||||
isPrivate,
|
||||
title,
|
||||
cleanUrl,
|
||||
url,
|
||||
|
||||
@@ -85,9 +85,7 @@ class ContainerRepository extends _$ContainerRepository {
|
||||
}
|
||||
|
||||
Future<void> deleteContainer(String id) async {
|
||||
await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.closeAllTabsByContainer(id);
|
||||
await ref.read(tabDataRepositoryProvider.notifier).closeContainerTabs(id);
|
||||
|
||||
return ref.read(tabDatabaseProvider).containerDao.deleteContainer(id);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ final class ContainerRepositoryProvider
|
||||
}
|
||||
|
||||
String _$containerRepositoryHash() =>
|
||||
r'8c9252a49a096c29279ad3066f6e5936e15524b2';
|
||||
r'2a377774f9f18604bb90cb1a8eb7cda5f944c601';
|
||||
|
||||
abstract class _$ContainerRepository extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
@@ -73,11 +73,39 @@ class TabDataRepository extends _$TabDataRepository {
|
||||
.assignOrderKey(tabId, orderKey: orderKey);
|
||||
}
|
||||
|
||||
Future<int> closeAllTabsByContainer(String? containerId) async {
|
||||
Future<int> closeAllTabs({
|
||||
bool includeRegular = true,
|
||||
bool includePrivate = true,
|
||||
}) async {
|
||||
final tabIds = await ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.getContainerTabIds(containerId)
|
||||
.getAllTabIds(
|
||||
includeRegular: includeRegular,
|
||||
includePrivate: includePrivate,
|
||||
)
|
||||
.get();
|
||||
|
||||
if (tabIds.isNotEmpty) {
|
||||
await ref.read(tabRepositoryProvider.notifier).closeTabs(tabIds);
|
||||
}
|
||||
|
||||
return tabIds.length;
|
||||
}
|
||||
|
||||
Future<int> closeContainerTabs(
|
||||
String? containerId, {
|
||||
bool includeRegular = true,
|
||||
bool includePrivate = true,
|
||||
}) async {
|
||||
final tabIds = await ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.getContainerTabIds(
|
||||
containerId,
|
||||
includeRegular: includeRegular,
|
||||
includePrivate: includePrivate,
|
||||
)
|
||||
.get();
|
||||
|
||||
if (tabIds.isNotEmpty) {
|
||||
|
||||
@@ -41,7 +41,7 @@ final class TabDataRepositoryProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$tabDataRepositoryHash() => r'3d9149efb88f256cc826eb538f13331b49438a7c';
|
||||
String _$tabDataRepositoryHash() => r'63ab2765ea5be640d6c6a596852fee0df5a958e4';
|
||||
|
||||
abstract class _$TabDataRepository extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
@@ -22,10 +22,15 @@ import 'dart:io';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||
|
||||
Future<void> exitApp(ProviderContainer container) async {
|
||||
logger.i('Preparing exit');
|
||||
|
||||
await container
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.closeAllTabs(includeRegular: false);
|
||||
|
||||
await SystemNavigator.pop();
|
||||
logger.i('SystemNavigator popped');
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// dart format width=80
|
||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/internal/migrations.dart';
|
||||
import 'schema_v2.dart' as v2;
|
||||
import 'schema_v3.dart' as v3;
|
||||
|
||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
@override
|
||||
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
||||
switch (version) {
|
||||
case 2:
|
||||
return v2.DatabaseAtV2(db);
|
||||
case 3:
|
||||
return v3.DatabaseAtV3(db);
|
||||
default:
|
||||
throw MissingSchemaException(version, versions);
|
||||
}
|
||||
}
|
||||
|
||||
static const versions = const [2, 3];
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
// 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/tabs/data/database/database.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'generated/schema.dart';
|
||||
|
||||
import 'generated/schema_v2.dart' as v2;
|
||||
import 'generated/schema_v3.dart' as v3;
|
||||
|
||||
void main() {
|
||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||
late SchemaVerifier verifier;
|
||||
|
||||
setUpAll(() {
|
||||
verifier = SchemaVerifier(GeneratedHelper());
|
||||
});
|
||||
|
||||
group('simple database migrations', () {
|
||||
// These simple tests verify all possible schema updates with a simple (no
|
||||
// data) migration. This is a quick way to ensure that written database
|
||||
// migrations properly alter the schema.
|
||||
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 = TabDatabase(schema.newConnection());
|
||||
await verifier.migrateAndValidate(db, toVersion);
|
||||
await db.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// The following template shows how to write tests ensuring your migrations
|
||||
// preserve existing data.
|
||||
// Testing this can be useful for migrations that change existing columns
|
||||
// (e.g. by alterating their type or constraints). Migrations that only add
|
||||
// tables or columns typically don't need these advanced tests. For more
|
||||
// information, see https://drift.simonbinder.eu/migrations/tests/#verifying-data-integrity
|
||||
// TODO: This generated template shows how these tests could be written. Adopt
|
||||
// it to your own needs when testing migrations with data integrity.
|
||||
test('migration from v2 to v3 does not corrupt data', () async {
|
||||
// Add data to insert into the old database, and the expected rows after the
|
||||
// migration.
|
||||
// TODO: Fill these lists
|
||||
final oldContainerData = <v2.ContainerData>[];
|
||||
final expectedNewContainerData = <v3.ContainerData>[];
|
||||
|
||||
final oldTabData = <v2.TabData>[];
|
||||
final expectedNewTabData = <v3.TabData>[];
|
||||
|
||||
final oldTabFtsData = <v2.TabFtsData>[];
|
||||
final expectedNewTabFtsData = <v3.TabFtsData>[];
|
||||
|
||||
await verifier.testWithDataIntegrity(
|
||||
oldVersion: 2,
|
||||
newVersion: 3,
|
||||
createOld: v2.DatabaseAtV2.new,
|
||||
createNew: v3.DatabaseAtV3.new,
|
||||
openTestedDatabase: TabDatabase.new,
|
||||
createItems: (batch, oldDb) {
|
||||
batch.insertAll(oldDb.container, oldContainerData);
|
||||
batch.insertAll(oldDb.tab, oldTabData);
|
||||
batch.insertAll(oldDb.tabFts, oldTabFtsData);
|
||||
},
|
||||
validateItems: (newDb) async {
|
||||
expect(
|
||||
expectedNewContainerData,
|
||||
await newDb.select(newDb.container).get(),
|
||||
);
|
||||
expect(expectedNewTabData, await newDb.select(newDb.tab).get());
|
||||
expect(expectedNewTabFtsData, await newDb.select(newDb.tabFts).get());
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user