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 { useColors } from '../../lib/theme';
import { FormSheet } from '../FormSheet';
import { HalfDonut } from '../common/HalfDonut';
import { useBlockerStatsStore } from '../../stores/blockerStats';
type Props = {
visible: boolean;
state: ProtectionState;
/** True wenn Gerät MDM-managed ist — versteckt Cooldown-CTA, zeigt Trustee-Hinweis. */
mdmManaged?: boolean;
onClose: () => void;
onRequestDeactivation: () => void;
onTalkToLyra: () => void;
};
// Brand colors
const HERO_COLOR = '#f97316'; // orange-500 (counter accent)
const SEG_REVIEW = '#f59e0b';
const SEG_APPROVED = '#16a34a';
const SEG_REJECTED = '#ef4444';
export function ProtectionDetailsSheet({
visible,
state,
mdmManaged,
onClose,
onRequestDeactivation,
}: Props) {
const { t, i18n } = useTranslation();
const colors = useColors();
const insets = useSafeAreaInsets();
const localeTag = i18n.language === 'de' ? 'de-DE' : 'en-US';
const stats = useBlockerStatsStore((s) => s.stats);
const loadingStats = useBlockerStatsStore((s) => s.loading);
const refreshStatsIfStale = useBlockerStatsStore((s) => s.refreshIfStale);
useEffect(() => {
if (!visible) return;
refreshStatsIfStale(90_000).catch(() => {});
}, [visible, refreshStatsIfStale]);
const globalCount = stats?.current ?? state.blocklistCount;
const weeklyAdded = stats?.weeklyAdded ?? 0;
const monthlyAdded = stats?.monthlyAdded ?? 0;
const myInReview = stats?.mySubmissions?.inReview ?? 0;
const myApproved = stats?.mySubmissions?.approved ?? 0;
const myRejected = stats?.mySubmissions?.rejected ?? 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, 5, 6, 7, 8, 9].map((n) => (
))}
{mdmManaged ? (
/* MDM-Modus: Cooldown-Flow nicht möglich — Trustee-Hinweis statt Button */
{t('blocker.mdm_deactivate_title')}
{t('blocker.mdm_deactivate_body')}
) : (
/* Normal-Modus: Cooldown-Flow */
{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}
)}
);
}