refactored sync & bangs

This commit is contained in:
Fabian Freund
2024-06-12 08:34:48 +02:00
parent ff40d57ce7
commit b1e2b65a0c
22 changed files with 1023 additions and 327 deletions
@@ -9,7 +9,7 @@ part 'bang.g.dart';
class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin { class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
BangDao(super.db); BangDao(super.db);
Selectable<Bang> getBangs({Iterable<BangGroup?>? groups}) { Selectable<Bang> getBangList({Iterable<BangGroup>? groups}) {
final selectable = select(db.bang); final selectable = select(db.bang);
if (groups != null) { if (groups != null) {
selectable.where((t) => t.group.isInValues(groups)); selectable.where((t) => t.group.isInValues(groups));
@@ -18,11 +18,17 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
return selectable; return selectable;
} }
SingleSelectable<int> getBangCount({Iterable<BangGroup>? groups}) {
return db.bang.count(
where: (groups != null) ? (t) => t.group.isInValues(groups) : null,
);
}
SingleOrNullSelectable<BangData> getBangData(String trigger) { SingleOrNullSelectable<BangData> getBangData(String trigger) {
return select(db.bangDataView)..where((t) => t.trigger.equals(trigger)); return select(db.bangDataView)..where((t) => t.trigger.equals(trigger));
} }
Selectable<BangData> getBangDataList({Iterable<BangGroup?>? groups}) { Selectable<BangData> getBangDataList({Iterable<BangGroup>? groups}) {
final selectable = select(db.bangDataView); final selectable = select(db.bangDataView);
if (groups != null) { if (groups != null) {
selectable.where((t) => t.group.isInValues(groups)); selectable.where((t) => t.group.isInValues(groups));
@@ -33,9 +39,10 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
return selectable; return selectable;
} }
Selectable<BangData> getFrequentBangDataList({Iterable<BangGroup?>? groups}) { Selectable<BangData> getFrequentBangDataList({Iterable<BangGroup>? groups}) {
final selectable = select(db.bangDataView) final selectable = select(db.bangDataView)
..where((t) => t.frequency.isBiggerThanValue(0)); ..where((t) => t.frequency.isBiggerThanValue(0));
if (groups != null) { if (groups != null) {
selectable.where((t) => t.group.isInValues(groups)); selectable.where((t) => t.group.isInValues(groups));
} }
@@ -48,7 +55,7 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
return selectable; return selectable;
} }
Future<int> increaseFrequency(String trigger) { Future<int> increaseBangFrequency(String trigger) {
return db.bangFrequency.insertOne( return db.bangFrequency.insertOne(
BangFrequencyCompanion.insert( BangFrequencyCompanion.insert(
trigger: trigger, trigger: trigger,
@@ -68,11 +75,7 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
return db.bangQuery(query: db.buildQuery(searchString)); return db.bangQuery(query: db.buildQuery(searchString));
} }
Future<void> insertBangs(Iterable<Bang> bangs) { Future<int> upsertBangIcon(String trigger, Uint8List iconData) {
return db.bang.insertAll(bangs);
}
Future<int> upsertIcon(String trigger, Uint8List iconData) {
return db.bangIcon.insertOne( return db.bangIcon.insertOne(
BangIconCompanion.insert( BangIconCompanion.insert(
trigger: trigger, trigger: trigger,
@@ -82,17 +85,4 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
mode: InsertMode.insertOrReplace, mode: InsertMode.insertOrReplace,
); );
} }
Future<void> replaceBangs(Iterable<Bang> bangs) {
return batch(
(batch) {
batch.replaceAll(db.bang, bangs);
},
);
}
Future<int> deleteBangs(Iterable<String> triggers) {
final statement = delete(db.bang)..where((t) => t.trigger.isIn(triggers));
return statement.go();
}
} }
@@ -0,0 +1,85 @@
import 'package:bang_navigator/features/bangs/data/database/database.dart';
import 'package:bang_navigator/features/bangs/data/models/bang.dart';
import 'package:drift/drift.dart';
part 'sync.g.dart';
@DriftAccessor()
class SyncDao extends DatabaseAccessor<BangDatabase> with _$SyncDaoMixin {
SyncDao(super.db);
SingleOrNullSelectable<DateTime?> lastSyncOfGroup(BangGroup group) {
final query = selectOnly(db.bangSync)
..addColumns([db.bangSync.lastSync])
..where(db.bangSync.group.equalsValue(group));
return query.map((row) => row.read(db.bangSync.lastSync));
}
Future<void> upsertLastSyncOfGroup(BangGroup group, DateTime lastSync) {
return db.bangSync.insertOne(
BangSyncCompanion.insert(
group: Value(group),
lastSync: lastSync,
),
onConflict: DoUpdate(
(old) => BangSyncCompanion.custom(lastSync: Variable(lastSync)),
),
);
}
Future<void> insertBangs(Iterable<Bang> bangs) {
return db.bang.insertAll(bangs);
}
Future<void> replaceBangs(Iterable<Bang> bangs) {
return batch(
(batch) {
batch.replaceAll(db.bang, bangs);
},
);
}
Future<int> deleteBangs(Iterable<String> triggers) {
final statement = delete(db.bang)..where((t) => t.trigger.isIn(triggers));
return statement.go();
}
Future<void> syncBangs({
required BangGroup group,
required Iterable<Bang> remoteBangs,
required DateTime syncTime,
}) async {
final remoteBangMap =
Map.fromEntries(remoteBangs.map((e) => MapEntry(e.trigger, e)));
final localBangMap =
await db.bangDao.getBangList(groups: [group]).get().then(
(bangs) =>
Map.fromEntries(bangs.map((e) => MapEntry(e.trigger, e))),
);
final remoteBangTriggers = remoteBangMap.keys.toSet();
final localBangTriggers = localBangMap.keys.toSet();
final removedBangs = localBangTriggers.difference(remoteBangTriggers);
final addedBangs = remoteBangTriggers.difference(localBangTriggers).map(
(e) => remoteBangMap[e]!,
);
final changedBangs = remoteBangTriggers
.intersection(localBangTriggers)
.where((e) => remoteBangMap[e] != localBangMap[e])
.map(
(e) => remoteBangMap[e]!,
);
await db.transaction(
() async {
await deleteBangs(removedBangs);
await insertBangs(addedBangs);
await replaceBangs(changedBangs);
await upsertLastSyncOfGroup(group, syncTime);
},
);
}
}
@@ -0,0 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sync.dart';
// ignore_for_file: type=lint
mixin _$SyncDaoMixin on DatabaseAccessor<BangDatabase> {}
@@ -1,4 +1,5 @@
import 'package:bang_navigator/features/bangs/data/database/daos/bang.dart'; import 'package:bang_navigator/features/bangs/data/database/daos/bang.dart';
import 'package:bang_navigator/features/bangs/data/database/daos/sync.dart';
import 'package:bang_navigator/features/bangs/data/database/drift/converters/bang_format.dart'; import 'package:bang_navigator/features/bangs/data/database/drift/converters/bang_format.dart';
import 'package:bang_navigator/features/bangs/data/models/bang.dart'; import 'package:bang_navigator/features/bangs/data/models/bang.dart';
import 'package:bang_navigator/features/bangs/data/models/bang_data.dart'; import 'package:bang_navigator/features/bangs/data/models/bang_data.dart';
@@ -7,7 +8,10 @@ import 'package:drift/drift.dart';
part 'database.g.dart'; part 'database.g.dart';
@DriftDatabase(include: {'database.drift'}, daos: [BangDao]) @DriftDatabase(
include: {'database.drift'},
daos: [BangDao, SyncDao],
)
class BangDatabase extends _$BangDatabase with PrefixQueryBuilderMixin { class BangDatabase extends _$BangDatabase with PrefixQueryBuilderMixin {
@override @override
final int schemaVersion = 1; final int schemaVersion = 1;
@@ -4,7 +4,7 @@ import 'package:bang_navigator/features/bangs/data/models/bang.dart';
CREATE TABLE bang ( CREATE TABLE bang (
"trigger" TEXT PRIMARY KEY NOT NULL, "trigger" TEXT PRIMARY KEY NOT NULL,
"group" ENUM(BangGroup), "group" ENUM(BangGroup) NOT NULL,
website_name TEXT NOT NULL, website_name TEXT NOT NULL,
domain TEXT NOT NULL, domain TEXT NOT NULL,
url_template TEXT NOT NULL, url_template TEXT NOT NULL,
@@ -13,6 +13,11 @@ CREATE TABLE bang (
format TEXT MAPPED BY `const BangFormatConverter()` format TEXT MAPPED BY `const BangFormatConverter()`
) WITH Bang; ) WITH Bang;
CREATE TABLE bang_sync (
"group" ENUM(BangGroup) PRIMARY KEY NOT NULL,
last_sync DATETIME NOT NULL
);
CREATE TABLE bang_frequency ( CREATE TABLE bang_frequency (
"trigger" TEXT PRIMARY KEY NOT NULL REFERENCES bang ("trigger") ON DELETE CASCADE, "trigger" TEXT PRIMARY KEY NOT NULL REFERENCES bang ("trigger") ON DELETE CASCADE,
frequency INTEGER NOT NULL, frequency INTEGER NOT NULL,
@@ -13,12 +13,12 @@ class BangTable extends Table with TableInfo<BangTable, Bang> {
type: DriftSqlType.string, type: DriftSqlType.string,
requiredDuringInsert: true, requiredDuringInsert: true,
$customConstraints: 'PRIMARY KEY NOT NULL'); $customConstraints: 'PRIMARY KEY NOT NULL');
late final GeneratedColumnWithTypeConverter<BangGroup?, int> group = late final GeneratedColumnWithTypeConverter<BangGroup, int> group =
GeneratedColumn<int>('group', aliasedName, true, GeneratedColumn<int>('group', aliasedName, false,
type: DriftSqlType.int, type: DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: true,
$customConstraints: '') $customConstraints: 'NOT NULL')
.withConverter<BangGroup?>(BangTable.$convertergroupn); .withConverter<BangGroup>(BangTable.$convertergroup);
late final GeneratedColumn<String> websiteName = GeneratedColumn<String>( late final GeneratedColumn<String> websiteName = GeneratedColumn<String>(
'website_name', aliasedName, false, 'website_name', aliasedName, false,
type: DriftSqlType.string, type: DriftSqlType.string,
@@ -80,8 +80,8 @@ class BangTable extends Table with TableInfo<BangTable, Bang> {
.read(DriftSqlType.string, data['${effectivePrefix}trigger'])!, .read(DriftSqlType.string, data['${effectivePrefix}trigger'])!,
urlTemplate: attachedDatabase.typeMapping urlTemplate: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}url_template'])!, .read(DriftSqlType.string, data['${effectivePrefix}url_template'])!,
group: BangTable.$convertergroupn.fromSql(attachedDatabase.typeMapping group: BangTable.$convertergroup.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}group'])), .read(DriftSqlType.int, data['${effectivePrefix}group'])!),
category: attachedDatabase.typeMapping category: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}category']), .read(DriftSqlType.string, data['${effectivePrefix}category']),
subCategory: attachedDatabase.typeMapping subCategory: attachedDatabase.typeMapping
@@ -98,8 +98,6 @@ class BangTable extends Table with TableInfo<BangTable, Bang> {
static JsonTypeConverter2<BangGroup, int, int> $convertergroup = static JsonTypeConverter2<BangGroup, int, int> $convertergroup =
const EnumIndexConverter<BangGroup>(BangGroup.values); const EnumIndexConverter<BangGroup>(BangGroup.values);
static JsonTypeConverter2<BangGroup?, int?, int?> $convertergroupn =
JsonTypeConverter2.asNullable($convertergroup);
static TypeConverter<Set<BangFormat>?, String?> $converterformat = static TypeConverter<Set<BangFormat>?, String?> $converterformat =
const BangFormatConverter(); const BangFormatConverter();
@override @override
@@ -108,7 +106,7 @@ class BangTable extends Table with TableInfo<BangTable, Bang> {
class BangCompanion extends UpdateCompanion<Bang> { class BangCompanion extends UpdateCompanion<Bang> {
final Value<String> trigger; final Value<String> trigger;
final Value<BangGroup?> group; final Value<BangGroup> group;
final Value<String> websiteName; final Value<String> websiteName;
final Value<String> domain; final Value<String> domain;
final Value<String> urlTemplate; final Value<String> urlTemplate;
@@ -129,7 +127,7 @@ class BangCompanion extends UpdateCompanion<Bang> {
}); });
BangCompanion.insert({ BangCompanion.insert({
required String trigger, required String trigger,
this.group = const Value.absent(), required BangGroup group,
required String websiteName, required String websiteName,
required String domain, required String domain,
required String urlTemplate, required String urlTemplate,
@@ -138,6 +136,7 @@ class BangCompanion extends UpdateCompanion<Bang> {
this.format = const Value.absent(), this.format = const Value.absent(),
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}) : trigger = Value(trigger), }) : trigger = Value(trigger),
group = Value(group),
websiteName = Value(websiteName), websiteName = Value(websiteName),
domain = Value(domain), domain = Value(domain),
urlTemplate = Value(urlTemplate); urlTemplate = Value(urlTemplate);
@@ -167,7 +166,7 @@ class BangCompanion extends UpdateCompanion<Bang> {
BangCompanion copyWith( BangCompanion copyWith(
{Value<String>? trigger, {Value<String>? trigger,
Value<BangGroup?>? group, Value<BangGroup>? group,
Value<String>? websiteName, Value<String>? websiteName,
Value<String>? domain, Value<String>? domain,
Value<String>? urlTemplate, Value<String>? urlTemplate,
@@ -196,7 +195,7 @@ class BangCompanion extends UpdateCompanion<Bang> {
} }
if (group.present) { if (group.present) {
map['group'] = map['group'] =
Variable<int>(BangTable.$convertergroupn.toSql(group.value)); Variable<int>(BangTable.$convertergroup.toSql(group.value));
} }
if (websiteName.present) { if (websiteName.present) {
map['website_name'] = Variable<String>(websiteName.value); map['website_name'] = Variable<String>(websiteName.value);
@@ -240,6 +239,159 @@ class BangCompanion extends UpdateCompanion<Bang> {
} }
} }
class BangSync extends Table with TableInfo<BangSync, BangSyncData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
BangSync(this.attachedDatabase, [this._alias]);
late final GeneratedColumnWithTypeConverter<BangGroup, int> group =
GeneratedColumn<int>('group', aliasedName, false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'PRIMARY KEY NOT NULL')
.withConverter<BangGroup>(BangSync.$convertergroup);
late final GeneratedColumn<DateTime> lastSync = GeneratedColumn<DateTime>(
'last_sync', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL');
@override
List<GeneratedColumn> get $columns => [group, lastSync];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'bang_sync';
@override
Set<GeneratedColumn> get $primaryKey => {group};
@override
BangSyncData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return BangSyncData(
group: BangSync.$convertergroup.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}group'])!),
lastSync: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}last_sync'])!,
);
}
@override
BangSync createAlias(String alias) {
return BangSync(attachedDatabase, alias);
}
static JsonTypeConverter2<BangGroup, int, int> $convertergroup =
const EnumIndexConverter<BangGroup>(BangGroup.values);
@override
bool get dontWriteConstraints => true;
}
class BangSyncData extends DataClass implements Insertable<BangSyncData> {
final BangGroup group;
final DateTime lastSync;
const BangSyncData({required this.group, required this.lastSync});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
{
map['group'] = Variable<int>(BangSync.$convertergroup.toSql(group));
}
map['last_sync'] = Variable<DateTime>(lastSync);
return map;
}
factory BangSyncData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return BangSyncData(
group: BangSync.$convertergroup
.fromJson(serializer.fromJson<int>(json['group'])),
lastSync: serializer.fromJson<DateTime>(json['last_sync']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'group': serializer.toJson<int>(BangSync.$convertergroup.toJson(group)),
'last_sync': serializer.toJson<DateTime>(lastSync),
};
}
BangSyncData copyWith({BangGroup? group, DateTime? lastSync}) => BangSyncData(
group: group ?? this.group,
lastSync: lastSync ?? this.lastSync,
);
@override
String toString() {
return (StringBuffer('BangSyncData(')
..write('group: $group, ')
..write('lastSync: $lastSync')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(group, lastSync);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is BangSyncData &&
other.group == this.group &&
other.lastSync == this.lastSync);
}
class BangSyncCompanion extends UpdateCompanion<BangSyncData> {
final Value<BangGroup> group;
final Value<DateTime> lastSync;
const BangSyncCompanion({
this.group = const Value.absent(),
this.lastSync = const Value.absent(),
});
BangSyncCompanion.insert({
this.group = const Value.absent(),
required DateTime lastSync,
}) : lastSync = Value(lastSync);
static Insertable<BangSyncData> custom({
Expression<int>? group,
Expression<DateTime>? lastSync,
}) {
return RawValuesInsertable({
if (group != null) 'group': group,
if (lastSync != null) 'last_sync': lastSync,
});
}
BangSyncCompanion copyWith(
{Value<BangGroup>? group, Value<DateTime>? lastSync}) {
return BangSyncCompanion(
group: group ?? this.group,
lastSync: lastSync ?? this.lastSync,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (group.present) {
map['group'] = Variable<int>(BangSync.$convertergroup.toSql(group.value));
}
if (lastSync.present) {
map['last_sync'] = Variable<DateTime>(lastSync.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('BangSyncCompanion(')
..write('group: $group, ')
..write('lastSync: $lastSync')
..write(')'))
.toString();
}
}
class BangFrequency extends Table class BangFrequency extends Table
with TableInfo<BangFrequency, BangFrequencyData> { with TableInfo<BangFrequency, BangFrequencyData> {
@override @override
@@ -812,7 +964,7 @@ class BangDataView extends ViewInfo<BangDataView, BangData>
@override @override
Map<SqlDialect, String> get createViewStatements => { Map<SqlDialect, String> get createViewStatements => {
SqlDialect.sqlite: SqlDialect.sqlite:
'CREATE VIEW bang_data_view WITH BangData AS SELECT b.*, bf.frequency, bf.last_used, bi.icon_data FROM bang AS b LEFT JOIN bang_frequency AS bf ON b."trigger" = bf."trigger" LEFT JOIN bang_icon AS bi ON b."trigger" = bi."trigger"', 'CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used, bi.icon_data FROM bang AS b LEFT JOIN bang_frequency AS bf ON b."trigger" = bf."trigger" LEFT JOIN bang_icon AS bi ON b."trigger" = bi."trigger"',
}; };
@override @override
BangDataView get asDslTable => this; BangDataView get asDslTable => this;
@@ -846,9 +998,9 @@ class BangDataView extends ViewInfo<BangDataView, BangData>
late final GeneratedColumn<String> trigger = GeneratedColumn<String>( late final GeneratedColumn<String> trigger = GeneratedColumn<String>(
'trigger', aliasedName, false, 'trigger', aliasedName, false,
type: DriftSqlType.string); type: DriftSqlType.string);
late final GeneratedColumnWithTypeConverter<BangGroup?, int> group = late final GeneratedColumnWithTypeConverter<BangGroup, int> group =
GeneratedColumn<int>('group', aliasedName, true, type: DriftSqlType.int) GeneratedColumn<int>('group', aliasedName, false, type: DriftSqlType.int)
.withConverter<BangGroup?>(BangTable.$convertergroupn); .withConverter<BangGroup>(BangTable.$convertergroup);
late final GeneratedColumn<String> websiteName = GeneratedColumn<String>( late final GeneratedColumn<String> websiteName = GeneratedColumn<String>(
'website_name', aliasedName, false, 'website_name', aliasedName, false,
type: DriftSqlType.string); type: DriftSqlType.string);
@@ -892,6 +1044,7 @@ abstract class _$BangDatabase extends GeneratedDatabase {
_$BangDatabase(QueryExecutor e) : super(e); _$BangDatabase(QueryExecutor e) : super(e);
_$BangDatabaseManager get managers => _$BangDatabaseManager(this); _$BangDatabaseManager get managers => _$BangDatabaseManager(this);
late final BangTable bang = BangTable(this); late final BangTable bang = BangTable(this);
late final BangSync bangSync = BangSync(this);
late final BangFrequency bangFrequency = BangFrequency(this); late final BangFrequency bangFrequency = BangFrequency(this);
late final BangIcon bangIcon = BangIcon(this); late final BangIcon bangIcon = BangIcon(this);
late final BangFts bangFts = BangFts(this); late final BangFts bangFts = BangFts(this);
@@ -906,6 +1059,7 @@ abstract class _$BangDatabase extends GeneratedDatabase {
'CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN INSERT INTO bang_fts (bang_fts, "rowid", "trigger", website_name) VALUES (\'delete\', old."rowid", old."trigger", old.website_name);INSERT INTO bang_fts ("rowid", "trigger", website_name) VALUES (new."rowid", new."trigger", new.website_name);END', 'CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN INSERT INTO bang_fts (bang_fts, "rowid", "trigger", website_name) VALUES (\'delete\', old."rowid", old."trigger", old.website_name);INSERT INTO bang_fts ("rowid", "trigger", website_name) VALUES (new."rowid", new."trigger", new.website_name);END',
'bang_after_update'); 'bang_after_update');
late final BangDao bangDao = BangDao(this as BangDatabase); late final BangDao bangDao = BangDao(this as BangDatabase);
late final SyncDao syncDao = SyncDao(this as BangDatabase);
Future<int> optimizeFtsIndex() { Future<int> optimizeFtsIndex() {
return customInsert( return customInsert(
'INSERT INTO bang_fts (bang_fts) VALUES (\'optimize\')', 'INSERT INTO bang_fts (bang_fts) VALUES (\'optimize\')',
@@ -946,6 +1100,7 @@ abstract class _$BangDatabase extends GeneratedDatabase {
@override @override
List<DatabaseSchemaEntity> get allSchemaEntities => [ List<DatabaseSchemaEntity> get allSchemaEntities => [
bang, bang,
bangSync,
bangFrequency, bangFrequency,
bangIcon, bangIcon,
bangFts, bangFts,
@@ -996,6 +1151,91 @@ abstract class _$BangDatabase extends GeneratedDatabase {
); );
} }
typedef $BangSyncInsertCompanionBuilder = BangSyncCompanion Function({
Value<BangGroup> group,
required DateTime lastSync,
});
typedef $BangSyncUpdateCompanionBuilder = BangSyncCompanion Function({
Value<BangGroup> group,
Value<DateTime> lastSync,
});
class $BangSyncTableManager extends RootTableManager<
_$BangDatabase,
BangSync,
BangSyncData,
$BangSyncFilterComposer,
$BangSyncOrderingComposer,
$BangSyncProcessedTableManager,
$BangSyncInsertCompanionBuilder,
$BangSyncUpdateCompanionBuilder> {
$BangSyncTableManager(_$BangDatabase db, BangSync table)
: super(TableManagerState(
db: db,
table: table,
filteringComposer: $BangSyncFilterComposer(ComposerState(db, table)),
orderingComposer: $BangSyncOrderingComposer(ComposerState(db, table)),
getChildManagerBuilder: (p) => $BangSyncProcessedTableManager(p),
getUpdateCompanionBuilder: ({
Value<BangGroup> group = const Value.absent(),
Value<DateTime> lastSync = const Value.absent(),
}) =>
BangSyncCompanion(
group: group,
lastSync: lastSync,
),
getInsertCompanionBuilder: ({
Value<BangGroup> group = const Value.absent(),
required DateTime lastSync,
}) =>
BangSyncCompanion.insert(
group: group,
lastSync: lastSync,
),
));
}
class $BangSyncProcessedTableManager extends ProcessedTableManager<
_$BangDatabase,
BangSync,
BangSyncData,
$BangSyncFilterComposer,
$BangSyncOrderingComposer,
$BangSyncProcessedTableManager,
$BangSyncInsertCompanionBuilder,
$BangSyncUpdateCompanionBuilder> {
$BangSyncProcessedTableManager(super.$state);
}
class $BangSyncFilterComposer extends FilterComposer<_$BangDatabase, BangSync> {
$BangSyncFilterComposer(super.$state);
ColumnWithTypeConverterFilters<BangGroup, BangGroup, int> get group =>
$state.composableBuilder(
column: $state.table.group,
builder: (column, joinBuilders) => ColumnWithTypeConverterFilters(
column,
joinBuilders: joinBuilders));
ColumnFilters<DateTime> get lastSync => $state.composableBuilder(
column: $state.table.lastSync,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
}
class $BangSyncOrderingComposer
extends OrderingComposer<_$BangDatabase, BangSync> {
$BangSyncOrderingComposer(super.$state);
ColumnOrderings<int> get group => $state.composableBuilder(
column: $state.table.group,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<DateTime> get lastSync => $state.composableBuilder(
column: $state.table.lastSync,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
}
typedef $BangFrequencyInsertCompanionBuilder = BangFrequencyCompanion Function({ typedef $BangFrequencyInsertCompanionBuilder = BangFrequencyCompanion Function({
required String trigger, required String trigger,
required int frequency, required int frequency,
@@ -1301,6 +1541,8 @@ class $BangFtsOrderingComposer
class _$BangDatabaseManager { class _$BangDatabaseManager {
final _$BangDatabase _db; final _$BangDatabase _db;
_$BangDatabaseManager(this._db); _$BangDatabaseManager(this._db);
$BangSyncTableManager get bangSync =>
$BangSyncTableManager(_db, _db.bangSync);
$BangFrequencyTableManager get bangFrequency => $BangFrequencyTableManager get bangFrequency =>
$BangFrequencyTableManager(_db, _db.bangFrequency); $BangFrequencyTableManager(_db, _db.bangFrequency);
$BangIconTableManager get bangIcon => $BangIconTableManager get bangIcon =>
@@ -8,6 +8,7 @@ import 'package:json_annotation/json_annotation.dart';
part 'bang.g.dart'; part 'bang.g.dart';
enum BangGroup { enum BangGroup {
general,
assistant, assistant,
kagi, kagi,
} }
+32 -12
View File
@@ -1,40 +1,60 @@
import 'package:bang_navigator/features/bangs/data/models/bang.dart';
import 'package:bang_navigator/features/bangs/data/models/bang_data.dart'; import 'package:bang_navigator/features/bangs/data/models/bang_data.dart';
import 'package:bang_navigator/features/bangs/domain/repositories/bang.dart'; import 'package:bang_navigator/features/bangs/domain/repositories/bang_data.dart';
import 'package:bang_navigator/features/bangs/domain/repositories/sync.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'providers.g.dart'; part 'providers.g.dart';
@Riverpod() @Riverpod()
Stream<BangData?> bang(BangRef ref, String? trigger) { Stream<BangData?> bangData(BangDataRef ref, String? trigger) {
final repository = ref.watch(bangRepositoryProvider.notifier); final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchBang(trigger); return repository.watchBang(trigger);
} }
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
Stream<BangData?> kagiSearchBang(KagiSearchBangRef ref) { Stream<BangData?> kagiSearchBangData(KagiSearchBangDataRef ref) {
final repository = ref.watch(bangRepositoryProvider.notifier); final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchBang('kagi'); return repository.watchBang('kagi');
} }
@Riverpod() @Riverpod()
Stream<List<BangData>> allBangs(AllBangsRef ref) { Stream<List<BangData>> bangDataList(BangDataListRef ref) {
final repository = ref.watch(bangRepositoryProvider.notifier); final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchBangs(); return repository.watchBangs();
} }
@Riverpod() @Riverpod()
Stream<List<BangData>> frequentBangs(FrequentBangsRef ref) { Stream<List<BangData>> frequentBangDataList(FrequentBangDataListRef ref) {
final repository = ref.watch(bangRepositoryProvider.notifier); final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchFrequentBangs(); return repository.watchFrequentBangs();
} }
@Riverpod() @Riverpod()
Future<BangData> ensureIconAvailable( Future<BangData> bangDataEnsureIcon(
EnsureIconAvailableRef ref, BangDataEnsureIconRef ref,
BangData bang, BangData bang,
) { ) {
final repository = ref.watch(bangRepositoryProvider.notifier); final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.ensureIconAvailable(bang).then( return repository.ensureIconAvailable(bang).then(
(value) => value.value, (value) => value.value,
); );
} }
@Riverpod()
Stream<DateTime?> lastSyncOfGroup(
LastSyncOfGroupRef ref,
BangGroup group,
) {
final repository = ref.watch(bangSyncRepositoryProvider.notifier);
return repository.watchLastSyncOfGroup(group);
}
@Riverpod()
Stream<int> bangCountOfGroup(
BangCountOfGroupRef ref,
BangGroup group,
) {
final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchBangCount(group);
}
+358 -97
View File
@@ -6,7 +6,7 @@ part of 'providers.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$bangHash() => r'e3d44af52e27831b568f292d38b447a443acec95'; String _$bangDataHash() => r'd0e96885458be8893c6b19735b8792bdd1b9bcb8';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -29,27 +29,27 @@ class _SystemHash {
} }
} }
/// See also [bang]. /// See also [bangData].
@ProviderFor(bang) @ProviderFor(bangData)
const bangProvider = BangFamily(); const bangDataProvider = BangDataFamily();
/// See also [bang]. /// See also [bangData].
class BangFamily extends Family<AsyncValue<BangData?>> { class BangDataFamily extends Family<AsyncValue<BangData?>> {
/// See also [bang]. /// See also [bangData].
const BangFamily(); const BangDataFamily();
/// See also [bang]. /// See also [bangData].
BangProvider call( BangDataProvider call(
String? trigger, String? trigger,
) { ) {
return BangProvider( return BangDataProvider(
trigger, trigger,
); );
} }
@override @override
BangProvider getProviderOverride( BangDataProvider getProviderOverride(
covariant BangProvider provider, covariant BangDataProvider provider,
) { ) {
return call( return call(
provider.trigger, provider.trigger,
@@ -68,29 +68,31 @@ class BangFamily extends Family<AsyncValue<BangData?>> {
_allTransitiveDependencies; _allTransitiveDependencies;
@override @override
String? get name => r'bangProvider'; String? get name => r'bangDataProvider';
} }
/// See also [bang]. /// See also [bangData].
class BangProvider extends AutoDisposeStreamProvider<BangData?> { class BangDataProvider extends AutoDisposeStreamProvider<BangData?> {
/// See also [bang]. /// See also [bangData].
BangProvider( BangDataProvider(
String? trigger, String? trigger,
) : this._internal( ) : this._internal(
(ref) => bang( (ref) => bangData(
ref as BangRef, ref as BangDataRef,
trigger, trigger,
), ),
from: bangProvider, from: bangDataProvider,
name: r'bangProvider', name: r'bangDataProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$bangHash, const bool.fromEnvironment('dart.vm.product')
dependencies: BangFamily._dependencies, ? null
allTransitiveDependencies: BangFamily._allTransitiveDependencies, : _$bangDataHash,
dependencies: BangDataFamily._dependencies,
allTransitiveDependencies: BangDataFamily._allTransitiveDependencies,
trigger: trigger, trigger: trigger,
); );
BangProvider._internal( BangDataProvider._internal(
super._createNotifier, { super._createNotifier, {
required super.name, required super.name,
required super.dependencies, required super.dependencies,
@@ -104,12 +106,12 @@ class BangProvider extends AutoDisposeStreamProvider<BangData?> {
@override @override
Override overrideWith( Override overrideWith(
Stream<BangData?> Function(BangRef provider) create, Stream<BangData?> Function(BangDataRef provider) create,
) { ) {
return ProviderOverride( return ProviderOverride(
origin: this, origin: this,
override: BangProvider._internal( override: BangDataProvider._internal(
(ref) => create(ref as BangRef), (ref) => create(ref as BangDataRef),
from: from, from: from,
name: null, name: null,
dependencies: null, dependencies: null,
@@ -122,12 +124,12 @@ class BangProvider extends AutoDisposeStreamProvider<BangData?> {
@override @override
AutoDisposeStreamProviderElement<BangData?> createElement() { AutoDisposeStreamProviderElement<BangData?> createElement() {
return _BangProviderElement(this); return _BangDataProviderElement(this);
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return other is BangProvider && other.trigger == trigger; return other is BangDataProvider && other.trigger == trigger;
} }
@override @override
@@ -139,88 +141,90 @@ class BangProvider extends AutoDisposeStreamProvider<BangData?> {
} }
} }
mixin BangRef on AutoDisposeStreamProviderRef<BangData?> { mixin BangDataRef on AutoDisposeStreamProviderRef<BangData?> {
/// The parameter `trigger` of this provider. /// The parameter `trigger` of this provider.
String? get trigger; String? get trigger;
} }
class _BangProviderElement extends AutoDisposeStreamProviderElement<BangData?> class _BangDataProviderElement
with BangRef { extends AutoDisposeStreamProviderElement<BangData?> with BangDataRef {
_BangProviderElement(super.provider); _BangDataProviderElement(super.provider);
@override @override
String? get trigger => (origin as BangProvider).trigger; String? get trigger => (origin as BangDataProvider).trigger;
} }
String _$kagiSearchBangHash() => r'ec830f26cf1d23e1e75747e1523cb1868e77d40b'; String _$kagiSearchBangDataHash() =>
r'4dbeaab0541d7c8214d097523d139eede2e3f975';
/// See also [kagiSearchBang]. /// See also [kagiSearchBangData].
@ProviderFor(kagiSearchBang) @ProviderFor(kagiSearchBangData)
final kagiSearchBangProvider = StreamProvider<BangData?>.internal( final kagiSearchBangDataProvider = StreamProvider<BangData?>.internal(
kagiSearchBang, kagiSearchBangData,
name: r'kagiSearchBangProvider', name: r'kagiSearchBangDataProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$kagiSearchBangHash, : _$kagiSearchBangDataHash,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef KagiSearchBangRef = StreamProviderRef<BangData?>; typedef KagiSearchBangDataRef = StreamProviderRef<BangData?>;
String _$allBangsHash() => r'212201f81e21fd0c1b5fb192359fb6ff8269a2a6'; String _$bangDataListHash() => r'f5544ce7b3cc0f3e0fc3db25a99456c3cd080c1c';
/// See also [allBangs]. /// See also [bangDataList].
@ProviderFor(allBangs) @ProviderFor(bangDataList)
final allBangsProvider = AutoDisposeStreamProvider<List<BangData>>.internal( final bangDataListProvider = AutoDisposeStreamProvider<List<BangData>>.internal(
allBangs, bangDataList,
name: r'allBangsProvider', name: r'bangDataListProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$allBangsHash, const bool.fromEnvironment('dart.vm.product') ? null : _$bangDataListHash,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef AllBangsRef = AutoDisposeStreamProviderRef<List<BangData>>; typedef BangDataListRef = AutoDisposeStreamProviderRef<List<BangData>>;
String _$frequentBangsHash() => r'12867819c6e2bbc5f2b6342aacc286f62d3a1803'; String _$frequentBangDataListHash() =>
r'adf8d0b72adfa8faffadd7f1a294ce3945e22fec';
/// See also [frequentBangs]. /// See also [frequentBangDataList].
@ProviderFor(frequentBangs) @ProviderFor(frequentBangDataList)
final frequentBangsProvider = final frequentBangDataListProvider =
AutoDisposeStreamProvider<List<BangData>>.internal( AutoDisposeStreamProvider<List<BangData>>.internal(
frequentBangs, frequentBangDataList,
name: r'frequentBangsProvider', name: r'frequentBangDataListProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$frequentBangsHash, : _$frequentBangDataListHash,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef FrequentBangsRef = AutoDisposeStreamProviderRef<List<BangData>>; typedef FrequentBangDataListRef = AutoDisposeStreamProviderRef<List<BangData>>;
String _$ensureIconAvailableHash() => String _$bangDataEnsureIconHash() =>
r'2e112c88da8cf9bf12bdc5e5d525d59fe97a9419'; r'69b59b582a7b165b8076a1478d0d433ec0846ccb';
/// See also [ensureIconAvailable]. /// See also [bangDataEnsureIcon].
@ProviderFor(ensureIconAvailable) @ProviderFor(bangDataEnsureIcon)
const ensureIconAvailableProvider = EnsureIconAvailableFamily(); const bangDataEnsureIconProvider = BangDataEnsureIconFamily();
/// See also [ensureIconAvailable]. /// See also [bangDataEnsureIcon].
class EnsureIconAvailableFamily extends Family<AsyncValue<BangData>> { class BangDataEnsureIconFamily extends Family<AsyncValue<BangData>> {
/// See also [ensureIconAvailable]. /// See also [bangDataEnsureIcon].
const EnsureIconAvailableFamily(); const BangDataEnsureIconFamily();
/// See also [ensureIconAvailable]. /// See also [bangDataEnsureIcon].
EnsureIconAvailableProvider call( BangDataEnsureIconProvider call(
BangData bang, BangData bang,
) { ) {
return EnsureIconAvailableProvider( return BangDataEnsureIconProvider(
bang, bang,
); );
} }
@override @override
EnsureIconAvailableProvider getProviderOverride( BangDataEnsureIconProvider getProviderOverride(
covariant EnsureIconAvailableProvider provider, covariant BangDataEnsureIconProvider provider,
) { ) {
return call( return call(
provider.bang, provider.bang,
@@ -239,32 +243,32 @@ class EnsureIconAvailableFamily extends Family<AsyncValue<BangData>> {
_allTransitiveDependencies; _allTransitiveDependencies;
@override @override
String? get name => r'ensureIconAvailableProvider'; String? get name => r'bangDataEnsureIconProvider';
} }
/// See also [ensureIconAvailable]. /// See also [bangDataEnsureIcon].
class EnsureIconAvailableProvider extends AutoDisposeFutureProvider<BangData> { class BangDataEnsureIconProvider extends AutoDisposeFutureProvider<BangData> {
/// See also [ensureIconAvailable]. /// See also [bangDataEnsureIcon].
EnsureIconAvailableProvider( BangDataEnsureIconProvider(
BangData bang, BangData bang,
) : this._internal( ) : this._internal(
(ref) => ensureIconAvailable( (ref) => bangDataEnsureIcon(
ref as EnsureIconAvailableRef, ref as BangDataEnsureIconRef,
bang, bang,
), ),
from: ensureIconAvailableProvider, from: bangDataEnsureIconProvider,
name: r'ensureIconAvailableProvider', name: r'bangDataEnsureIconProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$ensureIconAvailableHash, : _$bangDataEnsureIconHash,
dependencies: EnsureIconAvailableFamily._dependencies, dependencies: BangDataEnsureIconFamily._dependencies,
allTransitiveDependencies: allTransitiveDependencies:
EnsureIconAvailableFamily._allTransitiveDependencies, BangDataEnsureIconFamily._allTransitiveDependencies,
bang: bang, bang: bang,
); );
EnsureIconAvailableProvider._internal( BangDataEnsureIconProvider._internal(
super._createNotifier, { super._createNotifier, {
required super.name, required super.name,
required super.dependencies, required super.dependencies,
@@ -278,12 +282,12 @@ class EnsureIconAvailableProvider extends AutoDisposeFutureProvider<BangData> {
@override @override
Override overrideWith( Override overrideWith(
FutureOr<BangData> Function(EnsureIconAvailableRef provider) create, FutureOr<BangData> Function(BangDataEnsureIconRef provider) create,
) { ) {
return ProviderOverride( return ProviderOverride(
origin: this, origin: this,
override: EnsureIconAvailableProvider._internal( override: BangDataEnsureIconProvider._internal(
(ref) => create(ref as EnsureIconAvailableRef), (ref) => create(ref as BangDataEnsureIconRef),
from: from, from: from,
name: null, name: null,
dependencies: null, dependencies: null,
@@ -296,12 +300,12 @@ class EnsureIconAvailableProvider extends AutoDisposeFutureProvider<BangData> {
@override @override
AutoDisposeFutureProviderElement<BangData> createElement() { AutoDisposeFutureProviderElement<BangData> createElement() {
return _EnsureIconAvailableProviderElement(this); return _BangDataEnsureIconProviderElement(this);
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return other is EnsureIconAvailableProvider && other.bang == bang; return other is BangDataEnsureIconProvider && other.bang == bang;
} }
@override @override
@@ -313,18 +317,275 @@ class EnsureIconAvailableProvider extends AutoDisposeFutureProvider<BangData> {
} }
} }
mixin EnsureIconAvailableRef on AutoDisposeFutureProviderRef<BangData> { mixin BangDataEnsureIconRef on AutoDisposeFutureProviderRef<BangData> {
/// The parameter `bang` of this provider. /// The parameter `bang` of this provider.
BangData get bang; BangData get bang;
} }
class _EnsureIconAvailableProviderElement class _BangDataEnsureIconProviderElement
extends AutoDisposeFutureProviderElement<BangData> extends AutoDisposeFutureProviderElement<BangData>
with EnsureIconAvailableRef { with BangDataEnsureIconRef {
_EnsureIconAvailableProviderElement(super.provider); _BangDataEnsureIconProviderElement(super.provider);
@override @override
BangData get bang => (origin as EnsureIconAvailableProvider).bang; BangData get bang => (origin as BangDataEnsureIconProvider).bang;
}
String _$lastSyncOfGroupHash() => r'4906dc24970eccda89d94697f98cf70e241ea4ec';
/// See also [lastSyncOfGroup].
@ProviderFor(lastSyncOfGroup)
const lastSyncOfGroupProvider = LastSyncOfGroupFamily();
/// See also [lastSyncOfGroup].
class LastSyncOfGroupFamily extends Family<AsyncValue<DateTime?>> {
/// See also [lastSyncOfGroup].
const LastSyncOfGroupFamily();
/// See also [lastSyncOfGroup].
LastSyncOfGroupProvider call(
BangGroup group,
) {
return LastSyncOfGroupProvider(
group,
);
}
@override
LastSyncOfGroupProvider getProviderOverride(
covariant LastSyncOfGroupProvider provider,
) {
return call(
provider.group,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'lastSyncOfGroupProvider';
}
/// See also [lastSyncOfGroup].
class LastSyncOfGroupProvider extends AutoDisposeStreamProvider<DateTime?> {
/// See also [lastSyncOfGroup].
LastSyncOfGroupProvider(
BangGroup group,
) : this._internal(
(ref) => lastSyncOfGroup(
ref as LastSyncOfGroupRef,
group,
),
from: lastSyncOfGroupProvider,
name: r'lastSyncOfGroupProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$lastSyncOfGroupHash,
dependencies: LastSyncOfGroupFamily._dependencies,
allTransitiveDependencies:
LastSyncOfGroupFamily._allTransitiveDependencies,
group: group,
);
LastSyncOfGroupProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.group,
}) : super.internal();
final BangGroup group;
@override
Override overrideWith(
Stream<DateTime?> Function(LastSyncOfGroupRef provider) create,
) {
return ProviderOverride(
origin: this,
override: LastSyncOfGroupProvider._internal(
(ref) => create(ref as LastSyncOfGroupRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
group: group,
),
);
}
@override
AutoDisposeStreamProviderElement<DateTime?> createElement() {
return _LastSyncOfGroupProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is LastSyncOfGroupProvider && other.group == group;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, group.hashCode);
return _SystemHash.finish(hash);
}
}
mixin LastSyncOfGroupRef on AutoDisposeStreamProviderRef<DateTime?> {
/// The parameter `group` of this provider.
BangGroup get group;
}
class _LastSyncOfGroupProviderElement
extends AutoDisposeStreamProviderElement<DateTime?>
with LastSyncOfGroupRef {
_LastSyncOfGroupProviderElement(super.provider);
@override
BangGroup get group => (origin as LastSyncOfGroupProvider).group;
}
String _$bangCountOfGroupHash() => r'10b363679230c0abf6a6ba7d0be2d0367a7ed215';
/// See also [bangCountOfGroup].
@ProviderFor(bangCountOfGroup)
const bangCountOfGroupProvider = BangCountOfGroupFamily();
/// See also [bangCountOfGroup].
class BangCountOfGroupFamily extends Family<AsyncValue<int>> {
/// See also [bangCountOfGroup].
const BangCountOfGroupFamily();
/// See also [bangCountOfGroup].
BangCountOfGroupProvider call(
BangGroup group,
) {
return BangCountOfGroupProvider(
group,
);
}
@override
BangCountOfGroupProvider getProviderOverride(
covariant BangCountOfGroupProvider provider,
) {
return call(
provider.group,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'bangCountOfGroupProvider';
}
/// See also [bangCountOfGroup].
class BangCountOfGroupProvider extends AutoDisposeStreamProvider<int> {
/// See also [bangCountOfGroup].
BangCountOfGroupProvider(
BangGroup group,
) : this._internal(
(ref) => bangCountOfGroup(
ref as BangCountOfGroupRef,
group,
),
from: bangCountOfGroupProvider,
name: r'bangCountOfGroupProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$bangCountOfGroupHash,
dependencies: BangCountOfGroupFamily._dependencies,
allTransitiveDependencies:
BangCountOfGroupFamily._allTransitiveDependencies,
group: group,
);
BangCountOfGroupProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.group,
}) : super.internal();
final BangGroup group;
@override
Override overrideWith(
Stream<int> Function(BangCountOfGroupRef provider) create,
) {
return ProviderOverride(
origin: this,
override: BangCountOfGroupProvider._internal(
(ref) => create(ref as BangCountOfGroupRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
group: group,
),
);
}
@override
AutoDisposeStreamProviderElement<int> createElement() {
return _BangCountOfGroupProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is BangCountOfGroupProvider && other.group == group;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, group.hashCode);
return _SystemHash.finish(hash);
}
}
mixin BangCountOfGroupRef on AutoDisposeStreamProviderRef<int> {
/// The parameter `group` of this provider.
BangGroup get group;
}
class _BangCountOfGroupProviderElement
extends AutoDisposeStreamProviderElement<int> with BangCountOfGroupRef {
_BangCountOfGroupProviderElement(super.provider);
@override
BangGroup get group => (origin as BangCountOfGroupProvider).group;
} }
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -1,154 +0,0 @@
import 'package:bang_navigator/domain/services/generic_website.dart';
import 'package:bang_navigator/features/bangs/data/database/database.dart';
import 'package:bang_navigator/features/bangs/data/models/bang.dart';
import 'package:bang_navigator/features/bangs/data/models/bang_data.dart';
import 'package:bang_navigator/features/bangs/data/providers.dart';
import 'package:bang_navigator/features/bangs/data/services/bang_source.dart';
import 'package:bang_navigator/utils/image_helper.dart';
import 'package:drift/isolate.dart';
import 'package:exceptions/exceptions.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'bang.g.dart';
Future<Result<void>> _syncBangGroupStatic(
BangSource source,
BangDatabase db,
Uri url,
BangGroup? group,
) async {
final result = await source.getBangs(url, group);
return result.flatMapAsync(
(value) async {
final sourcedBangMap =
Map.fromEntries(value.map((e) => MapEntry(e.trigger, e)));
final localBangMap =
await db.bangDao.getBangs(groups: [group]).get().then(
(bangs) =>
Map.fromEntries(bangs.map((e) => MapEntry(e.trigger, e))),
);
final sourcedBangTriggers = sourcedBangMap.keys.toSet();
final localBangTriggers = localBangMap.keys.toSet();
final removedBangs = localBangTriggers.difference(sourcedBangTriggers);
final addedBangs = sourcedBangTriggers.difference(localBangTriggers).map(
(e) => sourcedBangMap[e]!,
);
final changedBangs = sourcedBangTriggers
.intersection(localBangTriggers)
.where((e) => sourcedBangMap[e] != localBangMap[e])
.map(
(e) => sourcedBangMap[e]!,
);
await db.bangDao.deleteBangs(removedBangs);
await db.bangDao.insertBangs(addedBangs);
await db.bangDao.replaceBangs(changedBangs);
await db.optimizeFtsIndex();
},
);
}
@Riverpod(keepAlive: true)
class BangRepository extends _$BangRepository {
late BangDatabase _db;
@override
void build() {
_db = ref.watch(bangDatabaseProvider);
}
Stream<BangData?> watchBang(String? trigger) {
if (trigger != null) {
return _db.bangDao.getBangData(trigger).watchSingleOrNull();
} else {
return Stream.value(null);
}
}
Stream<List<BangData>> watchBangs({Iterable<BangGroup?>? groups}) {
return _db.bangDao.getBangDataList(groups: groups).watch();
}
Stream<List<BangData>> watchFrequentBangs({Iterable<BangGroup?>? groups}) {
return _db.bangDao.getFrequentBangDataList(groups: groups).watch();
}
Future<void> increaseFrequency(String trigger) {
return _db.bangDao.increaseFrequency(trigger);
}
Future<Result<BangData>> ensureIconAvailable(BangData bang) async {
if (bang.iconData != null) {
return Result.success(bang);
}
final result = await ref
.read(genericWebsiteServiceProvider.notifier)
.getFaviconBytes(Uri.parse(bang.getUrl('').origin));
return result.flatMapAsync(
(faviconBytes) async {
if (faviconBytes != null && await isImageValid(faviconBytes)) {
await _db.bangDao.upsertIcon(bang.trigger, faviconBytes);
return bang.copyWith.iconData(faviconBytes);
}
return bang;
},
);
}
Future<Result<void>> syncBangGroup(
Uri url,
BangGroup? group,
) =>
_syncBangGroupStatic(
ref.read(bangSourceProvider.notifier),
_db,
url,
group,
);
Future<void> syncAllBangGroups() async {
await _db.computeWithDatabase(
connect: BangDatabase.new,
computation: (db) async {
final ref = ProviderContainer();
final source = ref.read(bangSourceProvider.notifier);
await _syncBangGroupStatic(
source,
db,
Uri.parse(
'https://raw.githubusercontent.com/kagisearch/bangs/main/data/bangs.json',
),
null,
);
await _syncBangGroupStatic(
source,
db,
Uri.parse(
'https://raw.githubusercontent.com/kagisearch/bangs/main/data/kagi_bangs.json',
),
BangGroup.kagi,
);
await _syncBangGroupStatic(
source,
db,
Uri.parse(
'https://raw.githubusercontent.com/kagisearch/bangs/main/data/assistant_bangs.json',
),
BangGroup.assistant,
);
},
);
}
}
@@ -0,0 +1,74 @@
import 'package:bang_navigator/domain/services/generic_website.dart';
import 'package:bang_navigator/features/bangs/data/database/database.dart';
import 'package:bang_navigator/features/bangs/data/models/bang.dart';
import 'package:bang_navigator/features/bangs/data/models/bang_data.dart';
import 'package:bang_navigator/features/bangs/data/providers.dart';
import 'package:bang_navigator/utils/image_helper.dart';
import 'package:drift/drift.dart';
import 'package:exceptions/exceptions.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'bang_data.g.dart';
@Riverpod(keepAlive: true)
class BangDataRepository extends _$BangDataRepository {
late BangDatabase _db;
@override
void build() {
_db = ref.watch(bangDatabaseProvider);
}
Stream<BangData?> watchBang(String? trigger) {
if (trigger != null) {
return _db.bangDao.getBangData(trigger).watchSingleOrNull();
} else {
return Stream.value(null);
}
}
Stream<int> watchBangCount(BangGroup group) {
return _db.bangDao.getBangCount(groups: [group]).watchSingle();
}
Stream<List<BangData>> watchBangs({Iterable<BangGroup>? groups}) {
return _db.bangDao.getBangDataList(groups: groups).watch();
}
Stream<List<BangData>> watchFrequentBangs({Iterable<BangGroup>? groups}) {
return _db.bangDao.getFrequentBangDataList(groups: groups).watch();
}
Future<void> increaseFrequency(String trigger) {
return _db.bangDao.increaseBangFrequency(trigger);
}
Future<Result<BangData>> ensureIconAvailable(BangData bang) async {
if (bang.iconData != null) {
return Result.success(bang);
}
final result = await ref
.read(genericWebsiteServiceProvider.notifier)
.getFaviconBytes(Uri.parse(bang.getUrl('').origin));
return result.flatMapAsync(
(faviconBytes) async {
if (faviconBytes != null && await isImageValid(faviconBytes)) {
await _db.bangDao.upsertBangIcon(bang.trigger, faviconBytes);
return bang.copyWith.iconData(faviconBytes);
}
return bang;
},
);
}
Future<int> clearIconData() {
return _db.bangIcon.deleteAll();
}
Future<int> clearFrequency() {
return _db.bangFrequency.deleteAll();
}
}
@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bang_data.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$bangDataRepositoryHash() =>
r'6ac3a59546f6d7e22f3c37aacd43a3579978eb50';
/// See also [BangDataRepository].
@ProviderFor(BangDataRepository)
final bangDataRepositoryProvider =
NotifierProvider<BangDataRepository, void>.internal(
BangDataRepository.new,
name: r'bangDataRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$bangDataRepositoryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$BangDataRepository = Notifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -0,0 +1,133 @@
import 'package:bang_navigator/features/bangs/data/database/database.dart';
import 'package:bang_navigator/features/bangs/data/models/bang.dart';
import 'package:bang_navigator/features/bangs/data/providers.dart';
import 'package:bang_navigator/features/bangs/data/services/bang_source.dart';
import 'package:drift/isolate.dart';
import 'package:exceptions/exceptions.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'sync.g.dart';
@Riverpod(keepAlive: true)
class BangSyncRepository extends _$BangSyncRepository {
late BangDatabase _db;
BangSyncRepository();
static Future<Result<void>> _fetchAndSync({
required BangSource sourceRepository,
required BangDatabase db,
required Uri url,
required BangGroup group,
required Duration? syncInterval,
}) async {
if (syncInterval != null) {
final lastSync =
await db.syncDao.lastSyncOfGroup(group).getSingleOrNull();
if (lastSync != null &&
lastSync.difference(DateTime.now()) < syncInterval) {
return Result.success(null);
}
}
final result = await sourceRepository.getBangs(url, group);
return result.flatMapAsync(
(remoteBangs) async {
await db.syncDao.syncBangs(
group: group,
remoteBangs: remoteBangs,
syncTime: DateTime.now(),
);
await db.optimizeFtsIndex();
},
);
}
Future<Result<void>> _syncBangGroup(
BangGroup group,
Uri url,
Duration? syncInterval,
) async {
try {
return Result.success(
await _db.computeWithDatabase(
connect: BangDatabase.new,
computation: (db) async {
final ref = ProviderContainer();
final result = await _fetchAndSync(
sourceRepository: ref.read(bangSourceProvider.notifier),
db: db,
url: url,
group: group,
syncInterval: syncInterval,
);
//Throw if necessary
return result.value;
},
),
);
} catch (e) {
return Result.failure(
ErrorMessage(
message: "Failed to sync Bangs (${group.name})",
source: 'BangSync',
details: e,
),
);
}
}
Stream<DateTime?> watchLastSyncOfGroup(BangGroup group) {
return _db.syncDao.lastSyncOfGroup(group).watchSingleOrNull();
}
Future<Result<void>> syncGeneralBangs({Duration? syncInterval}) =>
_syncBangGroup(
BangGroup.general,
Uri.parse(
'https://raw.githubusercontent.com/kagisearch/bangs/main/data/bangs.json',
),
syncInterval,
);
Future<Result<void>> syncKagiBangs({Duration? syncInterval}) =>
_syncBangGroup(
BangGroup.kagi,
Uri.parse(
'https://raw.githubusercontent.com/kagisearch/bangs/main/data/kagi_bangs.json',
),
syncInterval,
);
Future<Result<void>> syncAssistantBangs({Duration? syncInterval}) =>
_syncBangGroup(
BangGroup.assistant,
Uri.parse(
'https://raw.githubusercontent.com/kagisearch/bangs/main/data/assistant_bangs.json',
),
syncInterval,
);
Future<Map<BangGroup, Result<void>>> syncAllBangGroups({
Duration? syncInterval,
}) async {
//Run isolated operations
final generalSync = syncGeneralBangs(syncInterval: syncInterval);
final assistantSync = syncAssistantBangs(syncInterval: syncInterval);
final kagiSync = syncKagiBangs(syncInterval: syncInterval);
return {
BangGroup.general: await generalSync,
BangGroup.assistant: await assistantSync,
BangGroup.kagi: await kagiSync,
};
}
@override
void build() {
_db = ref.watch(bangDatabaseProvider);
}
}
@@ -1,25 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bang.dart'; part of 'sync.dart';
// ************************************************************************** // **************************************************************************
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$bangRepositoryHash() => r'd405550e2ad11c57f3644f18390348efb71bdf0f'; String _$bangSyncRepositoryHash() =>
r'79b9c561638e96eb404ceeecb4b50dec31f0c610';
/// See also [BangRepository]. /// See also [BangSyncRepository].
@ProviderFor(BangRepository) @ProviderFor(BangSyncRepository)
final bangRepositoryProvider = NotifierProvider<BangRepository, void>.internal( final bangSyncRepositoryProvider =
BangRepository.new, NotifierProvider<BangSyncRepository, void>.internal(
name: r'bangRepositoryProvider', BangSyncRepository.new,
name: r'bangSyncRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$bangRepositoryHash, : _$bangSyncRepositoryHash,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef _$BangRepository = Notifier<void>; typedef _$BangSyncRepository = Notifier<void>;
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -12,7 +12,7 @@ class BangScreen extends HookConsumerWidget {
const BangScreen({super.key}); const BangScreen({super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final bangsAsync = ref.watch(allBangsProvider); final bangsAsync = ref.watch(bangDataListProvider);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@@ -13,7 +13,7 @@ class BangIcon extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final bangAsync = ref.watch(ensureIconAvailableProvider(bangData)); final bangAsync = ref.watch(bangDataEnsureIconProvider(bangData));
final bang = bangAsync.valueOrNull ?? bangData; final bang = bangAsync.valueOrNull ?? bangData;
return Skeletonizer( return Skeletonizer(
@@ -3,7 +3,7 @@
import 'dart:async'; import 'dart:async';
import 'package:bang_navigator/features/bangs/data/models/bang_data.dart'; import 'package:bang_navigator/features/bangs/data/models/bang_data.dart';
import 'package:bang_navigator/features/bangs/domain/repositories/bang.dart'; import 'package:bang_navigator/features/bangs/domain/repositories/bang_data.dart';
import 'package:bang_navigator/features/search_browser/domain/entities/sheet.dart'; import 'package:bang_navigator/features/search_browser/domain/entities/sheet.dart';
import 'package:bang_navigator/features/search_browser/domain/services/create_tab.dart'; import 'package:bang_navigator/features/search_browser/domain/services/create_tab.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@@ -86,7 +86,7 @@ class SelectedBangTrigger extends _$SelectedBangTrigger {
@Riverpod() @Riverpod()
Stream<BangData?> selectedBangData(SelectedBangDataRef ref) { Stream<BangData?> selectedBangData(SelectedBangDataRef ref) {
final repository = ref.watch(bangRepositoryProvider.notifier); final repository = ref.watch(bangDataRepositoryProvider.notifier);
final selectedBangTrigger = ref.watch(selectedBangTriggerProvider); final selectedBangTrigger = ref.watch(selectedBangTriggerProvider);
return repository.watchBang(selectedBangTrigger); return repository.watchBang(selectedBangTrigger);
} }
@@ -6,7 +6,7 @@ part of 'providers.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$selectedBangDataHash() => r'6d99a08f4a02027cc007ee1df30e5ac7923ca5f5'; String _$selectedBangDataHash() => r'619555a048e9d38602ceffa42dae1ba7d68ced89';
/// See also [selectedBangData]. /// See also [selectedBangData].
@ProviderFor(selectedBangData) @ProviderFor(selectedBangData)
@@ -22,7 +22,7 @@ class BangChips extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final frequentBangsAsync = ref.watch(frequentBangsProvider); final frequentBangsAsync = ref.watch(frequentBangDataListProvider);
return frequentBangsAsync.when( return frequentBangsAsync.when(
data: (bangs) { data: (bangs) {
@@ -1,5 +1,5 @@
import 'package:bang_navigator/features/bangs/domain/providers.dart'; import 'package:bang_navigator/features/bangs/domain/providers.dart';
import 'package:bang_navigator/features/bangs/domain/repositories/bang.dart'; import 'package:bang_navigator/features/bangs/domain/repositories/bang_data.dart';
import 'package:bang_navigator/features/bangs/presentation/widgets/bang_icon.dart'; import 'package:bang_navigator/features/bangs/presentation/widgets/bang_icon.dart';
import 'package:bang_navigator/features/kagi/domain/repositories/autosuggest.dart'; import 'package:bang_navigator/features/kagi/domain/repositories/autosuggest.dart';
import 'package:bang_navigator/features/search_browser/domain/providers.dart'; import 'package:bang_navigator/features/search_browser/domain/providers.dart';
@@ -109,7 +109,7 @@ class SearchTab extends HookConsumerWidget {
final focusNode = useFocusNode(); final focusNode = useFocusNode();
final selectedBangAsync = ref.watch(selectedBangDataProvider); final selectedBangAsync = ref.watch(selectedBangDataProvider);
final defaultSearchBangAsync = ref.watch(kagiSearchBangProvider); final defaultSearchBangAsync = ref.watch(kagiSearchBangDataProvider);
final activeBang = final activeBang =
selectedBangAsync.valueOrNull ?? defaultSearchBangAsync.valueOrNull; selectedBangAsync.valueOrNull ?? defaultSearchBangAsync.valueOrNull;
@@ -231,7 +231,7 @@ class SearchTab extends HookConsumerWidget {
if (activeBang != null && if (activeBang != null &&
(formKey.currentState?.validate() == true)) { (formKey.currentState?.validate() == true)) {
await ref await ref
.read(bangRepositoryProvider.notifier) .read(bangDataRepositoryProvider.notifier)
.increaseFrequency(activeBang.trigger); .increaseFrequency(activeBang.trigger);
onSubmit(activeBang.getUrl(textController.text)); onSubmit(activeBang.getUrl(textController.text));
@@ -266,7 +266,7 @@ class SearchTab extends HookConsumerWidget {
if (activeBang != null && if (activeBang != null &&
(formKey.currentState?.validate() == true)) { (formKey.currentState?.validate() == true)) {
await ref await ref
.read(bangRepositoryProvider.notifier) .read(bangDataRepositoryProvider.notifier)
.increaseFrequency(activeBang.trigger); .increaseFrequency(activeBang.trigger);
onSubmit(activeBang.getUrl(textController.text)); onSubmit(activeBang.getUrl(textController.text));
@@ -158,7 +158,7 @@ class _WebViewState extends ConsumerState<WebView> {
if (selectedText != null && selectedText.isNotEmpty) { if (selectedText != null && selectedText.isNotEmpty) {
final searchBang = final searchBang =
await ref.read(kagiSearchBangProvider.future); await ref.read(kagiSearchBangDataProvider.future);
await ref await ref
.read(switchNewTabControllerProvider.notifier) .read(switchNewTabControllerProvider.notifier)
+3 -3
View File
@@ -1,7 +1,7 @@
import 'package:bang_navigator/core/logger.dart'; import 'package:bang_navigator/core/logger.dart';
import 'package:bang_navigator/core/providers.dart'; import 'package:bang_navigator/core/providers.dart';
import 'package:bang_navigator/features/about/data/repositories/package_info_repository.dart'; import 'package:bang_navigator/features/about/data/repositories/package_info_repository.dart';
import 'package:bang_navigator/features/bangs/domain/repositories/bang.dart'; import 'package:bang_navigator/features/bangs/domain/repositories/sync.dart';
import 'package:bang_navigator/features/search_browser/domain/services/session.dart'; import 'package:bang_navigator/features/search_browser/domain/services/session.dart';
import 'package:bang_navigator/features/settings/data/repositories/settings_repository.dart'; import 'package:bang_navigator/features/settings/data/repositories/settings_repository.dart';
import 'package:bang_navigator/presentation/hooks/on_initialization.dart'; import 'package:bang_navigator/presentation/hooks/on_initialization.dart';
@@ -42,8 +42,8 @@ void main() async {
await ref.read(packageInfoProvider.future); await ref.read(packageInfoProvider.future);
await ref await ref
.read(bangRepositoryProvider.notifier) .read(bangSyncRepositoryProvider.notifier)
.syncAllBangGroups(); .syncAllBangGroups(syncInterval: const Duration(days: 7));
final settings = final settings =
await ref.read(settingsRepositoryProvider.future); await ref.read(settingsRepositoryProvider.future);