262 lines
9.1 KiB
JavaScript
262 lines
9.1 KiB
JavaScript
const ORIGINAL_MARKETPLACE_URL = 'https://git.waifuboard.app/ItsSkaiya/WaifuBoard-Extensions/raw/branch/main/marketplace.json';
|
|
const MARKETPLACE_JSON_URL = `/api/proxy?url=${encodeURIComponent(ORIGINAL_MARKETPLACE_URL)}`;
|
|
const INSTALLED_EXTENSIONS_API = '/api/extensions';
|
|
const UPDATE_EXTENSIONS_API = '/api/extensions/update';
|
|
|
|
const marketplaceContent = document.getElementById('marketplace-content');
|
|
const filterSelect = document.getElementById('extension-filter');
|
|
const updateAllBtn = document.getElementById('btn-update-all');
|
|
|
|
const modal2 = document.getElementById('customModal');
|
|
const modalTitle = document.getElementById('modalTitle');
|
|
const modalMessage = document.getElementById('modalMessage');
|
|
const modalConfirmBtn = document.getElementById('modalConfirmButton');
|
|
const modalCloseBtn = document.getElementById('modalCloseButton');
|
|
|
|
let marketplaceMetadata = {};
|
|
let installedExtensions = [];
|
|
let currentTab = 'marketplace';
|
|
|
|
async function loadMarketplace() {
|
|
showSkeletons();
|
|
try {
|
|
const [metaRes, installedRes] = await Promise.all([
|
|
fetch(MARKETPLACE_JSON_URL).then(res => res.json()),
|
|
fetch(INSTALLED_EXTENSIONS_API).then(res => res.json())
|
|
]);
|
|
|
|
marketplaceMetadata = metaRes.extensions;
|
|
installedExtensions = (installedRes.extensions || []).map(e => e.toLowerCase());
|
|
|
|
initTabs();
|
|
renderGroupedView();
|
|
|
|
if (filterSelect) {
|
|
filterSelect.addEventListener('change', () => renderGroupedView());
|
|
}
|
|
|
|
if (updateAllBtn) {
|
|
updateAllBtn.onclick = handleUpdateAll;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading marketplace:', error);
|
|
marketplaceContent.innerHTML = `<div class="error-msg">Error al cargar el marketplace.</div>`;
|
|
}
|
|
}
|
|
|
|
function initTabs() {
|
|
const tabs = document.querySelectorAll('.tab-button');
|
|
tabs.forEach(tab => {
|
|
tab.onclick = () => {
|
|
tabs.forEach(t => t.classList.remove('active'));
|
|
tab.classList.add('active');
|
|
currentTab = tab.dataset.tab;
|
|
|
|
if (updateAllBtn) {
|
|
if (currentTab === 'installed') {
|
|
updateAllBtn.classList.remove('hidden');
|
|
} else {
|
|
updateAllBtn.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
renderGroupedView();
|
|
};
|
|
});
|
|
}
|
|
|
|
async function handleUpdateAll() {
|
|
const originalText = updateAllBtn.innerText;
|
|
try {
|
|
updateAllBtn.disabled = true;
|
|
updateAllBtn.innerText = 'Updating...';
|
|
|
|
const res = await fetch(UPDATE_EXTENSIONS_API, { method: 'POST' });
|
|
if (!res.ok) throw new Error('Update failed');
|
|
|
|
const data = await res.json();
|
|
|
|
if (data.updated && data.updated.length > 0) {
|
|
|
|
const list = data.updated.join(', ');
|
|
window.NotificationUtils.success(`Updated: ${list}`);
|
|
|
|
await loadMarketplace();
|
|
} else {
|
|
window.NotificationUtils.info('Everything is up to date.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Update All Error:', error);
|
|
window.NotificationUtils.error('Failed to perform bulk update.');
|
|
} finally {
|
|
updateAllBtn.disabled = false;
|
|
updateAllBtn.innerText = originalText;
|
|
}
|
|
}
|
|
|
|
function renderGroupedView() {
|
|
marketplaceContent.innerHTML = '';
|
|
const activeFilter = filterSelect.value;
|
|
const groups = {};
|
|
|
|
let listToRender = [];
|
|
|
|
if (currentTab === 'marketplace') {
|
|
for (const [id, data] of Object.entries(marketplaceMetadata)) {
|
|
listToRender.push({
|
|
id,
|
|
...data,
|
|
isInstalled: installedExtensions.includes(id.toLowerCase())
|
|
});
|
|
}
|
|
} else {
|
|
for (const [id, data] of Object.entries(marketplaceMetadata)) {
|
|
if (installedExtensions.includes(id.toLowerCase())) {
|
|
listToRender.push({ id, ...data, isInstalled: true });
|
|
}
|
|
}
|
|
|
|
installedExtensions.forEach(id => {
|
|
const existsInMeta = Object.keys(marketplaceMetadata).some(k => k.toLowerCase() === id);
|
|
if (!existsInMeta) {
|
|
listToRender.push({
|
|
id: id,
|
|
name: id.charAt(0).toUpperCase() + id.slice(1),
|
|
type: 'Local',
|
|
author: 'Unknown',
|
|
isInstalled: true
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
listToRender.forEach(ext => {
|
|
const type = ext.type || 'Other';
|
|
if (activeFilter !== 'All' && type !== activeFilter) return;
|
|
if (!groups[type]) groups[type] = [];
|
|
groups[type].push(ext);
|
|
});
|
|
|
|
const sortedTypes = Object.keys(groups).sort();
|
|
|
|
if (sortedTypes.length === 0) {
|
|
marketplaceContent.innerHTML = `<p class="empty-msg">No extensions found for this criteria.</p>`;
|
|
return;
|
|
}
|
|
|
|
sortedTypes.forEach(type => {
|
|
const section = document.createElement('div');
|
|
section.className = 'category-group';
|
|
|
|
const title = document.createElement('h2');
|
|
title.className = 'marketplace-section-title';
|
|
title.innerText = type.replace('-', ' ');
|
|
|
|
const grid = document.createElement('div');
|
|
grid.className = 'marketplace-grid';
|
|
|
|
groups[type].forEach(ext => grid.appendChild(createCard(ext)));
|
|
|
|
section.appendChild(title);
|
|
section.appendChild(grid);
|
|
marketplaceContent.appendChild(section);
|
|
});
|
|
}
|
|
|
|
function createCard(ext) {
|
|
const card = document.createElement('div');
|
|
card.className = `extension-card ${ext.nsfw ? 'nsfw-ext' : ''} ${ext.broken ? 'broken-ext' : ''}`;
|
|
|
|
const iconUrl = `https://www.google.com/s2/favicons?domain=${ext.domain}&sz=128`;
|
|
|
|
let buttonHtml = '';
|
|
if (ext.isInstalled) {
|
|
buttonHtml = `<button class="extension-action-button btn-uninstall">Uninstall</button>`;
|
|
} else if (ext.broken) {
|
|
buttonHtml = `<button class="extension-action-button" style="background: #4b5563; cursor: not-allowed;" disabled>Broken</button>`;
|
|
} else {
|
|
buttonHtml = `<button class="extension-action-button btn-install">Install</button>`;
|
|
}
|
|
|
|
card.innerHTML = `
|
|
<img class="extension-icon" src="${iconUrl}" onerror="this.src='/public/assets/waifuboards.ico'">
|
|
<div class="card-content-wrapper">
|
|
<h3 class="extension-name">${ext.name}</h3>
|
|
<span class="extension-author">by ${ext.author || 'Unknown'}</span>
|
|
<p class="extension-description">${ext.description || 'No description available.'}</p>
|
|
<div class="extension-tags">
|
|
<span class="extension-status-badge badge-${ext.isInstalled ? 'installed' : (ext.broken ? 'local' : 'available')}">
|
|
${ext.isInstalled ? 'Installed' : (ext.broken ? 'Broken' : 'Available')}
|
|
</span>
|
|
${ext.nsfw ? '<span class="extension-status-badge badge-local">NSFW</span>' : ''}
|
|
</div>
|
|
</div>
|
|
${buttonHtml}
|
|
`;
|
|
|
|
const btn = card.querySelector('.extension-action-button');
|
|
if (!ext.broken || ext.isInstalled) {
|
|
btn.onclick = () => ext.isInstalled ? promptUninstall(ext) : handleInstall(ext);
|
|
}
|
|
|
|
return card;
|
|
}
|
|
|
|
function showModal(title, message, showConfirm = false, onConfirm = null) {
|
|
modalTitle.innerText = title;
|
|
modalMessage.innerText = message;
|
|
if (showConfirm) {
|
|
modalConfirmBtn.classList.remove('hidden');
|
|
modalConfirmBtn.onclick = () => { hideModal(); if (onConfirm) onConfirm(); };
|
|
} else {
|
|
modalConfirmBtn.classList.add('hidden');
|
|
}
|
|
modalCloseBtn.onclick = hideModal;
|
|
modal2.classList.remove('hidden');
|
|
}
|
|
|
|
function hideModal() { modal2.classList.add('hidden'); }
|
|
|
|
async function handleInstall(ext) {
|
|
try {
|
|
const res = await fetch('/api/extensions/install', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ url: ext.entry })
|
|
});
|
|
if (res.ok) {
|
|
installedExtensions.push(ext.id.toLowerCase());
|
|
renderGroupedView();
|
|
window.NotificationUtils.success(`${ext.name} installed!`);
|
|
}
|
|
} catch (e) { window.NotificationUtils.error('Install failed.'); }
|
|
}
|
|
|
|
function promptUninstall(ext) {
|
|
showModal('Confirm', `Uninstall ${ext.name}?`, true, () => handleUninstall(ext));
|
|
}
|
|
|
|
async function handleUninstall(ext) {
|
|
try {
|
|
const res = await fetch('/api/extensions/uninstall', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ fileName: ext.id + '.js' })
|
|
});
|
|
if (res.ok) {
|
|
installedExtensions = installedExtensions.filter(id => id !== ext.id.toLowerCase());
|
|
renderGroupedView();
|
|
window.NotificationUtils.info(`${ext.name} uninstalled.`);
|
|
}
|
|
} catch (e) { window.NotificationUtils.error('Uninstall failed.'); }
|
|
}
|
|
|
|
function showSkeletons() {
|
|
marketplaceContent.innerHTML = `
|
|
<div class="marketplace-grid">
|
|
${Array(3).fill('<div class="extension-card skeleton"></div>').join('')}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', loadMarketplace); |