import 'package:drift/drift.dart'; import 'package:hiddify/core/database/app_database.dart'; import 'package:hiddify/core/database/tables/database_tables.dart'; import 'package:hiddify/features/profile/model/profile_sort_enum.dart'; import 'package:hiddify/utils/utils.dart'; part 'profile_data_source.g.dart'; abstract interface class ProfileDataSource { Future getById(String id); Future getByUrl(String url); Future getByName(String name); Stream watchActiveProfile(); Stream watchProfilesCount(); Stream> watchAll({ required ProfilesSort sort, required SortMode sortMode, }); Future insert(ProfileEntriesCompanion entry); Future edit(String id, ProfileEntriesCompanion entry); Future deleteById(String id); } Map orderMap = { SortMode.ascending: OrderingMode.asc, SortMode.descending: OrderingMode.desc, }; @DriftAccessor(tables: [ProfileEntries]) class ProfileDao extends DatabaseAccessor with _$ProfileDaoMixin, InfraLogger implements ProfileDataSource { ProfileDao(super.db); @override Future getById(String id) async { return (profileEntries.select()..where((tbl) => tbl.id.equals(id))) .getSingleOrNull(); } @override Future getByUrl(String url) async { return (select(profileEntries) ..where((tbl) => tbl.url.like('%$url%')) ..limit(1)) .getSingleOrNull(); } @override Future getByName(String name) async { return (select(profileEntries) ..where((tbl) => tbl.name.equals(name)) ..limit(1)) .getSingleOrNull(); } @override Stream watchActiveProfile() { return (profileEntries.select() ..where((tbl) => tbl.active.equals(true)) ..limit(1)) .watchSingleOrNull(); } @override Stream watchProfilesCount() { final count = profileEntries.id.count(); return (profileEntries.selectOnly()..addColumns([count])) .map((exp) => exp.read(count)!) .watchSingle(); } @override Stream> watchAll({ required ProfilesSort sort, required SortMode sortMode, }) { return (profileEntries.select() ..orderBy( [ (tbl) { final trafficRatio = (tbl.download + tbl.upload) / tbl.total; final isExpired = tbl.expire.isSmallerOrEqualValue(DateTime.now()); return OrderingTerm( expression: (trafficRatio.isNull() | trafficRatio.isSmallerThanValue(1)) & (isExpired.isNull() | isExpired.equals(false)), mode: OrderingMode.desc, ); }, switch (sort) { ProfilesSort.name => (tbl) => OrderingTerm( expression: tbl.name, mode: orderMap[sortMode]!, ), ProfilesSort.lastUpdate => (tbl) => OrderingTerm( expression: tbl.lastUpdate, mode: orderMap[sortMode]!, ), }, ], )) .watch(); } @override Future insert(ProfileEntriesCompanion entry) async { await transaction( () async { if (entry.active.present && entry.active.value) { await update(profileEntries) .write(const ProfileEntriesCompanion(active: Value(false))); } await into(profileEntries).insert(entry); }, ); } @override Future edit(String id, ProfileEntriesCompanion entry) async { await transaction( () async { if (entry.active.present && entry.active.value) { await update(profileEntries) .write(const ProfileEntriesCompanion(active: Value(false))); } await (update(profileEntries)..where((tbl) => tbl.id.equals(id))) .write(entry); }, ); } @override Future deleteById(String id) async { await transaction( () async { await (delete(profileEntries)..where((tbl) => tbl.id.equals(id))).go(); }, ); } }