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 [processing, setProcessing] = useState(false);
|
||||||
const [checkingIds, setCheckingIds] = useState<string[]>([]);
|
const [checkingIds, setCheckingIds] = useState<string[]>([]);
|
||||||
const [checkErrors, setCheckErrors] = useState<Record<string, 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 [modalId, setModalId] = useState<string | null>(null);
|
||||||
const [bulkOpen, setBulkOpen] = useState(false);
|
const [bulkOpen, setBulkOpen] = useState(false);
|
||||||
|
|
||||||
@@ -796,6 +800,10 @@ export default function UploadPage() {
|
|||||||
for (const temp of targets) {
|
for (const temp of targets) {
|
||||||
if (!temp.file_url) continue;
|
if (!temp.file_url) continue;
|
||||||
if (/\.(mp4|webm)$/i.test(temp.original_filename)) continue;
|
if (/\.(mp4|webm)$/i.test(temp.original_filename)) continue;
|
||||||
|
setIqdbProgress((current) => ({
|
||||||
|
done: current?.done ?? 0,
|
||||||
|
total: (current?.total ?? 0) + 1,
|
||||||
|
}));
|
||||||
try {
|
try {
|
||||||
const blob = await fetchTempBlob(temp.file_url);
|
const blob = await fetchTempBlob(temp.file_url);
|
||||||
const results = await iqdbSearch(
|
const results = await iqdbSearch(
|
||||||
@@ -822,6 +830,9 @@ export default function UploadPage() {
|
|||||||
[temp.temp_id]: describeError(error),
|
[temp.temp_id]: describeError(error),
|
||||||
}));
|
}));
|
||||||
} finally {
|
} finally {
|
||||||
|
setIqdbProgress((current) =>
|
||||||
|
current ? { ...current, done: current.done + 1 } : current,
|
||||||
|
);
|
||||||
setCheckingIds((current) =>
|
setCheckingIds((current) =>
|
||||||
current.filter((value) => value !== temp.temp_id),
|
current.filter((value) => value !== temp.temp_id),
|
||||||
);
|
);
|
||||||
@@ -830,6 +841,9 @@ export default function UploadPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setProcessing(false);
|
setProcessing(false);
|
||||||
setCheckingIds([]);
|
setCheckingIds([]);
|
||||||
|
setIqdbProgress((current) =>
|
||||||
|
current === null || current.done >= current.total ? null : current,
|
||||||
|
);
|
||||||
invalidateUploads();
|
invalidateUploads();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1010,14 +1024,30 @@ export default function UploadPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{busy || processing ? (
|
{busy || processing ? (
|
||||||
|
<div className="flex w-56 flex-col gap-1.5">
|
||||||
<p className="flex items-center gap-2 text-xs text-ctp-overlay0">
|
<p className="flex items-center gap-2 text-xs text-ctp-overlay0">
|
||||||
<Spinner className="h-3.5 w-3.5" />
|
<Spinner className="h-3.5 w-3.5 shrink-0" />
|
||||||
|
<span className="truncate">
|
||||||
{busy
|
{busy
|
||||||
? `Uploading ${Math.min(uploadedCount + 1, entries.length)}/${entries.length}…`
|
? `Uploading ${Math.min(uploadedCount + 1, entries.length)}/${entries.length}…`
|
||||||
: checkingIds.length > 0
|
: iqdbProgress
|
||||||
? `Checking ${checkingIds.length} file(s) against IQDB…`
|
? `Checking IQDB — ${iqdbProgress.done}/${iqdbProgress.total}`
|
||||||
: "Processing uploads…"}
|
: "Processing uploads…"}
|
||||||
|
</span>
|
||||||
</p>
|
</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}
|
) : null}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -1120,7 +1150,10 @@ export default function UploadPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : 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) => (
|
{entries.map((entry) => (
|
||||||
<div
|
<div
|
||||||
key={entry.id}
|
key={entry.id}
|
||||||
|
|||||||
Reference in New Issue
Block a user