diff --git a/src/views/wallet/receive.tsx b/src/views/wallet/receive.tsx
deleted file mode 100644
index 31d6350c0..000000000
--- a/src/views/wallet/receive.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import { useState } from "react";
-import { useActionHub } from "applesauce-react/hooks";
-import { Button, Flex, Spacer, Textarea, useToast } from "@chakra-ui/react";
-import { getDecodedToken, Token } from "@cashu/cashu-ts";
-import { ReceiveToken } from "applesauce-wallet/actions";
-import { useLocation, useNavigate } from "react-router-dom";
-
-import SimpleView from "../../components/layout/presets/simple-view";
-import { getCashuWallet } from "../../services/cashu-mints";
-import RouterLink from "../../components/router-link";
-import QRCodeScannerButton from "../../components/qr-code/qr-code-scanner-button";
-
-export default function WalletReceiveView() {
- const location = useLocation();
- const actions = useActionHub();
- const navigate = useNavigate();
- const toast = useToast();
-
- const [input, setInput] = useState(location.state?.input ?? "");
-
- const [loading, setLoading] = useState(false);
- const receive = async () => {
- setLoading(true);
- try {
- const decoded = getDecodedToken(input.trim());
- const originalAmount = decoded.proofs.reduce((t, p) => t + p.amount, 0);
-
- // swap tokens
- const wallet = await getCashuWallet(decoded.mint);
- const proofs = await wallet.receive(decoded);
- const token: Token = { mint: decoded.mint, proofs };
-
- const amount = token.proofs.reduce((t, p) => t + p.amount, 0);
- const fee = originalAmount - amount;
-
- // save new tokens
- await actions.run(ReceiveToken, token, undefined, fee || undefined);
-
- toast({ status: "success", description: `Received ${amount} sats` });
-
- navigate("/wallet");
- } catch (error) {
- if (error instanceof Error) toast({ status: "error", description: error.message });
- console.log(error);
- }
- setLoading(false);
- };
-
- return (
-
-
- );
-}
diff --git a/src/views/wallet/receive/index.tsx b/src/views/wallet/receive/index.tsx
new file mode 100644
index 000000000..060d10527
--- /dev/null
+++ b/src/views/wallet/receive/index.tsx
@@ -0,0 +1,68 @@
+import { useState } from "react";
+import { Button, Flex, IconButton, Spacer, Textarea, useToast } from "@chakra-ui/react";
+import { getDecodedToken, getEncodedToken } from "@cashu/cashu-ts";
+import { decodeTokenFromEmojiString } from "applesauce-wallet/helpers";
+import { useLocation, useNavigate } from "react-router-dom";
+
+import SimpleView from "../../../components/layout/presets/simple-view";
+import RouterLink from "../../../components/router-link";
+import QRCodeScannerButton from "../../../components/qr-code/qr-code-scanner-button";
+import Clipboard from "../../../components/icons/clipboard";
+
+export default function WalletReceiveView() {
+ const location = useLocation();
+ const navigate = useNavigate();
+ const toast = useToast();
+
+ const [input, setInput] = useState(location.state?.input ?? "");
+
+ const handleScannerResult = (result: string) => {
+ try {
+ const token = getDecodedToken(result.trim());
+ navigate("/wallet/receive/token", { state: { token: getEncodedToken(token) }, replace: true });
+ } catch (error) {
+ setInput(result);
+ }
+ };
+
+ const receive = () => {
+ try {
+ const token = getDecodedToken(input.trim());
+ navigate("/wallet/receive/token", { state: { token: getEncodedToken(token) }, replace: true });
+ } catch (error) {
+ if (error instanceof Error) toast({ status: "error", description: error.message });
+ }
+ };
+
+ return (
+
+
+ );
+}
diff --git a/src/views/wallet/receive/token.tsx b/src/views/wallet/receive/token.tsx
new file mode 100644
index 000000000..0cbabbf84
--- /dev/null
+++ b/src/views/wallet/receive/token.tsx
@@ -0,0 +1,70 @@
+import { Flex, Spacer, Button, Text, Card, CardBody, useToast, useDisclosure, ButtonGroup } from "@chakra-ui/react";
+import { useLocation, Navigate, useNavigate } from "react-router-dom";
+import { useActionHub } from "applesauce-react/hooks";
+import { getDecodedToken, Token } from "@cashu/cashu-ts";
+import { ReceiveToken } from "applesauce-wallet/actions";
+
+import SimpleView from "../../../components/layout/presets/simple-view";
+import RouterLink from "../../../components/router-link";
+import CashuMintFavicon from "../../../components/cashu/cashu-mint-favicon";
+import CashuMintName from "../../../components/cashu/cashu-mint-name";
+import useAsyncErrorHandler from "../../../hooks/use-async-error-handler";
+import { getCashuWallet } from "../../../services/cashu-mints";
+
+export default function WalletReceiveTokenView() {
+ const navigate = useNavigate();
+ const toast = useToast();
+ const actions = useActionHub();
+ const location = useLocation();
+ const more = useDisclosure();
+
+ const token: string = location.state?.token;
+ if (!token) return ;
+
+ const decoded = getDecodedToken(token);
+ const originalAmount = decoded.proofs.reduce((t, p) => t + p.amount, 0);
+
+ const receive = useAsyncErrorHandler(async () => {
+ try {
+ // swap tokens
+ const wallet = await getCashuWallet(decoded.mint);
+ const proofs = await wallet.receive(decoded);
+ const token: Token = { mint: decoded.mint, proofs };
+
+ const amount = token.proofs.reduce((t, p) => t + p.amount, 0);
+ const fee = originalAmount - amount;
+
+ // save new tokens
+ await actions.run(ReceiveToken, token, undefined, fee || undefined);
+
+ toast({ status: "success", description: `Received ${amount} sats` });
+
+ navigate("/wallet");
+ } catch (error) {
+ if (error instanceof Error) toast({ status: "error", description: error.message });
+ console.log(error);
+ }
+ }, [decoded, originalAmount, actions, navigate, toast]);
+
+ const swap = useAsyncErrorHandler(async () => {}, [decoded, originalAmount, actions, navigate, toast]);
+
+ return (
+
+
+
+
+ {originalAmount} sats
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/views/wallet/routes.tsx b/src/views/wallet/routes.tsx
index e2fd9530b..0530640ba 100644
--- a/src/views/wallet/routes.tsx
+++ b/src/views/wallet/routes.tsx
@@ -3,7 +3,8 @@ import RequireActiveAccount from "../../components/router/require-active-account
import { lazy } from "react";
const WalletHomeView = lazy(() => import("."));
-const WalletReceiveView = lazy(() => import("./receive"));
+const WalletReceiveView = lazy(() => import("./receive/index"));
+const WalletReceiveTokenView = lazy(() => import("./receive/token"));
const WalletSendView = lazy(() => import("./send/index"));
const WalletSendCashuView = lazy(() => import("./send/cashu"));
const WalletSendTokenView = lazy(() => import("./send/token"));
@@ -17,7 +18,13 @@ export default [
),
},
- { path: "receive", Component: WalletReceiveView },
+ {
+ path: "receive",
+ children: [
+ { index: true, Component: WalletReceiveView },
+ { path: "token", Component: WalletReceiveTokenView },
+ ],
+ },
{
path: "send",
children: [
diff --git a/src/views/wallet/send/cashu.tsx b/src/views/wallet/send/cashu.tsx
index b83563f26..b085f07d8 100644
--- a/src/views/wallet/send/cashu.tsx
+++ b/src/views/wallet/send/cashu.tsx
@@ -20,8 +20,10 @@ export default function WalletSendCashuView() {
const balance = useStoreQuery(WalletBalanceQuery, [account.pubkey]);
const tokens = useStoreQuery(WalletTokensQuery, [account.pubkey, false]);
+ const defaultMint = balance && Object.keys(balance).reduce((a, b) => (balance[a] > balance[b] ? a : b));
+
const { register, getValues, watch, handleSubmit, formState } = useForm({
- defaultValues: { amount: 0, mint: "" },
+ defaultValues: { amount: 0, mint: defaultMint ?? "" },
mode: "all",
});
@@ -33,7 +35,9 @@ export default function WalletSendCashuView() {
const selected = dumbTokenSelection(tokens, values.amount, values.mint);
const wallet = await getCashuWallet(values.mint);
- // swap
+ await wallet.mint.getKeySets();
+
+ // swap tokens for send
const send = await wallet.send(values.amount, selected.proofs);
// save the change
diff --git a/src/views/wallet/send/index.tsx b/src/views/wallet/send/index.tsx
index 50ff50590..248f7f4d7 100644
--- a/src/views/wallet/send/index.tsx
+++ b/src/views/wallet/send/index.tsx
@@ -10,7 +10,7 @@ export default function WalletSendView() {
-
+
ECash
@@ -18,7 +18,7 @@ export default function WalletSendView() {
-
+
Lightning
diff --git a/src/views/wallet/send/token.tsx b/src/views/wallet/send/token.tsx
index 2e0f6796c..69deb3580 100644
--- a/src/views/wallet/send/token.tsx
+++ b/src/views/wallet/send/token.tsx
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
+import { filter, from, Observable, switchMap, take } from "rxjs";
import { Button, ButtonGroup, Flex, Spacer, useToast } from "@chakra-ui/react";
-import { ANIMATED_QR_INTERVAL, sendAnimated } from "applesauce-wallet/helpers";
+import { ANIMATED_QR_INTERVAL, encodeTokenToEmoji, sendAnimated } from "applesauce-wallet/helpers";
import { getDecodedToken, Proof, ProofState } from "@cashu/cashu-ts";
import { ReceiveToken } from "applesauce-wallet/actions";
import { useActionHub } from "applesauce-react/hooks";
@@ -10,7 +11,6 @@ import SimpleView from "../../../components/layout/presets/simple-view";
import RouterLink from "../../../components/router-link";
import { CopyIconButton } from "../../../components/copy-icon-button";
import QrCodeSvg from "../../../components/qr-code/qr-code-svg";
-import { filter, from, Observable, switchMap, take } from "rxjs";
import { getCashuWallet } from "../../../services/cashu-mints";
export default function WalletSendTokenView() {
@@ -103,6 +103,7 @@ export default function WalletSendTokenView() {
+ 🥜} />
)}
-
- }
- aria-label="Delete entry"
- onClick={() => deleteEvent(entry)}
- colorScheme="red"
- variant="ghost"
- />
-
+
{details && (
-
+
{details.mint && (
<>
@@ -102,11 +98,30 @@ function HistoryEntry({ entry }: { entry: NostrEvent }) {
>
)}
- {details.fee !== undefined && (
-
- fee: {details.fee}
-
- )}
+
+ : }
+ >
+ Details
+
+
+ )}
+ {more.isOpen && (
+
+
+
+ }
+ aria-label="Delete entry"
+ onClick={() => deleteEvent(entry)}
+ colorScheme="red"
+ variant="ghost"
+ />
+
)}
diff --git a/src/views/wallet/tabs/tokens.tsx b/src/views/wallet/tabs/tokens.tsx
index 459ffbaa3..d0efe402b 100644
--- a/src/views/wallet/tabs/tokens.tsx
+++ b/src/views/wallet/tabs/tokens.tsx
@@ -1,8 +1,9 @@
-import { useState } from "react";
+import { useMemo, useState } from "react";
import {
Button,
ButtonGroup,
Card,
+ CardBody,
CardFooter,
CardHeader,
Flex,
@@ -10,24 +11,37 @@ import {
IconButton,
Spacer,
Text,
+ useDisclosure,
} from "@chakra-ui/react";
import { useActiveAccount, useEventStore, useStoreQuery } from "applesauce-react/hooks";
import { WalletTokensQuery } from "applesauce-wallet/queries";
import { getTokenContent, isTokenContentLocked, unlockTokenContent } from "applesauce-wallet/helpers";
import { NostrEvent } from "nostr-tools";
-import { ProofState } from "@cashu/cashu-ts";
+import { getEncodedToken, ProofState } from "@cashu/cashu-ts";
import useAsyncErrorHandler from "../../../hooks/use-async-error-handler";
import useEventUpdate from "../../../hooks/use-event-update";
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
-import { ECashIcon, TrashIcon } from "../../../components/icons";
+import {
+ ChevronDownIcon,
+ ChevronUpIcon,
+ ECashIcon,
+ ExternalLinkIcon,
+ QrCodeIcon,
+ TrashIcon,
+} from "../../../components/icons";
import DebugEventButton from "../../../components/debug-modal/debug-event-button";
import { useDeleteEventContext } from "../../../providers/route/delete-event-provider";
import Timestamp from "../../../components/timestamp";
import { getCashuWallet } from "../../../services/cashu-mints";
import ConsolidateTokensButton from "../components/consolidate-tokens-button";
+import CashuMintFavicon from "../../../components/cashu/cashu-mint-favicon";
+import CashuMintName from "../../../components/cashu/cashu-mint-name";
+import { CopyIconButton } from "../../../components/copy-icon-button";
+import RouterLink from "../../../components/router-link";
function TokenEvent({ token }: { token: NostrEvent }) {
+ const more = useDisclosure();
const account = useActiveAccount();
const eventStore = useEventStore();
useEventUpdate(token.id);
@@ -54,6 +68,8 @@ function TokenEvent({ token }: { token: NostrEvent }) {
eventStore.update(token);
}, [token, account, eventStore]);
+ const encoded = useMemo(() => details && getEncodedToken(details), [details]);
+
return (
@@ -66,34 +82,64 @@ function TokenEvent({ token }: { token: NostrEvent }) {
)}
-
- }
- aria-label="Delete entry"
- onClick={() => deleteEvent(token)}
- colorScheme="red"
- variant="ghost"
- />
- {details && (
-
-
-
-
- {details.mint}
-
+
+ {details && (
+ <>
+
+
+ >
+ )}
+
+ : }
+ >
+ Details
+
+
+ {more.isOpen && (
+
+
+
+
+
+ {encoded && (
+ <>
+
+ }
+ aria-label="Show token"
+ variant="ghost"
+ />
+ >
+ )}
+
+
+ deleteEvent(token)}
+ colorScheme="red"
+ variant="ghost"
+ icon={}
+ />
+
)}