remove not needed logic from waku/react package

This commit is contained in:
Sasha 2025-10-08 00:36:42 +02:00
parent 8e9577fb25
commit bfbe6379ba
No known key found for this signature in database
11 changed files with 27 additions and 405 deletions

2
package-lock.json generated
View File

@ -37553,7 +37553,7 @@
"node": ">=22"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
"react": "^16.8.0 || ^17 || ^18 || ^19"
}
},
"packages/react/node_modules/@typescript-eslint/eslint-plugin": {

View File

@ -86,7 +86,7 @@
"rollup-plugin-typescript2": "^0.36.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
"react": "^16.8.0 || ^17 || ^18 || ^19"
},
"files": [
"dist",

View File

@ -1,53 +0,0 @@
import React from "react";
import type { ContentPair, ReactChildrenProps } from "./types.js";
import { useCreateContentPair } from "./useCreateContentPair.js";
type ContentPairContextType = Partial<ContentPair>;
const ContentPairContext = React.createContext<ContentPairContextType>({
decoder: undefined,
encoder: undefined
});
/**
* Hook to retrieve Encoder/Decoder pair from Context.
* @example
* const { encoder, decoder } = useContentPair();
* @returns {Object} { encoder, decoder }
*/
export const useContentPair = (): ContentPairContextType =>
React.useContext(ContentPairContext);
type ContentPairProviderProps = ReactChildrenProps & {
contentTopic: string;
ephemeral?: boolean;
};
/**
* Provider for creating Encoder/Decoder pair based on contentTopic
* @example
* const App = (props) => (
* <ContentPairProvider contentTopic="/toy-chat/2/huilong/proto">
* <Component />
* </ContentPairProvider>
* );
* const Component = (props) => {
* const { encoder, decoder } = useContentPair();
* ...
* };
* @param {string} contentTopic - content topic for configuring the pair
* @param {boolean} ephemeral - flag to set messages ephemeral according to RFC https://rfc.vac.dev/spec/14/
* @returns React ContentPair Provider component
*/
export const ContentPairProvider: React.FunctionComponent<
ContentPairProviderProps
> = (props) => {
const result = useCreateContentPair(props.contentTopic, props.ephemeral);
return (
<ContentPairContext.Provider value={result}>
{props.children}
</ContentPairContext.Provider>
);
};

View File

@ -1,11 +1,7 @@
import type { CreateNodeOptions, IWaku } from "@waku/interfaces";
import type { CreateNodeOptions, IWaku, LightNode } from "@waku/interfaces";
import React from "react";
import type {
BootstrapNodeOptions,
CreateNodeResult,
ReactChildrenProps
} from "./types.js";
import type { CreateNodeResult, ReactChildrenProps } from "./types.js";
import { useCreateLightNode } from "./useCreateWaku.js";
type WakuContextType<T extends IWaku> = CreateNodeResult<T>;
@ -28,35 +24,31 @@ const WakuContext = React.createContext<WakuContextType<IWaku>>({
* const { node, isLoading, error } = useWaku();
* @returns WakuContext
*/
export const useWaku = <T extends IWaku>(): WakuContextType<T> =>
React.useContext(WakuContext) as WakuContextType<T>;
export const useWaku = (): WakuContextType<LightNode> =>
React.useContext(WakuContext) as WakuContextType<LightNode>;
type ProviderProps<T> = ReactChildrenProps & BootstrapNodeOptions<T>;
type ProviderProps<T> = ReactChildrenProps & { options: T };
/**
* Provider for creating Light Node based on options passed.
* Provider for creating Waku node based on options passed.
* @example
* const App = (props) => (
* <LightNodeProvider options={{...}}>
* <WakuProvider options={{...}}>
* <Component />
* </LightNodeProvider>
* </WakuProvider>
* );
* const Component = (props) => {
* const { node, isLoading, error } = useWaku<LightNode>();
* const { node, isLoading, error } = useWaku();
* ...
* };
* @param {Object} props - options to create a node and other React props
* @param {CreateNodeOptions} props.options - optional options for creating Light Node
* @param {Protocols} props.protocols - optional protocols list to initiate node with
* @returns React Light Node provider component
*/
export const LightNodeProvider: React.FunctionComponent<
ProviderProps<CreateNodeOptions>
> = (props) => {
const result = useCreateLightNode({
options: props.options,
protocols: props.protocols
});
export const WakuProvider = (
props: ProviderProps<CreateNodeOptions>
): React.ReactElement => {
const result = useCreateLightNode(props.options);
return (
<WakuContext.Provider value={result}>{props.children}</WakuContext.Provider>

View File

@ -1,8 +1,2 @@
export { ContentPairProvider, useContentPair } from "./ContentPairProvider.js";
export { CreateNodeOptions } from "./types.js";
export { useCreateContentPair } from "./useCreateContentPair.js";
export { useCreateLightNode } from "./useCreateWaku.js";
export { useFilterMessages } from "./useFilterMessages.js";
export { useLightPush } from "./useLightPush.js";
export { useStoreMessages } from "./useStoreMessages.js";
export { LightNodeProvider, useWaku } from "./WakuProvider.js";
export type { CreateNodeOptions } from "./types.js";
export { WakuProvider, useWaku } from "./WakuProvider.js";

View File

@ -1,13 +1,8 @@
import type {
IDecodedMessage,
IDecoder,
IEncoder,
IWaku,
Protocols
} from "@waku/interfaces";
import type { IWaku } from "@waku/interfaces";
import type React from "react";
export type { CreateNodeOptions, AutoSharding } from "@waku/interfaces";
export type HookState = {
type HookState = {
isLoading: boolean;
error: undefined | string;
};
@ -16,16 +11,6 @@ export type CreateNodeResult<T extends IWaku> = HookState & {
node: undefined | T;
};
export type BootstrapNodeOptions<T = Record<string, never>> = {
options?: T;
protocols?: Protocols[];
};
export type ContentPair = {
encoder: IEncoder;
decoder: IDecoder<IDecodedMessage>;
};
export type ReactChildrenProps = {
children?: React.ReactNode;
};

View File

@ -1,59 +0,0 @@
import type {
AutoSharding,
IDecodedMessage,
IDecoder,
IEncoder
} from "@waku/interfaces";
import { createDecoder, createEncoder } from "@waku/sdk";
import { createRoutingInfo } from "@waku/utils";
import React from "react";
import type { ContentPair } from "./types.js";
const defaultNetworkConfig: AutoSharding = {
clusterId: 0,
numShardsInCluster: 8
};
/**
* Creates Encoder / Decoder pair for a given contentTopic.
* @param {string} contentTopic - topic to orient to
* @param {boolean} ephemeral - makes messages ephemeral, default to false
* @param {AutoSharding} networkConfig - optional network config, defaults to cluster 0 with 8 shards
* @returns {Object} Encoder / Decoder pair
*/
export const useCreateContentPair = (
contentTopic: string,
ephemeral = false,
networkConfig: AutoSharding = defaultNetworkConfig
): ContentPair => {
const routingInfo = React.useMemo(
() => createRoutingInfo(networkConfig, { contentTopic }),
[contentTopic, networkConfig.clusterId, networkConfig.numShardsInCluster]
);
const [encoder, setEncoder] = React.useState<IEncoder>(
createEncoder({ contentTopic, ephemeral, routingInfo })
);
const [decoder, setDecoder] = React.useState<IDecoder<IDecodedMessage>>(
createDecoder(contentTopic, routingInfo)
);
React.useEffect(() => {
const newRoutingInfo = createRoutingInfo(networkConfig, { contentTopic });
setEncoder(
createEncoder({ contentTopic, ephemeral, routingInfo: newRoutingInfo })
);
setDecoder(createDecoder(contentTopic, newRoutingInfo));
}, [
contentTopic,
ephemeral,
networkConfig.clusterId,
networkConfig.numShardsInCluster
]);
return {
encoder,
decoder
};
};

View File

@ -1,22 +1,19 @@
import type { CreateNodeOptions, IWaku, LightNode } from "@waku/interfaces";
import { createLightNode, waitForRemotePeer } from "@waku/sdk";
import { createLightNode } from "@waku/sdk";
import React from "react";
import type { BootstrapNodeOptions, CreateNodeResult } from "./types.js";
import type { CreateNodeResult } from "./types.js";
type NodeFactory<N, T = Record<string, never>> = (options?: T) => Promise<N>;
type CreateNodeParams<
N extends IWaku,
T = Record<string, never>
> = BootstrapNodeOptions<T> & {
type CreateNodeParams<N extends IWaku, T = Record<string, never>> = T & {
factory: NodeFactory<N, T>;
};
const useCreateNode = <N extends IWaku, T = Record<string, never>>(
params: CreateNodeParams<N, T>
): CreateNodeResult<N> => {
const { factory, options, protocols = [] } = params;
const { factory, ...options } = params;
const [node, setNode] = React.useState<N | undefined>(undefined);
const [isLoading, setLoading] = React.useState<boolean>(true);
@ -26,14 +23,14 @@ const useCreateNode = <N extends IWaku, T = Record<string, never>>(
let cancelled = false;
setLoading(true);
factory(options)
factory(options as T)
.then(async (node) => {
if (cancelled) {
return;
}
await node.start();
await waitForRemotePeer(node, protocols);
await node.waitForPeers();
setNode(node);
setLoading(false);
@ -56,7 +53,7 @@ const useCreateNode = <N extends IWaku, T = Record<string, never>>(
};
export const useCreateLightNode = (
params?: BootstrapNodeOptions<CreateNodeOptions>
params?: CreateNodeOptions
): CreateNodeResult<LightNode> => {
return useCreateNode<LightNode, CreateNodeOptions>({
...params,

View File

@ -1,89 +0,0 @@
import type {
IDecodedMessage,
IDecoder,
IFilter,
IWaku
} from "@waku/interfaces";
import { Logger } from "@waku/utils";
import React from "react";
import type { HookState } from "./types.js";
const log = new Logger("react:useFilterMessages");
type AbstractFilterNode = IWaku & {
filter: IFilter;
};
type UseFilterMessagesParams = {
node: undefined | AbstractFilterNode;
decoder: undefined | IDecoder<IDecodedMessage>;
};
type UseFilterMessagesResult = HookState & {
messages: IDecodedMessage[];
};
/**
* Returns messages from Filter subscription and keeps them up to date
* @example
* const { isLoading, error, message } = useFilterMessages({node, decoder});
* @param {Object} node - node that implements Filter, hook does nothing if undefined
* @param {Object} decoder - decoder to use for subscribing, hook does nothing if undefined
* @returns {Object} hook state (isLoading, error) and messages array
*/
export const useFilterMessages = (
params: UseFilterMessagesParams
): UseFilterMessagesResult => {
const { node, decoder } = params;
const [error, setError] = React.useState<undefined | string>(undefined);
const [isLoading, setLoading] = React.useState<boolean>(false);
const [messages, setMessage] = React.useState<IDecodedMessage[]>([]);
const pushMessage = React.useCallback(
(message: IDecodedMessage): void => {
if (!message) {
return;
}
setMessage((prev) => [...prev, message]);
},
[setMessage]
);
React.useEffect(() => {
if (!node || !decoder) {
return;
}
setLoading(true);
node.filter
.subscribe([decoder], pushMessage)
.then((success) => {
setLoading(false);
if (!success) {
setError("Failed to subscribe to filter");
}
})
.catch((err) => {
setLoading(false);
setError(
`Failed to subscribe to filter: ${err?.message || "no message"}`
);
});
return () => {
node.filter.unsubscribe([decoder]).catch((err) => {
log.error("Failed to unsubscribe:", err);
});
};
}, [node, decoder, pushMessage, setError, setLoading]);
return {
error,
messages,
isLoading
};
};

View File

@ -1,44 +0,0 @@
import type {
IEncoder,
ILightPush,
IMessage,
IWaku,
LightPushSDKResult
} from "@waku/interfaces";
import React from "react";
type AbstractLightPushNode = IWaku & {
lightPush: ILightPush;
};
type UseLightPushParams = {
encoder: undefined | IEncoder;
node: undefined | AbstractLightPushNode;
};
type PushFn = (message: IMessage) => Promise<LightPushSDKResult>;
type UseLightPushResult = {
push?: undefined | PushFn;
};
export const useLightPush = (
params: UseLightPushParams
): UseLightPushResult => {
const { node, encoder } = params;
const push = React.useCallback<PushFn>(
(message) => {
return node!.lightPush.send(encoder as IEncoder, message);
},
[node, encoder]
);
if (!node && !encoder) {
return {};
}
return {
push
};
};

View File

@ -1,101 +0,0 @@
import type {
IDecodedMessage,
IDecoder,
IStore,
IWaku,
QueryRequestParams
} from "@waku/interfaces";
import React from "react";
import type { HookState } from "./types.js";
type AbstractStoreNode = IWaku & {
store: IStore;
};
type UseStoreMessagesParams = {
node: undefined | AbstractStoreNode;
decoder: undefined | IDecoder<IDecodedMessage>;
options: QueryRequestParams;
};
type UseStoreMessagesResult = HookState & {
messages: IDecodedMessage[];
};
/**
* Hook for retrieving messages from Store protocol based on options
* @example
* const { isLoading, error, messages } = useStoreMessages({node, decoder, options});
* @param {Object} node - node that implement Store, hook does nothing if undefined
* @param {Object} decoder - decoder to use for getting messages, hook does nothing if undefined
* @param {QueryRequestParams} options - options to initiate query to get messages
* @returns {Object} hook state (isLoading, error) and messages array
*/
export const useStoreMessages = (
params: UseStoreMessagesParams
): UseStoreMessagesResult => {
const { node, decoder, options } = params;
const [error, setError] = React.useState<undefined | string>(undefined);
const [isLoading, setLoading] = React.useState<boolean>(false);
const [messages, setMessage] = React.useState<IDecodedMessage[]>([]);
const pushMessage = React.useCallback(
(newMessages: IDecodedMessage[]): void => {
if (!newMessages || !newMessages.length) {
return;
}
setMessage((prev) => [...prev, ...newMessages]);
},
[setMessage]
);
React.useEffect(() => {
if (!node || !decoder) {
return;
}
let cancelled = false;
setLoading(true);
Promise.resolve()
.then(async () => {
for await (const promises of node.store.queryGenerator(
[decoder],
options
)) {
if (cancelled) {
return;
}
const messagesRaw = await Promise.all(promises);
const filteredMessages = messagesRaw.filter(
(v): v is IDecodedMessage => !!v
);
pushMessage(filteredMessages);
}
setLoading(false);
})
.catch((err) => {
setLoading(false);
setError(
`Failed to query messages from store: ${err?.message || "no message"}`
);
});
return () => {
cancelled = true;
};
// TODO: missing dependency on options, it will prevent consecutive update if options change
}, [node, decoder, pushMessage, setError, setLoading]);
return {
error,
isLoading,
messages
};
};