Files
Git-Manager-Gui/renderer/settings-extra.js
T
2026-10-22 09:13:16 +00:00

286 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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();
})();