mirror of
https://github.com/hoornet/vega.git
synced 2026-05-09 05:39:10 -07:00
Add Quote / Repost (NIP-18, roadmap #6)
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -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<HTMLTextAreaElement>(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 (
|
||||
<article className="border-b border-border px-4 py-3 hover:bg-bg-hover transition-colors duration-100 group/card">
|
||||
<div className="flex gap-3">
|
||||
@@ -169,6 +184,21 @@ export function NoteCard({ event }: NoteCardProps) {
|
||||
>
|
||||
{liked ? "♥" : "♡"}{reactionCount !== null && reactionCount > 0 ? ` ${reactionCount}` : liked ? " liked" : " like"}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleRepost}
|
||||
disabled={reposting || reposted}
|
||||
className={`text-[11px] transition-colors disabled:cursor-default ${
|
||||
reposted ? "text-accent" : "text-text-dim hover:text-accent"
|
||||
}`}
|
||||
>
|
||||
{reposted ? "reposted ✓" : reposting ? "…" : "repost"}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowQuote(true)}
|
||||
className="text-[11px] text-text-dim hover:text-text transition-colors"
|
||||
>
|
||||
quote
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowZap(true)}
|
||||
className="text-[11px] text-text-dim hover:text-zap transition-colors"
|
||||
@@ -186,6 +216,15 @@ export function NoteCard({ event }: NoteCardProps) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showQuote && (
|
||||
<QuoteModal
|
||||
event={event}
|
||||
authorName={name}
|
||||
authorAvatar={avatar}
|
||||
onClose={() => setShowQuote(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Inline reply box */}
|
||||
{showReply && (
|
||||
<div className="mt-2 border-l-2 border-border pl-3">
|
||||
|
||||
Reference in New Issue
Block a user