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
This commit is contained in:
Gigi
2025-11-02 22:57:41 +01:00
parent fd2d4d106f
commit 1eca19154d
2 changed files with 53 additions and 11 deletions

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, useMemo } from 'react'
import { RelayPool } from 'applesauce-relay'
import { extractNaddrUris, replaceNostrUrisInMarkdownWithProfileLabels } from '../utils/nostrUriResolver'
import { extractNaddrUris, replaceNostrUrisInMarkdownWithProfileLabels, addLoadingClassToProfileLinks } from '../utils/nostrUriResolver'
import { fetchArticleTitles } from '../services/articleTitleResolver'
import { useProfileLabels } from './useProfileLabels'
@@ -138,7 +138,9 @@ export const useMarkdownToHTML = (
const rafId = requestAnimationFrame(() => {
if (previewRef.current && !isCancelled) {
const html = previewRef.current.innerHTML
let html = previewRef.current.innerHTML
// Post-process HTML to add loading class to profile links
html = addLoadingClassToProfileLinks(html, profileLoadingRef.current)
setRenderedHtml(html)
} else if (!isCancelled) {
console.warn('⚠️ markdownPreviewRef.current is null')

View File

@@ -338,20 +338,15 @@ export function replaceNostrUrisInMarkdownWithProfileLabels(
if (decoded.type === 'npub' || decoded.type === 'nprofile') {
const pubkey = decoded.type === 'npub' ? decoded.data : decoded.data.pubkey
// Check loading state FIRST - show loading even if we have a fallback label
const isLoading = profileLoading.get(pubkey)
if (isLoading === true) {
const label = getNostrUriLabel(encoded)
console.log(`[profile-loading-debug][nostr-uri-resolve] ${pubkey.slice(0, 16)}... is LOADING, showing loading state`)
// Wrap in span with profile-loading class for CSS styling
return `[<span class="profile-loading">${label}</span>](${link})`
}
// Check if we have a resolved profile name using pubkey as key
if (profileLabels.has(pubkey)) {
const displayName = profileLabels.get(pubkey)!
return `[${displayName}](${link})`
}
// If no resolved label yet, use fallback (will show loading via post-processing)
const label = getNostrUriLabel(encoded)
return `[${label}](${link})`
}
} catch (error) {
// Ignore decode errors, fall through to default label
@@ -363,6 +358,51 @@ export function replaceNostrUrisInMarkdownWithProfileLabels(
})
}
/**
* Post-process rendered HTML to add loading class to profile links that are still loading
* This is necessary because HTML inside markdown links doesn't render correctly
* @param html The rendered HTML string
* @param profileLoading Map of pubkey (hex) -> boolean indicating if profile is loading
* @returns HTML with profile-loading class added to loading profile links
*/
export function addLoadingClassToProfileLinks(
html: string,
profileLoading: Map<string, boolean>
): string {
if (profileLoading.size === 0) return html
// Find all <a> tags with href starting with /p/ (profile links)
return html.replace(/<a\s+[^>]*?href="\/p\/([^"]+)"[^>]*?>/g, (match, npub) => {
try {
// Decode npub to get pubkey
const decoded = decode(npub)
if (decoded.type !== 'npub') return match
const pubkey = decoded.data
// Check if this profile is loading
if (profileLoading.get(pubkey) === true) {
// Add profile-loading class if not already present
if (!match.includes('profile-loading')) {
// Insert class before the closing >
const classMatch = /class="([^"]*)"/.exec(match)
if (classMatch) {
// Update existing class attribute
return match.replace(/class="([^"]*)"/, `class="$1 profile-loading"`)
} else {
// Add new class attribute
return match.replace(/(<a\s+[^>]*?)>/, '$1 class="profile-loading">')
}
}
}
} catch (error) {
// If decoding fails, just return the original match
}
return match
})
}
/**
* Replace nostr: URIs in HTML with clickable links
* This is used when processing HTML content directly