import { useState, useMemo } from 'react'; import { View, Text, TouchableOpacity, ActivityIndicator, } from 'react-native'; import { Image } from 'expo-image'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import { SuccessAlert } from '../SuccessAlert'; import { ConfirmAlert } from '../ConfirmAlert'; import type { CustomDomain, Tier } from '../../hooks/useCustomDomains'; import { useColors } from '../../lib/theme'; // ─── Helpers ───────────────────────────────────────────────────────────── function timeAgo(input?: string | Date): string { if (!input) return ''; const date = typeof input === 'string' ? new Date(input) : input; const diffMs = Date.now() - date.getTime(); const minutes = Math.floor(diffMs / 60_000); if (minutes < 1) return 'jetzt'; if (minutes < 60) return `${minutes}m`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h`; const days = Math.floor(hours / 24); if (days < 7) return `${days}d`; const weeks = Math.floor(days / 7); if (weeks < 5) return `${weeks}w`; const months = Math.floor(days / 30); return `${months}mo`; } type Props = { domains: CustomDomain[]; tier: Tier; kind: 'web' | 'mail'; onSubmit?: (id: string) => Promise<{ ok: boolean }>; onUpgradePro?: () => void; }; // Sort-Reihenfolge: User sieht zuerst was Aufmerksamkeit braucht. // submitted (in Prüfung) > rejected (kann erneut) > active (settled OK) const STATUS_PRIORITY: Record = { submitted: 0, rejected: 1, active: 2, approved: 99, }; export function DomainGrid({ domains, tier, kind, onSubmit, onUpgradePro }: Props) { const { t } = useTranslation(); const colors = useColors(); const visible = useMemo(() => { return domains .filter((d) => { if (d.status === 'approved') return false; if (kind === 'mail') { return d.type === 'mail_domain'; } return d.type === 'web' || !d.type; }) .slice() .sort((a, b) => { const pa = STATUS_PRIORITY[a.status] ?? 99; const pb = STATUS_PRIORITY[b.status] ?? 99; if (pa !== pb) return pa - pb; const ta = a.addedAt ? new Date(a.addedAt).getTime() : 0; const tb = b.addedAt ? new Date(b.addedAt).getTime() : 0; return tb - ta; }); }, [domains, kind]); return ( {/* Limit-Reached Upsell (nur Free) */} {tier.atLimit && tier.plan === 'free' && ( {t('blocker.domain_limit_title')} {t('blocker.domain_limit_desc')} )} {/* Empty State */} {visible.length === 0 ? ( {kind === 'mail' ? t('blocker.empty_mail') : t('blocker.empty_web')} ) : ( )} ); } // ─── Tiles ──────────────────────────────────────────────────────────────── function DomainTilesGrid({ domains, tier, onSubmit, }: { domains: CustomDomain[]; tier: Tier; onSubmit?: (id: string) => Promise<{ ok: boolean }>; }) { // 3-Spalten-Grid via flex-wrap. Parent ScrollView (in blocker.tsx) handles scroll — // KEIN nested ScrollView hier, sonst kollabiert der Layout-Pass weil ScrollView // inner-content-view keine definierte Width für %-basierte Tile-Widths hat. return ( {domains.map((d) => ( ))} ); } function DomainTile({ domain, tier, onSubmit, }: { domain: CustomDomain; tier: Tier; onSubmit?: (id: string) => Promise<{ ok: boolean }>; }) { const { t } = useTranslation(); const colors = useColors(); const [submitting, setSubmitting] = useState(false); const [imgError, setImgError] = useState(false); const [successVisible, setSuccessVisible] = useState(false); const [successContent, setSuccessContent] = useState<{ title: string; message: string }>({ title: '', message: '', }); const [confirmVisible, setConfirmVisible] = useState(false); const stripped = domain.domain.replace(/^www\./, ''); const isLegend = tier.plan === 'legend'; const statusColor = (() => { switch (domain.status) { case 'submitted': return '#f59e0b'; case 'rejected': return '#FF3B30'; default: return '#007AFF'; } })(); const timeColor = domain.status === 'active' ? '#a3a3a3' : statusColor; const badgeLabel = (() => { switch (domain.status) { case 'submitted': return isLegend ? t('blocker.domain_badge_pruefung') : t('blocker.domain_badge_voting'); case 'rejected': return t('blocker.domain_badge_rejected'); default: return t('blocker.domain_badge_active'); } })(); const isResubmit = domain.status === 'rejected'; const confirmTitle = isLegend ? isResubmit ? t('blocker.domain_confirm_legend_resubmit') : t('blocker.domain_confirm_legend_first') : isResubmit ? t('blocker.domain_confirm_community_resubmit') : t('blocker.domain_confirm_community_first'); const confirmMessage = isLegend ? t('blocker.domain_confirm_legend_message', { domain: stripped }) : t('blocker.domain_confirm_community_message', { domain: stripped }); function openConfirm() { if (!onSubmit) return; setConfirmVisible(true); } async function handleConfirm() { setConfirmVisible(false); if (!onSubmit) return; setSubmitting(true); try { const result = await onSubmit(domain.id); if (result.ok) { setSuccessContent({ title: isLegend ? t('blocker.domain_success_legend_title') : t('blocker.domain_success_community_title'), message: isLegend ? t('blocker.domain_success_legend_message') : t('blocker.domain_success_community_message'), }); setSuccessVisible(true); } } finally { setSubmitting(false); } } const isMailDisplayName = domain.type === 'mail_display_name'; const isFreeAndUsed = tier.plan === 'free' && domain.status !== 'active'; const showSubmit = tier.canSubmit && domain.status === 'active' && !isMailDisplayName; const showResubmit = tier.canSubmit && domain.status === 'rejected' && !isMailDisplayName; const showInPruefungBtn = domain.status === 'submitted'; return ( {/* Top-Row: Zeit links · Badge rechts */} {timeAgo(domain.addedAt)} {badgeLabel} {/* Mitte: Icon + Domain-Name */} {domain.type === 'mail_domain' || domain.type === 'mail_display_name' ? ( ) : !imgError ? ( setImgError(true)} /> ) : ( {stripped.slice(0, 2).toUpperCase()} )} {stripped} {/* Bottom-Slot: ALWAYS rendered Container (28px), Inhalt je nach Status. */} {showInPruefungBtn && ( {isLegend ? t('blocker.domain_btn_rebreak_prueft') : t('blocker.domain_btn_in_abstimmung')} )} {showSubmit && ( {submitting ? ( ) : ( {t('blocker.domain_btn_freigeben')} )} )} {showResubmit && ( {submitting ? ( ) : ( {t('blocker.domain_btn_erneut')} )} )} setConfirmVisible(false)} /> setSuccessVisible(false)} /> ); }