Files
Git-Manager-Gui/renderer/Settings.jsx
2026-03-25 23:07:07 +01:00

184 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect, useRef } from 'react';
export default function Settings({ onClose }) {
const [githubToken, setGithubToken] = useState('');
const [giteaToken, setGiteaToken] = useState('');
const [giteaURL, setGiteaURL] = useState('');
const [avatarB64, setAvatarB64] = useState(null);
const [savedOk, setSavedOk] = useState(false);
const [avatarUploading, setAvatarUploading] = useState(false);
const fileInputRef = useRef(null);
function normalizeAndValidateGiteaUrl(rawUrl) {
const value = (rawUrl || '').trim();
if (!value) return { ok: true, value: '' };
let parsed;
try {
parsed = new URL(value);
} catch (_) {
return {
ok: false,
error: 'Ungültige Gitea-URL. Beispiel für IPv6: http://[2001:db8::1]:3000',
};
}
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
return {
ok: false,
error: 'Die Gitea-URL muss mit http:// oder https:// beginnen.',
};
}
return { ok: true, value: value.replace(/\/$/, '') };
}
useEffect(() => {
window.electronAPI.loadCredentials().then(data => {
if (data) {
setGithubToken(data.githubToken || '');
setGiteaToken(data.giteaToken || '');
setGiteaURL(data.giteaURL || '');
setAvatarB64(data.avatarB64 || null);
}
});
}, []);
function handleAvatarFileChange(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (ev) => setAvatarB64(ev.target.result);
reader.readAsDataURL(file);
}
async function save() {
const checkedUrl = normalizeAndValidateGiteaUrl(giteaURL);
if (!checkedUrl.ok) {
alert(checkedUrl.error);
return;
}
window.electronAPI.saveCredentials({
githubToken,
giteaToken,
giteaURL: checkedUrl.value,
avatarB64: avatarB64 || null
});
// Avatar automatisch zu Gitea pushen, wenn Token + URL vorhanden
if (avatarB64 && giteaToken && checkedUrl.value) {
setAvatarUploading(true);
const result = await window.electronAPI.updateGiteaAvatar({
token: giteaToken,
url: checkedUrl.value,
imageBase64: avatarB64
});
setAvatarUploading(false);
if (!result.ok) {
console.warn('Avatar-Upload fehlgeschlagen:', result.error);
}
}
setSavedOk(true);
setTimeout(() => setSavedOk(false), 2500);
}
return (
<div className="modalContent card settings-modal-content">
<div className="settings-header">
<div className="settings-header-inner">
<div className="settings-avatar-wrap" onClick={() => fileInputRef.current?.click()} title="Profilbild ändern">
{avatarB64
? <img src={avatarB64} alt="Avatar" className="settings-avatar-img" />
: <div className="settings-avatar-placeholder">👤</div>
}
<div className="settings-avatar-overlay"></div>
<input
ref={fileInputRef}
type="file"
accept="image/*"
style={{ display: 'none' }}
onChange={handleAvatarFileChange}
/>
</div>
<div>
<div className="settings-eyebrow">Konfiguration</div>
<h2> Einstellungen</h2>
<p className="settings-subtitle">
Zugangsdaten für GitHub und Gitea hinterlegen.
</p>
</div>
</div>
</div>
<div className="settings-layout">
<div className="settings-column settings-column--left">
<section className="settings-panel settings-panel--credentials">
<div className="settings-panel-header">
<div>
<h3>Zugangsdaten</h3>
<p>API-Zugriffe für GitHub und Gitea konfigurieren.</p>
</div>
</div>
<div className="settings-fields-grid">
<div className="input-group">
<label htmlFor="react-githubToken">GitHub Token</label>
<input
id="react-githubToken"
type="password"
placeholder="ghp_…"
value={githubToken}
onChange={e => setGithubToken(e.target.value)}
/>
</div>
<div className="input-group">
<label htmlFor="react-giteaToken">Gitea Token</label>
<input
id="react-giteaToken"
type="password"
placeholder="Token hier einfügen"
value={giteaToken}
onChange={e => setGiteaToken(e.target.value)}
/>
</div>
</div>
<div className="input-group input-group--wide">
<label htmlFor="react-giteaURL">Gitea URL</label>
<input
id="react-giteaURL"
type="text"
placeholder="https://gitea.example.com"
value={giteaURL}
onChange={e => setGiteaURL(e.target.value)}
/>
<div className="settings-connection-tools">
<div className="settings-inline-hint">
Hinweis: IPv6 mit Klammern eingeben, z.B. http://[2001:db8::1]:3000
</div>
</div>
</div>
</section>
</div>
</div>
<div className="modal-buttons settings-modal-actions">
<button
className="accent-btn"
onClick={save}
disabled={avatarUploading}
style={savedOk ? { background: 'var(--success)', borderColor: 'var(--success)' } : {}}
>
{avatarUploading ? '⏳ Bild wird hochgeladen…' : savedOk ? '✅ Gespeichert' : 'Speichern'}
</button>
{onClose && (
<button className="secondary" onClick={onClose}>Abbrechen</button>
)}
</div>
</div>
);
}