From 3ce64a449d63108143dac334bf5294d0696ed25d Mon Sep 17 00:00:00 2001 From: Shusui MOYATANI Date: Thu, 2 Mar 2023 02:27:53 +0900 Subject: [PATCH] update --- src/clients/useBatchedEvent.ts | 45 ++++++++++++++++++ src/clients/useConfig.ts | 1 + src/clients/useEvent.ts | 43 ++++------------- src/clients/useProfile.ts | 59 +++++++----------------- src/components/DeprecatedRepost.tsx | 26 ++++++----- src/components/Notification.tsx | 5 ++ src/components/UserNameDisplay.tsx | 25 ++++++++++ src/components/notification/Reaction.tsx | 8 ++-- src/hooks/useDatePulser.ts | 1 + src/pages/Home.tsx | 49 ++++---------------- 10 files changed, 133 insertions(+), 129 deletions(-) create mode 100644 src/clients/useBatchedEvent.ts create mode 100644 src/components/UserNameDisplay.tsx diff --git a/src/clients/useBatchedEvent.ts b/src/clients/useBatchedEvent.ts new file mode 100644 index 0000000..fb574fb --- /dev/null +++ b/src/clients/useBatchedEvent.ts @@ -0,0 +1,45 @@ +import { type Event as NostrEvent } from 'nostr-tools/event'; +import { type Filter } from 'nostr-tools/filter'; + +import useConfig from '@/clients/useConfig'; +import useBatch, { type Task } from '@/clients/useBatch'; +import useSubscription from '@/clients/useSubscription'; + +export type UseBatchedEventProps = { + generateKey: (args: TaskArgs) => string | number; + mergeFilters: (args: TaskArgs[]) => Filter[]; + extractKey: (event: NostrEvent) => string | number | undefined; +}; + +const useBatchedEvent = (propsProvider: () => UseBatchedEventProps) => { + return useBatch(() => { + return { + executor: (tasks) => { + const { generateKey, mergeFilters, extractKey } = propsProvider(); + // TODO relayUrlsを考慮する + const [config] = useConfig(); + + 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); + }, + })); + }, + }; + }); +}; + +export default useBatchedEvent; diff --git a/src/clients/useConfig.ts b/src/clients/useConfig.ts index 67cd591..40f9aa8 100644 --- a/src/clients/useConfig.ts +++ b/src/clients/useConfig.ts @@ -17,6 +17,7 @@ const InitialConfig: Config = { 'wss://relay.snort.social', 'wss://relay.current.fyi', 'wss://relay.nostr.wirednet.jp', + 'wss://relay.mostr.pub', ], }; diff --git a/src/clients/useEvent.ts b/src/clients/useEvent.ts index 6b8ddff..7cb9ed1 100644 --- a/src/clients/useEvent.ts +++ b/src/clients/useEvent.ts @@ -2,11 +2,10 @@ import { createMemo, type Accessor } from 'solid-js'; import { type Event as NostrEvent } from 'nostr-tools/event'; import { createQuery, type CreateQueryResult } from '@tanstack/solid-query'; -import useConfig from '@/clients/useConfig'; -import useBatch, { type Task } from '@/clients/useBatch'; -import useSubscription from '@/clients/useSubscription'; +import useBatchedEvent from '@/clients/useBatchedEvent'; export type UseEventProps = { + // TODO リレーURLを考慮したい relayUrls: string[]; eventId: string; }; @@ -16,36 +15,14 @@ export type UseEvent = { query: CreateQueryResult; }; -const { exec } = useBatch(() => { - return { - executor: (tasks) => { - // TODO relayUrlsを考慮する - const [config] = useConfig(); - const eventIdTaskMap = new Map>( - tasks.map((task) => [task.args.eventId, task]), - ); - const eventIds = Array.from(eventIdTaskMap.keys()); - - useSubscription(() => ({ - relayUrls: config().relayUrls, - filters: [ - { - ids: eventIds, - kinds: [1], - }, - ], - continuous: false, - onEvent: (event: NostrEvent) => { - if (event.id == null) return; - const task = eventIdTaskMap.get(event.id); - // possibly, the new event received - if (task == null) return; - task.resolve(event); - }, - })); - }, - }; -}); +const { exec } = useBatchedEvent(() => ({ + generateKey: ({ eventId }: UseEventProps) => eventId, + mergeFilters: (args: UseEventProps[]) => { + const eventIds = args.map((arg) => arg.eventId); + return [{ kinds: [1], ids: eventIds }]; + }, + extractKey: (event: NostrEvent) => event.id, +})); const useEvent = (propsProvider: () => UseEventProps): UseEvent => { const props = createMemo(propsProvider); diff --git a/src/clients/useProfile.ts b/src/clients/useProfile.ts index 240565e..084b30b 100644 --- a/src/clients/useProfile.ts +++ b/src/clients/useProfile.ts @@ -1,14 +1,14 @@ import { createMemo, type Accessor } from 'solid-js'; import { type Event as NostrEvent } from 'nostr-tools/event'; +import { type Filter } from 'nostr-tools/filter'; import { createQuery, type CreateQueryResult } from '@tanstack/solid-query'; -import useConfig from '@/clients/useConfig'; -import useBatch, { type Task } from '@/clients/useBatch'; -import useSubscription from '@/clients/useSubscription'; +import useBatchedEvent from '@/clients/useBatchedEvent'; +import { Task } from './useBatch'; // TODO zodにする // deleted等の特殊なもの -type StandardProfile = { +export type StandardProfile = { name?: string; about?: string; picture?: string; @@ -17,14 +17,14 @@ type StandardProfile = { lud16?: string; // NIP-57 }; -type NonStandardProfile = { +export type NonStandardProfile = { display_name?: string; website?: string; }; -type Profile = StandardProfile & NonStandardProfile; +export type Profile = StandardProfile & NonStandardProfile; -type UseProfileProps = { +export type UseProfileProps = { relayUrls: string[]; pubkey: string; }; @@ -34,40 +34,17 @@ type UseProfile = { query: CreateQueryResult; }; -const { exec } = useBatch(() => { - return { - executor: (tasks) => { - // TODO relayUrlsを考慮する - const [config] = useConfig(); - const pubkeyTaskMap = new Map>( - tasks.map((task) => [task.args.pubkey, task]), - ); - const pubkeys = Array.from(pubkeyTaskMap.keys()); - - useSubscription(() => ({ - relayUrls: config().relayUrls, - filters: [ - { - kinds: [0], - authors: pubkeys, - }, - ], - continuous: false, - onEvent: (event: NostrEvent) => { - if (event.id == null) return; - const task = pubkeyTaskMap.get(event.pubkey); - // possibly, the new event received - if (task == null) return; - task.resolve(event); - }, - })); - }, - }; -}); +const { exec } = useBatchedEvent(() => ({ + generateKey: ({ pubkey }: UseProfileProps): string => pubkey, + mergeFilters: (args: UseProfileProps[]): Filter[] => { + const pubkeys = args.map((arg) => arg.pubkey); + return [{ kinds: [0], authors: pubkeys }]; + }, + extractKey: (event: NostrEvent): string => event.pubkey, +})); const useProfile = (propsProvider: () => UseProfileProps): UseProfile => { const props = createMemo(propsProvider); - const query = createQuery( () => ['useProfile', props()] as const, ({ queryKey, signal }) => { @@ -82,11 +59,9 @@ const useProfile = (propsProvider: () => UseProfileProps): UseProfile => { ); const profile = () => { - const maybeProfile = query.data; - if (maybeProfile == null) return undefined; - + if (query.data == null) return undefined; // TODO 大きすぎたりしないかどうか、JSONかどうかのチェック - return JSON.parse(maybeProfile.content) as Profile; + return JSON.parse(query.data.content) as Profile; }; return { profile, query }; diff --git a/src/components/DeprecatedRepost.tsx b/src/components/DeprecatedRepost.tsx index 487a848..c69d9c2 100644 --- a/src/components/DeprecatedRepost.tsx +++ b/src/components/DeprecatedRepost.tsx @@ -1,5 +1,5 @@ // NIP-18 (DEPRECATED) -import { Show, type Component } from 'solid-js'; +import { Show, Switch, Match, type Component } from 'solid-js'; import { Event as NostrEvent } from 'nostr-tools/event'; import ArrowPathRoundedSquare from 'heroicons/24/outline/arrow-path-rounded-square.svg'; @@ -7,6 +7,7 @@ import useConfig from '@/clients/useConfig'; import useEvent from '@/clients/useEvent'; import useProfile from '@/clients/useProfile'; +import UserNameDisplay from '@/components/UserNameDisplay'; import TextNote from '@/components/TextNote'; export type DeprecatedRepostProps = { @@ -30,19 +31,22 @@ const DeprecatedRepost: Component = (props) => { -
- 0} fallback={props.event.pubkey}> - {profile()?.display_name} - +
+ {' Reposted'}
- loading {eventId()}} - > - - + + + + + +
+ {'loading '} + {eventId()} +
+
+
); }; diff --git a/src/components/Notification.tsx b/src/components/Notification.tsx index c25b5b9..2d0301f 100644 --- a/src/components/Notification.tsx +++ b/src/components/Notification.tsx @@ -3,6 +3,7 @@ import { Kind, type Event as NostrEvent } from 'nostr-tools/event'; import TextNote from '@/components/TextNote'; import Reaction from '@/components/notification/Reaction'; +import DeprecatedRepost from '@/components/DeprecatedRepost'; export type TimelineProps = { events: NostrEvent[]; @@ -19,6 +20,10 @@ const Timeline: Component = (props) => { + {/* TODO ちゃんとnotification用のコンポーネント使う */} + + + )} diff --git a/src/components/UserNameDisplay.tsx b/src/components/UserNameDisplay.tsx new file mode 100644 index 0000000..2f5c73c --- /dev/null +++ b/src/components/UserNameDisplay.tsx @@ -0,0 +1,25 @@ +import { Component, Switch, Match } from 'solid-js'; + +import useConfig from '@/clients/useConfig'; +import useProfile, { type Profile } from '@/clients/useProfile'; + +type UserNameDisplayProps = { + pubkey: string; +}; + +const UserNameDisplay: Component = (props) => { + const [config] = useConfig(); + const { profile } = useProfile(() => ({ + relayUrls: config().relayUrls, + pubkey: props.pubkey, + })); + + return ( + + 0}>{profile()?.display_name} + 0}>@{profile()?.name} + + ); +}; + +export default UserNameDisplay; diff --git a/src/components/notification/Reaction.tsx b/src/components/notification/Reaction.tsx index 178a520..ef338d1 100644 --- a/src/components/notification/Reaction.tsx +++ b/src/components/notification/Reaction.tsx @@ -2,10 +2,12 @@ import { Switch, Match, type Component, Show } from 'solid-js'; import { type Event as NostrEvent } from 'nostr-tools/event'; import HeartSolid from 'heroicons/24/solid/heart.svg'; +import UserNameDisplay from '@/components/UserNameDisplay'; +import TextNote from '@/components/TextNote'; + import useConfig from '@/clients/useConfig'; import useProfile from '@/clients/useProfile'; import useEvent from '@/clients/useEvent'; -import TextNote from '../TextNote'; type ReactionProps = { event: NostrEvent; @@ -52,9 +54,7 @@ const Reaction: Component = (props) => {
- - {profile()?.display_name} - + {' reacted'}
diff --git a/src/hooks/useDatePulser.ts b/src/hooks/useDatePulser.ts index 683dce3..2cc2418 100644 --- a/src/hooks/useDatePulser.ts +++ b/src/hooks/useDatePulser.ts @@ -2,6 +2,7 @@ import { createSignal, type Accessor } from 'solid-js'; const [currentDate, setCurrentDate] = createSignal(new Date()); +// 7 seconds is used for the interval so that the last digit of relative time is changed. setInterval(() => { setCurrentDate(new Date()); }, 7000); diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index e297782..442a055 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -10,49 +10,21 @@ import TextNote from '@/components/TextNote'; import useCommands from '@/clients/useCommands'; import useConfig from '@/clients/useConfig'; import useSubscription from '@/clients/useSubscription'; -import useShortcutKeys from '@/hooks/useShortcutKeys'; import useFollowings from '@/clients/useFollowings'; - -/* -type UseRelayProps = { pubkey: string }; - - -const publish = async (pool, event) => { - const pub = pool.publish(writeRelays, event); - - return new Promise((resolve, reject) => {}); -}; -*/ -// const relays = ['ws://localhost:8008']; -// -const pubkey = 'npub1jcsr6e38dcepf65nkmrc54mu8jd8y70eael9rv308wxpwep6sxwqgsscyc'; -const pubkeyHex = '96203d66276e3214ea93b6c78a577c3c9a7279f9ee7e51b22f3b8c17643a819c'; +import usePubkey from '@/clients/usePubkey'; +import useShortcutKeys from '@/hooks/useShortcutKeys'; useShortcutKeys({ onShortcut: (s) => console.log(s), }); -const dummyTextNote = ( - -); - const Home: Component = () => { const [config] = useConfig(); + const pubkey = usePubkey(); const commands = useCommands(); const { followings } = useFollowings(() => ({ relayUrls: config().relayUrls, - pubkey: pubkeyHex, + pubkey: pubkey(), })); const { events: followingsPosts } = useSubscription(() => ({ @@ -60,7 +32,7 @@ const Home: Component = () => { filters: [ { kinds: [1, 6], - authors: followings()?.map((f) => f.pubkey) ?? [pubkeyHex], + authors: [...followings()?.map((f) => f.pubkey), pubkey()] ?? [pubkey()], limit: 25, since: Math.floor(Date.now() / 1000) - 12 * 60 * 60, }, @@ -72,7 +44,7 @@ const Home: Component = () => { filters: [ { kinds: [1, 6], - authors: [pubkeyHex], + authors: [pubkey()], limit: 25, }, ], @@ -83,7 +55,7 @@ const Home: Component = () => { filters: [ { kinds: [1, 6, 7], - '#p': [pubkeyHex], + '#p': [pubkey()], limit: 25, since: Math.floor(Date.now() / 1000) - 12 * 60 * 60, }, @@ -101,6 +73,7 @@ const Home: Component = () => { ], })); + /* const { events: searchPosts } = useSubscription(() => ({ relayUrls: ['wss://relay.nostr.band/'], filters: [ @@ -112,12 +85,13 @@ const Home: Component = () => { }, ], })); + */ const handlePost = ({ content }: { content: string }) => { commands .publishTextNote({ relayUrls: config().relayUrls, - pubkey: pubkeyHex, + pubkey: pubkey(), content, }) .then(() => { @@ -144,9 +118,6 @@ const Home: Component = () => { - - - );