A mobile app view of atmoquest
0

Configure Feed

Select the types of activity you want to include in your feed.

atmoquest-mobile / api / hooks.ts
7.6 kB 249 lines
1import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; 2import { apiFetch } from './client'; 3 4// ─── Types ────────────────────────────────────────────────────────── 5 6export interface UserSession { 7 did: string; 8 handle: string; 9 isAdmin: boolean; 10} 11 12export interface Profile { 13 did: string; 14 handle: string; 15 displayName: string; 16 bio: string; 17 interests: string[]; 18 links: { label: string; url: string }[]; 19 location: string; 20 isHiring: boolean; 21 isLooking: boolean; 22} 23 24export interface Connection { 25 did: string; 26 handle: string; 27 displayName: string; 28 connectedAt: string; 29} 30 31export interface ConnectionNotes { 32 notes: string; 33 followUp: boolean; 34 updatedAt: string; 35} 36 37export interface Event { 38 token: string; 39 name: string; 40 description: string; 41 startDate: string; 42 endDate: string; 43 location: string; 44 lat?: number; 45 lng?: number; 46 radius?: number; 47 checkinCount: number; 48} 49 50export interface EventDetail extends Event { 51 attendees: { did: string; handle: string; displayName: string }[]; 52 stats: { 53 uniqueConnectors: number; 54 totalCheckins: number; 55 attendeeCount: number; 56 }; 57 isCheckedIn: boolean; 58} 59 60export interface Badge { 61 name: string; 62 earnedAt: string; 63 eventToken?: string; 64 svgUrl: string; 65} 66 67export interface Settings { 68 did: string; 69 handle: string; 70 showInAttendees: boolean; 71 showInLeaderboard: boolean; 72} 73 74// ─── Query Keys ───────────────────────────────────────────────────── 75 76export const queryKeys = { 77 session: ['session'] as const, 78 profile: ['profile'] as const, 79 userProfile: (did: string) => ['profile', did] as const, 80 connections: ['connections'] as const, 81 connectionNotes: (did: string) => ['connectionNotes', did] as const, 82 events: ['events'] as const, 83 eventDetail: (token: string) => ['event', token] as const, 84 eventStats: (token: string) => ['eventStats', token] as const, 85 badges: ['badges'] as const, 86 badgesRecent: ['badges', 'recent'] as const, 87 settings: ['settings'] as const, 88 settingsNotes: ['settings', 'notes'] as const, 89} as const; 90 91// ─── Auth ─────────────────────────────────────────────────────────── 92 93export function useSession() { 94 return useQuery({ 95 queryKey: queryKeys.session, 96 queryFn: () => apiFetch<UserSession>('/auth/session'), 97 retry: false, 98 staleTime: 5 * 60 * 1000, 99 }); 100} 101 102// ─── Profile ──────────────────────────────────────────────────────── 103 104export function useProfile() { 105 return useQuery({ 106 queryKey: queryKeys.profile, 107 queryFn: () => apiFetch<Profile>('/profile'), 108 staleTime: 5 * 60 * 1000, 109 }); 110} 111 112export function useUserProfile(did: string) { 113 return useQuery({ 114 queryKey: queryKeys.userProfile(did), 115 queryFn: () => apiFetch<Profile>(`/users/${encodeURIComponent(did)}`), 116 staleTime: 5 * 60 * 1000, 117 }); 118} 119 120export function useUpdateProfile() { 121 const queryClient = useQueryClient(); 122 return useMutation({ 123 mutationFn: (data: Partial<Profile>) => 124 apiFetch<Profile>('/profile', { method: 'PUT', body: JSON.stringify(data) }), 125 onSuccess: () => { 126 queryClient.invalidateQueries({ queryKey: queryKeys.profile }); 127 }, 128 }); 129} 130 131// ─── Connections ──────────────────────────────────────────────────── 132 133export function useConnections() { 134 return useQuery({ 135 queryKey: queryKeys.connections, 136 queryFn: () => apiFetch<Connection[]>('/connections'), 137 staleTime: 2 * 60 * 1000, 138 }); 139} 140 141export function useCreateConnection() { 142 const queryClient = useQueryClient(); 143 return useMutation({ 144 mutationFn: (did: string) => 145 apiFetch('/connections/' + encodeURIComponent(did), { method: 'POST' }), 146 onSuccess: () => { 147 queryClient.invalidateQueries({ queryKey: queryKeys.connections }); 148 }, 149 }); 150} 151 152export function useConnectionNotes(did: string) { 153 return useQuery({ 154 queryKey: queryKeys.connectionNotes(did), 155 queryFn: () => apiFetch<ConnectionNotes>(`/connections/${encodeURIComponent(did)}/notes`), 156 }); 157} 158 159export function useSaveConnectionNotes() { 160 const queryClient = useQueryClient(); 161 return useMutation({ 162 mutationFn: ({ did, notes, followUp }: { did: string; notes: string; followUp: boolean }) => 163 apiFetch(`/connections/${encodeURIComponent(did)}/notes`, { 164 method: 'PUT', 165 body: JSON.stringify({ notes, followUp }), 166 }), 167 onSuccess: (_, { did }) => { 168 queryClient.invalidateQueries({ queryKey: queryKeys.connectionNotes(did) }); 169 queryClient.invalidateQueries({ queryKey: queryKeys.settingsNotes }); 170 }, 171 }); 172} 173 174// ─── Events ───────────────────────────────────────────────────────── 175 176export function useEvents() { 177 return useQuery({ 178 queryKey: queryKeys.events, 179 queryFn: () => apiFetch<Event[]>('/events'), 180 staleTime: 60 * 1000, 181 }); 182} 183 184export function useEventDetail(token: string) { 185 return useQuery({ 186 queryKey: queryKeys.eventDetail(token), 187 queryFn: () => apiFetch<EventDetail>(`/events/${encodeURIComponent(token)}`), 188 staleTime: 60 * 1000, 189 }); 190} 191 192export function useEventStats(token: string) { 193 return useQuery({ 194 queryKey: queryKeys.eventStats(token), 195 queryFn: () => apiFetch<EventDetail['stats']>(`/events/${encodeURIComponent(token)}/stats`), 196 refetchInterval: 5000, 197 }); 198} 199 200export function useCheckin() { 201 const queryClient = useQueryClient(); 202 return useMutation({ 203 mutationFn: (token: string) => 204 apiFetch(`/events/${encodeURIComponent(token)}/checkin`, { method: 'POST' }), 205 onSuccess: (_, token) => { 206 queryClient.invalidateQueries({ queryKey: queryKeys.eventDetail(token) }); 207 queryClient.invalidateQueries({ queryKey: queryKeys.events }); 208 }, 209 }); 210} 211 212// ─── Badges ───────────────────────────────────────────────────────── 213 214export function useBadges() { 215 return useQuery({ 216 queryKey: queryKeys.badges, 217 queryFn: () => apiFetch<Badge[]>('/badges'), 218 staleTime: 5 * 60 * 1000, 219 }); 220} 221 222export function useRecentBadges() { 223 return useQuery({ 224 queryKey: queryKeys.badgesRecent, 225 queryFn: () => apiFetch<Badge[]>('/badges/recent'), 226 staleTime: 30 * 1000, 227 }); 228} 229 230// ─── Settings ─────────────────────────────────────────────────────── 231 232export function useSettings() { 233 return useQuery({ 234 queryKey: queryKeys.settings, 235 queryFn: () => apiFetch<Settings>('/settings'), 236 staleTime: 5 * 60 * 1000, 237 }); 238} 239 240export function useUpdatePrivacy() { 241 const queryClient = useQueryClient(); 242 return useMutation({ 243 mutationFn: (data: Partial<Pick<Settings, 'showInAttendees' | 'showInLeaderboard'>>) => 244 apiFetch('/settings/privacy', { method: 'PUT', body: JSON.stringify(data) }), 245 onSuccess: () => { 246 queryClient.invalidateQueries({ queryKey: queryKeys.settings }); 247 }, 248 }); 249}