applesauce-react 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ /// <reference types="react" />
2
+ import { Link } from "applesauce-content/nast";
3
+ export type LinkRenderer = (url: URL, node: Link) => JSX.Element | false | null;
4
+ export declare function buildLinkRenderer(handlers: LinkRenderer[]): import("react").NamedExoticComponent<import("./nast.js").ExtraProps<Link>>;
@@ -0,0 +1,23 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo, useMemo } from "react";
3
+ export function buildLinkRenderer(handlers) {
4
+ const LinkRenderer = ({ node }) => {
5
+ const content = useMemo(() => {
6
+ try {
7
+ const url = new URL(node.href);
8
+ for (const handler of handlers) {
9
+ try {
10
+ const content = handler(url, node);
11
+ if (content)
12
+ return content;
13
+ }
14
+ catch (e) { }
15
+ }
16
+ }
17
+ catch (error) { }
18
+ return null;
19
+ }, [node.href, node.value]);
20
+ return content || _jsx(_Fragment, { children: node.value });
21
+ };
22
+ return memo(LinkRenderer);
23
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./nast.js";
2
+ export * from "./build-link-renderer.js";
@@ -0,0 +1,2 @@
1
+ export * from "./nast.js";
2
+ export * from "./build-link-renderer.js";
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { Content, ContentMap, Root } from "applesauce-content/nast";
3
+ type Component<ComponentProps> = React.FunctionComponent<ComponentProps> | React.ComponentClass;
4
+ export type ExtraProps<T extends Content> = {
5
+ node: T;
6
+ };
7
+ export type ComponentMap = Partial<{
8
+ [k in keyof ContentMap]: Component<ExtraProps<ContentMap[k]>>;
9
+ }>;
10
+ /** Render a nostr syntax tree to JSX components */
11
+ export declare function renderNast(root: Root, components: ComponentMap): import("react/jsx-runtime").JSX.Element;
12
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /** Render a nostr syntax tree to JSX components */
3
+ export function renderNast(root, components) {
4
+ const indexes = {};
5
+ return (_jsx(_Fragment, { children: root.children.map((node) => {
6
+ indexes[node.type] = indexes[node.type] ?? 0;
7
+ const index = indexes[node.type];
8
+ indexes[node.type]++;
9
+ const Component = components[node.type];
10
+ if (!Component)
11
+ return null;
12
+ // @ts-expect-error
13
+ return _jsx(Component, { node: node }, node.type + "-" + index);
14
+ }) }));
15
+ }
@@ -1,2 +1,4 @@
1
1
  export * from "./use-observable.js";
2
2
  export * from "./use-store-query.js";
3
+ export * from "./use-render-nast.js";
4
+ export * from "./use-rendered-text-content.js";
@@ -1,2 +1,4 @@
1
1
  export * from "./use-observable.js";
2
2
  export * from "./use-store-query.js";
3
+ export * from "./use-render-nast.js";
4
+ export * from "./use-rendered-text-content.js";
@@ -2,16 +2,37 @@ import { useState, useEffect } from "react";
2
2
  import { isStateful } from "applesauce-core/observable";
3
3
  /** Subscribe to the value of an observable */
4
4
  export function useObservable(observable) {
5
- const [_, forceUpdate] = useState(0);
6
- const [value, update] = useState(observable && isStateful(observable) ? observable.value : undefined);
5
+ const [_count, update] = useState(0);
6
+ // const init = useRef(true);
7
+ // const value = useRef<T | undefined>(observable && isStateful(observable) ? observable.value : undefined);
8
+ // const prev = useRef(observable);
9
+ // const sub = useRef<ZenObservable.Subscription>();
10
+ // // This intentionally does not use useEffect
11
+ // // because we want the value to be returned on the first render
12
+ // if (!sub.current || prev.current !== observable) {
13
+ // prev.current = observable;
14
+ // if (sub.current) sub.current.unsubscribe();
15
+ // sub.current = observable?.subscribe((v) => {
16
+ // value.current = v;
17
+ // // only explicitly update if its not the first render
18
+ // if (!init.current) update(count + 1);
19
+ // });
20
+ // init.current = false;
21
+ // }
22
+ // // unsubscribe when unmount
23
+ // useEffect(() => {
24
+ // return () => {
25
+ // if (sub.current) sub.current.unsubscribe();
26
+ // };
27
+ // }, []);
28
+ // return value.current;
29
+ const [value, setValue] = useState(observable && isStateful(observable) ? observable.value : undefined);
7
30
  useEffect(() => {
8
- if (!observable)
9
- return;
10
- const s = observable.subscribe((v) => {
11
- update(v);
12
- forceUpdate(Math.random());
31
+ const sub = observable?.subscribe((v) => {
32
+ setValue(v);
33
+ update((c) => c + 1);
13
34
  });
14
- return () => s.unsubscribe();
35
+ return () => sub?.unsubscribe();
15
36
  }, [observable]);
16
37
  return value;
17
38
  }
@@ -0,0 +1,6 @@
1
+ /// <reference types="react" />
2
+ import { Root } from "applesauce-content/nast";
3
+ import { ComponentMap } from "../helpers/nast.js";
4
+ export { ComponentMap };
5
+ /** A hook to get the rendered output of a nostr syntax tree */
6
+ export declare function useRenderNast(root: Root | undefined, components: ComponentMap): JSX.Element | null;
@@ -0,0 +1,6 @@
1
+ import { useMemo } from "react";
2
+ import { renderNast } from "../helpers/nast.js";
3
+ /** A hook to get the rendered output of a nostr syntax tree */
4
+ export function useRenderNast(root, components) {
5
+ return useMemo(() => (root ? renderNast(root, components) : null), [root, Object.keys(components).join("|")]);
6
+ }
@@ -0,0 +1,18 @@
1
+ /// <reference types="react" />
2
+ import { getParsedTextContent } from "applesauce-content/text";
3
+ import { EventTemplate, NostrEvent } from "nostr-tools";
4
+ import { ComponentMap } from "../helpers/nast.js";
5
+ import { LinkRenderer } from "../helpers/build-link-renderer.js";
6
+ export { ComponentMap };
7
+ type Options = {
8
+ /** Override transformers */
9
+ transformers?: Parameters<typeof getParsedTextContent>[2];
10
+ /** If set will use {@link buildLinkRenderer} to render links */
11
+ linkRenderers?: LinkRenderer[];
12
+ /** Override event content */
13
+ content?: string;
14
+ /** Maximum length */
15
+ maxLength?: number;
16
+ };
17
+ /** Returns the parsed and render text content for an event */
18
+ export declare function useRenderedContent(event: NostrEvent | EventTemplate | string | undefined, components: ComponentMap, opts?: Options): JSX.Element | null;
@@ -0,0 +1,16 @@
1
+ import { useMemo } from "react";
2
+ import { getParsedTextContent } from "applesauce-content/text";
3
+ import { useRenderNast } from "./use-render-nast.js";
4
+ import { buildLinkRenderer } from "../helpers/build-link-renderer.js";
5
+ import { truncateContent } from "applesauce-content/nast";
6
+ /** Returns the parsed and render text content for an event */
7
+ export function useRenderedContent(event, components, opts) {
8
+ // if link renderers are set, override the link components
9
+ const _components = useMemo(() => (opts?.linkRenderers ? { ...components, link: buildLinkRenderer(opts.linkRenderers) } : components), [opts?.linkRenderers, components]);
10
+ // add additional transformers
11
+ const nast = useMemo(() => (event ? getParsedTextContent(event, opts?.content, opts?.transformers) : undefined), [event, opts?.content, opts?.transformers]);
12
+ let truncated = nast;
13
+ if (opts?.maxLength && nast)
14
+ truncated = truncateContent(nast, opts.maxLength);
15
+ return useRenderNast(truncated, _components);
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-react",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "React hooks for applesauce",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,10 +26,15 @@
26
26
  "./hooks/*": {
27
27
  "import": "./dist/hooks/*.js",
28
28
  "types": "./dist/hooks/*.d.ts"
29
+ },
30
+ "./helpers": {
31
+ "import": "./dist/helpers/index.js",
32
+ "types": "./dist/helpers/index.d.ts"
29
33
  }
30
34
  },
31
35
  "dependencies": {
32
- "applesauce-core": "^0.6.0",
36
+ "applesauce-content": "^0.7.0",
37
+ "applesauce-core": "^0.7.0",
33
38
  "nostr-tools": "^2.7.2",
34
39
  "react": "^18.3.1",
35
40
  "zen-observable": "^0.10.0"