refactor
This commit is contained in:
parent
28ec943a52
commit
78725c1d14
45 changed files with 698 additions and 766 deletions
|
|
@ -1,14 +1,14 @@
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { isDevEnv } from '@/lib/common'
|
||||
import { isDevEnv } from '@/lib/utils'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useTheme } from '@/providers/ThemeProvider'
|
||||
import { NstartModal } from 'nstart-modal'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AccountList from '../AccountList'
|
||||
import NostrConnectLogin from './NostrConnectionLogin'
|
||||
import GenerateNewAccount from './GenerateNewAccount'
|
||||
import NostrConnectLogin from './NostrConnectionLogin'
|
||||
import NpubLogin from './NpubLogin'
|
||||
import PrivateKeyLogin from './PrivateKeyLogin'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useFetchEvent } from '@/hooks'
|
||||
import { generateEventIdFromETag } from '@/lib/tag'
|
||||
import { generateBech32IdFromETag } from '@/lib/tag'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
|
@ -15,7 +15,7 @@ export default function BookmarkList() {
|
|||
|
||||
return (
|
||||
bookmarkListEvent.tags
|
||||
.map((tag) => (tag[0] === 'e' ? generateEventIdFromETag(tag) : undefined))
|
||||
.map((tag) => (tag[0] === 'e' ? generateBech32IdFromETag(tag) : undefined))
|
||||
.filter(Boolean) as `nevent1${string}`[]
|
||||
).reverse()
|
||||
}, [bookmarkListEvent])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
|
|||
import { Drawer, DrawerContent, DrawerOverlay, DrawerTrigger } from '@/components/ui/drawer'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { ExtendedKind } from '@/constants'
|
||||
import { getReplaceableEventIdentifier, getSharableEventId } from '@/lib/event'
|
||||
import { getReplaceableEventIdentifier, getNoteBech32Id } from '@/lib/event'
|
||||
import { toChachiChat } from '@/lib/link'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import clientService from '@/services/client.service'
|
||||
|
|
@ -139,7 +139,7 @@ export default function ClientSelect({
|
|||
<ClientSelectItem
|
||||
key={clientId}
|
||||
onClick={() => setOpen(false)}
|
||||
href={client.getUrl(originalNoteId ?? getSharableEventId(event!))}
|
||||
href={client.getUrl(originalNoteId ?? getNoteBech32Id(event!))}
|
||||
name={client.name}
|
||||
/>
|
||||
)
|
||||
|
|
@ -150,7 +150,7 @@ export default function ClientSelect({
|
|||
variant="ghost"
|
||||
className="w-full py-6 font-semibold"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(originalNoteId ?? getSharableEventId(event!))
|
||||
navigator.clipboard.writeText(originalNoteId ?? getNoteBech32Id(event!))
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import {
|
|||
EmbeddedWebsocketUrlParser,
|
||||
parseContent
|
||||
} from '@/lib/content-parser'
|
||||
import { extractEmojiInfosFromTags } from '@/lib/event'
|
||||
import { extractImageInfoFromTag } from '@/lib/tag'
|
||||
import { getImageInfosFromEvent } from '@/lib/event'
|
||||
import { getEmojiInfosFromEmojiTags, getImageInfoFromImetaTag } from '@/lib/tag'
|
||||
import { cn } from '@/lib/utils'
|
||||
import mediaUpload from '@/services/media-upload.service'
|
||||
import { TImageInfo } from '@/types'
|
||||
|
|
@ -45,9 +45,7 @@ const Content = memo(({ event, className }: { event: Event; className?: string }
|
|||
EmbeddedEmojiParser
|
||||
])
|
||||
|
||||
const imageInfos = event.tags
|
||||
.map((tag) => extractImageInfoFromTag(tag, event.pubkey))
|
||||
.filter(Boolean) as TImageInfo[]
|
||||
const imageInfos = getImageInfosFromEvent(event)
|
||||
const allImages = nodes
|
||||
.map((node) => {
|
||||
if (node.type === 'image') {
|
||||
|
|
@ -57,7 +55,7 @@ const Content = memo(({ event, className }: { event: Event; className?: string }
|
|||
}
|
||||
const tag = mediaUpload.getImetaTagByUrl(node.data)
|
||||
return tag
|
||||
? extractImageInfoFromTag(tag, event.pubkey)
|
||||
? getImageInfoFromImetaTag(tag, event.pubkey)
|
||||
: { url: node.data, pubkey: event.pubkey }
|
||||
}
|
||||
if (node.type === 'images') {
|
||||
|
|
@ -73,7 +71,7 @@ const Content = memo(({ event, className }: { event: Event; className?: string }
|
|||
.flat() as TImageInfo[]
|
||||
let imageIndex = 0
|
||||
|
||||
const emojiInfos = extractEmojiInfosFromTags(event.tags)
|
||||
const emojiInfos = getEmojiInfosFromEmojiTags(event.tags)
|
||||
|
||||
const lastNormalUrlNode = nodes.findLast((node) => node.type === 'url')
|
||||
const lastNormalUrl =
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getCommunityDefinition } from '@/lib/event'
|
||||
import { getCommunityDefinitionFromEvent } from '@/lib/event-metadata'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -14,7 +14,7 @@ export default function CommunityDefinitionPreview({
|
|||
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const metadata = useMemo(() => getCommunityDefinition(event), [event])
|
||||
const metadata = useMemo(() => getCommunityDefinitionFromEvent(event), [event])
|
||||
|
||||
return (
|
||||
<div className={cn('pointer-events-none', className)} onClick={onClick}>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getGroupMetadata } from '@/lib/event'
|
||||
import { getGroupMetadataFromEvent } from '@/lib/event-metadata'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -14,7 +14,7 @@ export default function GroupMetadataPreview({
|
|||
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const metadata = useMemo(() => getGroupMetadata(event), [event])
|
||||
const metadata = useMemo(() => getGroupMetadataFromEvent(event), [event])
|
||||
|
||||
return (
|
||||
<div className={cn('pointer-events-none', className)} onClick={onClick}>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getLiveEventMetadata } from '@/lib/event'
|
||||
import { getLiveEventMetadataFromEvent } from '@/lib/event-metadata'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -14,7 +14,7 @@ export default function LiveEventPreview({
|
|||
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const metadata = useMemo(() => getLiveEventMetadata(event), [event])
|
||||
const metadata = useMemo(() => getLiveEventMetadataFromEvent(event), [event])
|
||||
|
||||
return (
|
||||
<div className={cn('pointer-events-none', className)} onClick={onClick}>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getLongFormArticleMetadata } from '@/lib/event'
|
||||
import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -14,7 +14,7 @@ export default function LongFormArticlePreview({
|
|||
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const metadata = useMemo(() => getLongFormArticleMetadata(event), [event])
|
||||
const metadata = useMemo(() => getLongFormArticleMetadataFromEvent(event), [event])
|
||||
|
||||
return (
|
||||
<div className={cn('pointer-events-none', className)} onClick={onClick}>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
EmbeddedVideoParser,
|
||||
parseContent
|
||||
} from '@/lib/content-parser'
|
||||
import { extractEmojiInfosFromTags } from '@/lib/event'
|
||||
import { getEmojiInfosFromEmojiTags } from '@/lib/tag'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -36,7 +36,7 @@ export default function NormalContentPreview({
|
|||
])
|
||||
}, [event, translatedEvent])
|
||||
|
||||
const emojiInfos = extractEmojiInfosFromTags(event?.tags)
|
||||
const emojiInfos = getEmojiInfosFromEmojiTags(event?.tags)
|
||||
|
||||
return (
|
||||
<div className={cn('pointer-events-none', className)} onClick={onClick}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Carousel, CarouselApi, CarouselContent, CarouselItem } from '@/components/ui/carousel'
|
||||
import { isTouchDevice } from '@/lib/common'
|
||||
import { isTouchDevice } from '@/lib/utils'
|
||||
import { TImageInfo } from '@/types'
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getCommunityDefinition } from '@/lib/event'
|
||||
import { getCommunityDefinitionFromEvent } from '@/lib/event-metadata'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
import ClientSelect from '../ClientSelect'
|
||||
|
|
@ -11,7 +11,7 @@ export default function CommunityDefinition({
|
|||
event: Event
|
||||
className?: string
|
||||
}) {
|
||||
const metadata = useMemo(() => getCommunityDefinition(event), [event])
|
||||
const metadata = useMemo(() => getCommunityDefinitionFromEvent(event), [event])
|
||||
|
||||
const communityNameComponent = (
|
||||
<div className="text-xl font-semibold line-clamp-1">{metadata.name}</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getGroupMetadata } from '@/lib/event'
|
||||
import { getGroupMetadataFromEvent } from '@/lib/event-metadata'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
import ClientSelect from '../ClientSelect'
|
||||
|
|
@ -13,7 +13,7 @@ export default function GroupMetadata({
|
|||
originalNoteId?: string
|
||||
className?: string
|
||||
}) {
|
||||
const metadata = useMemo(() => getGroupMetadata(event), [event])
|
||||
const metadata = useMemo(() => getGroupMetadataFromEvent(event), [event])
|
||||
|
||||
const groupNameComponent = (
|
||||
<div className="text-xl font-semibold line-clamp-1">{metadata.name}</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useFetchEvent, useTranslatedEvent } from '@/hooks'
|
|||
import { createFakeEvent } from '@/lib/event'
|
||||
import { toNjump, toNote } from '@/lib/link'
|
||||
import { isValidPubkey } from '@/lib/pubkey'
|
||||
import { generateEventIdFromATag } from '@/lib/tag'
|
||||
import { generateBech32IdFromATag } from '@/lib/tag'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useSecondaryPage } from '@/PageManager'
|
||||
import { Event } from 'nostr-tools'
|
||||
|
|
@ -70,7 +70,7 @@ function HighlightSource({ event }: { event: Event }) {
|
|||
return sourceTag[1]
|
||||
}
|
||||
if (sourceTag[0] === 'a') {
|
||||
return generateEventIdFromATag(sourceTag)
|
||||
return generateBech32IdFromATag(sourceTag)
|
||||
}
|
||||
}, [sourceTag])
|
||||
const pubkey = useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Badge } from '@/components/ui/badge'
|
||||
import { getLiveEventMetadata } from '@/lib/event'
|
||||
import { getLiveEventMetadataFromEvent } from '@/lib/event-metadata'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -8,7 +8,7 @@ import Image from '../Image'
|
|||
|
||||
export default function LiveEvent({ event, className }: { event: Event; className?: string }) {
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const metadata = useMemo(() => getLiveEventMetadata(event), [event])
|
||||
const metadata = useMemo(() => getLiveEventMetadataFromEvent(event), [event])
|
||||
|
||||
const liveStatusComponent =
|
||||
metadata.status &&
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Badge } from '@/components/ui/badge'
|
||||
import { getLongFormArticleMetadata } from '@/lib/event'
|
||||
import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -14,7 +14,7 @@ export default function LongFormArticle({
|
|||
className?: string
|
||||
}) {
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const metadata = useMemo(() => getLongFormArticleMetadata(event), [event])
|
||||
const metadata = useMemo(() => getLongFormArticleMetadataFromEvent(event), [event])
|
||||
|
||||
const titleComponent = <div className="text-xl font-semibold line-clamp-2">{metadata.title}</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useSecondaryPage } from '@/PageManager'
|
||||
import { ExtendedKind } from '@/constants'
|
||||
import {
|
||||
extractImageInfosFromEventTags,
|
||||
getParentEventId,
|
||||
getImageInfosFromEvent,
|
||||
getParentBech32Id,
|
||||
getUsingClient,
|
||||
isNsfwEvent,
|
||||
isPictureEvent
|
||||
|
|
@ -47,11 +47,11 @@ export default function Note({
|
|||
const { push } = useSecondaryPage()
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const parentEventId = useMemo(
|
||||
() => (hideParentNotePreview ? undefined : getParentEventId(event)),
|
||||
() => (hideParentNotePreview ? undefined : getParentBech32Id(event)),
|
||||
[event, hideParentNotePreview]
|
||||
)
|
||||
const imageInfos = useMemo(
|
||||
() => (isPictureEvent(event) ? extractImageInfosFromEventTags(event) : []),
|
||||
() => (isPictureEvent(event) ? getImageInfosFromEvent(event) : []),
|
||||
[event]
|
||||
)
|
||||
const usingClient = useMemo(() => getUsingClient(event), [event])
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { getSharableEventId } from '@/lib/event'
|
||||
import { getNoteBech32Id } from '@/lib/event'
|
||||
import { toNjump } from '@/lib/link'
|
||||
import { pubkeyToNpub } from '@/lib/pubkey'
|
||||
import { useMuteList } from '@/providers/MuteListProvider'
|
||||
|
|
@ -56,7 +56,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||
<Button
|
||||
onClick={() => {
|
||||
setIsDrawerOpen(false)
|
||||
navigator.clipboard.writeText(getSharableEventId(event))
|
||||
navigator.clipboard.writeText(getNoteBech32Id(event))
|
||||
}}
|
||||
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
|
||||
variant="ghost"
|
||||
|
|
@ -78,7 +78,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||
<Button
|
||||
onClick={() => {
|
||||
setIsDrawerOpen(false)
|
||||
navigator.clipboard.writeText(toNjump(getSharableEventId(event)))
|
||||
navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))
|
||||
}}
|
||||
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
|
||||
variant="ghost"
|
||||
|
|
@ -149,9 +149,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(getSharableEventId(event))}
|
||||
>
|
||||
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(getNoteBech32Id(event))}>
|
||||
<Copy />
|
||||
{t('Copy event ID')}
|
||||
</DropdownMenuItem>
|
||||
|
|
@ -162,7 +160,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||
{t('Copy user ID')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(toNjump(getSharableEventId(event)))}
|
||||
onClick={() => navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))}
|
||||
>
|
||||
<Link />
|
||||
{t('Copy share link')}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from '@/components/ui/dropdown-menu'
|
||||
import { useNoteStatsById } from '@/hooks/useNoteStatsById'
|
||||
import { createRepostDraftEvent } from '@/lib/draft-event'
|
||||
import { getSharableEventId } from '@/lib/event'
|
||||
import { getNoteBech32Id } from '@/lib/event'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
|
|
@ -87,7 +87,7 @@ export default function RepostButton({ event }: { event: Event }) {
|
|||
<PostEditor
|
||||
open={isPostDialogOpen}
|
||||
setOpen={setIsPostDialogOpen}
|
||||
defaultContent={'\nnostr:' + getSharableEventId(event)}
|
||||
defaultContent={'\nnostr:' + getNoteBech32Id(event)}
|
||||
/>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useFetchEvent } from '@/hooks'
|
||||
import { extractZapInfoFromReceipt } from '@/lib/event'
|
||||
import { getZapInfoFromEvent } from '@/lib/event-metadata'
|
||||
import { formatAmount } from '@/lib/lightning'
|
||||
import { toNote, toProfile } from '@/lib/link'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
|
@ -24,7 +24,7 @@ export function ZapNotification({
|
|||
const { push } = useSecondaryPage()
|
||||
const { pubkey } = useNostr()
|
||||
const { senderPubkey, eventId, amount, comment } = useMemo(
|
||||
() => extractZapInfoFromReceipt(notification) ?? ({} as any),
|
||||
() => getZapInfoFromEvent(notification) ?? ({} as any),
|
||||
[notification]
|
||||
)
|
||||
const { event, isFetching } = useFetchEvent(eventId)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import {
|
|||
EmbeddedWebsocketUrlParser,
|
||||
parseContent
|
||||
} from '@/lib/content-parser'
|
||||
import { extractEmojiInfosFromTags, extractImageInfosFromEventTags } from '@/lib/event'
|
||||
import { getImageInfosFromEvent } from '@/lib/event'
|
||||
import { getEmojiInfosFromEmojiTags } from '@/lib/tag'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { memo, useMemo } from 'react'
|
||||
|
|
@ -22,7 +23,7 @@ import Emoji from '../Emoji'
|
|||
import { ImageCarousel } from '../ImageCarousel'
|
||||
|
||||
const PictureContent = memo(({ event, className }: { event: Event; className?: string }) => {
|
||||
const images = useMemo(() => extractImageInfosFromEventTags(event), [event])
|
||||
const images = useMemo(() => getImageInfosFromEvent(event), [event])
|
||||
|
||||
const nodes = parseContent(event.content, [
|
||||
EmbeddedNormalUrlParser,
|
||||
|
|
@ -33,7 +34,7 @@ const PictureContent = memo(({ event, className }: { event: Event; className?: s
|
|||
EmbeddedEmojiParser
|
||||
])
|
||||
|
||||
const emojiInfos = extractEmojiInfosFromTags(event.tags)
|
||||
const emojiInfos = getEmojiInfosFromEmojiTags(event.tags)
|
||||
|
||||
return (
|
||||
<div className={cn('text-wrap break-words whitespace-pre-wrap space-y-2', className)}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { EmbeddedHashtagParser, EmbeddedMentionParser, parseContent } from '@/lib/content-parser'
|
||||
import { extractImageInfosFromEventTags } from '@/lib/event'
|
||||
import { getImageInfosFromEvent } from '@/lib/event'
|
||||
import { toNote } from '@/lib/link'
|
||||
import { tagNameEquals } from '@/lib/tag'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
|
@ -21,7 +21,7 @@ export default function PictureNoteCard({
|
|||
className?: string
|
||||
}) {
|
||||
const { push } = useSecondaryPage()
|
||||
const images = useMemo(() => extractImageInfosFromEventTags(event), [event])
|
||||
const images = useMemo(() => getImageInfosFromEvent(event), [event])
|
||||
const title = useMemo(() => {
|
||||
const nodes = parseContent(event.tags.find(tagNameEquals('title'))?.[1] ?? event.content, [
|
||||
EmbeddedMentionParser,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { extractMentions } from '@/lib/event'
|
||||
import { useMuteList } from '@/providers/MuteListProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import client from '@/services/client.service'
|
||||
import { Check } from 'lucide-react'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { Event, nip19 } from 'nostr-tools'
|
||||
import { HTMLAttributes, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SimpleUserAvatar } from '../UserAvatar'
|
||||
import { SimpleUsername } from '../Username'
|
||||
import { useMuteList } from '@/providers/MuteListProvider'
|
||||
|
||||
export default function Mentions({
|
||||
content,
|
||||
|
|
@ -131,3 +131,50 @@ function PopoverCheckboxItem({
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
async function extractMentions(content: string, parentEvent?: Event) {
|
||||
const parentEventPubkey = parentEvent ? parentEvent.pubkey : undefined
|
||||
const pubkeys: string[] = []
|
||||
const relatedPubkeys: string[] = []
|
||||
const matches = content.match(
|
||||
/nostr:(npub1[a-z0-9]{58}|nprofile1[a-z0-9]+|note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g
|
||||
)
|
||||
|
||||
const addToSet = (arr: string[], pubkey: string) => {
|
||||
if (pubkey === parentEventPubkey) return
|
||||
if (!arr.includes(pubkey)) arr.push(pubkey)
|
||||
}
|
||||
|
||||
for (const m of matches || []) {
|
||||
try {
|
||||
const id = m.split(':')[1]
|
||||
const { type, data } = nip19.decode(id)
|
||||
if (type === 'nprofile') {
|
||||
addToSet(pubkeys, data.pubkey)
|
||||
} else if (type === 'npub') {
|
||||
addToSet(pubkeys, data)
|
||||
} else if (['nevent', 'note'].includes(type)) {
|
||||
const event = await client.fetchEvent(id)
|
||||
if (event) {
|
||||
addToSet(pubkeys, event.pubkey)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
if (parentEvent) {
|
||||
parentEvent.tags.forEach(([tagName, tagValue]) => {
|
||||
if (['p', 'P'].includes(tagName) && !!tagValue) {
|
||||
addToSet(relatedPubkeys, tagValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
pubkeys,
|
||||
relatedPubkeys: relatedPubkeys.filter((p) => !pubkeys.includes(p)),
|
||||
parentEventPubkey
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
||||
import {
|
||||
getEventCoordinate,
|
||||
getParentEventTag,
|
||||
getRootAddressableEventTag,
|
||||
getParentETag,
|
||||
getReplaceableEventCoordinate,
|
||||
getRootATag,
|
||||
getRootETag,
|
||||
getRootEventHexId,
|
||||
getRootEventTag,
|
||||
isReplaceable,
|
||||
isReplaceableEvent,
|
||||
isReplyNoteEvent
|
||||
} from '@/lib/event'
|
||||
import { generateEventIdFromETag, tagNameEquals } from '@/lib/tag'
|
||||
import { generateBech32IdFromETag, tagNameEquals } from '@/lib/tag'
|
||||
import { useSecondaryPage } from '@/PageManager'
|
||||
import { useReply } from '@/providers/ReplyProvider'
|
||||
import { useUserTrust } from '@/providers/UserTrustProvider'
|
||||
|
|
@ -36,16 +36,18 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||
const replies = useMemo(() => {
|
||||
const replyIdSet = new Set<string>()
|
||||
const replyEvents: NEvent[] = []
|
||||
const currentEventId = isReplaceable(event.kind) ? getEventCoordinate(event) : event.id
|
||||
let parentEventIds = [currentEventId]
|
||||
while (parentEventIds.length > 0) {
|
||||
const events = parentEventIds.flatMap((id) => repliesMap.get(id)?.events || [])
|
||||
const currentEventKey = isReplaceableEvent(event.kind)
|
||||
? getReplaceableEventCoordinate(event)
|
||||
: event.id
|
||||
let parentEventKeys = [currentEventKey]
|
||||
while (parentEventKeys.length > 0) {
|
||||
const events = parentEventKeys.flatMap((id) => repliesMap.get(id)?.events || [])
|
||||
events.forEach((evt) => {
|
||||
if (replyIdSet.has(evt.id)) return
|
||||
replyIdSet.add(evt.id)
|
||||
replyEvents.push(evt)
|
||||
})
|
||||
parentEventIds = events.map((evt) => evt.id)
|
||||
parentEventKeys = events.map((evt) => evt.id)
|
||||
}
|
||||
return replyEvents.sort((a, b) => a.created_at - b.created_at)
|
||||
}, [event.id, repliesMap])
|
||||
|
|
@ -59,22 +61,22 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||
|
||||
useEffect(() => {
|
||||
const fetchRootEvent = async () => {
|
||||
let root: TRootInfo = isReplaceable(event.kind)
|
||||
let root: TRootInfo = isReplaceableEvent(event.kind)
|
||||
? {
|
||||
type: 'A',
|
||||
id: getEventCoordinate(event),
|
||||
id: getReplaceableEventCoordinate(event),
|
||||
eventId: event.id,
|
||||
pubkey: event.pubkey,
|
||||
relay: client.getEventHint(event.id)
|
||||
}
|
||||
: { type: 'E', id: event.id, pubkey: event.pubkey }
|
||||
const rootEventTag = getRootEventTag(event)
|
||||
if (rootEventTag) {
|
||||
const [, rootEventHexId, , , rootEventPubkey] = rootEventTag
|
||||
const rootETag = getRootETag(event)
|
||||
if (rootETag) {
|
||||
const [, rootEventHexId, , , rootEventPubkey] = rootETag
|
||||
if (rootEventHexId && rootEventPubkey) {
|
||||
root = { type: 'E', id: rootEventHexId, pubkey: rootEventPubkey }
|
||||
} else {
|
||||
const rootEventId = generateEventIdFromETag(rootEventTag)
|
||||
const rootEventId = generateBech32IdFromETag(rootETag)
|
||||
if (rootEventId) {
|
||||
const rootEvent = await client.fetchEvent(rootEventId)
|
||||
if (rootEvent) {
|
||||
|
|
@ -83,7 +85,7 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||
}
|
||||
}
|
||||
} else if (event.kind === ExtendedKind.COMMENT) {
|
||||
const rootATag = getRootAddressableEventTag(event)
|
||||
const rootATag = getRootATag(event)
|
||||
if (rootATag) {
|
||||
const [, coordinate, relay] = rootATag
|
||||
const [, pubkey] = coordinate.split(':')
|
||||
|
|
@ -295,9 +297,9 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||
}
|
||||
}
|
||||
|
||||
const parentEventTag = getParentEventTag(reply)
|
||||
const parentEventOriginalId = parentEventTag?.[1]
|
||||
const parentEventId = parentEventTag ? generateEventIdFromETag(parentEventTag) : undefined
|
||||
const parentETag = getParentETag(reply)
|
||||
const parentEventHexId = parentETag?.[1]
|
||||
const parentEventId = parentETag ? generateBech32IdFromETag(parentETag) : undefined
|
||||
return (
|
||||
<div
|
||||
ref={(el) => (replyRefs.current[reply.id] = el)}
|
||||
|
|
@ -306,8 +308,8 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||
>
|
||||
<ReplyNote
|
||||
event={reply}
|
||||
parentEventId={event.id !== parentEventOriginalId ? parentEventId : undefined}
|
||||
onClickParent={() => parentEventOriginalId && highlightReply(parentEventOriginalId)}
|
||||
parentEventId={event.id !== parentEventHexId ? parentEventId : undefined}
|
||||
onClickParent={() => parentEventHexId && highlightReply(parentEventHexId)}
|
||||
highlight={highlightReplyId === reply.id}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue