summaryrefslogtreecommitdiff
path: root/src/modules/rules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rules')
-rw-r--r--src/modules/rules/hooks.ts100
-rw-r--r--src/modules/rules/utils.ts36
2 files changed, 69 insertions, 67 deletions
diff --git a/src/modules/rules/hooks.ts b/src/modules/rules/hooks.ts
index e13ea65..90a95f2 100644
--- a/src/modules/rules/hooks.ts
+++ b/src/modules/rules/hooks.ts
@@ -7,16 +7,18 @@ import {
refreshRuleProviderByName,
updateRuleProviders,
} from '~/api/rule-provider';
-import { fetchRules } from '~/api/rules';
+import { fetchRules, updateRuleDisabledStatus } from '~/api/rules';
import { ruleFilterText } from '~/store/rules';
import type { ClashAPIConfig } from '~/types';
-const { useCallback, useState } = React;
+import type { RulesTabKey } from './utils';
+
+const { useCallback, useMemo, useState } = React;
export function useUpdateRuleProviderItem(
name: string,
- apiConfig: ClashAPIConfig
-): [(ev: React.MouseEvent<HTMLButtonElement>) => unknown, boolean] {
+ apiConfig: ClashAPIConfig,
+): [() => void, boolean] {
const queryClient = useQueryClient();
const { mutate, isPending } = useMutation({
mutationFn: refreshRuleProviderByName,
@@ -24,16 +26,11 @@ export function useUpdateRuleProviderItem(
queryClient.invalidateQueries({ queryKey: ['/providers/rules'] });
},
});
- const onClickRefreshButton = (ev: React.MouseEvent<HTMLButtonElement>) => {
- ev.preventDefault();
- mutate({ name, apiConfig });
- };
- return [onClickRefreshButton, isPending];
+ const refresh = useCallback(() => mutate({ name, apiConfig }), [mutate, name, apiConfig]);
+ return [refresh, isPending];
}
-export function useUpdateAllRuleProviderItems(
- apiConfig: ClashAPIConfig
-): [(ev: React.MouseEvent<HTMLButtonElement>) => unknown, boolean] {
+export function useUpdateAllRuleProviderItems(apiConfig: ClashAPIConfig): [() => void, boolean] {
const queryClient = useQueryClient();
const { data: provider } = useRuleProviderQuery(apiConfig);
const { mutate, isPending } = useMutation({
@@ -42,19 +39,27 @@ export function useUpdateAllRuleProviderItems(
queryClient.invalidateQueries({ queryKey: ['/providers/rules'] });
},
});
- const onClickRefreshButton = (ev: React.MouseEvent<HTMLButtonElement>) => {
- ev.preventDefault();
- mutate({ names: provider.names, apiConfig });
- };
- return [onClickRefreshButton, isPending];
+ const refreshAll = useCallback(
+ () => mutate({ names: provider.names, apiConfig }),
+ [mutate, provider.names, apiConfig],
+ );
+ return [refreshAll, isPending];
}
-export function useInvalidateQueries() {
+export function useToggleRuleDisabled(apiConfig: ClashAPIConfig) {
const queryClient = useQueryClient();
- return useCallback(() => {
- queryClient.invalidateQueries({ queryKey: ['/rules'] });
- queryClient.invalidateQueries({ queryKey: ['/providers/rules'] });
- }, [queryClient]);
+ const { mutate, isPending } = useMutation({
+ mutationFn: ({ index, disabled }: { index: number; disabled: boolean }) =>
+ updateRuleDisabledStatus(apiConfig, { [index]: disabled }),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['/rules'] });
+ },
+ });
+ const toggleRule = useCallback(
+ (index: number, disabled: boolean) => mutate({ index, disabled }),
+ [mutate],
+ );
+ return { toggleRule, isPending };
}
export function useRuleProviderQuery(apiConfig: ClashAPIConfig) {
@@ -72,41 +77,40 @@ export function useRuleAndProvider(apiConfig: ClashAPIConfig) {
const { data: provider } = useRuleProviderQuery(apiConfig);
const [filterText] = useAtom(ruleFilterText);
- if (filterText === '') {
- return { rules, provider, isFetching };
- }
- const f = filterText.toLowerCase();
- return {
- rules: rules.filter((r) => r.payload.toLowerCase().indexOf(f) >= 0),
- isFetching,
- provider: {
- byName: provider.byName,
- names: provider.names.filter((t) => t.toLowerCase().indexOf(f) >= 0),
- },
- };
+ // 规则表动辄上千条,过滤结果必须缓存住:不然每次渲染都重算一遍,
+ // 而且新数组会让下游的 memo 全部失效
+ return useMemo(() => {
+ if (filterText === '') {
+ return { rules, provider, isFetching };
+ }
+ const f = filterText.toLowerCase();
+ return {
+ rules: rules.filter((r) => r.payload.toLowerCase().indexOf(f) >= 0),
+ isFetching,
+ provider: {
+ byName: provider.byName,
+ names: provider.names.filter((t) => t.toLowerCase().indexOf(f) >= 0),
+ },
+ };
+ }, [rules, provider, filterText, isFetching]);
}
export function useRulesPage(apiConfig: ClashAPIConfig) {
const { rules, provider } = useRuleAndProvider(apiConfig);
- const [activeTab, setActiveTab] = useState('rules');
- const isRulesTab = activeTab === 'rules';
+ // 标签出不出现看提供商总数,不看过滤结果——搜索不该把整个标签搞消失
+ const { data: allProviders } = useRuleProviderQuery(apiConfig);
+ const providerCount = allProviders.names.length;
- const handleTabKeyDown = useCallback(
- (tab: string) => (e: React.KeyboardEvent) => {
- if (e.key === 'Enter' || e.key === ' ') {
- setActiveTab(tab);
- }
- },
- []
- );
+ const [activeTab, setActiveTab] = useState<RulesTabKey>('rules');
+ const effectiveTab: RulesTabKey = providerCount > 0 ? activeTab : 'rules';
return {
rules,
provider,
- activeTab,
+ providerCount,
+ activeTab: effectiveTab,
setActiveTab,
- isRulesTab,
- handleTabKeyDown,
+ isRulesTab: effectiveTab === 'rules',
};
-} \ No newline at end of file
+}
diff --git a/src/modules/rules/utils.ts b/src/modules/rules/utils.ts
index c1d1464..772a1cc 100644
--- a/src/modules/rules/utils.ts
+++ b/src/modules/rules/utils.ts
@@ -1,24 +1,22 @@
-import { ClashAPIConfig } from '~/types';
+import type { RuleProvider } from '~/api/rule-provider';
+import type { RuleItem } from '~/api/rules';
+import type { ClashAPIConfig } from '~/types';
-export type RulesListItemData = {
- rules: any[] | null;
- provider: any;
- apiConfig: ClashAPIConfig;
+export type RuleProviderIndex = {
+ byName: Record<string, RuleProvider>;
+ names: string[];
};
-export function itemKey(index: number, { rules, provider }: RulesListItemData) {
- if (!rules) {
- return provider.names[index];
- }
- return rules[index].id;
-}
+export type RulesTabKey = 'rules' | 'providers';
-export function getItemSizeFactory({ isRulesTab }: { isRulesTab: boolean }) {
- return function getItemSize() {
- return isRulesTab ? 70 : 100;
- };
-}
+export type RulesRowProps = {
+ /** 规则标签页的数据;提供商标签页下为 null */
+ rules: RuleItem[] | null;
+ provider: RuleProviderIndex;
+ apiConfig: ClashAPIConfig;
+};
-export function formatQty(qty: number) {
- return qty < 100 ? String(qty) : '99+';
-}
+// 虚拟列表要求定高。两个数字都按「内容最多的那一行」定:
+// 规则行是 序号 + 两行内容(payload 长了会折行占两行),提供商行多一行元信息
+export const RULE_ROW_HEIGHT = 88;
+export const PROVIDER_ROW_HEIGHT = 100;