mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 05:54:19 +01:00
feat: useReplaceableEvent
This commit is contained in:
@@ -19,6 +19,11 @@ export type ReactionsTask = { type: 'Reactions'; mentionedEventId: string };
|
||||
export type ZapReceiptsTask = { type: 'ZapReceipts'; mentionedEventId: string };
|
||||
export type RepostsTask = { type: 'Reposts'; mentionedEventId: string };
|
||||
export type FollowingsTask = { type: 'Followings'; pubkey: string };
|
||||
export type ReplaceableEventTask = {
|
||||
type: 'ReplaceableEvent';
|
||||
kind: number;
|
||||
author: string;
|
||||
};
|
||||
export type ParameterizedReplaceableEventTask = {
|
||||
type: 'ParameterizedReplaceableEvent';
|
||||
kind: number;
|
||||
@@ -33,6 +38,7 @@ export type TaskArgs = [
|
||||
ReactionsTask,
|
||||
RepostsTask,
|
||||
ZapReceiptsTask,
|
||||
ReplaceableEventTask,
|
||||
ParameterizedReplaceableEventTask,
|
||||
];
|
||||
|
||||
@@ -69,6 +75,9 @@ setInterval(() => {
|
||||
setActiveBatchSubscriptions(count);
|
||||
}, 1000);
|
||||
|
||||
const keyForReplaceableEvent = ({ kind, author }: { kind: number; author: string }) =>
|
||||
`${kind}:${author}`;
|
||||
|
||||
const keyForParameterizedReplaceableEvent = ({
|
||||
kind,
|
||||
author,
|
||||
@@ -164,6 +173,20 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
||||
filtersBuilder: (ids) => [{ kinds: [Kind.Zap], '#e': ids }],
|
||||
eventKeyExtractor: (ev) => genericEvent(ev).lastTaggedEventId(),
|
||||
});
|
||||
const replaceableEventsTasks = createTasks<ReplaceableEventTask>({
|
||||
keyExtractor: keyForReplaceableEvent,
|
||||
filtersBuilder: (keys) => {
|
||||
const result: Filter[] = [];
|
||||
keys.forEach((key) => {
|
||||
const task = replaceableEventsTasks.tasks.get(key)?.[0];
|
||||
if (task == null) return;
|
||||
const { kind, author } = task.req;
|
||||
result.push({ kinds: [kind], authors: [author] });
|
||||
});
|
||||
return result;
|
||||
},
|
||||
eventKeyExtractor: (ev) => keyForReplaceableEvent({ kind: ev.kind, author: ev.pubkey }),
|
||||
});
|
||||
const parameterizedReplaceableEventsTasks = createTasks<ParameterizedReplaceableEventTask>({
|
||||
keyExtractor: keyForParameterizedReplaceableEvent,
|
||||
filtersBuilder: (keys) => {
|
||||
@@ -200,6 +223,8 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
||||
reactionsTasks.add(task);
|
||||
} else if (isBatchedEventsTaskOf<ZapReceiptsTask>('ZapReceipts')(task)) {
|
||||
zapReceiptsTasks.add(task);
|
||||
} else if (isBatchedEventsTaskOf<ReplaceableEventTask>('ReplaceableEvent')(task)) {
|
||||
replaceableEventsTasks.add(task);
|
||||
} else if (
|
||||
isBatchedEventsTaskOf<ParameterizedReplaceableEventTask>('ParameterizedReplaceableEvent')(
|
||||
task,
|
||||
@@ -218,6 +243,7 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
||||
...repostsTasks.buildFilter(),
|
||||
...reactionsTasks.buildFilter(),
|
||||
...zapReceiptsTasks.buildFilter(),
|
||||
...replaceableEventsTasks.buildFilter(),
|
||||
...parameterizedReplaceableEventsTasks.buildFilter(),
|
||||
];
|
||||
|
||||
@@ -237,6 +263,9 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
||||
if (event.kind === Kind.Zap) {
|
||||
if (zapReceiptsTasks.resolve(event)) return;
|
||||
}
|
||||
if (Kind.isReplaceableKind(event.kind)) {
|
||||
if (replaceableEventsTasks.resolve(event)) return;
|
||||
}
|
||||
if (Kind.isParameterizedReplaceableKind(event.kind)) {
|
||||
if (parameterizedReplaceableEventsTasks.resolve(event)) return;
|
||||
}
|
||||
@@ -255,6 +284,7 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
||||
repostsTasks,
|
||||
reactionsTasks,
|
||||
zapReceiptsTasks,
|
||||
replaceableEventsTasks,
|
||||
parameterizedReplaceableEventsTasks,
|
||||
},
|
||||
add,
|
||||
|
||||
56
src/nostr/useReplaceableEvent.ts
Normal file
56
src/nostr/useReplaceableEvent.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { createMemo } from 'solid-js';
|
||||
|
||||
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
|
||||
import { type Event as NostrEvent } from 'nostr-tools/pure';
|
||||
|
||||
import { pickLatestEvent } from '@/nostr/event/comparator';
|
||||
import { registerTask, BatchedEventsTask, ReplaceableEventTask } from '@/nostr/useBatchedEvents';
|
||||
import timeout from '@/utils/timeout';
|
||||
|
||||
export type UseReplacableEventProps = {
|
||||
kind: number;
|
||||
author: string;
|
||||
};
|
||||
|
||||
export type UseReplaceableEvent = {
|
||||
event: () => NostrEvent | null;
|
||||
query: CreateQueryResult<NostrEvent | null>;
|
||||
};
|
||||
|
||||
const useReplaceableEvent = (
|
||||
propsProvider: () => UseReplacableEventProps | null,
|
||||
): UseReplaceableEvent => {
|
||||
const queryClient = useQueryClient();
|
||||
const props = createMemo(propsProvider);
|
||||
|
||||
const query = createQuery(() => ({
|
||||
queryKey: ['useReplaceableEvent', props()] as const,
|
||||
queryFn: ({ queryKey, signal }) => {
|
||||
const [, currentProps] = queryKey;
|
||||
if (currentProps == null) return null;
|
||||
const { kind, author } = currentProps;
|
||||
const task = new BatchedEventsTask<ReplaceableEventTask>({
|
||||
type: 'ReplaceableEvent',
|
||||
kind,
|
||||
author,
|
||||
});
|
||||
const promise = task.firstEventPromise().catch(() => {
|
||||
throw new Error(`event not found: ${kind}:${author}`);
|
||||
});
|
||||
task.onUpdate((events) => {
|
||||
const latest = pickLatestEvent(events);
|
||||
queryClient.setQueryData(queryKey, latest);
|
||||
});
|
||||
registerTask({ task, signal });
|
||||
return timeout(15000, `useReplaceableEvent: ${kind}:${author}`)(promise);
|
||||
},
|
||||
staleTime: 5 * 60 * 1000, // 5 min
|
||||
gcTime: 4 * 60 * 60 * 1000, // 4 hour
|
||||
}));
|
||||
|
||||
const event = () => query.data ?? null;
|
||||
|
||||
return { event, query };
|
||||
};
|
||||
|
||||
export default useReplaceableEvent;
|
||||
Reference in New Issue
Block a user