/
useFollowings.ts
95 lines (75 loc) · 2.83 KB
/
useFollowings.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools/pure';
import { genericEvent } from '@/nostr/event';
import { latestEventQuery } from '@/nostr/query';
import { BatchedEventsTask, FollowingsTask, registerTask } from '@/nostr/useBatchedEvents';
type Following = {
pubkey: string;
mainRelayUrl?: string;
petname?: string;
};
type UseFollowingsProps = {
pubkey: string;
};
export type UseFollowings = {
followings: () => Following[];
followingPubkeys: () => string[];
invalidateFollowings: () => Promise<void>;
query: CreateQueryResult<NostrEvent | null>;
};
const buildMethods = (dataProvider: () => NostrEvent | undefined | null) => {
const followings = () => {
const data = dataProvider();
if (data == null) return [];
const result: Following[] = [];
// TODO zodにする
const event = genericEvent(data);
event.pTags().forEach((tag) => {
const [, followingPubkey, mainRelayUrl, petname] = tag;
const following: Following = { pubkey: followingPubkey, petname };
if (mainRelayUrl != null && mainRelayUrl.length > 0) {
following.mainRelayUrl = mainRelayUrl;
}
result.push(following);
});
return result;
};
const followingPubkeys = (): string[] => followings().map((follow) => follow.pubkey);
return { followings, followingPubkeys, data: dataProvider };
};
export const fetchLatestFollowings = async (
{ pubkey }: UseFollowingsProps,
signal?: AbortSignal,
) => {
const task = new BatchedEventsTask<FollowingsTask>({ type: 'Followings', pubkey });
registerTask({ task, signal });
const latestFollowings = await task.latestEventPromise();
return buildMethods(() => latestFollowings);
};
const useFollowings = (propsProvider: () => UseFollowingsProps | null): UseFollowings => {
const queryClient = useQueryClient();
const props = createMemo(propsProvider);
const genQueryKey = () => ['useFollowings', props()] as const;
const query = createQuery(() => ({
queryKey: genQueryKey(),
queryFn: latestEventQuery<ReturnType<typeof genQueryKey>>({
taskProvider: ([, currentProps]) => {
if (currentProps == null) return null;
const { pubkey } = currentProps;
return new BatchedEventsTask<FollowingsTask>({ type: 'Followings', pubkey });
},
queryClient,
}),
staleTime: 5 * 60 * 1000, // 5 min
gcTime: 3 * 24 * 60 * 60 * 1000, // 3 days
refetchOnMount: true,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchInterval: 0,
}));
const invalidateFollowings = (): Promise<void> =>
queryClient.invalidateQueries({ queryKey: genQueryKey() });
return { ...buildMethods(() => query.data), invalidateFollowings, query };
};
export default useFollowings;