import { useEffect, useRef, useState } from 'react'; import { View, Text, TouchableOpacity, ScrollView, Animated, ActivityIndicator, Easing, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import type { ProtectionState } from '../../lib/protection'; import { apiFetch } from '../../lib/api'; import { useColors } from '../../lib/theme'; import { FormSheet } from '../FormSheet'; import { HalfDonut } from '../common/HalfDonut'; type Props = { visible: boolean; state: ProtectionState; onClose: () => void; onRequestDeactivation: () => void; onTalkToLyra: () => void; }; type StatsResponse = { current: number; weeklyAdded: number; monthlyAdded: number; history: { label: string; count: number }[]; submissions: { inVote: number; inReview: number }; mySubmissions: { active: number; inVote: number; inReview: number }; avgPerUser: number; avgApprovalWaitDays: number; }; // Brand colors const HERO_COLOR = '#f97316'; // orange-500 (counter accent) const SEG_ACTIVE = '#16a34a'; const SEG_VOTE = '#3b82f6'; const SEG_REVIEW = '#f59e0b'; export function ProtectionDetailsSheet({ visible, state, onClose, onRequestDeactivation, }: Props) { const { t, i18n } = useTranslation(); const colors = useColors(); const insets = useSafeAreaInsets(); const localeTag = i18n.language === 'de' ? 'de-DE' : 'en-US'; const [stats, setStats] = useState(null); const [loadingStats, setLoadingStats] = useState(false); useEffect(() => { if (!visible) return; let alive = true; setLoadingStats(true); apiFetch('/api/blocklist/stats') .then((res) => { if (alive) setStats(res); }) .catch(() => { /* silent */ }) .finally(() => { if (alive) setLoadingStats(false); }); return () => { alive = false; }; }, [visible]); const globalCount = stats?.current ?? state.blocklistCount; const weeklyAdded = stats?.weeklyAdded ?? 0; const monthlyAdded = stats?.monthlyAdded ?? 0; const myActive = stats?.mySubmissions?.active ?? 0; const myInVote = stats?.mySubmissions?.inVote ?? 0; const myInReview = stats?.mySubmissions?.inReview ?? 0; const avgPerUser = stats?.avgPerUser ?? 0; const avgWait = stats?.avgApprovalWaitDays ?? 0; return ( {loadingStats && !stats ? ( ) : null} {/* HERO – Globale geblockte Domains: Counter (slow, color) + 2 Delta-Badges */} {t('blocker.kpi_global_label')} {/* SUBMISSIONS – Half Donut mit center-number + center-legend */} {t('blocker.kpi_submissions_title')} {t('blocker.kpi_submissions_subtitle')} {/* Centered Legend */} {/* AVG KPIs – kleiner */} {/* FAQ-Banner: Heading-Row mit Help-Icon rechts (kein gestapeltes Layout) */} {t('blocker.faq_heading')} {[1, 2, 3, 4].map((n) => ( ))} {/* "Schutz deaktivieren" – outline button: TouchableOpacity=card, inner View=flex-row */} {t('blocker.more_info_title')} ); } // ─── Animated Counter ────────────────────────────────────────────────────── function AnimatedCounter({ value, locale, decimals = 0, durationMs = 1200, style, }: { value: number; locale: string; decimals?: number; durationMs?: number; style?: any; }) { const anim = useRef(new Animated.Value(0)).current; const [display, setDisplay] = useState(0); useEffect(() => { anim.setValue(0); const listener = anim.addListener(({ value: v }) => { setDisplay(v * value); }); Animated.timing(anim, { toValue: 1, duration: durationMs, easing: Easing.out(Easing.cubic), useNativeDriver: false, }).start(); return () => anim.removeListener(listener); }, [value, anim, durationMs]); const formatted = display.toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals, }); return {formatted}; } // ─── Delta Badge (e.g. "+25 diese Woche ↗") ──────────────────────────────── function DeltaBadge({ value, label, locale, }: { value: number; label: string; locale: string; }) { const formatted = `+${value.toLocaleString(locale)}`; return ( {formatted} {label} ); } // ─── KPI Card (small) ────────────────────────────────────────────────────── function KpiCard({ icon, label, value, locale, decimals = 0, suffix, }: { icon: any; label: string; value: number; locale: string; decimals?: number; suffix?: string; }) { const colors = useColors(); return ( {label} {suffix ? ( {suffix} ) : null} ); } // ─── Legend Item (compact, centered row) ─────────────────────────────────── function LegendItem({ color, label, value, }: { color: string; label: string; value: number; }) { const colors = useColors(); return ( {value} {label} ); } // ─── FAQ Item (chevron AT END of header row, on right) ───────────────────── function FaqItem({ question, answer }: { question: string; answer: string }) { const colors = useColors(); const [open, setOpen] = useState(false); const rotateAnim = useRef(new Animated.Value(0)).current; useEffect(() => { Animated.timing(rotateAnim, { toValue: open ? 1 : 0, duration: 200, useNativeDriver: true, }).start(); }, [open, rotateAnim]); const rotate = rotateAnim.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '180deg'], }); return ( setOpen((v) => !v)} activeOpacity={0.75} > {question} {open && ( {answer} )} ); }