From d4f14b97b0696a00445c7425a26324b15a6fd453 Mon Sep 17 00:00:00 2001 From: alikia2x Date: Wed, 1 Jan 2025 16:09:45 +0800 Subject: [PATCH] improve: optimize timeline scrolling --- pages/rewind/index.tsx | 108 +++++++++++++++++++++++++---------- src/electron/server/index.ts | 3 + 2 files changed, 81 insertions(+), 30 deletions(-) diff --git a/pages/rewind/index.tsx b/pages/rewind/index.tsx index 30e6406..bb46ed0 100644 --- a/pages/rewind/index.tsx +++ b/pages/rewind/index.tsx @@ -45,15 +45,84 @@ export default function RewindPage() { const [currentFrameId, setCurrentFrameId] = useState(null); const [images, setImages] = useState>({}); const [isLoadingMore, setIsLoadingMore] = useState(false); - const lastRequestTime = useRef(Date.now()); const containerRef = useRef(null); const lastAvaliableFrameId = useRef(null); + const timeoutRef = useRef(null); + const updatedTimes = useRef(0); - useEffect(() => { - if (currentFrameId && images[currentFrameId]) { - lastAvaliableFrameId.current = currentFrameId; + const loadingQueue = useRef([]); + const isProcessingQueue = useRef(false); + + const processQueue = useCallback(async () => { + if (!port || isProcessingQueue.current || loadingQueue.current.length === 0) return; + + isProcessingQueue.current = true; + const frameId = loadingQueue.current.shift()!; + + try { + const startUpdateTimes = updatedTimes.current; + const response = await fetch(`http://localhost:${port}/frame/${frameId}`, { + headers: { + "x-api-key": apiKey + } + }); + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + setImages((prev) => { + const newImages = { ...prev, [frameId]: url }; + if (updatedTimes.current <= startUpdateTimes) { + lastAvaliableFrameId.current = frameId; + updatedTimes.current++; + } + return newImages; + }); + } catch (error) { + console.error(error); + } finally { + isProcessingQueue.current = false; + setTimeout(() => { + processQueue(); + }, 500); } - }, [images, currentFrameId]); + }, [apiKey, port]); + + const loadImage = useCallback( + (frameId: number) => { + if (!port || images[frameId]) return; + + // Add to queue if not already in it + if (!loadingQueue.current.includes(frameId)) { + loadingQueue.current.push(frameId); + // preserve up to 5 tasks in the queue + loadingQueue.current = loadingQueue.current.slice(-5); + } + + // Start processing if not already running + if (!isProcessingQueue.current) { + processQueue(); + } + }, + [images, port, processQueue] + ); + + // Load current frame after 400ms of inactivity + useEffect(() => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + if (currentFrameId) { + timeoutRef.current = setTimeout(() => { + loadImage(currentFrameId); + }, 400); + } + + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, [currentFrameId, loadImage]); // Fetch timeline data const fetchTimeline = useCallback( @@ -83,29 +152,6 @@ export default function RewindPage() { fetchTimeline(); }, [fetchTimeline]); - const loadImage = useCallback( - (frameId: number) => { - if (!port) return; - // Rate limit to at most 1 request every 200ms - const now = Date.now(); - if (images[frameId]) return; - - lastRequestTime.current = now; - fetch(`http://localhost:${port}/frame/${frameId}`, { - headers: { - "x-api-key": apiKey - } - }) - .then((res) => res.blob()) - .then((blob) => { - const url = URL.createObjectURL(blob); - setImages((prev) => ({ ...prev, [frameId]: url })); - }) - .catch(console.error); - }, - [apiKey, images, port] - ); - // Load initial images useEffect(() => { if (timeline.length > 0 && !currentFrameId) { @@ -134,6 +180,8 @@ export default function RewindPage() { const newIndex = Math.min(Math.max(currentIndex - delta, 0), timeline.length - 1); const newFrameId = timeline[newIndex].id; + console.log(currentFrameId, lastAvaliableFrameId); + if (newFrameId !== currentFrameId) { setCurrentFrameId(newFrameId); // Preload adjacent images @@ -178,7 +226,7 @@ export default function RewindPage() { {/* Time capsule */}
{currentFrameId @@ -205,7 +253,7 @@ export default function RewindPage() { }} > #{frame.id} - + {dayjs().diff(dayjs.unix(frame.createdAt), "second")} sec ago
diff --git a/src/electron/server/index.ts b/src/electron/server/index.ts index 877dea8..5cf793f 100644 --- a/src/electron/server/index.ts +++ b/src/electron/server/index.ts @@ -120,6 +120,9 @@ app.get("/frame/:id", async (c) => { if (existsSync(decodedPath)) { const imageBuffer = fs.readFileSync(decodedPath); + setTimeout(() => { + fs.unlinkSync(decodedPath); + }, 1000); return new Response(imageBuffer, { status: 200, headers: {