video_press/static/js/modules/state.js

119 lines
4 KiB
JavaScript

/**
* state.js
* --------
* Single shared application state object and all DOM element references.
*
* Centralising these here means every module imports the same live object —
* mutations made in one module are immediately visible to all others without
* any event bus or pub/sub layer.
*
* Also exports announce(), which every module uses to push messages to the
* ARIA live region for screen-reader users.
*/
// ─── Shared mutable state ────────────────────────────────────────────────────
export const state = {
/** Files returned by the last /api/scan call. */
scannedFiles: [],
/** Set of file paths the user has checked for compression. */
selectedPaths: new Set(),
/** job_id of the currently active or most-recently-seen compression job. */
currentJobId: null,
/** Active EventSource for the SSE progress stream. */
eventSource: null,
/** Per-file result objects accumulated during a compression run. */
compressionResults: [],
/** Current path shown in the server-side directory browser modal. */
browserPath: '/',
/**
* Index of the last SSE event we have processed.
* Passed as ?from=N when reconnecting so the server skips events
* we already applied to the UI.
*/
seenEventCount: 0,
/** Handle returned by setTimeout for the auto-reconnect retry. */
reconnectTimer: null,
};
// ─── DOM element references ───────────────────────────────────────────────────
const $ = id => document.getElementById(id);
export const els = {
// Step 1 — Configure source
dirInput: $('dir-input'),
browseBtn: $('browse-btn'),
minSizeInput: $('min-size-input'),
suffixInput: $('suffix-input'),
scanBtn: $('scan-btn'),
scanStatus: $('scan-status'),
// Directory browser modal
browserModal: $('browser-modal'),
browserList: $('browser-list'),
browserPath: $('browser-current-path'),
closeBrowser: $('close-browser'),
browserCancel: $('browser-cancel'),
browserSelect: $('browser-select'),
// Step 2 — File selection
sectionFiles: $('section-files'),
selectAllBtn: $('select-all-btn'),
deselectAllBtn: $('deselect-all-btn'),
selectionSummary: $('selection-summary'),
fileTbody: $('file-tbody'),
compressBtn: $('compress-btn'),
// Email notification opt-in
notifyChk: $('notify-chk'),
notifyEmailRow: $('notify-email-row'),
notifyEmail: $('notify-email'),
// Step 3 — Compression progress
sectionProgress: $('section-progress'),
progTotal: $('prog-total'),
progDone: $('prog-done'),
progStatus: $('prog-status'),
overallBar: $('overall-bar'),
overallBarFill: $('overall-bar-fill'),
overallPct: $('overall-pct'),
fileProgressList: $('file-progress-list'),
cancelBtn: $('cancel-btn'),
notifyStatus: $('notify-status'),
reconnectBtn: $('reconnect-btn'),
reconnectBtnBanner: $('reconnect-btn-banner'),
streamLostBanner: $('stream-lost-banner'),
// Step 4 — Results
sectionResults: $('section-results'),
resultsContent: $('results-content'),
restartBtn: $('restart-btn'),
// Header
themeToggle: $('theme-toggle'),
themeIcon: $('theme-icon'),
settingsBtn: $('settings-btn'),
// Accessibility live region
srAnnounce: $('sr-announce'),
};
// ─── Screen-reader announcements ─────────────────────────────────────────────
/**
* Push a message to the ARIA assertive live region.
* Clears first so repeated identical messages are still announced.
* @param {string} msg
*/
export function announce(msg) {
els.srAnnounce.textContent = '';
requestAnimationFrame(() => {
els.srAnnounce.textContent = msg;
});
}