From 0f998eac923a35c4bef3f74e7ba11f0b7e34a383 Mon Sep 17 00:00:00 2001 From: Jure <44338+hoornet@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:28:59 +0100 Subject: [PATCH] Add Quote / Repost (NIP-18, roadmap #6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - publishRepost: kind 6 event with stringified original event as content and ["e", id, "", "mention"] + ["p", pubkey] tags - publishQuote: kind 1 note with user's text + appended nostr:nevent1... reference and ["q", id] + ["p", pubkey] tags - QuoteModal: compose modal with live quoted-note preview (avatar, name, truncated content); Ctrl+Enter to post, Escape to close - NoteCard: "repost" (one-click, shows "reposted ✓") and "quote" (opens QuoteModal) added to the actions row alongside reply/like/zap Co-Authored-By: Claude Sonnet 4.6 --- src/components/feed/NoteCard.tsx | 41 +++++++++++- src/components/feed/QuoteModal.tsx | 102 +++++++++++++++++++++++++++++ src/lib/nostr/client.ts | 33 +++++++++- src/lib/nostr/index.ts | 2 +- 4 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 src/components/feed/QuoteModal.tsx diff --git a/src/components/feed/NoteCard.tsx b/src/components/feed/NoteCard.tsx index 07832f0..82a0383 100644 --- a/src/components/feed/NoteCard.tsx +++ b/src/components/feed/NoteCard.tsx @@ -6,9 +6,10 @@ import { useUserStore } from "../../stores/user"; import { useMuteStore } from "../../stores/mute"; import { useUIStore } from "../../stores/ui"; import { timeAgo, shortenPubkey } from "../../lib/utils"; -import { publishReaction, publishReply, getNDK } from "../../lib/nostr"; +import { publishReaction, publishReply, publishRepost, getNDK } from "../../lib/nostr"; import { NoteContent } from "./NoteContent"; import { ZapModal } from "../zap/ZapModal"; +import { QuoteModal } from "./QuoteModal"; interface NoteCardProps { event: NDKEvent; @@ -40,6 +41,9 @@ export function NoteCard({ event }: NoteCardProps) { const [replySent, setReplySent] = useState(false); const replyRef = useRef(null); const [showZap, setShowZap] = useState(false); + const [showQuote, setShowQuote] = useState(false); + const [reposting, setReposting] = useState(false); + const [reposted, setReposted] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const handleLike = async () => { @@ -83,6 +87,17 @@ export function NoteCard({ event }: NoteCardProps) { if (e.key === "Escape") setShowReply(false); }; + const handleRepost = async () => { + if (reposting || reposted) return; + setReposting(true); + try { + await publishRepost(event); + setReposted(true); + } finally { + setReposting(false); + } + }; + return (
@@ -169,6 +184,21 @@ export function NoteCard({ event }: NoteCardProps) { > {liked ? "♥" : "♡"}{reactionCount !== null && reactionCount > 0 ? ` ${reactionCount}` : liked ? " liked" : " like"} + + +
+ + {/* Compose */} +
+