Add sqlite store
Optimize android quick action Optimize backup and restore Optimize more details
This commit is contained in:
78
lib/database/database.dart
Normal file
78
lib/database/database.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
|
||||
part 'generated/database.g.dart';
|
||||
part 'links.dart';
|
||||
part 'profiles.dart';
|
||||
part 'rules.dart';
|
||||
part 'scripts.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [Profiles, Scripts, Rules, ProfileRuleLinks],
|
||||
daos: [ProfilesDao, ScriptsDao, RulesDao],
|
||||
)
|
||||
class Database extends _$Database {
|
||||
Database([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
static LazyDatabase _openConnection() {
|
||||
return LazyDatabase(() async {
|
||||
final databaseFile = File(await appPath.databasePath);
|
||||
return NativeDatabase.createInBackground(databaseFile);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> restore(
|
||||
List<Profile> profiles,
|
||||
List<Script> scripts,
|
||||
List<Rule> rules,
|
||||
List<ProfileRuleLink> links, {
|
||||
bool isOverride = false,
|
||||
}) async {
|
||||
if (profiles.isNotEmpty ||
|
||||
scripts.isNotEmpty ||
|
||||
rules.isNotEmpty ||
|
||||
links.isNotEmpty) {
|
||||
await batch((b) {
|
||||
isOverride
|
||||
? profilesDao.setAllWithBatch(b, profiles)
|
||||
: profilesDao.putAllWithBatch(
|
||||
b,
|
||||
profiles.map((item) => item.toCompanion()),
|
||||
);
|
||||
scriptsDao.setAllWithBatch(b, scripts);
|
||||
rulesDao.restoreWithBatch(b, rules, links);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TableInfoExt<Tbl extends Table, Row> on TableInfo<Tbl, Row> {
|
||||
void setAll(
|
||||
Batch batch,
|
||||
Iterable<Insertable<Row>> items, {
|
||||
required Expression<bool> Function(Tbl tbl) deleteFilter,
|
||||
}) async {
|
||||
batch.insertAllOnConflictUpdate(this, items);
|
||||
batch.deleteWhere(this, deleteFilter);
|
||||
}
|
||||
|
||||
Future<int> remove(Expression<bool> Function(Tbl tbl) filter) async {
|
||||
return await (delete()..where(filter)).go();
|
||||
}
|
||||
|
||||
Future<int> put(Insertable<Row> item) async {
|
||||
return await insertOnConflictUpdate(item);
|
||||
}
|
||||
}
|
||||
|
||||
final database = Database();
|
||||
2941
lib/database/generated/database.g.dart
Normal file
2941
lib/database/generated/database.g.dart
Normal file
File diff suppressed because it is too large
Load Diff
52
lib/database/links.dart
Normal file
52
lib/database/links.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
part of 'database.dart';
|
||||
|
||||
@DataClassName('RawProfileRuleLink')
|
||||
@TableIndex(
|
||||
name: 'idx_profile_scene_order',
|
||||
columns: {#profileId, #scene, #order},
|
||||
)
|
||||
class ProfileRuleLinks extends Table {
|
||||
@override
|
||||
String get tableName => 'profile_rule_mapping';
|
||||
|
||||
TextColumn get id => text()();
|
||||
|
||||
IntColumn get profileId => integer().nullable().references(
|
||||
Profiles,
|
||||
#id,
|
||||
onDelete: KeyAction.cascade,
|
||||
)();
|
||||
|
||||
IntColumn get ruleId =>
|
||||
integer().references(Rules, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get scene => textEnum<RuleScene>().nullable()();
|
||||
|
||||
TextColumn get order => text().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
extension RawProfileRuleLinkExt on RawProfileRuleLink {
|
||||
ProfileRuleLink toLink() {
|
||||
return ProfileRuleLink(
|
||||
profileId: profileId,
|
||||
ruleId: ruleId,
|
||||
scene: scene,
|
||||
order: order,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ProfileRuleLinksCompanionExt on ProfileRuleLink {
|
||||
ProfileRuleLinksCompanion toCompanion() {
|
||||
return ProfileRuleLinksCompanion.insert(
|
||||
id: key,
|
||||
ruleId: ruleId,
|
||||
scene: Value(scene),
|
||||
profileId: Value(profileId),
|
||||
order: Value(order),
|
||||
);
|
||||
}
|
||||
}
|
||||
168
lib/database/profiles.dart
Normal file
168
lib/database/profiles.dart
Normal file
@@ -0,0 +1,168 @@
|
||||
part of 'database.dart';
|
||||
|
||||
@DataClassName('RawProfile')
|
||||
class Profiles extends Table {
|
||||
@override
|
||||
String get tableName => 'profiles';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
|
||||
TextColumn get label => text()();
|
||||
|
||||
TextColumn get currentGroupName => text().nullable()();
|
||||
|
||||
TextColumn get url => text()();
|
||||
|
||||
DateTimeColumn get lastUpdateDate => dateTime().nullable()();
|
||||
|
||||
TextColumn get overwriteType => textEnum<OverwriteType>()();
|
||||
|
||||
IntColumn get scriptId => integer().nullable()();
|
||||
|
||||
IntColumn get autoUpdateDurationMillis => integer()();
|
||||
|
||||
TextColumn get subscriptionInfo =>
|
||||
text().map(const SubscriptionInfoConverter()).nullable()();
|
||||
|
||||
BoolColumn get autoUpdate => boolean()();
|
||||
|
||||
TextColumn get selectedMap => text().map(const StringMapConverter())();
|
||||
|
||||
TextColumn get unfoldSet => text().map(const StringSetConverter())();
|
||||
|
||||
IntColumn get order => integer().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
class SubscriptionInfoConverter
|
||||
extends TypeConverter<SubscriptionInfo?, String?> {
|
||||
const SubscriptionInfoConverter();
|
||||
|
||||
@override
|
||||
SubscriptionInfo? fromSql(String? fromDb) {
|
||||
if (fromDb == null) return null;
|
||||
return SubscriptionInfo.fromJson(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String? toSql(SubscriptionInfo? value) {
|
||||
if (value == null) return null;
|
||||
return json.encode(value.toJson());
|
||||
}
|
||||
}
|
||||
|
||||
@DriftAccessor(tables: [Profiles])
|
||||
class ProfilesDao extends DatabaseAccessor<Database> with _$ProfilesDaoMixin {
|
||||
ProfilesDao(super.attachedDatabase);
|
||||
|
||||
Selectable<Profile> all() {
|
||||
final stmt = profiles.select();
|
||||
stmt.orderBy([
|
||||
(t) => OrderingTerm(expression: t.order, nulls: NullsOrder.last),
|
||||
(t) => OrderingTerm.asc(t.id),
|
||||
]);
|
||||
return stmt.map((item) => item.toProfile());
|
||||
}
|
||||
|
||||
Future<void> setAll(Iterable<Profile> profiles) async {
|
||||
await batch((b) async {
|
||||
setAllWithBatch(b, profiles);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> putAll<T extends Table, D extends DataClass>(
|
||||
Iterable<Insertable<D>> items,
|
||||
) async {
|
||||
await batch((b) async {
|
||||
putAllWithBatch(b, items);
|
||||
});
|
||||
}
|
||||
|
||||
void putAllWithBatch<T extends Table, D extends DataClass>(
|
||||
Batch batch,
|
||||
Iterable<Insertable<D>> items,
|
||||
) {
|
||||
batch.insertAllOnConflictUpdate(profiles, items);
|
||||
}
|
||||
|
||||
void setAllWithBatch(Batch batch, Iterable<Profile> profiles) {
|
||||
final List<ProfilesCompanion> items = [];
|
||||
final List<int> ids = [];
|
||||
profiles.forEachIndexed((index, profile) {
|
||||
ids.add(profile.id);
|
||||
items.add(profile.toCompanion(index));
|
||||
});
|
||||
|
||||
this.profiles.setAll(batch, items, deleteFilter: (t) => t.id.isNotIn(ids));
|
||||
}
|
||||
}
|
||||
|
||||
class StringMapConverter extends TypeConverter<Map<String, String>, String> {
|
||||
const StringMapConverter();
|
||||
|
||||
@override
|
||||
Map<String, String> fromSql(String fromDb) {
|
||||
return Map<String, String>.from(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(Map<String, String> value) {
|
||||
return json.encode(value);
|
||||
}
|
||||
}
|
||||
|
||||
class StringSetConverter extends TypeConverter<Set<String>, String> {
|
||||
const StringSetConverter();
|
||||
|
||||
@override
|
||||
Set<String> fromSql(String fromDb) {
|
||||
return Set<String>.from(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(Set<String> value) {
|
||||
return json.encode(value.toList());
|
||||
}
|
||||
}
|
||||
|
||||
extension RawProfilExt on RawProfile {
|
||||
Profile toProfile() {
|
||||
return Profile(
|
||||
id: id,
|
||||
label: label,
|
||||
currentGroupName: currentGroupName,
|
||||
url: url,
|
||||
lastUpdateDate: lastUpdateDate,
|
||||
autoUpdateDuration: Duration(milliseconds: autoUpdateDurationMillis),
|
||||
subscriptionInfo: subscriptionInfo,
|
||||
autoUpdate: autoUpdate,
|
||||
selectedMap: selectedMap,
|
||||
unfoldSet: unfoldSet,
|
||||
overwriteType: overwriteType,
|
||||
scriptId: scriptId,
|
||||
order: order,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ProfilesCompanionExt on Profile {
|
||||
ProfilesCompanion toCompanion([int? order]) {
|
||||
return ProfilesCompanion.insert(
|
||||
id: Value(id),
|
||||
label: label,
|
||||
currentGroupName: Value(currentGroupName),
|
||||
url: url,
|
||||
lastUpdateDate: Value(lastUpdateDate),
|
||||
autoUpdateDurationMillis: autoUpdateDuration.inMilliseconds,
|
||||
subscriptionInfo: Value(subscriptionInfo),
|
||||
autoUpdate: autoUpdate,
|
||||
selectedMap: selectedMap,
|
||||
unfoldSet: unfoldSet,
|
||||
overwriteType: overwriteType,
|
||||
scriptId: Value(scriptId),
|
||||
order: Value(order ?? this.order),
|
||||
);
|
||||
}
|
||||
}
|
||||
283
lib/database/rules.dart
Normal file
283
lib/database/rules.dart
Normal file
@@ -0,0 +1,283 @@
|
||||
part of 'database.dart';
|
||||
|
||||
@DataClassName('RawRule')
|
||||
class Rules extends Table {
|
||||
@override
|
||||
String get tableName => 'rules';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
|
||||
TextColumn get value => text()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
@DriftAccessor(tables: [Rules, ProfileRuleLinks])
|
||||
class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
RulesDao(super.attachedDatabase);
|
||||
|
||||
Selectable<Rule> allGlobalAddedRules() {
|
||||
return _get();
|
||||
}
|
||||
|
||||
Selectable<Rule> allProfileAddedRules(int profileId) {
|
||||
return _get(profileId: profileId, scene: RuleScene.added);
|
||||
}
|
||||
|
||||
Selectable<Rule> allProfileDisabledRules(int profileId) {
|
||||
return _get(profileId: profileId, scene: RuleScene.disabled);
|
||||
}
|
||||
|
||||
Selectable<Rule> allAddedRules(int profileId) {
|
||||
final disabledIdsQuery = selectOnly(profileRuleLinks)
|
||||
..addColumns([profileRuleLinks.ruleId])
|
||||
..where(
|
||||
profileRuleLinks.profileId.equals(profileId) &
|
||||
profileRuleLinks.scene.equalsValue(RuleScene.disabled),
|
||||
);
|
||||
|
||||
final query = select(rules).join([
|
||||
innerJoin(profileRuleLinks, profileRuleLinks.ruleId.equalsExp(rules.id)),
|
||||
]);
|
||||
|
||||
query.where(
|
||||
(profileRuleLinks.profileId.isNull() |
|
||||
(profileRuleLinks.profileId.equals(profileId) &
|
||||
profileRuleLinks.scene.equalsValue(RuleScene.added))) &
|
||||
profileRuleLinks.ruleId.isNotInQuery(disabledIdsQuery),
|
||||
);
|
||||
|
||||
query.orderBy([
|
||||
OrderingTerm.desc(
|
||||
profileRuleLinks.profileId.isNull().caseMatch<int>(
|
||||
when: {const Constant(true): const Constant(1)},
|
||||
orElse: const Constant(0),
|
||||
),
|
||||
),
|
||||
OrderingTerm.desc(profileRuleLinks.order),
|
||||
]);
|
||||
|
||||
return query.map((row) {
|
||||
final ruleData = row.readTable(rules);
|
||||
final order = row.read(profileRuleLinks.order);
|
||||
return ruleData.toRule(order);
|
||||
});
|
||||
}
|
||||
|
||||
void restoreWithBatch(
|
||||
Batch batch,
|
||||
Iterable<Rule> rules,
|
||||
Iterable<ProfileRuleLink> links,
|
||||
) {
|
||||
batch.insertAllOnConflictUpdate(
|
||||
this.rules,
|
||||
rules.map((item) => item.toCompanion()),
|
||||
);
|
||||
final ruleIds = rules.map((item) => item.id);
|
||||
batch.deleteWhere(this.rules, (t) => t.id.isNotIn(ruleIds));
|
||||
batch.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
links.map((item) => item.toCompanion()),
|
||||
);
|
||||
final linkKeys = links.map((item) => item.key);
|
||||
batch.deleteWhere(profileRuleLinks, (t) => t.id.isNotIn(linkKeys));
|
||||
}
|
||||
|
||||
Future<void> delRules(Iterable<int> ruleIds) {
|
||||
return _delAll(ruleIds);
|
||||
}
|
||||
|
||||
Future<void> putGlobalRule(Rule rule) {
|
||||
return _put(rule);
|
||||
}
|
||||
|
||||
Future<void> putProfileAddedRule(int profileId, Rule rule) {
|
||||
return _put(rule, profileId: profileId, scene: RuleScene.added);
|
||||
}
|
||||
|
||||
Future<void> putProfileDisabledRule(int profileId, Rule rule) {
|
||||
return _put(rule, profileId: profileId, scene: RuleScene.added);
|
||||
}
|
||||
|
||||
Future<void> putGlobalRules(Iterable<Rule> rules) {
|
||||
return _putAll(rules);
|
||||
}
|
||||
|
||||
Future<void> setGlobalRules(Iterable<Rule> rules) {
|
||||
return _set(rules);
|
||||
}
|
||||
|
||||
Future<int> putDisabledLink(int profileId, int ruleId) async {
|
||||
return await profileRuleLinks.insertOnConflictUpdate(
|
||||
ProfileRuleLink(
|
||||
ruleId: ruleId,
|
||||
profileId: profileId,
|
||||
scene: RuleScene.disabled,
|
||||
).toCompanion(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> delDisabledLink(int profileId, int ruleId) async {
|
||||
return await profileRuleLinks.deleteOne(
|
||||
ProfileRuleLink(
|
||||
profileId: profileId,
|
||||
ruleId: ruleId,
|
||||
scene: RuleScene.disabled,
|
||||
).toCompanion(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> orderGlobalRule({
|
||||
required int ruleId,
|
||||
required String order,
|
||||
}) async {
|
||||
return await _order(ruleId: ruleId, order: order);
|
||||
}
|
||||
|
||||
Future<int> orderProfileAddedRule(
|
||||
int profileId, {
|
||||
required int ruleId,
|
||||
required String order,
|
||||
}) async {
|
||||
return await _order(
|
||||
ruleId: ruleId,
|
||||
order: order,
|
||||
profileId: profileId,
|
||||
scene: RuleScene.added,
|
||||
);
|
||||
}
|
||||
|
||||
Selectable<Rule> _get({int? profileId, RuleScene? scene}) {
|
||||
final query = select(rules).join([
|
||||
innerJoin(profileRuleLinks, profileRuleLinks.ruleId.equalsExp(rules.id)),
|
||||
]);
|
||||
|
||||
query.where(
|
||||
profileId == null
|
||||
? profileRuleLinks.profileId.isNull()
|
||||
: profileRuleLinks.profileId.equals(profileId) &
|
||||
profileRuleLinks.scene.equalsValue(scene),
|
||||
);
|
||||
|
||||
query.orderBy([
|
||||
OrderingTerm.desc(profileRuleLinks.order),
|
||||
OrderingTerm.desc(profileRuleLinks.id),
|
||||
]);
|
||||
|
||||
return query.map((row) {
|
||||
return row.readTable(rules).toRule(row.read(profileRuleLinks.order));
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> _order({
|
||||
required int ruleId,
|
||||
required String order,
|
||||
int? profileId,
|
||||
RuleScene? scene,
|
||||
}) async {
|
||||
final stmt = profileRuleLinks.update();
|
||||
stmt.where((t) {
|
||||
return (profileId == null
|
||||
? t.profileId.isNull()
|
||||
: t.profileId.equals(profileId)) &
|
||||
t.ruleId.equals(ruleId) &
|
||||
t.scene.equalsValue(scene);
|
||||
});
|
||||
return await stmt.write(ProfileRuleLinksCompanion(order: Value(order)));
|
||||
}
|
||||
|
||||
Future<int> _put(Rule rule, {int? profileId, RuleScene? scene}) async {
|
||||
return transaction(() async {
|
||||
final row = await rules.insertOnConflictUpdate(rule.toCompanion());
|
||||
if (row == 0) {
|
||||
return 0;
|
||||
}
|
||||
return await profileRuleLinks.insertOnConflictUpdate(
|
||||
ProfileRuleLink(
|
||||
ruleId: rule.id,
|
||||
profileId: profileId,
|
||||
scene: scene,
|
||||
).toCompanion(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _delAll(Iterable<int> ruleIds) async {
|
||||
await rules.deleteWhere((t) => t.id.isIn(ruleIds));
|
||||
}
|
||||
|
||||
Future<void> _putAll(
|
||||
Iterable<Rule> rules, {
|
||||
int? profileId,
|
||||
RuleScene? scene,
|
||||
}) async {
|
||||
await batch((b) {
|
||||
b.insertAllOnConflictUpdate(
|
||||
this.rules,
|
||||
rules.map((item) => item.toCompanion()),
|
||||
);
|
||||
b.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
rules.map(
|
||||
(item) => ProfileRuleLink(
|
||||
ruleId: item.id,
|
||||
profileId: profileId,
|
||||
scene: scene,
|
||||
).toCompanion(),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _set(
|
||||
Iterable<Rule> rules, {
|
||||
int? profileId,
|
||||
RuleScene? scene,
|
||||
}) async {
|
||||
await batch((b) {
|
||||
b.insertAllOnConflictUpdate(
|
||||
this.rules,
|
||||
rules.map((item) => item.toCompanion()),
|
||||
);
|
||||
|
||||
b.deleteWhere(
|
||||
profileRuleLinks,
|
||||
(t) =>
|
||||
(profileId == null
|
||||
? t.profileId.isNull()
|
||||
: t.profileId.equals(profileId)) &
|
||||
(scene == null ? const Constant(true) : t.scene.equalsValue(scene)),
|
||||
);
|
||||
|
||||
b.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
rules.map(
|
||||
(item) => ProfileRuleLink(
|
||||
ruleId: item.id,
|
||||
profileId: profileId,
|
||||
scene: scene,
|
||||
).toCompanion(),
|
||||
),
|
||||
);
|
||||
|
||||
b.deleteWhere(this.rules, (r) {
|
||||
final linkedIds = selectOnly(profileRuleLinks);
|
||||
linkedIds.addColumns([profileRuleLinks.ruleId]);
|
||||
return r.id.isNotInQuery(linkedIds);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension RawRuleExt on RawRule {
|
||||
Rule toRule([String? order]) {
|
||||
return Rule(id: id, value: value, order: order);
|
||||
}
|
||||
}
|
||||
|
||||
extension RulesCompanionExt on Rule {
|
||||
RulesCompanion toCompanion() {
|
||||
return RulesCompanion.insert(id: Value(id), value: value);
|
||||
}
|
||||
}
|
||||
63
lib/database/scripts.dart
Normal file
63
lib/database/scripts.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
part of 'database.dart';
|
||||
|
||||
@DataClassName('RawScript')
|
||||
class Scripts extends Table {
|
||||
@override
|
||||
String get tableName => 'scripts';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
|
||||
TextColumn get label => text()();
|
||||
|
||||
DateTimeColumn get lastUpdateTime => dateTime()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
@DriftAccessor(tables: [Scripts])
|
||||
class ScriptsDao extends DatabaseAccessor<Database> with _$ScriptsDaoMixin {
|
||||
ScriptsDao(super.attachedDatabase);
|
||||
|
||||
Selectable<Script> all() {
|
||||
return scripts.select().map((item) => item.toScript());
|
||||
}
|
||||
|
||||
Selectable<Script> get(int scriptId) {
|
||||
final stmt = scripts.select();
|
||||
stmt.where((t) => t.id.equals(scriptId));
|
||||
return stmt.map((it) => it.toScript());
|
||||
}
|
||||
|
||||
Future<void> setAll(Iterable<Script> scripts) async {
|
||||
await batch((b) async {
|
||||
await setAllWithBatch(b, scripts);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> setAllWithBatch(Batch batch, Iterable<Script> scripts) async {
|
||||
final List<ScriptsCompanion> items = [];
|
||||
final List<int> ids = [];
|
||||
for (final script in scripts) {
|
||||
ids.add(script.id);
|
||||
items.add(script.toCompanion());
|
||||
}
|
||||
this.scripts.setAll(batch, items, deleteFilter: (t) => t.id.isNotIn(ids));
|
||||
}
|
||||
}
|
||||
|
||||
extension RawScriptExt on RawScript {
|
||||
Script toScript() {
|
||||
return Script(id: id, label: label, lastUpdateTime: lastUpdateTime);
|
||||
}
|
||||
}
|
||||
|
||||
extension ScriptsCompanionExt on Script {
|
||||
ScriptsCompanion toCompanion() {
|
||||
return ScriptsCompanion.insert(
|
||||
id: Value(id),
|
||||
label: label,
|
||||
lastUpdateTime: lastUpdateTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user