summaryrefslogtreecommitdiff
path: root/src/modules/rules
diff options
context:
space:
mode:
authorLarvan2 <[email protected]>2026-03-15 15:01:57 +0800
committerLarvan2 <[email protected]>2026-03-15 15:01:57 +0800
commit0e420859f5f7011ba124c965d8319bf3bf4c5fe3 (patch)
tree2fc344b757e119ebae6e0b6243121fddba61603c /src/modules/rules
parent17c4d2855ffb6914fcbece27367bafdd27a4c182 (diff)
refactor: reorganize code
Diffstat (limited to 'src/modules/rules')
-rw-r--r--src/modules/rules/hooks.ts108
-rw-r--r--src/modules/rules/utils.ts24
2 files changed, 132 insertions, 0 deletions
diff --git a/src/modules/rules/hooks.ts b/src/modules/rules/hooks.ts
new file mode 100644
index 0000000..e99b978
--- /dev/null
+++ b/src/modules/rules/hooks.ts
@@ -0,0 +1,108 @@
+import * as React from 'react';
+import { useMutation, useQuery, useQueryClient } from 'react-query';
+import { useRecoilState } from 'recoil';
+
+import {
+ fetchRuleProviders,
+ refreshRuleProviderByName,
+ updateRuleProviders,
+} from '~/api/rule-provider';
+import { fetchRules } from '~/api/rules';
+import { ruleFilterText } from '~/store/rules';
+import type { ClashAPIConfig } from '~/types';
+
+const { useCallback, useState } = React;
+
+export function useUpdateRuleProviderItem(
+ name: string,
+ apiConfig: ClashAPIConfig
+): [(ev: React.MouseEvent<HTMLButtonElement>) => unknown, boolean] {
+ const queryClient = useQueryClient();
+ const { mutate, isLoading } = useMutation(refreshRuleProviderByName, {
+ onSuccess: () => {
+ queryClient.invalidateQueries('/providers/rules');
+ },
+ });
+ const onClickRefreshButton = (ev: React.MouseEvent<HTMLButtonElement>) => {
+ ev.preventDefault();
+ mutate({ name, apiConfig });
+ };
+ return [onClickRefreshButton, isLoading];
+}
+
+export function useUpdateAllRuleProviderItems(
+ apiConfig: ClashAPIConfig
+): [(ev: React.MouseEvent<HTMLButtonElement>) => unknown, boolean] {
+ const queryClient = useQueryClient();
+ const { data: provider } = useRuleProviderQuery(apiConfig);
+ const { mutate, isLoading } = useMutation(updateRuleProviders, {
+ onSuccess: () => {
+ queryClient.invalidateQueries('/providers/rules');
+ },
+ });
+ const onClickRefreshButton = (ev: React.MouseEvent<HTMLButtonElement>) => {
+ ev.preventDefault();
+ mutate({ names: provider.names, apiConfig });
+ };
+ return [onClickRefreshButton, isLoading];
+}
+
+export function useInvalidateQueries() {
+ const queryClient = useQueryClient();
+ return useCallback(() => {
+ queryClient.invalidateQueries('/rules');
+ queryClient.invalidateQueries('/providers/rules');
+ }, [queryClient]);
+}
+
+export function useRuleProviderQuery(apiConfig: ClashAPIConfig) {
+ return useQuery(['/providers/rules', apiConfig], () =>
+ fetchRuleProviders('/providers/rules', apiConfig)
+ );
+}
+
+export function useRuleAndProvider(apiConfig: ClashAPIConfig) {
+ const { data: rules, isFetching } = useQuery(['/rules', apiConfig], () =>
+ fetchRules('/rules', apiConfig)
+ );
+ const { data: provider } = useRuleProviderQuery(apiConfig);
+
+ const [filterText] = useRecoilState(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),
+ },
+ };
+}
+
+export function useRulesPage(apiConfig: ClashAPIConfig) {
+ const { rules, provider } = useRuleAndProvider(apiConfig);
+ const [activeTab, setActiveTab] = useState('rules');
+ const isRulesTab = activeTab === 'rules';
+
+ const handleTabKeyDown = useCallback(
+ (tab: string) => (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ setActiveTab(tab);
+ }
+ },
+ []
+ );
+
+ return {
+ rules,
+ provider,
+ activeTab,
+ setActiveTab,
+ isRulesTab,
+ handleTabKeyDown,
+ };
+} \ No newline at end of file
diff --git a/src/modules/rules/utils.ts b/src/modules/rules/utils.ts
new file mode 100644
index 0000000..c1d1464
--- /dev/null
+++ b/src/modules/rules/utils.ts
@@ -0,0 +1,24 @@
+import { ClashAPIConfig } from '~/types';
+
+export type RulesListItemData = {
+ rules: any[] | null;
+ provider: any;
+ apiConfig: ClashAPIConfig;
+};
+
+export function itemKey(index: number, { rules, provider }: RulesListItemData) {
+ if (!rules) {
+ return provider.names[index];
+ }
+ return rules[index].id;
+}
+
+export function getItemSizeFactory({ isRulesTab }: { isRulesTab: boolean }) {
+ return function getItemSize() {
+ return isRulesTab ? 70 : 100;
+ };
+}
+
+export function formatQty(qty: number) {
+ return qty < 100 ? String(qty) : '99+';
+}