* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix * fix: track native source modules with root-anchored runtime ignores
130 lines
6.2 KiB
JavaScript
130 lines
6.2 KiB
JavaScript
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();
|