import { useEffect, useRef, useState } from 'react'; import { Alert, AppState, Text, View } from 'react-native'; import { useTranslation } from 'react-i18next'; import { Ionicons } from '@expo/vector-icons'; import { FormSheet } from './FormSheet'; import { useColors } from '../lib/theme'; import { protection } from '../lib/protection'; import RebreakProtection from '../modules/rebreak-protection'; type StepState = 'pending' | 'done'; export function ProtectionOnboardingSheet({ visible, onComplete, onSkip, }: { visible: boolean; onComplete: () => void; onSkip: () => void; }) { const { t } = useTranslation(); const colors = useColors(); const [vpnState, setVpnState] = useState('pending'); const [a11yState, setA11yState] = useState('pending'); const [vpnLoading, setVpnLoading] = useState(false); const [a11yLoading, setA11yLoading] = useState(false); const vpnDone = vpnState === 'done'; const a11yDone = a11yState === 'done'; // AppState-Listener: User kehrt aus System-Settings zurück → State neu abfragen const refreshInFlightRef = useRef(false); const restartPromptShownRef = useRef(false); useEffect(() => { if (!visible) return; async function refresh() { if (refreshInFlightRef.current) return; refreshInFlightRef.current = true; try { const layers = await protection.getDeviceState(); const vpnActive = layers.vpn === true; const a11yActive = layers.accessibility === true; if (vpnActive) setVpnState('done'); if (a11yActive && vpnActive) { // Arm tamper-lock once a11y is enabled — activateFamilyControls() second // call goes through the armTamperLock() path. Without this, the service // is bound but stays passive because tamper_armed stays false. const r = await protection.activateFamilyControls(); if (r.enabled) setA11yState('done'); } } finally { refreshInFlightRef.current = false; } } refresh(); const sub = AppState.addEventListener('change', (next) => { if (next === 'active') refresh(); }); return () => sub.remove(); }, [visible]); // Beide Steps done → onComplete useEffect(() => { if (vpnDone && a11yDone) { let cancelled = false; (async () => { await maybeShowRestartPrompt(); if (!cancelled) onComplete(); })(); return () => { cancelled = true; }; } }, [vpnDone, a11yDone, onComplete]); function maybeShowRestartPrompt(): Promise { if (restartPromptShownRef.current) return Promise.resolve(); restartPromptShownRef.current = true; return new Promise((resolve) => { Alert.alert( t('onboarding.protection.android_restart_title'), t('onboarding.protection.android_restart_body'), [ { text: t('onboarding.protection.android_restart_later'), style: 'cancel', onPress: () => resolve(), }, { text: t('onboarding.protection.android_restart_now'), onPress: () => { (async () => { try { const result = await RebreakProtection.openPowerDialog?.(); if (!result?.opened) { await protection.openSystemSettings(); } } catch { await protection.openSystemSettings().catch(() => {}); } finally { resolve(); } })(); }, }, ], { cancelable: false }, ); }); } async function handleVpnStep() { setVpnLoading(true); try { const result = await protection.activateUrlFilter(); if (result.enabled) setVpnState('done'); } finally { setVpnLoading(false); } } async function handleA11yStep() { if (!vpnDone) return; setA11yLoading(true); try { const result = await protection.activateFamilyControls(); if (result.enabled) setA11yState('done'); } finally { setA11yLoading(false); } } return ( {t('protection_onboarding.sheet_intro')} {/* Step 1 — VPN */} {/* Step 2 — A11y */} {/* Skip */} {t('protection_onboarding.skip_cta')} ); } // ─── StepCard ──────────────────────────────────────────────────────────────── import { ActivityIndicator, TouchableOpacity } from 'react-native'; import type { ColorScheme } from '../lib/theme'; function StepCard({ stepNumber, title, description, state, loading, disabled, ctaLabel, onPress, colors, }: { stepNumber: number; title: string; description: string; state: StepState; loading: boolean; disabled: boolean; ctaLabel: string; onPress: () => void; colors: ColorScheme; }) { const done = state === 'done'; return ( {/* Header row */} {/* Step badge / checkmark */} {done ? ( ) : ( {stepNumber} )} {title} {/* Lock icon when disabled */} {disabled && ( )} {description} {!done && ( {loading ? ( ) : ( {ctaLabel} )} )} ); }