feat: show thread

This commit is contained in:
Shusui MOYATANI
2023-03-26 11:29:38 +09:00
parent 904c5a547c
commit 3abd0dd94e
12 changed files with 237 additions and 61 deletions

View File

@@ -1,5 +1,7 @@
import type { Component, JSX } from 'solid-js'; import { Show, type JSX, type Component } from 'solid-js';
import { useHandleCommand } from '@/hooks/useCommandBus'; import { useHandleCommand } from '@/hooks/useCommandBus';
import { ColumnContext, useColumnState } from '@/components/ColumnContext';
import ColumnContentDisplay from '@/components/ColumnContentDisplay';
export type ColumnProps = { export type ColumnProps = {
name: string; name: string;
@@ -12,6 +14,8 @@ export type ColumnProps = {
const Column: Component<ColumnProps> = (props) => { const Column: Component<ColumnProps> = (props) => {
let columnDivRef: HTMLDivElement | undefined; let columnDivRef: HTMLDivElement | undefined;
const columnState = useColumnState();
const width = () => props.width ?? 'medium'; const width = () => props.width ?? 'medium';
useHandleCommand(() => ({ useHandleCommand(() => ({
@@ -33,9 +37,10 @@ const Column: Component<ColumnProps> = (props) => {
})); }));
return ( return (
<ColumnContext.Provider value={columnState}>
<div <div
ref={columnDivRef} ref={columnDivRef}
class="flex w-[80vw] shrink-0 snap-center snap-always flex-col border-r sm:snap-align-none" class="relative flex w-[80vw] shrink-0 snap-center snap-always flex-col border-r sm:snap-align-none"
classList={{ classList={{
'sm:w-[500px]': width() === 'widest', 'sm:w-[500px]': width() === 'widest',
'sm:w-[350px]': width() === 'wide', 'sm:w-[350px]': width() === 'wide',
@@ -48,7 +53,22 @@ const Column: Component<ColumnProps> = (props) => {
<span class="column-name">{props.name}</span> <span class="column-name">{props.name}</span>
</div> </div>
<ul class="flex flex-col overflow-y-scroll scroll-smooth">{props.children}</ul> <ul class="flex flex-col overflow-y-scroll scroll-smooth">{props.children}</ul>
<Show when={columnState.columnState.content} keyed>
{(columnContent) => (
<div class="absolute h-full w-full bg-white">
<div class="flex h-8 shrink-0 items-center border-b bg-white px-2">
<button class="w-full text-left" onClick={() => columnState?.clearColumnContext()}>
</button>
</div> </div>
<ul class="flex h-full flex-col overflow-y-scroll scroll-smooth">
<ColumnContentDisplay columnContent={columnContent} />
</ul>
</div>
)}
</Show>
</div>
</ColumnContext.Provider>
); );
}; };

View File

@@ -0,0 +1,33 @@
import { Switch, Match, type Component } from 'solid-js';
import useConfig from '@/nostr/useConfig';
import { type ColumnContent } from '@/components/ColumnContext';
import Timeline from '@/components/Timeline';
import useSubscription from '@/nostr/useSubscription';
const RepliesDisplay: Component<{ eventId: string }> = (props) => {
const { config } = useConfig();
const { events } = useSubscription(() => ({
relayUrls: config().relayUrls,
filters: [
{ kinds: [1], ids: [props.eventId], limit: 25 },
{ kinds: [1], '#e': [props.eventId], limit: 25 },
],
}));
return <Timeline events={[...events()].reverse()} />;
};
const ColumnContentDisplay: Component<{ columnContent: ColumnContent }> = (props) => {
return (
<Switch>
<Match when={props.columnContent.type === 'Replies' && props.columnContent} keyed>
{(replies) => <RepliesDisplay eventId={replies.eventId} />}
</Match>
</Switch>
);
};
export default ColumnContentDisplay;

View File

@@ -0,0 +1,31 @@
import { createContext, useContext, type JSX } from 'solid-js';
import { createStore } from 'solid-js/store';
export type ColumnContent = {
type: 'Replies';
eventId: string;
};
export type ColumnState = {
content?: ColumnContent;
};
export type UseColumnState = {
columnState: ColumnState;
setColumnContent: (content: ColumnContent) => void;
clearColumnContext: () => void;
};
export const ColumnContext = createContext<UseColumnState>();
export const useColumnContext = () => useContext(ColumnContext);
export const useColumnState = (): UseColumnState => {
const [columnState, setColumnState] = createStore<ColumnState>({});
return {
columnState,
setColumnContent: (content: ColumnContent) => setColumnState('content', content),
clearColumnContext: () => setColumnState('content', undefined),
};
};

View File

@@ -186,7 +186,7 @@ const ConfigUI = (props: ConfigProps) => {
<div class="relative"> <div class="relative">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<h2 class="flex-1 text-center font-bold"></h2> <h2 class="flex-1 text-center font-bold"></h2>
<button class="absolute top-1 right-0 h-4 w-4" onClick={() => props.onClose?.()}> <button class="absolute top-1 right-0 z-0 h-4 w-4" onClick={() => props.onClose?.()}>
<XMark /> <XMark />
</button> </button>
</div> </div>

View File

@@ -17,7 +17,7 @@ const Modal: Component<ModalProps> = (props) => {
return ( return (
<div <div
ref={containerRef} ref={containerRef}
class="absolute top-0 left-0 flex h-screen w-screen cursor-default place-content-center place-items-center bg-black/30" class="absolute top-0 left-0 z-10 flex h-screen w-screen cursor-default place-content-center place-items-center bg-black/30"
onClick={handleClickContainer} onClick={handleClickContainer}
> >
{props.children} {props.children}

View File

@@ -29,11 +29,11 @@ export type ProfileDisplayProps = {
}; };
const FollowersCount: Component<{ pubkey: string }> = (props) => { const FollowersCount: Component<{ pubkey: string }> = (props) => {
const { followersPubkeys } = useFollowers(() => ({ const { count } = useFollowers(() => ({
pubkey: props.pubkey, pubkey: props.pubkey,
})); }));
return <span>{followersPubkeys().length}</span>; return <>{count()}</>;
}; };
const ProfileDisplay: Component<ProfileDisplayProps> = (props) => { const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
@@ -66,9 +66,11 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
); );
const following = () => myFollowingPubkeys().includes(props.pubkey); const following = () => myFollowingPubkeys().includes(props.pubkey);
const { followingPubkeys: userFollowingPubkeys } = useFollowings(() => ({ const { followingPubkeys: userFollowingPubkeys, query: userFollowingQuery } = useFollowings(
() => ({
pubkey: props.pubkey, pubkey: props.pubkey,
})); }),
);
const followed = () => { const followed = () => {
const p = pubkey(); const p = pubkey();
return p != null && userFollowingPubkeys().includes(p); return p != null && userFollowingPubkeys().includes(p);
@@ -86,6 +88,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
until: epoch(), until: epoch(),
}, },
], ],
continuous: false,
})); }));
return ( return (
@@ -210,14 +213,28 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
<div class="flex border-t px-4 py-2"> <div class="flex border-t px-4 py-2">
<div class="flex flex-1 flex-col items-start"> <div class="flex flex-1 flex-col items-start">
<div class="text-sm"></div> <div class="text-sm"></div>
<div class="text-xl">{userFollowingPubkeys().length}</div> <div class="text-xl">
<Show
when={userFollowingQuery.isFetched}
fallback={<span class="text-sm"></span>}
>
{userFollowingPubkeys().length}
</Show>
</div>
</div> </div>
<div class="flex flex-1 flex-col items-start"> <div class="flex flex-1 flex-col items-start">
<div class="text-sm"></div> <div class="text-sm"></div>
<div class="text-xl"> <div class="text-xl">
<Show <Show
when={showFollowers()} when={showFollowers()}
fallback={<button onClick={() => setShowFollowers(true)}></button>} fallback={
<button
class="text-sm hover:text-stone-800 hover:underline"
onClick={() => setShowFollowers(true)}
>
</button>
}
keyed keyed
> >
<FollowersCount pubkey={props.pubkey} /> <FollowersCount pubkey={props.pubkey} />

View File

@@ -8,11 +8,6 @@ import ArrowPathRoundedSquare from 'heroicons/24/outline/arrow-path-rounded-squa
import ChatBubbleLeft from 'heroicons/24/outline/chat-bubble-left.svg'; import ChatBubbleLeft from 'heroicons/24/outline/chat-bubble-left.svg';
import EllipsisHorizontal from 'heroicons/24/outline/ellipsis-horizontal.svg'; import EllipsisHorizontal from 'heroicons/24/outline/ellipsis-horizontal.svg';
import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionDisplay';
import ContentWarningDisplay from '@/components/textNote/ContentWarningDisplay';
import TextNoteContentDisplay from '@/components/textNote/TextNoteContentDisplay';
import NotePostForm from '@/components/NotePostForm';
import eventWrapper from '@/core/event'; import eventWrapper from '@/core/event';
import useProfile from '@/nostr/useProfile'; import useProfile from '@/nostr/useProfile';
@@ -23,12 +18,19 @@ import useReactions from '@/nostr/useReactions';
import useDeprecatedReposts from '@/nostr/useDeprecatedReposts'; import useDeprecatedReposts from '@/nostr/useDeprecatedReposts';
import useFormatDate from '@/hooks/useFormatDate'; import useFormatDate from '@/hooks/useFormatDate';
import useModalState from '@/hooks/useModalState';
import UserNameDisplay from '@/components/UserDisplayName';
import TextNoteDisplayById from '@/components/textNote/TextNoteDisplayById';
import { useColumnContext } from '@/components/ColumnContext';
import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionDisplay';
import ContentWarningDisplay from '@/components/textNote/ContentWarningDisplay';
import TextNoteContentDisplay from '@/components/textNote/TextNoteContentDisplay';
import NotePostForm from '@/components/NotePostForm';
import ensureNonNull from '@/utils/ensureNonNull'; import ensureNonNull from '@/utils/ensureNonNull';
import npubEncodeFallback from '@/utils/npubEncodeFallback'; import npubEncodeFallback from '@/utils/npubEncodeFallback';
import useModalState from '@/hooks/useModalState'; import useSubscription from '@/nostr/useSubscription';
import UserNameDisplay from '../UserDisplayName';
import TextNoteDisplayById from './TextNoteDisplayById';
export type TextNoteDisplayProps = { export type TextNoteDisplayProps = {
event: NostrEvent; event: NostrEvent;
@@ -43,6 +45,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
const formatDate = useFormatDate(); const formatDate = useFormatDate();
const pubkey = usePubkey(); const pubkey = usePubkey();
const { showProfile } = useModalState(); const { showProfile } = useModalState();
const columnContext = useColumnContext();
const [showReplyForm, setShowReplyForm] = createSignal(false); const [showReplyForm, setShowReplyForm] = createSignal(false);
const closeReplyForm = () => setShowReplyForm(false); const closeReplyForm = () => setShowReplyForm(false);
@@ -60,11 +63,11 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
})); }));
const { reactions, isReactedBy, invalidateReactions } = useReactions(() => ({ const { reactions, isReactedBy, invalidateReactions } = useReactions(() => ({
eventId: props.event.id, // TODO いつかなおす eventId: props.event.id,
})); }));
const { reposts, isRepostedBy, invalidateDeprecatedReposts } = useDeprecatedReposts(() => ({ const { reposts, isRepostedBy, invalidateDeprecatedReposts } = useDeprecatedReposts(() => ({
eventId: props.event.id, // TODO いつかなおす eventId: props.event.id,
})); }));
const commands = useCommands(); const commands = useCommands();
@@ -118,7 +121,9 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
const createdAt = () => formatDate(event().createdAtAsDate()); const createdAt = () => formatDate(event().createdAtAsDate());
const handleRepost: JSX.EventHandler<HTMLButtonElement, MouseEvent> = () => { const handleRepost: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (ev) => {
ev.stopPropagation();
if (isRepostedByMe()) { if (isRepostedByMe()) {
// TODO remove reaction // TODO remove reaction
return; return;
@@ -134,7 +139,9 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
}); });
}; };
const handleReaction: JSX.EventHandler<HTMLButtonElement, MouseEvent> = () => { const handleReaction: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (ev) => {
ev.stopPropagation();
if (isReactedByMe()) { if (isReactedByMe()) {
// TODO remove reaction // TODO remove reaction
return; return;
@@ -158,11 +165,22 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
}); });
return ( return (
<div class="nostr-textnote flex flex-col"> <div
class="nostr-textnote flex flex-col"
onClick={() => {
columnContext?.setColumnContent({
type: 'Replies',
eventId: event().rootEvent()?.id ?? props.event.id,
});
}}
>
<div class="flex w-full gap-1"> <div class="flex w-full gap-1">
<button <button
class="author-icon h-10 w-10 shrink-0 overflow-hidden" class="author-icon h-10 w-10 shrink-0 overflow-hidden"
onClick={() => showProfile(event().pubkey)} onClick={(ev) => {
ev.stopPropagation();
showProfile(event().pubkey);
}}
> >
<Show when={author()?.picture}> <Show when={author()?.picture}>
{/* TODO 画像は脆弱性回避のためにimgじゃない方法で読み込みたい */} {/* TODO 画像は脆弱性回避のためにimgじゃない方法で読み込みたい */}
@@ -173,7 +191,10 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
<div class="flex justify-between gap-1 text-xs"> <div class="flex justify-between gap-1 text-xs">
<button <button
class="author flex min-w-0 truncate hover:text-blue-500" class="author flex min-w-0 truncate hover:text-blue-500"
onClick={() => showProfile(event().pubkey)} onClick={() => {
ev.stopPropagation();
showProfile(event().pubkey);
}}
> >
{/* TODO link to author */} {/* TODO link to author */}
<Show when={(author()?.display_name?.length ?? 0) > 0}> <Show when={(author()?.display_name?.length ?? 0) > 0}>
@@ -211,7 +232,10 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
{(replyToPubkey: string) => ( {(replyToPubkey: string) => (
<button <button
class="pr-1 text-blue-500 hover:underline" class="pr-1 text-blue-500 hover:underline"
onClick={() => showProfile(replyToPubkey)} onClick={(ev) => {
ev.stopPropagation();
showProfile(replyToPubkey);
}}
> >
<GeneralUserMentionDisplay pubkey={replyToPubkey} /> <GeneralUserMentionDisplay pubkey={replyToPubkey} />
</button> </button>
@@ -229,7 +253,10 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
<Show when={overflow()}> <Show when={overflow()}>
<button <button
class="text-xs text-stone-600 hover:text-stone-800" class="text-xs text-stone-600 hover:text-stone-800"
onClick={() => setShowOverflow((current) => !current)} onClick={(ev) => {
ev.stopPropagation();
setShowOverflow((current) => !current);
}}
> >
<Show when={!showOverflow()} fallback="隠す"> <Show when={!showOverflow()} fallback="隠す">
@@ -240,7 +267,10 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
<div class="actions flex w-48 items-center justify-between gap-8 pt-1"> <div class="actions flex w-48 items-center justify-between gap-8 pt-1">
<button <button
class="h-4 w-4 shrink-0 text-zinc-400" class="h-4 w-4 shrink-0 text-zinc-400"
onClick={() => setShowReplyForm((current) => !current)} onClick={() => {
stopPropagation();
setShowReplyForm((current) => !current);
}}
> >
<ChatBubbleLeft /> <ChatBubbleLeft />
</button> </button>
@@ -285,7 +315,10 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
<div> <div>
<button <button
class="h-4 w-4 text-zinc-400" class="h-4 w-4 text-zinc-400"
onClick={() => setShowMenu((current) => !current)} onClick={(ev) => {
ev.stopPropagation();
setShowMenu((current) => !current);
}}
> >
<EllipsisHorizontal /> <EllipsisHorizontal />
</button> </button>

View File

@@ -8,7 +8,7 @@ export type Task<TaskArgs, TaskResult> = {
}; };
export type UseBatchProps<TaskArgs, TaskResult> = { export type UseBatchProps<TaskArgs, TaskResult> = {
executor: (task: Task<TaskArgs, TaskResult>[]) => void; executor: (tasks: Task<TaskArgs, TaskResult>[]) => void;
interval?: number; interval?: number;
batchSize?: number; batchSize?: number;
}; };

View File

@@ -9,11 +9,14 @@ import {
import { type Event as NostrEvent, type Filter, Kind } from 'nostr-tools'; import { type Event as NostrEvent, type Filter, Kind } from 'nostr-tools';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query'; import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import timeout from '@/utils/timeout';
import useBatch, { type Task } from '@/nostr/useBatch';
import eventWrapper from '@/core/event'; import eventWrapper from '@/core/event';
import useConfig from './useConfig';
import usePool from './usePool'; import useBatch, { type Task } from '@/nostr/useBatch';
import useStats from '@/nostr/useStats';
import useConfig from '@/nostr/useConfig';
import usePool from '@/nostr/usePool';
import timeout from '@/utils/timeout';
type TaskArg = type TaskArg =
| { type: 'Profile'; pubkey: string } | { type: 'Profile'; pubkey: string }
@@ -63,7 +66,7 @@ export type UseTextNoteProps = {
}; };
export type UseTextNote = { export type UseTextNote = {
event: Accessor<NostrEvent | null>; event: () => NostrEvent | null;
query: CreateQueryResult<NostrEvent | null>; query: CreateQueryResult<NostrEvent | null>;
}; };
@@ -73,8 +76,8 @@ export type UseReactionsProps = {
}; };
export type UseReactions = { export type UseReactions = {
reactions: Accessor<NostrEvent[]>; reactions: () => NostrEvent[];
reactionsGroupedByContent: Accessor<Map<string, NostrEvent[]>>; reactionsGroupedByContent: () => Map<string, NostrEvent[]>;
isReactedBy: (pubkey: string) => boolean; isReactedBy: (pubkey: string) => boolean;
invalidateReactions: () => Promise<void>; invalidateReactions: () => Promise<void>;
query: CreateQueryResult<NostrEvent[]>; query: CreateQueryResult<NostrEvent[]>;
@@ -86,7 +89,7 @@ export type UseDeprecatedRepostsProps = {
}; };
export type UseDeprecatedReposts = { export type UseDeprecatedReposts = {
reposts: Accessor<NostrEvent[]>; reposts: () => NostrEvent[];
isRepostedBy: (pubkey: string) => boolean; isRepostedBy: (pubkey: string) => boolean;
invalidateDeprecatedReposts: () => Promise<void>; invalidateDeprecatedReposts: () => Promise<void>;
query: CreateQueryResult<NostrEvent[]>; query: CreateQueryResult<NostrEvent[]>;
@@ -104,18 +107,22 @@ type Following = {
}; };
export type UseFollowings = { export type UseFollowings = {
followings: Accessor<Following[]>; followings: () => Following[];
followingPubkeys: Accessor<string[]>; followingPubkeys: () => string[];
query: CreateQueryResult<NostrEvent | null>; query: CreateQueryResult<NostrEvent | null>;
}; };
let count = 0; let count = 0;
setInterval(() => console.log('batchSub', count), 1000); const { setActiveBatchSubscriptions } = useStats();
setInterval(() => {
setActiveBatchSubscriptions(count);
}, 1000);
const { exec } = useBatch<TaskArg, TaskRes>(() => ({ const { exec } = useBatch<TaskArg, TaskRes>(() => ({
interval: 2000, interval: 2000,
batchSize: 100, batchSize: 150,
executor: (tasks) => { executor: (tasks) => {
const profileTasks = new Map<string, Task<TaskArg, TaskRes>[]>(); const profileTasks = new Map<string, Task<TaskArg, TaskRes>[]>();
const textNoteTasks = new Map<string, Task<TaskArg, TaskRes>[]>(); const textNoteTasks = new Map<string, Task<TaskArg, TaskRes>[]>();
@@ -201,7 +208,7 @@ const { exec } = useBatch<TaskArg, TaskRes>(() => ({
const { config } = useConfig(); const { config } = useConfig();
const pool = usePool(); const pool = usePool();
const sub = pool().sub(config().relayUrls, filters); const sub = pool().sub(config().relayUrls, filters, {});
count += 1; count += 1;
@@ -300,7 +307,6 @@ export const useProfile = (propsProvider: () => UseProfileProps | null): UseProf
export const useTextNote = (propsProvider: () => UseTextNoteProps | null): UseTextNote => { export const useTextNote = (propsProvider: () => UseTextNoteProps | null): UseTextNote => {
const props = createMemo(propsProvider); const props = createMemo(propsProvider);
const queryClient = useQueryClient();
const query = createQuery( const query = createQuery(
() => ['useTextNote', props()] as const, () => ['useTextNote', props()] as const,
@@ -317,8 +323,11 @@ export const useTextNote = (propsProvider: () => UseTextNoteProps | null): UseTe
}, },
{ {
// Text notes never change, so they can be stored for a long time. // Text notes never change, so they can be stored for a long time.
// However, events tend to be unreferenced as time passes.
staleTime: 4 * 60 * 60 * 1000, // 4 hour staleTime: 4 * 60 * 60 * 1000, // 4 hour
cacheTime: 4 * 60 * 60 * 1000, // 4 hour cacheTime: 4 * 60 * 60 * 1000, // 4 hour
refetchOnWindowFocus: false,
refetchOnMount: false,
}, },
); );
@@ -449,6 +458,7 @@ export const useFollowings = (propsProvider: () => UseFollowingsProps | null): U
staleTime: 5 * 60 * 1000, // 5 min staleTime: 5 * 60 * 1000, // 5 min
cacheTime: 24 * 60 * 60 * 1000, // 24 hour cacheTime: 24 * 60 * 60 * 1000, // 24 hour
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
refetchInterval: 5 * 60 * 1000, // 5 min
}, },
); );

View File

@@ -22,5 +22,7 @@ export default function useFollowers(propsProvider: () => UseFollowersProps) {
const followersPubkeys = () => uniq(events()?.map((ev) => ev.pubkey)); const followersPubkeys = () => uniq(events()?.map((ev) => ev.pubkey));
return { followersPubkeys }; const count = () => followersPubkeys().length;
return { followersPubkeys, count };
} }

26
src/nostr/useStats.ts Normal file
View File

@@ -0,0 +1,26 @@
import { createStore } from 'solid-js/store';
export type Stats = {
activeSubscriptions: number;
activeBatchSubscriptions: number;
};
const [stats, setStats] = createStore<Stats>({
activeSubscriptions: 0,
activeBatchSubscriptions: 0,
});
const useStats = () => {
const setActiveSubscriptions = (count: number) => setStats('activeSubscriptions', count);
const setActiveBatchSubscriptions = (count: number) =>
setStats('activeBatchSubscriptions', count);
return {
stats,
setActiveSubscriptions,
setActiveBatchSubscriptions,
};
};
export default useStats;

View File

@@ -2,6 +2,7 @@ import { createSignal, createEffect, onCleanup } from 'solid-js';
import type { Event as NostrEvent, Filter, SubscriptionOptions } from 'nostr-tools'; import type { Event as NostrEvent, Filter, SubscriptionOptions } from 'nostr-tools';
import uniqBy from 'lodash/uniqBy'; import uniqBy from 'lodash/uniqBy';
import usePool from '@/nostr/usePool'; import usePool from '@/nostr/usePool';
import useStats from './useStats';
export type UseSubscriptionProps = { export type UseSubscriptionProps = {
relayUrls: string[]; relayUrls: string[];
@@ -27,7 +28,10 @@ const sortEvents = (events: NostrEvent[]) =>
let count = 0; let count = 0;
setInterval(() => console.log('sub', count), 1000); const { setActiveSubscriptions } = useStats();
setInterval(() => {
setActiveSubscriptions(count);
}, 1000);
const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => { const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => {
const pool = usePool(); const pool = usePool();