diff --git a/apps/rebreak-native/components/FormSheet.tsx b/apps/rebreak-native/components/FormSheet.tsx index baaf51b..8f3cfdb 100644 --- a/apps/rebreak-native/components/FormSheet.tsx +++ b/apps/rebreak-native/components/FormSheet.tsx @@ -5,8 +5,8 @@ import { Modal, PanResponder, Platform, - Pressable, Text, + TouchableOpacity, View, useWindowDimensions, } from 'react-native'; @@ -188,7 +188,8 @@ export function FormSheet({ return ( {/* Backdrop */} - diff --git a/apps/rebreak-native/components/blocker/AddDomainSheet.tsx b/apps/rebreak-native/components/blocker/AddDomainSheet.tsx index bda20ab..9e8c2ad 100644 --- a/apps/rebreak-native/components/blocker/AddDomainSheet.tsx +++ b/apps/rebreak-native/components/blocker/AddDomainSheet.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { ActivityIndicator, Image, @@ -22,22 +22,42 @@ type InputKind = 'web' | 'mail'; type Props = { visible: boolean; tier: Tier; + initialType?: InputKind; onClose: () => void; onAdd: (pattern: string, kind: InputKind) => Promise<{ ok: boolean; error?: string; alreadyGlobal?: boolean }>; }; -export function AddDomainSheet({ visible, tier, onClose, onAdd }: Props) { +export function AddDomainSheet({ visible, tier, initialType, onClose, onAdd }: Props) { const { t } = useTranslation(); const colors = useColors(); - const [kind, setKind] = useState('web'); + const [kind, setKind] = useState(initialType ?? 'web'); const [input, setInput] = useState(''); const [confirmPermanent, setConfirmPermanent] = useState(false); const [adding, setAdding] = useState(false); const [error, setError] = useState(null); const [fieldsDone, setFieldsDone] = useState(false); + useEffect(() => { + if (visible) setKind(initialType ?? 'web'); + }, [visible, initialType]); + const normalizedWeb = kind === 'web' ? normalizeDomain(input) : ''; + // For mail input: if the user typed a full address (local@domain.tld), strip + // the local-part and keep only the domain — casino affiliates rotate the + // local-part rapidly (communications@, newsletter@, info@, …) while the + // sender-domain stays stable. Blocking on the domain is more durable. + // A bare token without "@" stays as-is (will be matched as display-name on + // the backend). + const mailPattern = (() => { + if (kind !== 'mail') return ''; + const raw = input.trim(); + if (!raw) return ''; + const atIdx = raw.lastIndexOf('@'); + if (atIdx === -1) return raw; + return raw.slice(atIdx + 1).trim().toLowerCase(); + })(); + function close() { setInput(''); setConfirmPermanent(false); @@ -63,7 +83,7 @@ export function AddDomainSheet({ visible, tier, onClose, onAdd }: Props) { if (!isInputValid() || !confirmPermanent || adding) return; setAdding(true); setError(null); - const pattern = kind === 'web' ? input : input.trim(); + const pattern = kind === 'web' ? input : mailPattern; const result = await onAdd(pattern, kind); setAdding(false); if (result.ok) { @@ -72,6 +92,10 @@ export function AddDomainSheet({ visible, tier, onClose, onAdd }: Props) { } if (result.alreadyGlobal) { setError(t('blocker.add_sheet_already_global', { domain: normalizedWeb || input.trim() })); + } else if (result.error?.includes('WEB_LIMIT_REACHED')) { + setError(t('blocker.error_web_limit_reached')); + } else if (result.error?.includes('MAIL_LIMIT_REACHED')) { + setError(t('blocker.error_mail_limit_reached')); } else { setError(result.error ?? t('blocker.add_sheet_add_failed')); } @@ -223,7 +247,7 @@ export function AddDomainSheet({ visible, tier, onClose, onAdd }: Props) { }} numberOfLines={1} > - {input.trim() || inputPlaceholder} + {mailPattern || inputPlaceholder} )}