const browserApi = globalThis.browser || null; const chromeApi = globalThis.chrome || null; const $ = selector => document.querySelector(selector); const state = { queue: [], current: 0, serverUrl: 'http://localhost:8124' }; function sendMessage(message) { if (browserApi) return browserApi.runtime.sendMessage(message); return new Promise((resolve, reject) => { chromeApi.runtime.sendMessage(message, response => { const error = chromeApi.runtime.lastError; if (error) reject(new Error(error.message)); else resolve(response); }); }); } function storageGet(keys) { if (browserApi) return browserApi.storage.local.get(keys); return new Promise(resolve => chromeApi.storage.local.get(keys, resolve)); } function storageSet(values) { if (browserApi) return browserApi.storage.local.set(values); return new Promise(resolve => chromeApi.storage.local.set(values, resolve)); } function openTab(url) { if (browserApi) return browserApi.tabs.create({ url }); return chromeApi.tabs.create({ url }); } async function ensureServerPermission(url) { const parsed = new URL(url); const origin = `${parsed.protocol}//${parsed.host}/*`; const permissions = browserApi?.permissions || chromeApi?.permissions; if (!permissions) return true; if (browserApi) { if (await permissions.contains({ origins: [origin] })) return true; return permissions.request({ origins: [origin] }); } return new Promise(resolve => permissions.contains({ origins: [origin] }, has => has ? resolve(true) : permissions.request({ origins: [origin] }, resolve))); } async function api(path, method = 'GET', body) { const response = await sendMessage({ type: 'api', serverUrl: state.serverUrl, path, method, body }); if (!response?.ok) throw new Error(response?.data?.error || response?.error || 'Server nicht erreichbar'); return response.data; } function setConnection(ok, text) { const chip = $('#connection-chip'); chip.className = `connection-chip ${ok ? 'ok' : 'error'}`; chip.querySelector('span').textContent = text; } async function imageData(filename) { if (!filename) return ''; const url = `${state.serverUrl}/uploads/${String(filename).split('/').map(encodeURIComponent).join('/')}`; const response = await sendMessage({ type: 'fetch-image', url }); return response?.ok ? response.dataUrl : ''; } function formatDate(value) { const date = new Date(String(value || '').replace(' ', 'T') + 'Z'); return Number.isNaN(date.getTime()) ? '–' : date.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); } async function renderCurrent() { const listing = state.queue[state.current]; $('#listing-card').classList.toggle('hidden', !listing); $('#empty-card').classList.toggle('hidden', !!listing); if (!listing) return; $('#listing-title').textContent = listing.title || 'Ohne Titel'; $('#listing-meta').textContent = [listing.size, listing.color, listing.condition].filter(Boolean).join(' · ') || 'Noch keine Eigenschaften'; $('#listing-sku').textContent = listing.sku || `VD-${String(listing.id).padStart(4, '0')}`; $('#listing-price').textContent = listing.price ? `${Number(listing.price).toFixed(2).replace('.', ',')} €` : '–'; $('#listing-created').textContent = formatDate(listing.created_at); const src = await imageData(listing.photos?.[0]); const image = $('#listing-image'); if (src) { image.src = src; image.classList.remove('hidden'); } else { image.removeAttribute('src'); image.classList.add('hidden'); } } async function loadQueue() { try { const data = await api('/api/extension/queue?platform=vinted'); state.queue = data.queue || []; state.current = Math.min(state.current, Math.max(0, state.queue.length - 1)); setConnection(true, 'Verbunden'); $('#queue-state').textContent = state.queue.length ? 'Bereit zur Übertragung' : 'Queue leer'; $('#queue-count').textContent = `${state.queue.length} / ${data.total || state.queue.length}`; $('#queue-progress-fill').style.width = `${data.total ? ((data.total - state.queue.length) / data.total) * 100 : 0}%`; await renderCurrent(); } catch (error) { setConnection(false, 'Offline'); $('#queue-state').textContent = error.message; $('#listing-card').classList.add('hidden'); $('#empty-card').classList.remove('hidden'); } } async function init() { const settings = await storageGet(['serverUrl', 'provider', 'language']); state.serverUrl = String(settings.serverUrl || 'http://localhost:8124').replace(/\/$/, ''); $('#server-url').value = state.serverUrl; if (settings.provider) $('#provider').value = settings.provider; if (settings.language) $('#language').value = settings.language; await loadQueue(); } function openUrl(url) { openTab(url); } $('#settings-toggle').addEventListener('click', () => { $('#dashboard-view').classList.add('hidden'); $('#settings-view').classList.remove('hidden'); }); $('#back-dashboard').addEventListener('click', () => { $('#settings-view').classList.add('hidden'); $('#dashboard-view').classList.remove('hidden'); }); $('#save-settings').addEventListener('click', async () => { state.serverUrl = $('#server-url').value.trim().replace(/\/$/, ''); if (!await ensureServerPermission(state.serverUrl)) throw new Error('Zugriff auf die Serveradresse wurde nicht erlaubt.'); await storageSet({ serverUrl: state.serverUrl, provider: $('#provider').value, language: $('#language').value }); $('#settings-view').classList.add('hidden'); $('#dashboard-view').classList.remove('hidden'); await loadQueue(); }); $('#next-listing').addEventListener('click', async () => { if (state.queue.length) state.current = (state.current + 1) % state.queue.length; await renderCurrent(); }); $('#fill-listing').addEventListener('click', async () => { const listing = state.queue[state.current]; if (!listing) return; await storageSet({ pendingFillListingId: listing.id }); openUrl('https://www.vinted.de/items/new'); window.close(); }); $('#prepare-images').addEventListener('click', () => openUrl(`${state.serverUrl}/#publish`)); $('#open-queue').addEventListener('click', () => openUrl(`${state.serverUrl}/#publish`)); $('#open-app').addEventListener('click', () => openUrl(state.serverUrl)); $('#empty-open-app').addEventListener('click', () => openUrl(state.serverUrl)); init();