116 lines
4.5 KiB
JavaScript
116 lines
4.5 KiB
JavaScript
// ── FTP Browser ───────────────────────────────────────────────────────────────
|
|
|
|
let ftpBrowserResolve = null;
|
|
let ftpBrowserCwd = '/';
|
|
|
|
function openFtpBrowser(startPath = '/') {
|
|
return new Promise(resolve => {
|
|
ftpBrowserResolve = resolve;
|
|
openAccessibleDialog($('ftp-browser-overlay'), $('ftp-browser-close'));
|
|
ftpBrowserNavigate(startPath);
|
|
});
|
|
}
|
|
|
|
function closeFtpBrowser(result) {
|
|
closeAccessibleDialog($('ftp-browser-overlay'));
|
|
if (ftpBrowserResolve) { ftpBrowserResolve(result); ftpBrowserResolve = null; }
|
|
}
|
|
|
|
async function ftpBrowserNavigate(path) {
|
|
ftpBrowserCwd = path;
|
|
$('ftp-browser-cwd').textContent = path;
|
|
$('ftp-browser-up').disabled = (path === '/');
|
|
const body = $('ftp-browser-body');
|
|
body.innerHTML = '<div class="ftp-browser-loading">Loading…</div>';
|
|
try {
|
|
const entries = await window.pork.ftpListDir(path);
|
|
if (!entries.length) {
|
|
body.innerHTML = '<div class="ftp-browser-loading">Empty folder.</div>';
|
|
return;
|
|
}
|
|
body.innerHTML = entries.map(e => `
|
|
<div class="ftp-browser-entry ${e.isDir ? 'ftp-entry-dir' : 'ftp-entry-file'}"
|
|
data-name="${escHtml(e.name)}" data-isdir="${e.isDir}">
|
|
<span class="ftp-entry-icon">${e.isDir ? '📁' : '📄'}</span>
|
|
<span class="ftp-entry-name">${escHtml(e.name)}</span>
|
|
</div>
|
|
`).join('');
|
|
} catch (e) {
|
|
body.innerHTML = `<div class="ftp-browser-loading" style="color:var(--red)">Error: ${escHtml(e.message)}</div>`;
|
|
}
|
|
}
|
|
|
|
$('ftp-browser-body').addEventListener('click', e => {
|
|
const entry = e.target.closest('.ftp-entry-dir');
|
|
if (!entry) return;
|
|
const name = entry.dataset.name;
|
|
const next = (ftpBrowserCwd.endsWith('/') ? ftpBrowserCwd : ftpBrowserCwd + '/') + name;
|
|
ftpBrowserNavigate(next);
|
|
});
|
|
|
|
$('ftp-browser-up').addEventListener('click', () => {
|
|
const parts = ftpBrowserCwd.replace(/\/$/, '').split('/').filter(Boolean);
|
|
parts.pop();
|
|
ftpBrowserNavigate('/' + parts.join('/') || '/');
|
|
});
|
|
|
|
$('ftp-browser-select').addEventListener('click', () => closeFtpBrowser(ftpBrowserCwd));
|
|
$('ftp-browser-cancel').addEventListener('click', () => closeFtpBrowser(null));
|
|
$('ftp-browser-close').addEventListener('click', () => closeFtpBrowser(null));
|
|
$('ftp-browser-overlay').addEventListener('keydown', event => {
|
|
if (event.key === 'Escape') closeFtpBrowser(null);
|
|
});
|
|
|
|
$('btn-browse-ftp').addEventListener('click', async () => {
|
|
if (!state.connected) { setStatus('Connect to FTP first to browse remote paths', 'error'); return; }
|
|
const selected = await openFtpBrowser(ftpBrowserCwd || '/');
|
|
if (!selected) return;
|
|
// Auto-add immediately — no separate "+ Add" click required
|
|
if (remoteGamePaths.some(p => p.path === selected)) {
|
|
setStatus('That path is already in the list', 'ok');
|
|
return;
|
|
}
|
|
const label = $('new-remote-label').value.trim()
|
|
|| selected.replace(/\/$/, '').split('/').filter(Boolean).pop()
|
|
|| selected;
|
|
remoteGamePaths = [...remoteGamePaths, { label, path: selected }];
|
|
await saveRemoteGamePaths();
|
|
renderRemoteGamePaths();
|
|
$('new-remote-label').value = '';
|
|
$('new-remote-path').value = '';
|
|
});
|
|
|
|
// Add payload source
|
|
$('btn-add-payload-source').addEventListener('click', async () => {
|
|
const name = $('new-source-name').value.trim();
|
|
const url = $('new-source-url').value.trim();
|
|
if (!name || !url) { setStatus('Enter both a name and a source URL', 'error'); return; }
|
|
try {
|
|
await window.pork.payloadSourcesAdd(name, url);
|
|
$('new-source-name').value = '';
|
|
$('new-source-url').value = '';
|
|
await renderSettingsPayloadSources();
|
|
setStatus(`Source "${name}" added`, 'ok');
|
|
} catch (e) {
|
|
setStatus(`Failed to add source: ${e.message}`, 'error');
|
|
}
|
|
});
|
|
|
|
// Delegated clicks in settings payload source list
|
|
$('settings-payload-sources').addEventListener('click', async e => {
|
|
const row = e.target.closest('[data-source-id]');
|
|
if (!row) return;
|
|
const id = Number(row.dataset.sourceId);
|
|
|
|
if (e.target.closest('.btn-source-remove')) {
|
|
const confirmation = await showExactConfirmation('Remove this payload source? Downloaded files will not be deleted.', 'VERWIJDER');
|
|
if (!confirmation) return;
|
|
await window.pork.payloadSourcesRemove(id, confirmation);
|
|
await renderSettingsPayloadSources();
|
|
} else if (e.target.closest('.source-toggle')) {
|
|
await window.pork.payloadSourcesToggle(id);
|
|
await renderSettingsPayloadSources();
|
|
}
|
|
});
|
|
|