Browse Source

more parsing updates

imwald
Silberengel 5 months ago
parent
commit
88bcaf3e15
  1. 2
      src/components/ContentPreview/Content.tsx
  2. 1
      src/components/Embedded/EmbeddedMention.tsx
  3. 12
      src/components/Note/index.tsx
  4. 20
      src/components/Username/index.tsx
  5. 23
      src/lib/nostr-parser.tsx
  6. 13
      src/pages/secondary/NotePage/index.tsx

2
src/components/ContentPreview/Content.tsx

@ -32,7 +32,7 @@ export default function Content({
}, [content]) }, [content])
return ( return (
<span className={cn('pointer-events-none', className)}> <span className={cn(className)}>
{nodes.map((node, index) => { {nodes.map((node, index) => {
if (node.type === 'image' || node.type === 'images') { if (node.type === 'image' || node.type === 'images') {
return index > 0 ? ` [${t('Image')}]` : `[${t('Image')}]` return index > 0 ? ` [${t('Image')}]` : `[${t('Image')}]`

1
src/components/Embedded/EmbeddedMention.tsx

@ -7,6 +7,7 @@ export function EmbeddedMention({ userId, className }: { userId: string; classNa
userId={userId} userId={userId}
showAt showAt
className={cn('text-primary font-normal inline', className)} className={cn('text-primary font-normal inline', className)}
style={{ verticalAlign: 'baseline' }}
withoutSkeleton withoutSkeleton
/> />
) )

12
src/components/Note/index.tsx

@ -156,7 +156,17 @@ export default function Note({
} }
return ( return (
<div className={className}> <div
className={`${className} clickable`}
onClick={(e) => {
// Don't navigate if clicking on interactive elements
const target = e.target as HTMLElement
if (target.closest('button') || target.closest('[role="button"]') || target.closest('a')) {
return
}
navigateToNote(toNote(event))
}}
>
<div className="flex justify-between items-start gap-2"> <div className="flex justify-between items-start gap-2">
<div className="flex items-center space-x-2 flex-1"> <div className="flex items-center space-x-2 flex-1">
<UserAvatar userId={event.pubkey} size={size === 'small' ? 'medium' : 'normal'} /> <UserAvatar userId={event.pubkey} size={size === 'small' ? 'medium' : 'normal'} />

20
src/components/Username/index.tsx

@ -9,13 +9,15 @@ export default function Username({
showAt = false, showAt = false,
className, className,
skeletonClassName, skeletonClassName,
withoutSkeleton = false withoutSkeleton = false,
style
}: { }: {
userId: string userId: string
showAt?: boolean showAt?: boolean
className?: string className?: string
skeletonClassName?: string skeletonClassName?: string
withoutSkeleton?: boolean withoutSkeleton?: boolean
style?: React.CSSProperties
}) { }) {
const { profile } = useFetchProfile(userId) const { profile } = useFetchProfile(userId)
const { navigateToProfile } = useSmartProfileNavigation() const { navigateToProfile } = useSmartProfileNavigation()
@ -35,13 +37,14 @@ export default function Username({
const { username, pubkey } = profile const { username, pubkey } = profile
return ( return (
<div <span
className={cn('truncate hover:underline cursor-pointer', className)} className={cn('truncate hover:underline cursor-pointer', className)}
style={{ verticalAlign: 'baseline', ...style }}
onClick={() => navigateToProfile(toProfile(pubkey))} onClick={() => navigateToProfile(toProfile(pubkey))}
> >
{showAt && '@'} {showAt && '@'}
{username} {username}
</div> </span>
) )
} }
@ -50,13 +53,15 @@ export function SimpleUsername({
showAt = false, showAt = false,
className, className,
skeletonClassName, skeletonClassName,
withoutSkeleton = false withoutSkeleton = false,
style
}: { }: {
userId: string userId: string
showAt?: boolean showAt?: boolean
className?: string className?: string
skeletonClassName?: string skeletonClassName?: string
withoutSkeleton?: boolean withoutSkeleton?: boolean
style?: React.CSSProperties
}) { }) {
const { profile } = useFetchProfile(userId) const { profile } = useFetchProfile(userId)
@ -75,9 +80,12 @@ export function SimpleUsername({
const { username } = profile const { username } = profile
return ( return (
<div className={cn('truncate', className)}> <span
className={cn('truncate', className)}
style={{ verticalAlign: 'baseline', ...style }}
>
{showAt && '@'} {showAt && '@'}
{username} {username}
</div> </span>
) )
} }

23
src/lib/nostr-parser.tsx

@ -173,12 +173,33 @@ export function parseNostrContent(content: string, event?: Event): ParsedNostrCo
const bech32Id = match[1] const bech32Id = match[1]
const nostrType = getNostrType(bech32Id) const nostrType = getNostrType(bech32Id)
// Add spacing around handles if they're not at the beginning or end of a line
const isAtStart = start === 0 || content[start - 1] === '\n'
const isAtEnd = end === content.length || content[end] === '\n'
const needsSpaceBefore = !isAtStart && content[start - 1] !== ' '
const needsSpaceAfter = !isAtEnd && content[end] !== ' '
if (needsSpaceBefore) {
elements.push({
type: 'text',
content: ' '
})
}
elements.push({ elements.push({
type: 'nostr', type: 'nostr',
content: match[0], content: match[0],
bech32Id, bech32Id,
nostrType: nostrType || undefined nostrType: nostrType || undefined
}) })
if (needsSpaceAfter) {
elements.push({
type: 'text',
content: ' '
})
}
} else if (['image', 'video', 'audio'].includes(type) && url) { } else if (['image', 'video', 'audio'].includes(type) && url) {
elements.push({ elements.push({
type: type as 'image' | 'video' | 'audio', type: type as 'image' | 'video' | 'audio',
@ -485,7 +506,7 @@ export function renderNostrContent(parsedContent: ParsedNostrContent, className?
<EmbeddedMention <EmbeddedMention
key={index} key={index}
userId={element.bech32Id} userId={element.bech32Id}
className="not-prose inline-block" className="inline"
/> />
) )
} else if (['nevent', 'naddr', 'note'].includes(element.nostrType)) { } else if (['nevent', 'naddr', 'note'].includes(element.nostrType)) {

13
src/pages/secondary/NotePage/index.tsx

@ -201,12 +201,21 @@ function ParentNote({
'flex space-x-1 px-[0.4375rem] py-1 items-center rounded-full border clickable text-sm text-muted-foreground', 'flex space-x-1 px-[0.4375rem] py-1 items-center rounded-full border clickable text-sm text-muted-foreground',
event && 'hover:text-foreground' event && 'hover:text-foreground'
)} )}
onClick={() => { onClick={(e) => {
e.stopPropagation()
navigateToNote(toNote(event ?? eventBech32Id)) navigateToNote(toNote(event ?? eventBech32Id))
}} }}
> >
{event && <UserAvatar userId={event.pubkey} size="tiny" className="shrink-0" />} {event && <UserAvatar userId={event.pubkey} size="tiny" className="shrink-0" />}
<ContentPreview className="truncate" event={event} /> <div
className="truncate flex-1"
onClick={(e) => {
e.stopPropagation()
navigateToNote(toNote(event ?? eventBech32Id))
}}
>
<ContentPreview event={event} />
</div>
</div> </div>
{isConsecutive ? ( {isConsecutive ? (
<div className="ml-5 w-px h-3 bg-border" /> <div className="ml-5 w-px h-3 bg-border" />

Loading…
Cancel
Save