mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Split form.css into form-base.css and form-admin.css, drop dead upload-progress code
Also introduces $extraCssAdmin support in head.php for admin-only stylesheets (form-admin.css, filepond CSS, system.css). Admin pages now use $extraCssAdmin for admin-only assets and $extraCss for shared stylesheets like form-base.css.
This commit is contained in:
@@ -1,221 +0,0 @@
|
||||
/**
|
||||
* upload-progress.js
|
||||
*
|
||||
* Intercepts admin form submissions with files and submits via XMLHttpRequest.
|
||||
* Polls GET /admin/actions/upload-progress.php?token=xxx for server-side
|
||||
* processing progress (PeerTube uploads, file moves).
|
||||
*
|
||||
* Progress display:
|
||||
* 0%–25% : browser → server upload (XHR upload.progress)
|
||||
* 25%–99% : server processing (polled from progress endpoint)
|
||||
* 100% : response received — "Téléversé avec succès", then redirect
|
||||
*/
|
||||
(() => {
|
||||
const FORMS = document.querySelectorAll("form[data-upload-progress]");
|
||||
if (!FORMS.length) return;
|
||||
|
||||
const POLL_INTERVAL = 400;
|
||||
const UPLOAD_CAP = 25;
|
||||
const PROCESSING_MAX = 99;
|
||||
const SUCCESS_DELAY = 800;
|
||||
|
||||
for (const form of FORMS) {
|
||||
const progressWrap = form.querySelector("#upload-progress-wrap");
|
||||
const progressBar = form.querySelector("#upload-progress-bar");
|
||||
const progressLabel = form.querySelector("#upload-progress-label");
|
||||
const progressFile = form.querySelector("#upload-progress-file");
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const tokenInput = form.querySelector('input[name="progress_token"]');
|
||||
|
||||
if (!progressBar || !progressWrap) continue;
|
||||
|
||||
function collectFileNames() {
|
||||
const names = [];
|
||||
// Check raw <input type="file"> elements (non-FilePond)
|
||||
const inputs = form.querySelectorAll('input[type="file"]');
|
||||
for (const fi of inputs) {
|
||||
if (fi.files) {
|
||||
for (const f of fi.files) {
|
||||
if (f.name) names.push(f.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Read processed file names from FilePond instances (async mode)
|
||||
if (typeof FilePond !== "undefined") {
|
||||
const pondInputs = form.querySelectorAll(".tfe-file-picker");
|
||||
for (const pi of pondInputs) {
|
||||
const pond = FilePond.find(pi);
|
||||
if (pond) {
|
||||
const pondFiles = pond.getFiles();
|
||||
for (const pf of pondFiles) {
|
||||
// Only count successfully uploaded files (have serverId)
|
||||
if (pf.serverId) {
|
||||
const name = pf.filename || pf.file?.name || pf.serverId;
|
||||
if (name) names.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
form.addEventListener("submit", (e) => {
|
||||
// ── Guard: block submit if any FilePond item is still uploading ──
|
||||
if (typeof FilePond !== "undefined") {
|
||||
let stillUploading = false;
|
||||
const pondInputs = form.querySelectorAll(".tfe-file-picker");
|
||||
for (const pi of pondInputs) {
|
||||
const pond = FilePond.find(pi);
|
||||
if (pond) {
|
||||
const pondFiles = pond.getFiles();
|
||||
for (const pf of pondFiles) {
|
||||
if (
|
||||
pf.status === FilePond.FileStatus.PROCESSING ||
|
||||
pf.status === FilePond.FileStatus.IDLE
|
||||
) {
|
||||
stillUploading = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stillUploading) break;
|
||||
}
|
||||
if (stillUploading) {
|
||||
e.preventDefault();
|
||||
progressLabel.textContent =
|
||||
"Veuillez attendre la fin du téléversement…";
|
||||
progressWrap.style.display = "";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const fileNames = collectFileNames();
|
||||
if (!fileNames.length) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const token = tokenInput ? tokenInput.value : "";
|
||||
|
||||
progressWrap.style.display = "";
|
||||
progressBar.value = 0;
|
||||
progressBar.removeAttribute("data-complete");
|
||||
progressLabel.textContent = "Téléversement en cours…";
|
||||
progressFile.textContent =
|
||||
fileNames.length === 1 ? fileNames[0] : `${fileNames.length} fichiers`;
|
||||
if (submitBtn) submitBtn.disabled = true;
|
||||
|
||||
const fd = new FormData(form);
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
let _uploadDone = false;
|
||||
let lastUploadPct = 0;
|
||||
let pollingTimer = null;
|
||||
|
||||
/** Poll server-side progress */
|
||||
function startPolling() {
|
||||
if (pollingTimer || !token) return;
|
||||
progressLabel.textContent = "Traitement en cours…";
|
||||
pollingTimer = setInterval(() => {
|
||||
fetch(
|
||||
"/admin/actions/upload-progress.php?token=" +
|
||||
encodeURIComponent(token),
|
||||
)
|
||||
.then((r) => r.json())
|
||||
.then((data) => {
|
||||
if (data?.stage && data.stage !== "upload") {
|
||||
const pct = Math.min(
|
||||
PROCESSING_MAX,
|
||||
Math.max(UPLOAD_CAP, data.pct || UPLOAD_CAP),
|
||||
);
|
||||
progressBar.value = pct;
|
||||
if (data.file) {
|
||||
progressFile.textContent = data.file;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
/* ignore poll errors */
|
||||
});
|
||||
}, POLL_INTERVAL);
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (pollingTimer) {
|
||||
clearInterval(pollingTimer);
|
||||
pollingTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function finishSuccess() {
|
||||
stopPolling();
|
||||
progressBar.value = 100;
|
||||
progressBar.setAttribute("data-complete", "");
|
||||
progressLabel.textContent = "Téléversé avec succès";
|
||||
progressFile.textContent = "";
|
||||
}
|
||||
|
||||
// ── Upload phase (0% → UPLOAD_CAP) ──
|
||||
xhr.upload.addEventListener("progress", (evt) => {
|
||||
if (evt.lengthComputable) {
|
||||
const rawPct = Math.round((evt.loaded / evt.total) * 100);
|
||||
const scaled = Math.round((rawPct / 100) * UPLOAD_CAP);
|
||||
if (scaled > lastUploadPct) {
|
||||
lastUploadPct = scaled;
|
||||
progressBar.value = scaled;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
xhr.upload.addEventListener("loadend", () => {
|
||||
_uploadDone = true;
|
||||
progressBar.value = UPLOAD_CAP;
|
||||
startPolling();
|
||||
});
|
||||
|
||||
// ── Response handling ──
|
||||
xhr.addEventListener("readystatechange", () => {
|
||||
if (xhr.readyState !== XMLHttpRequest.DONE) return;
|
||||
|
||||
stopPolling();
|
||||
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
finishSuccess();
|
||||
|
||||
setTimeout(() => {
|
||||
const finalUrl = xhr.responseURL || "";
|
||||
if (finalUrl && finalUrl !== form.action) {
|
||||
window.location.href = finalUrl;
|
||||
} else {
|
||||
document.open();
|
||||
document.write(xhr.responseText);
|
||||
document.close();
|
||||
}
|
||||
}, SUCCESS_DELAY);
|
||||
} else {
|
||||
progressLabel.textContent = "Erreur";
|
||||
progressFile.textContent = "Échec du téléversement";
|
||||
document.open();
|
||||
document.write(xhr.responseText);
|
||||
document.close();
|
||||
}
|
||||
});
|
||||
|
||||
xhr.addEventListener("error", () => {
|
||||
stopPolling();
|
||||
progressLabel.textContent = "Erreur réseau";
|
||||
progressFile.textContent = "";
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
});
|
||||
|
||||
xhr.addEventListener("abort", () => {
|
||||
stopPolling();
|
||||
progressWrap.style.display = "none";
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
});
|
||||
|
||||
xhr.open("POST", form.action, true);
|
||||
xhr.send(fd);
|
||||
});
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user