mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 06:34:24 +01:00
fix: initialize profile labels synchronously from cache for instant display
- Use useMemo to check localStorage cache synchronously during render, before useEffect - Initialize useState with labels from cache, so first render shows cached profiles immediately - Add detailed logging for cache operations to debug caching issues - Fix ESLint warnings about unused variables and dependencies This eliminates the delay where profiles were only resolved after useEffect ran, causing profiles to display instantly on page reload when cached.
This commit is contained in:
@@ -49,20 +49,63 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
||||
}
|
||||
}, [content])
|
||||
|
||||
const [profileLabels, setProfileLabels] = useState<Map<string, string>>(new Map())
|
||||
// Initialize labels synchronously from cache on first render to avoid delay
|
||||
const initialLabels = useMemo(() => {
|
||||
if (profileData.length === 0) {
|
||||
return new Map<string, string>()
|
||||
}
|
||||
|
||||
const allPubkeys = profileData.map(({ pubkey }) => pubkey)
|
||||
const cachedProfiles = loadCachedProfiles(allPubkeys)
|
||||
const labels = new Map<string, string>()
|
||||
|
||||
profileData.forEach(({ encoded, pubkey }) => {
|
||||
const cachedProfile = cachedProfiles.get(pubkey)
|
||||
if (cachedProfile) {
|
||||
try {
|
||||
const profileData = JSON.parse(cachedProfile.content || '{}') as { name?: string; display_name?: string; nip05?: string }
|
||||
const displayName = profileData.display_name || profileData.name || profileData.nip05
|
||||
if (displayName) {
|
||||
labels.set(encoded, `@${displayName}`)
|
||||
}
|
||||
} catch {
|
||||
// Ignore parsing errors, will fetch later
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return labels
|
||||
}, [profileData])
|
||||
|
||||
const [profileLabels, setProfileLabels] = useState<Map<string, string>>(initialLabels)
|
||||
const lastLoggedSize = useRef<number>(0)
|
||||
|
||||
// Build initial labels: localStorage cache -> eventStore -> fetch from relays
|
||||
useEffect(() => {
|
||||
const startTime = Date.now()
|
||||
console.log(`[${ts()}] [npub-resolve] Building labels, profileData:`, profileData.length, 'hasEventStore:', !!eventStore)
|
||||
console.log(`[${ts()}] [npub-resolve] Building labels, profileData:`, profileData.length, 'hasEventStore:', !!eventStore, 'hasRelayPool:', !!relayPool)
|
||||
|
||||
// Extract all pubkeys
|
||||
const allPubkeys = profileData.map(({ pubkey }) => pubkey)
|
||||
|
||||
if (allPubkeys.length === 0) {
|
||||
console.log(`[${ts()}] [npub-resolve] No pubkeys to resolve, clearing labels`)
|
||||
setProfileLabels(new Map())
|
||||
return
|
||||
}
|
||||
|
||||
// First, check localStorage cache (synchronous, instant)
|
||||
const cacheStartTime = Date.now()
|
||||
const cachedProfiles = loadCachedProfiles(allPubkeys)
|
||||
console.log(`[${ts()}] [npub-resolve] Found in localStorage cache:`, cachedProfiles.size, 'out of', allPubkeys.length)
|
||||
const cacheDuration = Date.now() - cacheStartTime
|
||||
console.log(`[${ts()}] [npub-resolve] Found in localStorage cache:`, cachedProfiles.size, 'out of', allPubkeys.length, 'in', cacheDuration, 'ms')
|
||||
|
||||
// Log which pubkeys were found in cache
|
||||
if (cachedProfiles.size > 0) {
|
||||
cachedProfiles.forEach((_profile, pubkey) => {
|
||||
console.log(`[${ts()}] [npub-resolve] Cached profile found:`, pubkey.slice(0, 16) + '...')
|
||||
})
|
||||
}
|
||||
|
||||
// Add cached profiles to EventStore for consistency
|
||||
if (eventStore) {
|
||||
@@ -71,15 +114,22 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
||||
}
|
||||
}
|
||||
|
||||
// Build initial labels from localStorage cache and eventStore
|
||||
const labels = new Map<string, string>()
|
||||
// Build labels from localStorage cache and eventStore (initialLabels already has cache, add eventStore)
|
||||
// Start with labels from initial cache lookup (in useMemo)
|
||||
const labels = new Map<string, string>(initialLabels)
|
||||
|
||||
const pubkeysToFetch: string[] = []
|
||||
|
||||
profileData.forEach(({ encoded, pubkey }) => {
|
||||
// Skip if already resolved from initial cache
|
||||
if (labels.has(encoded)) {
|
||||
return
|
||||
}
|
||||
|
||||
let profileEvent: { content: string } | null = null
|
||||
let foundSource = ''
|
||||
|
||||
// Check localStorage cache first
|
||||
// Check localStorage cache first (should already be checked in initialLabels, but double-check)
|
||||
const cachedProfile = cachedProfiles.get(pubkey)
|
||||
if (cachedProfile) {
|
||||
profileEvent = cachedProfile
|
||||
@@ -101,9 +151,11 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
||||
labels.set(encoded, `@${displayName}`)
|
||||
console.log(`[${ts()}] [npub-resolve] Found in ${foundSource}:`, encoded.slice(0, 30) + '...', '->', displayName)
|
||||
} else {
|
||||
console.log(`[${ts()}] [npub-resolve] Profile from ${foundSource} has no display name, will fetch:`, pubkey.slice(0, 16) + '...')
|
||||
pubkeysToFetch.push(pubkey)
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error(`[${ts()}] [npub-resolve] Error parsing profile from ${foundSource}:`, err)
|
||||
pubkeysToFetch.push(pubkey)
|
||||
}
|
||||
} else {
|
||||
@@ -112,7 +164,13 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
||||
})
|
||||
|
||||
// Update labels with what we found in localStorage cache and eventStore
|
||||
setProfileLabels(new Map(labels))
|
||||
const initialResolveTime = Date.now() - startTime
|
||||
console.log(`[${ts()}] [npub-resolve] Initial resolution complete:`, labels.size, 'labels resolved in', initialResolveTime, 'ms. Will fetch', pubkeysToFetch.length, 'missing profiles.')
|
||||
|
||||
// Only update if labels changed (avoid unnecessary re-renders)
|
||||
if (labels.size !== profileLabels.size || Array.from(labels.keys()).some(k => labels.get(k) !== profileLabels.get(k))) {
|
||||
setProfileLabels(new Map(labels))
|
||||
}
|
||||
|
||||
// Fetch missing profiles asynchronously
|
||||
if (pubkeysToFetch.length > 0 && relayPool && eventStore) {
|
||||
|
||||
@@ -40,7 +40,8 @@ export function getCachedProfile(pubkey: string): NostrEvent | null {
|
||||
|
||||
return event
|
||||
} catch (err) {
|
||||
// Silently handle cache read errors
|
||||
// Log cache read errors for debugging
|
||||
console.error(`[profile-cache] Error reading cached profile for ${pubkey.slice(0, 16)}...:`, err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -51,7 +52,10 @@ export function getCachedProfile(pubkey: string): NostrEvent | null {
|
||||
*/
|
||||
export function cacheProfile(profile: NostrEvent): void {
|
||||
try {
|
||||
if (profile.kind !== 0) return // Only cache kind:0 (profile) events
|
||||
if (profile.kind !== 0) {
|
||||
console.warn(`[profile-cache] Attempted to cache non-profile event (kind ${profile.kind})`)
|
||||
return // Only cache kind:0 (profile) events
|
||||
}
|
||||
|
||||
const cacheKey = getProfileCacheKey(profile.pubkey)
|
||||
const cached: CachedProfile = {
|
||||
@@ -59,8 +63,11 @@ export function cacheProfile(profile: NostrEvent): void {
|
||||
timestamp: Date.now()
|
||||
}
|
||||
localStorage.setItem(cacheKey, JSON.stringify(cached))
|
||||
console.log(`[profile-cache] Cached profile:`, profile.pubkey.slice(0, 16) + '...')
|
||||
} catch (err) {
|
||||
// Silently fail - don't block the UI if caching fails
|
||||
// Log caching errors for debugging
|
||||
console.error(`[profile-cache] Failed to cache profile ${profile.pubkey.slice(0, 16)}...:`, err)
|
||||
// Don't block the UI if caching fails
|
||||
// Handles quota exceeded, invalid data, and other errors gracefully
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user