(() => { 'use strict'; const token = decodeURIComponent(location.pathname.split('/').filter(Boolean).pop() || ''); const cameraInput = document.getElementById('camera-input'); const libraryInput = document.getElementById('library-input'); const preview = document.getElementById('mobile-preview'); const uploadButton = document.getElementById('upload-btn'); const uploadPanel = document.getElementById('upload-panel'); const successPanel = document.getElementById('success-panel'); const errorPanel = document.getElementById('error-panel'); const status = document.getElementById('session-status'); const count = document.getElementById('photo-count'); const expiresCopy = document.getElementById('expires-copy'); const progress = document.getElementById('upload-progress'); const sourceButtons = [ document.getElementById('open-camera-btn'), document.getElementById('open-library-btn'), document.getElementById('add-camera-btn'), document.getElementById('add-library-btn'), document.getElementById('success-camera-btn'), document.getElementById('success-library-btn'), ].filter(Boolean); let selected = []; let serverFiles = []; let remaining = 10; function showError(message) { uploadPanel.classList.add('hidden'); successPanel.classList.add('hidden'); errorPanel.classList.remove('hidden'); document.getElementById('error-copy').textContent = message; status.className = 'session-status'; status.innerHTML = 'Upload nicht verfügbar'; } function openCamera() { if (remaining - selected.length <= 0) return; cameraInput.click(); } function openLibrary() { if (remaining - selected.length <= 0) return; libraryInput.click(); } function render() { preview.innerHTML = ''; selected.forEach((file, index) => { const figure = document.createElement('figure'); const image = document.createElement('img'); image.alt = file.name || `Foto ${index + 1}`; const objectUrl = URL.createObjectURL(file); image.src = objectUrl; image.addEventListener('load', () => URL.revokeObjectURL(objectUrl), { once: true }); image.addEventListener('error', () => { URL.revokeObjectURL(objectUrl); image.remove(); const fallback = document.createElement('div'); fallback.className = 'mobile-preview-fallback'; fallback.innerHTML = `Bild ${index + 1}${file.name || 'Foto ausgewählt'}`; figure.prepend(fallback); }, { once: true }); const remove = document.createElement('button'); remove.type = 'button'; remove.textContent = '×'; remove.setAttribute('aria-label', `${file.name || 'Bild'} entfernen`); remove.addEventListener('click', () => { selected.splice(index, 1); render(); }); figure.append(image, remove); preview.appendChild(figure); }); const total = serverFiles.length + selected.length; count.textContent = `${total} von 10 Bildern`; uploadButton.disabled = selected.length === 0; uploadButton.textContent = selected.length ? `${selected.length} ${selected.length === 1 ? 'Foto' : 'Fotos'} zu Vendoo senden` : 'Fotos zu Vendoo senden'; const noSlots = remaining - selected.length <= 0; sourceButtons.forEach(button => { button.disabled = noSlots; }); } function addFiles(files) { const candidates = [...files].filter(file => file.type.startsWith('image/') || /\.(heic|heif)$/i.test(file.name || '')); const slots = Math.max(0, remaining - selected.length); selected.push(...candidates.slice(0, slots)); render(); } async function loadSession() { try { const response = await fetch(`/mobile-upload-api/${encodeURIComponent(token)}`, { cache: 'no-store' }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Upload-Link konnte nicht geladen werden.'); serverFiles = data.files || []; remaining = data.remaining ?? Math.max(0, 10 - serverFiles.length); status.className = 'session-status ok'; status.innerHTML = 'Mit Vendoo verbunden'; uploadPanel.classList.remove('hidden'); const expires = new Date(String(data.expires_at).replace(' ', 'T') + (String(data.expires_at).includes('Z') ? '' : 'Z')); expiresCopy.textContent = Number.isNaN(expires.getTime()) ? '' : `gültig bis ${expires.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}`; render(); } catch (error) { showError(error.message); } } async function upload() { if (!selected.length) return; uploadButton.disabled = true; sourceButtons.forEach(button => { button.disabled = true; }); progress.classList.remove('hidden'); try { const form = new FormData(); selected.forEach(file => form.append('photos', file, file.name || `mobile-${Date.now()}.jpg`)); const response = await fetch(`/mobile-upload-api/${encodeURIComponent(token)}/photos?platform=mobile`, { method: 'POST', body: form, }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Upload fehlgeschlagen.'); serverFiles = data.files || serverFiles; remaining = data.remaining ?? Math.max(0, 10 - serverFiles.length); selected = []; render(); uploadPanel.classList.add('hidden'); successPanel.classList.remove('hidden'); } catch (error) { showError(error.message); } finally { progress.classList.add('hidden'); uploadButton.disabled = false; sourceButtons.forEach(button => { button.disabled = false; }); } } function returnToUploadAndOpen(opener) { successPanel.classList.add('hidden'); uploadPanel.classList.remove('hidden'); window.setTimeout(opener, 50); } cameraInput.addEventListener('change', () => { addFiles(cameraInput.files || []); cameraInput.value = ''; }); libraryInput.addEventListener('change', () => { addFiles(libraryInput.files || []); libraryInput.value = ''; }); document.getElementById('open-camera-btn').addEventListener('click', openCamera); document.getElementById('open-library-btn').addEventListener('click', openLibrary); document.getElementById('add-camera-btn').addEventListener('click', openCamera); document.getElementById('add-library-btn').addEventListener('click', openLibrary); document.getElementById('success-camera-btn').addEventListener('click', () => returnToUploadAndOpen(openCamera)); document.getElementById('success-library-btn').addEventListener('click', () => returnToUploadAndOpen(openLibrary)); uploadButton.addEventListener('click', upload); loadSession(); })();