286 lines
13 KiB
JavaScript
286 lines
13 KiB
JavaScript
// 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('prefShowSplash', p.showSplash);
|
||
set('prefCloseAction', p.closeAction);
|
||
set('prefCloneDir', p.defaultCloneDir);
|
||
set('prefSecretScan', p.uploadSecretScan);
|
||
set('prefBlockSecrets', p.uploadBlockSecrets);
|
||
set('prefUploadPreview', p.uploadPreview);
|
||
set('prefNotifyTransfers', p.notifyTransfers);
|
||
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'),
|
||
showSplash: chk('prefShowSplash'),
|
||
closeAction: val('prefCloseAction'),
|
||
defaultCloneDir: val('prefCloneDir'),
|
||
uploadSecretScan: chk('prefSecretScan'),
|
||
uploadBlockSecrets: chk('prefBlockSecrets'),
|
||
uploadPreview: val('prefUploadPreview'),
|
||
notifyTransfers: val('prefNotifyTransfers'),
|
||
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();
|
||
})();
|