import { useState } from 'react'; import { ActivityIndicator, Alert, Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { PinInput } from '../../../components/PinInput'; import { useAuth } from '../../../lib/authContext'; import { changePin, regenerateRecoveryCode, resetIdentity, retryLegacyMigration, type LegacyMigrationReport, } from '../../../lib/userIdentity'; import { colors } from '../../../theme/colors'; export default function SecuritySettings() { const { userId, refreshUserKeyState } = useAuth(); const [oldPin, setOldPin] = useState(''); const [newPin, setNewPin] = useState(''); const [newRecovery, setNewRecovery] = useState(null); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [report, setReport] = useState(null); async function handleChangePin() { if (!userId) return; if (oldPin.length !== 6 || newPin.length !== 6) { setError('Beide PINs müssen 6 Ziffern haben.'); return; } setSubmitting(true); setError(null); try { await changePin({ userId, oldPin, newPin }); setOldPin(''); setNewPin(''); Alert.alert('PIN geändert', 'Die neue PIN gilt sofort auf allen Geräten.'); } catch (e) { setError(e instanceof Error ? e.message : 'PIN-Änderung fehlgeschlagen'); } finally { setSubmitting(false); } } async function handleRegenerateRecovery() { if (!userId) return; setSubmitting(true); setError(null); try { const code = await regenerateRecoveryCode({ userId }); setNewRecovery(code); } catch (e) { setError(e instanceof Error ? e.message : 'Recovery-Code-Erzeugung fehlgeschlagen'); } finally { setSubmitting(false); } } function handleReset() { if (!userId) return; Alert.alert( 'Identität zurücksetzen?', 'Alle bisherigen Chats werden für dich unlesbar. Diese Aktion kann nicht rückgängig gemacht werden.', [ { text: 'Abbrechen', style: 'cancel' }, { text: 'Zurücksetzen', style: 'destructive', onPress: async () => { setSubmitting(true); try { await resetIdentity({ userId, pin: '000000' }); await refreshUserKeyState(); } catch (e) { setError(e instanceof Error ? e.message : 'Reset fehlgeschlagen'); } finally { setSubmitting(false); } }, }, ], ); } async function handleRetryMigration() { if (!userId) return; setSubmitting(true); setError(null); try { const r = await retryLegacyMigration(userId); setReport(r); } catch (e) { setError(e instanceof Error ? e.message : 'Migration fehlgeschlagen'); } finally { setSubmitting(false); } } return ( PIN ändern Alte PIN Neue PIN {submitting ? ( ) : ( PIN aktualisieren )} Recovery-Code Neuen Recovery-Code erzeugen {newRecovery && ( {newRecovery} )} Migration Migration erneut versuchen {report && ( Geräte (Server): {report.serverDevices} Lokale Schlüssel im Vault:{' '} {report.strongholdKeysFromServerDevices + report.strongholdKeysFromBundleScan} Versucht: {report.attempted}, Erfolgreich: {report.migrated} Übersprungen: kein lokaler Schlüssel = {report.noStrongholdKey}, Decrypt-Fehler ={' '} {report.decryptFailed}, RPC-Fehler = {report.rpcFailed} )} Gefahrenbereich Identität zurücksetzen {error && {error}} ); } const styles = StyleSheet.create({ container: { padding: 16, gap: 8, backgroundColor: colors.bg }, section: { color: colors.text, fontSize: 16, fontWeight: '700', marginTop: 16 }, label: { color: colors.textMuted, fontSize: 12, marginTop: 4 }, primary: { backgroundColor: colors.accent, paddingVertical: 14, borderRadius: 12, alignItems: 'center', marginTop: 8, }, primaryText: { color: colors.text, fontWeight: '700' }, danger: { backgroundColor: 'transparent', borderColor: colors.danger, borderWidth: 1 }, dangerText: { color: colors.danger }, codeBox: { backgroundColor: colors.surface, padding: 12, borderRadius: 10, marginTop: 4, }, codeText: { color: colors.text, fontFamily: 'Courier', fontSize: 16, letterSpacing: 1.2, textAlign: 'center', }, report: { backgroundColor: colors.surface, padding: 12, borderRadius: 10, marginTop: 4, gap: 4, }, reportLine: { color: colors.text, fontSize: 13 }, error: { color: colors.danger, marginTop: 12 }, });