Files
MWClash/lib/providers/database.dart
chen08209 3f5e7b80bc cache
2026-03-10 19:24:23 +08:00

392 lines
11 KiB
Dart

import 'dart:async';
import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/database/database.dart';
import 'package:fl_clash/models/models.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'generated/database.g.dart';
@riverpod
Stream<List<Profile>> profilesStream(Ref ref) {
return database.profilesDao.all().watch();
}
@riverpod
Stream<List<Rule>> addedRulesStream(Ref ref, int profileId) {
return database.rulesDao.allAddedRules(profileId).watch();
}
@riverpod
Future<List<Rule>> addedRules(Ref ref, int profileId) {
return database.rulesDao.allAddedRules(profileId).get();
}
@riverpod
Stream<int> customRulesCount(Ref ref, int profileId) {
return database.rulesDao.profileCustomRulesCount(profileId).watchSingle();
}
@riverpod
Stream<int> proxyGroupsCount(Ref ref, int profileId) {
return database.proxyGroupsDao.count(profileId).watchSingle();
}
@Riverpod(keepAlive: true)
class Profiles extends _$Profiles {
@override
List<Profile> build() {
return ref.watch(profilesStreamProvider).value ?? [];
}
void put(Profile profile) {
final vm2 = state.copyAndAddProfile(profile);
final nextProfiles = vm2.a;
final newProfile = vm2.b;
state = nextProfiles;
database.profiles.put(newProfile.toCompanion());
}
void del(int id) {
final newProfiles = state.where((element) => element.id != id).toList();
state = newProfiles;
database.profiles.remove((t) => t.id.equals(id));
}
void updateProfile(int profileId, Profile Function(Profile profile) builder) {
final index = state.indexWhere((element) => element.id == profileId);
if (index == -1) {
return;
}
final List<Profile> profilesTemp = List.from(state);
final newProfile = builder(profilesTemp[index]);
profilesTemp[index] = newProfile;
state = profilesTemp;
database.profiles.put(newProfile.toCompanion());
}
void setAndReorder(List<Profile> profiles) {
final newProfiles = List<Profile>.from(profiles);
state = newProfiles;
database.profilesDao.setAll(profiles);
}
void reorder(List<Profile> profiles) {
final newProfiles = List<Profile>.from(profiles);
state = newProfiles;
final List<ProfilesCompanion> needUpdateProfiles = [];
newProfiles.forEachIndexed((index, item) {
if (item.order != index) {
needUpdateProfiles.add(item.toCompanion(index));
}
});
database.profilesDao.putAll(needUpdateProfiles);
}
@override
bool updateShouldNotify(List<Profile> previous, List<Profile> next) {
return !profileListEquality.equals(previous, next);
}
}
@riverpod
class Scripts extends _$Scripts with AsyncNotifierMixin {
@override
Stream<List<Script>> build() {
return database.scriptsDao.all().watch();
}
@override
List<Script> get value => state.value ?? [];
void put(Script script) {
final list = List<Script>.from(value);
final index = value.indexWhere((item) => item.id == script.id);
if (index != -1) {
list[index] = script;
} else {
list.add(script);
}
value = list;
database.scripts.put(script.toCompanion());
}
void del(int id) {
final index = value.indexWhere((item) => item.id == id);
if (index == -1) {
return;
}
final list = List<Script>.from(value);
list.removeAt(index);
value = list;
database.scripts.remove((t) => t.id.equals(id));
}
bool isExits(String label) {
return value.indexWhere((item) => item.label == label) != -1;
}
@override
bool updateShouldNotify(
AsyncValue<List<Script>> previous,
AsyncValue<List<Script>> next,
) {
return !scriptListEquality.equals(previous.value, next.value);
}
}
@riverpod
class GlobalRules extends _$GlobalRules with AsyncNotifierMixin {
@override
Stream<List<Rule>> build() {
return database.rulesDao.allGlobalAddedRules().watch();
}
@override
List<Rule> get value => state.value ?? [];
@override
bool updateShouldNotify(
AsyncValue<List<Rule>> previous,
AsyncValue<List<Rule>> next,
) {
return !ruleListEquality.equals(previous.value, next.value);
}
void delAll(Iterable<int> ruleIds) {
value = List<Rule>.from(value.where((item) => !ruleIds.contains(item.id)));
database.rulesDao.delRules(ruleIds);
}
void put(Rule rule) {
final Rule newRule;
if (rule.order?.isNotEmpty != true) {
newRule = rule.copyWith(
order: indexing.generateKeyBetween(null, value.firstOrNull?.order),
);
} else {
newRule = rule;
}
value = value.copyAndPut(newRule);
database.rulesDao.putGlobalRule(newRule);
}
void order(int oldIndex, int newIndex) {
int insertIndex = newIndex;
if (oldIndex < newIndex) {
insertIndex -= 1;
}
final nextItems = List<Rule>.from(value);
final item = nextItems.removeAt(oldIndex);
nextItems.insert(insertIndex, item);
value = nextItems;
final preOrder = nextItems.safeGet(insertIndex - 1)?.order;
final nextOrder = nextItems.safeGet(insertIndex + 1)?.order;
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
database.rulesDao.orderGlobalRule(ruleId: item.id, order: newOrder);
}
}
@riverpod
class ProfileAddedRules extends _$ProfileAddedRules with AsyncNotifierMixin {
@override
Stream<List<Rule>> build(int profileId) {
return database.rulesDao.allProfileAddedRules(profileId).watch();
}
@override
List<Rule> get value => state.value ?? [];
@override
bool updateShouldNotify(
AsyncValue<List<Rule>> previous,
AsyncValue<List<Rule>> next,
) {
return !ruleListEquality.equals(previous.value, next.value);
}
void put(Rule rule) {
final Rule newRule;
if (rule.order?.isNotEmpty != true) {
newRule = rule.copyWith(
order: indexing.generateKeyBetween(null, value.firstOrNull?.order),
);
} else {
newRule = rule;
}
value = value.copyAndPut(newRule);
database.rulesDao.putProfileAddedRule(profileId, newRule);
}
void delAll(Iterable<int> ruleIds) {
value = List<Rule>.from(value.where((item) => !ruleIds.contains(item.id)));
database.rulesDao.delRules(ruleIds);
}
void order(int oldIndex, int newIndex) {
int insertIndex = newIndex;
if (oldIndex < newIndex) {
insertIndex -= 1;
}
final nextItems = List<Rule>.from(value);
final item = nextItems.removeAt(oldIndex);
nextItems.insert(insertIndex, item);
value = nextItems;
final preOrder = nextItems.safeGet(insertIndex - 1)?.order;
final nextOrder = nextItems.safeGet(insertIndex + 1)?.order;
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
database.rulesDao.orderProfileAddedRule(
profileId,
ruleId: item.id,
order: newOrder,
);
}
}
@riverpod
class ProfileCustomRules extends _$ProfileCustomRules with AsyncNotifierMixin {
@override
Stream<List<Rule>> build(int profileId) {
return database.rulesDao.allProfileCustomRules(profileId).watch();
}
@override
List<Rule> get value => state.value ?? [];
@override
bool updateShouldNotify(
AsyncValue<List<Rule>> previous,
AsyncValue<List<Rule>> next,
) {
return !ruleListEquality.equals(previous.value, next.value);
}
void put(Rule rule) {
final Rule newRule;
if (rule.order?.isNotEmpty != true) {
newRule = rule.copyWith(
order: indexing.generateKeyBetween(null, value.firstOrNull?.order),
);
} else {
newRule = rule;
}
value = value.copyAndPut(newRule);
database.rulesDao.putProfileCustomRule(profileId, newRule);
}
void delAll(Iterable<int> ruleIds) {
value = List<Rule>.from(value.where((item) => !ruleIds.contains(item.id)));
database.rulesDao.delRules(ruleIds);
}
void order(int oldIndex, int newIndex) {
int insertIndex = newIndex;
if (oldIndex < newIndex) {
insertIndex -= 1;
}
final nextItems = List<Rule>.from(value);
final item = nextItems.removeAt(oldIndex);
nextItems.insert(insertIndex, item);
value = nextItems;
final preOrder = nextItems.safeGet(insertIndex - 1)?.order;
final nextOrder = nextItems.safeGet(insertIndex + 1)?.order;
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
database.rulesDao.orderProfileCustomRule(
profileId,
ruleId: item.id,
order: newOrder,
);
}
}
@riverpod
class ProxyGroups extends _$ProxyGroups with AsyncNotifierMixin {
@override
Stream<List<ProxyGroup>> build(int profileId) {
return database.proxyGroupsDao.all(profileId).watch();
}
@override
bool updateShouldNotify(
AsyncValue<List<ProxyGroup>> previous,
AsyncValue<List<ProxyGroup>> next,
) {
return !proxyGroupsEquality.equals(previous.value, next.value);
}
void order(int oldIndex, int newIndex) {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final nextItems = List<ProxyGroup>.from(value);
final item = nextItems.removeAt(oldIndex);
nextItems.insert(newIndex, item);
value = nextItems;
final preOrder = nextItems.safeGet(newIndex - 1)?.order;
final nextOrder = nextItems.safeGet(newIndex + 1)?.order;
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
database.proxyGroupsDao.order(profileId, proxyGroup: item, order: newOrder);
}
@override
List<ProxyGroup> get value => state.value ?? [];
}
@Riverpod(name: 'proxyGroupProvider')
class ProxyGroupProvider extends _$ProxyGroupProvider with AsyncNotifierMixin {
@override
Stream<ProxyGroup> build(int profileId, String name) {
return database.proxyGroupsDao.get(profileId, name).watchSingle();
}
@override
ProxyGroup get value => state.requireValue;
}
@riverpod
class ProfileDisabledRuleIds extends _$ProfileDisabledRuleIds
with AsyncNotifierMixin {
@override
List<int> get value => state.value ?? [];
@override
Stream<List<int>> build(int profileId) {
return database.rulesDao
.allProfileDisabledRules(profileId)
.map((item) => item.id)
.watch();
}
@override
bool updateShouldNotify(
AsyncValue<List<int>> previous,
AsyncValue<List<int>> next,
) {
return !intListEquality.equals(previous.value, next.value);
}
void _put(int ruleId) {
var newList = List<int>.from(value);
final index = newList.indexWhere((item) => item == ruleId);
if (index != -1) {
newList[index] = ruleId;
} else {
newList.insert(0, ruleId);
}
value = newList;
}
void del(int ruleId) {
List<int> newList = List.from(value);
newList = newList.where((item) => item != ruleId).toList();
value = newList;
database.rulesDao.delDisabledLink(profileId, ruleId);
}
void put(int ruleId) {
_put(ruleId);
database.rulesDao.putDisabledLink(profileId, ruleId);
}
}