Stop Firefox from squashing upload tiles and show IQDB queue progress
The upload grid was its own scroll container (max-height + overflow on the same element). Firefox sizes auto grid rows to min-content in that setup, so every tile collapsed to its footer height and the image was clipped to a wide strip; Chromium sizes them to max-content, which is why this only showed up in the user's browser. auto-rows-max pins rows to max-content in both; verified with headless Firefox screenshots and Chromium measurements (60 tiles render 152x194 each, 1845px of content in a 639px scroller). The box is now 70vh so it behaves as a proper fixed gallery area with its own scrollbar instead of shrinking with the item count. IQDB progress: the page header now shows a live 'Checking IQDB — x/y' counter with a bar while background checks run, so the queue is visible without opening the per-file modal (which keeps its spinner, candidates and per-file errors).
This commit is contained in:
@@ -679,6 +679,10 @@ export default function UploadPage() {
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const [checkingIds, setCheckingIds] = useState<string[]>([]);
|
||||
const [checkErrors, setCheckErrors] = useState<Record<string, string>>({});
|
||||
const [iqdbProgress, setIqdbProgress] = useState<{
|
||||
done: number;
|
||||
total: number;
|
||||
} | null>(null);
|
||||
const [modalId, setModalId] = useState<string | null>(null);
|
||||
const [bulkOpen, setBulkOpen] = useState(false);
|
||||
|
||||
@@ -796,6 +800,10 @@ export default function UploadPage() {
|
||||
for (const temp of targets) {
|
||||
if (!temp.file_url) continue;
|
||||
if (/\.(mp4|webm)$/i.test(temp.original_filename)) continue;
|
||||
setIqdbProgress((current) => ({
|
||||
done: current?.done ?? 0,
|
||||
total: (current?.total ?? 0) + 1,
|
||||
}));
|
||||
try {
|
||||
const blob = await fetchTempBlob(temp.file_url);
|
||||
const results = await iqdbSearch(
|
||||
@@ -822,6 +830,9 @@ export default function UploadPage() {
|
||||
[temp.temp_id]: describeError(error),
|
||||
}));
|
||||
} finally {
|
||||
setIqdbProgress((current) =>
|
||||
current ? { ...current, done: current.done + 1 } : current,
|
||||
);
|
||||
setCheckingIds((current) =>
|
||||
current.filter((value) => value !== temp.temp_id),
|
||||
);
|
||||
@@ -830,6 +841,9 @@ export default function UploadPage() {
|
||||
} finally {
|
||||
setProcessing(false);
|
||||
setCheckingIds([]);
|
||||
setIqdbProgress((current) =>
|
||||
current === null || current.done >= current.total ? null : current,
|
||||
);
|
||||
invalidateUploads();
|
||||
}
|
||||
}
|
||||
@@ -1010,14 +1024,30 @@ export default function UploadPage() {
|
||||
</p>
|
||||
</div>
|
||||
{busy || processing ? (
|
||||
<p className="flex items-center gap-2 text-xs text-ctp-overlay0">
|
||||
<Spinner className="h-3.5 w-3.5" />
|
||||
{busy
|
||||
? `Uploading ${Math.min(uploadedCount + 1, entries.length)}/${entries.length}…`
|
||||
: checkingIds.length > 0
|
||||
? `Checking ${checkingIds.length} file(s) against IQDB…`
|
||||
: "Processing uploads…"}
|
||||
</p>
|
||||
<div className="flex w-56 flex-col gap-1.5">
|
||||
<p className="flex items-center gap-2 text-xs text-ctp-overlay0">
|
||||
<Spinner className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="truncate">
|
||||
{busy
|
||||
? `Uploading ${Math.min(uploadedCount + 1, entries.length)}/${entries.length}…`
|
||||
: iqdbProgress
|
||||
? `Checking IQDB — ${iqdbProgress.done}/${iqdbProgress.total}`
|
||||
: "Processing uploads…"}
|
||||
</span>
|
||||
</p>
|
||||
{!busy && iqdbProgress ? (
|
||||
<div className="h-1 overflow-hidden rounded-full bg-ctp-surface0">
|
||||
<div
|
||||
className="h-full bg-ctp-teal transition-[width]"
|
||||
style={{
|
||||
width: `${Math.round(
|
||||
(iqdbProgress.done / iqdbProgress.total) * 100,
|
||||
)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</header>
|
||||
|
||||
@@ -1120,7 +1150,10 @@ export default function UploadPage() {
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="grid max-h-[60vh] grid-cols-[repeat(auto-fill,minmax(150px,1fr))] content-start gap-3 overflow-y-auto overscroll-contain pr-1">
|
||||
{/* auto-rows-max: when the grid is its own scroll container Firefox
|
||||
sizes auto rows to min-content and squashes the tiles; max-content
|
||||
keeps every tile at its aspect-ratio size and scrolls instead. */}
|
||||
<div className="grid max-h-[70vh] auto-rows-max grid-cols-[repeat(auto-fill,minmax(150px,1fr))] content-start gap-3 overflow-y-auto overscroll-contain pr-1">
|
||||
{entries.map((entry) => (
|
||||
<div
|
||||
key={entry.id}
|
||||
|
||||
Reference in New Issue
Block a user