2023-02-28 00:57:59 +01:00
|
|
|
import React from "react";
|
|
|
|
|
import type {
|
|
|
|
|
IDecodedMessage,
|
|
|
|
|
IDecoder,
|
2024-04-15 10:29:12 -07:00
|
|
|
IStoreSDK,
|
2024-09-19 01:47:57 +02:00
|
|
|
QueryRequestParams,
|
2023-02-28 00:57:59 +01:00
|
|
|
Waku,
|
|
|
|
|
} from "@waku/interfaces";
|
|
|
|
|
|
|
|
|
|
import type { HookState } from "./types";
|
|
|
|
|
|
|
|
|
|
type AbstractStoreNode = Waku & {
|
2024-04-15 10:29:12 -07:00
|
|
|
store: IStoreSDK;
|
2023-02-28 00:57:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type UseStoreMessagesParams = {
|
|
|
|
|
node: undefined | AbstractStoreNode;
|
|
|
|
|
decoder: undefined | IDecoder<IDecodedMessage>;
|
2024-09-19 01:47:57 +02:00
|
|
|
options: QueryRequestParams;
|
2023-02-28 00:57:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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
|
2024-09-19 01:47:57 +02:00
|
|
|
* @param {QueryRequestParams} options - options to initiate query to get messages
|
2023-02-28 00:57:59 +01:00
|
|
|
* @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,
|
|
|
|
|
};
|
|
|
|
|
};
|