From 939084167aafbabe0aafcf35a4eb2af6415a6692 Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 16 Jun 2023 20:31:33 +0100 Subject: [PATCH] @snort/system-react package --- packages/app/src/Element/CashuNuts.tsx | 5 +- packages/app/src/Element/Mention.tsx | 5 +- packages/app/src/Element/Nip05.tsx | 5 +- packages/app/src/Element/Nip5Service.tsx | 4 +- packages/app/src/Element/NoteFooter.tsx | 4 +- packages/app/src/Element/Poll.tsx | 5 +- packages/app/src/Element/ProfileImage.tsx | 7 ++- packages/app/src/Element/ProfilePreview.tsx | 9 +-- packages/app/src/Element/SubDebug.tsx | 6 +- packages/app/src/Element/Username.tsx | 5 +- packages/app/src/Element/ZapButton.tsx | 5 +- packages/app/src/Feed/BadgesFeed.ts | 7 ++- packages/app/src/Feed/EventFeed.ts | 5 +- packages/app/src/Feed/FollowersFeed.ts | 6 +- packages/app/src/Feed/FollowsFeed.ts | 5 +- packages/app/src/Feed/LoginFeed.ts | 7 ++- packages/app/src/Feed/MuteList.ts | 9 ++- packages/app/src/Feed/RelaysFeed.tsx | 6 +- packages/app/src/Feed/RelaysFeedFollows.tsx | 5 +- packages/app/src/Feed/ThreadFeed.ts | 5 +- packages/app/src/Feed/TimelineFeed.ts | 11 ++-- packages/app/src/Feed/ZapsFeed.ts | 5 +- .../app/src/Hooks/useNotelistSubscription.ts | 11 +++- packages/app/src/Hooks/useSystemState.tsx | 10 ---- packages/app/src/Hooks/useUserProfile.ts | 21 ------- packages/app/src/Pages/Layout.tsx | 4 +- packages/app/src/Pages/MessagesPage.tsx | 5 +- packages/app/src/Pages/ProfilePage.tsx | 5 +- packages/app/src/Pages/ZapPool.tsx | 4 +- packages/app/src/Pages/new/GetVerified.tsx | 5 +- packages/app/src/Pages/new/ProfileSetup.tsx | 4 +- packages/app/src/Pages/settings/Profile.tsx | 4 +- packages/app/src/Tasks/TaskList.tsx | 10 ++-- packages/system-react/README.md | 56 +++++++++++++++++++ packages/system-react/example/example.tsx | 48 ++++++++++++++++ packages/system-react/package.json | 23 ++++++++ packages/system-react/src/index.ts | 3 + .../src}/useRequestBuilder.tsx | 17 +++--- packages/system-react/src/useSystemState.tsx | 10 ++++ packages/system-react/src/useUserProfile.ts | 23 ++++++++ packages/system-react/tsconfig.json | 19 +++++++ packages/system/package.json | 2 +- packages/system/src/NostrSystem.ts | 31 ++++++++-- packages/system/src/ProfileCache.ts | 4 ++ packages/system/src/SystemWorker.ts | 2 +- packages/system/src/index.ts | 2 +- yarn.lock | 2 +- 47 files changed, 334 insertions(+), 122 deletions(-) delete mode 100644 packages/app/src/Hooks/useSystemState.tsx delete mode 100644 packages/app/src/Hooks/useUserProfile.ts create mode 100644 packages/system-react/README.md create mode 100644 packages/system-react/example/example.tsx create mode 100644 packages/system-react/package.json create mode 100644 packages/system-react/src/index.ts rename packages/{app/src/Hooks => system-react/src}/useRequestBuilder.tsx (72%) create mode 100644 packages/system-react/src/useSystemState.tsx create mode 100644 packages/system-react/src/useUserProfile.ts create mode 100644 packages/system-react/tsconfig.json diff --git a/packages/app/src/Element/CashuNuts.tsx b/packages/app/src/Element/CashuNuts.tsx index bf1a2a9d..e2332810 100644 --- a/packages/app/src/Element/CashuNuts.tsx +++ b/packages/app/src/Element/CashuNuts.tsx @@ -2,7 +2,8 @@ import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import useLogin from "Hooks/useLogin"; -import { useUserProfile } from "Hooks/useUserProfile"; +import { useUserProfile } from "@snort/system-react"; +import { System } from "index"; interface Token { token: Array<{ @@ -16,7 +17,7 @@ interface Token { export default function CashuNuts({ token }: { token: string }) { const login = useLogin(); - const profile = useUserProfile(login.publicKey); + const profile = useUserProfile(System, login.publicKey); async function copyToken(e: React.MouseEvent, token: string) { e.stopPropagation(); diff --git a/packages/app/src/Element/Mention.tsx b/packages/app/src/Element/Mention.tsx index 9fe4723e..8e53f26d 100644 --- a/packages/app/src/Element/Mention.tsx +++ b/packages/app/src/Element/Mention.tsx @@ -2,12 +2,13 @@ import { useMemo } from "react"; import { Link } from "react-router-dom"; import { HexKey } from "@snort/system"; -import { useUserProfile } from "Hooks/useUserProfile"; +import { useUserProfile } from "@snort/system-react"; import { profileLink } from "SnortUtils"; import { getDisplayName } from "Element/ProfileImage"; +import { System } from "index"; export default function Mention({ pubkey, relays }: { pubkey: HexKey; relays?: Array | string }) { - const user = useUserProfile(pubkey); + const user = useUserProfile(System, pubkey); const name = useMemo(() => { return getDisplayName(user, pubkey); diff --git a/packages/app/src/Element/Nip05.tsx b/packages/app/src/Element/Nip05.tsx index 5e1de7ab..27df2988 100644 --- a/packages/app/src/Element/Nip05.tsx +++ b/packages/app/src/Element/Nip05.tsx @@ -2,10 +2,11 @@ import "./Nip05.css"; import { HexKey } from "@snort/system"; import Icon from "Icons/Icon"; -import { useUserProfile } from "Hooks/useUserProfile"; +import { useUserProfile } from "@snort/system-react"; +import { System } from "index"; export function useIsVerified(pubkey: HexKey, bypassCheck?: boolean) { - const profile = useUserProfile(pubkey); + const profile = useUserProfile(System, pubkey); return { isVerified: bypassCheck || profile?.isNostrAddressValid }; } diff --git a/packages/app/src/Element/Nip5Service.tsx b/packages/app/src/Element/Nip5Service.tsx index 3d2368a3..678f7007 100644 --- a/packages/app/src/Element/Nip5Service.tsx +++ b/packages/app/src/Element/Nip5Service.tsx @@ -17,7 +17,7 @@ import { import AsyncButton from "Element/AsyncButton"; import SendSats from "Element/SendSats"; import Copy from "Element/Copy"; -import { useUserProfile } from "Hooks/useUserProfile"; +import { useUserProfile } from "@snort/system-react"; import useEventPublisher from "Feed/EventPublisher"; import { debounce } from "SnortUtils"; import useLogin from "Hooks/useLogin"; @@ -44,7 +44,7 @@ export default function Nip5Service(props: Nip05ServiceProps) { const { helpText = true } = props; const { formatMessage } = useIntl(); const pubkey = useLogin().publicKey; - const user = useUserProfile(pubkey); + const user = useUserProfile(System, pubkey); const publisher = useEventPublisher(); const svc = useMemo(() => new ServiceProvider(props.service), [props.service]); const [serviceConfig, setServiceConfig] = useState(); diff --git a/packages/app/src/Element/NoteFooter.tsx b/packages/app/src/Element/NoteFooter.tsx index 959ec04b..3dc38f8b 100644 --- a/packages/app/src/Element/NoteFooter.tsx +++ b/packages/app/src/Element/NoteFooter.tsx @@ -5,6 +5,7 @@ import { Menu, MenuItem } from "@szhsin/react-menu"; import { useLongPress } from "use-long-press"; import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "@snort/system"; import { LNURL } from "@snort/shared"; +import { useUserProfile } from "@snort/system-react"; import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; @@ -17,7 +18,6 @@ import { ReBroadcaster } from "Element/ReBroadcaster"; import Reactions from "Element/Reactions"; import SendSats from "Element/SendSats"; import { ParsedZap, ZapsSummary } from "Element/Zap"; -import { useUserProfile } from "Hooks/useUserProfile"; import { RootState } from "State/Store"; import { setReplyTo, setShow, reset } from "State/NoteCreator"; import { @@ -73,7 +73,7 @@ export default function NoteFooter(props: NoteFooterProps) { const login = useLogin(); const { pinned, bookmarked, publicKey, preferences: prefs, relays } = login; const { mute, block } = useModeration(); - const author = useUserProfile(ev.pubkey); + const author = useUserProfile(System, ev.pubkey); const interactionCache = useInteractionCache(publicKey, ev.id); const publisher = useEventPublisher(); const showNoteCreatorModal = useSelector((s: RootState) => s.noteCreator.show); diff --git a/packages/app/src/Element/Poll.tsx b/packages/app/src/Element/Poll.tsx index 65cbf949..ac027950 100644 --- a/packages/app/src/Element/Poll.tsx +++ b/packages/app/src/Element/Poll.tsx @@ -2,17 +2,18 @@ import { TaggedRawEvent } from "@snort/system"; import { LNURL } from "@snort/shared"; import { useState } from "react"; import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; +import { useUserProfile } from "@snort/system-react"; import { ParsedZap } from "Element/Zap"; import Text from "Element/Text"; import useEventPublisher from "Feed/EventPublisher"; import { useWallet } from "Wallet"; -import { useUserProfile } from "Hooks/useUserProfile"; import { unwrap } from "SnortUtils"; import { formatShort } from "Number"; import Spinner from "Icons/Spinner"; import SendSats from "Element/SendSats"; import useLogin from "Hooks/useLogin"; +import { System } from "index"; interface PollProps { ev: TaggedRawEvent; @@ -24,7 +25,7 @@ export default function Poll(props: PollProps) { const publisher = useEventPublisher(); const { wallet } = useWallet(); const { preferences: prefs, publicKey: myPubKey, relays } = useLogin(); - const pollerProfile = useUserProfile(props.ev.pubkey); + const pollerProfile = useUserProfile(System, props.ev.pubkey); const [error, setError] = useState(""); const [invoice, setInvoice] = useState(""); const [voting, setVoting] = useState(); diff --git a/packages/app/src/Element/ProfileImage.tsx b/packages/app/src/Element/ProfileImage.tsx index 5745b9c7..22b8e133 100644 --- a/packages/app/src/Element/ProfileImage.tsx +++ b/packages/app/src/Element/ProfileImage.tsx @@ -1,13 +1,14 @@ import "./ProfileImage.css"; import React, { useMemo } from "react"; +import { Link } from "react-router-dom"; import { HexKey, NostrPrefix, MetadataCache } from "@snort/system"; +import { useUserProfile } from "@snort/system-react"; -import { useUserProfile } from "Hooks/useUserProfile"; import { hexToBech32, profileLink } from "SnortUtils"; import Avatar from "Element/Avatar"; import Nip05 from "Element/Nip05"; -import { Link } from "react-router-dom"; +import { System } from "index"; export interface ProfileImageProps { pubkey: HexKey; @@ -30,7 +31,7 @@ export default function ProfileImage({ verifyNip, overrideUsername, }: ProfileImageProps) { - const user = useUserProfile(pubkey); + const user = useUserProfile(System, pubkey); const nip05 = defaultNip ? defaultNip : user?.nip05; const name = useMemo(() => { diff --git a/packages/app/src/Element/ProfilePreview.tsx b/packages/app/src/Element/ProfilePreview.tsx index 6397e778..cd20293c 100644 --- a/packages/app/src/Element/ProfilePreview.tsx +++ b/packages/app/src/Element/ProfilePreview.tsx @@ -1,11 +1,12 @@ import "./ProfilePreview.css"; import { ReactNode } from "react"; +import { HexKey } from "@snort/system"; +import { useUserProfile } from "@snort/system-react"; +import { useInView } from "react-intersection-observer"; import ProfileImage from "Element/ProfileImage"; import FollowButton from "Element/FollowButton"; -import { useUserProfile } from "Hooks/useUserProfile"; -import { HexKey } from "@snort/system"; -import { useInView } from "react-intersection-observer"; +import { System } from "index"; export interface ProfilePreviewProps { pubkey: HexKey; @@ -18,7 +19,7 @@ export interface ProfilePreviewProps { export default function ProfilePreview(props: ProfilePreviewProps) { const pubkey = props.pubkey; const { ref, inView } = useInView({ triggerOnce: true }); - const user = useUserProfile(inView ? pubkey : undefined); + const user = useUserProfile(System, inView ? pubkey : undefined); const options = { about: true, ...props.options, diff --git a/packages/app/src/Element/SubDebug.tsx b/packages/app/src/Element/SubDebug.tsx index bf1707fe..f47477c6 100644 --- a/packages/app/src/Element/SubDebug.tsx +++ b/packages/app/src/Element/SubDebug.tsx @@ -1,11 +1,11 @@ import "./SubDebug.css"; import { useState } from "react"; +import { ReqFilter } from "@snort/system"; +import { useSystemState } from "@snort/system-react"; import useRelayState from "Feed/RelayState"; import Tabs, { Tab } from "Element/Tabs"; import { unwrap } from "SnortUtils"; -import useSystemState from "Hooks/useSystemState"; -import { ReqFilter } from "@snort/system"; import { useCopy } from "useCopy"; import { System } from "index"; @@ -15,7 +15,7 @@ function RelayInfo({ id }: { id: string }) { } function Queries() { - const qs = useSystemState(); + const qs = useSystemState(System); const { copy } = useCopy(); function countElements(filters: Array) { diff --git a/packages/app/src/Element/Username.tsx b/packages/app/src/Element/Username.tsx index 3235cd7d..1e22d02e 100644 --- a/packages/app/src/Element/Username.tsx +++ b/packages/app/src/Element/Username.tsx @@ -2,12 +2,13 @@ import { MouseEvent } from "react"; import { useNavigate, Link } from "react-router-dom"; import { HexKey } from "@snort/system"; +import { useUserProfile } from "@snort/system-react"; -import { useUserProfile } from "Hooks/useUserProfile"; import { profileLink } from "SnortUtils"; +import { System } from "index"; export default function Username({ pubkey, onLinkVisit }: { pubkey: HexKey; onLinkVisit(): void }) { - const user = useUserProfile(pubkey); + const user = useUserProfile(System, pubkey); const navigate = useNavigate(); function onClick(ev: MouseEvent) { diff --git a/packages/app/src/Element/ZapButton.tsx b/packages/app/src/Element/ZapButton.tsx index 1910fda1..291af207 100644 --- a/packages/app/src/Element/ZapButton.tsx +++ b/packages/app/src/Element/ZapButton.tsx @@ -1,13 +1,14 @@ import "./ZapButton.css"; import { useState } from "react"; import { HexKey } from "@snort/system"; +import { useUserProfile } from "@snort/system-react"; -import { useUserProfile } from "Hooks/useUserProfile"; import SendSats from "Element/SendSats"; import Icon from "Icons/Icon"; +import { System } from "index"; const ZapButton = ({ pubkey, lnurl, children }: { pubkey: HexKey; lnurl?: string; children?: React.ReactNode }) => { - const profile = useUserProfile(pubkey); + const profile = useUserProfile(System, pubkey); const [zap, setZap] = useState(false); const service = lnurl ?? (profile?.lud16 || profile?.lud06); if (!service) return null; diff --git a/packages/app/src/Feed/BadgesFeed.ts b/packages/app/src/Feed/BadgesFeed.ts index 8397f8ce..5cbcd393 100644 --- a/packages/app/src/Feed/BadgesFeed.ts +++ b/packages/app/src/Feed/BadgesFeed.ts @@ -1,8 +1,9 @@ import { useMemo } from "react"; import { EventKind, HexKey, Lists, RequestBuilder, FlatNoteStore, ReplaceableNoteStore } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; import { unwrap, findTag, chunks } from "SnortUtils"; -import useRequestBuilder from "Hooks/useRequestBuilder"; +import { System } from "index"; type BadgeAwards = { pubkeys: string[]; @@ -17,7 +18,7 @@ export default function useProfileBadges(pubkey?: HexKey) { return b; }, [pubkey]); - const profileBadges = useRequestBuilder(ReplaceableNoteStore, sub); + const profileBadges = useRequestBuilder(System, ReplaceableNoteStore, sub); const profile = useMemo(() => { if (profileBadges.data) { @@ -57,7 +58,7 @@ export default function useProfileBadges(pubkey?: HexKey) { return b; }, [profile, ds]); - const awards = useRequestBuilder(FlatNoteStore, awardsSub); + const awards = useRequestBuilder(System, FlatNoteStore, awardsSub); const result = useMemo(() => { if (awards.data) { diff --git a/packages/app/src/Feed/EventFeed.ts b/packages/app/src/Feed/EventFeed.ts index 77196c2d..2a9c908f 100644 --- a/packages/app/src/Feed/EventFeed.ts +++ b/packages/app/src/Feed/EventFeed.ts @@ -1,8 +1,9 @@ import { useMemo } from "react"; import { NostrPrefix, RequestBuilder, ReplaceableNoteStore, NostrLink } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import { unwrap } from "SnortUtils"; +import { System } from "index"; export default function useEventFeed(link: NostrLink) { const sub = useMemo(() => { @@ -24,5 +25,5 @@ export default function useEventFeed(link: NostrLink) { return b; }, [link]); - return useRequestBuilder(ReplaceableNoteStore, sub); + return useRequestBuilder(System, ReplaceableNoteStore, sub); } diff --git a/packages/app/src/Feed/FollowersFeed.ts b/packages/app/src/Feed/FollowersFeed.ts index dc2f79b8..03c5e092 100644 --- a/packages/app/src/Feed/FollowersFeed.ts +++ b/packages/app/src/Feed/FollowersFeed.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { HexKey, EventKind, PubkeyReplaceableNoteStore, RequestBuilder } from "@snort/system"; - -import useRequestBuilder from "Hooks/useRequestBuilder"; +import { useRequestBuilder } from "@snort/system-react"; +import { System } from "index"; export default function useFollowersFeed(pubkey?: HexKey) { const sub = useMemo(() => { @@ -11,7 +11,7 @@ export default function useFollowersFeed(pubkey?: HexKey) { return b; }, [pubkey]); - const followersFeed = useRequestBuilder(PubkeyReplaceableNoteStore, sub); + const followersFeed = useRequestBuilder(System, PubkeyReplaceableNoteStore, sub); const followers = useMemo(() => { const contactLists = followersFeed.data?.filter( diff --git a/packages/app/src/Feed/FollowsFeed.ts b/packages/app/src/Feed/FollowsFeed.ts index 5f7b260b..98078180 100644 --- a/packages/app/src/Feed/FollowsFeed.ts +++ b/packages/app/src/Feed/FollowsFeed.ts @@ -1,8 +1,9 @@ import { useMemo } from "react"; import { HexKey, TaggedRawEvent, EventKind, PubkeyReplaceableNoteStore, RequestBuilder } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; +import { System } from "index"; export default function useFollowsFeed(pubkey?: HexKey) { const { publicKey, follows } = useLogin(); @@ -15,7 +16,7 @@ export default function useFollowsFeed(pubkey?: HexKey) { return b; }, [isMe, pubkey]); - const contactFeed = useRequestBuilder(PubkeyReplaceableNoteStore, sub); + const contactFeed = useRequestBuilder(System, PubkeyReplaceableNoteStore, sub); return useMemo(() => { if (isMe) { return follows.item; diff --git a/packages/app/src/Feed/LoginFeed.ts b/packages/app/src/Feed/LoginFeed.ts index 9ac53b26..2f740192 100644 --- a/packages/app/src/Feed/LoginFeed.ts +++ b/packages/app/src/Feed/LoginFeed.ts @@ -1,13 +1,13 @@ import { useEffect, useMemo } from "react"; import { TaggedRawEvent, Lists, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system"; import debug from "debug"; +import { useRequestBuilder } from "@snort/system-react"; import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils"; import { makeNotification, sendNotification } from "Notifications"; import useEventPublisher from "Feed/EventPublisher"; import { getMutedKeys } from "Feed/MuteList"; import useModeration from "Hooks/useModeration"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import { DmCache } from "Cache"; import useLogin from "Hooks/useLogin"; import { addSubscription, setBlocked, setBookmarked, setFollows, setMuted, setPinned, setRelays, setTags } from "Login"; @@ -15,6 +15,7 @@ import { SnortPubKey } from "Const"; import { SubscriptionEvent } from "Subscription"; import useRelaysFeedFollows from "./RelaysFeedFollows"; import { UserRelays } from "Cache"; +import { System } from "index"; /** * Managed loading data for the current logged in user @@ -61,7 +62,7 @@ export default function useLoginFeed() { return b; }, [pubKey]); - const loginFeed = useRequestBuilder(FlatNoteStore, subLogin); + const loginFeed = useRequestBuilder(System, FlatNoteStore, subLogin); // update relays and follow lists useEffect(() => { @@ -153,7 +154,7 @@ export default function useLoginFeed() { } } - const listsFeed = useRequestBuilder(FlatNoteStore, subLists); + const listsFeed = useRequestBuilder(System, FlatNoteStore, subLists); useEffect(() => { if (listsFeed.data) { diff --git a/packages/app/src/Feed/MuteList.ts b/packages/app/src/Feed/MuteList.ts index 5e7c75d5..4dfc77be 100644 --- a/packages/app/src/Feed/MuteList.ts +++ b/packages/app/src/Feed/MuteList.ts @@ -7,10 +7,11 @@ import { ParameterizedReplaceableNoteStore, RequestBuilder, } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; import { getNewest } from "SnortUtils"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; +import { System } from "index"; export default function useMutedFeed(pubkey?: HexKey) { const { publicKey, muted } = useLogin(); @@ -23,7 +24,11 @@ export default function useMutedFeed(pubkey?: HexKey) { return b; }, [pubkey]); - const mutedFeed = useRequestBuilder(ParameterizedReplaceableNoteStore, sub); + const mutedFeed = useRequestBuilder( + System, + ParameterizedReplaceableNoteStore, + sub + ); const mutedList = useMemo(() => { if (pubkey && mutedFeed.data) { diff --git a/packages/app/src/Feed/RelaysFeed.tsx b/packages/app/src/Feed/RelaysFeed.tsx index e6fe274e..d710e62a 100644 --- a/packages/app/src/Feed/RelaysFeed.tsx +++ b/packages/app/src/Feed/RelaysFeed.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { HexKey, FullRelaySettings, EventKind, RequestBuilder, ReplaceableNoteStore } from "@snort/system"; - -import useRequestBuilder from "Hooks/useRequestBuilder"; +import { useRequestBuilder } from "@snort/system-react"; +import { System } from "index"; export default function useRelaysFeed(pubkey?: HexKey) { const sub = useMemo(() => { @@ -11,7 +11,7 @@ export default function useRelaysFeed(pubkey?: HexKey) { return b; }, [pubkey]); - const relays = useRequestBuilder(ReplaceableNoteStore, sub); + const relays = useRequestBuilder(System, ReplaceableNoteStore, sub); if (!relays.data?.content) { return [] as FullRelaySettings[]; diff --git a/packages/app/src/Feed/RelaysFeedFollows.tsx b/packages/app/src/Feed/RelaysFeedFollows.tsx index 248ed587..57dc8240 100644 --- a/packages/app/src/Feed/RelaysFeedFollows.tsx +++ b/packages/app/src/Feed/RelaysFeedFollows.tsx @@ -8,11 +8,12 @@ import { PubkeyReplaceableNoteStore, RequestBuilder, } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; import debug from "debug"; import { sanitizeRelayUrl } from "SnortUtils"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import { UserRelays } from "Cache"; +import { System } from "index"; interface RelayList { pubkey: string; @@ -79,7 +80,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array(PubkeyReplaceableNoteStore, sub); + const relays = useRequestBuilder(System, PubkeyReplaceableNoteStore, sub); const notesRelays = relays.data?.filter(a => a.kind === EventKind.Relays) ?? []; const notesContactLists = relays.data?.filter(a => a.kind === EventKind.ContactList) ?? []; return useMemo(() => { diff --git a/packages/app/src/Feed/ThreadFeed.ts b/packages/app/src/Feed/ThreadFeed.ts index 1b0af776..b8488dcf 100644 --- a/packages/app/src/Feed/ThreadFeed.ts +++ b/packages/app/src/Feed/ThreadFeed.ts @@ -1,9 +1,10 @@ import { useEffect, useMemo, useState } from "react"; import { u256, EventKind, NostrLink, FlatNoteStore, RequestBuilder } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; import { appendDedupe } from "SnortUtils"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; +import { System } from "index"; interface RelayTaggedEventId { id: u256; @@ -54,7 +55,7 @@ export default function useThreadFeed(link: NostrLink) { return sub; }, [trackingEvents, trackingATags, allEvents, pref]); - const store = useRequestBuilder(FlatNoteStore, sub); + const store = useRequestBuilder(System, FlatNoteStore, sub); useEffect(() => { setTrackingATags([]); diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index e9ceb7f3..f8a0a377 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -1,10 +1,11 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import { EventKind, u256, FlatNoteStore, RequestBuilder } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import useTimelineWindow from "Hooks/useTimelineWindow"; import useLogin from "Hooks/useLogin"; +import { System } from "index"; export interface TimelineFeedOptions { method: "TIME_RANGE" | "LIMIT_UNTIL"; @@ -108,7 +109,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel return rb?.builder ?? null; }, [until, since, options.method, pref, createBuilder]); - const main = useRequestBuilder(FlatNoteStore, sub); + const main = useRequestBuilder(System, FlatNoteStore, sub); const subRealtime = useMemo(() => { const rb = createBuilder(); @@ -122,7 +123,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel return rb?.builder ?? null; }, [pref.autoShowLatest, createBuilder]); - const latest = useRequestBuilder(FlatNoteStore, subRealtime); + const latest = useRequestBuilder(System, FlatNoteStore, subRealtime); useEffect(() => { // clear store if changing relays @@ -168,7 +169,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel return rb.numFilters > 0 ? rb : null; }, [main.data, pref, subject.type]); - const related = useRequestBuilder(FlatNoteStore, subNext); + const related = useRequestBuilder(System, FlatNoteStore, subNext); return { main: main.data, diff --git a/packages/app/src/Feed/ZapsFeed.ts b/packages/app/src/Feed/ZapsFeed.ts index 6fd52ba4..abe76ef8 100644 --- a/packages/app/src/Feed/ZapsFeed.ts +++ b/packages/app/src/Feed/ZapsFeed.ts @@ -1,8 +1,9 @@ import { useMemo } from "react"; import { HexKey, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; import { parseZap } from "Element/Zap"; -import useRequestBuilder from "Hooks/useRequestBuilder"; +import { System } from "index"; export default function useZapsFeed(pubkey?: HexKey) { const sub = useMemo(() => { @@ -12,7 +13,7 @@ export default function useZapsFeed(pubkey?: HexKey) { return b; }, [pubkey]); - const zapsFeed = useRequestBuilder(FlatNoteStore, sub); + const zapsFeed = useRequestBuilder(System, FlatNoteStore, sub); const zaps = useMemo(() => { if (zapsFeed.data) { diff --git a/packages/app/src/Hooks/useNotelistSubscription.ts b/packages/app/src/Hooks/useNotelistSubscription.ts index f9e1eee2..2916bcc6 100644 --- a/packages/app/src/Hooks/useNotelistSubscription.ts +++ b/packages/app/src/Hooks/useNotelistSubscription.ts @@ -7,9 +7,10 @@ import { ParameterizedReplaceableNoteStore, RequestBuilder, } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; -import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; +import { System } from "index"; export default function useNotelistSubscription(pubkey: HexKey | undefined, l: Lists, defaultIds: HexKey[]) { const { preferences, publicKey } = useLogin(); @@ -23,7 +24,11 @@ export default function useNotelistSubscription(pubkey: HexKey | undefined, l: L return rb; }, [pubkey]); - const listStore = useRequestBuilder(ParameterizedReplaceableNoteStore, sub); + const listStore = useRequestBuilder( + System, + ParameterizedReplaceableNoteStore, + sub + ); const etags = useMemo(() => { if (isMe) return defaultIds; // there should only be a single event here because we only load 1 pubkey @@ -45,7 +50,7 @@ export default function useNotelistSubscription(pubkey: HexKey | undefined, l: L return s; }, [etags, pubkey, preferences]); - const store = useRequestBuilder(FlatNoteStore, esub); + const store = useRequestBuilder(System, FlatNoteStore, esub); return store.data ?? []; } diff --git a/packages/app/src/Hooks/useSystemState.tsx b/packages/app/src/Hooks/useSystemState.tsx deleted file mode 100644 index 78932907..00000000 --- a/packages/app/src/Hooks/useSystemState.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useSyncExternalStore } from "react"; -import { SystemSnapshot } from "@snort/system"; -import { System } from "index"; - -export default function useSystemState() { - return useSyncExternalStore( - cb => System.hook(cb), - () => System.snapshot() - ); -} diff --git a/packages/app/src/Hooks/useUserProfile.ts b/packages/app/src/Hooks/useUserProfile.ts deleted file mode 100644 index e10a2840..00000000 --- a/packages/app/src/Hooks/useUserProfile.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useEffect, useSyncExternalStore } from "react"; - -import { HexKey, MetadataCache } from "@snort/system"; -import { ProfileLoader } from "index"; -import { UserCache } from "Cache"; - -export function useUserProfile(pubKey?: HexKey): MetadataCache | undefined { - const user = useSyncExternalStore( - h => UserCache.hook(h, pubKey), - () => UserCache.getFromCache(pubKey) - ); - - useEffect(() => { - if (pubKey) { - ProfileLoader.TrackMetadata(pubKey); - return () => ProfileLoader.UntrackMetadata(pubKey); - } - }, [pubKey]); - - return user; -} diff --git a/packages/app/src/Pages/Layout.tsx b/packages/app/src/Pages/Layout.tsx index 014ee7b9..7f267a27 100644 --- a/packages/app/src/Pages/Layout.tsx +++ b/packages/app/src/Pages/Layout.tsx @@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { FormattedMessage } from "react-intl"; +import { useUserProfile } from "@snort/system-react"; import messages from "./messages"; @@ -18,7 +19,6 @@ import { useDmCache } from "Hooks/useDmsCache"; import { mapPlanName } from "./subscribe"; import useLogin from "Hooks/useLogin"; import Avatar from "Element/Avatar"; -import { useUserProfile } from "Hooks/useUserProfile"; import { profileLink } from "SnortUtils"; import { getCurrentSubscription } from "Subscription"; import Toaster from "Toaster"; @@ -147,7 +147,7 @@ const AccountHeader = () => { const { isMuted } = useModeration(); const { publicKey, latestNotification, readNotifications } = useLogin(); const dms = useDmCache(); - const profile = useUserProfile(publicKey); + const profile = useUserProfile(System, publicKey); const hasNotifications = useMemo( () => latestNotification > readNotifications, diff --git a/packages/app/src/Pages/MessagesPage.tsx b/packages/app/src/Pages/MessagesPage.tsx index 3aa0e6e4..65a67072 100644 --- a/packages/app/src/Pages/MessagesPage.tsx +++ b/packages/app/src/Pages/MessagesPage.tsx @@ -2,6 +2,7 @@ import React, { useMemo, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useNavigate } from "react-router-dom"; import { HexKey, NostrEvent, NostrPrefix } from "@snort/system"; +import { useUserProfile } from "@snort/system-react"; import UnreadCount from "Element/UnreadCount"; import ProfileImage, { getDisplayName } from "Element/ProfileImage"; @@ -14,12 +15,12 @@ import usePageWidth from "Hooks/usePageWidth"; import NoteTime from "Element/NoteTime"; import DmWindow from "Element/DmWindow"; import Avatar from "Element/Avatar"; -import { useUserProfile } from "Hooks/useUserProfile"; import Icon from "Icons/Icon"; import Text from "Element/Text"; import "./MessagesPage.css"; import messages from "./messages"; +import { System } from "index"; const TwoCol = 768; const ThreeCol = 1500; @@ -126,7 +127,7 @@ export default function MessagesPage() { } function ProfileDmActions({ pubkey }: { pubkey: string }) { - const profile = useUserProfile(pubkey); + const profile = useUserProfile(System, pubkey); const { block, unblock, isBlocked } = useModeration(); const blocked = isBlocked(pubkey); diff --git a/packages/app/src/Pages/ProfilePage.tsx b/packages/app/src/Pages/ProfilePage.tsx index 401a690a..2f819094 100644 --- a/packages/app/src/Pages/ProfilePage.tsx +++ b/packages/app/src/Pages/ProfilePage.tsx @@ -4,6 +4,7 @@ import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; import { encodeTLV, EventKind, HexKey, NostrPrefix, tryParseNostrLink } from "@snort/system"; import { LNURL } from "@snort/shared"; +import { useUserProfile } from "@snort/system-react"; import { getReactions, unwrap } from "SnortUtils"; import { formatShort } from "Number"; @@ -19,7 +20,6 @@ import useBookmarkFeed from "Feed/BookmarkFeed"; import useFollowersFeed from "Feed/FollowersFeed"; import useFollowsFeed from "Feed/FollowsFeed"; import useProfileBadges from "Feed/BadgesFeed"; -import { useUserProfile } from "Hooks/useUserProfile"; import useModeration from "Hooks/useModeration"; import useZapsFeed from "Feed/ZapsFeed"; import { default as ZapElement } from "Element/Zap"; @@ -47,6 +47,7 @@ import { getNip05PubKey } from "Pages/LoginPage"; import useLogin from "Hooks/useLogin"; import messages from "./messages"; +import { System } from "index"; const NOTES = 0; const REACTIONS = 1; @@ -104,7 +105,7 @@ export default function ProfilePage() { const params = useParams(); const navigate = useNavigate(); const [id, setId] = useState(); - const user = useUserProfile(id); + const user = useUserProfile(System, id); const loginPubKey = useLogin().publicKey; const isMe = loginPubKey === id; const [showLnQr, setShowLnQr] = useState(false); diff --git a/packages/app/src/Pages/ZapPool.tsx b/packages/app/src/Pages/ZapPool.tsx index 44614edd..b600d1b8 100644 --- a/packages/app/src/Pages/ZapPool.tsx +++ b/packages/app/src/Pages/ZapPool.tsx @@ -2,6 +2,7 @@ import "./ZapPool.css"; import { useMemo, useSyncExternalStore } from "react"; import { FormattedMessage, FormattedNumber } from "react-intl"; +import { useUserProfile } from "@snort/system-react"; import { SnortPubKey } from "Const"; import ProfilePreview from "Element/ProfilePreview"; @@ -9,7 +10,6 @@ import useLogin from "Hooks/useLogin"; import { UploaderServices } from "Upload"; import { bech32ToHex, getRelayName, unwrap } from "SnortUtils"; import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController"; -import { useUserProfile } from "Hooks/useUserProfile"; import AsyncButton from "Element/AsyncButton"; import { useWallet } from "Wallet"; import { System } from "index"; @@ -35,7 +35,7 @@ const DataProviders = [ function ZapTarget({ target }: { target: ZapPoolRecipient }) { const login = useLogin(); - const profile = useUserProfile(target.pubkey); + const profile = useUserProfile(System, target.pubkey); const hasAddress = profile?.lud16 || profile?.lud06; const defaultZapMount = Math.ceil(login.preferences.defaultZapAmount * (target.split / 100)); return ( diff --git a/packages/app/src/Pages/new/GetVerified.tsx b/packages/app/src/Pages/new/GetVerified.tsx index 0a68f6ba..e05812da 100644 --- a/packages/app/src/Pages/new/GetVerified.tsx +++ b/packages/app/src/Pages/new/GetVerified.tsx @@ -1,20 +1,21 @@ import { useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; +import { useUserProfile } from "@snort/system-react"; import Logo from "Element/Logo"; import { Nip5Services } from "Pages/Verification"; import Nip5Service from "Element/Nip5Service"; import ProfileImage from "Element/ProfileImage"; -import { useUserProfile } from "Hooks/useUserProfile"; import useLogin from "Hooks/useLogin"; import messages from "./messages"; +import { System } from "index"; export default function GetVerified() { const navigate = useNavigate(); const { publicKey } = useLogin(); - const user = useUserProfile(publicKey); + const user = useUserProfile(System, publicKey); const [isVerified, setIsVerified] = useState(false); const name = user?.name || "nostrich"; const [nip05, setNip05] = useState(`${name}@snort.social`); diff --git a/packages/app/src/Pages/new/ProfileSetup.tsx b/packages/app/src/Pages/new/ProfileSetup.tsx index 8023d8b6..0440efd8 100644 --- a/packages/app/src/Pages/new/ProfileSetup.tsx +++ b/packages/app/src/Pages/new/ProfileSetup.tsx @@ -2,11 +2,11 @@ import { useEffect, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; import { mapEventToProfile } from "@snort/system"; +import { useUserProfile } from "@snort/system-react"; import Logo from "Element/Logo"; import useEventPublisher from "Feed/EventPublisher"; import useLogin from "Hooks/useLogin"; -import { useUserProfile } from "Hooks/useUserProfile"; import { UserCache } from "Cache"; import AvatarEditor from "Element/AvatarEditor"; import { DISCOVER } from "."; @@ -16,7 +16,7 @@ import messages from "./messages"; export default function ProfileSetup() { const login = useLogin(); - const myProfile = useUserProfile(login.publicKey); + const myProfile = useUserProfile(System, login.publicKey); const [username, setUsername] = useState(""); const [picture, setPicture] = useState(""); const { formatMessage } = useIntl(); diff --git a/packages/app/src/Pages/settings/Profile.tsx b/packages/app/src/Pages/settings/Profile.tsx index 145dd583..0fcb459c 100644 --- a/packages/app/src/Pages/settings/Profile.tsx +++ b/packages/app/src/Pages/settings/Profile.tsx @@ -4,10 +4,10 @@ import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; import { mapEventToProfile } from "@snort/system"; +import { useUserProfile } from "@snort/system-react"; import { System } from "index"; import useEventPublisher from "Feed/EventPublisher"; -import { useUserProfile } from "Hooks/useUserProfile"; import { openFile } from "SnortUtils"; import useFileUpload from "Upload"; import AsyncButton from "Element/AsyncButton"; @@ -26,7 +26,7 @@ export interface ProfileSettingsProps { export default function ProfileSettings(props: ProfileSettingsProps) { const navigate = useNavigate(); const { publicKey: id } = useLogin(); - const user = useUserProfile(id ?? ""); + const user = useUserProfile(System, id ?? ""); const publisher = useEventPublisher(); const uploader = useFileUpload(); diff --git a/packages/app/src/Tasks/TaskList.tsx b/packages/app/src/Tasks/TaskList.tsx index 18b08870..b4993679 100644 --- a/packages/app/src/Tasks/TaskList.tsx +++ b/packages/app/src/Tasks/TaskList.tsx @@ -1,17 +1,19 @@ -import useLogin from "Hooks/useLogin"; -import { useUserProfile } from "Hooks/useUserProfile"; -import Icon from "Icons/Icon"; import { useState } from "react"; +import { useUserProfile } from "@snort/system-react"; + +import useLogin from "Hooks/useLogin"; +import Icon from "Icons/Icon"; import { UITask } from "Tasks"; import { DonateTask } from "./DonateTask"; import { Nip5Task } from "./Nip5Task"; +import { System } from "index"; const AllTasks: Array = [new Nip5Task(), new DonateTask()]; AllTasks.forEach(a => a.load()); export const TaskList = () => { const publicKey = useLogin().publicKey; - const user = useUserProfile(publicKey); + const user = useUserProfile(System, publicKey); const [, setTick] = useState(0); function muteTask(t: UITask) { diff --git a/packages/system-react/README.md b/packages/system-react/README.md new file mode 100644 index 00000000..001b3af2 --- /dev/null +++ b/packages/system-react/README.md @@ -0,0 +1,56 @@ +## @snort/system-react + +React hooks for @snort/system + +Sample: +```js +import { useMemo } from "react" +import { useRequestBuilder, useUserProfile } from "@snort/system-react"; +import { FlatNoteStore, NostrSystem, RequestBuilder, TaggedRawEvent } from "@snort/system" + +// singleton nostr system class +const System = new NostrSystem({}); + +// some bootstrap relays +[ + "wss://relay.snort.social", + "wss://nos.lol" +].forEach(r => System.ConnectToRelay(r, { read: true, write: false })); + +export function Note({ ev }: { ev: TaggedRawEvent }) { + // get profile from cache or request a profile from relays + const profile = useUserProfile(System, ev.pubkey); + + return
+ Post by: {profile.name ?? profile.display_name} +

+ {ev.content} +

+
+} + +export function UserPosts(props: { pubkey: string }) { + const sub = useMemo(() => { + const rb = new RequestBuilder("get-posts"); + rb.withFilter() + .authors([props.pubkey]) + .kinds([1]) + .limit(10); + + return rb; + }, [props.pubkey]); + + const data = useRequestBuilder(System, FlatNoteStore, sub); + return ( + <> + {data.data.map(a => )} + + ) +} + +export function MyApp() { + return ( + + ) +} +``` \ No newline at end of file diff --git a/packages/system-react/example/example.tsx b/packages/system-react/example/example.tsx new file mode 100644 index 00000000..abbba635 --- /dev/null +++ b/packages/system-react/example/example.tsx @@ -0,0 +1,48 @@ +import { useMemo } from "react" +import { useRequestBuilder, useUserProfile } from "../src"; + +import { FlatNoteStore, NostrSystem, RequestBuilder, TaggedRawEvent } from "@snort/system" + +const System = new NostrSystem({}); + +// some bootstrap relays +[ + "wss://relay.snort.social", + "wss://nos.lol" +].forEach(r => System.ConnectToRelay(r, { read: true, write: false })); + +export function Note({ ev }: { ev: TaggedRawEvent }) { + const profile = useUserProfile(System, ev.pubkey); + + return
+ Post by: {profile.name ?? profile.display_name} +

+ {ev.content} +

+
+} + +export function UserPosts(props: { pubkey: string }) { + const sub = useMemo(() => { + const rb = new RequestBuilder("get-posts"); + rb.withFilter() + .authors([props.pubkey]) + .kinds([1]) + .limit(10); + + return rb; + }, [props.pubkey]); + + const data = useRequestBuilder(System, FlatNoteStore, sub); + return ( + <> + {data.data.map(a => )} + + ) +} + +export function MyApp() { + return ( + + ) +} \ No newline at end of file diff --git a/packages/system-react/package.json b/packages/system-react/package.json new file mode 100644 index 00000000..93e0a0c4 --- /dev/null +++ b/packages/system-react/package.json @@ -0,0 +1,23 @@ +{ + "name": "@snort/system-react", + "version": "1.0.0", + "description": "React hooks for @snort/system", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "repository": "https://git.v0l.io/Kieran/snort", + "author": "Kieran", + "license": "GPL-3.0-or-later", + "private": false, + "scripts": { + "build": "tsc" + }, + "files": [ + "src", + "dist" + ], + "dependencies": { + "@snort/system": "", + "@snort/shared": "", + "react": "^18.2.0" + } +} diff --git a/packages/system-react/src/index.ts b/packages/system-react/src/index.ts new file mode 100644 index 00000000..035aa6f2 --- /dev/null +++ b/packages/system-react/src/index.ts @@ -0,0 +1,3 @@ +export * from "./useRequestBuilder"; +export * from "./useSystemState"; +export * from "./useUserProfile"; \ No newline at end of file diff --git a/packages/app/src/Hooks/useRequestBuilder.tsx b/packages/system-react/src/useRequestBuilder.tsx similarity index 72% rename from packages/app/src/Hooks/useRequestBuilder.tsx rename to packages/system-react/src/useRequestBuilder.tsx index d982ed9c..a27dcf90 100644 --- a/packages/app/src/Hooks/useRequestBuilder.tsx +++ b/packages/system-react/src/useRequestBuilder.tsx @@ -1,15 +1,18 @@ import { useSyncExternalStore } from "react"; -import { RequestBuilder, EmptySnapshot, NoteStore, StoreSnapshot } from "@snort/system"; -import { unwrap } from "SnortUtils"; -import { System } from "index"; +import { RequestBuilder, EmptySnapshot, NoteStore, StoreSnapshot, SystemInterface } from "@snort/system"; +import { unwrap } from "@snort/shared"; +/** + * Send a query to the relays and wait for data + */ const useRequestBuilder = >( - type: { new (): TStore }, + system: SystemInterface, + type: { new(): TStore }, rb: RequestBuilder | null ) => { const subscribe = (onChanged: () => void) => { if (rb) { - const q = System.Query(type, rb); + const q = system.Query(type, rb); const release = q.feed.hook(onChanged); q.uncancel(); return () => { @@ -22,7 +25,7 @@ const useRequestBuilder = => { - const q = System.GetQuery(rb?.id ?? ""); + const q = system.GetQuery(rb?.id ?? ""); if (q) { return unwrap(q).feed?.snapshot as StoreSnapshot; } @@ -34,4 +37,4 @@ const useRequestBuilder = ) { + return useSyncExternalStore( + cb => system.hook(cb), + () => system.snapshot() + ); +} diff --git a/packages/system-react/src/useUserProfile.ts b/packages/system-react/src/useUserProfile.ts new file mode 100644 index 00000000..c8ba6b53 --- /dev/null +++ b/packages/system-react/src/useUserProfile.ts @@ -0,0 +1,23 @@ +import { useSyncExternalStore } from "react"; +import { HexKey, MetadataCache, NostrSystem } from "@snort/system"; + +/** + * Gets a profile from cache or requests it from the relays + */ +export function useUserProfile(system: NostrSystem, pubKey?: HexKey): MetadataCache | undefined { + return useSyncExternalStore( + h => { + if (pubKey) { + system.ProfileLoader.TrackMetadata(pubKey); + } + const release = system.ProfileLoader.Cache.hook(h, pubKey); + return () => { + release(); + if (pubKey) { + system.ProfileLoader.UntrackMetadata(pubKey); + } + } + }, + () => system.ProfileLoader.Cache.getFromCache(pubKey) + ); +} diff --git a/packages/system-react/tsconfig.json b/packages/system-react/tsconfig.json new file mode 100644 index 00000000..64e15d18 --- /dev/null +++ b/packages/system-react/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "target": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "noImplicitOverride": true, + "module": "CommonJS", + "jsx": "react-jsx", + "strict": true, + "declaration": true, + "declarationMap": true, + "inlineSourceMap": true, + "outDir": "dist", + "skipLibCheck": true + }, + "include": ["src/**/*.ts"], + "files": ["src/index.ts"] +} diff --git a/packages/system/package.json b/packages/system/package.json index 6e2803be..56d16768 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -1,6 +1,6 @@ { "name": "@snort/system", - "version": "1.0.4", + "version": "1.0.5", "description": "Snort nostr system package", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/system/src/NostrSystem.ts b/packages/system/src/NostrSystem.ts index c5a92657..c815a341 100644 --- a/packages/system/src/NostrSystem.ts +++ b/packages/system/src/NostrSystem.ts @@ -1,13 +1,13 @@ import debug from "debug"; -import { unwrap, sanitizeRelayUrl, ExternalStore } from "@snort/shared"; +import { unwrap, sanitizeRelayUrl, ExternalStore, FeedCache } from "@snort/shared"; import { NostrEvent, TaggedRawEvent } from "./Nostr"; import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; import { Query } from "./Query"; import { RelayCache } from "./GossipModel"; import { NoteStore } from "./NoteCollection"; import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder"; -import { SystemInterface, SystemSnapshot } from "."; +import { MetadataCache, ProfileLoaderService, SystemInterface, SystemSnapshot, UserProfileCache, UserRelaysCache } from "."; /** * Manages nostr content retrieval system @@ -35,13 +35,36 @@ export class NostrSystem extends ExternalStore implements System */ #relayCache: RelayCache; - constructor(props: { authHandler?: AuthHandler, relayCache: RelayCache }) { + /** + * Storage class for user profiles + */ + #profileCache: FeedCache; + + /** + * Profile loading service + */ + #profileLoader: ProfileLoaderService; + + constructor(props: { + authHandler?: AuthHandler, + relayCache?: RelayCache, + profileCache?: FeedCache + }) { super(); this.#handleAuth = props.authHandler; - this.#relayCache = props.relayCache; + this.#relayCache = props.relayCache ?? new UserRelaysCache(); + this.#profileCache = props.profileCache ?? new UserProfileCache(); + this.#profileLoader = new ProfileLoaderService(this, this.#profileCache); this.#cleanup(); } + /** + * Profile loader service allows you to request profiles + */ + get ProfileLoader() { + return this.#profileLoader; + } + get Sockets(): ConnectionStateSnapshot[] { return [...this.#sockets.values()].map(a => a.snapshot()); } diff --git a/packages/system/src/ProfileCache.ts b/packages/system/src/ProfileCache.ts index 740036bc..9fefe90b 100644 --- a/packages/system/src/ProfileCache.ts +++ b/packages/system/src/ProfileCache.ts @@ -22,6 +22,10 @@ export class ProfileLoaderService { this.#FetchMetadata(); } + get Cache() { + return this.#cache; + } + /** * Request profile metadata for a set of pubkeys */ diff --git a/packages/system/src/SystemWorker.ts b/packages/system/src/SystemWorker.ts index 431f594f..9615742e 100644 --- a/packages/system/src/SystemWorker.ts +++ b/packages/system/src/SystemWorker.ts @@ -27,7 +27,7 @@ export class SystemWorker extends ExternalStore implements Syste throw new Error("Method not implemented."); } - Query(type: new () => T, req: RequestBuilder | null): Query | undefined { + Query(type: new () => T, req: RequestBuilder | null): Query { throw new Error("Method not implemented."); } diff --git a/packages/system/src/index.ts b/packages/system/src/index.ts index 0afd5134..0157234d 100644 --- a/packages/system/src/index.ts +++ b/packages/system/src/index.ts @@ -33,7 +33,7 @@ export interface SystemInterface { HandleAuth?: AuthHandler; get Sockets(): Array; GetQuery(id: string): Query | undefined; - Query(type: { new(): T }, req: RequestBuilder | null): Query | undefined; + Query(type: { new(): T }, req: RequestBuilder | null): Query; ConnectToRelay(address: string, options: RelaySettings): Promise; DisconnectRelay(address: string): void; BroadcastEvent(ev: NostrEvent): void; diff --git a/yarn.lock b/yarn.lock index aae381b4..d05a5285 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8707,7 +8707,7 @@ react-twitter-embed@^4.0.4: react@^18.2.0: version "18.2.0" - resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0"