2026-04-27 17:33:52 +03:00
|
|
|
const boxPanel = document.querySelector(".box-panel[data-box-id]");
|
|
|
|
|
const boxStatus = document.querySelector(".box-statusbar span:first-child");
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.box-file[aria-disabled="true"]').forEach((item) => {
|
|
|
|
|
item.addEventListener("click", (event) => {
|
|
|
|
|
if (item.getAttribute("aria-disabled") === "true") {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function updateBoxFile(file) {
|
|
|
|
|
const item = document.querySelector(`.box-file[data-file-id="${file.id}"]`);
|
|
|
|
|
if (!item) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const meta = item.querySelector(".box-file-meta");
|
2026-04-28 18:44:16 +03:00
|
|
|
const icon = item.querySelector(".box-file-icon");
|
2026-04-27 17:33:52 +03:00
|
|
|
const isComplete = file.status === "complete";
|
|
|
|
|
const isFailed = file.status === "failed";
|
|
|
|
|
|
|
|
|
|
item.classList.toggle("is-complete", isComplete);
|
|
|
|
|
item.classList.toggle("is-failed", isFailed);
|
|
|
|
|
item.classList.toggle("is-loading", !isComplete && !isFailed);
|
2026-04-28 18:44:16 +03:00
|
|
|
item.classList.toggle("has-thumbnail", Boolean(file.thumbnail_path));
|
2026-04-27 17:33:52 +03:00
|
|
|
item.dataset.status = file.status;
|
|
|
|
|
item.title = file.title;
|
|
|
|
|
|
|
|
|
|
if (isComplete) {
|
|
|
|
|
item.href = file.download_path;
|
|
|
|
|
item.setAttribute("download", "");
|
|
|
|
|
item.removeAttribute("aria-disabled");
|
|
|
|
|
} else {
|
|
|
|
|
item.href = "#";
|
|
|
|
|
item.removeAttribute("download");
|
|
|
|
|
item.setAttribute("aria-disabled", "true");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (meta) {
|
|
|
|
|
meta.textContent = `${file.status_label} · ${file.size_label}`;
|
|
|
|
|
}
|
2026-04-28 18:44:16 +03:00
|
|
|
|
|
|
|
|
if (icon) {
|
|
|
|
|
icon.src = file.thumbnail_path || file.icon_path;
|
|
|
|
|
}
|
2026-04-27 17:33:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function refreshBoxStatus() {
|
|
|
|
|
if (!boxPanel) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const boxID = boxPanel.dataset.boxId;
|
|
|
|
|
const response = await fetch(`/box/${boxID}/status`);
|
|
|
|
|
if (!response.ok) {
|
2026-04-27 17:49:19 +03:00
|
|
|
return true;
|
2026-04-27 17:33:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
result.files.forEach(updateBoxFile);
|
|
|
|
|
|
|
|
|
|
if (boxStatus) {
|
|
|
|
|
const completeCount = result.files.filter((file) => file.status === "complete").length;
|
|
|
|
|
boxStatus.textContent = `${completeCount}/${result.files.length} ready`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:44:16 +03:00
|
|
|
return result.files.some((file) => {
|
|
|
|
|
const isUploading = file.status === "pending" || file.status === "uploading";
|
|
|
|
|
const isWaitingForThumbnail = file.status === "complete" && !file.thumbnail_status && !file.thumbnail_path;
|
|
|
|
|
return isUploading || isWaitingForThumbnail || file.thumbnail_status === "processing";
|
|
|
|
|
});
|
2026-04-27 17:33:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (boxPanel) {
|
2026-04-27 17:49:19 +03:00
|
|
|
const pollMS = Number.parseInt(boxPanel.dataset.pollMs, 10) || 5000;
|
2026-04-27 17:33:52 +03:00
|
|
|
const timer = setInterval(async () => {
|
2026-04-27 17:49:19 +03:00
|
|
|
try {
|
|
|
|
|
const hasLoadingFiles = await refreshBoxStatus();
|
|
|
|
|
if (!hasLoadingFiles) {
|
|
|
|
|
clearInterval(timer);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// Keep polling through temporary network/server hiccups; otherwise
|
|
|
|
|
// an in-progress file can appear stuck forever after one bad poll.
|
2026-04-27 17:33:52 +03:00
|
|
|
}
|
2026-04-27 17:49:19 +03:00
|
|
|
}, pollMS);
|
2026-04-27 17:33:52 +03:00
|
|
|
}
|