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 ZapReceiptsTask = { type: 'ZapReceipts'; mentionedEventId: string };
|
||||||
export type RepostsTask = { type: 'Reposts'; mentionedEventId: string };
|
export type RepostsTask = { type: 'Reposts'; mentionedEventId: string };
|
||||||
export type FollowingsTask = { type: 'Followings'; pubkey: string };
|
export type FollowingsTask = { type: 'Followings'; pubkey: string };
|
||||||
|
export type ReplaceableEventTask = {
|
||||||
|
type: 'ReplaceableEvent';
|
||||||
|
kind: number;
|
||||||
|
author: string;
|
||||||
|
};
|
||||||
export type ParameterizedReplaceableEventTask = {
|
export type ParameterizedReplaceableEventTask = {
|
||||||
type: 'ParameterizedReplaceableEvent';
|
type: 'ParameterizedReplaceableEvent';
|
||||||
kind: number;
|
kind: number;
|
||||||
@@ -33,6 +38,7 @@ export type TaskArgs = [
|
|||||||
ReactionsTask,
|
ReactionsTask,
|
||||||
RepostsTask,
|
RepostsTask,
|
||||||
ZapReceiptsTask,
|
ZapReceiptsTask,
|
||||||
|
ReplaceableEventTask,
|
||||||
ParameterizedReplaceableEventTask,
|
ParameterizedReplaceableEventTask,
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -69,6 +75,9 @@ setInterval(() => {
|
|||||||
setActiveBatchSubscriptions(count);
|
setActiveBatchSubscriptions(count);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
const keyForReplaceableEvent = ({ kind, author }: { kind: number; author: string }) =>
|
||||||
|
`${kind}:${author}`;
|
||||||
|
|
||||||
const keyForParameterizedReplaceableEvent = ({
|
const keyForParameterizedReplaceableEvent = ({
|
||||||
kind,
|
kind,
|
||||||
author,
|
author,
|
||||||
@@ -164,6 +173,20 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
|||||||
filtersBuilder: (ids) => [{ kinds: [Kind.Zap], '#e': ids }],
|
filtersBuilder: (ids) => [{ kinds: [Kind.Zap], '#e': ids }],
|
||||||
eventKeyExtractor: (ev) => genericEvent(ev).lastTaggedEventId(),
|
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>({
|
const parameterizedReplaceableEventsTasks = createTasks<ParameterizedReplaceableEventTask>({
|
||||||
keyExtractor: keyForParameterizedReplaceableEvent,
|
keyExtractor: keyForParameterizedReplaceableEvent,
|
||||||
filtersBuilder: (keys) => {
|
filtersBuilder: (keys) => {
|
||||||
@@ -200,6 +223,8 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
|||||||
reactionsTasks.add(task);
|
reactionsTasks.add(task);
|
||||||
} else if (isBatchedEventsTaskOf<ZapReceiptsTask>('ZapReceipts')(task)) {
|
} else if (isBatchedEventsTaskOf<ZapReceiptsTask>('ZapReceipts')(task)) {
|
||||||
zapReceiptsTasks.add(task);
|
zapReceiptsTasks.add(task);
|
||||||
|
} else if (isBatchedEventsTaskOf<ReplaceableEventTask>('ReplaceableEvent')(task)) {
|
||||||
|
replaceableEventsTasks.add(task);
|
||||||
} else if (
|
} else if (
|
||||||
isBatchedEventsTaskOf<ParameterizedReplaceableEventTask>('ParameterizedReplaceableEvent')(
|
isBatchedEventsTaskOf<ParameterizedReplaceableEventTask>('ParameterizedReplaceableEvent')(
|
||||||
task,
|
task,
|
||||||
@@ -218,6 +243,7 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
|||||||
...repostsTasks.buildFilter(),
|
...repostsTasks.buildFilter(),
|
||||||
...reactionsTasks.buildFilter(),
|
...reactionsTasks.buildFilter(),
|
||||||
...zapReceiptsTasks.buildFilter(),
|
...zapReceiptsTasks.buildFilter(),
|
||||||
|
...replaceableEventsTasks.buildFilter(),
|
||||||
...parameterizedReplaceableEventsTasks.buildFilter(),
|
...parameterizedReplaceableEventsTasks.buildFilter(),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -237,6 +263,9 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
|||||||
if (event.kind === Kind.Zap) {
|
if (event.kind === Kind.Zap) {
|
||||||
if (zapReceiptsTasks.resolve(event)) return;
|
if (zapReceiptsTasks.resolve(event)) return;
|
||||||
}
|
}
|
||||||
|
if (Kind.isReplaceableKind(event.kind)) {
|
||||||
|
if (replaceableEventsTasks.resolve(event)) return;
|
||||||
|
}
|
||||||
if (Kind.isParameterizedReplaceableKind(event.kind)) {
|
if (Kind.isParameterizedReplaceableKind(event.kind)) {
|
||||||
if (parameterizedReplaceableEventsTasks.resolve(event)) return;
|
if (parameterizedReplaceableEventsTasks.resolve(event)) return;
|
||||||
}
|
}
|
||||||
@@ -255,6 +284,7 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
|
|||||||
repostsTasks,
|
repostsTasks,
|
||||||
reactionsTasks,
|
reactionsTasks,
|
||||||
zapReceiptsTasks,
|
zapReceiptsTasks,
|
||||||
|
replaceableEventsTasks,
|
||||||
parameterizedReplaceableEventsTasks,
|
parameterizedReplaceableEventsTasks,
|
||||||
},
|
},
|
||||||
add,
|
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