diff --git a/src/components/DownloadResult.tsx b/src/components/DownloadResult.tsx index 9cb04286..e510163c 100644 --- a/src/components/DownloadResult.tsx +++ b/src/components/DownloadResult.tsx @@ -6,7 +6,6 @@ import { formatBytes } from "@/lib/utils"; import { Download, RotateCcw, Share2, AlertCircle, Volume2, VolumeX } from "lucide-react"; import LottiePlayer from "./LottiePlayer"; import { NativeShareButton } from "./NativeShareButton"; -import successAnim from "@/lib/lottie/success.json"; import { cn } from "@/lib/utils"; const SHARE_TWEET_TEXT = @@ -32,6 +31,7 @@ interface Props { export default function DownloadResult({ result, onReset, soundOnCompletion, onToggleSound }: Props) { const defaultName = `reframe_${result.width}x${result.height}`; const [name, setName] = useState(defaultName); + const [successAnim, setSuccessAnim] = useState(null); const invalidCharRegex = /[<>:"/\\|?*]/; const isValid = !invalidCharRegex.test(name) && name.trim().length > 0; @@ -50,6 +50,26 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT }); } }, [soundOnCompletion]); + + useEffect(() => { + let cancelled = false; + + import("@/lib/lottie/success.json") + .then((mod) => { + if (!cancelled) { + setSuccessAnim(mod.default ?? mod); + } + }) + .catch(() => { + if (!cancelled) { + setSuccessAnim(null); + } + }); + + return () => { + cancelled = true; + }; + }, []); const handleReset = () => { if (window.confirm("This will clear the current video and all settings. Continue?")) { onReset(); @@ -61,7 +81,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
- + {successAnim ? : null}

Export complete

diff --git a/src/components/ExportOverlay.tsx b/src/components/ExportOverlay.tsx index 702461cb..5cae1aa6 100644 --- a/src/components/ExportOverlay.tsx +++ b/src/components/ExportOverlay.tsx @@ -4,7 +4,6 @@ import FocusTrap from "focus-trap-react"; import { useEffect, useRef, useCallback, useState } from "react"; import { ExportStatus } from "@/lib/types"; import LottiePlayer from "./LottiePlayer"; -import spinnerAnim from "@/lib/lottie/spinner.json"; import TipCarousel from "./TipCarousel"; interface Props { @@ -26,6 +25,7 @@ export default function ExportOverlay({ status, progress, exportStartedAt, onCan const previousFocusRef = useRef(null); const focusAnchorRef = useRef(null); const [elapsedMs, setElapsedMs] = useState(0); + const [spinnerAnim, setSpinnerAnim] = useState(null); const handleKeyDown = useCallback((e: KeyboardEvent) => { if (e.key === "Escape") { @@ -74,6 +74,26 @@ export default function ExportOverlay({ status, progress, exportStartedAt, onCan return () => window.clearInterval(timer); }, [status, exportStartedAt]); + useEffect(() => { + let cancelled = false; + + import("@/lib/lottie/spinner.json") + .then((mod) => { + if (!cancelled) { + setSpinnerAnim(mod.default ?? mod); + } + }) + .catch(() => { + if (!cancelled) { + setSpinnerAnim(null); + } + }); + + return () => { + cancelled = true; + }; + }, []); + if (!visible) return null; const isLoading = status === "loading-engine"; @@ -105,12 +125,14 @@ export default function ExportOverlay({ status, progress, exportStartedAt, onCan aria-hidden="true" />
-

diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx index f94c17ef..99824b4c 100644 --- a/src/components/FileUpload.tsx +++ b/src/components/FileUpload.tsx @@ -3,7 +3,6 @@ import { useRef, useState, useEffect, useCallback } from "react"; import { Film, FolderOpen } from "lucide-react"; import LottiePlayer from "./LottiePlayer"; -import uploadAnim from "@/lib/lottie/upload.json"; import { cn, formatBytes, formatDuration } from "@/lib/utils"; import { MAX_FILE_SIZE, WARNING_FILE_SIZE } from "@/lib/types"; @@ -26,6 +25,7 @@ export default function FileUpload({ const [pageDragging, setPageDragging] = useState(false); const [error, setError] = useState(""); const [warning, setWarning] = useState(""); + const [uploadAnim, setUploadAnim] = useState(null); const dragCounterRef = useRef(0); // ── Keyboard shortcut Ctrl+O ────────────────────────── @@ -40,6 +40,26 @@ export default function FileUpload({ return () => document.removeEventListener("keydown", handler); }, []); + useEffect(() => { + let cancelled = false; + + import("@/lib/lottie/upload.json") + .then((mod) => { + if (!cancelled) { + setUploadAnim(mod.default ?? mod); + } + }) + .catch(() => { + if (!cancelled) { + setUploadAnim(null); + } + }); + + return () => { + cancelled = true; + }; + }, []); + // ── Page-level drag overlay ─────────────────────────── // Uses a counter so nested dragenter/dragleave don't flicker useEffect(() => { @@ -174,7 +194,7 @@ export default function FileUpload({ { const f = e.target.files?.[0]; @@ -216,7 +236,7 @@ export default function FileUpload({ )}
- + {uploadAnim ? : null}
@@ -305,7 +325,7 @@ export default function FileUpload({ { const f = e.target.files?.[0]; diff --git a/src/lib/ffmpeg.worker.ts b/src/lib/ffmpeg.worker.ts index f72ef583..4cd4cf96 100644 --- a/src/lib/ffmpeg.worker.ts +++ b/src/lib/ffmpeg.worker.ts @@ -486,7 +486,7 @@ async function runExport(request: ExportRequest): Promise { } let missingAudioDetected = false; - const logListener = ({ message }: { message: string }) => { + logListener = ({ message }: { message: string }) => { const msg = message.toLowerCase(); if ( msg.includes("matches no streams") ||