- Fix merge logic in useEffect that syncs profileLabels state
- Previously was overwriting newly resolved labels when initialLabels changed
- Now preserves existing labels and only adds missing ones from initialLabels
- This fixes the issue where profileLabels was being reset to 0 after applyPendingUpdates
- Add debug logs to track when useEffect sync runs
- Add debug logs in applyPendingUpdates to see when updates are applied
- Add debug logs in scheduleBatchedUpdate to track RAF scheduling
- Add debug logs when adding to pending updates
- Add debug logs for profileLabelsKey computation to verify state updates
- Will help diagnose why profileLabels stays at size 0 despite profiles resolving
- Separate markdown processing from HTML extraction
- Add useEffect that watches processedMarkdown and extracts HTML
- Use double RAF to ensure ReactMarkdown has finished rendering before extracting
- This fixes the issue where resolved profile names weren't updating in the article view
- Add debug logs to track HTML extraction after processedMarkdown changes
- Add [shimmer-debug] prefixed logs to trace loading state flow
- Log when profiles are marked as loading in useProfileLabels
- Log when loading state is cleared after profile resolution
- Log detailed post-processing steps in addLoadingClassToProfileLinks
- Log markdown replacement decisions in replaceNostrUrisInMarkdownWithProfileLabels
- Log HTML changes and class counts in useMarkdownToHTML
- All logs use [shimmer-debug] prefix for easy filtering
- HTML inside markdown links doesn't render correctly with rehype-raw
- Instead, post-process rendered HTML to find profile links (/p/npub...)
- Decode npub to get pubkey and check loading state
- Add profile-loading class directly to <a> tags
- This ensures the loading shimmer appears on the actual link element
- Check loading state FIRST before checking for resolved labels
- Profiles have fallback labels immediately, which caused early return
- Now loading shimmer will show even when fallback label exists
- This fixes the issue where shimmer never appeared
- Changed useProfileLabels to use pubkey as key for canonical identification
- Updated replaceNostrUrisInMarkdownWithProfileLabels to extract pubkey and use it for lookup
- This fixes the key mismatch issue where different nprofile encodings map to the same pubkey
- Multiple nprofile strings can refer to the same pubkey (different relay hints)
- Using pubkey as key is the Nostr standard way to identify profiles
- Check if encoded value from regex matches Map keys
- Log full comparison when mismatch detected
- Will help identify if regex capture group format differs from Map storage format
- Log the exact encoded value being processed
- Log sample of Map keys for comparison
- Will help identify format mismatch between markdown and Map storage
- Log when replacement function is called with Map sizes
- Log all loading keys in the Map
- Log detailed info for each npub/nprofile found: type, hasLoading, isLoading
- Will help identify if encoded IDs don't match or loading state isn't detected
- Use stable string keys instead of Map objects as dependencies
- Only clear rendered HTML when markdown content actually changes
- Use refs to access latest Map values without triggering re-renders
- Prevents excessive markdown reprocessing on every profile update
- Should significantly reduce screen flickering during profile resolution
- Add logs to useProfileLabels for loading state tracking
- Add logs to markdown processing to track when content is cleared/reprocessed
- Add logs to article loader for refresh behavior
- Add logs to ResolvedMention and NostrMentionLink for loading detection
- Add logs to nostr URI resolver when loading state is shown
- All logs prefixed with meaningful tags for easy filtering
- Extend useProfileLabels to return loading Map alongside labels
- Update markdown replacement to show loading indicator for unresolved profiles
- Add loading state detection to ResolvedMention and NostrMentionLink components
- Add CSS animation for profile-loading class with opacity pulse
- Respect prefers-reduced-motion for accessibility
- Capture refs at effect level and use in cleanup function
- This satisfies react-hooks/exhaustive-deps rule for cleanup functions
- Prevents stale closure issues while keeping code clean
- Sync state when initialLabels changes (e.g., content changes)
- Flush pending batched updates after EOSE completes
- Flush pending updates in cleanup to avoid losing updates
- Better handling of profile data changes vs same profiles
Fixes issue where @npub... placeholders sometimes weren't replaced
until refresh. Now all profile updates are guaranteed to be applied.
- Use requestAnimationFrame to batch rapid profile label updates
- Collect pending updates in a ref instead of updating state immediately
- Apply all pending updates in one render cycle
- Add cleanup to cancel pending RAF on unmount/effect cleanup
This prevents flickering when multiple profiles stream in quickly while
still maintaining progressive updates as profiles arrive.
- Add optional onEvent callback to fetchProfiles (following queryEvents pattern)
- Remove all timeouts - rely entirely on EOSE signals
- Update useProfileLabels to use reactive streaming callback
- Labels update progressively as profiles arrive from relays
- Remove unused timer/takeUntil imports
- Backwards compatible: other callers of fetchProfiles still work
This follows the controller pattern from fetching-data-with-controllers rule:
'Since we are streaming results, we should NEVER use timeouts for fetching
data. We should always rely on EOSE.'
Increase timeout from 6s to 10s to give slow relays (including purplepag.es)
more time to respond with profile metadata. This may help find profiles that
were timing out before.
Add logic to check if purplepag.es is in the active relay pool when fetching
profiles. If not, add it temporarily to ensure we query this relay for
profile metadata. This should help find profiles that might not be available
on other relays.
Also adds debug logging to show which active relays are being queried.
Add comprehensive logs prefixed with [fetch-profiles] to track:
- How many profiles are requested
- Cache lookup results
- Relay query configuration
- Each profile event as it's received
- Summary of fetched vs missing profiles
- Which profiles weren't found on relays
This will help diagnose why only 9/19 profiles are being returned.
Add detailed debug logs prefixed with [profile-labels] and [markdown-replace]
to track the profile resolution flow:
- Profile identifier extraction from content
- Cache lookup and eventStore checks
- Profile fetching from relays
- Label updates when profiles resolve
- Markdown URI replacement with profile labels
This will help diagnose why profile names aren't resolving correctly.
Previously, useProfileLabels would set fallback npub labels immediately for
missing profiles, then skip updating them when profiles were fetched because
the condition checked if the label already existed.
Now we track which profiles were being fetched (pubkeysToFetch) and update
their labels even if they already have fallback labels set, allowing profiles
to resolve progressively from fallback npubs to actual names as they load.
- Add getProfileDisplayName() utility function for consistent profile name resolution
- Update all components to use standardized npub fallback format instead of hex
- Fix useProfileLabels hook to include fallback npub labels when profiles lack names
- Refactor NostrMentionLink to eliminate duplication between npub/nprofile cases
- Remove debug console.log statements from RichContent component
- Update AuthorCard, SidebarHeader, HighlightItem, Support, BlogPostCard, ResolvedMention, and useEventLoader to use new utilities
- Remove all debug console.log/error statements (39+) and ts() helpers
- Eliminate redundant localStorage cache check in useProfileLabels
- Standardize fallback display format using getNpubFallbackDisplay() utility
- Update ResolvedMention to use npub format consistently
- Add LRU eviction strategy: limit to 1000 cached profiles, evict oldest when full
- Track lastAccessed timestamp for each cached profile
- Automatically evict old profiles when quota is exceeded
- Reduce error logging spam: only log quota error once per session
- Silently handle cache errors to match articleService pattern
- Proactively evict before caching when approaching limit
This prevents localStorage quota exceeded errors and ensures
the most recently accessed profiles remain cached.
- 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.
- Add localStorage caching functions to profileService.ts following articleService.ts pattern
- getCachedProfile: get single cached profile with TTL validation (30 days)
- cacheProfile: save profile to localStorage with error handling
- loadCachedProfiles: batch load multiple profiles from cache
- Modify fetchProfiles() to check localStorage cache first, only fetch missing/expired profiles, and cache fetched profiles
- Update useProfileLabels hook to check localStorage before EventStore, add cached profiles to EventStore for consistency
- Update logging to show cache hits from localStorage
- Benefits: instant profile resolution on page reload, reduced relay queries, offline support for previously-seen profiles
- Add duration tracking for fetchProfiles (shows how long it takes)
- Add total time tracking for entire resolution process
- Reduce log noise by only logging when profileLabels size changes
- Helps identify performance bottlenecks
- Add timestamp helper function (HH:mm:ss.SSS format)
- Update all console.log/error statements to include timestamps
- Helps identify timing bottlenecks in profile resolution
- fetchProfiles returns profiles that we should use immediately
- Check returned array first, then fallback to eventStore lookup
- Fixes issue where profiles were returned but not used for resolution
- Log fetchProfiles return count
- Log profile events found in store vs missing
- Log profiles with names vs without names
- Help diagnose why 0 profiles are being resolved
- Poll eventStore every 200ms for up to 2 seconds after fetchProfiles
- Accumulate resolved labels across checks instead of resetting
- Add detailed logging to diagnose why profiles aren't resolving
- Fixes issue where profiles arrive asynchronously after fetchProfiles completes
- After fetchProfiles completes, re-check eventStore for all profiles
- This ensures profiles are resolved even if fetchProfiles returns partial results
- Fixes issue where only 5 out of 19 profiles were being resolved
- Show @derggg instead of @npub1derggg for truncated npubs
- Update getNostrUriLabel to skip first 5 chars ('npub1')
- Update NostrMentionLink fallback display to match
- Add logs to useProfileLabels hook
- Add logs to useMarkdownToHTML hook
- Add logs to RichContent component
- Add logs to extractNostrUris function
- Add error handling with fallbacks