import { useEffect, useState } from 'react'; import { ActivityIndicator, Image, ScrollView, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import { isValidDomain, normalizeDomain, type Tier, } from '../../hooks/useCustomDomains'; import { useColors, type ColorScheme } from '../../lib/theme'; import { FormSheet } from '../FormSheet'; type Props = { visible: boolean; tier: Tier; onClose: () => void; onAdd: (pattern: string, kind?: 'web' | 'mail') => Promise<{ ok: boolean; error?: string; alreadyGlobal?: boolean }>; }; function detectKind(input: string): 'web' | 'mail' | null { const raw = input.trim(); if (!raw) return null; if (raw.includes('@')) return 'mail'; if (raw.includes('.')) return 'web'; return null; } function mailDomain(input: string): string { const raw = input.trim(); const atIdx = raw.lastIndexOf('@'); if (atIdx === -1) return raw.toLowerCase(); return raw.slice(atIdx + 1).trim().toLowerCase(); } export function AddDomainSheet({ visible, tier, onClose, onAdd }: Props) { const { t } = useTranslation(); const colors = useColors(); const [input, setInput] = useState(''); const [confirmPermanent, setConfirmPermanent] = useState(false); const [adding, setAdding] = useState(false); const [error, setError] = useState(null); // User-Override über den Auto-Detect. null = follow auto-detect, sonst forced. const [kindOverride, setKindOverride] = useState<'web' | 'mail' | null>(null); const detected = detectKind(input); const kind: 'web' | 'mail' | null = kindOverride ?? detected; const normalizedWeb = kind === 'web' ? normalizeDomain(input) : ''; const normalizedMail = kind === 'mail' ? mailDomain(input) : ''; // Reset override sobald User komplett neuen Input tippt useEffect(() => { if (!input) setKindOverride(null); }, [input]); function close() { setInput(''); setKindOverride(null); setConfirmPermanent(false); setError(null); onClose(); } function isInputValid(): boolean { if (kind === 'web') return isValidDomain(input); if (kind === 'mail') return normalizedMail.length > 0; return false; } async function handleAdd() { if (!isInputValid() || !confirmPermanent || adding) return; setAdding(true); setError(null); const pattern = kind === 'web' ? normalizeDomain(input) : normalizedMail; // Pass kind explicitly — we've already stripped the local-part for mail, // so the backend's auto-detect (which keys on the "@" character) can no // longer infer the type from the pattern alone. Without this hint a // "info@only4-subscribers.com" entry would land as type=web because // the @ disappeared during the strip. const result = await onAdd(pattern, kind === 'mail' ? 'mail' : 'web'); setAdding(false); if (result.ok) { close(); return; } if (result.alreadyGlobal) { setError(t('blocker.add_sheet_already_global', { domain: pattern })); } else { const raw = (result.error ?? '').toLowerCase(); if (raw.includes('web_limit_reached')) { setError(t('blocker.error_web_limit_reached')); } else if (raw.includes('mail_limit_reached')) { setError(t('blocker.error_mail_limit_reached')); } else if (raw.includes('invalid_mail_domain') || raw.includes('display_name_not_supported')) { setError(t('blocker.error_invalid_mail')); } else if (raw.includes('invalid_domain') || raw.includes('invalid_pattern')) { setError(t('blocker.error_invalid_input')); } else if (raw.includes('eintrag bereits vorhanden') || raw.includes('duplicate')) { setError(t('blocker.error_duplicate')); } else { // Letzter Fallback: niemals raw JSON anzeigen. Wenn message-Feld da war, kommt's // sauber als String an — sonst generic Fehler. setError(t('blocker.add_sheet_add_failed')); } } } const warningText = tier.plan === 'free' ? t('blocker.add_sheet_warning_free') : t('blocker.add_sheet_warning_pro'); const canSubmit = isInputValid() && confirmPermanent && !adding; return ( {/* Input field */} {t('blocker.add_sheet_label')} { setInput(v); setError(null); }} placeholder={t('blocker.add_sheet_placeholder')} placeholderTextColor={colors.textMuted} keyboardType="email-address" autoCapitalize="none" autoCorrect={false} style={{ backgroundColor: colors.surfaceElevated, borderRadius: 10, padding: 12, fontSize: 14, fontFamily: 'Nunito_400Regular', color: colors.text, borderWidth: 1, borderColor: error ? '#dc2626' : colors.border, }} /> {error && ( {error} )} {/* Help text */} {t('blocker.add_sheet_help')} {/* Preview card */} {/* Override toggle — User kann Auto-Detect korrigieren falls falsch erkannt */} {detected !== null && ( setKindOverride(kind === 'mail' ? 'web' : 'mail')} activeOpacity={0.7} style={{ flexDirection: 'row', alignItems: 'center', gap: 10, paddingHorizontal: 12, paddingVertical: 10, borderRadius: 12, borderWidth: 1, borderColor: colors.border, backgroundColor: colors.surface, }} > {t('blocker.kind_override_label')} )} {/* Warning card */} {warningText} {/* Confirm checkbox */} setConfirmPermanent((v) => !v)} activeOpacity={0.7} style={{ flexDirection: 'row', alignItems: 'flex-start', gap: 10, paddingVertical: 4, }} > {confirmPermanent && } {t('blocker.add_sheet_confirm_permanent')} {/* Buttons */} {t('common.cancel')} {adding ? ( ) : ( {t('blocker.add_sheet_cta')} )} ); } // ─── PreviewCard ────────────────────────────────────────────────────────────── function PreviewCard({ kind, normalizedWeb, normalizedMail, placeholder, colors, t, }: { kind: 'web' | 'mail' | null; normalizedWeb: string; normalizedMail: string; placeholder: string; colors: ColorScheme; t: (key: string, opts?: Record) => string; }) { if (kind === 'web') { return ( {t('blocker.preview_web', { value: normalizedWeb || '…' })} ); } if (kind === 'mail') { return ( {t('blocker.preview_mail', { value: normalizedMail || '…' })} ); } return ( {t('blocker.preview_invalid')} ); }