mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-09 20:29:17 +02:00
add reply button to note feed
This commit is contained in:
parent
6dd619613a
commit
8fd08ed3ea
5
.changeset/beige-laws-argue.md
Normal file
5
.changeset/beige-laws-argue.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add reply button to note feed
|
@ -12,6 +12,7 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Link,
|
Link,
|
||||||
useBreakpointValue,
|
useBreakpointValue,
|
||||||
|
useDisclosure,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { UserAvatarLink } from "../user-avatar-link";
|
import { UserAvatarLink } from "../user-avatar-link";
|
||||||
@ -27,23 +28,26 @@ import appSettings from "../../services/settings/app-settings";
|
|||||||
import EventVerificationIcon from "../event-verification-icon";
|
import EventVerificationIcon from "../event-verification-icon";
|
||||||
import { RepostButton } from "./components/repost-button";
|
import { RepostButton } from "./components/repost-button";
|
||||||
import { QuoteRepostButton } from "./components/quote-repost-button";
|
import { QuoteRepostButton } from "./components/quote-repost-button";
|
||||||
import { ExternalLinkIcon } from "../icons";
|
import { ExternalLinkIcon, ReplyIcon } from "../icons";
|
||||||
import NoteContentWithWarning from "./note-content-with-warning";
|
import NoteContentWithWarning from "./note-content-with-warning";
|
||||||
import { TrustProvider } from "../../providers/trust";
|
import { TrustProvider } from "../../providers/trust";
|
||||||
import { NoteLink } from "../note-link";
|
import { NoteLink } from "../note-link";
|
||||||
import { useRegisterIntersectionEntity } from "../../providers/intersection-observer";
|
import { useRegisterIntersectionEntity } from "../../providers/intersection-observer";
|
||||||
import BookmarkButton from "./components/bookmark-button";
|
import BookmarkButton from "./components/bookmark-button";
|
||||||
import EventReactionButtons from "../event-reactions";
|
|
||||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||||
import NoteReactions from "./components/note-reactions";
|
import NoteReactions from "./components/note-reactions";
|
||||||
|
import ReplyForm from "../../views/note/components/reply-form";
|
||||||
|
import { getReferences } from "../../helpers/nostr/events";
|
||||||
|
|
||||||
export type NoteProps = {
|
export type NoteProps = {
|
||||||
event: NostrEvent;
|
event: NostrEvent;
|
||||||
variant?: CardProps["variant"];
|
variant?: CardProps["variant"];
|
||||||
|
showReplyButton?: boolean;
|
||||||
};
|
};
|
||||||
export const Note = React.memo(({ event, variant = "outline" }: NoteProps) => {
|
export const Note = React.memo(({ event, variant = "outline", showReplyButton }: NoteProps) => {
|
||||||
const account = useCurrentAccount();
|
const account = useCurrentAccount();
|
||||||
const { showReactions, showSignatureVerification } = useSubject(appSettings);
|
const { showReactions, showSignatureVerification } = useSubject(appSettings);
|
||||||
|
const replyForm = useDisclosure();
|
||||||
|
|
||||||
// if there is a parent intersection observer, register this card
|
// if there is a parent intersection observer, register this card
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
@ -79,6 +83,9 @@ export const Note = React.memo(({ event, variant = "outline" }: NoteProps) => {
|
|||||||
{showReactionsOnNewLine && reactionButtons}
|
{showReactionsOnNewLine && reactionButtons}
|
||||||
<Flex gap="2" w="full" alignItems="center">
|
<Flex gap="2" w="full" alignItems="center">
|
||||||
<ButtonGroup size="xs" variant="ghost" isDisabled={account?.readonly ?? true}>
|
<ButtonGroup size="xs" variant="ghost" isDisabled={account?.readonly ?? true}>
|
||||||
|
{showReplyButton && (
|
||||||
|
<IconButton icon={<ReplyIcon />} aria-label="Reply" title="Reply" onClick={replyForm.onOpen} />
|
||||||
|
)}
|
||||||
<RepostButton event={event} />
|
<RepostButton event={event} />
|
||||||
<QuoteRepostButton event={event} />
|
<QuoteRepostButton event={event} />
|
||||||
<NoteZapButton event={event} />
|
<NoteZapButton event={event} />
|
||||||
@ -103,6 +110,15 @@ export const Note = React.memo(({ event, variant = "outline" }: NoteProps) => {
|
|||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</ExpandProvider>
|
</ExpandProvider>
|
||||||
|
{replyForm.isOpen && (
|
||||||
|
<ReplyForm
|
||||||
|
item={{ event, replies: [], refs: getReferences(event) }}
|
||||||
|
onCancel={replyForm.onClose}
|
||||||
|
onSubmitted={replyForm.onClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TrustProvider>
|
</TrustProvider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default Note;
|
||||||
|
@ -15,7 +15,7 @@ import { safeRelayUrl } from "../../../helpers/url";
|
|||||||
const RenderEvent = React.memo(({ event }: { event: NostrEvent }) => {
|
const RenderEvent = React.memo(({ event }: { event: NostrEvent }) => {
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Text:
|
case Kind.Text:
|
||||||
return <Note event={event} />;
|
return <Note event={event} showReplyButton />;
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote event={event} />;
|
return <RepostNote event={event} />;
|
||||||
case STREAM_KIND:
|
case STREAM_KIND:
|
||||||
|
@ -63,7 +63,7 @@ export default function RepostNote({ event }: { event: NostrEvent }) {
|
|||||||
</Text>
|
</Text>
|
||||||
<NoteMenu event={event} size="sm" variant="link" aria-label="note options" />
|
<NoteMenu event={event} size="sm" variant="link" aria-label="note options" />
|
||||||
</Flex>
|
</Flex>
|
||||||
{loading ? <SkeletonText /> : note ? <Note event={note} /> : <ErrorFallback error={error} />}
|
{loading ? <SkeletonText /> : note ? <Note event={note} showReplyButton /> : <ErrorFallback error={error} />}
|
||||||
</Flex>
|
</Flex>
|
||||||
</TrustProvider>
|
</TrustProvider>
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,7 @@ import { Flex, Spinner } from "@chakra-ui/react";
|
|||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
import { Note } from "../../components/note";
|
import Note from "../../components/note";
|
||||||
import { isHexKey } from "../../helpers/nip19";
|
import { isHexKey } from "../../helpers/nip19";
|
||||||
import { useThreadLoader } from "../../hooks/use-thread-loader";
|
import { useThreadLoader } from "../../hooks/use-thread-loader";
|
||||||
import { ThreadPost } from "./components/thread-post";
|
import { ThreadPost } from "./components/thread-post";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Box, Button, useToast } from "@chakra-ui/react";
|
import { Box, Button, Flex, useToast } from "@chakra-ui/react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { ParsedStream, buildChatMessage } from "../../../../helpers/nostr/stream";
|
import { ParsedStream, buildChatMessage } from "../../../../helpers/nostr/stream";
|
||||||
@ -45,17 +45,19 @@ export default function ChatMessageForm({ stream }: { stream: ParsedStream }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box as="form" borderRadius="md" flexShrink={0} display="flex" gap="2" px="2" pb="2" onSubmit={sendMessage}>
|
<Box borderRadius="md" flexShrink={0} display="flex" gap="2" px="2" pb="2">
|
||||||
<MagicInput
|
<Flex as="form" onSubmit={sendMessage} gap="2" flex={1}>
|
||||||
placeholder="Message"
|
<MagicInput
|
||||||
autoComplete="off"
|
placeholder="Message"
|
||||||
isRequired
|
autoComplete="off"
|
||||||
value={getValues().content}
|
isRequired
|
||||||
onChange={(e) => setValue("content", e.target.value)}
|
value={getValues().content}
|
||||||
/>
|
onChange={(e) => setValue("content", e.target.value)}
|
||||||
<Button colorScheme="brand" type="submit" isLoading={formState.isSubmitting}>
|
/>
|
||||||
Send
|
<Button colorScheme="brand" type="submit" isLoading={formState.isSubmitting}>
|
||||||
</Button>
|
Send
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
<StreamZapButton stream={stream} onZap={reset} initComment={getValues().content} />
|
<StreamZapButton stream={stream} onZap={reset} initComment={getValues().content} />
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user