import { API_ENDPOINTS, buildApiUrl } from '@/assets/config/api'; import { useAuth } from '@/assets/contexts/useAuth'; import { cacheContacts, getCachedContacts } from '@/assets/services/offlineStorage'; import { colors } from '@/assets/styles/colors'; import { ContactData, ContactResponse, SocialMedia } from '@/assets/types'; import styles from '@/styles/screens/tabs/contactos.styles'; import { FontAwesome } from '@expo/vector-icons'; import * as Clipboard from 'expo-clipboard'; import { useEffect, useState } from 'react'; import { LoadingSpinner } from '@/assets/components/LoadingSpinner'; import { Alert, Image, Linking, Pressable, RefreshControl, ScrollView, Text, View, } from 'react-native'; // ─── helpers ────────────────────────────────────────────────────────────────── const openPhone = (phone: string) => { const clean = phone.replace(/[^\d+]/g, ''); Linking.openURL(`tel:${clean}`).catch(() => Alert.alert('Erro', 'Não foi possível abrir a aplicação de telefone.') ); }; const openEmail = (email: string) => { Linking.openURL(`mailto:${email}`).catch(() => Alert.alert('Erro', 'Não foi possível abrir a aplicação de e-mail.') ); }; const openWhatsApp = (value: string) => { const clean = value.replace(/[^\d+]/g, ''); const url = `https://wa.me/${clean}`; Linking.openURL(url).catch(() => Alert.alert('Erro', 'Não foi possível abrir o WhatsApp.') ); }; const openMaps = (address: string, lat?: number, lng?: number) => { const url = lat != null && lng != null ? `maps://?ll=${lat},${lng}&q=${encodeURIComponent(address)}` : `maps:?q=${encodeURIComponent(address)}`; Linking.openURL(url).catch(() => Linking.openURL( lat != null && lng != null ? `https://www.google.com/maps/search/?api=1&query=${lat},${lng}` : `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(address)}` ).catch(() => Alert.alert('Erro', 'Não foi possível abrir a aplicação de mapas.')) ); }; const copyToClipboard = async (value: string, label: string) => { await Clipboard.setStringAsync(value); Alert.alert('Copiado', `${label} copiado para a área de transferência.`); }; const extractPhone = (url: string): string => { const match = url.match(/(?:phone=|wa\.me\/)([\d+]+)/); if (match) return `+${match[1].replace(/^\+/, '')}`; const clean = url.replace(/[^\d+]/g, ''); return clean.startsWith('+') ? clean : `+${clean}`; }; const parseCoords = (raw?: string | null): { lat: number; lng: number } | null => { if (!raw) return null; const parts = raw.split(',').map((s) => parseFloat(s.trim())); if (parts.length === 2 && !isNaN(parts[0]) && !isNaN(parts[1])) { return { lat: parts[0], lng: parts[1] }; } return null; }; const parseHorarios = (raw: string): { dia: string; horas: string }[] => { return raw .split('\n') .map((line) => line.trim()) .filter(Boolean) .map((line) => { const sep = line.lastIndexOf(':'); if (sep > 0) { return { dia: line.slice(0, sep).trim(), horas: line.slice(sep + 1).trim() }; } const parts = line.split(/\s{2,}|\t/); if (parts.length >= 2) { return { dia: parts[0].trim(), horas: parts.slice(1).join(' ').trim() }; } return { dia: line, horas: '' }; }); }; const SOCIAL_ICONS: Record = { facebook: 'facebook', instagram: 'instagram', tiktok: 'music', // FontAwesome doesn't have tiktok twitter: 'twitter', linkedin: 'linkedin', youtube: 'youtube-play', }; // ─── screen ─────────────────────────────────────────────────────────────────── export default function Contactos() { const { token } = useAuth(); const [contactData, setContactData] = useState(null); const [socials, setSocials] = useState([]); const [isLoading, setIsLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [mapLoading, setMapLoading] = useState(true); const [mapError, setMapError] = useState(false); const fetchContacts = async () => { // 1. Mostrar cache imediatamente enquanto atualiza em fundo const cached = await getCachedContacts(); if (cached.contactData) { setContactData(cached.contactData); setSocials(cached.socials); setIsLoading(false); } try { const formData = new FormData(); if (token) formData.append('token', token); const response = await fetch(buildApiUrl(API_ENDPOINTS.CONTACTS), { method: 'POST', headers: { Accept: 'application/json' }, body: formData, }); const data: ContactResponse = await response.json(); console.log('data', data); if (data.status === 200 && data.contact) { const validSocials = (data.socials || []).filter((s) => s.value?.trim()); setContactData(data.contact); setSocials(validSocials); setError(null); await cacheContacts(data.contact, validSocials); } else if (!cached.contactData) { setError(data.message || 'Não foi possível carregar os contactos.'); } } catch { if (!cached.contactData) { setError('Sem ligação à internet e sem dados guardados.'); } } finally { setIsLoading(false); } }; useEffect(() => { fetchContacts(); }, [token]); const onRefresh = async () => { setRefreshing(true); await fetchContacts(); setRefreshing(false); }; if (isLoading) { return ( ); } if (error && !contactData) { return ( {error} ); } const horarios = contactData?.horarios ? parseHorarios(contactData.horarios) : []; const coords = parseCoords(contactData?.coordenadas); const mapUrl = coords ? `https://staticmap.openstreetmap.de/staticmap.php?center=${coords.lat},${coords.lng}&zoom=15&size=600x300&markers=${coords.lat},${coords.lng},red-pushpin` : null; return ( }> Contactos Agência {/* ── Ações rápidas ── */} {!!contactData?.mobilePhone && ( openPhone(contactData.telephone)}> Ligar Agora {contactData.telephone} )} {!!contactData?.email && ( openEmail(contactData.email)}> Enviar Email {contactData.email} )} {!!contactData?.mobilePhone && ( openWhatsApp(contactData.mobilePhone!)}> Falar no Chat {extractPhone(contactData.mobilePhone)} )} {/* ── Localização ── */} {!!contactData?.address && ( Localização {!!mapUrl && ( openMaps(contactData.address, coords?.lat, coords?.lng)} style={styles.mapImageWrap}> { setMapLoading(true); setMapError(false); }} onLoad={() => setMapLoading(false)} onError={() => { setMapLoading(false); setMapError(true); }} /> {mapLoading && !mapError && ( )} {mapError && ( Mapa indisponível )} )} {contactData.address} copyToClipboard(contactData.address, 'Morada')}> openMaps(contactData.address, coords?.lat, coords?.lng)}> Obter Direções )} {/* ── Horário de Funcionamento ── */} {horarios.length > 0 && ( Horário de Funcionamento {horarios.map((item, idx) => ( {item.dia} {item.horas || '—'} ))} )} {/* ── Redes Sociais ── */} {socials.length > 0 && ( Redes Sociais {socials.map((social, idx) => ( Linking.openURL(social.value).catch(() => null)}> ))} )} ); }