238 lines
6.7 KiB
TypeScript
Executable File
238 lines
6.7 KiB
TypeScript
Executable File
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<SentPing[]>([]);
|
|
const [enrichedPings, setEnrichedPings] = useState<EnrichedSentPing[]>([]);
|
|
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<EnrichedSentPing> => {
|
|
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,
|
|
};
|
|
}
|