refactor
This commit is contained in:
parent
c52b56871c
commit
241ead7a03
66
packages/app/src/Element/CashuNuts.tsx
Normal file
66
packages/app/src/Element/CashuNuts.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -6,15 +6,13 @@ import { visit, SKIP } from "unist-util-visit";
|
|||||||
import * as unist from "unist";
|
import * as unist from "unist";
|
||||||
import { HexKey, NostrPrefix } from "@snort/nostr";
|
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 { 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 Invoice from "Element/Invoice";
|
||||||
import Hashtag from "Element/Hashtag";
|
import Hashtag from "Element/Hashtag";
|
||||||
import Mention from "Element/Mention";
|
import Mention from "Element/Mention";
|
||||||
import HyperText from "Element/HyperText";
|
import HyperText from "Element/HyperText";
|
||||||
|
import CashuNuts from "Element/CashuNuts";
|
||||||
|
|
||||||
export type Fragment = string | React.ReactNode;
|
export type Fragment = string | React.ReactNode;
|
||||||
|
|
||||||
@ -72,46 +70,11 @@ export default function Text({ content, tags, creator, disableMedia, depth }: Te
|
|||||||
}
|
}
|
||||||
|
|
||||||
function extractCashuTokens(fragments: Fragment[]) {
|
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
|
return fragments
|
||||||
.map(f => {
|
.map(f => {
|
||||||
if (typeof f === "string" && f.includes("cashuA")) {
|
if (typeof f === "string" && f.includes("cashuA")) {
|
||||||
return f.split(CashuRegex).map(a => {
|
return f.split(CashuRegex).map(a => {
|
||||||
if (!a.startsWith("cashuA") || a.length < 10) {
|
return <CashuNuts token={a} />;
|
||||||
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 f;
|
return f;
|
||||||
|
@ -220,7 +220,7 @@ export default function LoginPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{error.length > 0 ? <b className="error">{error}</b> : null}
|
{error.length > 0 ? <b className="error">{error}</b> : null}
|
||||||
<p className="login-note">
|
<p>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Only the secret key can be used to publish (sign events), everything else logs you in read-only mode."
|
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"
|
description="Explanation for public key only login is read-only"
|
||||||
|
@ -422,6 +422,10 @@ body.scroll-lock {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
small.xs {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
.pointer {
|
.pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user