Global variables consolidated into the ‘state’ object

This commit is contained in:
2025-08-30 01:05:35 +02:00
parent 5a3551a0f5
commit ccefdd23f3

View File

@@ -1,14 +1,16 @@
(() => { (() => {
"use strict"; "use strict";
let AppConfig = null;
let ErrorTimeout = null;
let Files = {};
const DEFAULT_DROPZONE_TEXT = "Drag & drop files here or click to select"; const DEFAULT_DROPZONE_TEXT = "Drag & drop files here or click to select";
const UI = {}; const state = {
config: null,
files: {},
ui: {},
errorTimeout: null,
};
async function appLoop() { async function appLoop() {
if (AppConfig === null) { if (state.config === null) {
return; return;
} }
@@ -34,22 +36,22 @@
if (!res.ok) { if (!res.ok) {
console.error("HTTP error:", res.status); console.error("HTTP error:", res.status);
} }
AppConfig = await res.json(); state.config = await res.json();
} catch (err) { } catch (err) {
console.error("Failed to load config:", err); console.error("Failed to load config:", err);
AppConfig = null; state.config = null;
} }
} }
async function fetchFiles() { async function fetchFiles() {
if (AppConfig.Modes.Sinkhole) { if (state.config.Modes.Sinkhole) {
clearFileList(); clearFileList();
return; return;
} }
try { try {
const files = await fetchFileList(); const files = await fetchFileList();
Files = {}; state.files = {};
clearFileList(); clearFileList();
renderFileList(files); renderFileList(files);
} catch (err) { } catch (err) {
@@ -58,7 +60,9 @@
} }
async function fetchFileList() { async function fetchFileList() {
const res = await fetch(AppConfig.Endpoints.Files, { cache: "no-store" }); const res = await fetch(state.config.Endpoints.Files, {
cache: "no-store",
});
if (!res.ok) throw new Error("HTTP " + res.status); if (!res.ok) throw new Error("HTTP " + res.status);
return res.json(); return res.json();
} }
@@ -69,7 +73,7 @@
try { try {
const res = await fetch( const res = await fetch(
AppConfig.Endpoints.FilesDelete.replace( state.config.Endpoints.FilesDelete.replace(
":filename", ":filename",
encodeURIComponent(file.Name) encodeURIComponent(file.Name)
), ),
@@ -89,20 +93,23 @@
} }
function addEventListeners() { function addEventListeners() {
UI.dropzone.addEventListener("click", () => UI.fileInput.click()); state.ui.dropzone.addEventListener("click", () =>
UI.fileInput.addEventListener("change", () => { state.ui.fileInput.click()
if (UI.fileInput.files.length > 0) uploadFiles(UI.fileInput.files); );
state.ui.fileInput.addEventListener("change", () => {
if (state.ui.fileInput.files.length > 0)
uploadFiles(state.ui.fileInput.files);
}); });
UI.dropzone.addEventListener("dragover", (e) => { state.ui.dropzone.addEventListener("dragover", (e) => {
e.preventDefault(); e.preventDefault();
UI.dropzone.style.borderColor = "#0fff50"; state.ui.dropzone.style.borderColor = "#0fff50";
}); });
UI.dropzone.addEventListener("dragleave", () => { state.ui.dropzone.addEventListener("dragleave", () => {
UI.dropzone.style.borderColor = "#888"; state.ui.dropzone.style.borderColor = "#888";
}); });
UI.dropzone.addEventListener("drop", (e) => { state.ui.dropzone.addEventListener("drop", (e) => {
e.preventDefault(); e.preventDefault();
UI.dropzone.style.borderColor = "#888"; state.ui.dropzone.style.borderColor = "#888";
if (e.dataTransfer.files.length > 0) uploadFiles(e.dataTransfer.files); if (e.dataTransfer.files.length > 0) uploadFiles(e.dataTransfer.files);
}); });
} }
@@ -164,14 +171,14 @@
} }
function clearFileList() { function clearFileList() {
if (UI.fileList) UI.fileList.innerHTML = ""; if (state.ui.fileList) state.ui.fileList.innerHTML = "";
} }
function createDownloadLink(file) { function createDownloadLink(file) {
const size = humanReadableSize(file.Size); const size = humanReadableSize(file.Size);
const link = document.createElement("a"); const link = document.createElement("a");
link.className = "download-link"; link.className = "download-link";
link.href = AppConfig.Endpoints.FilesGet.replace( link.href = state.config.Endpoints.FilesGet.replace(
":filename", ":filename",
encodeURIComponent(file.Name) encodeURIComponent(file.Name)
); );
@@ -190,10 +197,10 @@
} }
function finishUpload(success) { function finishUpload(success) {
UI.overallProgressContainer.style.display = "none"; state.ui.overallProgressContainer.style.display = "none";
UI.overallProgress.value = 0; state.ui.overallProgress.value = 0;
UI.overallStatus.textContent = ""; state.ui.overallStatus.textContent = "";
UI.currentFileName.textContent = ""; state.ui.currentFileName.textContent = "";
fetchFiles(); fetchFiles();
if (success) { if (success) {
showSuccess("Upload successful"); showSuccess("Upload successful");
@@ -201,16 +208,16 @@
} }
function getUIElements() { function getUIElements() {
UI.currentFileName = document.getElementById("currentFileName"); state.ui.currentFileName = document.getElementById("currentFileName");
UI.dropzone = document.getElementById("dropzone"); state.ui.dropzone = document.getElementById("dropzone");
UI.fileInput = document.getElementById("fileInput"); state.ui.fileInput = document.getElementById("fileInput");
UI.fileList = document.getElementById("file-list"); state.ui.fileList = document.getElementById("file-list");
UI.overallProgress = document.getElementById("overallProgress"); state.ui.overallProgress = document.getElementById("overallProgress");
UI.overallStatus = document.getElementById("overallStatus"); state.ui.overallStatus = document.getElementById("overallStatus");
UI.overallProgressContainer = document.getElementById( state.ui.overallProgressContainer = document.getElementById(
"overallProgressContainer" "overallProgressContainer"
); );
UI.sinkholeModeInfo = document.getElementById("sinkholeModeInfo"); state.ui.sinkholeModeInfo = document.getElementById("sinkholeModeInfo");
} }
function humanReadableSize(bytes) { function humanReadableSize(bytes) {
@@ -232,26 +239,26 @@
} }
function initUIProgress() { function initUIProgress() {
UI.overallProgressContainer.style.display = "block"; state.ui.overallProgressContainer.style.display = "block";
UI.overallProgress.value = 0; state.ui.overallProgress.value = 0;
UI.overallStatus.textContent = ""; state.ui.overallStatus.textContent = "";
UI.currentFileName.textContent = ""; state.ui.currentFileName.textContent = "";
} }
function renderFileList(files) { function renderFileList(files) {
if (!UI.fileList) return; if (!state.ui.fileList) return;
files.forEach((file) => { files.forEach((file) => {
Files[file.Name] = true; state.files[file.Name] = true;
const li = document.createElement("li"); const li = document.createElement("li");
li.appendChild(createDownloadLink(file)); li.appendChild(createDownloadLink(file));
if (!AppConfig.Modes.Readonly) { if (!state.config.Modes.Readonly) {
li.appendChild(createDeleteLink(file)); li.appendChild(createDeleteLink(file));
} }
UI.fileList.appendChild(li); state.ui.fileList.appendChild(li);
}); });
} }
@@ -300,15 +307,15 @@
} }
function showMessage(msg, type, duration = 2000) { function showMessage(msg, type, duration = 2000) {
UI.dropzone.innerHTML = msg; state.ui.dropzone.innerHTML = msg;
UI.dropzone.classList.add(type); state.ui.dropzone.classList.add(type);
if (ErrorTimeout) clearTimeout(ErrorTimeout); if (state.errorTimeout) clearTimeout(state.errorTimeout);
ErrorTimeout = setTimeout(() => { state.errorTimeout = setTimeout(() => {
UI.dropzone.innerHTML = DEFAULT_DROPZONE_TEXT; state.ui.dropzone.innerHTML = DEFAULT_DROPZONE_TEXT;
UI.dropzone.classList.remove(type); state.ui.dropzone.classList.remove(type);
ErrorTimeout = null; state.errorTimeout = null;
}, duration); }, duration);
} }
@@ -317,18 +324,18 @@
} }
function updateUI() { function updateUI() {
if (AppConfig.Modes.Readonly) { if (state.config.Modes.Readonly) {
UI.dropzone.style.display = "none"; state.ui.dropzone.style.display = "none";
} else { } else {
UI.dropzone.style.display = "block"; state.ui.dropzone.style.display = "block";
} }
if (AppConfig.Modes.Sinkhole) { if (state.config.Modes.Sinkhole) {
UI.fileList.style.display = "none"; state.ui.fileList.style.display = "none";
UI.sinkholeModeInfo.style.display = "block"; state.ui.sinkholeModeInfo.style.display = "block";
} else { } else {
UI.fileList.style.display = "block"; state.ui.fileList.style.display = "block";
UI.sinkholeModeInfo.style.display = "none"; state.ui.sinkholeModeInfo.style.display = "none";
} }
} }
@@ -353,7 +360,7 @@
} }
const file = files[currentIndex]; const file = files[currentIndex];
UI.currentFileName.textContent = file.name; state.ui.currentFileName.textContent = file.name;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
const form = new FormData(); const form = new FormData();
@@ -386,7 +393,7 @@
uploadNext(); uploadNext();
}); });
xhr.open("POST", AppConfig.Endpoints.Upload); xhr.open("POST", state.config.Endpoints.Upload);
xhr.send(form); xhr.send(form);
} }
@@ -396,7 +403,7 @@
function updateProgressUI(totalUploaded, totalSize, startTime) { function updateProgressUI(totalUploaded, totalSize, startTime) {
const percent = (totalUploaded / totalSize) * 100; const percent = (totalUploaded / totalSize) * 100;
UI.overallProgress.value = percent; state.ui.overallProgress.value = percent;
const elapsed = (Date.now() - startTime) / 1000; const elapsed = (Date.now() - startTime) / 1000;
const speed = totalUploaded / elapsed; const speed = totalUploaded / elapsed;
@@ -407,7 +414,7 @@
const min = Math.floor(etaSec / 60); const min = Math.floor(etaSec / 60);
const sec = Math.floor(etaSec % 60); const sec = Math.floor(etaSec % 60);
UI.overallStatus.textContent = state.ui.overallStatus.textContent =
`${percent.toFixed(1)}% (${(totalSize / 1024 / 1024).toFixed( `${percent.toFixed(1)}% (${(totalSize / 1024 / 1024).toFixed(
1 1
)} MB total) — ` + )} MB total) — ` +
@@ -423,7 +430,7 @@
showError("Invalid filename: .upload"); showError("Invalid filename: .upload");
return false; return false;
} }
if (safeName in Files) { if (safeName in state.files) {
showError("File already exists: " + f.name); showError("File already exists: " + f.name);
return false; return false;
} }