swith to bang composite key
This commit is contained in:
@@ -48,8 +48,12 @@ class BangDao extends DatabaseAccessor<BangDatabase> with $BangDaoMixin {
|
||||
);
|
||||
}
|
||||
|
||||
SingleOrNullSelectable<BangData> getBangData(String trigger) {
|
||||
return select(db.bangDataView)..where((t) => t.trigger.equals(trigger));
|
||||
SingleOrNullSelectable<BangData> getBangData(
|
||||
BangGroup group,
|
||||
String trigger,
|
||||
) {
|
||||
return select(db.bangDataView)
|
||||
..where((t) => t.group.equalsValue(group) & t.trigger.equals(trigger));
|
||||
}
|
||||
|
||||
Selectable<BangData> getBangDataList({
|
||||
@@ -102,10 +106,11 @@ class BangDao extends DatabaseAccessor<BangDatabase> with $BangDaoMixin {
|
||||
return selectable;
|
||||
}
|
||||
|
||||
Future<int> increaseBangFrequency(String trigger) {
|
||||
Future<int> increaseBangFrequency(BangGroup group, String trigger) {
|
||||
return db.bangFrequency.insertOne(
|
||||
BangFrequencyCompanion.insert(
|
||||
trigger: trigger,
|
||||
group: group,
|
||||
frequency: 1,
|
||||
lastUsed: DateTime.now(),
|
||||
),
|
||||
@@ -130,17 +135,23 @@ class BangDao extends DatabaseAccessor<BangDatabase> with $BangDaoMixin {
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> addSearchEntry(String trigger, String searchQuery) {
|
||||
Future<int> addSearchEntry(
|
||||
BangGroup group,
|
||||
String trigger,
|
||||
String searchQuery,
|
||||
) {
|
||||
return db.bangHistory.insertOne(
|
||||
BangHistoryCompanion.insert(
|
||||
searchQuery: searchQuery,
|
||||
trigger: trigger,
|
||||
group: group,
|
||||
searchDate: DateTime.now(),
|
||||
),
|
||||
onConflict: DoUpdate(
|
||||
target: [db.bangHistory.searchQuery],
|
||||
(old) => BangHistoryCompanion(
|
||||
trigger: Value(trigger),
|
||||
group: Value(group),
|
||||
searchDate: Value(DateTime.now()),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -55,8 +55,9 @@ class SyncDao extends DatabaseAccessor<BangDatabase> with $SyncDaoMixin {
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> deleteBangs(Iterable<String> triggers) {
|
||||
final statement = delete(db.bang)..where((t) => t.trigger.isIn(triggers));
|
||||
Future<int> deleteBangs(BangGroup group, Iterable<String> triggers) {
|
||||
final statement = delete(db.bang)
|
||||
..where((t) => t.group.equalsValue(group) & t.trigger.isIn(triggers));
|
||||
return statement.go();
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ class SyncDao extends DatabaseAccessor<BangDatabase> with $SyncDaoMixin {
|
||||
.map((e) => remoteBangMap[e]!);
|
||||
|
||||
await db.transaction(() async {
|
||||
await deleteBangs(removedBangs);
|
||||
await deleteBangs(group, removedBangs);
|
||||
await insertBangs(addedBangs);
|
||||
await replaceBangs(changedBangs);
|
||||
await upsertLastSyncOfGroup(group, syncTime);
|
||||
|
||||
@@ -5,14 +5,15 @@ import 'package:weblibre/features/bangs/data/models/bang.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/search_history_entry.dart';
|
||||
|
||||
CREATE TABLE bang (
|
||||
"trigger" TEXT PRIMARY KEY NOT NULL,
|
||||
"trigger" TEXT NOT NULL,
|
||||
"group" ENUM(BangGroup) NOT NULL,
|
||||
website_name TEXT NOT NULL,
|
||||
domain TEXT NOT NULL,
|
||||
url_template TEXT NOT NULL,
|
||||
category TEXT,
|
||||
sub_category TEXT,
|
||||
format TEXT MAPPED BY `const BangFormatConverter()`
|
||||
format TEXT MAPPED BY `const BangFormatConverter()`,
|
||||
PRIMARY KEY ("trigger", "group")
|
||||
) WITH Bang;
|
||||
|
||||
CREATE TABLE bang_sync (
|
||||
@@ -21,15 +22,20 @@ CREATE TABLE bang_sync (
|
||||
);
|
||||
|
||||
CREATE TABLE bang_frequency (
|
||||
"trigger" TEXT PRIMARY KEY NOT NULL REFERENCES bang ("trigger") ON DELETE CASCADE,
|
||||
"trigger" TEXT NOT NULL,
|
||||
"group" ENUM(BangGroup) NOT NULL,
|
||||
frequency INTEGER NOT NULL,
|
||||
last_used DATETIME NOT NULL
|
||||
last_used DATETIME NOT NULL,
|
||||
PRIMARY KEY ("trigger", "group"),
|
||||
FOREIGN KEY ("trigger", "group") REFERENCES bang ("trigger", "group") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE bang_history (
|
||||
search_query TEXT UNIQUE NOT NULL,
|
||||
"trigger" TEXT NOT NULL REFERENCES bang ("trigger"),
|
||||
search_date DATETIME NOT NULL
|
||||
"trigger" TEXT NOT NULL,
|
||||
"group" ENUM(BangGroup) NOT NULL,
|
||||
search_date DATETIME NOT NULL,
|
||||
FOREIGN KEY ("trigger", "group") REFERENCES bang ("trigger", "group")
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE bang_fts
|
||||
@@ -48,7 +54,7 @@ CREATE VIEW bang_data_view WITH BangData AS
|
||||
FROM
|
||||
bang b
|
||||
LEFT JOIN
|
||||
bang_frequency bf ON b."trigger" = bf."trigger";
|
||||
bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group";
|
||||
|
||||
-- Triggers to keep the FTS index up to date.
|
||||
CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN
|
||||
@@ -90,7 +96,7 @@ queryBangs WITH BangData:
|
||||
INNER JOIN
|
||||
bang b ON b.rowid = fts.rowid
|
||||
LEFT JOIN
|
||||
bang_frequency bf ON b."trigger" = bf."trigger"
|
||||
bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group"
|
||||
CROSS JOIN
|
||||
weights
|
||||
ORDER BY
|
||||
@@ -114,7 +120,7 @@ queryBangsBasic WITH BangData:
|
||||
INNER JOIN
|
||||
bang b ON b.rowid = fts.rowid
|
||||
LEFT JOIN
|
||||
bang_frequency bf ON b."trigger" = bf."trigger"
|
||||
bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group"
|
||||
CROSS JOIN
|
||||
weights
|
||||
WHERE
|
||||
@@ -160,4 +166,4 @@ evictHistoryEntries:
|
||||
FROM bang_history
|
||||
ORDER BY search_date DESC
|
||||
LIMIT -1 OFFSET :limit
|
||||
);
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,8 @@
|
||||
*/
|
||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_group.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_key.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/entities/browser_icon.dart';
|
||||
|
||||
part 'bang_data.g.dart';
|
||||
@@ -30,6 +32,9 @@ class BangData extends Bang {
|
||||
|
||||
final BrowserIcon? icon;
|
||||
|
||||
@override
|
||||
BangGroup get group => super.group!;
|
||||
|
||||
BangData({
|
||||
required super.websiteName,
|
||||
required super.domain,
|
||||
@@ -43,6 +48,8 @@ class BangData extends Bang {
|
||||
this.icon,
|
||||
}) : frequency = frequency ?? 0;
|
||||
|
||||
BangKey toKey() => BangKey(group: group, trigger: trigger);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
...super.hashParameters,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:nullability/nullability.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_group.dart';
|
||||
|
||||
class BangKey {
|
||||
final String trigger;
|
||||
final BangGroup group;
|
||||
|
||||
const BangKey({required this.group, required this.trigger});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '${group.name}::$trigger';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is BangKey &&
|
||||
runtimeType == other.runtimeType &&
|
||||
trigger == other.trigger &&
|
||||
group == other.group;
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(trigger, group);
|
||||
|
||||
static BangKey? tryFromString(String key) {
|
||||
try {
|
||||
final [group, trigger] = key.split('::');
|
||||
return BangKey(
|
||||
group: BangGroup.values.firstWhere((g) => g.name == group),
|
||||
trigger: trigger,
|
||||
);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BangKeyConverter implements JsonConverter<BangKey?, String?> {
|
||||
const BangKeyConverter();
|
||||
|
||||
@override
|
||||
BangKey? fromJson(String? json) {
|
||||
return json.mapNotNull((json) => BangKey.tryFromString(json));
|
||||
}
|
||||
|
||||
@override
|
||||
String? toJson(BangKey? object) {
|
||||
return object?.toString();
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class BangDataSourceService extends _$BangDataSourceService {
|
||||
@override
|
||||
void build() {}
|
||||
|
||||
Future<Result<List<Bang>>> fetchRemoteBangs(Uri url, BangGroup? group) {
|
||||
Future<Result<List<Bang>>> fetchRemoteBangs(Uri url, BangGroup group) {
|
||||
return Result.fromAsync(() async {
|
||||
return await compute((args) async {
|
||||
final client = http.Client();
|
||||
@@ -51,13 +51,8 @@ class BangDataSourceService extends _$BangDataSourceService {
|
||||
}
|
||||
}, [url.toString()]).then(
|
||||
(json) => json.map((e) {
|
||||
var bang = Bang.fromJson(e as Map<String, dynamic>);
|
||||
|
||||
if (group != null) {
|
||||
bang = bang.copyWith.group(group);
|
||||
}
|
||||
|
||||
return bang;
|
||||
final bang = Bang.fromJson(e as Map<String, dynamic>);
|
||||
return bang.copyWith.group(group);
|
||||
}).toList(),
|
||||
);
|
||||
}, exceptionHandler: handleHttpError);
|
||||
|
||||
@@ -42,7 +42,7 @@ final class BangDataSourceServiceProvider
|
||||
}
|
||||
|
||||
String _$bangDataSourceServiceHash() =>
|
||||
r'2ff811ece417ae498dacc04a2fa963cffb34c8a9';
|
||||
r'b1bd96bbd834de0f71af86d7f791d367d0a837be';
|
||||
|
||||
abstract class _$BangDataSourceService extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
@@ -21,6 +21,7 @@ import 'package:riverpod/riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_data.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_group.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_key.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/search_history_entry.dart';
|
||||
import 'package:weblibre/features/bangs/domain/repositories/data.dart';
|
||||
import 'package:weblibre/features/bangs/domain/repositories/sync.dart';
|
||||
@@ -30,20 +31,20 @@ part 'bangs.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Stream<BangData?> defaultSearchBangData(Ref ref) {
|
||||
final trigger = ref.watch(
|
||||
final key = ref.watch(
|
||||
generalSettingsWithDefaultsProvider.select(
|
||||
(value) => value.defaultSearchProvider,
|
||||
),
|
||||
);
|
||||
|
||||
final repository = ref.watch(bangDataRepositoryProvider.notifier);
|
||||
return repository.watchBang(trigger);
|
||||
return repository.watchBang(key);
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
Stream<BangData?> bangData(Ref ref, String trigger) {
|
||||
Stream<BangData?> bangData(Ref ref, BangKey key) {
|
||||
final repository = ref.watch(bangDataRepositoryProvider.notifier);
|
||||
return repository.watchBang(trigger);
|
||||
return repository.watchBang(key);
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
|
||||
@@ -42,7 +42,7 @@ final class DefaultSearchBangDataProvider
|
||||
}
|
||||
|
||||
String _$defaultSearchBangDataHash() =>
|
||||
r'5fa3f588b57a82dd8fffb2082e495108a242ef72';
|
||||
r'5f43b8989219cf3cb2f5ca65df351b6cb100427f';
|
||||
|
||||
@ProviderFor(bangData)
|
||||
const bangDataProvider = BangDataFamily._();
|
||||
@@ -53,7 +53,7 @@ final class BangDataProvider
|
||||
with $FutureModifier<BangData?>, $StreamProvider<BangData?> {
|
||||
const BangDataProvider._({
|
||||
required BangDataFamily super.from,
|
||||
required String super.argument,
|
||||
required BangKey super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'bangDataProvider',
|
||||
@@ -79,7 +79,7 @@ final class BangDataProvider
|
||||
|
||||
@override
|
||||
Stream<BangData?> create(Ref ref) {
|
||||
final argument = this.argument as String;
|
||||
final argument = this.argument as BangKey;
|
||||
return bangData(ref, argument);
|
||||
}
|
||||
|
||||
@@ -94,10 +94,10 @@ final class BangDataProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$bangDataHash() => r'b10d3a74e19d30fc372b9ecbf7146bccd9fdf83b';
|
||||
String _$bangDataHash() => r'bd9f5ec8b29aab74620a9b5a4246cb7e3b2fd377';
|
||||
|
||||
final class BangDataFamily extends $Family
|
||||
with $FunctionalFamilyOverride<Stream<BangData?>, String> {
|
||||
with $FunctionalFamilyOverride<Stream<BangData?>, BangKey> {
|
||||
const BangDataFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
@@ -107,8 +107,8 @@ final class BangDataFamily extends $Family
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
BangDataProvider call(String trigger) =>
|
||||
BangDataProvider._(argument: trigger, from: this);
|
||||
BangDataProvider call(BangKey key) =>
|
||||
BangDataProvider._(argument: key, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'bangDataProvider';
|
||||
|
||||
@@ -34,8 +34,9 @@ class BangSearch extends _$BangSearch {
|
||||
Future<Uri> triggerBangSearch(BangData bang, String searchQuery) async {
|
||||
final bangDataNotifier = ref.read(bangDataRepositoryProvider.notifier);
|
||||
|
||||
await bangDataNotifier.increaseFrequency(bang.trigger);
|
||||
await bangDataNotifier.increaseFrequency(bang.group, bang.trigger);
|
||||
await bangDataNotifier.addSearchEntry(
|
||||
bang.group,
|
||||
bang.trigger,
|
||||
searchQuery,
|
||||
maxEntryCount: 3,
|
||||
|
||||
@@ -33,7 +33,7 @@ final class BangSearchProvider
|
||||
BangSearch create() => BangSearch();
|
||||
}
|
||||
|
||||
String _$bangSearchHash() => r'6b7452d48698c01870c5c75f0c10cadaafc481c8';
|
||||
String _$bangSearchHash() => r'66080335f1677e6a4636c41dd5619de3828375e6';
|
||||
|
||||
abstract class _$BangSearch extends $StreamNotifier<List<BangData>> {
|
||||
Stream<List<BangData>> build();
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'package:drift/drift.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_data.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_group.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_key.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/search_history_entry.dart';
|
||||
import 'package:weblibre/features/bangs/data/providers.dart';
|
||||
|
||||
@@ -33,12 +34,12 @@ class BangDataRepository extends _$BangDataRepository {
|
||||
@override
|
||||
void build() {}
|
||||
|
||||
Stream<BangData?> watchBang(String? trigger) {
|
||||
if (trigger != null) {
|
||||
Stream<BangData?> watchBang(BangKey? key) {
|
||||
if (key != null) {
|
||||
return ref
|
||||
.read(bangDatabaseProvider)
|
||||
.bangDao
|
||||
.getBangData(trigger)
|
||||
.getBangData(key.group, key.trigger)
|
||||
.watchSingleOrNull();
|
||||
} else {
|
||||
return Stream.value(null);
|
||||
@@ -104,14 +105,15 @@ class BangDataRepository extends _$BangDataRepository {
|
||||
.watch();
|
||||
}
|
||||
|
||||
Future<void> increaseFrequency(String trigger) {
|
||||
Future<void> increaseFrequency(BangGroup group, String trigger) {
|
||||
return ref
|
||||
.read(bangDatabaseProvider)
|
||||
.bangDao
|
||||
.increaseBangFrequency(trigger);
|
||||
.increaseBangFrequency(group, trigger);
|
||||
}
|
||||
|
||||
Future<void> addSearchEntry(
|
||||
BangGroup group,
|
||||
String trigger,
|
||||
String searchQuery, {
|
||||
required int maxEntryCount,
|
||||
@@ -119,7 +121,7 @@ class BangDataRepository extends _$BangDataRepository {
|
||||
final db = ref.read(bangDatabaseProvider);
|
||||
//Pack in a transaction to bundle rebuilds of watch() queries
|
||||
return db.transaction(() async {
|
||||
await db.bangDao.addSearchEntry(trigger, searchQuery);
|
||||
await db.bangDao.addSearchEntry(group, trigger, searchQuery);
|
||||
await db.definitionsDrift.evictHistoryEntries(limit: maxEntryCount);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ final class BangDataRepositoryProvider
|
||||
}
|
||||
|
||||
String _$bangDataRepositoryHash() =>
|
||||
r'5e25ce123d4667a0b12316f5b771b6776c40f280';
|
||||
r'93f1338da86c693f524df377fe01d0ecf7f5ff80';
|
||||
|
||||
abstract class _$BangDataRepository extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/core/routing/routes.dart';
|
||||
import 'package:weblibre/features/bangs/data/models/bang_key.dart';
|
||||
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
|
||||
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
||||
@@ -38,7 +39,7 @@ class BangCategoriesScreen extends HookConsumerWidget {
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
final trigger = await const BangSearchRoute().push<String?>(
|
||||
final trigger = await const BangSearchRoute().push<BangKey?>(
|
||||
context,
|
||||
);
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class BangListScreen extends HookConsumerWidget {
|
||||
onTap: () {
|
||||
ref
|
||||
.read(selectedBangTriggerProvider().notifier)
|
||||
.setTrigger(bang.trigger);
|
||||
.setTrigger(bang.toKey());
|
||||
|
||||
final settings = ref.read(
|
||||
generalSettingsWithDefaultsProvider,
|
||||
|
||||
@@ -90,7 +90,7 @@ class BangSearchScreen extends HookConsumerWidget {
|
||||
return BangDetails(
|
||||
bang,
|
||||
onTap: () {
|
||||
context.pop(bang.trigger);
|
||||
context.pop(bang.toKey());
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -148,7 +148,7 @@ class SiteSearch extends HookConsumerWidget {
|
||||
|
||||
ref
|
||||
.read(selectedBangTriggerProvider(domain: domain).notifier)
|
||||
.setTrigger(bang.trigger);
|
||||
.setTrigger(bang.toKey());
|
||||
},
|
||||
onDeleted: (bang) {
|
||||
if (ref.read(selectedBangTriggerProvider(domain: domain)) ==
|
||||
|
||||
Reference in New Issue
Block a user