/ apps / web / src / hooks / useGunDBReferents.ts
useGunDBReferents.ts
  1  import { useState, useCallback } from 'react'
  2  import { litProtocolService } from '../lib/litProtocol'
  3  import { useLitSession } from './useLitSession'
  4  import { gunDBService } from '../services/database/gundb'
  5  import { optimisticCache } from '../services/database/gundb/optimisticCache'
  6  import { isPopularSong, getPopularSongData } from '../constants/popularSongs'
  7  
  8  export interface GunDBReferent {
  9    id: number
 10    fragment: string
 11    range?: { start?: number; end?: number }
 12    annotation?: {
 13      body: string
 14      votes_total: number
 15      verified: boolean
 16    }
 17  }
 18  
 19  interface UseGunDBReferentsReturn {
 20    fetchReferents: (geniusId: number, page?: number) => Promise<GunDBReferent[]>
 21    isFetching: boolean
 22    error: string | null
 23    hasMore: boolean
 24  }
 25  
 26  export interface FetchReferentsResult {
 27    referents: GunDBReferent[]
 28    hasMore: boolean
 29  }
 30  
 31  export function useGunDBReferents(): UseGunDBReferentsReturn {
 32    const [isFetching, setIsFetching] = useState(false)
 33    const [error, setError] = useState<string | null>(null)
 34    const [hasMore, setHasMore] = useState(false)
 35    const { sessionSigs, createSession } = useLitSession()
 36  
 37    const fetchReferents = useCallback(async (geniusId: number, page: number = 1): Promise<GunDBReferent[]> => {
 38      setIsFetching(true)
 39      setError(null)
 40  
 41      try {
 42        // Check if this is a popular song with pre-populated data
 43        if (isPopularSong(geniusId) && page === 1) {
 44          console.log('🌟 Using pre-populated referents for popular song:', geniusId)
 45          const popularData = getPopularSongData(geniusId)
 46          if (popularData && popularData.referents) {
 47            // Transform to match the expected format
 48            const referents = popularData.referents.map((ref: any, index) => ({
 49              id: index + 1,
 50              fragment: ref.fragment,
 51              range: { start: ref.start_index, end: ref.end_index }
 52            }))
 53            setHasMore(false) // Popular songs have all referents pre-loaded
 54            setIsFetching(false)
 55            return referents
 56          }
 57        }
 58  
 59        const cacheKey = `referents:${geniusId}:${page}`
 60        
 61        const result = await optimisticCache.getOrExecute(
 62          cacheKey,
 63          async () => {
 64            // Check GunDB cache
 65            console.log(`🔍 Checking GunDB cache for referents: song=${geniusId}, page=${page}`)
 66            const cached = await gunDBService.getReferents(geniusId, page)
 67            if (cached) {
 68              console.log(`📦 Using cached referents for song ${geniusId}`)
 69              setHasMore(cached.hasMore)
 70              return cached
 71            }
 72            return null
 73          },
 74          async () => {
 75            console.log('📡 Cache miss, need to fetch from Genius API...')
 76            
 77            // Create Lit session
 78            let currentSessionSigs = sessionSigs
 79            if (!currentSessionSigs) {
 80              console.log('🔐 Creating new Lit session for referents fetch...')
 81              currentSessionSigs = await createSession()
 82              if (!currentSessionSigs) {
 83                throw new Error('Failed to create Lit session')
 84              }
 85            }
 86  
 87            console.log('🚀 Executing Genius referents fetcher Lit Action...')
 88            
 89            // Load the Lit Action code
 90            const geniusReferentsFetcherCode = await fetch('/lit-actions/genius-referents-fetcher/geniusReferentsFetcher.js').then(r => r.text())
 91            
 92            const result = await litProtocolService.litNodeClient!.executeJs({
 93              code: geniusReferentsFetcherCode,
 94              sessionSigs: currentSessionSigs,
 95              jsParams: {
 96                songId: geniusId.toString(),
 97                page,
 98                perPage: 50
 99              }
100            })
101  
102            const response = JSON.parse(result.response as string)
103            
104            if (!response.success) {
105              throw new Error(response.error || 'Failed to fetch referents')
106            }
107  
108            console.log(`✅ Fetched ${response.referents.length} referents from Genius API`)
109            
110            // Save to GunDB cache for next time
111            await gunDBService.saveReferents(geniusId, page, response.referents, response.hasMore)
112            
113            return {
114              referents: response.referents,
115              hasMore: response.hasMore
116            }
117          }
118        )
119        
120        if (result) {
121          setHasMore(result.hasMore)
122          return result.referents
123        }
124        
125        return []
126      } catch (err) {
127        const errorMessage = err instanceof Error ? err.message : 'Failed to fetch referents'
128        console.error('❌ Fetch referents error:', errorMessage)
129        setError(errorMessage)
130        return []
131      } finally {
132        setIsFetching(false)
133      }
134    }, [sessionSigs, createSession])
135  
136    return {
137      fetchReferents,
138      isFetching,
139      error,
140      hasMore
141    }
142  }