mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 06:34:24 +01:00
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
This commit is contained in:
@@ -5,6 +5,7 @@ import { faUserCircle } from '@fortawesome/free-solid-svg-icons'
|
|||||||
import { useEventModel } from 'applesauce-react/hooks'
|
import { useEventModel } from 'applesauce-react/hooks'
|
||||||
import { Models } from 'applesauce-core'
|
import { Models } from 'applesauce-core'
|
||||||
import { nip19 } from 'nostr-tools'
|
import { nip19 } from 'nostr-tools'
|
||||||
|
import { getProfileDisplayName } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
interface AuthorCardProps {
|
interface AuthorCardProps {
|
||||||
authorPubkey: string
|
authorPubkey: string
|
||||||
@@ -16,9 +17,7 @@ const AuthorCard: React.FC<AuthorCardProps> = ({ authorPubkey, clickable = true
|
|||||||
const profile = useEventModel(Models.ProfileModel, [authorPubkey])
|
const profile = useEventModel(Models.ProfileModel, [authorPubkey])
|
||||||
|
|
||||||
const getAuthorName = () => {
|
const getAuthorName = () => {
|
||||||
if (profile?.name) return profile.name
|
return getProfileDisplayName(profile, authorPubkey)
|
||||||
if (profile?.display_name) return profile.display_name
|
|
||||||
return `${authorPubkey.slice(0, 8)}...${authorPubkey.slice(-8)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const authorImage = profile?.picture || profile?.image
|
const authorImage = profile?.picture || profile?.image
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { BlogPostPreview } from '../services/exploreService'
|
|||||||
import { useEventModel } from 'applesauce-react/hooks'
|
import { useEventModel } from 'applesauce-react/hooks'
|
||||||
import { Models } from 'applesauce-core'
|
import { Models } from 'applesauce-core'
|
||||||
import { isKnownBot } from '../config/bots'
|
import { isKnownBot } from '../config/bots'
|
||||||
|
import { getProfileDisplayName } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
interface BlogPostCardProps {
|
interface BlogPostCardProps {
|
||||||
post: BlogPostPreview
|
post: BlogPostPreview
|
||||||
@@ -24,8 +25,7 @@ const BlogPostCard: React.FC<BlogPostCardProps> = ({ post, href, level, readingP
|
|||||||
// No need to preload all images at once - this causes ERR_INSUFFICIENT_RESOURCES
|
// No need to preload all images at once - this causes ERR_INSUFFICIENT_RESOURCES
|
||||||
// when there are many blog posts.
|
// when there are many blog posts.
|
||||||
|
|
||||||
const displayName = profile?.name || profile?.display_name ||
|
const displayName = getProfileDisplayName(profile, post.author)
|
||||||
`${post.author.slice(0, 8)}...${post.author.slice(-4)}`
|
|
||||||
const rawName = (profile?.name || profile?.display_name || '').toLowerCase()
|
const rawName = (profile?.name || profile?.display_name || '').toLowerCase()
|
||||||
|
|
||||||
// Hide bot authors by name/display_name
|
// Hide bot authors by name/display_name
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import CompactButton from './CompactButton'
|
|||||||
import { HighlightCitation } from './HighlightCitation'
|
import { HighlightCitation } from './HighlightCitation'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import NostrMentionLink from './NostrMentionLink'
|
import NostrMentionLink from './NostrMentionLink'
|
||||||
|
import { getProfileDisplayName } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
// Helper to detect if a URL is an image
|
// Helper to detect if a URL is an image
|
||||||
const isImageUrl = (url: string): boolean => {
|
const isImageUrl = (url: string): boolean => {
|
||||||
@@ -127,9 +128,7 @@ export const HighlightItem: React.FC<HighlightItemProps> = ({
|
|||||||
|
|
||||||
// Get display name for the user
|
// Get display name for the user
|
||||||
const getUserDisplayName = () => {
|
const getUserDisplayName = () => {
|
||||||
if (profile?.name) return profile.name
|
return getProfileDisplayName(profile, highlight.pubkey)
|
||||||
if (profile?.display_name) return profile.display_name
|
|
||||||
return `${highlight.pubkey.slice(0, 8)}...` // fallback to short pubkey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react'
|
|||||||
import { nip19 } from 'nostr-tools'
|
import { nip19 } from 'nostr-tools'
|
||||||
import { useEventModel } from 'applesauce-react/hooks'
|
import { useEventModel } from 'applesauce-react/hooks'
|
||||||
import { Models, Helpers } from 'applesauce-core'
|
import { Models, Helpers } from 'applesauce-core'
|
||||||
|
import { getProfileDisplayName } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
const { getPubkeyFromDecodeResult } = Helpers
|
const { getPubkeyFromDecodeResult } = Helpers
|
||||||
|
|
||||||
@@ -46,14 +47,10 @@ const NostrMentionLink: React.FC<NostrMentionLinkProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render based on decoded type
|
// Helper function to render profile links (used for both npub and nprofile)
|
||||||
switch (decoded.type) {
|
const renderProfileLink = (pubkey: string) => {
|
||||||
case 'npub': {
|
const npub = nip19.npubEncode(pubkey)
|
||||||
const pk = decoded.data
|
const displayName = getProfileDisplayName(profile, pubkey)
|
||||||
const npub = nip19.npubEncode(pk)
|
|
||||||
// Fallback: show npub without "npub1" prefix
|
|
||||||
const fallbackDisplay = `@${npub.slice(5, 12)}...`
|
|
||||||
const displayName = profile?.name || profile?.display_name || profile?.nip05 || fallbackDisplay
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
@@ -65,22 +62,16 @@ const NostrMentionLink: React.FC<NostrMentionLinkProps> = ({
|
|||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render based on decoded type
|
||||||
|
switch (decoded.type) {
|
||||||
|
case 'npub': {
|
||||||
|
const pk = decoded.data
|
||||||
|
return renderProfileLink(pk)
|
||||||
|
}
|
||||||
case 'nprofile': {
|
case 'nprofile': {
|
||||||
const { pubkey: pk } = decoded.data
|
const { pubkey: pk } = decoded.data
|
||||||
const npub = nip19.npubEncode(pk)
|
return renderProfileLink(pk)
|
||||||
// Fallback: show npub without "npub1" prefix
|
|
||||||
const fallbackDisplay = `@${npub.slice(5, 12)}...`
|
|
||||||
const displayName = profile?.name || profile?.display_name || profile?.nip05 || fallbackDisplay
|
|
||||||
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
href={`/p/${npub}`}
|
|
||||||
className={className}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
@{displayName}
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
case 'naddr': {
|
case 'naddr': {
|
||||||
const { kind, pubkey: pk, identifier: addrIdentifier } = decoded.data
|
const { kind, pubkey: pk, identifier: addrIdentifier } = decoded.data
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom'
|
|||||||
import { useEventModel } from 'applesauce-react/hooks'
|
import { useEventModel } from 'applesauce-react/hooks'
|
||||||
import { Models, Helpers } from 'applesauce-core'
|
import { Models, Helpers } from 'applesauce-core'
|
||||||
import { decode, npubEncode } from 'nostr-tools/nip19'
|
import { decode, npubEncode } from 'nostr-tools/nip19'
|
||||||
import { getNpubFallbackDisplay } from '../utils/nostrUriResolver'
|
import { getProfileDisplayName } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
const { getPubkeyFromDecodeResult } = Helpers
|
const { getPubkeyFromDecodeResult } = Helpers
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ const ResolvedMention: React.FC<ResolvedMentionProps> = ({ encoded }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const profile = pubkey ? useEventModel(Models.ProfileModel, [pubkey]) : undefined
|
const profile = pubkey ? useEventModel(Models.ProfileModel, [pubkey]) : undefined
|
||||||
const display = profile?.name || profile?.display_name || profile?.nip05 || (pubkey ? getNpubFallbackDisplay(pubkey) : encoded)
|
const display = pubkey ? getProfileDisplayName(profile, pubkey) : encoded
|
||||||
const npub = pubkey ? npubEncode(pubkey) : undefined
|
const npub = pubkey ? npubEncode(pubkey) : undefined
|
||||||
|
|
||||||
if (npub) {
|
if (npub) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react'
|
|||||||
import NostrMentionLink from './NostrMentionLink'
|
import NostrMentionLink from './NostrMentionLink'
|
||||||
import { Tokens } from 'applesauce-content/helpers'
|
import { Tokens } from 'applesauce-content/helpers'
|
||||||
|
|
||||||
// Helper to add timestamps to logs
|
// Helper to add timestamps to error logs
|
||||||
const ts = () => {
|
const ts = () => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const ms = now.getMilliseconds().toString().padStart(3, '0')
|
const ms = now.getMilliseconds().toString().padStart(3, '0')
|
||||||
@@ -26,8 +26,6 @@ const RichContent: React.FC<RichContentProps> = ({
|
|||||||
content,
|
content,
|
||||||
className = 'bookmark-content'
|
className = 'bookmark-content'
|
||||||
}) => {
|
}) => {
|
||||||
console.log(`[${ts()}] [npub-resolve] RichContent: Rendering, content length:`, content?.length || 0)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Pattern to match:
|
// Pattern to match:
|
||||||
// 1. nostr: URIs (nostr:npub1..., nostr:note1..., etc.) using applesauce Tokens.nostrLink
|
// 1. nostr: URIs (nostr:npub1..., nostr:note1..., etc.) using applesauce Tokens.nostrLink
|
||||||
@@ -37,7 +35,6 @@ const RichContent: React.FC<RichContentProps> = ({
|
|||||||
const combinedPattern = new RegExp(`(${nostrPattern.source}|${urlPattern.source})`, 'gi')
|
const combinedPattern = new RegExp(`(${nostrPattern.source}|${urlPattern.source})`, 'gi')
|
||||||
|
|
||||||
const parts = content.split(combinedPattern)
|
const parts = content.split(combinedPattern)
|
||||||
console.log(`[${ts()}] [npub-resolve] RichContent: Split into parts:`, parts.length)
|
|
||||||
|
|
||||||
// Helper to check if a string is a nostr identifier (without mutating regex state)
|
// Helper to check if a string is a nostr identifier (without mutating regex state)
|
||||||
const isNostrIdentifier = (str: string): boolean => {
|
const isNostrIdentifier = (str: string): boolean => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Models } from 'applesauce-core'
|
|||||||
import IconButton from './IconButton'
|
import IconButton from './IconButton'
|
||||||
import { faBooks } from '../icons/customIcons'
|
import { faBooks } from '../icons/customIcons'
|
||||||
import { preloadImage } from '../hooks/useImageCache'
|
import { preloadImage } from '../hooks/useImageCache'
|
||||||
|
import { getProfileDisplayName } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
interface SidebarHeaderProps {
|
interface SidebarHeaderProps {
|
||||||
onToggleCollapse: () => void
|
onToggleCollapse: () => void
|
||||||
@@ -29,10 +30,7 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogou
|
|||||||
|
|
||||||
const getUserDisplayName = () => {
|
const getUserDisplayName = () => {
|
||||||
if (!activeAccount) return 'Unknown User'
|
if (!activeAccount) return 'Unknown User'
|
||||||
if (profile?.name) return profile.name
|
return getProfileDisplayName(profile, activeAccount.pubkey)
|
||||||
if (profile?.display_name) return profile.display_name
|
|
||||||
if (profile?.nip05) return profile.nip05
|
|
||||||
return `${activeAccount.pubkey.slice(0, 8)}...${activeAccount.pubkey.slice(-8)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const profileImage = getProfileImage()
|
const profileImage = getProfileImage()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { Models } from 'applesauce-core'
|
|||||||
import { useEventModel } from 'applesauce-react/hooks'
|
import { useEventModel } from 'applesauce-react/hooks'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { nip19 } from 'nostr-tools'
|
import { nip19 } from 'nostr-tools'
|
||||||
|
import { getProfileDisplayName } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
interface SupportProps {
|
interface SupportProps {
|
||||||
relayPool: RelayPool
|
relayPool: RelayPool
|
||||||
@@ -182,7 +183,7 @@ const SupporterCard: React.FC<SupporterCardProps> = ({ supporter, isWhale }) =>
|
|||||||
const profile = useEventModel(Models.ProfileModel, [supporter.pubkey])
|
const profile = useEventModel(Models.ProfileModel, [supporter.pubkey])
|
||||||
|
|
||||||
const picture = profile?.picture
|
const picture = profile?.picture
|
||||||
const name = profile?.name || profile?.display_name || `${supporter.pubkey.slice(0, 8)}...`
|
const name = getProfileDisplayName(profile, supporter.pubkey)
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
const npub = nip19.npubEncode(supporter.pubkey)
|
const npub = nip19.npubEncode(supporter.pubkey)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ReadableContent } from '../services/readerService'
|
|||||||
import { eventManager } from '../services/eventManager'
|
import { eventManager } from '../services/eventManager'
|
||||||
import { fetchProfiles } from '../services/profileService'
|
import { fetchProfiles } from '../services/profileService'
|
||||||
import { useDocumentTitle } from './useDocumentTitle'
|
import { useDocumentTitle } from './useDocumentTitle'
|
||||||
|
import { getNpubFallbackDisplay } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
interface UseEventLoaderProps {
|
interface UseEventLoaderProps {
|
||||||
eventId?: string
|
eventId?: string
|
||||||
@@ -40,7 +41,7 @@ export function useEventLoader({
|
|||||||
// Initial title
|
// Initial title
|
||||||
let title = `Note (${event.kind})`
|
let title = `Note (${event.kind})`
|
||||||
if (event.kind === 1) {
|
if (event.kind === 1) {
|
||||||
title = `Note by @${event.pubkey.slice(0, 8)}...`
|
title = `Note by ${getNpubFallbackDisplay(event.pubkey)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit immediately
|
// Emit immediately
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Helpers, IEventStore } from 'applesauce-core'
|
|||||||
import { getContentPointers } from 'applesauce-factory/helpers'
|
import { getContentPointers } from 'applesauce-factory/helpers'
|
||||||
import { RelayPool } from 'applesauce-relay'
|
import { RelayPool } from 'applesauce-relay'
|
||||||
import { fetchProfiles, loadCachedProfiles } from '../services/profileService'
|
import { fetchProfiles, loadCachedProfiles } from '../services/profileService'
|
||||||
|
import { getNpubFallbackDisplay } from '../utils/nostrUriResolver'
|
||||||
|
|
||||||
const { getPubkeyFromDecodeResult, encodeDecodeResult } = Helpers
|
const { getPubkeyFromDecodeResult, encodeDecodeResult } = Helpers
|
||||||
|
|
||||||
@@ -55,9 +56,15 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
|||||||
const displayName = profileData.display_name || profileData.name || profileData.nip05
|
const displayName = profileData.display_name || profileData.name || profileData.nip05
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
labels.set(encoded, `@${displayName}`)
|
labels.set(encoded, `@${displayName}`)
|
||||||
|
} else {
|
||||||
|
// Use fallback npub display if profile has no name
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
labels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore parsing errors, will fetch later
|
// Use fallback npub display if parsing fails
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
labels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -113,16 +120,30 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
|||||||
if (displayName) {
|
if (displayName) {
|
||||||
labels.set(encoded, `@${displayName}`)
|
labels.set(encoded, `@${displayName}`)
|
||||||
} else {
|
} else {
|
||||||
pubkeysToFetch.push(pubkey)
|
// Use fallback npub display if profile has no name
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
labels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
pubkeysToFetch.push(pubkey)
|
// Use fallback npub display if parsing fails
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
labels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// No profile found yet, will use fallback after fetch or keep empty
|
||||||
|
// We'll set fallback labels for missing profiles at the end
|
||||||
pubkeysToFetch.push(pubkey)
|
pubkeysToFetch.push(pubkey)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Set fallback labels for profiles that weren't found
|
||||||
|
profileData.forEach(({ encoded, pubkey }) => {
|
||||||
|
if (!labels.has(encoded)) {
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
labels.set(encoded, fallback)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
setProfileLabels(new Map(labels))
|
setProfileLabels(new Map(labels))
|
||||||
|
|
||||||
// Fetch missing profiles asynchronously
|
// Fetch missing profiles asynchronously
|
||||||
@@ -142,9 +163,15 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
|||||||
const displayName = profileData.display_name || profileData.name || profileData.nip05
|
const displayName = profileData.display_name || profileData.name || profileData.nip05
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
updatedLabels.set(encoded, `@${displayName}`)
|
updatedLabels.set(encoded, `@${displayName}`)
|
||||||
|
} else {
|
||||||
|
// Use fallback npub display if profile has no name
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
updatedLabels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore parsing errors
|
// Use fallback npub display if parsing fails
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
updatedLabels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
} else if (eventStore) {
|
} else if (eventStore) {
|
||||||
// Fallback: check eventStore (in case fetchProfiles stored but didn't return)
|
// Fallback: check eventStore (in case fetchProfiles stored but didn't return)
|
||||||
@@ -155,11 +182,25 @@ export function useProfileLabels(content: string, relayPool?: RelayPool | null):
|
|||||||
const displayName = profileData.display_name || profileData.name || profileData.nip05
|
const displayName = profileData.display_name || profileData.name || profileData.nip05
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
updatedLabels.set(encoded, `@${displayName}`)
|
updatedLabels.set(encoded, `@${displayName}`)
|
||||||
|
} else {
|
||||||
|
// Use fallback npub display if profile has no name
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
updatedLabels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore parsing errors
|
// Use fallback npub display if parsing fails
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
updatedLabels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// No profile found, use fallback
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
updatedLabels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// No eventStore, use fallback
|
||||||
|
const fallback = getNpubFallbackDisplay(pubkey)
|
||||||
|
updatedLabels.set(encoded, fallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -134,6 +134,23 @@ export function getNpubFallbackDisplay(pubkey: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get display name for a profile with consistent priority order
|
||||||
|
* Returns: profile.name || profile.display_name || profile.nip05 || npub fallback
|
||||||
|
* @param profile Profile object with optional name, display_name, and nip05 fields
|
||||||
|
* @param pubkey The pubkey in hex format (required for fallback)
|
||||||
|
* @returns Display name string
|
||||||
|
*/
|
||||||
|
export function getProfileDisplayName(
|
||||||
|
profile: { name?: string; display_name?: string; nip05?: string } | null | undefined,
|
||||||
|
pubkey: string
|
||||||
|
): string {
|
||||||
|
if (profile?.name) return profile.name
|
||||||
|
if (profile?.display_name) return profile.display_name
|
||||||
|
if (profile?.nip05) return profile.nip05
|
||||||
|
return getNpubFallbackDisplay(pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process markdown to replace nostr URIs while skipping those inside markdown links
|
* Process markdown to replace nostr URIs while skipping those inside markdown links
|
||||||
* This prevents nested markdown link issues when nostr identifiers appear in URLs
|
* This prevents nested markdown link issues when nostr identifiers appear in URLs
|
||||||
|
|||||||
Reference in New Issue
Block a user