mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 21:31:43 +01:00
cleanup debug modal
This commit is contained in:
parent
dbd724a118
commit
bf6d243d61
82
pnpm-lock.yaml
generated
82
pnpm-lock.yaml
generated
@ -92,25 +92,25 @@ importers:
|
||||
version: 4.9.2(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
applesauce-channel:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
version: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-content:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
version: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-core:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
version: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-lists:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
version: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-net:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
version: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-react:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
version: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-signer:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
version: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
bech32:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@ -1869,26 +1869,26 @@ packages:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
applesauce-channel@0.0.0-next-20241119142128:
|
||||
resolution: {integrity: sha512-AynaT+pM9fNBValeuj3ad1/KNEcLyGo2oupnXYvvY1G0cgVSW6L3YAO+p5VA/XCrWGsecnGQksJYVAvLAGqDHQ==}
|
||||
applesauce-channel@0.0.0-next-20241119173145:
|
||||
resolution: {integrity: sha512-+2bysV0gNdC2AKrylO+xP8ETp2eGbPbw7lz1kHI2p+fA5OIFn0j+6Ge8wEGv4E9n9eLIxa6welZKqWolCR0xDg==}
|
||||
|
||||
applesauce-content@0.0.0-next-20241119142128:
|
||||
resolution: {integrity: sha512-e6oPmu6fDRFzfra9WpxffytgFE4o5Ot5LgCE8uZNTUZYY3/f05xW4qPJAIuIddP3RK19AvsXsXHDHotQ+KV07g==}
|
||||
applesauce-content@0.0.0-next-20241119173145:
|
||||
resolution: {integrity: sha512-JSjnM2rRCEhsDH0PzsfN4iFetOoE1UVY13ug2dsYLYM7K/ZZDgCj1W7wtIFZQKQANrqUs0Cx4zdhTTnSnbdDmQ==}
|
||||
|
||||
applesauce-core@0.0.0-next-20241119142128:
|
||||
resolution: {integrity: sha512-mftudp86ZKQYH+eFThOUj7yt+gFgMlFdLBW7VEHJAnVlXj1FLPQohvf6C0kpFtMNxnAPMlRCPGLUQn5z32VBfA==}
|
||||
applesauce-core@0.0.0-next-20241119173145:
|
||||
resolution: {integrity: sha512-Y0w+uc+ByojiyrFgQInY2Klg51sYnTWPXvWRdT7RP1rw4WkOTvLvek1y6VoUmrcZG7Ss0f/eKiQiqhWamNIxDQ==}
|
||||
|
||||
applesauce-lists@0.0.0-next-20241119142128:
|
||||
resolution: {integrity: sha512-QUrorAGEcj7b0jIarVhUWs32VlRc2ovpE6GOxa9rxHln8aRsJ2VEtKKTxc+dpMhnG5hB1v4MT9LXnvSnulw3mA==}
|
||||
applesauce-lists@0.0.0-next-20241119173145:
|
||||
resolution: {integrity: sha512-XzJtS5ZNnIE3YUh3eppilyGIpfnE1RqV4VuQqoNGhGYwbbn2b3o8Z0+dKe7+VyGu50HGcCAlhvdTjgV6VgMiXw==}
|
||||
|
||||
applesauce-net@0.0.0-next-20241119142128:
|
||||
resolution: {integrity: sha512-/vsNdP+icA/K4So8ilqh5YMu77bXQ5F4kuV0WZai5BAeIW+sbvPju+7jWUTs/AFSNFDLLL8jzx7FzXe2Ui4tuA==}
|
||||
applesauce-net@0.0.0-next-20241119173145:
|
||||
resolution: {integrity: sha512-TUdIY+2Ida1gMg4KTcPoByRdw9uLd3bl8ztyS51adfeKow0Qs7bAhuBLuzLav0sUwG8QzWVGqBHgVOf/KX7uTw==}
|
||||
|
||||
applesauce-react@0.0.0-next-20241119142128:
|
||||
resolution: {integrity: sha512-FJzgri22ZLDHp8imsnvGDKu3k0LhXewolElyCyJZnNP20tlsjHjwGrnScaNTYFtnsbDwRx3UhF9NiYsiWwZS1g==}
|
||||
applesauce-react@0.0.0-next-20241119173145:
|
||||
resolution: {integrity: sha512-uMyAPFJVxT8/jPatxw/X/HmVSSXCyXyDg7vXpexr+aejryidIXIUDI2aZnaHgNbknx8QsqeeXP2d5AgvCrDnZA==}
|
||||
|
||||
applesauce-signer@0.0.0-next-20241119142128:
|
||||
resolution: {integrity: sha512-/hL/rSzNR4Y6UQ58/d8MOzOXccwHbR1DIkHOvn+Dir3ZYs5qDXFsSwhC4zNmmHab7Vo7aVgmCpE6TKzY026u6w==}
|
||||
applesauce-signer@0.0.0-next-20241119173145:
|
||||
resolution: {integrity: sha512-uLqsdLinVtwaNp1YfNMdK8ohPO/GxLK2F+srWYFUeKFErY08bXp8cbQubuaD9FYVaUOOWERFrqwoGv+FEb5Msw==}
|
||||
|
||||
argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
@ -3124,8 +3124,8 @@ packages:
|
||||
micromark-util-sanitize-uri@2.0.1:
|
||||
resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
|
||||
|
||||
micromark-util-subtokenize@2.0.2:
|
||||
resolution: {integrity: sha512-xKxhkB62vwHUuuxHe9Xqty3UaAsizV2YKq5OV344u3hFBbf8zIYrhYOWhAQb94MtMPkjTOzzjJ/hid9/dR5vFA==}
|
||||
micromark-util-subtokenize@2.0.3:
|
||||
resolution: {integrity: sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==}
|
||||
|
||||
micromark-util-symbol@2.0.1:
|
||||
resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
|
||||
@ -6158,22 +6158,22 @@ snapshots:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
|
||||
applesauce-channel@0.0.0-next-20241119142128(typescript@5.6.3):
|
||||
applesauce-channel@0.0.0-next-20241119173145(typescript@5.6.3):
|
||||
dependencies:
|
||||
applesauce-core: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-core: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
nostr-tools: 2.10.3(typescript@5.6.3)
|
||||
rxjs: 7.8.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-content@0.0.0-next-20241119142128(typescript@5.6.3):
|
||||
applesauce-content@0.0.0-next-20241119173145(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@cashu/cashu-ts': 2.0.0-rc1
|
||||
'@types/hast': 3.0.4
|
||||
'@types/mdast': 4.0.4
|
||||
'@types/unist': 3.0.3
|
||||
applesauce-core: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-core: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
mdast-util-find-and-replace: 3.0.1
|
||||
nostr-tools: 2.10.3(typescript@5.6.3)
|
||||
remark: 15.0.1
|
||||
@ -6184,7 +6184,7 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-core@0.0.0-next-20241119142128(typescript@5.6.3):
|
||||
applesauce-core@0.0.0-next-20241119173145(typescript@5.6.3):
|
||||
dependencies:
|
||||
debug: 4.3.7
|
||||
json-stringify-deterministic: 1.0.12
|
||||
@ -6196,13 +6196,13 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-lists@0.0.0-next-20241119142128(typescript@5.6.3):
|
||||
applesauce-lists@0.0.0-next-20241119173145(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@noble/hashes': 1.5.0
|
||||
'@noble/secp256k1': 1.7.1
|
||||
'@scure/base': 1.1.9
|
||||
'@types/dom-serial': 1.0.6
|
||||
applesauce-core: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-core: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
debug: 4.3.7
|
||||
nostr-tools: 2.10.3(typescript@5.6.3)
|
||||
rxjs: 7.8.1
|
||||
@ -6210,9 +6210,9 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-net@0.0.0-next-20241119142128(typescript@5.6.3):
|
||||
applesauce-net@0.0.0-next-20241119173145(typescript@5.6.3):
|
||||
dependencies:
|
||||
applesauce-core: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-core: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
nanoid: 5.0.8
|
||||
nostr-tools: 2.10.3(typescript@5.6.3)
|
||||
rxjs: 7.8.1
|
||||
@ -6221,10 +6221,10 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-react@0.0.0-next-20241119142128(typescript@5.6.3):
|
||||
applesauce-react@0.0.0-next-20241119173145(typescript@5.6.3):
|
||||
dependencies:
|
||||
applesauce-content: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-core: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-content: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-core: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
nostr-tools: 2.10.3(typescript@5.6.3)
|
||||
react: 18.3.1
|
||||
rxjs: 7.8.1
|
||||
@ -6232,14 +6232,14 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-signer@0.0.0-next-20241119142128(typescript@5.6.3):
|
||||
applesauce-signer@0.0.0-next-20241119173145(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@noble/hashes': 1.5.0
|
||||
'@noble/secp256k1': 1.7.1
|
||||
'@scure/base': 1.1.9
|
||||
'@types/dom-serial': 1.0.6
|
||||
applesauce-core: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-net: 0.0.0-next-20241119142128(typescript@5.6.3)
|
||||
applesauce-core: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
applesauce-net: 0.0.0-next-20241119173145(typescript@5.6.3)
|
||||
debug: 4.3.7
|
||||
nanoid: 5.0.8
|
||||
nostr-tools: 2.10.3(typescript@5.6.3)
|
||||
@ -7641,7 +7641,7 @@ snapshots:
|
||||
micromark-util-html-tag-name: 2.0.1
|
||||
micromark-util-normalize-identifier: 2.0.1
|
||||
micromark-util-resolve-all: 2.0.1
|
||||
micromark-util-subtokenize: 2.0.2
|
||||
micromark-util-subtokenize: 2.0.3
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.1
|
||||
|
||||
@ -7788,7 +7788,7 @@ snapshots:
|
||||
micromark-util-encode: 2.0.1
|
||||
micromark-util-symbol: 2.0.1
|
||||
|
||||
micromark-util-subtokenize@2.0.2:
|
||||
micromark-util-subtokenize@2.0.3:
|
||||
dependencies:
|
||||
devlop: 1.1.0
|
||||
micromark-util-chunked: 2.0.1
|
||||
@ -7815,7 +7815,7 @@ snapshots:
|
||||
micromark-util-normalize-identifier: 2.0.1
|
||||
micromark-util-resolve-all: 2.0.1
|
||||
micromark-util-sanitize-uri: 2.0.1
|
||||
micromark-util-subtokenize: 2.0.2
|
||||
micromark-util-subtokenize: 2.0.3
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
|
14
src/app.tsx
14
src/app.tsx
@ -104,6 +104,7 @@ import PostSettings from "./views/settings/post";
|
||||
import AccountSettings from "./views/settings/accounts";
|
||||
import ArticlesHomeView from "./views/articles";
|
||||
import ArticleView from "./views/articles/article";
|
||||
import WalletView from "./views/wallet";
|
||||
const TracksView = lazy(() => import("./views/tracks"));
|
||||
const UserTracksTab = lazy(() => import("./views/user/tracks"));
|
||||
const UserVideosTab = lazy(() => import("./views/user/videos"));
|
||||
@ -336,6 +337,19 @@ const router = createHashRouter([
|
||||
{ path: "", element: <NotificationsView /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "wallet",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
element: (
|
||||
<RequireCurrentAccount>
|
||||
<WalletView />
|
||||
</RequireCurrentAccount>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "videos",
|
||||
children: [
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { IconButton, IconButtonProps, useToast } from "@chakra-ui/react";
|
||||
import { Button, ButtonProps, IconButton, IconButtonProps, useToast } from "@chakra-ui/react";
|
||||
|
||||
import { CheckIcon, CopyToClipboardIcon } from "./icons";
|
||||
|
||||
@ -8,7 +8,7 @@ type CopyIconButtonProps = Omit<IconButtonProps, "icon" | "value"> & {
|
||||
icon?: IconButtonProps["icon"];
|
||||
};
|
||||
|
||||
export const CopyIconButton = ({ value, icon, ...props }: CopyIconButtonProps) => {
|
||||
export function CopyIconButton({ value, icon, ...props }: CopyIconButtonProps) {
|
||||
const toast = useToast();
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
@ -27,4 +27,31 @@ export const CopyIconButton = ({ value, icon, ...props }: CopyIconButtonProps) =
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
type CopyButtonProps = Omit<ButtonProps, "icon" | "value"> & {
|
||||
value: string | undefined | (() => string);
|
||||
icon?: IconButtonProps["icon"];
|
||||
};
|
||||
export function CopyButton({ value, icon, children, ...props }: CopyButtonProps) {
|
||||
const toast = useToast();
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
return (
|
||||
<Button
|
||||
leftIcon={copied ? <CheckIcon boxSize="1.5em" /> : icon || <CopyToClipboardIcon boxSize="1.2em" />}
|
||||
onClick={() => {
|
||||
const v: string | undefined = typeof value === "function" ? value() : value;
|
||||
|
||||
if (v && navigator.clipboard && !copied) {
|
||||
navigator.clipboard.writeText(v);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} else toast({ description: v, isClosable: true, duration: null });
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{copied ? "Copied" : children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
@ -1,173 +1,102 @@
|
||||
import { PropsWithChildren, ReactNode, useCallback, useMemo, useState } from "react";
|
||||
import { ComponentType, useState } from "react";
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
Heading,
|
||||
AccordionItem,
|
||||
Accordion,
|
||||
AccordionPanel,
|
||||
AccordionIcon,
|
||||
AccordionButton,
|
||||
Box,
|
||||
ModalHeader,
|
||||
Code,
|
||||
AccordionPanelProps,
|
||||
Button,
|
||||
Text,
|
||||
ComponentWithAs,
|
||||
IconProps,
|
||||
IconButton,
|
||||
Flex,
|
||||
} from "@chakra-ui/react";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { ModalProps } from "@chakra-ui/react";
|
||||
import { getSeenRelays } from "applesauce-core/helpers";
|
||||
import { TextNoteContentSymbol } from "applesauce-content/text";
|
||||
import { Root } from "applesauce-content/nast";
|
||||
|
||||
import { getContentPointers, getContentTagRefs, getThreadReferences } from "../../helpers/nostr/event";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import RawValue from "./raw-value";
|
||||
import { CopyIconButton } from "../copy-icon-button";
|
||||
import DebugEventTags from "./event-tags";
|
||||
import { getSharableEventAddress } from "../../services/event-relay-hint";
|
||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||
import { EditIcon } from "../icons";
|
||||
import { RelayFavicon } from "../relay-favicon";
|
||||
import { ErrorBoundary } from "../error-boundary";
|
||||
import { CodeIcon, RelayIcon, ThreadIcon } from "../icons";
|
||||
import RawJsonPage from "./pages/raw";
|
||||
import PenTool01 from "../icons/pen-tool-01";
|
||||
import DebugContentPage from "./pages/content";
|
||||
import DebugThreadingPage from "./pages/threading";
|
||||
import Tag01 from "../icons/tag-01";
|
||||
import DebugTagsPage from "./pages/tags";
|
||||
import DebugEventRelaysPage from "./pages/relays";
|
||||
import Database01 from "../icons/database-01";
|
||||
import DebugEventCachePage from "./pages/cache";
|
||||
|
||||
function Section({
|
||||
label,
|
||||
children,
|
||||
actions,
|
||||
...props
|
||||
}: PropsWithChildren<{ label: string; actions?: ReactNode }> & Omit<AccordionPanelProps, "children">) {
|
||||
return (
|
||||
<AccordionItem>
|
||||
<h2>
|
||||
<AccordionButton>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
{label}
|
||||
</Box>
|
||||
{actions && <div onClick={(e) => e.stopPropagation()}>{actions}</div>}
|
||||
<AccordionIcon ml="2" />
|
||||
</AccordionButton>
|
||||
</h2>
|
||||
<AccordionPanel display="flex" flexDirection="column" gap="2" alignItems="flex-start" {...props}>
|
||||
{children}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
}
|
||||
type DebugTool = {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: ComponentWithAs<"svg", IconProps>;
|
||||
component: ComponentType<{ event: NostrEvent }>;
|
||||
};
|
||||
|
||||
function JsonCode({ data }: { data: any }) {
|
||||
const tools: DebugTool[] = [
|
||||
{ id: "content", name: "Content", icon: PenTool01, component: DebugContentPage },
|
||||
{ id: "json", name: "JSON", icon: CodeIcon, component: RawJsonPage },
|
||||
{ id: "threading", name: "Threading", icon: ThreadIcon, component: DebugThreadingPage },
|
||||
{ id: "tags", name: "Tags", icon: Tag01, component: DebugTagsPage },
|
||||
{ id: "relays", name: "Relays", icon: RelayIcon, component: DebugEventRelaysPage },
|
||||
{ id: "cache", name: "Cache", icon: Database01, component: DebugEventCachePage },
|
||||
];
|
||||
|
||||
function DefaultPage({ event, setSelected }: { setSelected: (id: string) => void; event: NostrEvent }) {
|
||||
return (
|
||||
<Code whiteSpace="pre" overflowX="auto" width="100%" p="4">
|
||||
{JSON.stringify(data, null, 2)}
|
||||
</Code>
|
||||
<>
|
||||
<RawValue heading="Event Id" value={event.id} />
|
||||
<RawValue heading="NIP-19 Encoded Id" value={nip19.noteEncode(event.id)} />
|
||||
<RawValue heading="NIP-19 Pointer" value={getSharableEventAddress(event)} />
|
||||
<Flex gap="2" flexWrap="wrap">
|
||||
{tools.map(({ icon: Icon, name, id }) => (
|
||||
<Button
|
||||
variant="outline"
|
||||
key={id}
|
||||
leftIcon={<Icon boxSize={10} mb="4" />}
|
||||
onClick={() => setSelected(id)}
|
||||
h="36"
|
||||
w="36"
|
||||
flexDirection="column"
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function EventDebugModal({ event, ...props }: { event: NostrEvent } & Omit<ModalProps, "children">) {
|
||||
const contentRefs = useMemo(() => getContentPointers(event.content), [event]);
|
||||
const publish = usePublishEvent();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const broadcast = useCallback(async () => {
|
||||
setLoading(true);
|
||||
await publish("Broadcast", event);
|
||||
setLoading(false);
|
||||
}, []);
|
||||
const [selected, setSelected] = useState("");
|
||||
|
||||
const nast = Reflect.get(event, TextNoteContentSymbol) as Root;
|
||||
const tool = tools.find((t) => t.id === selected);
|
||||
const Page = tool?.component;
|
||||
const IconComponent = tool?.icon;
|
||||
|
||||
return (
|
||||
<Modal size="6xl" {...props}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader p="4">{event.id}</ModalHeader>
|
||||
<ModalHeader px="4" pt="4" pb="2" display="flex" alignItems="center" gap="2">
|
||||
{tool && IconComponent && (
|
||||
<IconButton icon={<IconComponent boxSize={6} />} aria-label="Select Tool" onClick={() => setSelected("")} />
|
||||
)}
|
||||
<Text as="span">{tool?.name || event.id}</Text>
|
||||
</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody p="0">
|
||||
<Accordion allowToggle defaultIndex={event.content ? 1 : 2}>
|
||||
<Section label="IDs">
|
||||
<RawValue heading="Event Id" value={event.id} />
|
||||
<RawValue heading="NIP-19 Encoded Id" value={nip19.noteEncode(event.id)} />
|
||||
<RawValue heading="NIP-19 Pointer" value={getSharableEventAddress(event)} />
|
||||
</Section>
|
||||
<ModalBody px="4" pt="0" pb="4" display="flex" flexDirection="column" gap="2">
|
||||
{Page ? <Page event={event} /> : <DefaultPage setSelected={setSelected} event={event} />}
|
||||
|
||||
<Section
|
||||
label="Content"
|
||||
p="0"
|
||||
actions={<CopyIconButton aria-label="copy json" value={event.content} size="sm" />}
|
||||
>
|
||||
<Code whiteSpace="pre" overflowX="auto" width="100%" p="4">
|
||||
{event.content}
|
||||
</Code>
|
||||
|
||||
{contentRefs.length > 0 && (
|
||||
<>
|
||||
<Heading size="md" px="2">
|
||||
embeds
|
||||
</Heading>
|
||||
{contentRefs.map((pointer, i) => (
|
||||
<>
|
||||
<Code whiteSpace="pre" overflowX="auto" width="100%" p="4">
|
||||
{pointer.type + "\n"}
|
||||
{JSON.stringify(pointer.data, null, 2)}
|
||||
</Code>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Section>
|
||||
<Section
|
||||
label="JSON"
|
||||
p="0"
|
||||
actions={
|
||||
<>
|
||||
<Button
|
||||
leftIcon={<EditIcon />}
|
||||
as={RouterLink}
|
||||
to="/tools/publisher"
|
||||
size="sm"
|
||||
state={{ draft: event }}
|
||||
colorScheme="primary"
|
||||
mr="2"
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
<CopyIconButton aria-label="copy json" value={JSON.stringify(event, null, 2)} size="sm" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<JsonCode data={event} />
|
||||
</Section>
|
||||
<Section label="Threading" p="0">
|
||||
<JsonCode data={getThreadReferences(event)} />
|
||||
</Section>
|
||||
<Section label="Tags">
|
||||
<ErrorBoundary>
|
||||
<DebugEventTags event={event} />
|
||||
</ErrorBoundary>
|
||||
<Heading size="sm">Tags referenced in content</Heading>
|
||||
<JsonCode data={getContentTagRefs(event.content, event.tags)} />
|
||||
</Section>
|
||||
<Section label="Relays">
|
||||
<Text>Seen on:</Text>
|
||||
{Array.from(getSeenRelays(event) ?? []).map((url) => (
|
||||
<Text gap="1" key={url}>
|
||||
<RelayFavicon relay={url} size="xs" /> {url}
|
||||
</Text>
|
||||
))}
|
||||
<Button onClick={broadcast} mr="auto" colorScheme="primary" isLoading={loading}>
|
||||
Broadcast
|
||||
</Button>
|
||||
</Section>
|
||||
{nast && (
|
||||
<Section label="Parsed Content" p="0">
|
||||
<JsonCode data={nast.children} />
|
||||
</Section>
|
||||
)}
|
||||
</Accordion>
|
||||
{tool && (
|
||||
<Button aria-label="Back" onClick={() => setSelected("")}>
|
||||
Back
|
||||
</Button>
|
||||
)}
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
33
src/components/debug-modal/pages/cache.tsx
Normal file
33
src/components/debug-modal/pages/cache.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { CloseButton, Code, Flex, Text } from "@chakra-ui/react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import useEventUpdate from "../../../hooks/use-event-update";
|
||||
import { eventStore } from "../../../services/event-store";
|
||||
|
||||
export default function DebugEventCachePage({ event }: { event: NostrEvent }) {
|
||||
useEventUpdate(event.id);
|
||||
const fields = Object.getOwnPropertySymbols(event);
|
||||
const update = () => eventStore.update(event);
|
||||
|
||||
return (
|
||||
<Flex direction="column">
|
||||
{fields.map((field) => (
|
||||
<Flex gap="2" alignItems="center">
|
||||
<Text fontWeight="bold" whiteSpace="pre">
|
||||
{field.description}
|
||||
</Text>
|
||||
<Code fontFamily="monospace" isTruncated>
|
||||
{JSON.stringify(Reflect.get(event, field))}
|
||||
</Code>
|
||||
<CloseButton
|
||||
ml="auto"
|
||||
onClick={() => {
|
||||
Reflect.deleteProperty(event, field);
|
||||
update();
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
}
|
24
src/components/debug-modal/pages/content.tsx
Normal file
24
src/components/debug-modal/pages/content.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { Code, Flex } from "@chakra-ui/react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { TextNoteContentSymbol } from "applesauce-content/text";
|
||||
import { Root } from "applesauce-content/nast";
|
||||
|
||||
import { CopyButton } from "../../copy-icon-button";
|
||||
import RawJson from "../raw-json";
|
||||
|
||||
export default function DebugContentPage({ event }: { event: NostrEvent }) {
|
||||
const nast = Reflect.get(event, TextNoteContentSymbol) as Root;
|
||||
|
||||
return (
|
||||
<Flex gap="2" direction="column">
|
||||
<CopyButton value={event.content} variant="link" size="sm" ml="auto">
|
||||
Copy content
|
||||
</CopyButton>
|
||||
<Code whiteSpace="pre" overflowX="auto" width="100%" p="2">
|
||||
{event.content}
|
||||
</Code>
|
||||
|
||||
{nast && <RawJson heading="Parsed content" json={nast.children} />}
|
||||
</Flex>
|
||||
);
|
||||
}
|
29
src/components/debug-modal/pages/raw.tsx
Normal file
29
src/components/debug-modal/pages/raw.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { Button, ButtonGroup, Code } from "@chakra-ui/react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { EditIcon } from "../../icons";
|
||||
import { CopyButton } from "../../copy-icon-button";
|
||||
|
||||
export default function RawJsonPage({ event }: { event: NostrEvent }) {
|
||||
return (
|
||||
<>
|
||||
<ButtonGroup size="sm" ml="auto">
|
||||
<CopyButton value={JSON.stringify(event)}>Copy</CopyButton>
|
||||
<Button
|
||||
leftIcon={<EditIcon />}
|
||||
as={RouterLink}
|
||||
to="/tools/publisher"
|
||||
state={{ draft: event }}
|
||||
colorScheme="primary"
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<Code whiteSpace="pre" overflowX="auto" width="100%" p="2">
|
||||
{JSON.stringify(event, null, 2)}
|
||||
</Code>
|
||||
</>
|
||||
);
|
||||
}
|
31
src/components/debug-modal/pages/relays.tsx
Normal file
31
src/components/debug-modal/pages/relays.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { Button, Text } from "@chakra-ui/react";
|
||||
import { getSeenRelays } from "applesauce-core/helpers";
|
||||
|
||||
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
||||
import { RelayFavicon } from "../../relay-favicon";
|
||||
|
||||
export default function DebugEventRelaysPage({ event }: { event: NostrEvent }) {
|
||||
const publish = usePublishEvent();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const broadcast = useCallback(async () => {
|
||||
setLoading(true);
|
||||
await publish("Broadcast", event);
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text>Seen on:</Text>
|
||||
{Array.from(getSeenRelays(event) ?? []).map((url) => (
|
||||
<Text gap="1" key={url}>
|
||||
<RelayFavicon relay={url} size="xs" /> {url}
|
||||
</Text>
|
||||
))}
|
||||
<Button onClick={broadcast} mr="auto" colorScheme="primary" isLoading={loading}>
|
||||
Broadcast
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
18
src/components/debug-modal/pages/tags.tsx
Normal file
18
src/components/debug-modal/pages/tags.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import { ErrorBoundary } from "../../error-boundary";
|
||||
import DebugEventTags from "../event-tags";
|
||||
import RawJson from "../raw-json";
|
||||
import { getContentTagRefs } from "../../../helpers/nostr/event";
|
||||
|
||||
export default function DebugTagsPage({ event }: { event: NostrEvent }) {
|
||||
return (
|
||||
<Flex direction="column" gap="2" alignItems="flex-start" justifyContent="flex-start">
|
||||
<ErrorBoundary>
|
||||
<DebugEventTags event={event} />
|
||||
</ErrorBoundary>
|
||||
<RawJson heading="Tags referenced in content" json={getContentTagRefs(event.content, event.tags)} />
|
||||
</Flex>
|
||||
);
|
||||
}
|
14
src/components/debug-modal/pages/threading.tsx
Normal file
14
src/components/debug-modal/pages/threading.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { getNip10References } from "applesauce-core/helpers";
|
||||
|
||||
import { getThreadReferences } from "../../../helpers/nostr/event";
|
||||
import RawJson from "../raw-json";
|
||||
|
||||
export default function DebugThreadingPage({ event }: { event: NostrEvent }) {
|
||||
return (
|
||||
<>
|
||||
<RawJson heading="Legacy" json={getThreadReferences(event)} />
|
||||
<RawJson heading="NIP-10" json={getNip10References(event)} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -2,15 +2,13 @@ import { Box, Code, Flex, Heading } from "@chakra-ui/react";
|
||||
|
||||
export default function RawJson({ json, heading }: { heading: string; json: any }) {
|
||||
return (
|
||||
<Box>
|
||||
<Box w="full">
|
||||
<Heading size="sm" mb="2">
|
||||
{heading}
|
||||
</Heading>
|
||||
<Flex gap="2">
|
||||
<Code whiteSpace="pre" overflowX="auto" width="100%">
|
||||
{JSON.stringify(json, null, 2)}
|
||||
</Code>
|
||||
</Flex>
|
||||
<Code whiteSpace="pre" overflowX="auto" width="100%" p="2" rounded="md" w="full">
|
||||
{JSON.stringify(json, null, 2)}
|
||||
</Code>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box, Code, Flex, Heading } from "@chakra-ui/react";
|
||||
import { Box, Code, Heading } from "@chakra-ui/react";
|
||||
import { CopyIconButton } from "../copy-icon-button";
|
||||
|
||||
export default function RawValue({ value, heading }: { heading: string; value?: string | null }) {
|
||||
@ -7,12 +7,10 @@ export default function RawValue({ value, heading }: { heading: string; value?:
|
||||
<Heading size="sm" mb="2">
|
||||
{heading}
|
||||
</Heading>
|
||||
<Flex gap="2">
|
||||
<Code fontSize="md" wordBreak="break-all">
|
||||
{value}
|
||||
</Code>
|
||||
<CopyIconButton value={String(value)} size="xs" aria-label="copy" />
|
||||
</Flex>
|
||||
<Code py="1" pl="2" fontSize="md" wordBreak="break-all" userSelect="all" fontFamily="monospace" rounded="md">
|
||||
<CopyIconButton value={String(value)} size="sm" aria-label="copy" variant="ghost" float="right" ml="2" />
|
||||
{value}
|
||||
</Code>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useMemo } from "react";
|
||||
import { Box, Button, ButtonProps, Icon, Link, Text, others, useDisclosure } from "@chakra-ui/react";
|
||||
import { Link as RouterLink, useLocation } from "react-router-dom";
|
||||
import { nip19 } from "nostr-tools";
|
||||
@ -26,7 +27,7 @@ import KeyboardShortcut from "../keyboard-shortcut";
|
||||
import useRecentIds from "../../hooks/use-recent-ids";
|
||||
import { internalApps, internalTools } from "../../views/other-stuff/apps";
|
||||
import { App, AppIcon } from "../../views/other-stuff/component/app-card";
|
||||
import { useMemo } from "react";
|
||||
import Wallet02 from "../icons/wallet-02";
|
||||
|
||||
export default function NavItems() {
|
||||
const location = useLocation();
|
||||
@ -49,6 +50,7 @@ export default function NavItems() {
|
||||
else if (location.pathname.startsWith("/notifications")) active = "notifications";
|
||||
else if (location.pathname.startsWith("/launchpad")) active = "launchpad";
|
||||
else if (location.pathname.startsWith("/discovery")) active = "discovery";
|
||||
else if (location.pathname.startsWith("/wallet")) active = "wallet";
|
||||
else if (location.pathname.startsWith("/dm")) active = "dm";
|
||||
else if (location.pathname.startsWith("/streams")) active = "streams";
|
||||
else if (location.pathname.startsWith("/relays")) active = "relays";
|
||||
@ -149,6 +151,15 @@ export default function NavItems() {
|
||||
Messages
|
||||
{showShortcuts && <KeyboardShortcut letter="m" requireMeta ml="auto" />}
|
||||
</Button>
|
||||
{/* <Button
|
||||
as={RouterLink}
|
||||
to="/wallet"
|
||||
leftIcon={<Wallet02 boxSize={6} />}
|
||||
colorScheme={active === "wallet" ? "primary" : undefined}
|
||||
{...buttonProps}
|
||||
>
|
||||
Wallet
|
||||
</Button> */}
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
|
10
src/helpers/nostr/wallet.ts
Normal file
10
src/helpers/nostr/wallet.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { getTagValue } from "applesauce-core/helpers";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
export function getWalletName(wallet: NostrEvent) {
|
||||
return getTagValue(wallet, "name");
|
||||
}
|
||||
|
||||
export function getWalletDescription(wallet: NostrEvent) {
|
||||
return getTagValue(wallet, "description");
|
||||
}
|
13
src/hooks/use-event-update.ts
Normal file
13
src/hooks/use-event-update.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { eventStore } from "../services/event-store";
|
||||
|
||||
export default function useEventUpdate(id?: string) {
|
||||
const [_count, setCount] = useState(0);
|
||||
|
||||
const observable = useMemo(() => (id ? eventStore.event(id) : undefined), [id]);
|
||||
useEffect(() => {
|
||||
if (!observable) return;
|
||||
const sub = observable.subscribe(() => setCount((v) => v + 1));
|
||||
return () => sub.unsubscribe();
|
||||
}, [observable]);
|
||||
}
|
68
src/views/wallet/index.tsx
Normal file
68
src/views/wallet/index.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import { Badge, Button, Card, CardBody, CardFooter, CardHeader, Flex, Heading } from "@chakra-ui/react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import {
|
||||
getEventUID,
|
||||
getTagValue,
|
||||
hasHiddenTags,
|
||||
HiddenTagsSigner,
|
||||
isHiddenTagsLocked,
|
||||
unlockHiddenTags,
|
||||
} from "applesauce-core/helpers";
|
||||
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import useAsyncErrorHandler from "../../hooks/use-async-error-handler";
|
||||
import { getWalletDescription, getWalletName } from "../../helpers/nostr/wallet";
|
||||
import DebugEventButton from "../../components/debug-modal/debug-event-button";
|
||||
import useEventUpdate from "../../hooks/use-event-update";
|
||||
import { eventStore } from "../../services/event-store";
|
||||
|
||||
function Wallet({ wallet }: { wallet: NostrEvent }) {
|
||||
useEventUpdate(wallet.id);
|
||||
|
||||
const account = useCurrentAccount()!;
|
||||
const locked = hasHiddenTags(wallet) && isHiddenTagsLocked(wallet);
|
||||
|
||||
const unlock = useAsyncErrorHandler(async () => {
|
||||
const signer = account.signer;
|
||||
if (!signer || !signer.nip04) throw new Error("Missing signer");
|
||||
|
||||
await unlockHiddenTags(wallet, signer as HiddenTagsSigner, eventStore);
|
||||
}, [wallet, account]);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader display="flex" gap="2" p="2" alignItems="center">
|
||||
<Heading size="md">{getWalletName(wallet) || getTagValue(wallet, "d")}</Heading>
|
||||
{locked && <Badge colorScheme="orange">Locked</Badge>}
|
||||
<DebugEventButton event={wallet} variant="ghost" ml="auto" size="sm" />
|
||||
</CardHeader>
|
||||
<CardBody px="2" py="0">
|
||||
{getWalletDescription(wallet)}
|
||||
</CardBody>
|
||||
<CardFooter p="2">
|
||||
{locked && (
|
||||
<Button onClick={unlock} colorScheme="primary">
|
||||
Unlock
|
||||
</Button>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default function WalletView() {
|
||||
const account = useCurrentAccount()!;
|
||||
|
||||
const readRelays = useReadRelays();
|
||||
const { timeline } = useTimelineLoader("wallets", readRelays, { kinds: [37375], authors: [account.pubkey] });
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2">
|
||||
{timeline.map((wallet) => (
|
||||
<Wallet key={getEventUID(wallet)} wallet={wallet} />
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user