diff --git a/package.json b/package.json index ac6543325..33d69487c 100644 --- a/package.json +++ b/package.json @@ -43,13 +43,13 @@ "@uiw/codemirror-theme-github": "^4.23.0", "@uiw/react-codemirror": "^4.23.0", "@webscopeio/react-textarea-autocomplete": "^4.9.2", - "applesauce-channel": "^0.7.0", - "applesauce-content": "^0.7.0", - "applesauce-core": "^0.7.0", - "applesauce-net": "link:../applesauce/packages/net", - "applesauce-react": "^0.7.0", - "applesauce-lists": "link:../applesauce/packages/lists", - "applesauce-signer": "^0.7.0", + "applesauce-channel": "^0.8.0", + "applesauce-content": "^0.8.0", + "applesauce-core": "^0.8.0", + "applesauce-net": "^0.8.0", + "applesauce-react": "^0.8.0", + "applesauce-lists": "^0.8.0", + "applesauce-signer": "^0.8.0", "bech32": "^2.0.0", "blossom-client-sdk": "^0.7.0", "blossom-drive-sdk": "^0.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed6c7d43e..fc9c008c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,26 +94,26 @@ importers: specifier: ^4.9.2 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: ^0.7.0 - version: link:../applesauce/packages/channel + specifier: ^0.8.0 + version: 0.8.0(typescript@5.6.2) applesauce-content: - specifier: ^0.7.0 - version: link:../applesauce/packages/content + specifier: ^0.8.0 + version: 0.8.0(typescript@5.6.2) applesauce-core: - specifier: ^0.7.0 - version: link:../applesauce/packages/core + specifier: ^0.8.0 + version: 0.8.0(typescript@5.6.2) applesauce-lists: - specifier: link:../applesauce/packages/lists - version: link:../applesauce/packages/lists + specifier: ^0.8.0 + version: 0.8.0(typescript@5.6.2) applesauce-net: - specifier: link:../applesauce/packages/net - version: link:../applesauce/packages/net + specifier: ^0.8.0 + version: 0.8.0(typescript@5.6.2) applesauce-react: - specifier: ^0.7.0 - version: link:../applesauce/packages/react + specifier: ^0.8.0 + version: 0.8.0(typescript@5.6.2) applesauce-signer: - specifier: ^0.7.0 - version: link:../applesauce/packages/signer + specifier: ^0.8.0 + version: 0.8.0(typescript@5.6.2) bech32: specifier: ^2.0.0 version: 2.0.0 @@ -2310,6 +2310,27 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + applesauce-channel@0.8.0: + resolution: {integrity: sha512-WkE7A4WHGUdjCTwzfnfMms0KG2I62GN020wKNVIkvtdZAW1ummkSVVdcSPzZHSQZIabMRcJ5xv3tGf6EZ7tjxw==} + + applesauce-content@0.8.0: + resolution: {integrity: sha512-AtEeClCbZUy5lLppjiSGQRmZN86Xsv311yYom1gCr5hQmz+vsqKdqiWukMrlyODq3g7zkNoWaN3ZRt7RNQ35sw==} + + applesauce-core@0.8.0: + resolution: {integrity: sha512-ezH0ufSZMSS4EZlEcKSciLeq9bY2vv07pLBWLUrwZX3uCij3hcR8UtN7W78+XdWGNs0PIpSUPiWu3sEvVWFtFA==} + + applesauce-lists@0.8.0: + resolution: {integrity: sha512-3ITd1y3frDIsVCr0pk/7Nd9NMIqM/JB8Mp3UqBjjphdUBOUHc31WkZhcGxQgSJnt7x0rv9FVbmjsS3Q2I4cKnw==} + + applesauce-net@0.8.0: + resolution: {integrity: sha512-yT9q6Z2slFlUP52/IqR4jWfOOfrSulCUnH3svSqwtmNGEupBEkdvMD64fbKqTGKWhyT3KkydbfTiOFDzMWv4/w==} + + applesauce-react@0.8.0: + resolution: {integrity: sha512-ViLRORwJ5WeLshYlmPYq6t6IYST4ieuLeRu08ssR1DS3ihBFS+hjmDEht7WyR5qSGbkqqtMyqSBdYxp4mEUF0Q==} + + applesauce-signer@0.8.0: + resolution: {integrity: sha512-TunYBkFDXA4Kc6kOhuI0zCJ/+CCGzb98jZ+RXojvVmKxOb1/JJwf103R1mY6223EqhMJtvbwrRQE66n2CdPxCw==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -3365,6 +3386,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkifyjs@4.1.3: + resolution: {integrity: sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -4121,6 +4145,9 @@ packages: remark-wiki-link@2.0.1: resolution: {integrity: sha512-F8Eut1E7GWfFm4ZDTI6/4ejeZEHZgnVk6E933Yqd/ssYsc4AyI32aGakxwsGcEzbbE7dkWi1EfLlGAdGgOZOsA==} + remark@15.0.1: + resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + remove-accents@0.5.0: resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} @@ -4755,6 +4782,12 @@ packages: zen-observable@0.10.0: resolution: {integrity: sha512-iI3lT0iojZhKwT5DaFy2Ce42n3yFcLdFyOh01G7H0flMY60P8MJuVFEoJoNwXlmAyQ45GrjL6AcZmmlv8A5rbw==} + zen-observable@0.9.0: + resolution: {integrity: sha512-xBrvSw1htnb2VEYpgjG7etqztHSZ4KyKzJgFh/rucI7QzkbxD0rVNmIni6lia6KhWs7RBbBBLZksGKrCSaDAMQ==} + + zen-push@0.3.1: + resolution: {integrity: sha512-6XNTSVz8Z/5+8BzOQFQr+HOr/j7K+ZJfc4tdCCmuiq8y2Ph+aODqagZnXKbKIFZrQ3nSIcdek9gAuEe+p3zJUg==} + zustand@4.5.5: resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} engines: {node: '>=12.7.0'} @@ -7232,6 +7265,95 @@ snapshots: dependencies: color-convert: 2.0.1 + applesauce-channel@0.8.0(typescript@5.6.2): + dependencies: + applesauce-core: 0.8.0(typescript@5.6.2) + nostr-tools: 2.7.2(typescript@5.6.2) + rxjs: 7.8.1 + transitivePeerDependencies: + - supports-color + - typescript + + applesauce-content@0.8.0(typescript@5.6.2): + dependencies: + '@cashu/cashu-ts': 1.1.0 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + applesauce-core: 0.8.0(typescript@5.6.2) + linkifyjs: 4.1.3 + mdast-util-find-and-replace: 3.0.1 + nostr-tools: 2.7.2(typescript@5.6.2) + remark: 15.0.1 + remark-parse: 11.0.0 + unified: 11.0.5 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + applesauce-core@0.8.0(typescript@5.6.2): + dependencies: + debug: 4.3.7 + json-stringify-deterministic: 1.0.12 + nanoid: 5.0.7 + nostr-tools: 2.7.2(typescript@5.6.2) + rxjs: 7.8.1 + transitivePeerDependencies: + - supports-color + - typescript + + applesauce-lists@0.8.0(typescript@5.6.2): + 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.8.0(typescript@5.6.2) + debug: 4.3.7 + nostr-tools: 2.7.2(typescript@5.6.2) + rxjs: 7.8.1 + transitivePeerDependencies: + - supports-color + - typescript + + applesauce-net@0.8.0(typescript@5.6.2): + dependencies: + applesauce-core: 0.8.0(typescript@5.6.2) + nanoid: 5.0.7 + nostr-tools: 2.7.2(typescript@5.6.2) + rxjs: 7.8.1 + zen-push: 0.3.1 + transitivePeerDependencies: + - supports-color + - typescript + + applesauce-react@0.8.0(typescript@5.6.2): + dependencies: + applesauce-content: 0.8.0(typescript@5.6.2) + applesauce-core: 0.8.0(typescript@5.6.2) + nostr-tools: 2.7.2(typescript@5.6.2) + react: 18.3.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - supports-color + - typescript + + applesauce-signer@0.8.0(typescript@5.6.2): + 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.8.0(typescript@5.6.2) + applesauce-net: 0.8.0(typescript@5.6.2) + debug: 4.3.7 + nanoid: 5.0.7 + nostr-tools: 2.7.2(typescript@5.6.2) + transitivePeerDependencies: + - supports-color + - typescript + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -8399,6 +8521,8 @@ snapshots: lines-and-columns@1.2.4: {} + linkifyjs@4.1.3: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -9425,6 +9549,15 @@ snapshots: mdast-util-wiki-link: 0.1.2 micromark-extension-wiki-link: 0.0.4 + remark@15.0.1: + dependencies: + '@types/mdast': 4.0.4 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + remove-accents@0.5.0: {} repeat-string@1.6.1: {} @@ -10130,6 +10263,12 @@ snapshots: zen-observable@0.10.0: {} + zen-observable@0.9.0: {} + + zen-push@0.3.1: + dependencies: + zen-observable: 0.9.0 + zustand@4.5.5(@types/react@18.3.10)(react@18.3.1): dependencies: use-sync-external-store: 1.2.2(react@18.3.1) diff --git a/src/classes/relay-pool.ts b/src/classes/relay-pool.ts index 432d9090e..1d46d6043 100644 --- a/src/classes/relay-pool.ts +++ b/src/classes/relay-pool.ts @@ -44,7 +44,7 @@ export default class RelayPool implements IConnectionPool { const safeURL = safeRelayUrl(relayOrUrl); if (safeURL) { return this.relays.get(safeURL) || this.requestRelay(safeURL); - } else return null; + } else return; } else if (relayOrUrl instanceof URL) { return this.relays.get(relayOrUrl.toString()) || this.requestRelay(relayOrUrl.toString()); } diff --git a/src/components/content/cashu.tsx b/src/components/content/components/cashu.tsx similarity index 75% rename from src/components/content/cashu.tsx rename to src/components/content/components/cashu.tsx index 4ea9bf8cf..ab86e54a6 100644 --- a/src/components/content/cashu.tsx +++ b/src/components/content/components/cashu.tsx @@ -1,6 +1,6 @@ import { CashuToken } from "applesauce-content/nast"; -import InlineCachuCard from "../cashu/inline-cashu-card"; +import InlineCachuCard from "../../cashu/inline-cashu-card"; export default function Cashu({ node }: { node: CashuToken }) { return ; diff --git a/src/components/content/gallery.tsx b/src/components/content/components/gallery.tsx similarity index 87% rename from src/components/content/gallery.tsx rename to src/components/content/components/gallery.tsx index 982b60153..d925cefac 100644 --- a/src/components/content/gallery.tsx +++ b/src/components/content/components/gallery.tsx @@ -3,11 +3,11 @@ import { Link } from "@chakra-ui/react"; import { handleImageFallbacks } from "blossom-client-sdk"; import { NostrEvent } from "nostr-tools"; -import { EmbeddedImageProps, getPubkeyMediaServers, TrustImage, useImageThumbnail } from "../content/links"; -import { useRegisterSlide } from "../lightbox-provider"; -import PhotoGallery, { PhotoWithoutSize } from "../photo-gallery"; -import { useBreakpointValue } from "../../providers/global/breakpoint-provider"; -import ExpandableEmbed from "./components/expandable-embed"; +import { EmbeddedImageProps, getPubkeyMediaServers, TrustImage, useImageThumbnail } from "../links"; +import { useRegisterSlide } from "../../lightbox-provider"; +import PhotoGallery, { PhotoWithoutSize } from "../../photo-gallery"; +import { useBreakpointValue } from "../../../providers/global/breakpoint-provider"; +import ExpandableEmbed from "./expandable-embed"; // nevent1qqs8397rp8tt60f3lm8zldt8uqljuqw9axp8z79w0qsmj3r96lmg4tgpz3mhxue69uhhyetvv9ujuerpd46hxtnfduq3zamnwvaz7tmwdaehgun4v5hxxmmd0mkwa9 export const GalleryImage = forwardRef( diff --git a/src/components/content/ininle-emoji.tsx b/src/components/content/components/ininle-emoji.tsx similarity index 100% rename from src/components/content/ininle-emoji.tsx rename to src/components/content/components/ininle-emoji.tsx diff --git a/src/components/content/mention.tsx b/src/components/content/components/mention.tsx similarity index 84% rename from src/components/content/mention.tsx rename to src/components/content/components/mention.tsx index 4f2afd063..ade15d3c1 100644 --- a/src/components/content/mention.tsx +++ b/src/components/content/components/mention.tsx @@ -1,7 +1,7 @@ import { ComponentMap } from "applesauce-react/hooks"; -import UserLink from "../user/user-link"; -import { EmbedEventPointer } from "../embed-event"; +import UserLink from "../../user/user-link"; +import { EmbedEventPointer } from "../../embed-event"; const Mention: ComponentMap["mention"] = ({ node }) => { switch (node.decoded.type) { diff --git a/src/components/content/components/nip.tsx b/src/components/content/components/nip.tsx new file mode 100644 index 000000000..c61af0a20 --- /dev/null +++ b/src/components/content/components/nip.tsx @@ -0,0 +1,16 @@ +import { Link, Tooltip } from "@chakra-ui/react"; +import { NIPToken } from "../transform/nip-notation"; + +export default function NipDefinition({ node }: { node: NIPToken }) { + return ( + + + {node.value} + + + ); +} diff --git a/src/components/content/index.tsx b/src/components/content/index.tsx index f69c83b36..90ffe6c80 100644 --- a/src/components/content/index.tsx +++ b/src/components/content/index.tsx @@ -3,9 +3,11 @@ import { Link, Text } from "@chakra-ui/react"; import { Link as RouterLink } from "react-router-dom"; import { ComponentMap } from "applesauce-react/hooks"; -import Mention from "./mention"; -import Cashu from "./cashu"; -import { InlineEmoji } from "./ininle-emoji"; +import Mention from "./components/mention"; +import Cashu from "./components/cashu"; +import { InlineEmoji } from "./components/ininle-emoji"; +import NipDefinition from "./components/nip"; +import { ImageGallery } from "./components/gallery"; const InlineFedimintCard = lazy(() => import("../fedimint/inline-fedimint-card")); export const components: ComponentMap = { @@ -19,4 +21,6 @@ export const components: ComponentMap = { #{node.name} ), + nip: NipDefinition, + gallery: ({ node }) => , }; diff --git a/src/components/content/links-old.tsx b/src/components/content/links-old.tsx deleted file mode 100644 index 48e1edc00..000000000 --- a/src/components/content/links-old.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { memo, useMemo } from "react"; -import { Link } from "applesauce-content/nast"; -import { ComponentMap } from "applesauce-react/hooks"; - -export type LinkRenderer = (url: URL, node: Link) => JSX.Element | false | null; - -export default function buildLinkComponent(handlers: LinkRenderer[]) { - const LinkRenderer: ComponentMap["link"] = ({ node }) => { - const content = useMemo(() => { - try { - const url = new URL(node.href); - for (const handler of handlers) { - try { - const content = handler(url, node); - if (content) return content; - } catch (e) {} - } - } catch (error) {} - }, [node.href, node.value]); - - return content || <>{node.value}; - }; - - return memo(LinkRenderer); -} diff --git a/src/components/content/links/emoji.tsx b/src/components/content/links/emoji.tsx index 05f2dc2b8..84678df85 100644 --- a/src/components/content/links/emoji.tsx +++ b/src/components/content/links/emoji.tsx @@ -1,7 +1,7 @@ import { EmbedableContent, embedJSX } from "../../../helpers/embeds"; import { DraftNostrEvent, NostrEvent, isEmojiTag } from "../../../types/nostr-event"; import { getMatchEmoji } from "../../../helpers/regexp"; -import { InlineEmoji } from "../ininle-emoji"; +import { InlineEmoji } from "../components/ininle-emoji"; import { getEmojiTag } from "applesauce-core/helpers"; export function embedEmoji(content: EmbedableContent, note: NostrEvent | DraftNostrEvent) { diff --git a/src/components/content/links/index.ts b/src/components/content/links/index.ts index b594398d1..1b589ad80 100644 --- a/src/components/content/links/index.ts +++ b/src/components/content/links/index.ts @@ -6,7 +6,6 @@ export * from "./image"; export * from "./lightning"; export * from "./model"; export * from "./music"; -export * from "./nostr"; export * from "./reddit"; export * from "./simplex"; export * from "./twitter"; diff --git a/src/components/content/links/nostr.tsx b/src/components/content/links/nostr.tsx deleted file mode 100644 index a0437a2d1..000000000 --- a/src/components/content/links/nostr.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Link, Tooltip } from "@chakra-ui/react"; - -import { EmbedableContent, embedJSX } from "../../../helpers/embeds"; -import { NIP_NAMES } from "../../../views/relays/components/supported-nips"; - -export function embedNipDefinitions(content: EmbedableContent) { - return embedJSX(content, { - name: "nip-definition", - regexp: /nip-?(\d\d)/gi, - render: (match) => { - if (NIP_NAMES[match[1]]) { - return ( - - - {match[0]} - - - ); - } - return null; - }, - }); -} diff --git a/src/components/content/transform/nip-notation.ts b/src/components/content/transform/nip-notation.ts new file mode 100644 index 000000000..6d6669f50 --- /dev/null +++ b/src/components/content/transform/nip-notation.ts @@ -0,0 +1,50 @@ +import { Transformer } from "unified"; +import { Root, findAndReplace, Node } from "applesauce-content/nast"; + +import { NIP_NAMES } from "../../../views/relays/components/supported-nips"; + +export interface NIPToken extends Node { + type: "nip"; + nip: string; + name: string; + value: string; +} + +declare module "applesauce-content/nast" { + export interface NIPToken extends Node { + type: "nip"; + nip: string; + name: string; + value: string; + } + + export interface ContentMap { + nip: NIPToken; + } +} + +export function nipDefinitions(): Transformer { + return (tree) => { + findAndReplace(tree, [ + [ + /(?<=^|[^\p{L}])nip-?(\d{2,3})/giu, + (match: string, $1: string) => { + try { + const nip = $1; + const name = NIP_NAMES[nip]; + if (!name) return false; + + return { + type: "nip", + nip, + value: match, + name, + }; + } catch (error) {} + + return false; + }, + ], + ]); + }; +} diff --git a/src/components/note/timeline-note/text-note-contents.tsx b/src/components/note/timeline-note/text-note-contents.tsx index 7ec003961..92da8b908 100644 --- a/src/components/note/timeline-note/text-note-contents.tsx +++ b/src/components/note/timeline-note/text-note-contents.tsx @@ -2,7 +2,7 @@ import React, { Suspense, useMemo } from "react"; import { Box, BoxProps, Spinner } from "@chakra-ui/react"; import { EventTemplate, NostrEvent } from "nostr-tools"; import { useRenderedContent } from "applesauce-react/hooks"; -import { defaultTransformers } from "applesauce-content/text"; +import { defaultTransformers, galleries } from "applesauce-content/text"; import { renderWavlakeUrl, @@ -29,8 +29,9 @@ import { LightboxProvider } from "../../lightbox-provider"; import MediaOwnerProvider from "../../../providers/local/media-owner-provider"; import { components } from "../../content"; import { fedimintTokens } from "../../../helpers/fedimint"; +import { nipDefinitions } from "../../content/transform/nip-notation"; -const transformers = [...defaultTransformers, fedimintTokens]; +const transformers = [...defaultTransformers, galleries, nipDefinitions, fedimintTokens]; export type TextNoteContentsProps = { event: NostrEvent | EventTemplate; @@ -62,7 +63,6 @@ const linkRenderers = [ export const TextNoteContents = React.memo( ({ event, noOpenGraphLinks, maxLength, ...props }: TextNoteContentsProps & Omit) => { - // @ts-expect-error const content = useRenderedContent(event, components, { linkRenderers, transformers, maxLength }); return ( diff --git a/src/components/timeline-page/media-timeline/index.tsx b/src/components/timeline-page/media-timeline/index.tsx index 412424455..f50cfb668 100644 --- a/src/components/timeline-page/media-timeline/index.tsx +++ b/src/components/timeline-page/media-timeline/index.tsx @@ -11,7 +11,7 @@ import PhotoGallery, { PhotoWithoutSize } from "../../photo-gallery"; import { NostrEvent } from "../../../types/nostr-event"; import { useBreakpointValue } from "../../../providers/global/breakpoint-provider"; import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref"; -import { GalleryImage } from "../../content/gallery"; +import { GalleryImage } from "../../content/components/gallery"; function CustomGalleryImage({ event, ...props }: EmbeddedImageProps & { event: NostrEvent }) { const ref = useEventIntersectionRef(event); diff --git a/src/views/dms/components/direct-message-content.tsx b/src/views/dms/components/direct-message-content.tsx index fc17b1227..0fc92e3e1 100644 --- a/src/views/dms/components/direct-message-content.tsx +++ b/src/views/dms/components/direct-message-content.tsx @@ -54,7 +54,6 @@ export default function DirectMessageContent({ ...props }: { event: NostrEvent; text: string } & BoxProps) { const { plaintext } = useKind4Decrypt(event); - // @ts-expect-error const content = useRenderedContent(plaintext, components, { transformers, linkRenderers }); return (