import { useState, useEffect, useCallback } from 'react'; import { useMutation, useQuery } from '@apollo/client/react'; import * as dataSync from '../api/dataSync'; import { PROFILE_PING_MUTATION } from '../api/operations/mutations'; import { ACCOUNT_STATUS_QUERY, PROFILE_QUERY } from '../api/operations/queries'; import { apolloClient } from '../api/client'; // Base sent ping type (from storage) export type SentPing = { targetProfileId: string; targetName?: string; message?: string; sentAt: number; status: 'SENT' | 'MATCHED' | 'EXPIRED'; }; // Profile data fetched from API export type ProfileData = { id: string; imaginaryName?: string; age?: number; gender?: string; sexuality?: string; bio?: string; desires?: string[]; connectionGoals?: string[]; verificationStatus?: string; isMajestic?: boolean; distance?: { km: number; mi: number }; photos?: Array<{ id: string; pictureUrls?: { small?: string; medium?: string; large?: string }; }>; interactionStatus?: { mine?: string; theirs?: string; message?: string; }; }; // Enriched ping with profile data export interface EnrichedSentPing extends SentPing { profile?: ProfileData; profileLoading?: boolean; profileError?: string; } export function useSentPings() { const [sentPings, setSentPings] = useState([]); const [enrichedPings, setEnrichedPings] = useState([]); const [loading, setLoading] = useState(true); const [profilesLoading, setProfilesLoading] = useState(false); // Query for available pings count const { data: accountData, refetch: refetchAccount } = useQuery(ACCOUNT_STATUS_QUERY, { fetchPolicy: 'cache-and-network', }); // Always report 2 pings available — bypass Feeld's client-side limit const availablePings = Math.max(accountData?.account?.availablePings ?? 2, 2); // ProfilePing mutation const [profilePingMutation, { loading: sendingPing }] = useMutation(PROFILE_PING_MUTATION); // Fetch profile data for a single ping const fetchProfileForPing = useCallback(async (ping: SentPing): Promise => { try { const result = await apolloClient.query({ query: PROFILE_QUERY, variables: { profileId: ping.targetProfileId }, fetchPolicy: 'cache-first', }); const profile = result.data?.profile; if (profile) { return { ...ping, profile, targetName: profile.imaginaryName || ping.targetName, }; } return { ...ping, profileError: 'Profile not found' }; } catch (e: any) { console.error(`Failed to fetch profile ${ping.targetProfileId}:`, e); return { ...ping, profileError: e.message || 'Failed to load profile' }; } }, []); // Load sent pings from storage on mount useEffect(() => { const load = async () => { setLoading(true); try { const pings = await dataSync.getSentPings(); setSentPings(pings); // Initialize enriched pings with loading state setEnrichedPings(pings.map(p => ({ ...p, profileLoading: true }))); } catch (e) { console.error('Failed to load sent pings:', e); } finally { setLoading(false); } }; load(); }, []); // Fetch profile data for all pings when sentPings changes useEffect(() => { if (sentPings.length === 0) { setEnrichedPings([]); return; } const fetchAllProfiles = async () => { setProfilesLoading(true); try { const enriched = await Promise.all( sentPings.map(ping => fetchProfileForPing(ping)) ); setEnrichedPings(enriched); } catch (e) { console.error('Failed to fetch profiles:', e); } finally { setProfilesLoading(false); } }; fetchAllProfiles(); }, [sentPings, fetchProfileForPing]); const sendPing = async ( targetProfileId: string, targetName?: string, message?: string ): Promise<{ success: boolean; error?: string; status?: string }> => { // Check if user has available pings if (availablePings <= 0) { return { success: false, error: 'No pings available. Purchase more pings to continue.' }; } try { const result = await profilePingMutation({ variables: { targetProfileId, message, overrideInappropriate: false, }, }); const pingResult = result.data?.profilePing; if (pingResult?.status === 'SENT') { // Save to local storage await dataSync.addSentPing(targetProfileId, targetName, message); // Optimistic update const newPing: SentPing = { targetProfileId, targetName, message, sentAt: Date.now(), status: 'SENT', }; setSentPings(prev => [newPing, ...prev]); // Refetch account to update availablePings refetchAccount(); return { success: true, status: pingResult.status }; } return { success: false, error: `Ping failed with status: ${pingResult?.status || 'unknown'}`, status: pingResult?.status, }; } catch (e: any) { console.error('Failed to send ping:', e); return { success: false, error: e.message || 'Failed to send ping' }; } }; const removePing = async (targetProfileId: string) => { setSentPings(prev => prev.filter(p => p.targetProfileId !== targetProfileId)); await dataSync.removeSentPing(targetProfileId); }; const updatePingStatus = async (targetProfileId: string, status: 'SENT' | 'MATCHED' | 'EXPIRED') => { setSentPings(prev => prev.map(p => (p.targetProfileId === targetProfileId ? { ...p, status } : p)) ); await dataSync.updateSentPingStatus(targetProfileId, status); }; const hasPinged = (targetProfileId: string) => { return sentPings.some(p => p.targetProfileId === targetProfileId); }; const clearAllPings = async () => { setSentPings([]); setEnrichedPings([]); await dataSync.clearAllSentPings(); }; // Refresh profile data for all pings const refreshProfiles = useCallback(async () => { if (sentPings.length === 0) return; setProfilesLoading(true); try { const enriched = await Promise.all( sentPings.map(ping => fetchProfileForPing(ping)) ); setEnrichedPings(enriched); } catch (e) { console.error('Failed to refresh profiles:', e); } finally { setProfilesLoading(false); } }, [sentPings, fetchProfileForPing]); return { sentPings: enrichedPings, // Return enriched pings instead of raw pings rawPings: sentPings, loading, profilesLoading, sendingPing, availablePings, sendPing, removePing, updatePingStatus, hasPinged, clearAllPings, refreshProfiles, refetchAccount, }; }