Custom amounts on LNURL

This commit is contained in:
Kieran 2023-01-07 23:01:32 +00:00
parent 037f39e386
commit 18b6f19894
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
5 changed files with 49 additions and 19 deletions

View File

@ -2,7 +2,7 @@
background-color: #222; background-color: #222;
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 10px;
width: 50vw; width: 500px;
text-align: center; text-align: center;
min-height: 10vh; min-height: 10vh;
} }

View File

@ -11,15 +11,23 @@ export default function LNURLTip(props) {
const amounts = [50, 100, 500, 1_000, 5_000, 10_000]; const amounts = [50, 100, 500, 1_000, 5_000, 10_000];
const [payService, setPayService] = useState(""); const [payService, setPayService] = useState("");
const [amount, setAmount] = useState(0); const [amount, setAmount] = useState(0);
const [customAmount, setCustomAmount] = useState(0);
const [invoice, setInvoice] = useState(""); const [invoice, setInvoice] = useState("");
const [comment, setComment] = useState(""); const [comment, setComment] = useState("");
const [error, setError] = useState("") const [error, setError] = useState("")
const selectAmount = (a) => {
setError("");
setInvoice("");
setAmount(a);
};
async function fetchJson(url) { async function fetchJson(url) {
let rsp = await fetch(url); let rsp = await fetch(url);
if (rsp.ok) { if (rsp.ok) {
let data = await rsp.json(); let data = await rsp.json();
console.log(data); console.log(data);
setError("");
return data; return data;
} }
return null; return null;
@ -48,6 +56,7 @@ export default function LNURLTip(props) {
setError(data.reason); setError(data.reason);
} else { } else {
setInvoice(data.pr); setInvoice(data.pr);
setError("");
} }
} else { } else {
setError("Failed to load invoice"); setError("Failed to load invoice");
@ -57,6 +66,17 @@ export default function LNURLTip(props) {
} }
}; };
function custom() {
let min = (payService?.minSendable ?? 0) / 1000;
let max = (payService?.maxSendable ?? 21_000_000_000) / 1000;
return (
<div className="flex mb10">
<input type="number" min={min} max={max} className="f-grow mr10" value={customAmount} onChange={(e) => setCustomAmount(e.target.value)} />
<div className="btn" onClick={() => selectAmount(customAmount)}>Confirm</div>
</div>
);
}
useEffect(() => { useEffect(() => {
if (payService && amount > 0) { if (payService && amount > 0) {
loadInvoice(); loadInvoice();
@ -83,8 +103,11 @@ export default function LNURLTip(props) {
const metadata = useMemo(() => { const metadata = useMemo(() => {
if (payService) { if (payService) {
let meta = JSON.parse(payService.metadata); let meta = JSON.parse(payService.metadata);
let desc = meta.find(a => a[0] === "text/plain");
let image = meta.find(a => a[0] === "image/png;base64");
return { return {
description: meta.find(a => a[0] === "text/plain")[1] description: desc ? desc[1] : null,
image: image ? image[1] : null
}; };
} }
return null; return null;
@ -95,17 +118,21 @@ export default function LNURLTip(props) {
<Modal onClose={() => onClose()}> <Modal onClose={() => onClose()}>
<div className="lnurl-tip" onClick={(e) => e.stopPropagation()}> <div className="lnurl-tip" onClick={(e) => e.stopPropagation()}>
<h2> Send sats</h2> <h2> Send sats</h2>
<div className="f-ellipsis mb10">{service}</div> <div className="f-ellipsis mb10">{metadata?.description ?? service}</div>
<div className="f-ellipsis mb10">{metadata?.description}</div>
<div className="flex"> <div className="flex">
{payService?.commentAllowed > 0 ? {payService?.commentAllowed > 0 ?
<input type="text" placeholder="Comment" className="mb10 f-grow" maxLength={payService?.commentAllowed} onChange={(e) => setComment(e.target.value)} /> : null} <input type="text" placeholder="Comment" className="mb10 f-grow" maxLength={payService?.commentAllowed} onChange={(e) => setComment(e.target.value)} /> : null}
</div> </div>
<div className="mb10"> <div className="mb10">
{serviceAmounts.map(a => <span className={`pill ${amount === a ? "active" : ""}`} key={a} onClick={() => setAmount(a)}> {serviceAmounts.map(a => <span className={`pill ${amount === a ? "active" : ""}`} key={a} onClick={() => selectAmount(a)}>
{a.toLocaleString()} {a.toLocaleString()}
</span>)} </span>)}
{payService ?
<span className={`pill ${amount === -1 ? "active" : ""}`} onClick={() => selectAmount(-1)}>
Custom
</span> : null}
</div> </div>
{amount === -1 ? custom() : null}
{error ? <p className="error">{error}</p> : null} {error ? <p className="error">{error}</p> : null}
<QrCode link={invoice} /> <QrCode link={invoice} />
</div> </div>

View File

@ -22,10 +22,10 @@ export default function Note(props) {
const reactions = props.reactions; const reactions = props.reactions;
const deletion = props.deletion; const deletion = props.deletion;
const emojiReactions = reactions?.filter(({ Content }) => Content && Content !== "+" && Content !== "-" && Content !== "❤️") const emojiReactions = reactions?.filter(({ Content }) => Content && Content !== "+" && Content !== "-" && Content !== "❤️")
.reduce((acc, { Content }) => { .reduce((acc, { Content }) => {
const amount = acc[Content] || 0 const amount = acc[Content] || 0
return {...acc, [Content]: amount + 1 } return { ...acc, [Content]: amount + 1 }
}, {}) }, {})
const likes = reactions?.filter(({ Content }) => Content === "+" || Content === "❤️").length ?? 0 const likes = reactions?.filter(({ Content }) => Content === "+" || Content === "❤️").length ?? 0
const dislikes = reactions?.filter(({ Content }) => Content === "-").length ?? 0 const dislikes = reactions?.filter(({ Content }) => Content === "-").length ?? 0
const publisher = useEventPublisher(); const publisher = useEventPublisher();
@ -47,7 +47,7 @@ export default function Note(props) {
}; };
function hasReacted(emoji) { function hasReacted(emoji) {
return reactions?.find(({ PubKey, Content }) => Content === emoji && PubKey === login) return reactions?.find(({ PubKey, Content }) => Content === emoji && PubKey === login)
} }
const transformBody = useCallback(() => { const transformBody = useCallback(() => {
@ -156,15 +156,15 @@ export default function Note(props) {
<FontAwesomeIcon icon={faReply} /> <FontAwesomeIcon icon={faReply} />
</span> </span>
{Object.keys(emojiReactions).map((emoji) => { {Object.keys(emojiReactions).map((emoji) => {
return ( return (
<span className="pill" onClick={() => react(emoji)}> <span className="pill" onClick={() => react(emoji)}>
<span style={{ filter: hasReacted(emoji) ? 'none' : 'grayscale(1)' }}> <span style={{ filter: hasReacted(emoji) ? 'none' : 'grayscale(1)' }}>
{emoji} {emoji}
</span>
&nbsp;
{emojiReactions[emoji]}
</span> </span>
&nbsp; )
{emojiReactions[emoji]}
</span>
)
})} })}
<span className="pill" onClick={() => like()}> <span className="pill" onClick={() => like()}>
<FontAwesomeIcon color={liked ? "red" : "currentColor"} icon={faHeart} /> &nbsp; <FontAwesomeIcon color={liked ? "red" : "currentColor"} icon={faHeart} /> &nbsp;

View File

@ -32,6 +32,8 @@ export default function QrCode(props) {
elm.href = `lightning:${link}`; elm.href = `lightning:${link}`;
elm.click(); elm.click();
} }
} else {
qrRef.current.innerHTML = "";
} }
}, [link]); }, [link]);

View File

@ -65,7 +65,7 @@ code {
border-radius: 25px; border-radius: 25px;
} }
input[type="text"], input[type="password"], textarea { input[type="text"], input[type="password"], input[type="number"], textarea {
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
border: 0; border: 0;
@ -120,6 +120,7 @@ span.pill {
span.pill.active { span.pill.active {
background-color: #444; background-color: #444;
font-weight: bold;
} }
span.pill:hover { span.pill:hover {