mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-07-18 00:10:45 +00:00
feat(screenshots): queues and caching
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { TauriService } from "../../services/TauriService";
|
||||
|
||||
interface ScreenshotImageProps {
|
||||
path: string;
|
||||
className?: string;
|
||||
@@ -10,6 +9,50 @@ interface ScreenshotImageProps {
|
||||
fallbackSrc?: string;
|
||||
}
|
||||
|
||||
const imgCache = new Map<string, string>();
|
||||
let activeLoads = 0;
|
||||
const MAX_CONCURRENT = 4;
|
||||
const loadQueue: Array<() => void> = [];
|
||||
function dequeue() {
|
||||
while (activeLoads < MAX_CONCURRENT && loadQueue.length > 0) {
|
||||
const next = loadQueue.shift()!;
|
||||
activeLoads++;
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
function enqueueLoad(
|
||||
path: string,
|
||||
onLoad: (url: string) => void,
|
||||
onError: () => void,
|
||||
) {
|
||||
const cached = imgCache.get(path);
|
||||
if (cached) {
|
||||
onLoad(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
const run = () => {
|
||||
TauriService.readScreenshotAsDataUrl(path)
|
||||
.then((url) => {
|
||||
imgCache.set(path, url);
|
||||
onLoad(url);
|
||||
})
|
||||
.catch(() => onError())
|
||||
.finally(() => {
|
||||
activeLoads--;
|
||||
dequeue();
|
||||
});
|
||||
};
|
||||
|
||||
if (activeLoads < MAX_CONCURRENT) {
|
||||
activeLoads++;
|
||||
run();
|
||||
} else {
|
||||
loadQueue.push(run);
|
||||
}
|
||||
}
|
||||
|
||||
export function ScreenshotImage({
|
||||
path,
|
||||
className,
|
||||
@@ -20,22 +63,47 @@ export function ScreenshotImage({
|
||||
}: ScreenshotImageProps) {
|
||||
const [src, setSrc] = useState<string | undefined>(fallbackSrc);
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
|
||||
const loadedRef = useRef(false);
|
||||
useEffect(() => {
|
||||
const el = imgRef.current?.parentElement || imgRef.current;
|
||||
if (!el) return;
|
||||
let cancelled = false;
|
||||
setSrc(fallbackSrc);
|
||||
TauriService.readScreenshotAsDataUrl(path)
|
||||
.then((url) => {
|
||||
if (!cancelled) setSrc(url);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled && fallbackSrc) setSrc(fallbackSrc);
|
||||
});
|
||||
const doLoad = () => {
|
||||
if (loadedRef.current) return;
|
||||
loadedRef.current = true;
|
||||
enqueueLoad(
|
||||
path,
|
||||
(url) => {
|
||||
if (!cancelled) setSrc(url);
|
||||
},
|
||||
() => {
|
||||
if (!cancelled && fallbackSrc) setSrc(fallbackSrc);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
if (loading === "eager") {
|
||||
doLoad();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
observer.disconnect();
|
||||
doLoad();
|
||||
}
|
||||
},
|
||||
{ rootMargin: "800px" },
|
||||
);
|
||||
observer.observe(el);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [path, fallbackSrc]);
|
||||
|
||||
}, [path, fallbackSrc, loading]);
|
||||
const handleError = () => {
|
||||
if (fallbackSrc) setSrc(fallbackSrc);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user