import { useState } from 'react'; import { ActivityIndicator, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import { FormSheet } from '../FormSheet'; import { useMailInterval } from '../../hooks/useMailInterval'; import { useMailTitleEdit } from '../../hooks/useMailTitleEdit'; import { useMailConnect } from '../../hooks/useMailConnect'; import { useColors } from '../../lib/theme'; import type { MailAccount } from '../../hooks/useMailStatus'; type EditMode = 'list' | 'edit-title' | 'edit-email' | 'edit-password'; type Props = { visible: boolean; account: MailAccount; localTitle: string | null; isOAuth: boolean; plan: 'free' | 'pro' | 'legend'; disconnecting?: boolean; onClose: () => void; onTitleSaved: (newTitle: string | null) => void; onPasswordSaved: () => void; onDisconnectRequest: () => void; onIntervalChanged: () => void; }; const INTERVAL_OPTIONS_BY_PLAN: Record<'free' | 'pro' | 'legend', number[]> = { free: [4], pro: [1, 4, 8], legend: [1, 4, 8], }; function domainFromEmail(email: string): string { return email.split('@')[1] ?? email; } function SettingsRow({ label, value, onPress, destructive, }: { label: string; value?: string; onPress?: () => void; destructive?: boolean; }) { const labelColor = destructive ? '#dc2626' : '#0a0a0a'; const Wrapper = onPress ? TouchableOpacity : View; const wrapperProps = onPress ? { activeOpacity: 0.7 as const, onPress } : {}; return ( {label} {value !== undefined && ( {value} )} {onPress && !destructive && ( )} ); } function EditView({ label, value, onChangeText, onSave, onBack, saving, error, secureTextEntry, keyboardType, autoCapitalize, placeholder, }: { label: string; value: string; onChangeText: (v: string) => void; onSave: () => void; onBack: () => void; saving: boolean; error: string | null; secureTextEntry?: boolean; keyboardType?: TextInput['props']['keyboardType']; autoCapitalize?: TextInput['props']['autoCapitalize']; placeholder?: string; }) { const { t } = useTranslation(); const colors = useColors(); return ( {/* Back row */} {label} {/* Input */} {error && ( {error} )} {/* Save button */} {saving ? ( ) : ( {t('mail.title_save')} )} ); } export function MailAccountSettingsSheet({ visible, account, localTitle, isOAuth, plan, disconnecting, onClose, onTitleSaved, onPasswordSaved, onDisconnectRequest, onIntervalChanged, }: Props) { const { t } = useTranslation(); const { setInterval, updating } = useMailInterval(); const { saveTitle, saving: savingTitle, error: titleError } = useMailTitleEdit(); const { connect, connecting: connectingPassword, error: connectError } = useMailConnect(); const isLegend = plan === 'legend'; const intervalOptions = INTERVAL_OPTIONS_BY_PLAN[plan]; const displayTitle = localTitle ?? domainFromEmail(account.email); const [mode, setMode] = useState('list'); const [titleDraft, setTitleDraft] = useState(localTitle ?? ''); const [passwordDraft, setPasswordDraft] = useState(''); const [passwordVisible, setPasswordVisible] = useState(false); const [localError, setLocalError] = useState(null); function handleClose() { setMode('list'); setTitleDraft(localTitle ?? ''); setPasswordDraft(''); setPasswordVisible(false); setLocalError(null); onClose(); } function goBack() { setMode('list'); setLocalError(null); } async function handleSetInterval(value: number) { const res = await setInterval(account.id, value); if (res.ok) onIntervalChanged(); } async function handleSaveTitle() { const ok = await saveTitle(account.id, titleDraft); if (ok) { const newTitle = titleDraft.trim() || null; // Sheet ZUERST dismissen, dann nach Animation-Complete Parent informieren. // Sonst rendern Sheet (closing) + SuccessAlert (opening) gleichzeitig → iOS // wirft „already presenting"-Crash + Page-Freeze. ~350ms = FormSheet-Dismiss. handleClose(); setTimeout(() => onTitleSaved(newTitle), 350); } } async function handleSavePassword() { setLocalError(null); const result = await connect({ email: account.email, password: passwordDraft }); if (result.ok) { // Symmetrisch zu handleSaveTitle: Sheet zuerst dismissen, dann nach // Animation-Complete Parent informieren — sonst Modal-Conflict-Crash. handleClose(); setTimeout(() => onPasswordSaved(), 350); } else { setLocalError(result.error ?? t('mail.connect_failed')); } } const sheetTitle = mode === 'list' ? displayTitle : mode === 'edit-title' ? t('mail.row_title') : mode === 'edit-email' ? t('mail.row_email') : t('mail.row_password'); return ( {mode === 'list' && ( {/* Bezeichnung */} { setTitleDraft(localTitle ?? ''); setMode('edit-title'); }} /> {/* E-Mail */} setMode('edit-email') : undefined} /> {/* Passwort — nur IMAP */} {!isOAuth && ( { setPasswordDraft(''); setPasswordVisible(false); setMode('edit-password'); }} /> )} {/* Scan-Intervall */} {!isLegend ? ( {t('mail.scan_interval_label')} {intervalOptions.map((opt) => { const active = account.scanInterval === opt; const disabled = plan === 'free' || updating === account.id; return ( handleSetInterval(opt)} style={{ flex: 1, paddingVertical: 9, borderRadius: 10, alignItems: 'center', backgroundColor: active ? '#007AFF' : '#f5f5f5', opacity: disabled && !active ? 0.5 : 1, }} > {opt}h ); })} {plan === 'free' && ( {t('mail.free_scan_interval_hint')} )} ) : ( {t('mail.realtime_desc')} )} {/* Verbindung trennen */} {t('mail.row_disconnect')} )} {mode === 'edit-title' && ( )} {mode === 'edit-email' && ( {}} onSave={() => {}} onBack={goBack} saving={false} error={t('mail.email_change_not_supported')} keyboardType="email-address" autoCapitalize="none" placeholder={account.email} /> )} {mode === 'edit-password' && ( {/* Back row */} {t('mail.row_password')} {/* Password input with visibility toggle */} { setPasswordDraft(v); setLocalError(null); }} placeholder={t('mail.app_password_placeholder')} placeholderTextColor="#a3a3a3" secureTextEntry={!passwordVisible} autoCapitalize="none" autoCorrect={false} returnKeyType="done" onSubmitEditing={handleSavePassword} style={{ flex: 1, paddingVertical: 13, fontSize: 15, fontFamily: 'Nunito_400Regular', color: '#0a0a0a', }} /> setPasswordVisible((p) => !p)} hitSlop={8} > {(localError ?? connectError) && ( {localError ?? connectError} )} {connectingPassword ? ( ) : ( {t('mail.title_save')} )} )} ); }