import { useState, type ReactNode } from "react"; import { View, Text, TouchableOpacity, ActivityIndicator } from "react-native"; import { Ionicons } from "@expo/vector-icons"; // Geteilter Schutz-Setup-Flow (Reihenfolge + Gating). Quelle der Wahrheit für // Blocker UND Onboarding — NIE die Reihenfolge hier ändern ohne beide zu prüfen. // Android: VPN → Geräteadmin → a11y (a11y zuletzt, sonst blockt der Tamper-Lock // die Admin-Seite). iOS: App-Lock → Bildschirmzeit → URL-Filter. // ─── iOS Unsupervised 3-Step Setup Flow ────────────────────────────────────── type SetupFlowProps = { familyControlsActive: boolean; screentimeCode: string | null; screentimeConfirmed: boolean; screentimeSaving: boolean; urlFilterActive: boolean; onActivateFamilyControls: () => Promise<{ enabled: boolean; error?: string }>; onGenerateScreentimeCode: () => void; onConfirmScreentime: () => void; onActivateUrlFilter: () => Promise<{ enabled: boolean; error?: string }>; colors: ReturnType; t: ReturnType['t']; }; export function IosUnsupervisedSetupFlow({ familyControlsActive, screentimeCode, screentimeConfirmed, screentimeSaving, urlFilterActive, onActivateFamilyControls, onGenerateScreentimeCode, onConfirmScreentime, onActivateUrlFilter, colors, t, }: SetupFlowProps) { const step1Done = familyControlsActive; const step2Done = screentimeConfirmed; const step3Done = urlFilterActive; return ( ); } function SetupStep1({ done, onActivate, colors, t, }: { done: boolean; onActivate: () => Promise<{ enabled: boolean; error?: string }>; colors: ReturnType; t: ReturnType['t']; }) { const [busy, setBusy] = useState(false); async function handlePress() { if (done || busy) return; setBusy(true); try { await onActivate(); } finally { setBusy(false); } } return ( {!done && ( {busy ? : {t('blocker.setup_step1_cta')} } )} ); } function SetupStep2({ unlocked, code, confirmed, saving, onGenerate, onConfirm, colors, t, }: { unlocked: boolean; code: string | null; confirmed: boolean; saving: boolean; onGenerate: () => void; onConfirm: () => void; colors: ReturnType; t: ReturnType['t']; }) { return ( {unlocked && !confirmed && ( {!code ? ( {t('blocker.screentime_generate_cta')} ) : ( {t('blocker.screentime_code_label')} {code} {[ t('blocker.screentime_step1'), t('blocker.screentime_step2'), t('blocker.screentime_step3'), ].map((step, i) => ( {step} ))} {t('blocker.screentime_step_note')} {saving ? : {t('blocker.screentime_confirm_cta')} } )} )} ); } function SetupStep3({ unlocked, done, onActivate, colors, t, }: { unlocked: boolean; done: boolean; onActivate: () => Promise<{ enabled: boolean; error?: string }>; colors: ReturnType; t: ReturnType['t']; }) { const [busy, setBusy] = useState(false); async function handlePress() { if (done || busy) return; setBusy(true); try { await onActivate(); } finally { setBusy(false); } } return ( {unlocked && !done && ( {t('blocker.setup_step3_warning')} {busy ? : {t('blocker.setup_step3_cta')} } )} ); } // ─── Android 3-Step Setup Flow ─────────────────────────────────────────────── type AndroidSetupFlowProps = { vpnActive: boolean; // VPN-Aktivierung läuft → Spinner statt CTA, bis der Layer-State bestätigt ist. vpnActivating?: boolean; // = a11y-Service enabled UND Tamper-Lock armed (appDeletionLock). Nur "enabled" // reicht NICHT — der a11y-Service ist ohne armed-Flag komplett passiv. accessibilityLocked: boolean; deviceAdminActive: boolean; // Akku-Ausnahme (gegen Samsung-Sleep, der den a11y-Lock entbindet → Schutz weg). batteryUnrestricted: boolean; onActivateVpn: () => Promise<{ enabled: boolean; error?: string }>; onActivateAccessibility: () => Promise; onRequestDeviceAdmin: () => Promise<{ launched: boolean }>; onRequestBattery: () => Promise<{ opened: boolean; alreadyIgnored?: boolean }>; onOpenAppDetails: () => Promise<{ opened: boolean }>; colors: ReturnType; t: ReturnType['t']; }; export function AndroidSetupFlow({ vpnActive, vpnActivating, accessibilityLocked, deviceAdminActive, batteryUnrestricted, onActivateVpn, onActivateAccessibility, onRequestDeviceAdmin, onRequestBattery, onOpenAppDetails, colors, t, }: AndroidSetupFlowProps) { // Reihenfolge KRITISCH: VPN → Geräteadmin → Akku-Ausnahme → a11y. a11y MUSS // zuletzt, weil der Tamper-Lock (sobald armed) Settings-Seiten blockt. Die // Akku-Ausnahme MUSS VOR a11y: sonst schläfert Samsung den frisch aktivierten // a11y-Service wieder ein → Lock entbunden → Schutz wertlos. const vpnDone = vpnActive; const adminDone = deviceAdminActive; const batteryDone = batteryUnrestricted; const a11yDone = accessibilityLocked; return ( {/* Display-Step 2 = Geräteadmin (Komponente AndroidStep3, i18n android_step3_*). */} {/* Display-Step 3 = Akku-Ausnahme (gegen Samsung-Sleep, der a11y entbindet). */} {/* Display-Step 4 = a11y / ReBreak-Schutz (Komponente AndroidStep2, i18n android_step2_*). */} ); } function AndroidStep1({ done, pending, onActivate, colors, t, }: { done: boolean; pending?: boolean; onActivate: () => Promise<{ enabled: boolean; error?: string }>; colors: ReturnType; t: ReturnType['t']; }) { const [busy, setBusy] = useState(false); async function handlePress() { if (done || busy) return; setBusy(true); try { await onActivate(); } finally { setBusy(false); } } return ( {!done && (pending ? ( {t('blocker.activating')} ) : ( {busy ? : {t('blocker.android_step1_cta')} } ))} ); } function AndroidStep2({ unlocked, done, onActivate, colors, t, }: { unlocked: boolean; done: boolean; onActivate: () => Promise; colors: ReturnType; t: ReturnType['t']; }) { const [busy, setBusy] = useState(false); async function handlePress() { if (busy) return; setBusy(true); try { await onActivate(); } finally { setBusy(false); } } return ( {unlocked && !done && ( {[ t('blocker.android_step2_instruction1'), t('blocker.android_step2_instruction2'), t('blocker.android_step2_instruction3'), ].map((line, i) => ( {line} ))} {busy ? : {t('blocker.android_step2_cta')} } {t('blocker.android_step2_note')} )} ); } function AndroidStepBattery({ unlocked, done, onRequest, onOpenDetails, colors, t, }: { unlocked: boolean; done: boolean; onRequest: () => Promise<{ opened: boolean; alreadyIgnored?: boolean }>; onOpenDetails: () => Promise<{ opened: boolean }>; colors: ReturnType; t: ReturnType['t']; }) { const [busy, setBusy] = useState(false); async function handlePress() { if (done || busy) return; setBusy(true); try { await onRequest(); } finally { setBusy(false); } } return ( {unlocked && !done && ( {t('blocker.android_battery_body')} {busy ? : {t('blocker.android_battery_cta')} } { void onOpenDetails(); }} activeOpacity={0.7} style={{ paddingVertical: 4, alignItems: 'center' }}> {t('blocker.android_battery_samsung_hint')} )} ); } function AndroidStep3({ unlocked, done, onRequestAdmin, colors, t, }: { unlocked: boolean; done: boolean; onRequestAdmin: () => Promise<{ launched: boolean }>; colors: ReturnType; t: ReturnType['t']; }) { const [busy, setBusy] = useState(false); async function handlePress() { if (done || busy) return; setBusy(true); try { await onRequestAdmin(); } finally { setBusy(false); } } return ( {unlocked && !done && ( {t('blocker.android_step3_warning')} {busy ? : {t('blocker.android_step3_cta')} } )} ); } function SetupStepCard({ stepNumber, title, subtitle, done, unlocked, lockedHint, colors, children, }: { stepNumber: number; title: string; subtitle: string; done: boolean; unlocked: boolean; lockedHint?: string; colors: ReturnType; children?: ReactNode; }) { const borderColor = done ? '#86efac' : unlocked ? colors.border : colors.border; const cardBg = done ? '#f0fdf4' : colors.surface; const numberBg = done ? '#dcfce7' : unlocked ? colors.surfaceElevated : colors.surfaceElevated; const numberColor = done ? colors.success : unlocked ? colors.text : colors.textMuted; const titleColor = done ? colors.success : unlocked ? colors.text : colors.textMuted; return ( {done ? : {stepNumber} } {title} {!!subtitle && ( {subtitle} )} {done && } {lockedHint && !done && ( {lockedHint} )} {children} ); }