diff --git a/src/components/FileBox/index.tsx b/src/components/FileBox/index.tsx index 7975a637..b6a4a3b4 100644 --- a/src/components/FileBox/index.tsx +++ b/src/components/FileBox/index.tsx @@ -1,7 +1,8 @@ -import { FC, ReactElement } from "react"; +import { FC, ReactElement, useEffect, useState } from "react"; import clsx from "clsx"; import { useAppSelector } from "@/app/store"; +import useExpiredResMap from "@/hooks/useExpiredResMap"; import { formatBytes, fromNowTime, getFileIcon } from "@/utils"; import IconDownload from "@/assets/icons/download.svg"; import { @@ -79,11 +80,36 @@ const FileBox: FC = ({ from_uid, content }) => { + const [fetchError, setFetchError] = useState(false); + const { isExpired, setExpired } = useExpiredResMap(); const fromUser = useAppSelector((store) => store.users.byId[from_uid]); const icon = getFileIcon(file_type, name, "icon w-9 h-12"); - if (!content || !name) return null; + const expired = isExpired(content); + useEffect(() => { + const tryFetch = async (url: string) => { + try { + let statusNum = (await fetch(url)).status; + if (statusNum >= 400) { + setFetchError(true); + } + } catch (e) { + setFetchError(true); + } + }; + if (!expired && content) { + tryFetch(content); + } + }, [content, expired]); + useEffect(() => { + if (fetchError) { + setExpired(content); + } + }, [fetchError, content]); + + if (!content || expired || fetchError) return null; + const previewContent = renderPreview({ file_type, content, name }); - const withPreview = preview && previewContent; + const withPreview = preview && previewContent && !fetchError && !expired; return (
{ setError(true); }; const _size = formatBytes(size); - if (error) return ; + if (error) return ; return (
} }; -const ExpiredMessage = ({ type = "file" }: Props) => { +const ExpiredMessage = ({ type = "file", url = "" }: Props) => { + const { setExpired } = useExpiredResMap(); const { title, desc, icon } = InfoMap[type]; + useEffect(() => { + if (url) { + setExpired(url); + } + }, [url]); + return (
= ({ }; img.src = url; }, [url]); - if (status == "error") return ; + if (status == "error") return ; return (
; + if (error) return ; return (
{ setError(true); }; const tipClass = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"; - if (error) return ; + if (error) return ; return (
= ({ thumbnail = "", properties = { local_id: 0, name: "", size: 0, content_type: "" } }) => { + const { isExpired } = useExpiredResMap(); const [imageSize, setImageSize] = useState(null); const [uploadingFile, setUploadingFile] = useState(false); const removeLocalMessage = useRemoveLocalMessage({ context, id: to }); @@ -136,7 +139,9 @@ const FileMessage: FC = ({ const sending = uploadingFile || isSending; // image if (isImage(content_type, size)) - return ( + return isExpired(thumbnail || content) ? ( + + ) : ( = ({ thumbnail={thumbnail} /> ); - + const isVideo = content_type.startsWith("video"); + const isAudio = content_type.startsWith("audio"); + if (isExpired(content) && !sending) + return ; // video - if (content_type.startsWith("video") && !sending) + if (isVideo && !sending) return ; // audio - if (content_type.startsWith("audio") && !sending) + if (isAudio && !sending) return ; return ( { + const [map, setMap, remove] = useLocalstorageState(LOCAL_KEY, {} as Record); + const setExpired = (url: string) => { + const key = new URL(url).searchParams.get("file_path"); + if (!key || map[key]) return; + setMap((prev) => { + return { ...prev, [key]: Date.now() }; + }); + }; + const isExpired = (url: string) => { + const key = new URL(url).searchParams.get("file_path"); + if (!key || !map[key]) return false; + return true; + }; + // const removeFromExpired = (key: string) => { + // setMap((prev) => { + // const { [key]: _, ...rest } = prev; + // return rest; + // }); + // }; + const clearExpired = () => { + remove(); + }; + + return { setExpired, isExpired, clearExpired }; +}; +export default useExpiredResMap; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 7f937d54..490ce892 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -40,10 +40,9 @@ const HomePage = lazy(() => import("./home")); let toastId: string; const PageRoutes = () => { const { - ui: { online }, - fileMessages + ui: { online } } = useAppSelector((store) => { - return { ui: store.ui, fileMessages: store.fileMessage }; + return { ui: store.ui }; }, isEqual); // 提前获取device token useDeviceToken(vapidKey); @@ -283,7 +282,7 @@ const PageRoutes = () => { path="files" element={ - + } > diff --git a/src/routes/resources/index.tsx b/src/routes/resources/index.tsx index 259a8b2b..e5e817e2 100644 --- a/src/routes/resources/index.tsx +++ b/src/routes/resources/index.tsx @@ -1,10 +1,11 @@ // @ts-nocheck -import { memo, useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import clsx from "clsx"; import Masonry from "masonry-layout"; import { useAppSelector } from "@/app/store"; import FileBox from "@/components/FileBox"; +import useExpiredResMap from "@/hooks/useExpiredResMap"; import Filter from "./Filter"; import Search from "./Search"; import View from "./View"; @@ -37,15 +38,16 @@ const checkFilter = (data, filter, channelMessage) => { }; let msnry: Masonry | null; -function ResourceManagement({ fileMessages }) { +function ResourceManagement() { + const { isExpired } = useExpiredResMap(); const listContainerRef = useRef(); const [filter, setFilter] = useState({}); - const { message, view, channelMessage, userData } = useAppSelector((store) => { + const { message, view, channelMessage, fileMsgs } = useAppSelector((store) => { return { + fileMsgs: store.fileMessage, message: store.message, channelMessage: store.channelMessage, - view: store.ui.fileListView, - userData: store.users.byId + view: store.ui.fileListView }; }); @@ -83,6 +85,15 @@ function ResourceManagement({ fileMessages }) { } } }, [view, filter]); + // const fileMessages = fileMsgs.filter((id) => { + // const data = message[id]; + // if (!data) return false; + // const { content, thumbnail } = data; + // let url = thumbnail || content; + // console.log("ff fitler", url, isExpired(url)); + + // return !isExpired(url); + // }); return (
@@ -99,15 +110,17 @@ function ResourceManagement({ fileMessages }) { )} ref={listContainerRef} > - {fileMessages.map((id) => { + {fileMsgs.map((id) => { const data = message[id]; if (!data) return null; const isSelected = checkFilter(data, filter, channelMessage); // 过滤掉不存在的用户 // const fromUser = userData[data.from_uid]; if (!isSelected) return null; - const { mid, content, created_at, from_uid, properties } = data; + const { mid, thumbnail, content, created_at, from_uid, properties } = data; const { name, content_type, size } = properties ?? {}; + const url = thumbnail || content; + if (isExpired(url)) return null; return (
a.length === b.length && a.every((v, i) => v == b[i]); - -export default memo(ResourceManagement, (prevs, nexts) => { - return equals(prevs.fileMessages, nexts.fileMessages); -}); +export default ResourceManagement;