forked from Kieran/snort
feat: per event zap targets
This commit is contained in:
parent
293510069c
commit
8ef20c27b3
@ -1,6 +1,6 @@
|
|||||||
import "./NoteCreator.css";
|
import "./NoteCreator.css";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { RawEvent, TaggedRawEvent } from "@snort/nostr";
|
import { RawEvent, TaggedRawEvent } from "@snort/nostr";
|
||||||
|
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
@ -11,6 +11,7 @@ import Modal from "Element/Modal";
|
|||||||
import ProfileImage from "Element/ProfileImage";
|
import ProfileImage from "Element/ProfileImage";
|
||||||
import useFileUpload from "Upload";
|
import useFileUpload from "Upload";
|
||||||
import Note from "Element/Note";
|
import Note from "Element/Note";
|
||||||
|
import { LNURL } from "LNURL";
|
||||||
|
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
|
|
||||||
@ -40,16 +41,34 @@ export interface NoteCreatorProps {
|
|||||||
|
|
||||||
export function NoteCreator(props: NoteCreatorProps) {
|
export function NoteCreator(props: NoteCreatorProps) {
|
||||||
const { show, setShow, replyTo, onSend, autoFocus } = props;
|
const { show, setShow, replyTo, onSend, autoFocus } = props;
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
const publisher = useEventPublisher();
|
const publisher = useEventPublisher();
|
||||||
const [note, setNote] = useState("");
|
const [note, setNote] = useState("");
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [active, setActive] = useState(false);
|
const [active, setActive] = useState(false);
|
||||||
const [preview, setPreview] = useState<RawEvent>();
|
const [preview, setPreview] = useState<RawEvent>();
|
||||||
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||||
|
const [zapForward, setZapForward] = useState("");
|
||||||
const uploader = useFileUpload();
|
const uploader = useFileUpload();
|
||||||
|
|
||||||
async function sendNote() {
|
async function sendNote() {
|
||||||
if (note) {
|
if (note) {
|
||||||
const ev = replyTo ? await publisher.reply(replyTo, note) : await publisher.note(note);
|
let extraTags: Array<Array<string>> | undefined;
|
||||||
|
if (zapForward) {
|
||||||
|
try {
|
||||||
|
const svc = new LNURL(zapForward);
|
||||||
|
await svc.load();
|
||||||
|
} catch {
|
||||||
|
setError(
|
||||||
|
formatMessage({
|
||||||
|
defaultMessage: "Invalid LNURL",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
extraTags = [["zap", zapForward]];
|
||||||
|
}
|
||||||
|
const ev = replyTo ? await publisher.reply(replyTo, note, extraTags) : await publisher.note(note, extraTags);
|
||||||
console.debug("Sending note: ", ev);
|
console.debug("Sending note: ", ev);
|
||||||
publisher.broadcast(ev);
|
publisher.broadcast(ev);
|
||||||
setNote("");
|
setNote("");
|
||||||
@ -152,6 +171,9 @@ export function NoteCreator(props: NoteCreatorProps) {
|
|||||||
<button className="secondary" type="button" onClick={loadPreview}>
|
<button className="secondary" type="button" onClick={loadPreview}>
|
||||||
<FormattedMessage defaultMessage="Toggle Preview" />
|
<FormattedMessage defaultMessage="Toggle Preview" />
|
||||||
</button>
|
</button>
|
||||||
|
<button className="secondary" type="button" onClick={() => setShowAdvanced(s => !s)}>
|
||||||
|
<FormattedMessage defaultMessage="Advanced" />
|
||||||
|
</button>
|
||||||
<button className="secondary" type="button" onClick={cancel}>
|
<button className="secondary" type="button" onClick={cancel}>
|
||||||
<FormattedMessage {...messages.Cancel} />
|
<FormattedMessage {...messages.Cancel} />
|
||||||
</button>
|
</button>
|
||||||
@ -159,6 +181,25 @@ export function NoteCreator(props: NoteCreatorProps) {
|
|||||||
{replyTo ? <FormattedMessage {...messages.Reply} /> : <FormattedMessage {...messages.Send} />}
|
{replyTo ? <FormattedMessage {...messages.Reply} /> : <FormattedMessage {...messages.Send} />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{showAdvanced && (
|
||||||
|
<>
|
||||||
|
<h4>
|
||||||
|
<FormattedMessage defaultMessage="Forward Zaps" />
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage defaultMessage="All zaps sent to this note will be received by the following LNURL" />
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="w-max"
|
||||||
|
placeholder={formatMessage({
|
||||||
|
defaultMessage: "LNURL to forward zaps to",
|
||||||
|
})}
|
||||||
|
value={zapForward}
|
||||||
|
onChange={e => setZapForward(e.target.value)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -153,10 +153,18 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLNURL() {
|
||||||
|
return ev.Tags.find(a => a.Key === "zap")?.LNURL || author?.lud16 || author?.lud06;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTargetName() {
|
||||||
|
return ev.Tags.find(a => a.Key === "zap")?.LNURL || author?.display_name || author?.name;
|
||||||
|
}
|
||||||
|
|
||||||
async function fastZap(e?: React.MouseEvent) {
|
async function fastZap(e?: React.MouseEvent) {
|
||||||
if (zapping || e?.isPropagationStopped()) return;
|
if (zapping || e?.isPropagationStopped()) return;
|
||||||
|
|
||||||
const lnurl = author?.lud16 || author?.lud06;
|
const lnurl = getLNURL();
|
||||||
if (wallet?.isReady() && lnurl) {
|
if (wallet?.isReady() && lnurl) {
|
||||||
setZapping(true);
|
setZapping(true);
|
||||||
try {
|
try {
|
||||||
@ -203,7 +211,7 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (prefs.autoZap && !ZapCache.has(ev.id) && !isMine && !zapping) {
|
if (prefs.autoZap && !ZapCache.has(ev.id) && !isMine && !zapping) {
|
||||||
const lnurl = author?.lud16 || author?.lud06;
|
const lnurl = getLNURL();
|
||||||
if (wallet?.isReady() && lnurl) {
|
if (wallet?.isReady() && lnurl) {
|
||||||
setZapping(true);
|
setZapping(true);
|
||||||
queueMicrotask(async () => {
|
queueMicrotask(async () => {
|
||||||
@ -222,7 +230,7 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
}, [prefs.autoZap, author, zapping]);
|
}, [prefs.autoZap, author, zapping]);
|
||||||
|
|
||||||
function tipButton() {
|
function tipButton() {
|
||||||
const service = author?.lud16 || author?.lud06;
|
const service = getLNURL();
|
||||||
if (service) {
|
if (service) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -418,11 +426,11 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
zaps={zaps}
|
zaps={zaps}
|
||||||
/>
|
/>
|
||||||
<SendSats
|
<SendSats
|
||||||
lnurl={author?.lud16 || author?.lud06}
|
lnurl={getLNURL()}
|
||||||
onClose={() => setTip(false)}
|
onClose={() => setTip(false)}
|
||||||
show={tip}
|
show={tip}
|
||||||
author={author?.pubkey}
|
author={author?.pubkey}
|
||||||
target={author?.display_name || author?.name}
|
target={getTargetName()}
|
||||||
note={ev.id}
|
note={ev.id}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -176,10 +176,15 @@ export default function useEventPublisher() {
|
|||||||
return await signEvent(ev);
|
return await signEvent(ev);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
note: async (msg: string) => {
|
note: async (msg: string, extraTags?: Array<Array<string>>) => {
|
||||||
if (pubKey) {
|
if (pubKey) {
|
||||||
const ev = EventExt.forPubKey(pubKey, EventKind.TextNote);
|
const ev = EventExt.forPubKey(pubKey, EventKind.TextNote);
|
||||||
processContent(ev, msg);
|
processContent(ev, msg);
|
||||||
|
if (extraTags) {
|
||||||
|
for (const et of extraTags) {
|
||||||
|
ev.tags.push(et);
|
||||||
|
}
|
||||||
|
}
|
||||||
return await signEvent(ev);
|
return await signEvent(ev);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -200,7 +205,7 @@ export default function useEventPublisher() {
|
|||||||
/**
|
/**
|
||||||
* Reply to a note
|
* Reply to a note
|
||||||
*/
|
*/
|
||||||
reply: async (replyTo: TaggedRawEvent, msg: string) => {
|
reply: async (replyTo: TaggedRawEvent, msg: string, extraTags?: Array<Array<string>>) => {
|
||||||
if (pubKey) {
|
if (pubKey) {
|
||||||
const ev = EventExt.forPubKey(pubKey, EventKind.TextNote);
|
const ev = EventExt.forPubKey(pubKey, EventKind.TextNote);
|
||||||
|
|
||||||
@ -230,6 +235,11 @@ export default function useEventPublisher() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
processContent(ev, msg);
|
processContent(ev, msg);
|
||||||
|
if (extraTags) {
|
||||||
|
for (const et of extraTags) {
|
||||||
|
ev.tags.push(et);
|
||||||
|
}
|
||||||
|
}
|
||||||
return await signEvent(ev);
|
return await signEvent(ev);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -78,6 +78,9 @@
|
|||||||
"2k0Cv+": {
|
"2k0Cv+": {
|
||||||
"defaultMessage": "Dislikes ({n})"
|
"defaultMessage": "Dislikes ({n})"
|
||||||
},
|
},
|
||||||
|
"3Rx6Qo": {
|
||||||
|
"defaultMessage": "Advanced"
|
||||||
|
},
|
||||||
"3cc4Ct": {
|
"3cc4Ct": {
|
||||||
"defaultMessage": "Light"
|
"defaultMessage": "Light"
|
||||||
},
|
},
|
||||||
@ -278,6 +281,9 @@
|
|||||||
"FDguSC": {
|
"FDguSC": {
|
||||||
"defaultMessage": "{n} Zaps"
|
"defaultMessage": "{n} Zaps"
|
||||||
},
|
},
|
||||||
|
"FP+D3H": {
|
||||||
|
"defaultMessage": "LNURL to forward zaps to"
|
||||||
|
},
|
||||||
"FS3b54": {
|
"FS3b54": {
|
||||||
"defaultMessage": "Done!"
|
"defaultMessage": "Done!"
|
||||||
},
|
},
|
||||||
@ -437,6 +443,9 @@
|
|||||||
"OLEm6z": {
|
"OLEm6z": {
|
||||||
"defaultMessage": "Unknown login error"
|
"defaultMessage": "Unknown login error"
|
||||||
},
|
},
|
||||||
|
"P04gQm": {
|
||||||
|
"defaultMessage": "All zaps sent to this note will be received by the following LNURL"
|
||||||
|
},
|
||||||
"P61BTu": {
|
"P61BTu": {
|
||||||
"defaultMessage": "Copy Event JSON"
|
"defaultMessage": "Copy Event JSON"
|
||||||
},
|
},
|
||||||
@ -472,6 +481,9 @@
|
|||||||
"defaultMessage": "Art by {name}",
|
"defaultMessage": "Art by {name}",
|
||||||
"description": "Artwork attribution label"
|
"description": "Artwork attribution label"
|
||||||
},
|
},
|
||||||
|
"R1fEdZ": {
|
||||||
|
"defaultMessage": "Forward Zaps"
|
||||||
|
},
|
||||||
"R2OqnW": {
|
"R2OqnW": {
|
||||||
"defaultMessage": "Delete Account"
|
"defaultMessage": "Delete Account"
|
||||||
},
|
},
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"2LbrkB": "Enter password",
|
"2LbrkB": "Enter password",
|
||||||
"2a2YiP": "{n} Bookmarks",
|
"2a2YiP": "{n} Bookmarks",
|
||||||
"2k0Cv+": "Dislikes ({n})",
|
"2k0Cv+": "Dislikes ({n})",
|
||||||
|
"3Rx6Qo": "Advanced",
|
||||||
"3cc4Ct": "Light",
|
"3cc4Ct": "Light",
|
||||||
"3gOsZq": "Translators",
|
"3gOsZq": "Translators",
|
||||||
"3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}",
|
"3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}",
|
||||||
@ -90,6 +91,7 @@
|
|||||||
"Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.",
|
"Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.",
|
||||||
"F+B3x1": "We have also partnered with nostrplebs.com to give you more options",
|
"F+B3x1": "We have also partnered with nostrplebs.com to give you more options",
|
||||||
"FDguSC": "{n} Zaps",
|
"FDguSC": "{n} Zaps",
|
||||||
|
"FP+D3H": "LNURL to forward zaps to",
|
||||||
"FS3b54": "Done!",
|
"FS3b54": "Done!",
|
||||||
"FfYsOb": "An error has occured!",
|
"FfYsOb": "An error has occured!",
|
||||||
"FmXUJg": "follows you",
|
"FmXUJg": "follows you",
|
||||||
@ -142,6 +144,7 @@
|
|||||||
"OEW7yJ": "Zaps",
|
"OEW7yJ": "Zaps",
|
||||||
"OKhRC6": "Share",
|
"OKhRC6": "Share",
|
||||||
"OLEm6z": "Unknown login error",
|
"OLEm6z": "Unknown login error",
|
||||||
|
"P04gQm": "All zaps sent to this note will be received by the following LNURL",
|
||||||
"P61BTu": "Copy Event JSON",
|
"P61BTu": "Copy Event JSON",
|
||||||
"P7FD0F": "System (Default)",
|
"P7FD0F": "System (Default)",
|
||||||
"P7nJT9": "Total today (UTC): {amount} sats",
|
"P7nJT9": "Total today (UTC): {amount} sats",
|
||||||
@ -153,6 +156,7 @@
|
|||||||
"QTdJfH": "Create an Account",
|
"QTdJfH": "Create an Account",
|
||||||
"QawghE": "You can change your username at any point.",
|
"QawghE": "You can change your username at any point.",
|
||||||
"QxCuTo": "Art by {name}",
|
"QxCuTo": "Art by {name}",
|
||||||
|
"R1fEdZ": "Forward Zaps",
|
||||||
"R2OqnW": "Delete Account",
|
"R2OqnW": "Delete Account",
|
||||||
"RDZVQL": "Check",
|
"RDZVQL": "Check",
|
||||||
"RahCRH": "Expired",
|
"RahCRH": "Expired",
|
||||||
@ -321,4 +325,4 @@
|
|||||||
"zjJZBd": "You're ready!",
|
"zjJZBd": "You're ready!",
|
||||||
"zonsdq": "Failed to load LNURL service",
|
"zonsdq": "Failed to load LNURL service",
|
||||||
"zvCDao": "Automatically show latest notes"
|
"zvCDao": "Automatically show latest notes"
|
||||||
}
|
}
|
@ -12,6 +12,7 @@ export default class Tag {
|
|||||||
DTag?: string;
|
DTag?: string;
|
||||||
Index: number;
|
Index: number;
|
||||||
Invalid: boolean;
|
Invalid: boolean;
|
||||||
|
LNURL?: string;
|
||||||
|
|
||||||
constructor(tag: string[], index: number) {
|
constructor(tag: string[], index: number) {
|
||||||
this.Original = tag;
|
this.Original = tag;
|
||||||
@ -50,6 +51,10 @@ export default class Tag {
|
|||||||
this.PubKey = tag[1];
|
this.PubKey = tag[1];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "zap": {
|
||||||
|
this.LNURL = tag[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user