mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 21:31:43 +01:00
show fedimint tokens in content
This commit is contained in:
parent
f2f8186101
commit
e9efa336eb
5
.changeset/curvy-mice-return.md
Normal file
5
.changeset/curvy-mice-return.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Show fedimint tokens in content
|
@ -2,3 +2,4 @@ node_modules
|
||||
dist
|
||||
public/lib
|
||||
stats.html
|
||||
pnpm-lock.yaml
|
||||
|
@ -105,6 +105,7 @@
|
||||
"three-spritetext": "^1.8.2",
|
||||
"three-stdlib": "^2.29.11",
|
||||
"tiny-lru": "^11.2.11",
|
||||
"unified": "^11.0.5",
|
||||
"webln": "^0.3.2",
|
||||
"workbox-core": "7.0.0",
|
||||
"workbox-precaching": "7.0.0",
|
||||
|
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -279,6 +279,9 @@ importers:
|
||||
tiny-lru:
|
||||
specifier: ^11.2.11
|
||||
version: 11.2.11
|
||||
unified:
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5
|
||||
webln:
|
||||
specifier: ^0.3.2
|
||||
version: 0.3.2
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useAsync } from "react-use";
|
||||
import { Box, Button, ButtonGroup, Card, CardProps, Heading, IconButton, Link, Text } from "@chakra-ui/react";
|
||||
import { Box, Button, ButtonGroup, Card, CardProps, Heading, IconButton, Link, Spinner, Text } from "@chakra-ui/react";
|
||||
import { Token, getEncodedToken } from "@cashu/cashu-ts";
|
||||
|
||||
import { CopyIconButton } from "../copy-icon-button";
|
||||
@ -9,7 +9,6 @@ import { ECashIcon, WalletIcon } from "../icons";
|
||||
import { getMint } from "../../services/cashu-mints";
|
||||
import CurrencyDollar from "../icons/currency-dollar";
|
||||
import CurrencyEthereum from "../icons/currency-ethereum";
|
||||
import CurrencyRupeeCircle from "../icons/currency-rupee-circle";
|
||||
import CurrencyEuro from "../icons/currency-euro";
|
||||
import CurrencyYen from "../icons/currency-yen";
|
||||
import CurrencyPound from "../icons/currency-pound";
|
||||
@ -38,14 +37,14 @@ export default function InlineCachuCard({
|
||||
const account = useCurrentAccount();
|
||||
|
||||
encoded = encoded || getEncodedToken(token);
|
||||
const { value: spendable } = useAsync(async () => {
|
||||
const { value: spendable, loading } = useAsync(async () => {
|
||||
if (!token) return;
|
||||
for (const entry of token.token) {
|
||||
const mint = await getMint(entry.mint);
|
||||
const spent = await mint.check({ Ys: entry.proofs.map((p) => p.secret) });
|
||||
if (spent.states.some((v) => v.state === "SPENT")) return false;
|
||||
if (spent.states.some((v) => v.state === "UNSPENT")) return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}, [token]);
|
||||
|
||||
const amount = token?.token[0].proofs.reduce((acc, v) => acc + v.amount, 0);
|
||||
@ -98,17 +97,18 @@ export default function InlineCachuCard({
|
||||
icon={<WalletIcon boxSize={5} />}
|
||||
title="Open Wallet"
|
||||
aria-label="Open Wallet"
|
||||
href={`cashu://` + token}
|
||||
href={`cashu://` + encoded}
|
||||
/>
|
||||
{account && <RedeemButton token={encoded} />}
|
||||
</ButtonGroup>
|
||||
<Heading size="md" textDecoration={spendable === false ? "line-through" : undefined}>
|
||||
{denomination} {spendable === false ? " (Spent)" : ""}
|
||||
{denomination} {spendable === false ? " (Spent)" : loading ? <Spinner size="xs" /> : undefined}
|
||||
</Heading>
|
||||
{token && <Text fontSize="xs">Mint: {new URL(token.token[0].mint).hostname}</Text>}
|
||||
{token.unit && <Text fontSize="xs">Unit: {token.unit}</Text>}
|
||||
</Box>
|
||||
{token.memo && <Box>{token.memo}</Box>}
|
||||
{loading && <Spinner />}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
import { lazy } from "react";
|
||||
import { Text } from "@chakra-ui/react";
|
||||
import { ComponentMap } from "applesauce-react";
|
||||
|
||||
import Mention from "./mention";
|
||||
import Cashu from "./cashu";
|
||||
const InlineFedimintCard = lazy(() => import("../fedimint/inline-fedimint-card"));
|
||||
|
||||
export const components: ComponentMap = {
|
||||
text: ({ node }) => <Text as="span">{node.value}</Text>,
|
||||
mention: Mention,
|
||||
cashu: Cashu,
|
||||
fedimint: ({ node }) => <InlineFedimintCard token={node.token} />,
|
||||
};
|
||||
|
43
src/components/fedimint/inline-fedimint-card.tsx
Normal file
43
src/components/fedimint/inline-fedimint-card.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { Button, ButtonGroup, Card, CardProps, Heading, Link } from "@chakra-ui/react";
|
||||
|
||||
import { CopyIconButton } from "../copy-icon-button";
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
import { ECashIcon, WalletIcon } from "../icons";
|
||||
|
||||
export default function InlineFedimintCard({ token, ...props }: Omit<CardProps, "children"> & { token: string }) {
|
||||
const account = useCurrentAccount();
|
||||
|
||||
// const { value: amount } = useAsync(async () => {
|
||||
// const { FedimintWallet } = await import("@fedimint/core-web");
|
||||
// const wallet = new FedimintWallet();
|
||||
// const opened = await wallet.open("noStrudel");
|
||||
// if (opened) {
|
||||
// return await wallet.mint.parseNotes(token);
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
let UnitIcon = ECashIcon;
|
||||
let unitColor = "green.500";
|
||||
|
||||
return (
|
||||
<Card p="2" flexDirection="row" borderColor="green.500" gap="2" {...props}>
|
||||
<UnitIcon boxSize={10} color={unitColor} mr="2" mb="1" />
|
||||
<Heading size="md" alignItems="center">
|
||||
ecash
|
||||
</Heading>
|
||||
<ButtonGroup ml="auto">
|
||||
<CopyIconButton value={token} title="Copy Token" aria-label="Copy Token" variant="ghost" />
|
||||
<Button
|
||||
as={Link}
|
||||
leftIcon={<WalletIcon boxSize={5} />}
|
||||
colorScheme="primary"
|
||||
title="Open Wallet"
|
||||
aria-label="Open Wallet"
|
||||
href={`fedi://` + token}
|
||||
>
|
||||
Redeem
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Card>
|
||||
);
|
||||
}
|
@ -28,29 +28,9 @@ import { LightboxProvider } from "../../lightbox-provider";
|
||||
import MediaOwnerProvider from "../../../providers/local/media-owner-provider";
|
||||
import buildLinkComponent from "../../content/links";
|
||||
import { components } from "../../content";
|
||||
import { FedimintTokensTransformer } from "../../../helpers/fedimint";
|
||||
|
||||
// function buildContents(event: NostrEvent | EventTemplate, simpleLinks = false) {
|
||||
// let content: EmbedableContent = [event.content.trim()];
|
||||
|
||||
// // image gallery
|
||||
// content = embedImageGallery(content, event as NostrEvent);
|
||||
|
||||
// // bitcoin
|
||||
// content = embedLightningInvoice(content);
|
||||
|
||||
// // cashu
|
||||
// content = embedCashuTokens(content);
|
||||
|
||||
// // nostr
|
||||
// content = embedNostrLinks(content);
|
||||
// content = embedNostrMentions(content, event);
|
||||
// content = embedNostrHashtags(content, event);
|
||||
// content = embedNipDefinitions(content);
|
||||
// content = embedEmoji(content, event);
|
||||
// content = embedNostrWikiLinks(content);
|
||||
|
||||
// return content;
|
||||
// }
|
||||
const transformers = [FedimintTokensTransformer];
|
||||
|
||||
export type TextNoteContentsProps = {
|
||||
event: NostrEvent | EventTemplate;
|
||||
@ -98,7 +78,7 @@ export const TextNoteContents = React.memo(
|
||||
[LinkComponent],
|
||||
);
|
||||
|
||||
const content = useRenderedContent(event, componentsMap);
|
||||
const content = useRenderedContent(event, componentsMap, { transformers });
|
||||
|
||||
return (
|
||||
<MediaOwnerProvider owner={(event as NostrEvent).pubkey as string | undefined}>
|
||||
|
33
src/helpers/fedimint.ts
Normal file
33
src/helpers/fedimint.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Transformer } from "unified";
|
||||
import { Root, findAndReplace, Node } from "applesauce-content/nast";
|
||||
|
||||
declare module "applesauce-content/nast" {
|
||||
export interface FedimintToken extends Node {
|
||||
type: "fedimint";
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface ContentMap {
|
||||
fedimint: FedimintToken;
|
||||
}
|
||||
}
|
||||
|
||||
export function FedimintTokensTransformer(): Transformer<Root> {
|
||||
return (tree) => {
|
||||
findAndReplace(tree, [
|
||||
[
|
||||
/([A-Za-z0-9_+/-]{100,10000}={0,3})/gi,
|
||||
(_: string, $1: string) => {
|
||||
try {
|
||||
return {
|
||||
type: "fedimint",
|
||||
token: $1,
|
||||
};
|
||||
} catch (error) {}
|
||||
|
||||
return false;
|
||||
},
|
||||
],
|
||||
]);
|
||||
};
|
||||
}
|
15
src/hooks/use-trusted-mints.ts
Normal file
15
src/hooks/use-trusted-mints.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
import replaceableEventsService from "../services/replaceable-events";
|
||||
import { useReadRelays } from "./use-client-relays";
|
||||
import { useStoreQuery } from "applesauce-react";
|
||||
import TrustedMintsQuery from "../queries/trusted-mints";
|
||||
|
||||
export default function useTrustedMints(pubkey?: string) {
|
||||
const relays = useReadRelays();
|
||||
useEffect(() => {
|
||||
if (pubkey) replaceableEventsService.requestEvent(relays, 10019, pubkey);
|
||||
}, [pubkey, relays]);
|
||||
|
||||
return useStoreQuery(TrustedMintsQuery, pubkey ? [pubkey] : undefined);
|
||||
}
|
43
src/queries/trusted-mints.ts
Normal file
43
src/queries/trusted-mints.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { Query } from "applesauce-core";
|
||||
import { safeRelayUrl } from "applesauce-core/helpers";
|
||||
|
||||
type TrustedMints = {
|
||||
mints: string[];
|
||||
relays: Set<string>;
|
||||
pubkey?: string;
|
||||
};
|
||||
|
||||
export default function TrustedMintsQuery(pubkey: string): Query<TrustedMints | undefined> {
|
||||
return {
|
||||
key: pubkey,
|
||||
run: (events) =>
|
||||
events.replaceable(10019, pubkey).map((event) => {
|
||||
if (!event) return undefined;
|
||||
|
||||
const relays = new Set<string>();
|
||||
const mints: string[] = [];
|
||||
let pubkey: string | undefined = undefined;
|
||||
|
||||
for (const tag of event.tags) {
|
||||
switch (tag[0]) {
|
||||
case "mint":
|
||||
if (tag[1]) mints.push(tag[1]);
|
||||
break;
|
||||
|
||||
case "relay":
|
||||
if (tag[1]) {
|
||||
let safe = safeRelayUrl(tag[1]);
|
||||
if (safe) mints.push(safe);
|
||||
}
|
||||
break;
|
||||
|
||||
case "pubkey":
|
||||
if (tag[1]) pubkey = tag[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { relays, mints, pubkey };
|
||||
}),
|
||||
};
|
||||
}
|
@ -26,6 +26,9 @@ import { renderAudioUrl } from "../../../components/external-embeds/types/audio"
|
||||
import buildLinkComponent from "../../../components/content/links";
|
||||
import { components } from "../../../components/content";
|
||||
import { useKind4Decrypt } from "../../../hooks/use-kind4-decryption";
|
||||
import { FedimintTokensTransformer } from "../../../helpers/fedimint";
|
||||
|
||||
const transformers = [FedimintTokensTransformer];
|
||||
|
||||
export default function DirectMessageContent({
|
||||
event,
|
||||
@ -64,7 +67,7 @@ export default function DirectMessageContent({
|
||||
);
|
||||
|
||||
const { plaintext } = useKind4Decrypt(event);
|
||||
const content = useRenderedContent(event, componentsMap, plaintext);
|
||||
const content = useRenderedContent(event, componentsMap, { overrideContent: plaintext, transformers });
|
||||
|
||||
return (
|
||||
<TrustProvider event={event}>
|
||||
|
Loading…
x
Reference in New Issue
Block a user