Upload via GUI (28 Dateien)
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
// renderer/settings-extra.js
|
||||
// Ergänzungen der Einstellungen: App-Einstellungen (Identität, Uploads, Design, System),
|
||||
// ein gemeinsames "Speichern" für alle Bereiche, Warnung bei ungespeicherten Änderungen,
|
||||
// mehrere Gitea-Server, Token anzeigen/entfernen und Diagnose-Hilfen.
|
||||
// Wird nach renderer.js und features.js geladen und nutzt deren globale Helfer.
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
let savedPrefs = null;
|
||||
|
||||
/* ---------- Design ---------- */
|
||||
const systemDark = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
||||
|
||||
function applyTheme(theme) {
|
||||
const effective = theme === 'system' ? (systemDark && !systemDark.matches ? 'light' : 'dark') : theme;
|
||||
document.documentElement.dataset.theme = effective === 'light' ? 'light' : 'dark';
|
||||
}
|
||||
if (systemDark) {
|
||||
systemDark.addEventListener('change', () => { if (savedPrefs && savedPrefs.theme === 'system') applyTheme('system'); });
|
||||
}
|
||||
|
||||
/* ---------- Laden ---------- */
|
||||
async function loadPrefs() {
|
||||
const res = await window.electronAPI.prefsGet();
|
||||
if (!res || !res.ok) return null;
|
||||
savedPrefs = res.prefs;
|
||||
applyTheme(savedPrefs.theme);
|
||||
return savedPrefs;
|
||||
}
|
||||
|
||||
function fillPrefFields(p) {
|
||||
if (!p) return;
|
||||
const set = (id, v) => { const el = $(id); if (!el) return; if (el.type === 'checkbox') el.checked = !!v; else el.value = v; };
|
||||
set('prefGitName', p.gitName);
|
||||
set('prefGitEmail', p.gitEmail);
|
||||
set('prefTheme', p.theme);
|
||||
set('prefZoom', String(p.zoom));
|
||||
if ($('prefZoom') && !$('prefZoom').value) $('prefZoom').value = '100';
|
||||
set('prefAutoUpdate', p.autoUpdateCheck);
|
||||
set('prefCloseAction', p.closeAction);
|
||||
set('prefCloneDir', p.defaultCloneDir);
|
||||
set('prefSecretScan', p.uploadSecretScan);
|
||||
set('prefUploadPreview', p.uploadPreview);
|
||||
set('prefUploadExcludes', p.uploadExtraExcludes);
|
||||
}
|
||||
|
||||
function collectPrefs() {
|
||||
const val = (id) => ($(id) ? $(id).value : undefined);
|
||||
const chk = (id) => ($(id) ? $(id).checked : undefined);
|
||||
return {
|
||||
gitName: val('prefGitName'),
|
||||
gitEmail: val('prefGitEmail'),
|
||||
theme: val('prefTheme'),
|
||||
zoom: Number(val('prefZoom')),
|
||||
autoUpdateCheck: chk('prefAutoUpdate'),
|
||||
closeAction: val('prefCloseAction'),
|
||||
defaultCloneDir: val('prefCloneDir'),
|
||||
uploadSecretScan: chk('prefSecretScan'),
|
||||
uploadPreview: val('prefUploadPreview'),
|
||||
uploadExtraExcludes: val('prefUploadExcludes')
|
||||
};
|
||||
}
|
||||
|
||||
/* ---------- Ungespeicherte Änderungen ---------- */
|
||||
let snapshot = null;
|
||||
|
||||
function readFormState() {
|
||||
const state = {};
|
||||
document.querySelectorAll('#settingsModal input, #settingsModal select, #settingsModal textarea').forEach(el => {
|
||||
if (!el.id || el.type === 'file') return;
|
||||
state[el.id] = el.type === 'checkbox' ? el.checked : el.value;
|
||||
});
|
||||
return JSON.stringify(state);
|
||||
}
|
||||
|
||||
async function takeSnapshot() {
|
||||
// Erst nachdem alle Felder befüllt sind (Zugangsdaten, App-Einstellungen, Backup)
|
||||
fillPrefFields(await loadPrefs());
|
||||
if (window.gmBackupSettings) await window.gmBackupSettings.load();
|
||||
await loadProfiles();
|
||||
await refreshTransportState();
|
||||
snapshot = readFormState();
|
||||
}
|
||||
|
||||
// Wird vom "Abbrechen"-Knopf in renderer.js gefragt
|
||||
window.settingsCanClose = async function () {
|
||||
if (!snapshot || readFormState() === snapshot) return true;
|
||||
const discard = await showActionConfirmModal({
|
||||
title: 'Ungespeicherte Änderungen',
|
||||
message: 'Du hast Einstellungen geändert, aber nicht gespeichert. Änderungen verwerfen?',
|
||||
confirmText: 'Verwerfen',
|
||||
danger: true
|
||||
});
|
||||
if (discard && savedPrefs) applyTheme(savedPrefs.theme); // Vorschau zurücknehmen
|
||||
return discard;
|
||||
};
|
||||
|
||||
/* ---------- Gemeinsames Speichern ---------- */
|
||||
async function saveExtras() {
|
||||
const problems = [];
|
||||
const res = await window.electronAPI.prefsSet(collectPrefs());
|
||||
if (res && res.prefs) { savedPrefs = res.prefs; applyTheme(savedPrefs.theme); fillPrefFields(savedPrefs); }
|
||||
if (!res || !res.ok) problems.push(res && res.error ? res.error : 'App-Einstellungen nicht gespeichert');
|
||||
if (window.gmBackupSettings) {
|
||||
const b = await window.gmBackupSettings.save(true);
|
||||
if (!b || !b.ok) problems.push('Backup-Einstellung nicht gespeichert' + (b && b.error ? ': ' + b.error : ''));
|
||||
}
|
||||
snapshot = null;
|
||||
if (problems.length) showWarning(problems.join(' · '));
|
||||
}
|
||||
|
||||
/* ---------- Mehrere Gitea-Server ---------- */
|
||||
async function loadProfiles() {
|
||||
const sel = $('giteaProfileSelect');
|
||||
if (!sel) return;
|
||||
const creds = (await window.electronAPI.loadCredentials()) || {};
|
||||
const profiles = Array.isArray(creds.giteaProfiles) ? creds.giteaProfiles : [];
|
||||
sel.textContent = '';
|
||||
if (!profiles.length) {
|
||||
sel.append(new Option('Noch keine Profile gespeichert', ''));
|
||||
sel.disabled = true;
|
||||
} else {
|
||||
sel.disabled = false;
|
||||
for (const p of profiles) {
|
||||
const active = p.url === creds.giteaURL && p.token === creds.giteaToken;
|
||||
sel.append(new Option(`${p.name} – ${p.url}${active ? ' (aktiv)' : ''}`, p.name));
|
||||
}
|
||||
}
|
||||
$('btnGiteaProfileUse').disabled = $('btnGiteaProfileDelete').disabled = !profiles.length;
|
||||
}
|
||||
|
||||
async function saveProfile() {
|
||||
const name = $('giteaProfileName').value.trim();
|
||||
const url = $('giteaURL').value.trim();
|
||||
const token = $('giteaToken').value.trim();
|
||||
if (!name) { showWarning('Bitte einen Namen für das Profil eingeben.'); $('giteaProfileName').focus(); return; }
|
||||
if (!url || !token) { showWarning('Bitte zuerst Gitea-URL und Token unter „Zugangsdaten“ eintragen.'); return; }
|
||||
const creds = (await window.electronAPI.loadCredentials()) || {};
|
||||
const profiles = (Array.isArray(creds.giteaProfiles) ? creds.giteaProfiles : []).filter(p => p.name !== name);
|
||||
profiles.push({ name, url, token });
|
||||
profiles.sort((a, b) => a.name.localeCompare(b.name, 'de'));
|
||||
await window.electronAPI.saveCredentials({ ...creds, giteaProfiles: profiles });
|
||||
$('giteaProfileName').value = '';
|
||||
await loadProfiles();
|
||||
snapshot = readFormState();
|
||||
showSuccess(`Profil „${name}“ gespeichert`);
|
||||
}
|
||||
|
||||
async function useProfile() {
|
||||
const name = $('giteaProfileSelect').value;
|
||||
const creds = (await window.electronAPI.loadCredentials()) || {};
|
||||
const p = (creds.giteaProfiles || []).find(x => x.name === name);
|
||||
if (!p) return;
|
||||
$('giteaURL').value = p.url;
|
||||
$('giteaToken').value = p.token;
|
||||
// Wie ein normales "Speichern": übernimmt den Server und lädt danach die Repos neu
|
||||
$('btnSaveSettings').click();
|
||||
setTimeout(() => { if (typeof loadRepos === 'function') loadRepos(); }, 600);
|
||||
showInfo(`Wechsle zu „${p.name}“ …`);
|
||||
}
|
||||
|
||||
async function deleteProfile() {
|
||||
const name = $('giteaProfileSelect').value;
|
||||
if (!name) return;
|
||||
const ok = await showActionConfirmModal({
|
||||
title: 'Profil entfernen',
|
||||
message: `Das gespeicherte Profil „${name}“ wird entfernt. Der aktuell eingetragene Server bleibt unverändert.`,
|
||||
confirmText: 'Entfernen',
|
||||
danger: true
|
||||
});
|
||||
if (!ok) return;
|
||||
const creds = (await window.electronAPI.loadCredentials()) || {};
|
||||
await window.electronAPI.saveCredentials({ ...creds, giteaProfiles: (creds.giteaProfiles || []).filter(p => p.name !== name) });
|
||||
await loadProfiles();
|
||||
snapshot = readFormState();
|
||||
}
|
||||
|
||||
/* ---------- Token anzeigen / entfernen ---------- */
|
||||
function addTokenTools(inputId) {
|
||||
const input = $(inputId);
|
||||
if (!input || input.dataset.tools) return;
|
||||
input.dataset.tools = '1';
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'token-field';
|
||||
input.parentNode.insertBefore(wrap, input);
|
||||
wrap.appendChild(input);
|
||||
const eye = document.createElement('button');
|
||||
eye.type = 'button';
|
||||
eye.className = 'token-tool';
|
||||
eye.title = 'Token anzeigen';
|
||||
eye.textContent = '👁';
|
||||
eye.onclick = () => {
|
||||
const show = input.type === 'password';
|
||||
input.type = show ? 'text' : 'password';
|
||||
eye.title = show ? 'Token verbergen' : 'Token anzeigen';
|
||||
eye.classList.toggle('is-on', show);
|
||||
};
|
||||
const clear = document.createElement('button');
|
||||
clear.type = 'button';
|
||||
clear.className = 'token-tool';
|
||||
clear.title = 'Token entfernen (wirksam nach „Speichern“)';
|
||||
clear.textContent = '✕';
|
||||
clear.onclick = () => { input.value = ''; input.focus(); };
|
||||
wrap.append(eye, clear);
|
||||
}
|
||||
|
||||
/* ---------- Diagnose ---------- */
|
||||
async function refreshTransportState() {
|
||||
const el = $('gitTransportState');
|
||||
const btn = $('btnGitTransportClear');
|
||||
if (!el) return;
|
||||
const res = await window.electronAPI.gitTransportState();
|
||||
const blocked = (res && res.blocked) || [];
|
||||
if (!blocked.length) {
|
||||
el.textContent = '✅ Nicht gesperrt: Uploads, Löschen und Backup nutzen den schnellen Git-Weg, sobald der Server ihn anbietet.';
|
||||
if (btn) btn.disabled = true;
|
||||
return;
|
||||
}
|
||||
el.textContent = blocked.map(b => `⛔ ${b.host} gesperrt seit ${new Date(b.since).toLocaleString('de-DE')} – die App nutzt dort die langsamere API.`).join('\n');
|
||||
if (btn) { btn.disabled = false; btn.dataset.host = blocked[0].host; }
|
||||
}
|
||||
|
||||
async function openFolder(which) {
|
||||
const r = await window.electronAPI.openKnownFolder(which);
|
||||
if (!r || !r.ok) showWarning((r && r.error) || 'Ordner konnte nicht geöffnet werden');
|
||||
}
|
||||
|
||||
/* ---------- Verdrahtung ---------- */
|
||||
function wire() {
|
||||
addTokenTools('githubToken');
|
||||
addTokenTools('giteaToken');
|
||||
|
||||
const on = (id, fn) => { const el = $(id); if (el) el.addEventListener('click', fn); };
|
||||
on('btnGiteaProfileSave', saveProfile);
|
||||
on('btnGiteaProfileUse', useProfile);
|
||||
on('btnGiteaProfileDelete', deleteProfile);
|
||||
on('btnPrefCloneDir', async () => { const d = await window.electronAPI.selectFolder(); if (d) $('prefCloneDir').value = d; });
|
||||
on('btnRepoBackupOpen', () => openFolder('backup'));
|
||||
on('btnOpenAppData', () => openFolder('appData'));
|
||||
on('btnOpenLogs', () => openFolder('logs'));
|
||||
on('btnGitTransportClear', async (e) => {
|
||||
const r = await window.electronAPI.gitTransportClear(e.currentTarget.dataset.host || '');
|
||||
if (r && r.ok) showSuccess('Sperre aufgehoben. Der nächste Upload probiert wieder den schnellen Git-Weg.');
|
||||
refreshTransportState();
|
||||
});
|
||||
|
||||
// Design als Vorschau sofort zeigen; "Abbrechen" nimmt sie zurück
|
||||
const theme = $('prefTheme');
|
||||
if (theme) theme.addEventListener('change', () => applyTheme(theme.value));
|
||||
|
||||
// Gemeinsames Speichern: läuft zusätzlich zum bisherigen Speichern der Zugangsdaten
|
||||
on('btnSaveSettings', () => { saveExtras(); });
|
||||
|
||||
// Beim Öffnen der Einstellungen Stand merken (für die Warnung beim Abbrechen)
|
||||
on('btnSettings', () => setTimeout(takeSnapshot, 80));
|
||||
|
||||
// Standardordner beim Klonen mehrerer Repos vorschlagen
|
||||
on('btnBatchActions', () => setTimeout(() => {
|
||||
const field = $('batchCloneTarget');
|
||||
if (field && !field.value && savedPrefs && savedPrefs.defaultCloneDir) field.value = savedPrefs.defaultCloneDir;
|
||||
}, 50));
|
||||
|
||||
// Diagnose-Bereich: Sperrstatus aktualisieren, wenn er geöffnet wird
|
||||
const diagTab = document.querySelector('#settingsModal [data-settings-tab="diagnose"]');
|
||||
if (diagTab) diagTab.addEventListener('click', refreshTransportState);
|
||||
}
|
||||
|
||||
// Gemeinsamer Zugriff für features.js / renderer.js
|
||||
window.gmPrefs = {
|
||||
get: () => savedPrefs,
|
||||
load: loadPrefs,
|
||||
takeSnapshot
|
||||
};
|
||||
|
||||
loadPrefs().catch(() => {});
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', wire);
|
||||
else wire();
|
||||
})();
|
||||
Reference in New Issue
Block a user