You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.5 KiB
128 lines
4.5 KiB
import ContentPreview from '@/components/ContentPreview' |
|
import UserAvatar from '@/components/UserAvatar' |
|
import { Skeleton } from '@/components/ui/skeleton' |
|
import { Separator } from '@/components/ui/separator' |
|
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' |
|
import { |
|
getParentBech32Id, |
|
getParentEventHexId, |
|
getRootBech32Id, |
|
getRootEventHexId |
|
} from '@/lib/event' |
|
import { getCachedThreadContextEvents } from '@/lib/navigation-related-events' |
|
import { resolveNoteEventSync } from '@/lib/resolve-note-event-sync' |
|
import client from '@/services/client.service' |
|
import type { Event } from 'nostr-tools' |
|
import { useTranslation } from 'react-i18next' |
|
|
|
function peekThreadContextEvent(bech32OrHex: string | undefined): Event | undefined { |
|
if (!bech32OrHex?.trim()) return undefined |
|
return client.peekSessionCachedEvent(bech32OrHex.trim()) |
|
} |
|
|
|
function ThreadContextPreview({ event }: { event: Event }) { |
|
return ( |
|
<div className="mb-2 rounded-lg border border-border/80 bg-muted/20 px-3 py-2 opacity-90"> |
|
<div className="flex items-center gap-2"> |
|
<UserAvatar userId={event.pubkey} size="small" className="shrink-0" deferRemoteAvatar={false} /> |
|
<div className="min-w-0 flex-1 truncate text-sm text-muted-foreground"> |
|
<ContentPreview event={event} /> |
|
</div> |
|
</div> |
|
</div> |
|
) |
|
} |
|
|
|
/** |
|
* Shown while the lazy NotePage chunk loads. Renders the clicked note from the navigation/session |
|
* cache when available so the secondary panel is not blank. |
|
*/ |
|
export default function NotePageInstantShell({ |
|
id, |
|
index, |
|
hideTitlebar = false, |
|
initialEvent |
|
}: { |
|
id?: string |
|
index?: number |
|
hideTitlebar?: boolean |
|
initialEvent?: Event |
|
}) { |
|
const { t } = useTranslation() |
|
const event = resolveNoteEventSync(id, initialEvent) |
|
|
|
if (!event) { |
|
return ( |
|
<SecondaryPageLayout |
|
index={index} |
|
title={hideTitlebar ? undefined : t('Note')} |
|
> |
|
<div className="px-4 pt-3"> |
|
<div className="flex items-center space-x-2"> |
|
<Skeleton className="h-10 w-10 rounded-full" /> |
|
<div className="flex-1 w-0"> |
|
<div className="py-1"> |
|
<Skeleton className="h-4 w-16" /> |
|
</div> |
|
<div className="py-0.5"> |
|
<Skeleton className="h-3 w-12" /> |
|
</div> |
|
</div> |
|
</div> |
|
<div className="pt-2"> |
|
<div className="my-1"> |
|
<Skeleton className="my-1 mt-2 h-4 w-full" /> |
|
</div> |
|
<div className="my-1"> |
|
<Skeleton className="my-1 h-4 w-2/3" /> |
|
</div> |
|
</div> |
|
</div> |
|
</SecondaryPageLayout> |
|
) |
|
} |
|
|
|
const cachedContext = getCachedThreadContextEvents(event) |
|
const parentBech32 = getParentBech32Id(event) |
|
const rootBech32 = getRootBech32Id(event) |
|
const parentHex = getParentEventHexId(event)?.toLowerCase() |
|
const rootHex = getRootEventHexId(event)?.toLowerCase() |
|
const selfHex = event.id.toLowerCase() |
|
|
|
const parentEvent = |
|
cachedContext.find((e) => parentHex && e.id.toLowerCase() === parentHex) ?? |
|
peekThreadContextEvent(parentBech32) |
|
const rootEvent = |
|
cachedContext.find((e) => rootHex && e.id.toLowerCase() === rootHex) ?? |
|
peekThreadContextEvent(rootBech32) |
|
|
|
const parentEventForStrip = |
|
parentEvent && parentEvent.id.toLowerCase() !== selfHex ? parentEvent : undefined |
|
const rootEventForStrip = |
|
rootEvent && rootEvent.id.toLowerCase() !== selfHex ? rootEvent : undefined |
|
|
|
return ( |
|
<SecondaryPageLayout index={index} title={hideTitlebar ? undefined : t('Note')}> |
|
<div className="w-full px-4 pt-3"> |
|
{rootEventForStrip && parentEventForStrip?.id !== rootEventForStrip.id && ( |
|
<ThreadContextPreview event={rootEventForStrip} /> |
|
)} |
|
{parentEventForStrip && ( |
|
<> |
|
<ThreadContextPreview event={parentEventForStrip} /> |
|
<div className="ml-5 h-3 w-px bg-border" /> |
|
</> |
|
)} |
|
{(rootEventForStrip || parentEventForStrip) && <Separator className="my-3" />} |
|
<div className="select-text"> |
|
<div className="flex items-start gap-2"> |
|
<UserAvatar userId={event.pubkey} size="normal" className="shrink-0" deferRemoteAvatar={false} /> |
|
<div className="min-w-0 flex-1"> |
|
<ContentPreview event={event} className="text-base text-foreground whitespace-pre-wrap break-words" /> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</SecondaryPageLayout> |
|
) |
|
}
|
|
|