diff --git a/portal/templates/portal/shared_space/base.html b/portal/templates/portal/shared_space/base.html index 5faf136..7beb1d1 100644 --- a/portal/templates/portal/shared_space/base.html +++ b/portal/templates/portal/shared_space/base.html @@ -161,6 +161,18 @@ {% block content %}{% endblock %} + {% block extra_js %}{% endblock %} diff --git a/portal/templates/portal/shared_space/home.html b/portal/templates/portal/shared_space/home.html index fff59bb..dfbb2df 100644 --- a/portal/templates/portal/shared_space/home.html +++ b/portal/templates/portal/shared_space/home.html @@ -404,9 +404,10 @@ async function uploadFile(file, action) { // Store blob in IndexedDB so service worker can access it await dbPut('uploads', { id, blob: file, filename: file.name }); - // Tell service worker to upload - if (navigator.serviceWorker.controller) { - navigator.serviceWorker.controller.postMessage({ + try { + // Wait for SW controller if not ready yet (first visit) + const ctrl = await getSWController(); + ctrl.postMessage({ type: 'START_UPLOAD', id, uploadUrl: UPLOAD_URL, @@ -414,12 +415,47 @@ async function uploadFile(file, action) { filename: file.name, action: action || null, }); - } else { - // Fallback: no SW controller yet, upload directly - showError(item, file.name, 'Service worker not ready — please refresh and try again'); + } catch (e) { + // SW not available — fall back to direct upload from page + directUpload(item, id, file, action); } } +async function directUpload(item, id, file, action) { + try { + const fd = new FormData(); + fd.append('file', file, file.name); + fd.append('space', SPACE); + if (action) fd.append('action', action); + const resp = await fetch(UPLOAD_URL, { method: 'POST', body: fd }); + const result = await resp.json(); + await dbDelete('uploads', id); + if (resp.status === 409 && result.duplicate) { + showDuplicate(item, id, result, file.name); + } else if (resp.ok && result.success) { + showSuccess(item, result); + } else { + showError(item, file.name, result.error || 'Upload failed'); + } + } catch (err) { + showError(item, file.name, err.message); + } +} + +function getSWController() { + return new Promise((resolve, reject) => { + if (navigator.serviceWorker.controller) { + return resolve(navigator.serviceWorker.controller); + } + // Wait for the SW to claim this page (happens after first install) + const timeout = setTimeout(() => reject(new Error('SW timeout')), 10000); + navigator.serviceWorker.addEventListener('controllerchange', () => { + clearTimeout(timeout); + resolve(navigator.serviceWorker.controller); + }, { once: true }); + }); +} + // --- Listen for messages from service worker --- navigator.serviceWorker.addEventListener('message', (event) => { @@ -468,9 +504,9 @@ navigator.serviceWorker.addEventListener('message', (event) => { }); // On page load, ask SW for any completed results from background uploads -if (navigator.serviceWorker.controller) { - navigator.serviceWorker.controller.postMessage({ type: 'GET_RESULTS' }); -} +getSWController().then(ctrl => { + ctrl.postMessage({ type: 'GET_RESULTS' }); +}).catch(() => {}); function copyLink(btn, url) { navigator.clipboard.writeText(url).then(() => {