Commit Graph

2040 Commits

Author SHA1 Message Date
Gigi
f982781dd8 fix: move OG service files to api/services for Vercel compatibility
- Move ogStore, ogHtml, and articleMeta from src/services/ to api/services/
- Update imports in article-og.ts and article-og-refresh.ts
- Update import paths in articleMeta.ts (lib/profile and src/config/relays)
- Remove old files from src/services/
- Clean up ESLint config to only reference api/**/*.ts

This fixes the ERR_MODULE_NOT_FOUND error on Vercel by ensuring
serverless functions can access the service modules.
2025-11-07 18:52:30 +01:00
Gigi
a73c7db9d3 fix: resolve linting and type errors
- Add ESLint override for Node.js environment in api/ and services files
- Fix WebSocket redeclaration warning in articleMeta.ts
- Fix import path for profile utility (../../lib/profile)
- Install @types/ws for TypeScript support
- Remove unused @ts-expect-error directive
2025-11-07 18:46:24 +01:00
Gigi
c81b7b89d1 feat: implement storage-backed OG previews with Upstash Redis
- Add ogStore service for Redis get/set operations
- Extract shared logic: ogHtml (generateHtml, escapeHtml) and articleMeta (relay/gateway fetching)
- Refactor article-og endpoint to read from Redis, try gateway on miss, trigger background refresh
- Add article-og-refresh endpoint for background relay fetching and caching
- Update vercel.json with refresh function config
- Remove WebSocket dependencies from main OG endpoint for faster crawler responses
2025-11-07 18:41:08 +01:00
Gigi
ed3e8e9799 refactor(shared): move profile helpers to lib and import from API and src\n\n- Fix serverless import resolution by avoiding src/** in API\n- Keep code DRY with single shared module 2025-11-07 16:43:02 +01:00
Gigi
48213fa584 style: add subtle table styling that matches app theme
- Add comprehensive table styles with borders, padding, and spacing
- Style table headers with elevated background
- Add subtle row striping for better readability
- Support text alignment (left, center, right)
- Maintain mobile responsiveness with horizontal scrolling
- Use theme CSS variables for consistent theming across light/dark modes
2025-11-07 15:04:03 +01:00
Gigi
01722cff38 feat: change default nostr gateway to njump.to 2025-11-06 19:12:59 +01:00
Gigi
103e104cb2 chore: remove unused React import from VideoEmbedProcessor 2025-11-05 23:05:14 +01:00
Gigi
5389397e9b fix(mobile): use selectionchange event for immediate text selection detection
Replace mouseup/touchend handlers with selectionchange event listener
for more reliable mobile text selection detection. This fixes the issue
where the highlight button required an extra tap to become active on
mobile devices.

- Extract selection checking logic into shared checkSelection function
- Use selectionchange event with requestAnimationFrame for immediate detection
- Remove onMouseUp and onTouchEnd props from VideoEmbedProcessor
- Simplify code by eliminating separate mouse/touch event handlers
2025-11-05 23:04:38 +01:00
Gigi
da76cb247c perf(highlights): optimize highlight application with multiple improvements
- perf: collect text nodes once instead of per highlight (O(n×m) -> O(n+m))
- fix: correct normalized index mapping algorithm for whitespace handling
- feat: allow nested mark elements for overlapping highlights
- perf: add caching for highlighted HTML results with TTL and size limits
2025-11-03 01:34:02 +01:00
Gigi
a30943686e fix: correct Helpers import path in nostrUriResolver
Helpers should be imported from 'applesauce-core', not 'applesauce-core/helpers'
2025-11-02 23:43:49 +01:00
Gigi
d4b78d9484 refactor: standardize applesauce helpers for npub/nprofile detection
Replace manual type checking and pubkey extraction with getPubkeyFromDecodeResult helper:
- Update getNostrUriLabel to use helper instead of manual npub/nprofile cases
- Update replaceNostrUrisInMarkdownWithProfileLabels to use helper
- Update addLoadingClassToProfileLinks to use helper
- Simplify NostrMentionLink by removing redundant type checks
- Update Bookmarks.tsx to use helper for profile pubkey extraction

This eliminates duplicate logic and ensures consistent handling of npub/nprofile
across the codebase using applesauce helpers.
2025-11-02 23:42:59 +01:00
Gigi
66de230f66 refactor: standardize @ prefix handling and improve npub/nprofile display
- Fix getNpubFallbackDisplay to return names without @ prefix
- Update all call sites to consistently add @ when rendering mentions
- Fix incomplete error handling in getNpubFallbackDisplay catch block
- Add nprofile support to addLoadingClassToProfileLinks
- Extract shared isProfileInCacheOrStore utility to eliminate duplicate loading state checks
- Update ResolvedMention and NostrMentionLink to use shared utility

This ensures consistent @ prefix handling across all profile display contexts
and eliminates code duplication for profile loading state detection.
2025-11-02 23:36:46 +01:00
Gigi
8cb77864bc fix: resolve TypeScript errors in nostrUriResolver.tsx
- Add explicit type annotations for decoded variable and npub parameter
- Use switch statement for better type narrowing when checking npub type
2025-11-02 23:13:37 +01:00
Gigi
ea3c130cc3 chore: remove console.log debug output 2025-11-02 23:11:40 +01:00
Gigi
f417ed8210 fix: resolve race condition in profile label updates
Fix regression where npubs/nprofiles weren't being replaced with profile names.
The issue was a race condition: loading state was cleared immediately, but labels
were applied asynchronously via RAF, causing the condition check to fail.

Changes:
- Apply profile labels immediately when profiles resolve, instead of batching via RAF
- Update condition check to explicitly handle undefined loading state (isLoading !== true)
- This ensures labels are available in the Map when loading becomes false
2025-11-02 23:08:20 +01:00
Gigi
945b9502bc fix: preserve profile labels from pending updates in useEffect
- 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
2025-11-02 23:05:16 +01:00
Gigi
4a432bac8d debug: add logs to trace profile labels batching
- 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
2025-11-02 23:03:33 +01:00
Gigi
541d30764e fix: extract HTML after ReactMarkdown renders processedMarkdown
- 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
2025-11-02 23:02:26 +01:00
Gigi
7c2b373254 debug: add comprehensive shimmer debug logs
- 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
2025-11-02 23:00:32 +01:00
Gigi
0bf33f1a7d debug: add log to verify post-processing adds loading class
- Log when loading class is added to profile links during post-processing
- Will help verify the shimmer is being applied correctly
2025-11-02 22:59:28 +01:00
Gigi
1eca19154d fix: post-process rendered HTML to add loading class to profile links
- 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
2025-11-02 22:57:41 +01:00
Gigi
fd2d4d106f fix: check loading state before resolved labels to show shimmer
- 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
2025-11-02 22:55:14 +01:00
Gigi
d41cbb5305 refactor: use pubkey (hex) as Map key instead of encoded nprofile/npub strings
- 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
2025-11-02 22:52:49 +01:00
Gigi
f57a4d4f1b debug: add key mismatch detection to identify format differences
- 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
2025-11-02 22:50:05 +01:00
Gigi
4b03f32d21 debug: add logs to compare encoded format between markdown extraction and Map keys
- Log the exact encoded value being processed
- Log sample of Map keys for comparison
- Will help identify format mismatch between markdown and Map storage
2025-11-02 22:48:52 +01:00
Gigi
8f1288b1a2 debug: add detailed logs to nostr URI resolver for loading state detection
- 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
2025-11-02 22:47:43 +01:00
Gigi
7ec87b66d8 fix: reduce markdown reprocessing to prevent flicker
- 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
2025-11-02 22:42:03 +01:00
Gigi
27dde5afa2 debug: add common prefix [profile-loading-debug] to all debug logs
All profile loading related debug logs now have the common prefix for easy filtering in console.
2025-11-02 22:40:08 +01:00
Gigi
3b2732681d fix: remove unused variables in debug log filter 2025-11-02 22:39:24 +01:00
Gigi
51a4b545e9 debug: add comprehensive logging for profile loading states and article refresh
- 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
2025-11-02 22:39:07 +01:00
Gigi
7e5972a6e2 fix: correct Hooks import path from applesauce-react
- Import Hooks from 'applesauce-react' instead of 'applesauce-react/hooks'
- Fixes TypeScript errors in ResolvedMention and NostrMentionLink
2025-11-02 22:30:37 +01:00
Gigi
156cf31625 feat: add loading states for profile lookups in articles
- 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
2025-11-02 22:29:35 +01:00
Gigi
ee7df54d87 refactor(profiles): standardize profile name extraction and improve code quality
- Create centralized profileUtils.ts with extractProfileDisplayName function
- Standardize profile name priority order: name || display_name || nip05 || fallback
- Replace duplicate profile parsing code across 6+ locations
- Add request deduplication to fetchProfiles to prevent duplicate relay requests
- Simplify RAF batching logic in useProfileLabels with helper functions
- Fix RichContent.tsx error when content.split() produces undefined parts
- Remove unused eventCount variable in profileService
- Fix React Hook dependency warnings by wrapping scheduleBatchedUpdate in useCallback
2025-11-02 22:21:43 +01:00
Gigi
15c016ad5e chore: remove console.log debug output from profile services and hooks 2025-11-02 22:07:33 +01:00
Gigi
b0574d3f8e fix: resolve React hooks exhaustive-deps linter warning
- 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
2025-11-02 22:05:08 +01:00
Gigi
4fd6605666 fix: ensure profile labels always update correctly
- 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.
2025-11-02 22:01:35 +01:00
Gigi
76a117cdda fix: batch profile label updates to prevent UI flickering
- 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.
2025-11-02 21:53:22 +01:00
Gigi
d4c6747d98 refactor: remove timeouts and make profile fetching reactive
- 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.'
2025-11-02 21:48:39 +01:00
Gigi
6b221e4d13 perf: increase remote relay timeout for profile fetches
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.
2025-11-02 21:43:00 +01:00
Gigi
7ec2ddcceb debug: add log before fetchProfiles call to verify it's being invoked 2025-11-02 21:42:07 +01:00
Gigi
5ce13c667d feat: ensure purplepag.es relay is used for profile lookups
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.
2025-11-02 21:41:03 +01:00
Gigi
c1877a40e9 debug: add detailed logging to fetchProfiles function
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.
2025-11-02 21:40:14 +01:00
Gigi
18a38d054f debug: add comprehensive logging to profile label resolution
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.
2025-11-02 21:37:14 +01:00
Gigi
500cec88d0 fix: allow profile labels to update from fallback to resolved names
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.
2025-11-02 21:34:57 +01:00
Gigi
affd80ca2e refactor: standardize profile display name fallbacks across codebase
- 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
2025-11-02 21:31:16 +01:00
Gigi
5e1ed6b8de refactor: clean up npub/nprofile display implementation
- 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
2025-11-02 21:26:06 +01:00
Gigi
5d36d6de4f refactor: add logging to verify initialLabels are set correctly from cache 2025-11-02 21:10:56 +01:00
Gigi
93eb8a63de fix: implement LRU cache eviction to handle QuotaExceededError
- 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.
2025-11-02 21:09:11 +01:00
Gigi
6074caaae3 refactor: change profile-cache log prefix to npub-cache for consistency 2025-11-02 21:07:44 +01:00
Gigi
d206ff228e fix: remove unnecessary label comparison and fix useEffect dependencies 2025-11-02 21:06:40 +01:00