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.
117 lines
3.8 KiB
117 lines
3.8 KiB
import { Skeleton } from '@/components/ui/skeleton' |
|
import { isMentioningMutedUsers, isNip18RepostKind, isNip56ReportEvent } from '@/lib/event' |
|
import ReportCard from '@/components/ReportCard' |
|
import { useContentPolicy } from '@/providers/ContentPolicyProvider' |
|
import { useMuteList } from '@/contexts/mute-list-context' |
|
import { muteSetHas } from '@/lib/mute-set' |
|
import { Event } from 'nostr-tools' |
|
import { memo, useMemo } from 'react' |
|
import MainNoteCard from './MainNoteCard' |
|
import RepostNoteCard from './RepostNoteCard' |
|
|
|
const NoteCard = memo(function NoteCard({ |
|
event, |
|
className, |
|
filterMutedNotes = true, |
|
pinned = false, |
|
hideParentNotePreview = false, |
|
bottomNoteLabel, |
|
fetchNoteStatsIfMissing = true, |
|
deferAuthorAvatar = true, |
|
searchListPreview = false |
|
}: { |
|
event: Event |
|
className?: string |
|
filterMutedNotes?: boolean |
|
pinned?: boolean |
|
/** When true, hide the parent/root note preview (e.g. when showing quotes of the current note). */ |
|
hideParentNotePreview?: boolean |
|
/** Optional label rendered at the bottom of the card (e.g. why this event is in a composed feed). */ |
|
bottomNoteLabel?: string |
|
fetchNoteStatsIfMissing?: boolean |
|
deferAuthorAvatar?: boolean |
|
searchListPreview?: boolean |
|
}) { |
|
const { mutePubkeySet } = useMuteList() |
|
const { hideContentMentioningMutedUsers } = useContentPolicy() |
|
const shouldHide = useMemo(() => { |
|
if (filterMutedNotes && muteSetHas(mutePubkeySet, event.pubkey)) { |
|
return true |
|
} |
|
if (hideContentMentioningMutedUsers && isMentioningMutedUsers(event, mutePubkeySet)) { |
|
return true |
|
} |
|
return false |
|
}, [event, filterMutedNotes, mutePubkeySet]) |
|
if (shouldHide) return null |
|
|
|
if (isNip56ReportEvent(event)) { |
|
return <ReportCard event={event} className={className} /> |
|
} |
|
if (isNip18RepostKind(event.kind)) { |
|
return ( |
|
<RepostNoteCard |
|
event={event} |
|
className={className} |
|
filterMutedNotes={filterMutedNotes} |
|
pinned={pinned} |
|
bottomNoteLabel={bottomNoteLabel} |
|
deferAuthorAvatar={deferAuthorAvatar} |
|
/> |
|
) |
|
} |
|
return ( |
|
<MainNoteCard |
|
event={event} |
|
className={className} |
|
pinned={pinned} |
|
hideParentNotePreview={hideParentNotePreview} |
|
bottomNoteLabel={bottomNoteLabel} |
|
fetchNoteStatsIfMissing={fetchNoteStatsIfMissing} |
|
deferAuthorAvatar={deferAuthorAvatar} |
|
searchListPreview={searchListPreview} |
|
/> |
|
) |
|
}, (prevProps, nextProps) => { |
|
// Custom comparison function for memo |
|
return ( |
|
prevProps.event.id === nextProps.event.id && |
|
prevProps.event.created_at === nextProps.event.created_at && |
|
prevProps.className === nextProps.className && |
|
prevProps.filterMutedNotes === nextProps.filterMutedNotes && |
|
prevProps.pinned === nextProps.pinned && |
|
prevProps.hideParentNotePreview === nextProps.hideParentNotePreview && |
|
prevProps.bottomNoteLabel === nextProps.bottomNoteLabel && |
|
prevProps.fetchNoteStatsIfMissing === nextProps.fetchNoteStatsIfMissing && |
|
prevProps.deferAuthorAvatar === nextProps.deferAuthorAvatar && |
|
prevProps.searchListPreview === nextProps.searchListPreview |
|
) |
|
}) |
|
|
|
export default NoteCard |
|
|
|
export function NoteCardLoadingSkeleton() { |
|
return ( |
|
<div className="px-4 py-3"> |
|
<div className="flex items-center space-x-2"> |
|
<Skeleton className="w-10 h-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="w-full h-4 my-1 mt-2" /> |
|
</div> |
|
<div className="my-1"> |
|
<Skeleton className="w-2/3 h-4 my-1" /> |
|
</div> |
|
</div> |
|
</div> |
|
) |
|
}
|
|
|