From 30e6e894ed7659f6a591992d440cf580284b476d Mon Sep 17 00:00:00 2001 From: Shusui MOYATANI Date: Sun, 5 Mar 2023 09:05:21 +0900 Subject: [PATCH] update --- src/clients/useBatchedEvent.ts | 61 ++++++++--------- src/clients/useBatchedEvents.ts | 102 ++++++++++++++-------------- src/clients/useCachedEvents.ts | 12 ++-- src/clients/useDeprecatedReposts.ts | 1 + src/clients/useEvent.ts | 5 +- src/clients/useFollowings.ts | 6 +- src/clients/useProfile.ts | 4 +- src/clients/usePubkey.ts | 29 ++++++-- src/clients/useReactions.ts | 3 +- src/components/Hello.tsx | 0 src/pages/Home.tsx | 2 + src/utils/ensureNonNull.ts | 14 ++++ src/utils/sleep.ts | 7 ++ 13 files changed, 144 insertions(+), 102 deletions(-) create mode 100644 src/components/Hello.tsx create mode 100644 src/utils/ensureNonNull.ts create mode 100644 src/utils/sleep.ts diff --git a/src/clients/useBatchedEvent.ts b/src/clients/useBatchedEvent.ts index 7bf5555..2f8cfac 100644 --- a/src/clients/useBatchedEvent.ts +++ b/src/clients/useBatchedEvent.ts @@ -16,40 +16,37 @@ export type UseBatchedEventProps = { const useBatchedEvent = (propsProvider: () => UseBatchedEventProps) => { const props = createMemo(propsProvider); - return useBatch(() => { - return { - interval: props().interval, - executor: (tasks) => { - const { generateKey, mergeFilters, extractKey } = props(); - // TODO relayUrlsを考慮する - const [config] = useConfig(); + return useBatch(() => ({ + interval: props().interval, + executor: (tasks) => { + const { generateKey, mergeFilters, extractKey } = props(); + // TODO relayUrlsを考慮する + const [config] = useConfig(); - const keyTaskMap = new Map>( - tasks.map((task) => [generateKey(task.args), task]), - ); - const filters = mergeFilters(tasks.map((task) => task.args)); + const keyTaskMap = new Map>( + tasks.map((task) => [generateKey(task.args), task]), + ); + const filters = mergeFilters(tasks.map((task) => task.args)); - useSubscription(() => ({ - relayUrls: config().relayUrls, - filters, - continuous: false, - onEvent: (event: NostrEvent) => { - const key = extractKey(event); - if (key == null) return; - const task = keyTaskMap.get(key); - // possibly, the new event received - if (task == null) return; - task.resolve(event); - }, - onEOSE: () => { - tasks.forEach((task) => { - task.reject(new Error('NotFound')); - }); - }, - })); - }, - }; - }); + useSubscription(() => ({ + relayUrls: config().relayUrls, + filters, + continuous: false, + onEvent: (event: NostrEvent) => { + const key = extractKey(event); + if (key == null) return; + const task = keyTaskMap.get(key); + if (task == null) return; + task.resolve(event); + }, + onEOSE: () => { + tasks.forEach((task) => { + task.reject(new Error(`NotFound: ${JSON.stringify(filters)}`)); + }); + }, + })); + }, + })); }; export default useBatchedEvent; diff --git a/src/clients/useBatchedEvents.ts b/src/clients/useBatchedEvents.ts index 07a6596..364b346 100644 --- a/src/clients/useBatchedEvents.ts +++ b/src/clients/useBatchedEvents.ts @@ -35,64 +35,62 @@ const addEvent = const useBatchedEvents = (propsProvider: () => UseBatchedEventsProps) => { const props = createMemo(propsProvider); - return useBatch>(() => { - return { - interval: props().interval, - executor: (tasks) => { - const { generateKey, mergeFilters, extractKey } = props(); - // TODO relayUrlsを考慮する - const [config] = useConfig(); + return useBatch>(() => ({ + interval: props().interval, + executor: (tasks) => { + const { generateKey, mergeFilters, extractKey } = props(); + // TODO relayUrlsを考慮する + const [config] = useConfig(); - const keyTaskMap = new Map>>( - tasks.map((task) => [generateKey(task.args), task]), - ); - const filters = mergeFilters(tasks.map((task) => task.args)); - const keyEventSignalsMap = new Map>(); + const keyTaskMap = new Map>>( + tasks.map((task) => [generateKey(task.args), task]), + ); + const filters = mergeFilters(tasks.map((task) => task.args)); + const keyEventSignalsMap = new Map>(); - const getSignalForKey = (key: string | number): Signal => { - const eventsSignal = - keyEventSignalsMap.get(key) ?? - createSignal({ - events: [], - completed: false, - }); - keyEventSignalsMap.set(key, eventsSignal); - return eventsSignal; - }; - const didReceivedEventsForKey = (key: string | number): boolean => - keyEventSignalsMap.has(key); + const getSignalForKey = (key: string | number): Signal => { + const eventsSignal = + keyEventSignalsMap.get(key) ?? + createSignal({ + events: [], + completed: false, + }); + keyEventSignalsMap.set(key, eventsSignal); + return eventsSignal; + }; + const didReceivedEventsForKey = (key: string | number): boolean => + keyEventSignalsMap.has(key); - useSubscription(() => ({ - relayUrls: config().relayUrls, - filters, - continuous: false, - onEvent: (event: NostrEvent) => { - const key = extractKey(event); - if (key == null) return; - const task = keyTaskMap.get(key); - if (task == null) return; + useSubscription(() => ({ + relayUrls: config().relayUrls, + filters, + continuous: false, + onEvent: (event: NostrEvent) => { + const key = extractKey(event); + if (key == null) return; + const task = keyTaskMap.get(key); + if (task == null) return; - const [events, setEvents] = getSignalForKey(key); + const [events, setEvents] = getSignalForKey(key); - setEvents(addEvent(event)); + setEvents(addEvent(event)); - task.resolve(events); - }, - onEOSE: () => { - tasks.forEach((task) => { - const key = generateKey(task.args); - if (didReceivedEventsForKey(key)) { - const [, setEvents] = getSignalForKey(key); - setEvents(completeBatchedEvents); - } else { - task.resolve(emptyBatchedEvents); - } - }); - }, - })); - }, - }; - }); + task.resolve(events); + }, + onEOSE: () => { + tasks.forEach((task) => { + const key = generateKey(task.args); + if (didReceivedEventsForKey(key)) { + const [, setEvents] = getSignalForKey(key); + setEvents(completeBatchedEvents); + } else { + task.resolve(emptyBatchedEvents); + } + }); + }, + })); + }, + })); }; export default useBatchedEvents; diff --git a/src/clients/useCachedEvents.ts b/src/clients/useCachedEvents.ts index 32bec2c..370cb04 100644 --- a/src/clients/useCachedEvents.ts +++ b/src/clients/useCachedEvents.ts @@ -49,20 +49,20 @@ const getEvents = async ({ * This is useful when you want to fetch some data which change occasionally: * profile or following list, reactions, and something like that. */ -const useCachedEvents = (propsProvider: () => UseSubscriptionProps) => { +const useCachedEvents = (propsProvider: () => UseSubscriptionProps | null) => { const pool = usePool(); return createQuery( () => { - const { relayUrls, filters, continuous, options } = propsProvider(); - return ['useCachedEvents', relayUrls, filters, continuous, options] as const; + const currentProps = propsProvider(); + return ['useCachedEvents', currentProps] as const; }, ({ queryKey, signal }) => { - const [, relayUrls, filters, continuous, options] = queryKey; - return getEvents({ pool: pool(), relayUrls, filters, options, continuous, signal }); + const [, currentProps] = queryKey; + if (currentProps == null) return []; + return getEvents({ pool: pool(), signal, ...currentProps }); }, { - // 5 minutes staleTime: 5 * 60 * 1000, cacheTime: 15 * 60 * 1000, }, diff --git a/src/clients/useDeprecatedReposts.ts b/src/clients/useDeprecatedReposts.ts index 4b81785..9b70e05 100644 --- a/src/clients/useDeprecatedReposts.ts +++ b/src/clients/useDeprecatedReposts.ts @@ -39,6 +39,7 @@ const useDeprecatedReposts = ( () => queryKey(), ({ queryKey: currentQueryKey, signal }) => { const [, currentProps] = currentQueryKey; + if (currentProps == null) return () => ({ events: [], completed: false }); return timeout( 15000, `useDeprecatedReposts: ${currentProps.eventId}`, diff --git a/src/clients/useEvent.ts b/src/clients/useEvent.ts index e324037..2b11b0d 100644 --- a/src/clients/useEvent.ts +++ b/src/clients/useEvent.ts @@ -13,7 +13,7 @@ export type UseEventProps = { export type UseEvent = { event: Accessor; - query: CreateQueryResult; + query: CreateQueryResult; }; const { exec } = useBatchedEvent(() => ({ @@ -25,12 +25,13 @@ const { exec } = useBatchedEvent(() => ({ extractKey: (event: NostrEvent) => event.id, })); -const useEvent = (propsProvider: () => UseEventProps): UseEvent => { +const useEvent = (propsProvider: () => UseEventProps | null): UseEvent => { const props = createMemo(propsProvider); const query = createQuery( () => ['useEvent', props()] as const, ({ queryKey, signal }) => { const [, currentProps] = queryKey; + if (currentProps == null) return undefined; return timeout(15000, `useEvent: ${currentProps.eventId}`)(exec(currentProps, signal)); }, { diff --git a/src/clients/useFollowings.ts b/src/clients/useFollowings.ts index 45cda79..e16cd91 100644 --- a/src/clients/useFollowings.ts +++ b/src/clients/useFollowings.ts @@ -1,3 +1,4 @@ +import { createMemo } from 'solid-js'; import useCachedEvents from '@/clients/useCachedEvents'; type UseFollowingsProps = { @@ -12,8 +13,11 @@ type Following = { }; const useFollowings = (propsProvider: () => UseFollowingsProps) => { + const props = createMemo(propsProvider); const query = useCachedEvents(() => { - const { relayUrls, pubkey } = propsProvider(); + const currentProps = props(); + if (currentProps == null) return null; + const { relayUrls, pubkey } = currentProps; return { relayUrls, filters: [ diff --git a/src/clients/useProfile.ts b/src/clients/useProfile.ts index 4aed0c8..f698df9 100644 --- a/src/clients/useProfile.ts +++ b/src/clients/useProfile.ts @@ -43,12 +43,14 @@ const { exec } = useBatchedEvent(() => ({ extractKey: (event: NostrEvent): string => event.pubkey, })); -const useProfile = (propsProvider: () => UseProfileProps): UseProfile => { +const useProfile = (propsProvider: () => UseProfileProps | null): UseProfile => { const props = createMemo(propsProvider); + const query = createQuery( () => ['useProfile', props()] as const, ({ queryKey, signal }) => { const [, currentProps] = queryKey; + if (currentProps == null) return null; // TODO timeoutと同時にsignalでキャンセルするようにしたい return timeout(15000, `useProfile: ${currentProps.pubkey}`)(exec(currentProps, signal)); }, diff --git a/src/clients/usePubkey.ts b/src/clients/usePubkey.ts index b12aeae..965b5d4 100644 --- a/src/clients/usePubkey.ts +++ b/src/clients/usePubkey.ts @@ -6,13 +6,28 @@ const [pubkey, setPubkey] = createSignal(undefined); const usePubkey = (): Accessor => { onMount(() => { - if (window.nostr != null && pubkey() == null && !asking) { - asking = true; - window.nostr - .getPublicKey() - .then((key) => setPubkey(key)) - .catch((err) => console.error(`failed to obtain public key: ${err}`)); - } + let count = 0; + const intervalId = setInterval(() => { + if (count >= 5) { + clearInterval(intervalId); + if (pubkey() == null && !asking) { + if (window.nostr == null) { + throw new Error('Failed to obtain public key: Timeout. window.nostr is not defined.'); + } + throw new Error('Failed to obtain public key: Timeout'); + } + return; + } + + if (window.nostr != null && pubkey() == null && !asking) { + asking = true; + window.nostr + .getPublicKey() + .then((key) => setPubkey(key)) + .catch((err) => console.error(`failed to obtain public key: ${err}`)); + } + count += 1; + }, 1000); }); return pubkey; diff --git a/src/clients/useReactions.ts b/src/clients/useReactions.ts index 87ab369..76a5395 100644 --- a/src/clients/useReactions.ts +++ b/src/clients/useReactions.ts @@ -29,7 +29,7 @@ const { exec } = useBatchedEvents(() => ({ }, })); -const useReactions = (propsProvider: () => UseReactionsProps): UseReactions => { +const useReactions = (propsProvider: () => UseReactionsProps | null): UseReactions => { const queryClient = useQueryClient(); const props = createMemo(propsProvider); const queryKey = createMemo(() => ['useReactions', props()] as const); @@ -38,6 +38,7 @@ const useReactions = (propsProvider: () => UseReactionsProps): UseReactions => { () => queryKey(), ({ queryKey: currentQueryKey, signal }) => { const [, currentProps] = currentQueryKey; + if (currentProps == null) return () => ({ events: [], completed: false }); return timeout(15000, `useReactions: ${currentProps.eventId}`)(exec(currentProps, signal)); }, { diff --git a/src/components/Hello.tsx b/src/components/Hello.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 2bf9939..e25e073 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -13,6 +13,7 @@ import useSubscription from '@/clients/useSubscription'; import useFollowings from '@/clients/useFollowings'; import usePubkey from '@/clients/usePubkey'; import useShortcutKeys from '@/hooks/useShortcutKeys'; +import ensureNonNull from '@/hooks/ensureNonNull'; useShortcutKeys({ onShortcut: (s) => console.log(s), @@ -106,6 +107,7 @@ const Home: Component = () => { }); }; + const japaneseRegex = /[あ-ん]/; return (
} /> diff --git a/src/utils/ensureNonNull.ts b/src/utils/ensureNonNull.ts new file mode 100644 index 0000000..865c028 --- /dev/null +++ b/src/utils/ensureNonNull.ts @@ -0,0 +1,14 @@ +export type TupleNonNull = { + [P in keyof T]: NonNullable; +}; + +const ensureNonNull = + (tuple: T) => + (f: (tupleNonNull: TupleNonNull) => R): R | null => { + if (tuple.some((e) => e == null)) { + return null; + } + return f(tuple as TupleNonNull); + }; + +export default ensureNonNull; diff --git a/src/utils/sleep.ts b/src/utils/sleep.ts new file mode 100644 index 0000000..7fadffb --- /dev/null +++ b/src/utils/sleep.ts @@ -0,0 +1,7 @@ +const sleep = (ms: number): Promise => { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}; + +export default sleep;