fix: screenshots on windows and android

This commit is contained in:
neoapps-dev 2026-08-13 19:18:38 +03:00
parent bef3532c93
commit 8c4e36b23a
5 changed files with 30 additions and 102 deletions

7
src-tauri/Cargo.lock generated
View file

@ -1913,6 +1913,12 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "http-range"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "httparse"
version = "1.10.1"
@ -4742,6 +4748,7 @@ dependencies = [
"gtk",
"heck 0.5.0",
"http 1.4.0",
"http-range",
"image 0.25.10",
"jni",
"libc",

View file

@ -13,7 +13,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = ["tray-icon", "image-png"] }
tauri = { version = "2", features = ["protocol-asset", "tray-icon", "image-png"] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View file

@ -10,6 +10,15 @@
"frontendDist": "../dist"
},
"app": {
"security": {
"assetProtocol": {
"enable": true,
"scope": {
"allow": ["**"],
"requireLiteralLeadingDot": false
}
}
},
"windows": [
{
"label": "main",

View file

@ -1,5 +1,5 @@
import { useState, useEffect, useRef } from "react";
import { TauriService } from "../../services/TauriService";
import { useState } from "react";
import { convertFileSrc } from "@tauri-apps/api/core";
interface ScreenshotImageProps {
path: string;
className?: string;
@ -9,50 +9,6 @@ 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,
@ -61,62 +17,18 @@ export function ScreenshotImage({
style,
fallbackSrc,
}: 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;
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, loading]);
const handleError = () => {
if (fallbackSrc) setSrc(fallbackSrc);
};
const [hasError, setHasError] = useState(false);
return (
<img
ref={imgRef}
src={src}
src={hasError && fallbackSrc ? fallbackSrc : convertFileSrc(path)}
className={className}
alt={alt}
loading={loading}
style={style}
onError={handleError}
onError={() => {
if (fallbackSrc) setHasError(true);
}}
/>
);
}

View file

@ -274,7 +274,7 @@ const ScreenshotsView = memo(function ScreenshotsView() {
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-[200] bg-black/90 flex flex-col items-center justify-center p-8 backdrop-blur-md"
className="fixed inset-0 z-[200] bg-black/90 flex flex-col items-center justify-center p-4 sm:p-8 backdrop-blur-md"
onClick={() => setSelectedScreenshot(null)}
>
<motion.div
@ -282,7 +282,7 @@ const ScreenshotsView = memo(function ScreenshotsView() {
animate={{ scale: 1, opacity: 1, y: 0 }}
exit={{ scale: 0.9, opacity: 0, y: 20 }}
transition={{ type: "spring", damping: 25, stiffness: 300 }}
className="relative max-w-5xl w-full flex flex-col items-center border-2 border-[#555] rounded-sm p-2"
className="relative max-w-5xl w-full max-h-[92vh] overflow-y-auto flex flex-col items-center border-2 border-[#555] rounded-sm p-2"
style={{
backgroundImage: "url('/images/frame_background.png')",
backgroundSize: "100% 100%",
@ -290,11 +290,11 @@ const ScreenshotsView = memo(function ScreenshotsView() {
}}
onClick={(e) => e.stopPropagation()}
>
<div className="relative w-full aspect-video bg-black/60 overflow-hidden border border-[#444] rounded-sm">
<div className="relative w-full aspect-video max-h-[55vh] bg-black/60 overflow-hidden border border-[#444] rounded-sm">
<ScreenshotImage
path={selectedScreenshot.path}
className="w-full h-full object-contain"
fallbackSrc="/images/Pack_Icon.png"
fallbackSrc="/images/Folder_Icon.png"
/>
<div className="absolute bottom-4 left-6 right-6 flex items-end justify-between pointer-events-none">
<div className="flex flex-col gap-1">
@ -318,7 +318,7 @@ const ScreenshotsView = memo(function ScreenshotsView() {
</div>
</div>
<div className="flex gap-6 mt-6 mb-2 w-full justify-center px-6">
<div className="flex flex-wrap gap-4 sm:gap-6 mt-4 sm:mt-6 mb-2 w-full justify-center px-4 sm:px-6">
<button
onMouseEnter={() => setModalFocusIndex(0)}
onClick={() => handleOpenFolder(selectedScreenshot)}
@ -395,7 +395,7 @@ const ScreenshotsView = memo(function ScreenshotsView() {
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
className="w-[420px] p-6 border-2 border-[#555] rounded-sm flex flex-col items-center"
className="w-[420px] max-w-[92vw] p-4 sm:p-6 border-2 border-[#555] rounded-sm flex flex-col items-center"
style={{
backgroundImage: "url('/images/frame_background.png')",
backgroundSize: "100% 100%",