Files
rabbit/src/nostr/useReactions.ts
2023-12-21 00:49:59 +09:00

83 lines
2.5 KiB
TypeScript

import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools';
import useConfig from '@/core/useConfig';
import { reaction } from '@/nostr/event';
import { eventsQuery } from '@/nostr/query';
import { BatchedEventsTask } from '@/nostr/useBatchedEvents';
export type UseReactionsProps = {
eventId: string;
};
export type UseReactions = {
reactions: () => NostrEvent[];
reactionsGrouped: () => Map<string, NostrEvent[]>;
isReactedBy: (pubkey: string) => boolean;
isReactedByWithEmoji: (pubkey: string) => boolean;
query: CreateQueryResult<NostrEvent[]>;
};
const EmojiRegex = /\p{Emoji_Presentation}/u;
export const queryKeyUseReactions = (props: UseReactionsProps | null) =>
['useReactions', props] as const;
const useReactions = (propsProvider: () => UseReactionsProps | null): UseReactions => {
const { shouldMuteEvent } = useConfig();
const queryClient = useQueryClient();
const genQueryKey = createMemo(() => queryKeyUseReactions(propsProvider()));
const query = createQuery(() => ({
queryKey: genQueryKey(),
queryFn: eventsQuery<ReturnType<typeof queryKeyUseReactions>>({
taskProvider: ([, currentProps]) => {
if (currentProps == null) return null;
const { eventId: mentionedEventId } = currentProps;
return new BatchedEventsTask({ type: 'Reactions', mentionedEventId });
},
queryClient,
}),
staleTime: 1 * 60 * 1000, // 1 min
cacheTime: 4 * 60 * 60 * 1000, // 4 hour
refetchInterval: 1 * 60 * 1000, // 1 min
}));
const reactions = () => {
const data = query.data ?? [];
return data.filter((ev) => !shouldMuteEvent(ev));
};
const reactionsGrouped = () => {
const result = new Map<string, NostrEvent[]>();
reactions().forEach((event) => {
const key = reaction(event).isCustomEmoji()
? `${event.content}${reaction(event).getUrl() ?? ''}`
: event.content;
const events = result.get(key) ?? [];
events.push(event);
result.set(key, events);
});
return result;
};
const isReactedBy = (pubkey: string): boolean =>
reactions().findIndex((event) => event.pubkey === pubkey) !== -1;
const isReactedByWithEmoji = (pubkey: string): boolean =>
reactions().findIndex((event) => event.pubkey === pubkey && EmojiRegex.test(event.content)) !==
-1;
return {
reactions,
reactionsGrouped,
isReactedBy,
isReactedByWithEmoji,
query,
};
};
export default useReactions;