feat: quote repost
All checks were successful
continuous-integration/drone/push Build is passing

closes #217
This commit is contained in:
Kieran 2023-10-13 12:40:39 +01:00
parent ddb8e623f4
commit a647fd09b3
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
5 changed files with 101 additions and 25 deletions

View File

@ -95,6 +95,22 @@ export function NoteCreator() {
extraTags ??= [];
extraTags.push(...note.pollOptions.map((a, i) => ["poll_option", i.toString(), a]));
}
// add quote repost
if (note.quote) {
if (!note.note.endsWith("\n")) {
note.note += "\n";
}
const link = NostrLink.fromEvent(note.quote);
note.note += `nostr:${link.encode()}`;
const quoteTag = link.toEventTag();
if (quoteTag) {
extraTags ??= [];
if (quoteTag[0] === "e") {
quoteTag[0] = "q"; // how to 'q' tag replacable events?
}
extraTags.push(quoteTag);
}
}
const hk = (eb: EventBuilder) => {
extraTags?.forEach(t => eb.tag(t));
eb.kind(kind);
@ -464,18 +480,42 @@ export function NoteCreator() {
return (
<>
{note.replyTo && (
<Note
data={note.replyTo}
related={[]}
options={{
showFooter: false,
showContextMenu: false,
showTime: false,
canClick: false,
showMedia: false,
longFormPreview: true,
}}
/>
<>
<h4>
<FormattedMessage defaultMessage="Reply To" />
</h4>
<Note
data={note.replyTo}
related={[]}
options={{
showFooter: false,
showContextMenu: false,
showTime: false,
canClick: false,
showMedia: false,
longFormPreview: true,
}}
/>
</>
)}
{note.quote && (
<>
<h4>
<FormattedMessage defaultMessage="Quote Repost" />
</h4>
<Note
data={note.quote}
related={[]}
options={{
showFooter: false,
showContextMenu: false,
showTime: false,
canClick: false,
showMedia: false,
longFormPreview: true,
}}
/>
</>
)}
{note.preview && getPreviewNote()}
{!note.preview && (

View File

@ -1,8 +1,9 @@
import React, { HTMLProps, useContext, useEffect, useState } from "react";
import { useIntl } from "react-intl";
import { FormattedMessage, useIntl } from "react-intl";
import { useLongPress } from "use-long-press";
import { TaggedNostrEvent, ParsedZap, countLeadingZeros, NostrLink } from "@snort/system";
import { SnortContext, useUserProfile } from "@snort/system-react";
import { Menu, MenuItem } from "@szhsin/react-menu";
import { formatShort } from "Number";
import useEventPublisher from "Hooks/useEventPublisher";
@ -20,6 +21,7 @@ import { System } from "index";
import { Zapper, ZapTarget } from "Zapper";
import { getDisplayName } from "Element/User/DisplayName";
import { useNoteCreator } from "State/NoteCreator";
import Icon from "Icons/Icon";
import messages from "../messages";
@ -56,8 +58,8 @@ export default function NoteFooter(props: NoteFooterProps) {
const author = useUserProfile(ev.pubkey);
const interactionCache = useInteractionCache(publicKey, ev.id);
const publisher = useEventPublisher();
const note = useNoteCreator(n => ({ show: n.show, replyTo: n.replyTo, update: n.update }));
const willRenderNoteCreator = note.show && note.replyTo?.id === ev.id;
const note = useNoteCreator(n => ({ show: n.show, replyTo: n.replyTo, update: n.update, quote: n.quote }));
const willRenderNoteCreator = note.show && (note.replyTo?.id === ev.id || note.quote);
const [tip, setTip] = useState(false);
const [zapping, setZapping] = useState(false);
const walletState = useWallet();
@ -211,16 +213,40 @@ export default function NoteFooter(props: NoteFooterProps) {
function repostIcon() {
if (readonly) return;
return (
<AsyncFooterIcon
className={hasReposted() ? "reacted" : ""}
iconName="repeat"
title={formatMessage({ defaultMessage: "Repost" })}
value={reposts.length}
onClick={async () => {
if (readonly) return;
await repost();
}}
/>
<Menu
menuButton={
<AsyncFooterIcon
className={hasReposted() ? "reacted" : ""}
iconName="repeat"
title={formatMessage({ defaultMessage: "Repost" })}
value={reposts.length}
/>
}
menuClassName="ctx-menu"
align="start">
<div className="close-menu-container">
{/* This menu item serves as a "close menu" button;
it allows the user to click anywhere nearby the menu to close it. */}
<MenuItem>
<div className="close-menu" />
</MenuItem>
</div>
<MenuItem onClick={() => repost()} disabled={hasReposted()}>
<Icon name="repeat" />
<FormattedMessage defaultMessage="Repost" />
</MenuItem>
<MenuItem
onClick={() =>
note.update(n => {
n.reset();
n.quote = ev;
n.show = true;
})
}>
<Icon name="edit" />
<FormattedMessage defaultMessage="Quote Repost" />
</MenuItem>
</Menu>
);
}

View File

@ -12,6 +12,7 @@ interface NoteCreatorDataSnapshot {
advanced: boolean;
preview?: NostrEvent;
replyTo?: TaggedNostrEvent;
quote?: TaggedNostrEvent;
selectedCustomRelays?: Array<string>;
zapSplits?: Array<ZapTarget>;
sensitive?: string;
@ -55,6 +56,7 @@ class NoteCreatorStore extends ExternalStore<NoteCreatorDataSnapshot> {
d.sendStarted = false;
d.preview = undefined;
d.replyTo = undefined;
d.quote = undefined;
d.selectedCustomRelays = undefined;
d.zapSplits = undefined;
d.sensitive = undefined;

View File

@ -241,6 +241,9 @@
"89q5wc": {
"defaultMessage": "Confirm Reposts"
},
"8ED/4u": {
"defaultMessage": "Reply To"
},
"8Kboo2": {
"defaultMessage": "Scan this QR code with your signer app to get started"
},
@ -344,6 +347,9 @@
"C5xzTC": {
"defaultMessage": "Premium"
},
"C7642/": {
"defaultMessage": "Quote Repost"
},
"C81/uG": {
"defaultMessage": "Logout"
},

View File

@ -79,6 +79,7 @@
"7hp70g": "NIP-05",
"8/vBbP": "Reposts ({n})",
"89q5wc": "Confirm Reposts",
"8ED/4u": "Reply To",
"8Kboo2": "Scan this QR code with your signer app to get started",
"8QDesP": "Zap {n} sats",
"8Rkoyb": "Recipient",
@ -112,6 +113,7 @@
"BjNwZW": "Nostr address (nip05)",
"C1LjMx": "Lightning Donation",
"C5xzTC": "Premium",
"C7642/": "Quote Repost",
"C81/uG": "Logout",
"C8HhVE": "Suggested Follows",
"CHTbO3": "Failed to load invoice",