This commit is contained in:
Kieran 2023-05-09 16:25:59 +01:00
parent c52b56871c
commit 241ead7a03
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
4 changed files with 74 additions and 41 deletions

View File

@ -0,0 +1,66 @@
import { getDecodedToken } from "@cashu/cashu-ts";
import { useMemo } from "react";
import { FormattedMessage } from "react-intl";
import useLogin from "Hooks/useLogin";
import { useUserProfile } from "Hooks/useUserProfile";
export default function CashuNuts({ token }: { token: string }) {
const login = useLogin();
const profile = useUserProfile(login.publicKey);
async function copyToken(e: React.MouseEvent<HTMLButtonElement>, token: string) {
e.stopPropagation();
await navigator.clipboard.writeText(token);
}
async function redeemToken(e: React.MouseEvent<HTMLButtonElement>, token: string) {
e.stopPropagation();
const lnurl = profile?.lud16 ?? "";
const url = `https://redeem.cashu.me?token=${encodeURIComponent(token)}&lightning=${encodeURIComponent(lnurl)}`;
window.open(url, "_blank");
}
const cashu = useMemo(() => {
try {
if (!token.startsWith("cashuA") || token.length < 10) {
return;
}
return getDecodedToken(token);
} catch {
// ignored
}
}, [token]);
if (!cashu) return <>{token}</>;
return (
<div className="note-invoice">
<div className="flex f-between">
<div>
<h4>
<FormattedMessage defaultMessage="Cashu token" />
</h4>
<p>
<FormattedMessage
defaultMessage="Amount: {amount} sats"
values={{
amount: cashu.token[0].proofs.reduce((acc, v) => acc + v.amount, 0),
}}
/>
</p>
<small className="xs">
<FormattedMessage defaultMessage="Mint: {url}" values={{ url: cashu.token[0].mint }} />
</small>
</div>
<div>
<button onClick={e => copyToken(e, token)} className="mr5">
<FormattedMessage defaultMessage="Copy" description="Button: Copy Cashu token" />
</button>
<button onClick={e => redeemToken(e, token)}>
<FormattedMessage defaultMessage="Redeem" description="Button: Redeem Cashu token" />
</button>
</div>
</div>
</div>
);
}

View File

@ -6,15 +6,13 @@ import { visit, SKIP } from "unist-util-visit";
import * as unist from "unist";
import { HexKey, NostrPrefix } from "@snort/nostr";
import { getDecodedToken } from "@cashu/cashu-ts";
import { FormattedMessage } from "react-intl";
import { MentionRegex, InvoiceRegex, HashtagRegex, CashuRegex } from "Const";
import { eventLink, hexToBech32, splitByUrl, unwrap } from "Util";
import { eventLink, hexToBech32, splitByUrl, unwrap, validateNostrLink } from "Util";
import Invoice from "Element/Invoice";
import Hashtag from "Element/Hashtag";
import Mention from "Element/Mention";
import HyperText from "Element/HyperText";
import CashuNuts from "Element/CashuNuts";
export type Fragment = string | React.ReactNode;
@ -72,46 +70,11 @@ export default function Text({ content, tags, creator, disableMedia, depth }: Te
}
function extractCashuTokens(fragments: Fragment[]) {
async function copyToken(token: string) {
await navigator.clipboard.writeText(token);
}
async function redeemToken(token: string) {
const url = `https://redeem.cashu.me?token=${encodeURIComponent(token)}&lightning=${encodeURIComponent(
"callebtc@ln.tips"
)}`;
window.open(url, "_blank");
}
return fragments
.map(f => {
if (typeof f === "string" && f.includes("cashuA")) {
return f.split(CashuRegex).map(a => {
if (!a.startsWith("cashuA") || a.length < 10) {
return a;
}
const cashu = getDecodedToken(a);
if (cashu && cashu.token[0].proofs) {
return (
<div className="note-invoice" style={{ paddingRight: "0px" }}>
<div className="flex f-between">
<div>
<h4>Cashu token</h4>
<p>Amount: {cashu.token[0].proofs.reduce((acc, v) => (acc += v.amount), 0)} sats</p>
<p style={{ fontSize: "0.6em" }}>Mint: {cashu.token[0].mint}</p>
</div>
<div>
<button style={{ margin: "5px" }} type="button" onClick={() => copyToken(a)}>
<FormattedMessage defaultMessage="Copy" description="Button: Copy Cashu token" />
</button>
<button type="button" onClick={() => redeemToken(a)}>
<FormattedMessage defaultMessage="Redeem" description="Button: Redeem Cashu token" />
</button>
</div>
</div>
</div>
);
}
return a;
return <CashuNuts token={a} />;
});
}
return f;

View File

@ -220,7 +220,7 @@ export default function LoginPage() {
/>
</div>
{error.length > 0 ? <b className="error">{error}</b> : null}
<p className="login-note">
<p>
<FormattedMessage
defaultMessage="Only the secret key can be used to publish (sign events), everything else logs you in read-only mode."
description="Explanation for public key only login is read-only"

View File

@ -422,6 +422,10 @@ body.scroll-lock {
height: 100vh;
}
small.xs {
font-size: small;
}
.pointer {
cursor: pointer;
}