diff --git a/src/components/profile/group-item.tsx b/src/components/profile/group-item.tsx index f7cd2364b..058e84382 100644 --- a/src/components/profile/group-item.tsx +++ b/src/components/profile/group-item.tsx @@ -9,10 +9,8 @@ import { alpha, styled, } from "@mui/material"; -import { convertFileSrc } from "@tauri-apps/api/core"; -import { useEffect, useState } from "react"; -import { downloadIconCache } from "@/services/cmds"; +import { useIconCache } from "@/hooks/use-icon-cache"; interface Props { type: "prepend" | "original" | "delete" | "append"; group: IProxyGroupConfig; @@ -38,40 +36,10 @@ export const GroupItem = (props: Props) => { const dragListeners = sortable ? sortableListeners : undefined; const dragNodeRef = sortable ? sortableSetNodeRef : undefined; - const [iconCachePath, setIconCachePath] = useState(""); - - useEffect(() => { - let cancelled = false; - const initIconCachePath = async () => { - const icon = group.icon?.trim() ?? ""; - if (icon.startsWith("http")) { - try { - const fileName = - group.name.replaceAll(" ", "") + "-" + getFileName(icon); - const iconPath = await downloadIconCache(icon, fileName); - if (!cancelled) { - setIconCachePath(convertFileSrc(iconPath)); - } - } catch { - if (!cancelled) { - setIconCachePath(""); - } - } - } else if (!cancelled) { - setIconCachePath(""); - } - }; - - void initIconCachePath(); - - return () => { - cancelled = true; - }; - }, [group.icon, group.name]); - - function getFileName(url: string) { - return url.substring(url.lastIndexOf("/") + 1); - } + const iconCachePath = useIconCache({ + icon: group.icon, + cacheKey: group.name.replaceAll(" ", ""), + }); return ( { const mode = useThemeMode(); const isDark = mode === "light" ? false : true; const itembackgroundcolor = isDark ? "#282A36" : "#ffffff"; - const [iconCachePath, setIconCachePath] = useState(""); - - const initIconCachePath = useCallback(async () => { - if (group.icon && group.icon.trim().startsWith("http")) { - const fileName = - group.name.replaceAll(" ", "") + "-" + getFileName(group.icon); - const iconPath = await downloadIconCache(group.icon, fileName); - setIconCachePath(convertFileSrc(iconPath)); - } else { - setIconCachePath(""); - } - }, [group.icon, group.name]); - - useEffect(() => { - initIconCachePath(); - }, [initIconCachePath]); - - function getFileName(url: string) { - return url.substring(url.lastIndexOf("/") + 1); - } + const iconCachePath = useIconCache({ + icon: group.icon, + cacheKey: group.name.replaceAll(" ", ""), + enabled: enable_group_icon, + }); const proxyColItemsMemo = useMemo(() => { if (type !== 4 || !proxyCol) { diff --git a/src/components/test/test-item.tsx b/src/components/test/test-item.tsx index 9f6743f1e..a36ae29b5 100644 --- a/src/components/test/test-item.tsx +++ b/src/components/test/test-item.tsx @@ -2,15 +2,15 @@ import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { LanguageRounded } from "@mui/icons-material"; import { Box, Divider, MenuItem, Menu, styled, alpha } from "@mui/material"; -import { convertFileSrc } from "@tauri-apps/api/core"; import { UnlistenFn } from "@tauri-apps/api/event"; import { useLockFn } from "ahooks"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { BaseLoading } from "@/components/base"; +import { useIconCache } from "@/hooks/use-icon-cache"; import { useListen } from "@/hooks/use-listen"; -import { cmdTestDelay, downloadIconCache } from "@/services/cmds"; +import { cmdTestDelay } from "@/services/cmds"; import delayManager from "@/services/delay"; import { showNotice } from "@/services/notice-service"; import { debugLog } from "@/utils/debug"; @@ -46,7 +46,7 @@ export const TestItem = ({ const [position, setPosition] = useState({ left: 0, top: 0 }); const [delay, setDelay] = useState(-1); const { uid, name, icon, url } = itemData; - const [iconCachePath, setIconCachePath] = useState(""); + const iconCachePath = useIconCache({ icon, cacheKey: uid }); const { addListener } = useListen(); const onDelay = useCallback(async () => { @@ -55,24 +55,6 @@ export const TestItem = ({ setDelay(result); }, [url]); - const initIconCachePath = useCallback(async () => { - if (icon && icon.trim().startsWith("http")) { - const fileName = uid + "-" + getFileName(icon); - const iconPath = await downloadIconCache(icon, fileName); - setIconCachePath(convertFileSrc(iconPath)); - } else { - setIconCachePath(""); - } - }, [icon, uid]); - - useEffect(() => { - void initIconCachePath(); - }, [initIconCachePath]); - - function getFileName(url: string) { - return url.substring(url.lastIndexOf("/") + 1); - } - const onEditTest = () => { setAnchorEl(null); onEdit(); diff --git a/src/hooks/use-icon-cache.ts b/src/hooks/use-icon-cache.ts new file mode 100644 index 000000000..7585be8b7 --- /dev/null +++ b/src/hooks/use-icon-cache.ts @@ -0,0 +1,54 @@ +import { convertFileSrc } from "@tauri-apps/api/core"; +import { useMemo } from "react"; +import useSWR from "swr"; + +import { downloadIconCache } from "@/services/cmds"; +import { SWR_DEFAULTS } from "@/services/config"; + +export interface UseIconCacheOptions { + icon?: string | null; + cacheKey?: string; + enabled?: boolean; +} + +const getFileNameFromUrl = (url: string) => { + const lastSlashIndex = url.lastIndexOf("/"); + return lastSlashIndex >= 0 ? url.slice(lastSlashIndex + 1) : url; +}; + +export const useIconCache = ({ + icon, + cacheKey, + enabled = true, +}: UseIconCacheOptions) => { + const iconValue = icon?.trim() ?? ""; + const cacheKeyValue = cacheKey?.trim() ?? ""; + + const swrKey = useMemo(() => { + if (!enabled || !iconValue.startsWith("http") || cacheKeyValue === "") { + return null; + } + + return ["icon-cache", iconValue, cacheKeyValue] as const; + }, [enabled, iconValue, cacheKeyValue]); + + const { data } = useSWR( + swrKey, + async () => { + try { + const fileName = `${cacheKeyValue}-${getFileNameFromUrl(iconValue)}`; + const iconPath = await downloadIconCache(iconValue, fileName); + return convertFileSrc(iconPath); + } catch { + return ""; + } + }, + SWR_DEFAULTS, + ); + + if (!swrKey) { + return ""; + } + + return data ?? ""; +};