From 3052836f3b8184611b3b42f45a13404c0b43668b Mon Sep 17 00:00:00 2001 From: Linrador <68631622+Linrador@users.noreply.github.com> Date: Tue, 31 Mar 2026 18:02:58 +0200 Subject: [PATCH] updated --- frontend/src/App.tsx | 160 ++++---- frontend/src/components/ui/Downloads.tsx | 83 +++- .../src/components/ui/FinishedDownloads.tsx | 358 +----------------- 3 files changed, 159 insertions(+), 442 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 312ffaf..e8d6f7a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -751,7 +751,7 @@ export default function App() { } finally { donePrefetchInFlightRef.current = false } - }, [doneSort, includeKeep]) + }, [doneSort, includeKeep, donePageSize]) const loadDoneCount = useCallback(async () => { const now = Date.now() @@ -794,6 +794,11 @@ export default function App() { donePrefetchRef.current = null + const totalPages = getDoneTotalPages(doneCount, donePageSize) + if (pageToLoad < totalPages) { + void prefetchDonePage(pageToLoad + 1) + } + return } @@ -822,10 +827,15 @@ export default function App() { if (count !== doneCount) { setDoneCount(count) } + + const totalPages = getDoneTotalPages(count, donePageSize) + if (pageToLoad < totalPages) { + void prefetchDonePage(pageToLoad + 1) + } } catch { // ignore } - }, [donePage, doneSort, prefetchDonePage, includeKeep, doneCount]) + }, [donePage, doneSort, donePageSize, prefetchDonePage, includeKeep, doneCount]) useEffect(() => { try { @@ -2767,6 +2777,47 @@ export default function App() { } } + const fillDonePageFromPrefetch = useCallback((opts?: { removedFile?: string }) => { + const removedFile = String(opts?.removedFile ?? '').trim() + + setDoneJobs((prev) => { + const filtered = removedFile + ? prev.filter((j) => baseName(j.output || '') !== removedFile) + : [...prev] + + const need = Math.max(0, donePageSize - filtered.length) + if (need <= 0) return filtered + + const prefetchKey = makePrefetchKey(donePage + 1, doneSort) + const buf = donePrefetchRef.current + + if (!buf || buf.key !== prefetchKey || !Array.isArray(buf.items) || buf.items.length === 0) { + return filtered + } + + const next: RecordJob[] = [...filtered] + const used = new Set( + next.map((x) => String(x.id || baseName(x.output || '')).trim()) + ) + + while (next.length < donePageSize && buf.items.length > 0) { + const cand = buf.items.shift()! + const id = String(cand.id || baseName(cand.output || '')).trim() + if (!id || used.has(id)) continue + used.add(id) + next.push(cand) + } + + donePrefetchRef.current = { + ...buf, + items: buf.items, + ts: buf.ts, + } + + return next + }) + }, [donePage, donePageSize, doneSort]) + const handleDeleteJobWithUndo = useCallback( async (job: RecordJob): Promise => { const file = baseName(job.output || '') @@ -2783,45 +2834,14 @@ export default function App() { ), onSuccess: async () => { window.setTimeout(() => { - // ✅ Done-Liste lokal bereinigen + Seite direkt wieder auffüllen (aus Prefetch) - setDoneJobs((prev) => { - const filtered = prev.filter((j) => baseName(j.output || '') !== file) + fillDonePageFromPrefetch({ removedFile: file }) - const need = donePageSize - filtered.length - if (need <= 0) return filtered - - const prefetchKey = makePrefetchKey(donePage + 1, doneSort) - const buf = donePrefetchRef.current - - if (!buf || buf.key !== prefetchKey || !Array.isArray(buf.items) || buf.items.length === 0) { - return filtered - } - - const next: RecordJob[] = [...filtered] - const used = new Set(next.map((x) => String(x.id || baseName(x.output || '')).trim())) - - while (next.length < donePageSize && buf.items.length > 0) { - const cand = buf.items.shift()! - const id = String(cand.id || baseName(cand.output || '')).trim() - if (!id || used.has(id)) continue - used.add(id) - next.push(cand) - } - - donePrefetchRef.current = { ...buf, items: buf.items, ts: buf.ts } - return next - }) - - // ✅ Count sofort optimistisch runter setDoneCount((c) => Math.max(0, c - 1)) - - // ✅ Running-/Player-State bereinigen (falls offen) setJobs((prev) => prev.filter((j) => baseName(j.output || '') !== file)) setPlayerJob((prev) => (prev && baseName(prev.output || '') === file ? null : prev)) - // ✅ Prefetch wieder nachfüllen – aber nur wenn es noch eine nächste Seite gibt const nextDoneCount = Math.max(0, doneCount - 1) - const totalPagesAfterDelete = getDoneTotalPages(nextDoneCount) + const totalPagesAfterDelete = getDoneTotalPages(nextDoneCount, donePageSize) if (donePage < totalPagesAfterDelete) { void prefetchDonePage(donePage + 1) @@ -2841,7 +2861,15 @@ export default function App() { return } }, - [runFinishedFileAction, notify, donePage, doneSort, prefetchDonePage, doneCount] + [ + runFinishedFileAction, + notify, + fillDonePageFromPrefetch, + donePage, + prefetchDonePage, + doneCount, + loadDoneCount, + ] ) const handleKeepJob = useCallback( @@ -2856,45 +2884,14 @@ export default function App() { run: () => apiJSON(`/api/record/keep?file=${encodeURIComponent(file)}`, { method: 'POST' }), onSuccess: async () => { window.setTimeout(() => { - // ✅ Done-Liste lokal bereinigen + Seite direkt wieder auffüllen (aus Prefetch) - setDoneJobs((prev) => { - const filtered = prev.filter((j) => baseName(j.output || '') !== file) + fillDonePageFromPrefetch({ removedFile: file }) - const need = donePageSize - filtered.length - if (need <= 0) return filtered - - const prefetchKey = makePrefetchKey(donePage + 1, doneSort) - const buf = donePrefetchRef.current - - if (!buf || buf.key !== prefetchKey || !Array.isArray(buf.items) || buf.items.length === 0) { - return filtered - } - - const next: RecordJob[] = [...filtered] - const used = new Set(next.map((x) => String(x.id || baseName(x.output || '')).trim())) - - while (next.length < donePageSize && buf.items.length > 0) { - const cand = buf.items.shift()! - const id = String(cand.id || baseName(cand.output || '')).trim() - if (!id || used.has(id)) continue - used.add(id) - next.push(cand) - } - - donePrefetchRef.current = { ...buf, items: buf.items, ts: buf.ts } - return next - }) - - // ✅ Entfernt aus Running + Player setJobs((prev) => prev.filter((j) => baseName(j.output || '') !== file)) setPlayerJob((prev) => (prev && baseName(prev.output || '') === file ? null : prev)) - - // ✅ Count runter setDoneCount((c) => Math.max(0, c - 1)) - // ✅ Prefetch wieder nachfüllen – aber nur wenn es noch eine nächste Seite gibt const nextDoneCount = Math.max(0, doneCount - 1) - const totalPagesAfterKeep = getDoneTotalPages(nextDoneCount) + const totalPagesAfterKeep = getDoneTotalPages(nextDoneCount, donePageSize) if (donePage < totalPagesAfterKeep) { void prefetchDonePage(donePage + 1) @@ -2914,8 +2911,8 @@ export default function App() { [ runFinishedFileAction, notify, + fillDonePageFromPrefetch, donePage, - doneSort, prefetchDonePage, loadDoneCount, doneCount, @@ -3310,18 +3307,21 @@ export default function App() { }, ms) } - const kick = () => void checkClipboard() - window.addEventListener('hover', kick) - document.addEventListener('visibilitychange', kick) + const kick = () => { + void checkClipboard() + } - schedule(0) + window.addEventListener('focus', kick) + document.addEventListener('visibilitychange', kick) - return () => { - cancelled = true - if (timer) window.clearTimeout(timer) - window.removeEventListener('hover', kick) - document.removeEventListener('visibilitychange', kick) - } + schedule(0) + + return () => { + cancelled = true + if (timer) window.clearTimeout(timer) + window.removeEventListener('focus', kick) + document.removeEventListener('visibilitychange', kick) + } }, [autoAddEnabled, autoStartEnabled, enqueueStart]) if (!authChecked) { diff --git a/frontend/src/components/ui/Downloads.tsx b/frontend/src/components/ui/Downloads.tsx index 41ebff4..294d3f5 100644 --- a/frontend/src/components/ui/Downloads.tsx +++ b/frontend/src/components/ui/Downloads.tsx @@ -3,6 +3,7 @@ 'use client' import { useMemo, useState, useCallback, useEffect, useRef } from 'react' +import type { ReactNode } from 'react' import Table, { type Column } from './Table' import Card from './Card' import Button from './Button' @@ -563,6 +564,46 @@ function DiskEmergencyBadge() { ) } +function LazyMountWhenVisible({ + children, + placeholder, + rootMargin = '300px', +}: { + children: ReactNode + placeholder: ReactNode + rootMargin?: string +}) { + const hostRef = useRef(null) + const [visible, setVisible] = useState(false) + + useEffect(() => { + if (visible) return + + const el = hostRef.current + if (!el) return + + const io = new IntersectionObserver( + (entries) => { + const entry = entries[0] + if (entry?.isIntersecting) { + setVisible(true) + io.disconnect() + } + }, + { rootMargin, threshold: 0.01 } + ) + + io.observe(el) + return () => io.disconnect() + }, [visible, rootMargin]) + + return ( +
+ {visible ? children : placeholder} +
+ ) +} + export default function Downloads({ jobs, pending = [], @@ -952,24 +993,30 @@ export default function Downloads({ const j = r.job return (
- + + } + > + +
) } diff --git a/frontend/src/components/ui/FinishedDownloads.tsx b/frontend/src/components/ui/FinishedDownloads.tsx index 051eed4..2a959f6 100644 --- a/frontend/src/components/ui/FinishedDownloads.tsx +++ b/frontend/src/components/ui/FinishedDownloads.tsx @@ -1289,7 +1289,6 @@ export default function FinishedDownloads({ const GALLERY_CARD_SCALE_KEY = 'finishedDownloads_galleryCardScale_v1' const LAST_UNDO_KEY = 'finishedDownloads_lastUndo_v1' - const allMode = loadMode === 'all' const requestedTeaserPlaybackMode: TeaserPlaybackMode = teaserPlayback ?? 'hover' const teaserPlaybackMode: TeaserPlaybackMode = pauseTeasers ? 'still' : requestedTeaserPlaybackMode @@ -1327,7 +1326,6 @@ export default function FinishedDownloads({ const [removingKeys, setRemovingKeys] = React.useState>(() => new Set()) const [hiddenFiles, setHiddenFiles] = React.useState>(() => new Set()) - const [isLoading, setIsLoading] = React.useState(false) const [undoing, setUndoing] = React.useState(false) const [bulkBusy, setBulkBusy] = React.useState(false) @@ -1339,11 +1337,6 @@ export default function FinishedDownloads({ const [renamedFiles, setRenamedFiles] = React.useState>({}) - const [overrideDoneJobs, setOverrideDoneJobs] = React.useState(null) - const [overrideDoneJobsKey, setOverrideDoneJobsKey] = React.useState(null) - const [overrideDoneTotal, setOverrideDoneTotal] = React.useState(null) - const [refillTick, setRefillTick] = React.useState(0) - const [view, setView] = React.useState('table') const [mobileOptionsOpen, setMobileOptionsOpen] = React.useState(false) const [selectionActionsOpen, setSelectionActionsOpen] = React.useState(false) @@ -1396,12 +1389,6 @@ export default function FinishedDownloads({ const teaserKeyRafRef = React.useRef(null) const pendingTeaserKeyRef = React.useRef(null) - const refillInFlightRef = React.useRef(false) - const refillQueuedWhileInFlightRef = React.useRef(false) - const refillSessionRef = React.useRef(0) - const refillTimerRef = React.useRef(null) - const refillRetryRef = React.useRef(0) - const countHintRef = React.useRef({ pending: 0, t: 0, timer: 0 as any }) const scaleLiveRafRef = React.useRef(null) @@ -1418,8 +1405,6 @@ export default function FinishedDownloads({ const removeTimersRef = React.useRef>(new Map()) - const galleryHydratedKeyRef = React.useRef(null) - // ----------------------------------------------------------------------------- // memoized derived data // ----------------------------------------------------------------------------- @@ -1467,39 +1452,17 @@ export default function FinishedDownloads({ activeTagSet.size > 0 || searchTokens.some((t) => t.length >= 2) const globalFilterActive = searchActiveForGlobalFetch - const effectiveAllMode = globalFilterActive || allMode + const effectiveAllMode = globalFilterActive || loadMode === 'all' const galleryPageSize = Math.max(2, galleryColumnCount * 2) - const serverPageSize = pageSize const effectivePageSize = view === 'gallery' ? galleryPageSize : pageSize - const galleryHydrationKey = - view === 'gallery' && loadMode === 'paged' && !effectiveAllMode - ? `${page}__${sortMode}__${includeKeep ? 1 : 0}__${effectivePageSize}` - : null - - const hasMatchingGalleryOverride = - view === 'gallery' && - galleryHydrationKey != null && - overrideDoneJobsKey === galleryHydrationKey - - const doneJobsPage = - effectiveAllMode - ? (overrideDoneJobs ?? doneJobs) - : hasMatchingGalleryOverride && overrideDoneJobs - ? overrideDoneJobs - : doneJobs - - const doneTotalPage = - effectiveAllMode - ? (overrideDoneTotal ?? doneTotal) - : hasMatchingGalleryOverride && overrideDoneTotal != null - ? overrideDoneTotal - : doneTotal + const doneJobsPage = doneJobs + const doneTotalPage = doneTotal const applyRenamedOutputLocal = useCallback( (job: RecordJob): RecordJob => { @@ -1701,9 +1664,7 @@ export default function FinishedDownloads({ const emptyFolder = !effectiveAllMode && totalItemsForPagination === 0 const emptyByFilter = globalFilterActive && visibleRows.length === 0 - const emptyAll = allMode && visibleRows.length === 0 - const isEmptyState = emptyFolder || emptyAll || emptyByFilter - const showLoadingCard = isLoading && !isEmptyState + const isEmptyState = emptyFolder || emptyByFilter const selectedCount = selectedFiles.length const allPageSelected = @@ -1837,42 +1798,6 @@ export default function FinishedDownloads({ const clearTagFilter = useCallback(() => setTagFilter([]), []) - const fetchAllDoneJobs = useCallback( - async (signal?: AbortSignal) => { - setIsLoading(true) - - try { - const res = await fetch( - `/api/record/done?all=1&sort=${encodeURIComponent(sortMode)}&withCount=1${includeKeep ? '&includeKeep=1' : ''}`, - { - cache: 'no-store' as any, - signal, - } - ) - if (!res.ok) return - - const data = await res.json().catch(() => null) - const items = Array.isArray(data?.items) ? (data.items as RecordJob[]) : [] - const count = Number(data?.count ?? data?.totalCount ?? items.length) - - setOverrideDoneJobs(items) - setOverrideDoneTotal(Number.isFinite(count) ? count : items.length) - } finally { - setIsLoading(false) - } - }, - [sortMode, includeKeep] - ) - - const queueRefill = useCallback(() => { - if (refillTimerRef.current != null) return - - refillTimerRef.current = window.setTimeout(() => { - refillTimerRef.current = null - setRefillTick((n) => n + 1) - }, 80) - }, []) - const flushResolutionsSoon = useCallback(() => { if (resolutionsFlushTimerRef.current != null) return resolutionsFlushTimerRef.current = window.setTimeout(() => { @@ -2273,28 +2198,16 @@ export default function FinishedDownloads({ return (await r.json().catch(() => null)) as any }, onSuccess: async (result: any) => { - if (onDeleteJob) { - const undoToken = typeof result?.undoToken === 'string' ? result.undoToken : '' - if (undoToken) { - setLastAction({ kind: 'delete', undoToken, originalFile: file, rowKey: key }) - } else { - setLastAction(null) - } - return - } - - const from = (result?.from === 'keep' ? 'keep' : 'done') as 'done' | 'keep' const undoToken = typeof result?.undoToken === 'string' ? result.undoToken : '' if (undoToken) { - setLastAction({ kind: 'delete', undoToken, originalFile: file, rowKey: key, from }) + setLastAction({ kind: 'delete', undoToken, originalFile: file, rowKey: key }) } else { setLastAction(null) } - queueRefill() emitCountHint(-1) - }, + } }) return res.ok @@ -2305,7 +2218,6 @@ export default function FinishedDownloads({ onDeleteJob, withFileReleaseRetry, runFileMutation, - queueRefill, emitCountHint, ] ) @@ -2355,9 +2267,8 @@ export default function FinishedDownloads({ setLastAction({ kind: 'keep', keptFile, originalFile: file, rowKey: key }) - queueRefill() emitCountHint(includeKeep ? 0 : -1) - }, + } }) return res.ok @@ -2368,7 +2279,6 @@ export default function FinishedDownloads({ deletingKeys, withFileReleaseRetry, runFileMutation, - queueRefill, emitCountHint, includeKeep, ] @@ -2468,7 +2378,6 @@ export default function FinishedDownloads({ const visibleDelta = lastAction.from === 'keep' && !includeKeep ? 0 : +1 emitCountHint(visibleDelta) - queueRefill() setLastAction(null) return } @@ -2490,7 +2399,6 @@ export default function FinishedDownloads({ unhideRow(restoredFile, restoredFile) emitCountHint(+1) - queueRefill() setLastAction(null) return } @@ -2513,7 +2421,6 @@ export default function FinishedDownloads({ applyRename(oldFile, newFile) } - queueRefill() setLastAction(null) return } @@ -2526,7 +2433,6 @@ export default function FinishedDownloads({ lastAction, undoing, notify, - queueRefill, includeKeep, emitCountHint, applyRename, @@ -2594,10 +2500,6 @@ export default function FinishedDownloads({ } setLastAction({ kind: 'hot', currentFile: apiNew }) - - if (!onToggleHot || sortMode === 'file_asc' || sortMode === 'file_desc') { - queueRefill() - } }, onError: async () => { clearRenamePair(oldFile, optimisticNew) @@ -2613,8 +2515,6 @@ export default function FinishedDownloads({ clearRenamePair, onToggleHot, withFileReleaseRetry, - queueRefill, - sortMode, ] ) @@ -2807,7 +2707,6 @@ export default function FinishedDownloads({ const deletedCount = Number(data?.deleted ?? results.filter((x: any) => x?.ok).length ?? 0) clearSelection() - queueRefill() emitCountHint(-deletedCount) if (deletedCount > 0) { @@ -2824,7 +2723,6 @@ export default function FinishedDownloads({ selectedItems, animateRemove, clearSelection, - queueRefill, emitCountHint, notify, ]) @@ -2869,7 +2767,6 @@ export default function FinishedDownloads({ const keptCount = Number(data?.kept ?? results.filter((x: any) => x?.ok).length ?? 0) clearSelection() - queueRefill() emitCountHint(includeKeep ? 0 : -keptCount) if (keptCount > 0) { @@ -2886,7 +2783,6 @@ export default function FinishedDownloads({ selectedItems, animateRemove, clearSelection, - queueRefill, emitCountHint, includeKeep, notify, @@ -2905,21 +2801,6 @@ export default function FinishedDownloads({ } }, [view, galleryColumnCount, pageSize, onPageSizeChange]) - useEffect(() => { - if (effectiveAllMode) return - setOverrideDoneJobs(null) - setOverrideDoneTotal(null) - setOverrideDoneJobsKey(null) - }, [effectiveAllMode]) - - useEffect(() => { - if (view === 'gallery') return - - setOverrideDoneJobs(null) - setOverrideDoneTotal(null) - setOverrideDoneJobsKey(null) - }, [view]) - useEffect(() => { try { const raw = localStorage.getItem(GALLERY_CARD_SCALE_KEY) @@ -3250,73 +3131,6 @@ export default function FinishedDownloads({ }, [lastAction]) useEffect(() => { - if (!effectiveAllMode) return - - const ac = new AbortController() - - const t = window.setTimeout(() => { - fetchAllDoneJobs(ac.signal).catch(() => {}) - }, 250) - - return () => { - window.clearTimeout(t) - ac.abort() - } - }, [effectiveAllMode, fetchAllDoneJobs]) - - useEffect(() => { - if (refillTick === 0) return - - const ac = new AbortController() - let alive = true - let finished = false - const mySession = ++refillSessionRef.current - - const finishRefill = () => { - if (finished) return - finished = true - - if (refillSessionRef.current !== mySession) return - - refillInFlightRef.current = false - - if (refillQueuedWhileInFlightRef.current) { - refillQueuedWhileInFlightRef.current = false - queueRefill() - } - } - - refillInFlightRef.current = true - - // ✅ paged-mode hier NICHT mehr laden - if (!effectiveAllMode) { - finishRefill() - return () => { - alive = false - ac.abort() - finishRefill() - } - } - - ;(async () => { - try { - await fetchAllDoneJobs(ac.signal) - if (alive) refillRetryRef.current = 0 - } catch { - } finally { - if (alive) finishRefill() - } - })() - - return () => { - alive = false - ac.abort() - finishRefill() - } - }, [refillTick, effectiveAllMode, fetchAllDoneJobs, queueRefill]) - - useEffect(() => { - if (isLoading) return if (effectiveAllMode) return if (totalItemsForPagination < 0) return @@ -3326,7 +3140,6 @@ export default function FinishedDownloads({ onPageChange(totalPages) } }, [ - isLoading, effectiveAllMode, totalItemsForPagination, effectivePageSize, @@ -3334,103 +3147,6 @@ export default function FinishedDownloads({ onPageChange, ]) - useEffect(() => { - if (loadMode !== 'paged') return - if (effectiveAllMode) return - if (view !== 'gallery') return - if (!galleryHydrationKey) return - - const currentCount = hasMatchingGalleryOverride - ? (overrideDoneJobs?.length ?? 0) - : doneJobs.length - - const wantedCount = effectivePageSize - const totalCount = doneTotal - - const pageStart = (page - 1) * wantedCount - const maxItemsOnThisPage = Math.max(0, totalCount - pageStart) - - if (maxItemsOnThisPage <= 0) { - galleryHydratedKeyRef.current = galleryHydrationKey - return - } - - const targetCount = Math.min(wantedCount, maxItemsOnThisPage) - - if (currentCount >= targetCount) { - galleryHydratedKeyRef.current = galleryHydrationKey - return - } - - if (galleryHydratedKeyRef.current === galleryHydrationKey) { - return - } - - const ac = new AbortController() - let cancelled = false - - setIsLoading(true) - - ;(async () => { - try { - const res = await fetch( - `/api/record/done?page=${page}&pageSize=${targetCount}&sort=${encodeURIComponent(sortMode)}${ - includeKeep ? '&includeKeep=1' : '' - }`, - { cache: 'no-store' as any, signal: ac.signal } - ) - - if (!res.ok || cancelled || ac.signal.aborted) return - - const data = await res.json().catch(() => null) - if (cancelled || ac.signal.aborted) return - - const items = Array.isArray(data?.items) - ? (data.items as RecordJob[]) - : Array.isArray(data) - ? data - : [] - - const countRaw = Number(data?.count ?? data?.totalCount ?? items.length) - const count = Number.isFinite(countRaw) && countRaw >= 0 ? countRaw : 0 - - setOverrideDoneJobs(items) - setOverrideDoneTotal(count) - setOverrideDoneJobsKey(galleryHydrationKey) - galleryHydratedKeyRef.current = galleryHydrationKey - } catch (err: any) { - if (err?.name !== 'AbortError') { - console.error('gallery hydration failed', err) - } - } finally { - if (!cancelled) { - setIsLoading(false) - } - } - })() - - return () => { - cancelled = true - ac.abort() - } - }, [ - loadMode, - effectiveAllMode, - view, - page, - sortMode, - includeKeep, - doneJobs, - doneTotal, - effectivePageSize, - serverPageSize, - galleryHydrationKey, - ]) - - useEffect(() => { - galleryHydratedKeyRef.current = null - }, [page, sortMode, includeKeep, effectivePageSize, view]) - useEffect(() => { const m = new Map() for (const j of viewRows) { @@ -3516,37 +3232,12 @@ export default function FinishedDownloads({ if (detail.phase === 'success') { markDeleting(key, false) - - if (refillInFlightRef.current) { - refillQueuedWhileInFlightRef.current = true - } else { - queueRefill() - } } } window.addEventListener('finished-downloads:delete', onExternalDelete as EventListener) return () => window.removeEventListener('finished-downloads:delete', onExternalDelete as EventListener) - }, [animateRemove, markDeleting, queueRefill, restoreRow]) - - useEffect(() => { - const onReload = () => { - if (document.hidden) return - - if (refillInFlightRef.current) { - if (!refillQueuedWhileInFlightRef.current) { - refillQueuedWhileInFlightRef.current = true - } - return - } - - if (refillTimerRef.current != null) return - queueRefill() - } - - window.addEventListener('finished-downloads:reload', onReload as EventListener) - return () => window.removeEventListener('finished-downloads:reload', onReload as EventListener) - }, [queueRefill]) + }, [animateRemove, markDeleting, restoreRow]) useEffect(() => { const onExternalRename = (ev: Event) => { @@ -3706,8 +3397,7 @@ export default function FinishedDownloads({ @@ -3866,8 +3556,7 @@ export default function FinishedDownloads({ @@ -4253,26 +3942,7 @@ export default function FinishedDownloads({ - {showLoadingCard ? ( - -
-
-
- Lade Downloads… -
-
- Bitte warte einen Moment. -
-
- - -
-
- ) : (emptyFolder || emptyAll) ? ( + {emptyFolder ? (
@@ -4332,7 +4002,7 @@ export default function FinishedDownloads({ }} isSmall={isSmall} jobForDetails={jobForDetails} - isLoading={isLoading} + isLoading={false} blurPreviews={blurPreviews} durations={durations} teaserState={teaserState} @@ -4394,7 +4064,7 @@ export default function FinishedDownloads({ {view === 'table' && ( { @@ -4465,7 +4135,7 @@ export default function FinishedDownloads({ if (checked) selectPageRows(pageRows) else deselectPageRows(pageRows) }} - isLoading={isLoading} + isLoading={false} jobForDetails={jobForDetails} blurPreviews={blurPreviews} durations={durations}