mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-10-08 20:09:17 +02:00
small timeline improvements
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { memo } from "react";
|
||||||
import { ErrorBoundary as ErrorBoundaryHelper, FallbackProps } from "react-error-boundary";
|
import { ErrorBoundary as ErrorBoundaryHelper, FallbackProps } from "react-error-boundary";
|
||||||
import { Alert, AlertIcon, AlertTitle, AlertDescription } from "@chakra-ui/react";
|
import { Alert, AlertIcon, AlertTitle, AlertDescription } from "@chakra-ui/react";
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@ export function ErrorFallback({ error, resetErrorBoundary }: Partial<FallbackPro
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ErrorBoundary = ({ children, ...props }: { children: React.ReactNode }) => (
|
export const ErrorBoundary = memo(({ children, ...props }: { children: React.ReactNode }) => (
|
||||||
<ErrorBoundaryHelper FallbackComponent={ErrorFallback} {...props}>
|
<ErrorBoundaryHelper FallbackComponent={ErrorFallback} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</ErrorBoundaryHelper>
|
</ErrorBoundaryHelper>
|
||||||
);
|
));
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { useRef } from "react";
|
import { useRef, memo } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
@@ -111,106 +111,104 @@ export type NoteProps = Omit<CardProps, "children"> & {
|
|||||||
registerIntersectionEntity?: boolean;
|
registerIntersectionEntity?: boolean;
|
||||||
clickable?: boolean;
|
clickable?: boolean;
|
||||||
};
|
};
|
||||||
export const Note = React.memo(
|
export function Note({
|
||||||
({
|
event,
|
||||||
event,
|
variant = "outline",
|
||||||
variant = "outline",
|
showReplyButton,
|
||||||
showReplyButton,
|
showReplyLine = true,
|
||||||
showReplyLine = true,
|
hideDrawerButton,
|
||||||
hideDrawerButton,
|
registerIntersectionEntity = true,
|
||||||
registerIntersectionEntity = true,
|
clickable = true,
|
||||||
clickable = true,
|
...props
|
||||||
...props
|
}: NoteProps) {
|
||||||
}: NoteProps) => {
|
const account = useCurrentAccount();
|
||||||
const account = useCurrentAccount();
|
const { showReactions, showSignatureVerification } = useSubject(appSettings);
|
||||||
const { showReactions, showSignatureVerification } = useSubject(appSettings);
|
const replyForm = useDisclosure();
|
||||||
const replyForm = useDisclosure();
|
const detailsModal = useDisclosure();
|
||||||
const detailsModal = useDisclosure();
|
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
useRegisterIntersectionEntity(ref, event.id);
|
useRegisterIntersectionEntity(ref, event.id);
|
||||||
|
|
||||||
const showReactionsOnNewLine = useBreakpointValue({ base: true, lg: false });
|
const showReactionsOnNewLine = useBreakpointValue({ base: true, lg: false });
|
||||||
|
|
||||||
const reactionButtons = showReactions && <NoteReactions event={event} flexWrap="wrap" variant="ghost" size="sm" />;
|
const reactionButtons = showReactions && <NoteReactions event={event} flexWrap="wrap" variant="ghost" size="sm" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TrustProvider event={event}>
|
<TrustProvider event={event}>
|
||||||
<ExpandProvider>
|
<ExpandProvider>
|
||||||
<Card
|
<Card
|
||||||
as={LinkBox}
|
as={LinkBox}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
ref={registerIntersectionEntity ? ref : undefined}
|
ref={registerIntersectionEntity ? ref : undefined}
|
||||||
data-event-id={event.id}
|
data-event-id={event.id}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{clickable && (
|
{clickable && (
|
||||||
<HoverLinkOverlay
|
<HoverLinkOverlay
|
||||||
as={RouterLink}
|
as={RouterLink}
|
||||||
to={`/n/${getSharableEventAddress(event)}`}
|
to={`/n/${getSharableEventAddress(event)}`}
|
||||||
onClick={() => singleEventService.handleEvent(event)}
|
onClick={() => singleEventService.handleEvent(event)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<CardHeader p="2">
|
<CardHeader p="2">
|
||||||
<Flex flex="1" gap="2" alignItems="center">
|
<Flex flex="1" gap="2" alignItems="center">
|
||||||
<UserAvatarLink pubkey={event.pubkey} size={["xs", "sm"]} />
|
<UserAvatarLink pubkey={event.pubkey} size={["xs", "sm"]} />
|
||||||
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
|
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
|
||||||
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
|
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
|
||||||
<POWIcon event={event} boxSize={5} />
|
<POWIcon event={event} boxSize={5} />
|
||||||
<Flex grow={1} />
|
<Flex grow={1} />
|
||||||
{showSignatureVerification && <EventVerificationIcon event={event} />}
|
{showSignatureVerification && <EventVerificationIcon event={event} />}
|
||||||
{!hideDrawerButton && (
|
{!hideDrawerButton && (
|
||||||
<OpenInDrawerButton
|
<OpenInDrawerButton
|
||||||
to={`/n/${getSharableEventAddress(event)}`}
|
to={`/n/${getSharableEventAddress(event)}`}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => singleEventService.handleEvent(event)}
|
onClick={() => singleEventService.handleEvent(event)}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
|
||||||
|
<Timestamp timestamp={event.created_at} />
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
<NoteCommunityMetadata event={event} />
|
||||||
|
{showReplyLine && <ReplyLine event={event} />}
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody p="0">
|
||||||
|
<NoteContentWithWarning event={event} />
|
||||||
|
</CardBody>
|
||||||
|
<CardFooter padding="2" display="flex" gap="2" flexDirection="column" alignItems="flex-start">
|
||||||
|
{showReactionsOnNewLine && reactionButtons}
|
||||||
|
<Flex gap="2" w="full" alignItems="center">
|
||||||
|
<ButtonGroup size="sm" variant="ghost" isDisabled={account?.readonly ?? true}>
|
||||||
|
{showReplyButton && (
|
||||||
|
<IconButton icon={<ReplyIcon />} aria-label="Reply" title="Reply" onClick={replyForm.onOpen} />
|
||||||
)}
|
)}
|
||||||
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
|
<RepostButton event={event} />
|
||||||
<Timestamp timestamp={event.created_at} />
|
<QuoteRepostButton event={event} />
|
||||||
</Link>
|
<NoteZapButton event={event} />
|
||||||
</Flex>
|
</ButtonGroup>
|
||||||
<NoteCommunityMetadata event={event} />
|
{!showReactionsOnNewLine && reactionButtons}
|
||||||
{showReplyLine && <ReplyLine event={event} />}
|
<Box flexGrow={1} />
|
||||||
</CardHeader>
|
<ButtonGroup size="sm" variant="ghost">
|
||||||
<CardBody p="0">
|
<NoteProxyLink event={event} />
|
||||||
<NoteContentWithWarning event={event} />
|
<NoteDetailsButton event={event} onClick={detailsModal.onOpen} />
|
||||||
</CardBody>
|
<BookmarkButton event={event} aria-label="Bookmark note" />
|
||||||
<CardFooter padding="2" display="flex" gap="2" flexDirection="column" alignItems="flex-start">
|
<NoteMenu event={event} aria-label="More Options" detailsClick={detailsModal.onOpen} />
|
||||||
{showReactionsOnNewLine && reactionButtons}
|
</ButtonGroup>
|
||||||
<Flex gap="2" w="full" alignItems="center">
|
</Flex>
|
||||||
<ButtonGroup size="sm" variant="ghost" isDisabled={account?.readonly ?? true}>
|
</CardFooter>
|
||||||
{showReplyButton && (
|
</Card>
|
||||||
<IconButton icon={<ReplyIcon />} aria-label="Reply" title="Reply" onClick={replyForm.onOpen} />
|
</ExpandProvider>
|
||||||
)}
|
{replyForm.isOpen && (
|
||||||
<RepostButton event={event} />
|
<ReplyForm
|
||||||
<QuoteRepostButton event={event} />
|
item={{ event, replies: [], refs: getThreadReferences(event) }}
|
||||||
<NoteZapButton event={event} />
|
onCancel={replyForm.onClose}
|
||||||
</ButtonGroup>
|
onSubmitted={replyForm.onClose}
|
||||||
{!showReactionsOnNewLine && reactionButtons}
|
/>
|
||||||
<Box flexGrow={1} />
|
)}
|
||||||
<ButtonGroup size="sm" variant="ghost">
|
{detailsModal.isOpen && <EventInteractionDetailsModal isOpen onClose={detailsModal.onClose} event={event} />}
|
||||||
<NoteProxyLink event={event} />
|
</TrustProvider>
|
||||||
<NoteDetailsButton event={event} onClick={detailsModal.onOpen} />
|
);
|
||||||
<BookmarkButton event={event} aria-label="Bookmark note" />
|
}
|
||||||
<NoteMenu event={event} aria-label="More Options" detailsClick={detailsModal.onOpen} />
|
|
||||||
</ButtonGroup>
|
|
||||||
</Flex>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</ExpandProvider>
|
|
||||||
{replyForm.isOpen && (
|
|
||||||
<ReplyForm
|
|
||||||
item={{ event, replies: [], refs: getThreadReferences(event) }}
|
|
||||||
onCancel={replyForm.onClose}
|
|
||||||
onSubmitted={replyForm.onClose}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{detailsModal.isOpen && <EventInteractionDetailsModal isOpen onClose={detailsModal.onClose} event={event} />}
|
|
||||||
</TrustProvider>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Note;
|
export default memo(Note);
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useRef } from "react";
|
import { memo, useRef } from "react";
|
||||||
import { Flex, Heading, Link, Text } from "@chakra-ui/react";
|
import { Flex, Heading, Link, Text } from "@chakra-ui/react";
|
||||||
import { kinds, nip18 } from "nostr-tools";
|
import { kinds, nip18 } from "nostr-tools";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
@@ -18,7 +18,7 @@ import { parseHardcodedNoteContent } from "../../../helpers/nostr/events";
|
|||||||
import { getEventCommunityPointer } from "../../../helpers/nostr/communities";
|
import { getEventCommunityPointer } from "../../../helpers/nostr/communities";
|
||||||
import LoadingNostrLink from "../../loading-nostr-link";
|
import LoadingNostrLink from "../../loading-nostr-link";
|
||||||
|
|
||||||
export default function RepostEvent({ event }: { event: NostrEvent }) {
|
function RepostEvent({ event }: { event: NostrEvent }) {
|
||||||
const muteFilter = useUserMuteFilter();
|
const muteFilter = useUserMuteFilter();
|
||||||
const hardCodedNote = parseHardcodedNoteContent(event);
|
const hardCodedNote = parseHardcodedNoteContent(event);
|
||||||
|
|
||||||
@@ -68,3 +68,5 @@ export default function RepostEvent({ event }: { event: NostrEvent }) {
|
|||||||
</TrustProvider>
|
</TrustProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default memo(RepostEvent);
|
||||||
|
@@ -27,8 +27,6 @@ function TimelineItem({ event, visible, minHeight }: { event: NostrEvent; visibl
|
|||||||
content = isReply(event) ? <ReplyNote event={event} /> : <Note event={event} showReplyButton />;
|
content = isReply(event) ? <ReplyNote event={event} /> : <Note event={event} showReplyButton />;
|
||||||
break;
|
break;
|
||||||
case kinds.Repost:
|
case kinds.Repost:
|
||||||
content = <RepostEvent event={event} />;
|
|
||||||
break;
|
|
||||||
case kinds.GenericRepost:
|
case kinds.GenericRepost:
|
||||||
content = <RepostEvent event={event} />;
|
content = <RepostEvent event={event} />;
|
||||||
break;
|
break;
|
||||||
|
Reference in New Issue
Block a user