added feature to configure contextual tab bar buttons

This commit is contained in:
Fabian Freund
2026-03-11 10:44:56 +01:00
parent 7121b1c236
commit 4f2145dfc2
34 changed files with 4331 additions and 313 deletions
@@ -0,0 +1,134 @@
/*
* 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/user/data/database/daos/toolbar_button_config.drift.dart';
import 'package:weblibre/features/user/data/database/database.dart';
import 'package:weblibre/features/user/data/database/definitions.drift.dart';
@DriftAccessor()
class ToolbarButtonConfigDao extends DatabaseAccessor<UserDatabase>
with $ToolbarButtonConfigDaoMixin {
ToolbarButtonConfigDao(super.attachedDatabase);
Selectable<ToolbarButtonConfig> selectAll() =>
db.toolbarButtonConfigs.select()
..orderBy([(t) => OrderingTerm.asc(t.orderKey)]);
Stream<List<ToolbarButtonConfig>> watchAll() => selectAll().watch();
Future<List<ToolbarButtonConfig>> getAll() => selectAll().get();
Future<void> upsert(ToolbarButtonConfig config) =>
into(db.toolbarButtonConfigs).insertOnConflictUpdate(config);
Future<void> assignOrderKey(String buttonId, {required String orderKey}) =>
(update(db.toolbarButtonConfigs)
..where((t) => t.buttonId.equals(buttonId)))
.write(ToolbarButtonConfigsCompanion(orderKey: Value(orderKey)));
Future<void> assignVisibility(String buttonId, {required bool visible}) =>
(update(db.toolbarButtonConfigs)
..where((t) => t.buttonId.equals(buttonId)))
.write(ToolbarButtonConfigsCompanion(isVisible: Value(visible)));
Future<void> assignFallback(String buttonId, String? fallbackId) =>
(update(db.toolbarButtonConfigs)
..where((t) => t.buttonId.equals(buttonId)))
.write(ToolbarButtonConfigsCompanion(fallbackId: Value(fallbackId)));
Future<void> replaceAll(List<ToolbarButtonConfig> configs) =>
transaction(() async {
await delete(db.toolbarButtonConfigs).go();
await _insertWithDeferredFallbacks(configs);
});
Future<void> seedMissing(
List<({String buttonId, bool defaultVisible, String? defaultFallback})>
defaults,
) async {
final existing = await getAll();
final existingIds = {for (final r in existing) r.buttonId};
final missing = defaults
.where((d) => !existingIds.contains(d.buttonId))
.toList();
if (missing.isEmpty) return;
await transaction(() async {
final inserted = <ToolbarButtonConfig>[];
for (final def in missing) {
final orderKey = await generateTrailingOrderKey().getSingle();
inserted.add(
ToolbarButtonConfig(
buttonId: def.buttonId,
orderKey: orderKey,
isVisible: def.defaultVisible,
fallbackId: def.defaultFallback,
),
);
await into(db.toolbarButtonConfigs).insert(
ToolbarButtonConfig(
buttonId: def.buttonId,
orderKey: orderKey,
isVisible: def.defaultVisible,
),
);
}
await _assignFallbacks(inserted);
});
}
SingleSelectable<String> generateLeadingOrderKey({int bucket = 0}) =>
db.definitionsDrift.toolbarLeadingOrderKey(bucket: bucket);
SingleSelectable<String> generateTrailingOrderKey({int bucket = 0}) =>
db.definitionsDrift.toolbarTrailingOrderKey(bucket: bucket);
SingleOrNullSelectable<String> generateOrderKeyAfterButtonId(
String buttonId,
) => db.definitionsDrift.toolbarOrderKeyAfterButton(buttonId: buttonId);
SingleSelectable<String> generateOrderKeyBeforeButtonId(String buttonId) =>
db.definitionsDrift.toolbarOrderKeyBeforeButton(buttonId: buttonId);
Future<void> _insertWithDeferredFallbacks(
List<ToolbarButtonConfig> configs,
) async {
for (final config in configs) {
await into(db.toolbarButtonConfigs).insert(
ToolbarButtonConfig(
buttonId: config.buttonId,
orderKey: config.orderKey,
isVisible: config.isVisible,
),
);
}
await _assignFallbacks(configs);
}
Future<void> _assignFallbacks(List<ToolbarButtonConfig> configs) async {
for (final config in configs) {
if (config.fallbackId == null) continue;
await assignFallback(config.buttonId, config.fallbackId);
}
}
}
@@ -0,0 +1,14 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:weblibre/features/user/data/database/database.dart' as i1;
mixin $ToolbarButtonConfigDaoMixin on i0.DatabaseAccessor<i1.UserDatabase> {
ToolbarButtonConfigDaoManager get managers =>
ToolbarButtonConfigDaoManager(this);
}
class ToolbarButtonConfigDaoManager {
final $ToolbarButtonConfigDaoMixin _db;
ToolbarButtonConfigDaoManager(this._db);
}
@@ -24,16 +24,17 @@ import 'package:flutter/foundation.dart';
import 'package:weblibre/features/user/data/database/daos/cache.dart';
import 'package:weblibre/features/user/data/database/daos/onboarding.dart';
import 'package:weblibre/features/user/data/database/daos/setting.dart';
import 'package:weblibre/features/user/data/database/daos/toolbar_button_config.dart';
import 'package:weblibre/features/user/data/database/database.drift.dart';
import 'package:weblibre/features/user/data/database/database.steps.dart';
@DriftDatabase(
include: {'definitions.drift'},
daos: [SettingDao, CacheDao, OnboardingDao],
daos: [SettingDao, CacheDao, OnboardingDao, ToolbarButtonConfigDao],
)
class UserDatabase extends $UserDatabase {
@override
final int schemaVersion = 2;
final int schemaVersion = 3;
@override
MigrationStrategy get migration => MigrationStrategy(
@@ -45,6 +46,8 @@ class UserDatabase extends $UserDatabase {
}
await customStatement('PRAGMA foreign_keys = ON;');
await onAfterOpen?.call(this);
},
onUpgrade: (m, from, to) async {
// Following the advice from https://drift.simonbinder.eu/Migrations/api/#general-tips
@@ -73,11 +76,17 @@ class UserDatabase extends $UserDatabase {
},
);
UserDatabase(super.e);
UserDatabase(super.e, {this.onAfterOpen});
final Future<void> Function(UserDatabase db)? onAfterOpen;
static final _upgrade = migrationSteps(
from1To2: (m, schema) async {
await m.createTable(schema.riverpod);
},
from2To3: (m, schema) async {
await m.createTable(schema.toolbarButtonConfigs);
await m.createIndex(schema.idxToolbarOrderKey);
},
);
}
@@ -8,8 +8,10 @@ import 'package:weblibre/features/user/data/database/database.dart' as i3;
import 'package:weblibre/features/user/data/database/daos/cache.dart' as i4;
import 'package:weblibre/features/user/data/database/daos/onboarding.dart'
as i5;
import 'package:drift/internal/modular.dart' as i6;
import 'package:sqlite3/common.dart' as i7;
import 'package:weblibre/features/user/data/database/daos/toolbar_button_config.dart'
as i6;
import 'package:drift/internal/modular.dart' as i7;
import 'package:sqlite3/common.dart' as i8;
abstract class $UserDatabase extends i0.GeneratedDatabase {
$UserDatabase(i0.QueryExecutor e) : super(e);
@@ -18,12 +20,16 @@ abstract class $UserDatabase extends i0.GeneratedDatabase {
late final i1.IconCache iconCache = i1.IconCache(this);
late final i1.Onboarding onboarding = i1.Onboarding(this);
late final i1.Riverpod riverpod = i1.Riverpod(this);
late final i1.ToolbarButtonConfigs toolbarButtonConfigs =
i1.ToolbarButtonConfigs(this);
late final i2.SettingDao settingDao = i2.SettingDao(this as i3.UserDatabase);
late final i4.CacheDao cacheDao = i4.CacheDao(this as i3.UserDatabase);
late final i5.OnboardingDao onboardingDao = i5.OnboardingDao(
this as i3.UserDatabase,
);
i1.DefinitionsDrift get definitionsDrift => i6.ReadDatabaseContainer(
late final i6.ToolbarButtonConfigDao toolbarButtonConfigDao =
i6.ToolbarButtonConfigDao(this as i3.UserDatabase);
i1.DefinitionsDrift get definitionsDrift => i7.ReadDatabaseContainer(
this,
).accessor<i1.DefinitionsDrift>(i1.DefinitionsDrift.new);
@override
@@ -35,6 +41,8 @@ abstract class $UserDatabase extends i0.GeneratedDatabase {
iconCache,
onboarding,
riverpod,
toolbarButtonConfigs,
i1.idxToolbarOrderKey,
];
}
@@ -49,9 +57,11 @@ class $UserDatabaseManager {
i1.$OnboardingTableManager(_db, _db.onboarding);
i1.$RiverpodTableManager get riverpod =>
i1.$RiverpodTableManager(_db, _db.riverpod);
i1.$ToolbarButtonConfigsTableManager get toolbarButtonConfigs =>
i1.$ToolbarButtonConfigsTableManager(_db, _db.toolbarButtonConfigs);
}
extension DefineFunctions on i7.CommonDatabase {
extension DefineFunctions on i8.CommonDatabase {
void defineFunctions({
required String Function(int, String?) lexoRankNext,
required String Function(int, String?) lexoRankPrevious,
@@ -60,7 +70,7 @@ extension DefineFunctions on i7.CommonDatabase {
}) {
createFunction(
functionName: 'lexo_rank_next',
argumentCount: const i7.AllowedArgumentCount(2),
argumentCount: const i8.AllowedArgumentCount(2),
function: (args) {
final arg0 = args[0] as int;
final arg1 = args[1] as String?;
@@ -69,7 +79,7 @@ extension DefineFunctions on i7.CommonDatabase {
);
createFunction(
functionName: 'lexo_rank_previous',
argumentCount: const i7.AllowedArgumentCount(2),
argumentCount: const i8.AllowedArgumentCount(2),
function: (args) {
final arg0 = args[0] as int;
final arg1 = args[1] as String?;
@@ -78,7 +88,7 @@ extension DefineFunctions on i7.CommonDatabase {
);
createFunction(
functionName: 'lexo_rank_reorder_after',
argumentCount: const i7.AllowedArgumentCount(2),
argumentCount: const i8.AllowedArgumentCount(2),
function: (args) {
final arg0 = args[0] as String?;
final arg1 = args[1] as String?;
@@ -87,7 +97,7 @@ extension DefineFunctions on i7.CommonDatabase {
);
createFunction(
functionName: 'lexo_rank_reorder_before',
argumentCount: const i7.AllowedArgumentCount(2),
argumentCount: const i8.AllowedArgumentCount(2),
function: (args) {
final arg0 = args[0] as String?;
final arg1 = args[1] as String?;
@@ -192,8 +192,128 @@ i1.GeneratedColumn<String> _column_10(String aliasedName) =>
type: i1.DriftSqlType.string,
$customConstraints: '',
);
final class Schema3 extends i0.VersionedSchema {
Schema3({required super.database}) : super(version: 3);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
setting,
iconCache,
onboarding,
riverpod,
toolbarButtonConfigs,
idxToolbarOrderKey,
];
late final Shape0 setting = Shape0(
source: i0.VersionedTable(
entityName: 'setting',
withoutRowId: false,
isStrict: true,
tableConstraints: [],
columns: [_column_0, _column_1, _column_2],
attachedDatabase: database,
),
alias: null,
);
late final Shape1 iconCache = Shape1(
source: i0.VersionedTable(
entityName: 'icon_cache',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_3, _column_4, _column_5],
attachedDatabase: database,
),
alias: null,
);
late final Shape2 onboarding = Shape2(
source: i0.VersionedTable(
entityName: 'onboarding',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_6, _column_7],
attachedDatabase: database,
),
alias: null,
);
late final Shape3 riverpod = Shape3(
source: i0.VersionedTable(
entityName: 'riverpod',
withoutRowId: true,
isStrict: false,
tableConstraints: [],
columns: [_column_0, _column_8, _column_9, _column_10],
attachedDatabase: database,
),
alias: null,
);
late final Shape4 toolbarButtonConfigs = Shape4(
source: i0.VersionedTable(
entityName: 'toolbar_button_configs',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_11, _column_12, _column_13, _column_14],
attachedDatabase: database,
),
alias: null,
);
final i1.Index idxToolbarOrderKey = i1.Index(
'idx_toolbar_order_key',
'CREATE INDEX idx_toolbar_order_key ON toolbar_button_configs (order_key)',
);
}
class Shape4 extends i0.VersionedTable {
Shape4({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get buttonId =>
columnsByName['button_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get orderKey =>
columnsByName['order_key']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get isVisible =>
columnsByName['is_visible']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get fallbackId =>
columnsByName['fallback_id']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<String> _column_11(String aliasedName) =>
i1.GeneratedColumn<String>(
'button_id',
aliasedName,
false,
type: i1.DriftSqlType.string,
$customConstraints: 'NOT NULL PRIMARY KEY',
);
i1.GeneratedColumn<String> _column_12(String aliasedName) =>
i1.GeneratedColumn<String>(
'order_key',
aliasedName,
false,
type: i1.DriftSqlType.string,
$customConstraints: 'NOT NULL',
);
i1.GeneratedColumn<int> _column_13(String aliasedName) =>
i1.GeneratedColumn<int>(
'is_visible',
aliasedName,
false,
type: i1.DriftSqlType.int,
$customConstraints: 'NOT NULL DEFAULT TRUE',
defaultValue: const i1.CustomExpression('TRUE'),
);
i1.GeneratedColumn<String> _column_14(String aliasedName) =>
i1.GeneratedColumn<String>(
'fallback_id',
aliasedName,
true,
type: i1.DriftSqlType.string,
$customConstraints:
'REFERENCES toolbar_button_configs(button_id)ON DELETE SET NULL',
);
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
}) {
return (currentVersion, database) async {
switch (currentVersion) {
@@ -202,6 +322,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema);
await from1To2(migrator, schema);
return 2;
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');
}
@@ -210,6 +335,7 @@ i0.MigrationStepWithVersion migrationSteps({
i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
}) => i0.VersionedSchema.stepByStepHelper(
step: migrationSteps(from1To2: from1To2),
step: migrationSteps(from1To2: from1To2, from2To3: from2To3),
);
@@ -22,6 +22,61 @@ CREATE TABLE riverpod (
destroyKey TEXT
) WITHOUT ROWID;
CREATE TABLE toolbar_button_configs (
button_id TEXT NOT NULL PRIMARY KEY,
order_key TEXT NOT NULL,
is_visible BOOLEAN NOT NULL DEFAULT TRUE,
fallback_id TEXT REFERENCES toolbar_button_configs(button_id) ON DELETE SET NULL
);
CREATE INDEX idx_toolbar_order_key ON toolbar_button_configs(order_key);
toolbarLeadingOrderKey(:bucket AS INTEGER):
SELECT lexo_rank_previous(
:bucket,
(
SELECT order_key
FROM toolbar_button_configs
ORDER BY order_key
LIMIT 1
)
);
toolbarTrailingOrderKey(:bucket AS INTEGER):
SELECT lexo_rank_next(
:bucket,
(
SELECT order_key
FROM toolbar_button_configs
ORDER BY order_key DESC
LIMIT 1
)
);
toolbarOrderKeyAfterButton(:button_id AS TEXT):
WITH ordered_table AS (
SELECT
button_id,
order_key,
LEAD(order_key) OVER (ORDER BY order_key) AS next_order_key
FROM toolbar_button_configs
)
SELECT lexo_rank_reorder_after(order_key, next_order_key)
FROM ordered_table
WHERE button_id = :button_id;
toolbarOrderKeyBeforeButton(:button_id AS TEXT):
WITH ordered_table AS (
SELECT
button_id,
order_key,
LAG(order_key) OVER (ORDER BY order_key) AS prev_order_key
FROM toolbar_button_configs
)
SELECT lexo_rank_reorder_before(order_key, prev_order_key)
FROM ordered_table
WHERE button_id = :button_id;
evictCacheEntries:
DELETE FROM icon_cache
WHERE rowid IN (
@@ -670,6 +670,199 @@ typedef $RiverpodProcessedTableManager =
i1.RiverpodData,
i0.PrefetchHooks Function()
>;
typedef $ToolbarButtonConfigsCreateCompanionBuilder =
i1.ToolbarButtonConfigsCompanion Function({
required String buttonId,
required String orderKey,
i0.Value<bool> isVisible,
i0.Value<String?> fallbackId,
i0.Value<int> rowid,
});
typedef $ToolbarButtonConfigsUpdateCompanionBuilder =
i1.ToolbarButtonConfigsCompanion Function({
i0.Value<String> buttonId,
i0.Value<String> orderKey,
i0.Value<bool> isVisible,
i0.Value<String?> fallbackId,
i0.Value<int> rowid,
});
class $ToolbarButtonConfigsFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.ToolbarButtonConfigs> {
$ToolbarButtonConfigsFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get buttonId => $composableBuilder(
column: $table.buttonId,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get orderKey => $composableBuilder(
column: $table.orderKey,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<bool> get isVisible => $composableBuilder(
column: $table.isVisible,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get fallbackId => $composableBuilder(
column: $table.fallbackId,
builder: (column) => i0.ColumnFilters(column),
);
}
class $ToolbarButtonConfigsOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.ToolbarButtonConfigs> {
$ToolbarButtonConfigsOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get buttonId => $composableBuilder(
column: $table.buttonId,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get orderKey => $composableBuilder(
column: $table.orderKey,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<bool> get isVisible => $composableBuilder(
column: $table.isVisible,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get fallbackId => $composableBuilder(
column: $table.fallbackId,
builder: (column) => i0.ColumnOrderings(column),
);
}
class $ToolbarButtonConfigsAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.ToolbarButtonConfigs> {
$ToolbarButtonConfigsAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get buttonId =>
$composableBuilder(column: $table.buttonId, builder: (column) => column);
i0.GeneratedColumn<String> get orderKey =>
$composableBuilder(column: $table.orderKey, builder: (column) => column);
i0.GeneratedColumn<bool> get isVisible =>
$composableBuilder(column: $table.isVisible, builder: (column) => column);
i0.GeneratedColumn<String> get fallbackId => $composableBuilder(
column: $table.fallbackId,
builder: (column) => column,
);
}
class $ToolbarButtonConfigsTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.ToolbarButtonConfigs,
i1.ToolbarButtonConfig,
i1.$ToolbarButtonConfigsFilterComposer,
i1.$ToolbarButtonConfigsOrderingComposer,
i1.$ToolbarButtonConfigsAnnotationComposer,
$ToolbarButtonConfigsCreateCompanionBuilder,
$ToolbarButtonConfigsUpdateCompanionBuilder,
(
i1.ToolbarButtonConfig,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.ToolbarButtonConfigs,
i1.ToolbarButtonConfig
>,
),
i1.ToolbarButtonConfig,
i0.PrefetchHooks Function()
> {
$ToolbarButtonConfigsTableManager(
i0.GeneratedDatabase db,
i1.ToolbarButtonConfigs table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$ToolbarButtonConfigsFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
i1.$ToolbarButtonConfigsOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () => i1
.$ToolbarButtonConfigsAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
i0.Value<String> buttonId = const i0.Value.absent(),
i0.Value<String> orderKey = const i0.Value.absent(),
i0.Value<bool> isVisible = const i0.Value.absent(),
i0.Value<String?> fallbackId = const i0.Value.absent(),
i0.Value<int> rowid = const i0.Value.absent(),
}) => i1.ToolbarButtonConfigsCompanion(
buttonId: buttonId,
orderKey: orderKey,
isVisible: isVisible,
fallbackId: fallbackId,
rowid: rowid,
),
createCompanionCallback:
({
required String buttonId,
required String orderKey,
i0.Value<bool> isVisible = const i0.Value.absent(),
i0.Value<String?> fallbackId = const i0.Value.absent(),
i0.Value<int> rowid = const i0.Value.absent(),
}) => i1.ToolbarButtonConfigsCompanion.insert(
buttonId: buttonId,
orderKey: orderKey,
isVisible: isVisible,
fallbackId: fallbackId,
rowid: rowid,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $ToolbarButtonConfigsProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.ToolbarButtonConfigs,
i1.ToolbarButtonConfig,
i1.$ToolbarButtonConfigsFilterComposer,
i1.$ToolbarButtonConfigsOrderingComposer,
i1.$ToolbarButtonConfigsAnnotationComposer,
$ToolbarButtonConfigsCreateCompanionBuilder,
$ToolbarButtonConfigsUpdateCompanionBuilder,
(
i1.ToolbarButtonConfig,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.ToolbarButtonConfigs,
i1.ToolbarButtonConfig
>,
),
i1.ToolbarButtonConfig,
i0.PrefetchHooks Function()
>;
class Setting extends i0.Table with i0.TableInfo<Setting, i1.SettingData> {
@override
@@ -1558,8 +1751,313 @@ class RiverpodCompanion extends i0.UpdateCompanion<i1.RiverpodData> {
}
}
class ToolbarButtonConfigs extends i0.Table
with i0.TableInfo<ToolbarButtonConfigs, i1.ToolbarButtonConfig> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
ToolbarButtonConfigs(this.attachedDatabase, [this._alias]);
late final i0.GeneratedColumn<String> buttonId = i0.GeneratedColumn<String>(
'button_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL PRIMARY KEY',
);
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<bool> isVisible = i0.GeneratedColumn<bool>(
'is_visible',
aliasedName,
false,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL DEFAULT TRUE',
defaultValue: const i0.CustomExpression('TRUE'),
);
late final i0.GeneratedColumn<String> fallbackId = i0.GeneratedColumn<String>(
'fallback_id',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
$customConstraints:
'REFERENCES toolbar_button_configs(button_id)ON DELETE SET NULL',
);
@override
List<i0.GeneratedColumn> get $columns => [
buttonId,
orderKey,
isVisible,
fallbackId,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'toolbar_button_configs';
@override
Set<i0.GeneratedColumn> get $primaryKey => {buttonId};
@override
i1.ToolbarButtonConfig map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.ToolbarButtonConfig(
buttonId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}button_id'],
)!,
orderKey: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}order_key'],
)!,
isVisible: attachedDatabase.typeMapping.read(
i0.DriftSqlType.bool,
data['${effectivePrefix}is_visible'],
)!,
fallbackId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}fallback_id'],
),
);
}
@override
ToolbarButtonConfigs createAlias(String alias) {
return ToolbarButtonConfigs(attachedDatabase, alias);
}
@override
bool get dontWriteConstraints => true;
}
class ToolbarButtonConfig extends i0.DataClass
implements i0.Insertable<i1.ToolbarButtonConfig> {
final String buttonId;
final String orderKey;
final bool isVisible;
final String? fallbackId;
const ToolbarButtonConfig({
required this.buttonId,
required this.orderKey,
required this.isVisible,
this.fallbackId,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['button_id'] = i0.Variable<String>(buttonId);
map['order_key'] = i0.Variable<String>(orderKey);
map['is_visible'] = i0.Variable<bool>(isVisible);
if (!nullToAbsent || fallbackId != null) {
map['fallback_id'] = i0.Variable<String>(fallbackId);
}
return map;
}
factory ToolbarButtonConfig.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return ToolbarButtonConfig(
buttonId: serializer.fromJson<String>(json['button_id']),
orderKey: serializer.fromJson<String>(json['order_key']),
isVisible: serializer.fromJson<bool>(json['is_visible']),
fallbackId: serializer.fromJson<String?>(json['fallback_id']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'button_id': serializer.toJson<String>(buttonId),
'order_key': serializer.toJson<String>(orderKey),
'is_visible': serializer.toJson<bool>(isVisible),
'fallback_id': serializer.toJson<String?>(fallbackId),
};
}
i1.ToolbarButtonConfig copyWith({
String? buttonId,
String? orderKey,
bool? isVisible,
i0.Value<String?> fallbackId = const i0.Value.absent(),
}) => i1.ToolbarButtonConfig(
buttonId: buttonId ?? this.buttonId,
orderKey: orderKey ?? this.orderKey,
isVisible: isVisible ?? this.isVisible,
fallbackId: fallbackId.present ? fallbackId.value : this.fallbackId,
);
ToolbarButtonConfig copyWithCompanion(i1.ToolbarButtonConfigsCompanion data) {
return ToolbarButtonConfig(
buttonId: data.buttonId.present ? data.buttonId.value : this.buttonId,
orderKey: data.orderKey.present ? data.orderKey.value : this.orderKey,
isVisible: data.isVisible.present ? data.isVisible.value : this.isVisible,
fallbackId: data.fallbackId.present
? data.fallbackId.value
: this.fallbackId,
);
}
@override
String toString() {
return (StringBuffer('ToolbarButtonConfig(')
..write('buttonId: $buttonId, ')
..write('orderKey: $orderKey, ')
..write('isVisible: $isVisible, ')
..write('fallbackId: $fallbackId')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(buttonId, orderKey, isVisible, fallbackId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.ToolbarButtonConfig &&
other.buttonId == this.buttonId &&
other.orderKey == this.orderKey &&
other.isVisible == this.isVisible &&
other.fallbackId == this.fallbackId);
}
class ToolbarButtonConfigsCompanion
extends i0.UpdateCompanion<i1.ToolbarButtonConfig> {
final i0.Value<String> buttonId;
final i0.Value<String> orderKey;
final i0.Value<bool> isVisible;
final i0.Value<String?> fallbackId;
final i0.Value<int> rowid;
const ToolbarButtonConfigsCompanion({
this.buttonId = const i0.Value.absent(),
this.orderKey = const i0.Value.absent(),
this.isVisible = const i0.Value.absent(),
this.fallbackId = const i0.Value.absent(),
this.rowid = const i0.Value.absent(),
});
ToolbarButtonConfigsCompanion.insert({
required String buttonId,
required String orderKey,
this.isVisible = const i0.Value.absent(),
this.fallbackId = const i0.Value.absent(),
this.rowid = const i0.Value.absent(),
}) : buttonId = i0.Value(buttonId),
orderKey = i0.Value(orderKey);
static i0.Insertable<i1.ToolbarButtonConfig> custom({
i0.Expression<String>? buttonId,
i0.Expression<String>? orderKey,
i0.Expression<bool>? isVisible,
i0.Expression<String>? fallbackId,
i0.Expression<int>? rowid,
}) {
return i0.RawValuesInsertable({
if (buttonId != null) 'button_id': buttonId,
if (orderKey != null) 'order_key': orderKey,
if (isVisible != null) 'is_visible': isVisible,
if (fallbackId != null) 'fallback_id': fallbackId,
if (rowid != null) 'rowid': rowid,
});
}
i1.ToolbarButtonConfigsCompanion copyWith({
i0.Value<String>? buttonId,
i0.Value<String>? orderKey,
i0.Value<bool>? isVisible,
i0.Value<String?>? fallbackId,
i0.Value<int>? rowid,
}) {
return i1.ToolbarButtonConfigsCompanion(
buttonId: buttonId ?? this.buttonId,
orderKey: orderKey ?? this.orderKey,
isVisible: isVisible ?? this.isVisible,
fallbackId: fallbackId ?? this.fallbackId,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (buttonId.present) {
map['button_id'] = i0.Variable<String>(buttonId.value);
}
if (orderKey.present) {
map['order_key'] = i0.Variable<String>(orderKey.value);
}
if (isVisible.present) {
map['is_visible'] = i0.Variable<bool>(isVisible.value);
}
if (fallbackId.present) {
map['fallback_id'] = i0.Variable<String>(fallbackId.value);
}
if (rowid.present) {
map['rowid'] = i0.Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('ToolbarButtonConfigsCompanion(')
..write('buttonId: $buttonId, ')
..write('orderKey: $orderKey, ')
..write('isVisible: $isVisible, ')
..write('fallbackId: $fallbackId, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
i0.Index get idxToolbarOrderKey => i0.Index(
'idx_toolbar_order_key',
'CREATE INDEX idx_toolbar_order_key ON toolbar_button_configs (order_key)',
);
class DefinitionsDrift extends i3.ModularAccessor {
DefinitionsDrift(i0.GeneratedDatabase db) : super(db);
i0.Selectable<String> toolbarLeadingOrderKey({required int bucket}) {
return customSelect(
'SELECT lexo_rank_previous(?1, (SELECT order_key FROM toolbar_button_configs ORDER BY order_key LIMIT 1)) AS _c0',
variables: [i0.Variable<int>(bucket)],
readsFrom: {toolbarButtonConfigs},
).map((i0.QueryRow row) => row.read<String>('_c0'));
}
i0.Selectable<String> toolbarTrailingOrderKey({required int bucket}) {
return customSelect(
'SELECT lexo_rank_next(?1, (SELECT order_key FROM toolbar_button_configs ORDER BY order_key DESC LIMIT 1)) AS _c0',
variables: [i0.Variable<int>(bucket)],
readsFrom: {toolbarButtonConfigs},
).map((i0.QueryRow row) => row.read<String>('_c0'));
}
i0.Selectable<String> toolbarOrderKeyAfterButton({required String buttonId}) {
return customSelect(
'WITH ordered_table AS (SELECT button_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 toolbar_button_configs) SELECT lexo_rank_reorder_after(order_key, next_order_key) AS _c0 FROM ordered_table WHERE button_id = ?1',
variables: [i0.Variable<String>(buttonId)],
readsFrom: {toolbarButtonConfigs},
).map((i0.QueryRow row) => row.read<String>('_c0'));
}
i0.Selectable<String> toolbarOrderKeyBeforeButton({
required String buttonId,
}) {
return customSelect(
'WITH ordered_table AS (SELECT button_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 toolbar_button_configs) SELECT lexo_rank_reorder_before(order_key, prev_order_key) AS _c0 FROM ordered_table WHERE button_id = ?1',
variables: [i0.Variable<String>(buttonId)],
readsFrom: {toolbarButtonConfigs},
).map((i0.QueryRow row) => row.read<String>('_c0'));
}
Future<int> evictCacheEntries({required int limit}) {
return customUpdate(
'DELETE FROM icon_cache WHERE "rowid" IN (SELECT "rowid" FROM icon_cache ORDER BY fetch_date DESC LIMIT -1 OFFSET ?1)',
@@ -1569,6 +2067,9 @@ class DefinitionsDrift extends i3.ModularAccessor {
);
}
i1.ToolbarButtonConfigs get toolbarButtonConfigs => i3.ReadDatabaseContainer(
attachedDatabase,
).resultSet<i1.ToolbarButtonConfigs>('toolbar_button_configs');
i1.IconCache get iconCache => i3.ReadDatabaseContainer(
attachedDatabase,
).resultSet<i1.IconCache>('icon_cache');
+7 -1
View File
@@ -27,6 +27,7 @@ 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/user/data/database/database.dart';
import 'package:weblibre/features/user/data/database/riverpod_storage.dart';
@@ -43,7 +44,12 @@ UserDatabase userDatabase(Ref ref) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
return NativeDatabase.createInBackground(file);
return NativeDatabase.createInBackground(
file,
setup: (database) {
registerLexorankFunctions(database);
},
);
}),
);
+1 -1
View File
@@ -48,7 +48,7 @@ final class UserDatabaseProvider
}
}
String _$userDatabaseHash() => r'8cc40197f9dbb85ccd8263984218753d3690954b';
String _$userDatabaseHash() => r'1d14f39dd506ec8890a0a15cc2546b3294a34a1c';
@ProviderFor(riverpodDatabaseStorage)
final riverpodDatabaseStorageProvider = RiverpodDatabaseStorageProvider._();