fix: 🐛
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { useFetchEvent } from '@/hooks'
|
import { useFetchEvent } from '@/hooks'
|
||||||
|
import { isSupportedKind } from '@/lib/event'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
@@ -68,6 +69,8 @@ export default function ParentNotePreview({
|
|||||||
{event && <UserAvatar className="shrink-0" userId={event.pubkey} size="tiny" />}
|
{event && <UserAvatar className="shrink-0" userId={event.pubkey} size="tiny" />}
|
||||||
{isMuted ? (
|
{isMuted ? (
|
||||||
<div className="truncate">[{t('This user has been muted')}]</div>
|
<div className="truncate">[{t('This user has been muted')}]</div>
|
||||||
|
) : !isSupportedKind(event.kind) ? (
|
||||||
|
<div className="truncate">[{t('Cannot handle event of kind k', { k: event.kind })}]</div>
|
||||||
) : (
|
) : (
|
||||||
<ContentPreview className="truncate" event={event} />
|
<ContentPreview className="truncate" event={event} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export default function Uploader({
|
|||||||
try {
|
try {
|
||||||
for (const file of event.target.files) {
|
for (const file of event.target.files) {
|
||||||
const result = await mediaUpload.upload(file)
|
const result = await mediaUpload.upload(file)
|
||||||
console.log('File uploaded successfully', result)
|
|
||||||
onUploadSuccess(result)
|
onUploadSuccess(result)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
||||||
import {
|
import {
|
||||||
|
getEventCoordinate,
|
||||||
getParentEventTag,
|
getParentEventTag,
|
||||||
|
getRootAddressableEventTag,
|
||||||
getRootEventHexId,
|
getRootEventHexId,
|
||||||
getRootEventTag,
|
getRootEventTag,
|
||||||
|
isReplaceable,
|
||||||
isReplyNoteEvent
|
isReplyNoteEvent
|
||||||
} from '@/lib/event'
|
} from '@/lib/event'
|
||||||
import { generateEventIdFromETag, tagNameEquals } from '@/lib/tag'
|
import { generateEventIdFromETag, tagNameEquals } from '@/lib/tag'
|
||||||
@@ -16,7 +19,10 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { LoadingBar } from '../LoadingBar'
|
import { LoadingBar } from '../LoadingBar'
|
||||||
import ReplyNote, { ReplyNoteSkeleton } from '../ReplyNote'
|
import ReplyNote, { ReplyNoteSkeleton } from '../ReplyNote'
|
||||||
|
|
||||||
type TRootInfo = { type: 'event'; id: string; pubkey: string } | { type: 'I'; id: string }
|
type TRootInfo =
|
||||||
|
| { type: 'E'; id: string; pubkey: string }
|
||||||
|
| { type: 'A'; id: string; eventId: string; pubkey: string; relay?: string }
|
||||||
|
| { type: 'I'; id: string }
|
||||||
|
|
||||||
const LIMIT = 100
|
const LIMIT = 100
|
||||||
const SHOW_COUNT = 10
|
const SHOW_COUNT = 10
|
||||||
@@ -30,7 +36,8 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||||||
const replies = useMemo(() => {
|
const replies = useMemo(() => {
|
||||||
const replyIdSet = new Set<string>()
|
const replyIdSet = new Set<string>()
|
||||||
const replyEvents: NEvent[] = []
|
const replyEvents: NEvent[] = []
|
||||||
let parentEventIds = [event.id]
|
const currentEventId = isReplaceable(event.kind) ? getEventCoordinate(event) : event.id
|
||||||
|
let parentEventIds = [currentEventId]
|
||||||
while (parentEventIds.length > 0) {
|
while (parentEventIds.length > 0) {
|
||||||
const events = parentEventIds.flatMap((id) => repliesMap.get(id)?.events || [])
|
const events = parentEventIds.flatMap((id) => repliesMap.get(id)?.events || [])
|
||||||
events.forEach((evt) => {
|
events.forEach((evt) => {
|
||||||
@@ -52,22 +59,36 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchRootEvent = async () => {
|
const fetchRootEvent = async () => {
|
||||||
let root: TRootInfo = { type: 'event', id: event.id, pubkey: event.pubkey }
|
let root: TRootInfo = isReplaceable(event.kind)
|
||||||
|
? {
|
||||||
|
type: 'A',
|
||||||
|
id: getEventCoordinate(event),
|
||||||
|
eventId: event.id,
|
||||||
|
pubkey: event.pubkey,
|
||||||
|
relay: client.getEventHint(event.id)
|
||||||
|
}
|
||||||
|
: { type: 'E', id: event.id, pubkey: event.pubkey }
|
||||||
const rootEventTag = getRootEventTag(event)
|
const rootEventTag = getRootEventTag(event)
|
||||||
if (rootEventTag) {
|
if (rootEventTag) {
|
||||||
const [, rootEventHexId, , , rootEventPubkey] = rootEventTag
|
const [, rootEventHexId, , , rootEventPubkey] = rootEventTag
|
||||||
if (rootEventHexId && rootEventPubkey) {
|
if (rootEventHexId && rootEventPubkey) {
|
||||||
root = { type: 'event', id: rootEventHexId, pubkey: rootEventPubkey }
|
root = { type: 'E', id: rootEventHexId, pubkey: rootEventPubkey }
|
||||||
} else {
|
} else {
|
||||||
const rootEventId = generateEventIdFromETag(rootEventTag)
|
const rootEventId = generateEventIdFromETag(rootEventTag)
|
||||||
if (rootEventId) {
|
if (rootEventId) {
|
||||||
const rootEvent = await client.fetchEvent(rootEventId)
|
const rootEvent = await client.fetchEvent(rootEventId)
|
||||||
if (rootEvent) {
|
if (rootEvent) {
|
||||||
root = { type: 'event', id: rootEvent.id, pubkey: rootEvent.pubkey }
|
root = { type: 'E', id: rootEvent.id, pubkey: rootEvent.pubkey }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (event.kind === ExtendedKind.COMMENT) {
|
} else if (event.kind === ExtendedKind.COMMENT) {
|
||||||
|
const rootATag = getRootAddressableEventTag(event)
|
||||||
|
if (rootATag) {
|
||||||
|
const [, coordinate, relay] = rootATag
|
||||||
|
const [, pubkey] = coordinate.split(':')
|
||||||
|
root = { type: 'A', id: coordinate, eventId: event.id, pubkey, relay }
|
||||||
|
}
|
||||||
const rootITag = event.tags.find(tagNameEquals('I'))
|
const rootITag = event.tags.find(tagNameEquals('I'))
|
||||||
if (rootITag) {
|
if (rootITag) {
|
||||||
root = { type: 'I', id: rootITag[1] }
|
root = { type: 'I', id: rootITag[1] }
|
||||||
@@ -110,13 +131,18 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||||||
(rootInfo as { pubkey?: string }).pubkey ?? event.pubkey
|
(rootInfo as { pubkey?: string }).pubkey ?? event.pubkey
|
||||||
)
|
)
|
||||||
const relayUrls = relayList.read.concat(BIG_RELAY_URLS)
|
const relayUrls = relayList.read.concat(BIG_RELAY_URLS)
|
||||||
const seenOn = client.getSeenEventRelayUrls(rootInfo.id)
|
const seenOn =
|
||||||
|
rootInfo.type === 'E'
|
||||||
|
? client.getSeenEventRelayUrls(rootInfo.id)
|
||||||
|
: rootInfo.type === 'A'
|
||||||
|
? client.getCurrentRelayUrls()
|
||||||
|
: []
|
||||||
relayUrls.unshift(...seenOn)
|
relayUrls.unshift(...seenOn)
|
||||||
|
|
||||||
const filters: (Omit<Filter, 'since' | 'until'> & {
|
const filters: (Omit<Filter, 'since' | 'until'> & {
|
||||||
limit: number
|
limit: number
|
||||||
})[] = []
|
})[] = []
|
||||||
if (rootInfo.type === 'event') {
|
if (rootInfo.type === 'E') {
|
||||||
filters.push({
|
filters.push({
|
||||||
'#e': [rootInfo.id],
|
'#e': [rootInfo.id],
|
||||||
kinds: [kinds.ShortTextNote],
|
kinds: [kinds.ShortTextNote],
|
||||||
@@ -129,6 +155,15 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||||||
limit: LIMIT
|
limit: LIMIT
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} else if (rootInfo.type === 'A') {
|
||||||
|
filters.push({
|
||||||
|
'#A': [rootInfo.id],
|
||||||
|
kinds: [ExtendedKind.COMMENT],
|
||||||
|
limit: LIMIT
|
||||||
|
})
|
||||||
|
if (rootInfo.relay) {
|
||||||
|
relayUrls.push(rootInfo.relay)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
filters.push({
|
filters.push({
|
||||||
'#I': [rootInfo.id],
|
'#I': [rootInfo.id],
|
||||||
|
|||||||
@@ -187,10 +187,12 @@ export async function createCommentDraftEvent(
|
|||||||
const {
|
const {
|
||||||
quoteEventIds,
|
quoteEventIds,
|
||||||
rootEventId,
|
rootEventId,
|
||||||
|
rootCoordinateTag,
|
||||||
rootKind,
|
rootKind,
|
||||||
rootPubkey,
|
rootPubkey,
|
||||||
rootUrl,
|
rootUrl,
|
||||||
parentEventId,
|
parentEventId,
|
||||||
|
parentCoordinate,
|
||||||
parentKind,
|
parentKind,
|
||||||
parentPubkey
|
parentPubkey
|
||||||
} = await extractCommentMentions(content, parentEvent)
|
} = await extractCommentMentions(content, parentEvent)
|
||||||
@@ -207,7 +209,9 @@ export async function createCommentDraftEvent(
|
|||||||
|
|
||||||
tags.push(...mentions.filter((pubkey) => pubkey !== parentPubkey).map((pubkey) => ['p', pubkey]))
|
tags.push(...mentions.filter((pubkey) => pubkey !== parentPubkey).map((pubkey) => ['p', pubkey]))
|
||||||
|
|
||||||
if (rootEventId) {
|
if (rootCoordinateTag) {
|
||||||
|
tags.push(rootCoordinateTag)
|
||||||
|
} else if (rootEventId) {
|
||||||
tags.push(
|
tags.push(
|
||||||
rootPubkey
|
rootPubkey
|
||||||
? ['E', rootEventId, client.getEventHint(rootEventId), rootPubkey]
|
? ['E', rootEventId, client.getEventHint(rootEventId), rootPubkey]
|
||||||
@@ -225,7 +229,9 @@ export async function createCommentDraftEvent(
|
|||||||
}
|
}
|
||||||
tags.push(
|
tags.push(
|
||||||
...[
|
...[
|
||||||
['e', parentEventId, client.getEventHint(parentEventId), parentPubkey],
|
parentCoordinate
|
||||||
|
? ['a', parentCoordinate, client.getEventHint(parentEventId)]
|
||||||
|
: ['e', parentEventId, client.getEventHint(parentEventId), parentPubkey],
|
||||||
['k', parentKind.toString()],
|
['k', parentKind.toString()],
|
||||||
['p', parentPubkey]
|
['p', parentPubkey]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { getAmountFromInvoice, getLightningAddressFromProfile } from './lightnin
|
|||||||
import { formatPubkey, pubkeyToNpub } from './pubkey'
|
import { formatPubkey, pubkeyToNpub } from './pubkey'
|
||||||
import {
|
import {
|
||||||
extractImageInfoFromTag,
|
extractImageInfoFromTag,
|
||||||
|
generateEventIdFromATag,
|
||||||
generateEventIdFromETag,
|
generateEventIdFromETag,
|
||||||
isReplyETag,
|
isReplyETag,
|
||||||
isRootETag,
|
isRootETag,
|
||||||
@@ -27,7 +28,7 @@ export function isNsfwEvent(event: Event) {
|
|||||||
|
|
||||||
export function isReplyNoteEvent(event: Event) {
|
export function isReplyNoteEvent(event: Event) {
|
||||||
if (event.kind === ExtendedKind.COMMENT) {
|
if (event.kind === ExtendedKind.COMMENT) {
|
||||||
return !!getParentEventTag(event)
|
return !!getParentEventTag(event) || !!getParentAddressableEventTag(event)
|
||||||
}
|
}
|
||||||
if (event.kind !== kinds.ShortTextNote) return false
|
if (event.kind !== kinds.ShortTextNote) return false
|
||||||
|
|
||||||
@@ -88,16 +89,27 @@ export function getParentEventTag(event?: Event) {
|
|||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getParentAddressableEventTag(event?: Event) {
|
||||||
|
if (!event || event.kind !== ExtendedKind.COMMENT) return undefined
|
||||||
|
|
||||||
|
return event.tags.find(tagNameEquals('a')) ?? event.tags.find(tagNameEquals('A'))
|
||||||
|
}
|
||||||
|
|
||||||
export function getParentEventHexId(event?: Event) {
|
export function getParentEventHexId(event?: Event) {
|
||||||
const tag = getParentEventTag(event)
|
const tag = getParentEventTag(event)
|
||||||
return tag?.[1]
|
return tag?.[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getParentEventId(event?: Event) {
|
export function getParentEventId(event?: Event) {
|
||||||
const tag = getParentEventTag(event)
|
const eTag = getParentEventTag(event)
|
||||||
if (!tag) return undefined
|
if (!eTag) {
|
||||||
|
const aTag = getParentAddressableEventTag(event)
|
||||||
|
if (!aTag) return undefined
|
||||||
|
|
||||||
return generateEventIdFromETag(tag)
|
return generateEventIdFromATag(aTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateEventIdFromETag(eTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRootEventTag(event?: Event) {
|
export function getRootEventTag(event?: Event) {
|
||||||
@@ -117,6 +129,12 @@ export function getRootEventTag(event?: Event) {
|
|||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRootAddressableEventTag(event?: Event) {
|
||||||
|
if (!event || event.kind !== ExtendedKind.COMMENT) return undefined
|
||||||
|
|
||||||
|
return event.tags.find(tagNameEquals('A'))
|
||||||
|
}
|
||||||
|
|
||||||
export function getRootEventHexId(event?: Event) {
|
export function getRootEventHexId(event?: Event) {
|
||||||
const tag = getRootEventTag(event)
|
const tag = getRootEventTag(event)
|
||||||
return tag?.[1]
|
return tag?.[1]
|
||||||
@@ -124,7 +142,12 @@ export function getRootEventHexId(event?: Event) {
|
|||||||
|
|
||||||
export function getRootEventId(event?: Event) {
|
export function getRootEventId(event?: Event) {
|
||||||
const tag = getRootEventTag(event)
|
const tag = getRootEventTag(event)
|
||||||
if (!tag) return undefined
|
if (!tag) {
|
||||||
|
const aTag = getRootAddressableEventTag(event)
|
||||||
|
if (!aTag) return undefined
|
||||||
|
|
||||||
|
return generateEventIdFromATag(aTag)
|
||||||
|
}
|
||||||
|
|
||||||
return generateEventIdFromETag(tag)
|
return generateEventIdFromETag(tag)
|
||||||
}
|
}
|
||||||
@@ -356,6 +379,13 @@ export async function extractRelatedEventIds(content: string, parentEvent?: Even
|
|||||||
|
|
||||||
export async function extractCommentMentions(content: string, parentEvent: Event) {
|
export async function extractCommentMentions(content: string, parentEvent: Event) {
|
||||||
const quoteEventIds: string[] = []
|
const quoteEventIds: string[] = []
|
||||||
|
const parentEventIsReplaceable = isReplaceable(parentEvent.kind)
|
||||||
|
const rootCoordinateTag =
|
||||||
|
parentEvent.kind === ExtendedKind.COMMENT
|
||||||
|
? parentEvent.tags.find(tagNameEquals('A'))
|
||||||
|
: isReplaceable(parentEvent.kind)
|
||||||
|
? ['A', getEventCoordinate(parentEvent), client.getEventHint(parentEvent.id)]
|
||||||
|
: undefined
|
||||||
const rootEventId =
|
const rootEventId =
|
||||||
parentEvent.kind === ExtendedKind.COMMENT
|
parentEvent.kind === ExtendedKind.COMMENT
|
||||||
? parentEvent.tags.find(tagNameEquals('E'))?.[1]
|
? parentEvent.tags.find(tagNameEquals('E'))?.[1]
|
||||||
@@ -374,6 +404,7 @@ export async function extractCommentMentions(content: string, parentEvent: Event
|
|||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const parentEventId = parentEvent.id
|
const parentEventId = parentEvent.id
|
||||||
|
const parentCoordinate = parentEventIsReplaceable ? getEventCoordinate(parentEvent) : undefined
|
||||||
const parentKind = parentEvent.kind
|
const parentKind = parentEvent.kind
|
||||||
const parentPubkey = parentEvent.pubkey
|
const parentPubkey = parentEvent.pubkey
|
||||||
|
|
||||||
@@ -399,10 +430,12 @@ export async function extractCommentMentions(content: string, parentEvent: Event
|
|||||||
return {
|
return {
|
||||||
quoteEventIds,
|
quoteEventIds,
|
||||||
rootEventId,
|
rootEventId,
|
||||||
|
rootCoordinateTag,
|
||||||
rootKind,
|
rootKind,
|
||||||
rootPubkey,
|
rootPubkey,
|
||||||
rootUrl,
|
rootUrl,
|
||||||
parentEventId,
|
parentEventId,
|
||||||
|
parentCoordinate,
|
||||||
parentKind,
|
parentKind,
|
||||||
parentPubkey
|
parentPubkey
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Skeleton } from '@/components/ui/skeleton'
|
|||||||
import { ExtendedKind } from '@/constants'
|
import { ExtendedKind } from '@/constants'
|
||||||
import { useFetchEvent } from '@/hooks'
|
import { useFetchEvent } from '@/hooks'
|
||||||
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
||||||
import { getParentEventId, getRootEventId, isPictureEvent } from '@/lib/event'
|
import { getParentEventId, getRootEventId, isPictureEvent, isSupportedKind } from '@/lib/event'
|
||||||
import { toNote, toNoteList } from '@/lib/link'
|
import { toNote, toNoteList } from '@/lib/link'
|
||||||
import { tagNameEquals } from '@/lib/tag'
|
import { tagNameEquals } from '@/lib/tag'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
@@ -158,6 +158,21 @@ function ParentNote({ eventId }: { eventId?: string }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isSupportedKind(event.kind)) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Card
|
||||||
|
className="flex space-x-1 p-1 items-center clickable text-sm text-muted-foreground hover:text-foreground"
|
||||||
|
onClick={() => push(toNote(eventId))}
|
||||||
|
>
|
||||||
|
<UserAvatar userId={event.pubkey} size="tiny" className="shrink-0" />
|
||||||
|
<div className="shrink-0">[{t('Cannot handle event of kind k', { k: event.kind })}]</div>
|
||||||
|
</Card>
|
||||||
|
<div className="ml-5 w-px h-2 bg-border" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { getParentEventTag, getRootEventTag } from '@/lib/event'
|
import {
|
||||||
|
getParentAddressableEventTag,
|
||||||
|
getParentEventTag,
|
||||||
|
getRootAddressableEventTag,
|
||||||
|
getRootEventTag
|
||||||
|
} from '@/lib/event'
|
||||||
import { Event } from 'nostr-tools'
|
import { Event } from 'nostr-tools'
|
||||||
import { createContext, useCallback, useContext, useState } from 'react'
|
import { createContext, useCallback, useContext, useState } from 'react'
|
||||||
|
|
||||||
@@ -29,15 +34,31 @@ export function ReplyProvider({ children }: { children: React.ReactNode }) {
|
|||||||
if (newReplyIdSet.has(reply.id)) return
|
if (newReplyIdSet.has(reply.id)) return
|
||||||
newReplyIdSet.add(reply.id)
|
newReplyIdSet.add(reply.id)
|
||||||
|
|
||||||
|
let rootId: string | undefined
|
||||||
const rootETag = getRootEventTag(reply)
|
const rootETag = getRootEventTag(reply)
|
||||||
if (rootETag) {
|
if (rootETag) {
|
||||||
const rootId = rootETag[1]
|
rootId = rootETag[1]
|
||||||
|
} else {
|
||||||
|
const rootATag = getRootAddressableEventTag(reply)
|
||||||
|
if (rootATag) {
|
||||||
|
rootId = rootATag[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rootId) {
|
||||||
newReplyEventMap.set(rootId, [...(newReplyEventMap.get(rootId) || []), reply])
|
newReplyEventMap.set(rootId, [...(newReplyEventMap.get(rootId) || []), reply])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parentId: string | undefined
|
||||||
const parentETag = getParentEventTag(reply)
|
const parentETag = getParentEventTag(reply)
|
||||||
if (parentETag) {
|
if (parentETag) {
|
||||||
const parentId = parentETag[1]
|
parentId = parentETag[1]
|
||||||
|
} else {
|
||||||
|
const parentATag = getParentAddressableEventTag(reply)
|
||||||
|
if (parentATag) {
|
||||||
|
parentId = parentATag[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parentId && parentId !== rootId) {
|
||||||
newReplyEventMap.set(parentId, [...(newReplyEventMap.get(parentId) || []), reply])
|
newReplyEventMap.set(parentId, [...(newReplyEventMap.get(parentId) || []), reply])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user