From b3804d805b907b776cd2081a1c31268318efe6fc Mon Sep 17 00:00:00 2001 From: byGalax Date: Fri, 15 May 2026 22:57:57 +0200 Subject: [PATCH] feat(desktop): UserKeySetup screen (PIN + optional recovery code) --- apps/desktop/src/components/UserKeySetup.tsx | 93 ++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 apps/desktop/src/components/UserKeySetup.tsx diff --git a/apps/desktop/src/components/UserKeySetup.tsx b/apps/desktop/src/components/UserKeySetup.tsx new file mode 100644 index 0000000..eb3ec04 --- /dev/null +++ b/apps/desktop/src/components/UserKeySetup.tsx @@ -0,0 +1,93 @@ +import { useCallback, useState } from 'react'; + +import { setupNewUserIdentity } from '../lib/userIdentity'; +import { PinInput } from './PinInput'; +import { ShieldIcon, SpinnerIcon } from './icons'; + +interface Props { userId: string; onComplete: () => void } +type Step = 'enter' | 'recovery'; + +export function UserKeySetup({ userId, onComplete }: Props) { + const [pin, setPin] = useState(''); + const [confirm, setConfirm] = useState(''); + const [withRecovery, setWithRecovery] = useState(true); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(null); + const [step, setStep] = useState('enter'); + const [recoveryCode, setRecoveryCode] = useState(null); + + const submit = useCallback(async () => { + if (pin.length !== 6 || confirm.length !== 6) { + setError('PIN muss 6 Ziffern haben.'); return; + } + if (pin !== confirm) { + setError('PINs stimmen nicht überein.'); return; + } + setBusy(true); setError(null); + try { + const r = await setupNewUserIdentity({ userId, pin, withRecovery }); + setRecoveryCode(r.recoveryCode); + if (withRecovery) setStep('recovery'); + else onComplete(); + } catch (err: unknown) { + setError(err instanceof Error ? err.message : String(err)); + } finally { setBusy(false); } + }, [pin, confirm, withRecovery, userId, onComplete]); + + if (step === 'recovery') { + return ( +
+
+ +

Recovery-Code

+
+

+ Speichere diesen Code an einem sicheren Ort (Passwortmanager, ausgedruckt). + Mit ihm kannst du deinen Account auch ohne PIN entsperren. +

+
+ {recoveryCode} +
+
+ + +
+
+ ); + } + + return ( +
{ e.preventDefault(); void submit(); }} + className="space-y-4 rounded-2xl border border-white/10 bg-ink-900/70 p-6 text-neutral-100" + > +
+ + +
+
+ + +
+ + {error &&

{error}

} + +
+ ); +}