generated from knso/webklar-preview-template
Datenschutz-Ausbau: Archiv-Verschlüsselung, App-Sperre, Auto-Löschen
- Brief-Archiv jetzt AES-256-GCM-verschlüsselt (expo-crypto, Schlüssel im SecureStore); Migration alter Klartext-Bestände beim ersten Laden; im Simulator auf der Platte verifiziert (nur Chiffrat, kein Klartext) - AppSchutz-Komponente: optionale App-Sperre (Face ID / Geräte-Code, expo-local-authentication) + Sichtschutz im App-Umschalter - Auto-Löschen alter Briefe (30/90 Tage, Einstellungen), pure Filter- Funktion mit Tests - UTF-8-Helfer (Umlaute/Arabisch/Emoji-sicher) mit Tests; gesamt 30 Tests - Einstellungen: neuer Abschnitt "Sicherheit" - Datenschutzerklärung (Web + Entwurf) um die neuen Maßnahmen ergänzt - Neu: rechtliches/datenschutz-factsheet.md für Kooperations-Pitches - EU-Verarbeitung geprüft: inference_geo bietet aktuell nur us/global, keine EU-Option (Haiku 4.5 gar nicht) — als Ausbaupfad dokumentiert Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
12ebf514fb
commit
ad1c2bf76c
16 files changed
+639
-18
No files matched your search
@@ -4,16 +4,30 @@
|
||||
* im Produktionsmodus läuft der KI-Zugang über den Backend-Proxy.
|
||||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Alert, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
|
||||
import {
|
||||
Alert,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import * as Notifications from 'expo-notifications';
|
||||
import * as LocalAuthentication from 'expo-local-authentication';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { RootStackParamList } from '../types';
|
||||
import {
|
||||
BONUS_ANALYSEN,
|
||||
holeApiKey,
|
||||
holeAppSperre,
|
||||
holeAutoLoeschTage,
|
||||
holeBonusAktiv,
|
||||
loeseBonusCodeEin,
|
||||
loescheAlleDaten,
|
||||
setzeAppSperre,
|
||||
setzeAutoLoeschTage,
|
||||
speichereApiKey,
|
||||
} from '../services/storage';
|
||||
import { NUTZT_PROXY } from '../services/claudeClient';
|
||||
@@ -30,12 +44,45 @@ export function EinstellungenScreen({ navigation }: Props) {
|
||||
const [gespeichert, setGespeichert] = useState(false);
|
||||
const [bonusCode, setBonusCode] = useState('');
|
||||
const [bonusAktiv, setBonusAktiv] = useState(false);
|
||||
const [sperreAktiv, setSperreAktiv] = useState(false);
|
||||
const [autoTage, setAutoTage] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
holeApiKey().then((k) => setKeyVorhanden(!!k));
|
||||
holeBonusAktiv().then(setBonusAktiv);
|
||||
holeAppSperre().then(setSperreAktiv);
|
||||
holeAutoLoeschTage().then(setAutoTage);
|
||||
}, []);
|
||||
|
||||
/** App-Sperre umschalten — beide Richtungen erfordern eine Authentifizierung. */
|
||||
const sperreUmschalten = async (an: boolean) => {
|
||||
if (an) {
|
||||
const stufe = await LocalAuthentication.getEnrolledLevelAsync();
|
||||
if (stufe === LocalAuthentication.SecurityLevel.NONE) {
|
||||
Alert.alert(
|
||||
'Keine Geräte-Sperre eingerichtet',
|
||||
'Bitte richten Sie zuerst in den Geräte-Einstellungen einen Code, Face ID oder Fingerabdruck ein.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const ergebnis = await LocalAuthentication.authenticateAsync({
|
||||
promptMessage: an ? 'App-Sperre aktivieren' : 'App-Sperre deaktivieren',
|
||||
cancelLabel: 'Abbrechen',
|
||||
});
|
||||
if (!ergebnis.success) return;
|
||||
await setzeAppSperre(an);
|
||||
setSperreAktiv(an);
|
||||
};
|
||||
|
||||
/** Aufbewahrungsdauer setzen und sofort anwenden. */
|
||||
const autoTageSetzen = async (tage: number) => {
|
||||
await setzeAutoLoeschTage(tage);
|
||||
setAutoTage(tage);
|
||||
// Sofort anwenden statt erst beim nächsten App-Start
|
||||
await useAppStore.getState().initialisiere();
|
||||
};
|
||||
|
||||
const codeEinloesen = async () => {
|
||||
const erfolg = await loeseBonusCodeEin(bonusCode);
|
||||
if (erfolg) {
|
||||
@@ -155,13 +202,57 @@ export function EinstellungenScreen({ navigation }: Props) {
|
||||
|
||||
<View style={styles.trenner} />
|
||||
|
||||
<Text style={styles.abschnittTitel}>Sicherheit</Text>
|
||||
<View style={styles.schalterZeile}>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={styles.text}>App-Sperre</Text>
|
||||
<Text style={styles.hinweis}>
|
||||
Face ID / Geräte-Code beim Öffnen der App — schützt Ihre Briefe,
|
||||
falls jemand Ihr Handy in die Hand bekommt.
|
||||
</Text>
|
||||
</View>
|
||||
<Switch
|
||||
value={sperreAktiv}
|
||||
onValueChange={sperreUmschalten}
|
||||
accessibilityLabel="App-Sperre umschalten"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Text style={[styles.text, { marginTop: abstand.m, fontWeight: '700' }]}>
|
||||
Briefe automatisch löschen
|
||||
</Text>
|
||||
<Text style={styles.hinweis}>
|
||||
Alte Briefe werden dann von selbst vom Gerät entfernt.
|
||||
</Text>
|
||||
{[
|
||||
{ tage: 0, label: 'Nie (Standard)' },
|
||||
{ tage: 30, label: 'Nach 30 Tagen' },
|
||||
{ tage: 90, label: 'Nach 90 Tagen' },
|
||||
].map(({ tage, label }) => (
|
||||
<TouchableOpacity
|
||||
key={tage}
|
||||
style={[styles.option, autoTage === tage && styles.optionAktiv]}
|
||||
onPress={() => autoTageSetzen(tage)}
|
||||
accessibilityRole="radio"
|
||||
accessibilityState={{ selected: autoTage === tage }}
|
||||
>
|
||||
<Text style={[styles.text, autoTage === tage && styles.optionAktivText]}>
|
||||
{autoTage === tage ? '● ' : '○ '}
|
||||
{label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
|
||||
<View style={styles.trenner} />
|
||||
|
||||
<Text style={styles.abschnittTitel}>Datenschutz</Text>
|
||||
<View style={styles.karte}>
|
||||
<Text style={styles.text}>
|
||||
• Brieffotos werden nur zur Analyse an den KI-Dienst Anthropic (USA)
|
||||
gesendet und dort nach spätestens 30 Tagen gelöscht. Sie werden nicht
|
||||
zum Training der KI verwendet.{'\n\n'}
|
||||
• Alle Ergebnisse liegen ausschließlich auf Ihrem Gerät.{'\n\n'}
|
||||
• Alle Ergebnisse liegen ausschließlich auf Ihrem Gerät — verschlüsselt
|
||||
gespeichert (AES-256).{'\n\n'}
|
||||
• Diese App ersetzt keine Rechtsberatung.
|
||||
</Text>
|
||||
</View>
|
||||
@@ -202,4 +293,20 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
karte: { backgroundColor: farben.flaeche, borderRadius: 14, padding: abstand.m },
|
||||
trenner: { height: 1, backgroundColor: farben.rand, marginVertical: abstand.l },
|
||||
schalterZeile: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: abstand.m,
|
||||
},
|
||||
option: {
|
||||
borderWidth: 1,
|
||||
borderColor: farben.rand,
|
||||
borderRadius: 12,
|
||||
padding: abstand.s,
|
||||
minHeight: 56,
|
||||
justifyContent: 'center',
|
||||
marginTop: abstand.s,
|
||||
},
|
||||
optionAktiv: { borderColor: farben.primaer, backgroundColor: farben.flaeche },
|
||||
optionAktivText: { fontWeight: '700', color: farben.primaer },
|
||||
});
|
||||
Reference in new issue
Block a user