Files
MWClash/lib/providers/state.dart
chen08209 b44cef071d Optimize default option
Optimize computed text size
2025-04-30 14:47:19 +08:00

558 lines
15 KiB
Dart

import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'app.dart';
import 'config.dart';
part 'generated/state.g.dart';
@riverpod
GroupsState currentGroupsState(Ref ref) {
final mode =
ref.watch(patchClashConfigProvider.select((state) => state.mode));
final groups = ref.watch(groupsProvider);
return GroupsState(
value: switch (mode) {
Mode.direct => [],
Mode.global => groups.toList(),
Mode.rule => groups
.where((item) => item.hidden == false)
.where((element) => element.name != GroupName.GLOBAL.name)
.toList(),
},
);
}
@riverpod
NavigationItemsState navigationsState(Ref ref) {
final openLogs = ref.watch(appSettingProvider).openLogs;
final hasProxies = ref.watch(
currentGroupsStateProvider.select((state) => state.value.isNotEmpty));
return NavigationItemsState(
value: navigation.getItems(
openLogs: openLogs,
hasProxies: hasProxies,
),
);
}
@riverpod
NavigationItemsState currentNavigationsState(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final navigationItemsState = ref.watch(navigationsStateProvider);
final navigationItemMode = switch (viewWidth <= maxMobileWidth) {
true => NavigationItemMode.mobile,
false => NavigationItemMode.desktop,
};
return NavigationItemsState(
value: navigationItemsState.value
.where(
(element) => element.modes.contains(navigationItemMode),
)
.toList(),
);
}
@riverpod
CoreState coreState(Ref ref) {
final vpnProps = ref.watch(vpnSettingProvider);
final currentProfile = ref.watch(currentProfileProvider);
final onlyStatisticsProxy = ref.watch(appSettingProvider).onlyStatisticsProxy;
return CoreState(
vpnProps: vpnProps,
onlyStatisticsProxy: onlyStatisticsProxy,
currentProfileName: currentProfile?.label ?? currentProfile?.id ?? "",
);
}
@riverpod
ClashConfigState clashConfigState(Ref ref) {
final clashConfig = ref.watch(patchClashConfigProvider);
final overrideDns = ref.watch(overrideDnsProvider);
final overrideData =
ref.watch(currentProfileProvider.select((state) => state?.overrideData));
final routeMode =
ref.watch(networkSettingProvider.select((state) => state.routeMode));
return ClashConfigState(
overrideDns: overrideDns,
clashConfig: clashConfig,
overrideData: overrideData ?? OverrideData(),
routeMode: routeMode,
);
}
@riverpod
ProxyState proxyState(Ref ref) {
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
final vm2 = ref.watch(networkSettingProvider.select(
(state) => VM2(
a: state.systemProxy,
b: state.bypassDomain,
),
));
final mixedPort = ref.watch(
patchClashConfigProvider.select((state) => state.mixedPort),
);
return ProxyState(
isStart: isStart,
systemProxy: vm2.a,
bassDomain: vm2.b,
port: mixedPort,
);
}
@riverpod
TrayState trayState(Ref ref) {
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
final networkProps = ref.watch(networkSettingProvider);
final clashConfig = ref.watch(
patchClashConfigProvider,
);
final appSetting = ref.watch(
appSettingProvider,
);
final groups = ref.watch(
groupsProvider,
);
final brightness = ref.watch(
appBrightnessProvider,
);
final selectedMap = ref.watch(selectedMapProvider);
return TrayState(
mode: clashConfig.mode,
port: clashConfig.mixedPort,
autoLaunch: appSetting.autoLaunch,
systemProxy: networkProps.systemProxy,
tunEnable: clashConfig.tun.enable,
isStart: isStart,
locale: appSetting.locale,
brightness: brightness,
groups: groups,
selectedMap: selectedMap,
);
}
@riverpod
VpnState vpnState(Ref ref) {
final vpnProps = ref.watch(vpnSettingProvider);
final stack = ref.watch(
patchClashConfigProvider.select((state) => state.tun.stack),
);
return VpnState(
stack: stack,
vpnProps: vpnProps,
);
}
@riverpod
HomeState homeState(Ref ref) {
final pageLabel = ref.watch(currentPageLabelProvider);
final navigationItems = ref.watch(currentNavigationsStateProvider).value;
final viewMode = ref.watch(viewModeProvider);
final locale = ref.watch(appSettingProvider).locale;
return HomeState(
pageLabel: pageLabel,
navigationItems: navigationItems,
viewMode: viewMode,
locale: locale,
);
}
@riverpod
DashboardState dashboardState(Ref ref) {
final dashboardWidgets =
ref.watch(appSettingProvider.select((state) => state.dashboardWidgets));
final viewWidth = ref.watch(viewWidthProvider);
return DashboardState(
dashboardWidgets: dashboardWidgets,
viewWidth: viewWidth,
);
}
@riverpod
ProxiesActionsState proxiesActionsState(Ref ref) {
final pageLabel = ref.watch(currentPageLabelProvider);
final hasProviders = ref.watch(providersProvider.select(
(state) => state.isNotEmpty,
));
final type = ref.watch(proxiesStyleSettingProvider.select(
(state) => state.type,
));
return ProxiesActionsState(
pageLabel: pageLabel,
hasProviders: hasProviders,
type: type,
);
}
@riverpod
StartButtonSelectorState startButtonSelectorState(Ref ref) {
final isInit = ref.watch(initProvider);
final hasProfile =
ref.watch(profilesProvider.select((state) => state.isNotEmpty));
return StartButtonSelectorState(
isInit: isInit,
hasProfile: hasProfile,
);
}
@riverpod
ProfilesSelectorState profilesSelectorState(Ref ref) {
final currentProfileId = ref.watch(currentProfileIdProvider);
final profiles = ref.watch(profilesProvider);
final columns = ref.watch(
viewWidthProvider.select((state) => utils.getProfilesColumns(state)));
return ProfilesSelectorState(
profiles: profiles,
currentProfileId: currentProfileId,
columns: columns,
);
}
@riverpod
ProxiesListSelectorState proxiesListSelectorState(Ref ref) {
final groupNames = ref.watch(currentGroupsStateProvider.select((state) {
return state.value.map((e) => e.name).toList();
}));
final currentUnfoldSet = ref.watch(unfoldSetProvider);
final proxiesStyle = ref.watch(proxiesStyleSettingProvider);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
return ProxiesListSelectorState(
groupNames: groupNames,
currentUnfoldSet: currentUnfoldSet,
proxiesSortType: proxiesStyle.sortType,
proxyCardType: proxiesStyle.cardType,
sortNum: sortNum,
columns: columns,
);
}
@riverpod
ProxiesSelectorState proxiesSelectorState(Ref ref) {
final groupNames = ref.watch(
currentGroupsStateProvider.select(
(state) {
return state.value.map((e) => e.name).toList();
},
),
);
final currentGroupName = ref.watch(currentProfileProvider.select(
(state) => state?.currentGroupName,
));
return ProxiesSelectorState(
groupNames: groupNames,
currentGroupName: currentGroupName,
);
}
@riverpod
GroupNamesState groupNamesState(Ref ref) {
return GroupNamesState(
groupNames: ref.watch(
currentGroupsStateProvider.select(
(state) {
return state.value.map((e) => e.name).toList();
},
),
),
);
}
@riverpod
ProxyGroupSelectorState proxyGroupSelectorState(Ref ref, String groupName) {
final proxiesStyle = ref.watch(
proxiesStyleSettingProvider,
);
final group = ref.watch(
currentGroupsStateProvider.select(
(state) => state.value.getGroup(groupName),
),
);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
return ProxyGroupSelectorState(
testUrl: group?.testUrl,
proxiesSortType: proxiesStyle.sortType,
proxyCardType: proxiesStyle.cardType,
sortNum: sortNum,
groupType: group?.type ?? GroupType.Selector,
proxies: group?.all ?? [],
columns: columns,
);
}
@riverpod
PackageListSelectorState packageListSelectorState(Ref ref) {
final packages = ref.watch(packagesProvider);
final accessControl =
ref.watch(vpnSettingProvider.select((state) => state.accessControl));
return PackageListSelectorState(
packages: packages,
accessControl: accessControl,
);
}
@riverpod
MoreToolsSelectorState moreToolsSelectorState(Ref ref) {
final viewMode = ref.watch(viewModeProvider);
final navigationItems = ref.watch(navigationsStateProvider.select((state) {
return state.value.where((element) {
final isMore = element.modes.contains(NavigationItemMode.more);
final isDesktop = element.modes.contains(NavigationItemMode.desktop);
if (isMore && !isDesktop) return true;
if (viewMode != ViewMode.mobile || !isMore) {
return false;
}
return true;
}).toList();
}));
return MoreToolsSelectorState(navigationItems: navigationItems);
}
@riverpod
bool isCurrentPage(
Ref ref,
PageLabel pageLabel, {
bool Function(PageLabel pageLabel, ViewMode viewMode)? handler,
}) {
final currentPageLabel = ref.watch(currentPageLabelProvider);
if (pageLabel == currentPageLabel) {
return true;
}
if (handler != null) {
final viewMode = ref.watch(viewModeProvider);
return handler(currentPageLabel, viewMode);
}
return false;
}
@riverpod
String getRealTestUrl(Ref ref, [String? testUrl]) {
final currentTestUrl = ref.watch(appSettingProvider).testUrl;
return testUrl.getSafeValue(currentTestUrl);
}
@riverpod
int? getDelay(
Ref ref, {
required String proxyName,
String? testUrl,
}) {
final currentTestUrl = ref.watch(getRealTestUrlProvider(testUrl));
final proxyCardState = ref.watch(
getProxyCardStateProvider(
proxyName,
),
);
final delay = ref.watch(
delayDataSourceProvider.select(
(state) {
final delayMap =
state[proxyCardState.testUrl.getSafeValue(currentTestUrl)];
return delayMap?[proxyCardState.proxyName];
},
),
);
return delay;
}
@riverpod
SelectedMap selectedMap(Ref ref) {
final selectedMap = ref.watch(
currentProfileProvider.select((state) => state?.selectedMap ?? {}),
);
return selectedMap;
}
@riverpod
Set<String> unfoldSet(Ref ref) {
final unfoldSet = ref.watch(
currentProfileProvider.select((state) => state?.unfoldSet ?? {}),
);
return unfoldSet;
}
@riverpod
HotKeyAction getHotKeyAction(Ref ref, HotAction hotAction) {
return ref.watch(
hotKeyActionsProvider.select(
(state) {
final index = state.indexWhere((item) => item.action == hotAction);
return index != -1
? state[index]
: HotKeyAction(
action: hotAction,
);
},
),
);
}
@riverpod
Profile? currentProfile(Ref ref) {
final profileId = ref.watch(currentProfileIdProvider);
return ref
.watch(profilesProvider.select((state) => state.getProfile(profileId)));
}
@riverpod
int getProxiesColumns(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final proxiesLayout =
ref.watch(proxiesStyleSettingProvider.select((state) => state.layout));
return utils.getProxiesColumns(viewWidth, proxiesLayout);
}
ProxyCardState _getProxyCardState(
List<Group> groups,
SelectedMap selectedMap,
ProxyCardState proxyDelayState,
) {
if (proxyDelayState.proxyName.isEmpty) return proxyDelayState;
final index =
groups.indexWhere((element) => element.name == proxyDelayState.proxyName);
if (index == -1) return proxyDelayState;
final group = groups[index];
final currentSelectedName = group
.getCurrentSelectedName(selectedMap[proxyDelayState.proxyName] ?? '');
if (currentSelectedName.isEmpty) {
return proxyDelayState;
}
return _getProxyCardState(
groups,
selectedMap,
proxyDelayState.copyWith(
proxyName: currentSelectedName,
testUrl: group.testUrl,
),
);
}
@riverpod
ProxyCardState getProxyCardState(Ref ref, String proxyName) {
final groups = ref.watch(groupsProvider);
final selectedMap = ref.watch(selectedMapProvider);
return _getProxyCardState(
groups, selectedMap, ProxyCardState(proxyName: proxyName));
}
@riverpod
String? getProxyName(Ref ref, String groupName) {
final proxyName =
ref.watch(selectedMapProvider.select((state) => state[groupName]));
return proxyName;
}
@riverpod
String? getSelectedProxyName(Ref ref, String groupName) {
final proxyName = ref.watch(getProxyNameProvider(groupName));
final group = ref.watch(
groupsProvider.select(
(state) => state.getGroup(groupName),
),
);
return group?.getCurrentSelectedName(proxyName ?? '');
}
@riverpod
String getProxyDesc(Ref ref, Proxy proxy) {
final groupTypeNamesList = GroupType.values.map((e) => e.name).toList();
if (!groupTypeNamesList.contains(proxy.type)) {
return proxy.type;
} else {
final groups = ref.watch(groupsProvider);
final index = groups.indexWhere((element) => element.name == proxy.name);
if (index == -1) return proxy.type;
final state = ref.watch(getProxyCardStateProvider(proxy.name));
return "${proxy.type}(${state.proxyName.isNotEmpty ? state.proxyName : '*'})";
}
}
@riverpod
class ProfileOverrideState extends _$ProfileOverrideState {
@override
ProfileOverrideStateModel build() {
return ProfileOverrideStateModel(
isEdit: false,
selectedRules: {},
);
}
updateState(
ProfileOverrideStateModel? Function(ProfileOverrideStateModel state)
builder,
) {
final value = builder(state);
if (value == null) {
return;
}
state = value;
}
}
@riverpod
OverrideData? getProfileOverrideData(Ref ref, String profileId) {
return ref.watch(
profilesProvider.select(
(state) => state.getProfile(profileId)?.overrideData,
),
);
}
@riverpod
VM2? layoutChange(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final textScale =
ref.watch(themeSettingProvider.select((state) => state.textScale));
return VM2(
a: viewWidth,
b: textScale,
);
}
@riverpod
ColorScheme genColorScheme(
Ref ref,
Brightness brightness, {
Color? color,
bool ignoreConfig = false,
}) {
final vm2 = ref.watch(
themeSettingProvider.select(
(state) => VM2(
a: state.primaryColor,
b: state.schemeVariant,
),
),
);
if (color == null && (ignoreConfig == true || vm2.a == null)) {
// if (globalState.corePalette != null) {
// return globalState.corePalette!.toColorScheme(brightness: brightness);
// }
return ColorScheme.fromSeed(
seedColor: globalState.corePalette
?.toColorScheme(brightness: brightness)
.primary ??
globalState.accentColor,
brightness: brightness,
dynamicSchemeVariant: vm2.b,
);
}
return ColorScheme.fromSeed(
seedColor: color ?? Color(vm2.a!),
brightness: brightness,
dynamicSchemeVariant: vm2.b,
);
}