refactor: replace recoil (#1137)

This commit is contained in:
Sukka 2024-06-07 12:27:37 +08:00 committed by GitHub
parent 96e044566c
commit 66dd510acc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 128 additions and 137 deletions

View File

@ -33,6 +33,7 @@
"ahooks": "^3.8.0", "ahooks": "^3.8.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"dayjs": "1.11.5", "dayjs": "1.11.5",
"foxact": "^0.2.34",
"i18next": "^23.11.5", "i18next": "^23.11.5",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"meta-json-schema": "1.18.5-alpha4", "meta-json-schema": "1.18.5-alpha4",
@ -48,7 +49,6 @@
"react-router-dom": "^6.23.1", "react-router-dom": "^6.23.1",
"react-transition-group": "^4.4.5", "react-transition-group": "^4.4.5",
"react-virtuoso": "^4.7.11", "react-virtuoso": "^4.7.11",
"recoil": "^0.7.7",
"swr": "^1.3.0", "swr": "^1.3.0",
"tar": "^6.2.1", "tar": "^6.2.1",
"types-pac": "^1.0.2" "types-pac": "^1.0.2"

View File

@ -52,6 +52,9 @@ importers:
dayjs: dayjs:
specifier: 1.11.5 specifier: 1.11.5
version: 1.11.5 version: 1.11.5
foxact:
specifier: ^0.2.34
version: 0.2.34(react@18.3.1)
i18next: i18next:
specifier: ^23.11.5 specifier: ^23.11.5
version: 23.11.5 version: 23.11.5
@ -97,9 +100,6 @@ importers:
react-virtuoso: react-virtuoso:
specifier: ^4.7.11 specifier: ^4.7.11
version: 4.7.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 4.7.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
recoil:
specifier: ^0.7.7
version: 0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
swr: swr:
specifier: ^1.3.0 specifier: ^1.3.0
version: 1.3.0(react@18.3.1) version: 1.3.0(react@18.3.1)
@ -2526,6 +2526,12 @@ packages:
} }
engines: { node: ">=10" } engines: { node: ">=10" }
client-only@0.0.1:
resolution:
{
integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==,
}
clsx@2.1.1: clsx@2.1.1:
resolution: resolution:
{ {
@ -2836,6 +2842,17 @@ packages:
} }
engines: { node: ">=12.20.0" } engines: { node: ">=12.20.0" }
foxact@0.2.34:
resolution:
{
integrity: sha512-9GrB4NPhTjaJ5pzMkfYFatLGgt5LWq3hhVhYR7zG/PaHhtt3ObOzdRVmmO/whh5E7W8JBykiS6RLtnjeLZLSeg==,
}
peerDependencies:
react: "*"
peerDependenciesMeta:
react:
optional: true
fs-extra@11.2.0: fs-extra@11.2.0:
resolution: resolution:
{ {
@ -2898,12 +2915,6 @@ packages:
integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==,
} }
hamt_plus@1.0.2:
resolution:
{
integrity: sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==,
}
has-flag@3.0.0: has-flag@3.0.0:
resolution: resolution:
{ {
@ -3876,21 +3887,6 @@ packages:
} }
engines: { node: ">=8.10.0" } engines: { node: ">=8.10.0" }
recoil@0.7.7:
resolution:
{
integrity: sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==,
}
peerDependencies:
react: ">=16.13.1"
react-dom: "*"
react-native: "*"
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
regenerate-unicode-properties@10.1.1: regenerate-unicode-properties@10.1.1:
resolution: resolution:
{ {
@ -4004,6 +4000,12 @@ packages:
} }
hasBin: true hasBin: true
server-only@0.0.1:
resolution:
{
integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==,
}
shebang-command@2.0.0: shebang-command@2.0.0:
resolution: resolution:
{ {
@ -6054,6 +6056,8 @@ snapshots:
chownr@2.0.0: {} chownr@2.0.0: {}
client-only@0.0.1: {}
clsx@2.1.1: {} clsx@2.1.1: {}
color-convert@1.9.3: color-convert@1.9.3:
@ -6233,6 +6237,13 @@ snapshots:
dependencies: dependencies:
fetch-blob: 3.2.0 fetch-blob: 3.2.0
foxact@0.2.34(react@18.3.1):
dependencies:
client-only: 0.0.1
server-only: 0.0.1
optionalDependencies:
react: 18.3.1
fs-extra@11.2.0: fs-extra@11.2.0:
dependencies: dependencies:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -6262,8 +6273,6 @@ snapshots:
graceful-fs@4.2.11: {} graceful-fs@4.2.11: {}
hamt_plus@1.0.2: {}
has-flag@3.0.0: {} has-flag@3.0.0: {}
hasown@2.0.2: hasown@2.0.2:
@ -6914,13 +6923,6 @@ snapshots:
dependencies: dependencies:
picomatch: 2.3.1 picomatch: 2.3.1
recoil@0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
hamt_plus: 1.0.2
react: 18.3.1
optionalDependencies:
react-dom: 18.3.1(react@18.3.1)
regenerate-unicode-properties@10.1.1: regenerate-unicode-properties@10.1.1:
dependencies: dependencies:
regenerate: 1.4.2 regenerate: 1.4.2
@ -7011,6 +7013,8 @@ snapshots:
semver@6.3.1: {} semver@6.3.1: {}
server-only@0.0.1: {}
shebang-command@2.0.0: shebang-command@2.0.0:
dependencies: dependencies:
shebang-regex: 3.0.0 shebang-regex: 3.0.0

View File

@ -1,8 +1,7 @@
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { useRecoilState } from "recoil";
import { alpha, createTheme, Shadows, Theme } from "@mui/material"; import { alpha, createTheme, Shadows, Theme } from "@mui/material";
import { appWindow } from "@tauri-apps/api/window"; import { appWindow } from "@tauri-apps/api/window";
import { atomThemeMode } from "@/services/states"; import { useSetThemeMode, useThemeMode } from "@/services/states";
import { defaultTheme, defaultDarkTheme } from "@/pages/_theme"; import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
@ -12,7 +11,8 @@ import { useVerge } from "@/hooks/use-verge";
export const useCustomTheme = () => { export const useCustomTheme = () => {
const { verge } = useVerge(); const { verge } = useVerge();
const { theme_mode, theme_setting } = verge ?? {}; const { theme_mode, theme_setting } = verge ?? {};
const [mode, setMode] = useRecoilState(atomThemeMode); const mode = useThemeMode();
const setMode = useSetThemeMode();
useEffect(() => { useEffect(() => {
const themeMode = ["light", "dark", "system"].includes(theme_mode!) const themeMode = ["light", "dark", "system"].includes(theme_mode!)

View File

@ -1,9 +1,8 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useEffect } from "react"; import { useEffect } from "react";
import { useRecoilValue, useSetRecoilState } from "recoil";
import { getClashLogs } from "@/services/cmds"; import { getClashLogs } from "@/services/cmds";
import { useClashInfo } from "@/hooks/use-clash"; import { useClashInfo } from "@/hooks/use-clash";
import { atomEnableLog, atomLogData } from "@/services/states"; import { useEnableLog, useSetLogData } from "@/services/states";
import { useWebsocket } from "@/hooks/use-websocket"; import { useWebsocket } from "@/hooks/use-websocket";
const MAX_LOG_NUM = 1000; const MAX_LOG_NUM = 1000;
@ -12,8 +11,8 @@ const MAX_LOG_NUM = 1000;
export const useLogSetup = () => { export const useLogSetup = () => {
const { clashInfo } = useClashInfo(); const { clashInfo } = useClashInfo();
const enableLog = useRecoilValue(atomEnableLog); const [enableLog] = useEnableLog();
const setLogData = useSetRecoilState(atomLogData); const setLogData = useSetLogData();
const { connect, disconnect } = useWebsocket((event) => { const { connect, disconnect } = useWebsocket((event) => {
const data = JSON.parse(event.data) as ILogItem; const data = JSON.parse(event.data) as ILogItem;

View File

@ -1,6 +1,5 @@
import { ReactNode, useEffect, useRef } from "react"; import { ReactNode, useEffect, useRef } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useRecoilValue } from "recoil";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
Button, Button,
@ -9,7 +8,7 @@ import {
DialogContent, DialogContent,
DialogTitle, DialogTitle,
} from "@mui/material"; } from "@mui/material";
import { atomThemeMode } from "@/services/states"; import { useThemeMode } from "@/services/states";
import { readProfileFile, saveProfileFile } from "@/services/cmds"; import { readProfileFile, saveProfileFile } from "@/services/cmds";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@ -90,7 +89,7 @@ export const EditorViewer = (props: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const editorRef = useRef<any>(); const editorRef = useRef<any>();
const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null); const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
const themeMode = useRecoilValue(atomThemeMode); const themeMode = useThemeMode();
useEffect(() => { useEffect(() => {
if (!open) return; if (!open) return;

View File

@ -2,7 +2,6 @@ import dayjs from "dayjs";
import { mutate } from "swr"; import { mutate } from "swr";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useRecoilState } from "recoil";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useSortable } from "@dnd-kit/sortable"; import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities"; import { CSS } from "@dnd-kit/utilities";
@ -17,7 +16,7 @@ import {
CircularProgress, CircularProgress,
} from "@mui/material"; } from "@mui/material";
import { RefreshRounded, DragIndicator } from "@mui/icons-material"; import { RefreshRounded, DragIndicator } from "@mui/icons-material";
import { atomLoadingCache } from "@/services/states"; import { useLoadingCache, useSetLoadingCache } from "@/services/states";
import { updateProfile, deleteProfile, viewProfile } from "@/services/cmds"; import { updateProfile, deleteProfile, viewProfile } from "@/services/cmds";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import { EditorViewer } from "@/components/profile/editor-viewer"; import { EditorViewer } from "@/components/profile/editor-viewer";
@ -47,7 +46,8 @@ export const ProfileItem = (props: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [anchorEl, setAnchorEl] = useState<any>(null); const [anchorEl, setAnchorEl] = useState<any>(null);
const [position, setPosition] = useState({ left: 0, top: 0 }); const [position, setPosition] = useState({ left: 0, top: 0 });
const [loadingCache, setLoadingCache] = useRecoilState(atomLoadingCache); const loadingCache = useLoadingCache();
const setLoadingCache = useSetLoadingCache();
const { uid, name = "Profile", extra, updated = 0 } = itemData; const { uid, name = "Profile", extra, updated = 0 } = itemData;

View File

@ -17,8 +17,7 @@ import { ProxyItem } from "./proxy-item";
import { ProxyItemMini } from "./proxy-item-mini"; import { ProxyItemMini } from "./proxy-item-mini";
import type { IRenderItem } from "./use-render-list"; import type { IRenderItem } from "./use-render-list";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { useRecoilState } from "recoil"; import { useThemeMode } from "@/services/states";
import { atomThemeMode } from "@/services/states";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { convertFileSrc } from "@tauri-apps/api/tauri"; import { convertFileSrc } from "@tauri-apps/api/tauri";
import { downloadIconCache } from "@/services/cmds"; import { downloadIconCache } from "@/services/cmds";
@ -38,7 +37,7 @@ export const ProxyRender = (props: RenderProps) => {
const { type, group, headState, proxy, proxyCol } = item; const { type, group, headState, proxy, proxyCol } = item;
const { verge } = useVerge(); const { verge } = useVerge();
const enable_group_icon = verge?.enable_group_icon ?? true; const enable_group_icon = verge?.enable_group_icon ?? true;
const [mode] = useRecoilState(atomThemeMode); const mode = useThemeMode();
const isDark = mode === "light" ? false : true; const isDark = mode === "light" ? false : true;
const itembackgroundcolor = isDark ? "#282A36" : "#ffffff"; const itembackgroundcolor = isDark ? "#282A36" : "#ffffff";
const [iconCachePath, setIconCachePath] = useState(""); const [iconCachePath, setIconCachePath] = useState("");

View File

@ -2,12 +2,11 @@ import useSWR from "swr";
import { forwardRef, useImperativeHandle, useState, useMemo } from "react"; import { forwardRef, useImperativeHandle, useState, useMemo } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { Box, LinearProgress } from "@mui/material"; import { Box, LinearProgress } from "@mui/material";
import { useRecoilState } from "recoil";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { relaunch } from "@tauri-apps/api/process"; import { relaunch } from "@tauri-apps/api/process";
import { checkUpdate, installUpdate } from "@tauri-apps/api/updater"; import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
import { BaseDialog, DialogRef, Notice } from "@/components/base"; import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { atomUpdateState } from "@/services/states"; import { useUpdateState, useSetUpdateState } from "@/services/states";
import { listen, Event, UnlistenFn } from "@tauri-apps/api/event"; import { listen, Event, UnlistenFn } from "@tauri-apps/api/event";
import { portableFlag } from "@/pages/_layout"; import { portableFlag } from "@/pages/_layout";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
@ -18,7 +17,9 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [updateState, setUpdateState] = useRecoilState(atomUpdateState);
const updateState = useUpdateState();
const setUpdateState = useSetUpdateState();
const { data: updateInfo } = useSWR("checkUpdate", checkUpdate, { const { data: updateInfo } = useSWR("checkUpdate", checkUpdate, {
errorRetryCount: 2, errorRetryCount: 2,

View File

@ -9,11 +9,17 @@ if (!window.ResizeObserver) {
import React from "react"; import React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { RecoilRoot } from "recoil"; import { ComposeContextProvider } from "foxact/compose-context-provider";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import { BaseErrorBoundary } from "./components/base"; import { BaseErrorBoundary } from "./components/base";
import Layout from "./pages/_layout"; import Layout from "./pages/_layout";
import "./services/i18n"; import "./services/i18n";
import {
LoadingCacheProvider,
LogDataProvider,
ThemeModeProvider,
UpdateStateProvider,
} from "./services/states";
const mainElementId = "root"; const mainElementId = "root";
const container = document.getElementById(mainElementId); const container = document.getElementById(mainElementId);
@ -37,14 +43,21 @@ document.addEventListener("keydown", (event) => {
} }
}); });
const contexts = [
<ThemeModeProvider />,
<LogDataProvider />,
<LoadingCacheProvider />,
<UpdateStateProvider />,
];
createRoot(container).render( createRoot(container).render(
<React.StrictMode> <React.StrictMode>
<RecoilRoot> <ComposeContextProvider contexts={contexts}>
<BaseErrorBoundary> <BaseErrorBoundary>
<BrowserRouter> <BrowserRouter>
<Layout /> <Layout />
</BrowserRouter> </BrowserRouter>
</BaseErrorBoundary> </BaseErrorBoundary>
</RecoilRoot> </ComposeContextProvider>
</React.StrictMode> </React.StrictMode>
); );

View File

@ -14,8 +14,7 @@ import { useVerge } from "@/hooks/use-verge";
import LogoSvg from "@/assets/image/logo.svg?react"; import LogoSvg from "@/assets/image/logo.svg?react";
import iconLight from "@/assets/image/icon_light.svg?react"; import iconLight from "@/assets/image/icon_light.svg?react";
import iconDark from "@/assets/image/icon_dark.svg?react"; import iconDark from "@/assets/image/icon_dark.svg?react";
import { atomThemeMode } from "@/services/states"; import { useThemeMode } from "@/services/states";
import { useRecoilState } from "recoil";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import { LayoutItem } from "@/components/layout/layout-item"; import { LayoutItem } from "@/components/layout/layout-item";
import { LayoutControl } from "@/components/layout/layout-control"; import { LayoutControl } from "@/components/layout/layout-control";
@ -36,7 +35,7 @@ dayjs.extend(relativeTime);
const OS = getSystem(); const OS = getSystem();
const Layout = () => { const Layout = () => {
const [mode] = useRecoilState(atomThemeMode); const mode = useThemeMode();
const isDark = mode === "light" ? false : true; const isDark = mode === "light" ? false : true;
const { t } = useTranslation(); const { t } = useTranslation();
const { theme } = useCustomTheme(); const { theme } = useCustomTheme();

View File

@ -1,12 +1,14 @@
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { Box, Button, IconButton, MenuItem } from "@mui/material"; import { Box, Button, IconButton, MenuItem } from "@mui/material";
import { useRecoilState } from "recoil";
import { Virtuoso } from "react-virtuoso"; import { Virtuoso } from "react-virtuoso";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { TableChartRounded, TableRowsRounded } from "@mui/icons-material"; import { TableChartRounded, TableRowsRounded } from "@mui/icons-material";
import { closeAllConnections } from "@/services/api"; import { closeAllConnections } from "@/services/api";
import { atomConnectionSetting } from "@/services/states"; import {
defaultConnectionSetting,
useConnectionSetting,
} from "@/services/states";
import { useClashInfo } from "@/hooks/use-clash"; import { useClashInfo } from "@/hooks/use-clash";
import { BaseEmpty, BasePage } from "@/components/base"; import { BaseEmpty, BasePage } from "@/components/base";
import { useWebsocket } from "@/hooks/use-websocket"; import { useWebsocket } from "@/hooks/use-websocket";
@ -34,9 +36,10 @@ const ConnectionsPage = () => {
const [curOrderOpt, setOrderOpt] = useState("Default"); const [curOrderOpt, setOrderOpt] = useState("Default");
const [connData, setConnData] = useState<IConnections>(initConn); const [connData, setConnData] = useState<IConnections>(initConn);
const [setting, setSetting] = useRecoilState(atomConnectionSetting); const [setting, setSetting] = useConnectionSetting();
const isTableLayout = setting.layout === "table"; const isTableLayout =
(setting || defaultConnectionSetting).layout === "table";
const orderOpts: Record<string, OrderFunc> = { const orderOpts: Record<string, OrderFunc> = {
Default: (list) => Default: (list) =>
@ -137,7 +140,7 @@ const ConnectionsPage = () => {
size="small" size="small"
onClick={() => onClick={() =>
setSetting((o) => setSetting((o) =>
o.layout === "list" o?.layout !== "table"
? { ...o, layout: "table" } ? { ...o, layout: "table" }
: { ...o, layout: "list" } : { ...o, layout: "list" }
) )

View File

@ -1,5 +1,4 @@
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { useRecoilState } from "recoil";
import { Box, Button, IconButton, MenuItem } from "@mui/material"; import { Box, Button, IconButton, MenuItem } from "@mui/material";
import { Virtuoso } from "react-virtuoso"; import { Virtuoso } from "react-virtuoso";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -7,7 +6,7 @@ import {
PlayCircleOutlineRounded, PlayCircleOutlineRounded,
PauseCircleOutlineRounded, PauseCircleOutlineRounded,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { atomEnableLog, atomLogData } from "@/services/states"; import { useEnableLog, useLogData, useSetLogData } from "@/services/states";
import { BaseEmpty, BasePage } from "@/components/base"; import { BaseEmpty, BasePage } from "@/components/base";
import LogItem from "@/components/log/log-item"; import LogItem from "@/components/log/log-item";
import { useCustomTheme } from "@/components/layout/use-custom-theme"; import { useCustomTheme } from "@/components/layout/use-custom-theme";
@ -16,8 +15,9 @@ import { BaseStyledSelect } from "@/components/base/base-styled-select";
const LogPage = () => { const LogPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [logData, setLogData] = useRecoilState(atomLogData); const logData = useLogData();
const [enableLog, setEnableLog] = useRecoilState(atomEnableLog); const setLogData = useSetLogData();
const [enableLog, setEnableLog] = useEnableLog();
const { theme } = useCustomTheme(); const { theme } = useCustomTheme();
const isDark = theme.palette.mode === "dark"; const isDark = theme.palette.mode === "dark";
const [logState, setLogState] = useState("all"); const [logState, setLogState] = useState("all");

View File

@ -1,7 +1,6 @@
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useSetRecoilState } from "recoil";
import { Box, Button, Grid, IconButton, Stack, Divider } from "@mui/material"; import { Box, Button, Grid, IconButton, Stack, Divider } from "@mui/material";
import { import {
DndContext, DndContext,
@ -35,7 +34,7 @@ import {
reorderProfile, reorderProfile,
createProfile, createProfile,
} from "@/services/cmds"; } from "@/services/cmds";
import { atomLoadingCache } from "@/services/states"; import { useSetLoadingCache, useThemeMode } from "@/services/states";
import { closeAllConnections } from "@/services/api"; import { closeAllConnections } from "@/services/api";
import { BasePage, DialogRef, Notice } from "@/components/base"; import { BasePage, DialogRef, Notice } from "@/components/base";
import { import {
@ -47,8 +46,6 @@ import { ProfileMore } from "@/components/profile/profile-more";
import { useProfiles } from "@/hooks/use-profiles"; import { useProfiles } from "@/hooks/use-profiles";
import { ConfigViewer } from "@/components/setting/mods/config-viewer"; import { ConfigViewer } from "@/components/setting/mods/config-viewer";
import { throttle } from "lodash-es"; import { throttle } from "lodash-es";
import { useRecoilState } from "recoil";
import { atomThemeMode } from "@/services/states";
import { BaseStyledTextField } from "@/components/base/base-styled-text-field"; import { BaseStyledTextField } from "@/components/base/base-styled-text-field";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { readTextFile } from "@tauri-apps/api/fs"; import { readTextFile } from "@tauri-apps/api/fs";
@ -239,7 +236,7 @@ const ProfilePage = () => {
}); });
// 更新所有订阅 // 更新所有订阅
const setLoadingCache = useSetRecoilState(atomLoadingCache); const setLoadingCache = useSetLoadingCache();
const onUpdateAll = useLockFn(async () => { const onUpdateAll = useLockFn(async () => {
const throttleMutate = throttle(mutateProfiles, 2000, { const throttleMutate = throttle(mutateProfiles, 2000, {
trailing: true, trailing: true,
@ -271,7 +268,7 @@ const ProfilePage = () => {
const text = await readText(); const text = await readText();
if (text) setUrl(text); if (text) setUrl(text);
}; };
const [mode] = useRecoilState(atomThemeMode); const mode = useThemeMode();
const islight = mode === "light" ? true : false; const islight = mode === "light" ? true : false;
const dividercolor = islight const dividercolor = islight
? "rgba(0, 0, 0, 0.06)" ? "rgba(0, 0, 0, 0.06)"

View File

@ -7,8 +7,7 @@ import { openWebUrl } from "@/services/cmds";
import SettingVerge from "@/components/setting/setting-verge"; import SettingVerge from "@/components/setting/setting-verge";
import SettingClash from "@/components/setting/setting-clash"; import SettingClash from "@/components/setting/setting-clash";
import SettingSystem from "@/components/setting/setting-system"; import SettingSystem from "@/components/setting/setting-system";
import { atomThemeMode } from "@/services/states"; import { useThemeMode } from "@/services/states";
import { useRecoilState } from "recoil";
const SettingPage = () => { const SettingPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -25,7 +24,7 @@ const SettingPage = () => {
return openWebUrl("https://clash-verge-rev.github.io/guide/log.html"); return openWebUrl("https://clash-verge-rev.github.io/guide/log.html");
}); });
const [mode] = useRecoilState(atomThemeMode); const mode = useThemeMode();
const isDark = mode === "light" ? false : true; const isDark = mode === "light" ? false : true;
return ( return (

View File

@ -1,73 +1,51 @@
import { atom } from "recoil"; import { createContextState } from "foxact/create-context-state";
import { useLocalStorage } from "foxact/use-local-storage";
export const atomThemeMode = atom<"light" | "dark">({ const [ThemeModeProvider, useThemeMode, useSetThemeMode] = createContextState<
key: "atomThemeMode", "light" | "dark"
default: "light", >("light");
});
export const atomLogData = atom<ILogItem[]>({ const [LogDataProvider, useLogData, useSetLogData] = createContextState<
key: "atomLogData", ILogItem[]
default: [], >([]);
});
export const atomEnableLog = atom<boolean>({ export const useEnableLog = () => useLocalStorage("enable-log", true);
key: "atomEnableLog",
effects: [
({ setSelf, onSet }) => {
const key = "enable-log";
try {
setSelf(localStorage.getItem(key) !== "false");
} catch {}
onSet((newValue, _, isReset) => {
try {
if (isReset) {
localStorage.removeItem(key);
} else {
localStorage.setItem(key, newValue.toString());
}
} catch {}
});
},
],
});
interface IConnectionSetting { interface IConnectionSetting {
layout: "table" | "list"; layout: "table" | "list";
} }
export const atomConnectionSetting = atom<IConnectionSetting>({ export const defaultConnectionSetting: IConnectionSetting = { layout: "table" };
key: "atomConnectionSetting",
effects: [
({ setSelf, onSet }) => {
const key = "connections-setting";
try { export const useConnectionSetting = () =>
const value = localStorage.getItem(key); useLocalStorage<IConnectionSetting>(
const data = value == null ? { layout: "table" } : JSON.parse(value); "connections-setting",
setSelf(data); defaultConnectionSetting,
} catch { {
setSelf({ layout: "table" }); serializer: JSON.stringify,
deserializer: JSON.parse,
} }
);
onSet((newValue) => {
try {
localStorage.setItem(key, JSON.stringify(newValue));
} catch {}
});
},
],
});
// save the state of each profile item loading // save the state of each profile item loading
export const atomLoadingCache = atom<Record<string, boolean>>({ const [LoadingCacheProvider, useLoadingCache, useSetLoadingCache] =
key: "atomLoadingCache", createContextState<Record<string, boolean>>({});
default: {},
});
// save update state // save update state
export const atomUpdateState = atom<boolean>({ const [UpdateStateProvider, useUpdateState, useSetUpdateState] =
key: "atomUpdateState", createContextState<boolean>(false);
default: false,
}); export {
ThemeModeProvider,
useThemeMode,
useSetThemeMode,
LogDataProvider,
useLogData,
useSetLogData,
LoadingCacheProvider,
useLoadingCache,
useSetLoadingCache,
UpdateStateProvider,
useUpdateState,
useSetUpdateState,
};