@snort/system-react package
continuous-integration/drone/push Build encountered an error Details

This commit is contained in:
Kieran 2023-06-16 20:31:33 +01:00
parent 28052e98b9
commit 939084167a
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
47 changed files with 334 additions and 122 deletions

View File

@ -2,7 +2,8 @@ import { useEffect, useState } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { useUserProfile } from "Hooks/useUserProfile"; import { useUserProfile } from "@snort/system-react";
import { System } from "index";
interface Token { interface Token {
token: Array<{ token: Array<{
@ -16,7 +17,7 @@ interface Token {
export default function CashuNuts({ token }: { token: string }) { export default function CashuNuts({ token }: { token: string }) {
const login = useLogin(); const login = useLogin();
const profile = useUserProfile(login.publicKey); const profile = useUserProfile(System, login.publicKey);
async function copyToken(e: React.MouseEvent<HTMLButtonElement>, token: string) { async function copyToken(e: React.MouseEvent<HTMLButtonElement>, token: string) {
e.stopPropagation(); e.stopPropagation();

View File

@ -2,12 +2,13 @@ import { useMemo } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { HexKey } from "@snort/system"; import { HexKey } from "@snort/system";
import { useUserProfile } from "Hooks/useUserProfile"; import { useUserProfile } from "@snort/system-react";
import { profileLink } from "SnortUtils"; import { profileLink } from "SnortUtils";
import { getDisplayName } from "Element/ProfileImage"; import { getDisplayName } from "Element/ProfileImage";
import { System } from "index";
export default function Mention({ pubkey, relays }: { pubkey: HexKey; relays?: Array<string> | string }) { export default function Mention({ pubkey, relays }: { pubkey: HexKey; relays?: Array<string> | string }) {
const user = useUserProfile(pubkey); const user = useUserProfile(System, pubkey);
const name = useMemo(() => { const name = useMemo(() => {
return getDisplayName(user, pubkey); return getDisplayName(user, pubkey);

View File

@ -2,10 +2,11 @@ import "./Nip05.css";
import { HexKey } from "@snort/system"; import { HexKey } from "@snort/system";
import Icon from "Icons/Icon"; 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) { export function useIsVerified(pubkey: HexKey, bypassCheck?: boolean) {
const profile = useUserProfile(pubkey); const profile = useUserProfile(System, pubkey);
return { isVerified: bypassCheck || profile?.isNostrAddressValid }; return { isVerified: bypassCheck || profile?.isNostrAddressValid };
} }

View File

@ -17,7 +17,7 @@ import {
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
import SendSats from "Element/SendSats"; import SendSats from "Element/SendSats";
import Copy from "Element/Copy"; import Copy from "Element/Copy";
import { useUserProfile } from "Hooks/useUserProfile"; import { useUserProfile } from "@snort/system-react";
import useEventPublisher from "Feed/EventPublisher"; import useEventPublisher from "Feed/EventPublisher";
import { debounce } from "SnortUtils"; import { debounce } from "SnortUtils";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
@ -44,7 +44,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
const { helpText = true } = props; const { helpText = true } = props;
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const pubkey = useLogin().publicKey; const pubkey = useLogin().publicKey;
const user = useUserProfile(pubkey); const user = useUserProfile(System, pubkey);
const publisher = useEventPublisher(); const publisher = useEventPublisher();
const svc = useMemo(() => new ServiceProvider(props.service), [props.service]); const svc = useMemo(() => new ServiceProvider(props.service), [props.service]);
const [serviceConfig, setServiceConfig] = useState<ServiceConfig>(); const [serviceConfig, setServiceConfig] = useState<ServiceConfig>();

View File

@ -5,6 +5,7 @@ import { Menu, MenuItem } from "@szhsin/react-menu";
import { useLongPress } from "use-long-press"; import { useLongPress } from "use-long-press";
import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "@snort/system"; import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "@snort/system";
import { LNURL } from "@snort/shared"; import { LNURL } from "@snort/shared";
import { useUserProfile } from "@snort/system-react";
import Icon from "Icons/Icon"; import Icon from "Icons/Icon";
import Spinner from "Icons/Spinner"; import Spinner from "Icons/Spinner";
@ -17,7 +18,6 @@ import { ReBroadcaster } from "Element/ReBroadcaster";
import Reactions from "Element/Reactions"; import Reactions from "Element/Reactions";
import SendSats from "Element/SendSats"; import SendSats from "Element/SendSats";
import { ParsedZap, ZapsSummary } from "Element/Zap"; import { ParsedZap, ZapsSummary } from "Element/Zap";
import { useUserProfile } from "Hooks/useUserProfile";
import { RootState } from "State/Store"; import { RootState } from "State/Store";
import { setReplyTo, setShow, reset } from "State/NoteCreator"; import { setReplyTo, setShow, reset } from "State/NoteCreator";
import { import {
@ -73,7 +73,7 @@ export default function NoteFooter(props: NoteFooterProps) {
const login = useLogin(); const login = useLogin();
const { pinned, bookmarked, publicKey, preferences: prefs, relays } = login; const { pinned, bookmarked, publicKey, preferences: prefs, relays } = login;
const { mute, block } = useModeration(); const { mute, block } = useModeration();
const author = useUserProfile(ev.pubkey); const author = useUserProfile(System, ev.pubkey);
const interactionCache = useInteractionCache(publicKey, ev.id); const interactionCache = useInteractionCache(publicKey, ev.id);
const publisher = useEventPublisher(); const publisher = useEventPublisher();
const showNoteCreatorModal = useSelector((s: RootState) => s.noteCreator.show); const showNoteCreatorModal = useSelector((s: RootState) => s.noteCreator.show);

View File

@ -2,17 +2,18 @@ import { TaggedRawEvent } from "@snort/system";
import { LNURL } from "@snort/shared"; import { LNURL } from "@snort/shared";
import { useState } from "react"; import { useState } from "react";
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
import { useUserProfile } from "@snort/system-react";
import { ParsedZap } from "Element/Zap"; import { ParsedZap } from "Element/Zap";
import Text from "Element/Text"; import Text from "Element/Text";
import useEventPublisher from "Feed/EventPublisher"; import useEventPublisher from "Feed/EventPublisher";
import { useWallet } from "Wallet"; import { useWallet } from "Wallet";
import { useUserProfile } from "Hooks/useUserProfile";
import { unwrap } from "SnortUtils"; import { unwrap } from "SnortUtils";
import { formatShort } from "Number"; import { formatShort } from "Number";
import Spinner from "Icons/Spinner"; import Spinner from "Icons/Spinner";
import SendSats from "Element/SendSats"; import SendSats from "Element/SendSats";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { System } from "index";
interface PollProps { interface PollProps {
ev: TaggedRawEvent; ev: TaggedRawEvent;
@ -24,7 +25,7 @@ export default function Poll(props: PollProps) {
const publisher = useEventPublisher(); const publisher = useEventPublisher();
const { wallet } = useWallet(); const { wallet } = useWallet();
const { preferences: prefs, publicKey: myPubKey, relays } = useLogin(); 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 [error, setError] = useState("");
const [invoice, setInvoice] = useState(""); const [invoice, setInvoice] = useState("");
const [voting, setVoting] = useState<number>(); const [voting, setVoting] = useState<number>();

View File

@ -1,13 +1,14 @@
import "./ProfileImage.css"; import "./ProfileImage.css";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { Link } from "react-router-dom";
import { HexKey, NostrPrefix, MetadataCache } from "@snort/system"; import { HexKey, NostrPrefix, MetadataCache } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { useUserProfile } from "Hooks/useUserProfile";
import { hexToBech32, profileLink } from "SnortUtils"; import { hexToBech32, profileLink } from "SnortUtils";
import Avatar from "Element/Avatar"; import Avatar from "Element/Avatar";
import Nip05 from "Element/Nip05"; import Nip05 from "Element/Nip05";
import { Link } from "react-router-dom"; import { System } from "index";
export interface ProfileImageProps { export interface ProfileImageProps {
pubkey: HexKey; pubkey: HexKey;
@ -30,7 +31,7 @@ export default function ProfileImage({
verifyNip, verifyNip,
overrideUsername, overrideUsername,
}: ProfileImageProps) { }: ProfileImageProps) {
const user = useUserProfile(pubkey); const user = useUserProfile(System, pubkey);
const nip05 = defaultNip ? defaultNip : user?.nip05; const nip05 = defaultNip ? defaultNip : user?.nip05;
const name = useMemo(() => { const name = useMemo(() => {

View File

@ -1,11 +1,12 @@
import "./ProfilePreview.css"; import "./ProfilePreview.css";
import { ReactNode } from "react"; 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 ProfileImage from "Element/ProfileImage";
import FollowButton from "Element/FollowButton"; import FollowButton from "Element/FollowButton";
import { useUserProfile } from "Hooks/useUserProfile"; import { System } from "index";
import { HexKey } from "@snort/system";
import { useInView } from "react-intersection-observer";
export interface ProfilePreviewProps { export interface ProfilePreviewProps {
pubkey: HexKey; pubkey: HexKey;
@ -18,7 +19,7 @@ export interface ProfilePreviewProps {
export default function ProfilePreview(props: ProfilePreviewProps) { export default function ProfilePreview(props: ProfilePreviewProps) {
const pubkey = props.pubkey; const pubkey = props.pubkey;
const { ref, inView } = useInView({ triggerOnce: true }); const { ref, inView } = useInView({ triggerOnce: true });
const user = useUserProfile(inView ? pubkey : undefined); const user = useUserProfile(System, inView ? pubkey : undefined);
const options = { const options = {
about: true, about: true,
...props.options, ...props.options,

View File

@ -1,11 +1,11 @@
import "./SubDebug.css"; import "./SubDebug.css";
import { useState } from "react"; import { useState } from "react";
import { ReqFilter } from "@snort/system";
import { useSystemState } from "@snort/system-react";
import useRelayState from "Feed/RelayState"; import useRelayState from "Feed/RelayState";
import Tabs, { Tab } from "Element/Tabs"; import Tabs, { Tab } from "Element/Tabs";
import { unwrap } from "SnortUtils"; import { unwrap } from "SnortUtils";
import useSystemState from "Hooks/useSystemState";
import { ReqFilter } from "@snort/system";
import { useCopy } from "useCopy"; import { useCopy } from "useCopy";
import { System } from "index"; import { System } from "index";
@ -15,7 +15,7 @@ function RelayInfo({ id }: { id: string }) {
} }
function Queries() { function Queries() {
const qs = useSystemState(); const qs = useSystemState(System);
const { copy } = useCopy(); const { copy } = useCopy();
function countElements(filters: Array<ReqFilter>) { function countElements(filters: Array<ReqFilter>) {

View File

@ -2,12 +2,13 @@ import { MouseEvent } from "react";
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
import { HexKey } from "@snort/system"; import { HexKey } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { useUserProfile } from "Hooks/useUserProfile";
import { profileLink } from "SnortUtils"; import { profileLink } from "SnortUtils";
import { System } from "index";
export default function Username({ pubkey, onLinkVisit }: { pubkey: HexKey; onLinkVisit(): void }) { export default function Username({ pubkey, onLinkVisit }: { pubkey: HexKey; onLinkVisit(): void }) {
const user = useUserProfile(pubkey); const user = useUserProfile(System, pubkey);
const navigate = useNavigate(); const navigate = useNavigate();
function onClick(ev: MouseEvent) { function onClick(ev: MouseEvent) {

View File

@ -1,13 +1,14 @@
import "./ZapButton.css"; import "./ZapButton.css";
import { useState } from "react"; import { useState } from "react";
import { HexKey } from "@snort/system"; import { HexKey } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { useUserProfile } from "Hooks/useUserProfile";
import SendSats from "Element/SendSats"; import SendSats from "Element/SendSats";
import Icon from "Icons/Icon"; import Icon from "Icons/Icon";
import { System } from "index";
const ZapButton = ({ pubkey, lnurl, children }: { pubkey: HexKey; lnurl?: string; children?: React.ReactNode }) => { 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 [zap, setZap] = useState(false);
const service = lnurl ?? (profile?.lud16 || profile?.lud06); const service = lnurl ?? (profile?.lud16 || profile?.lud06);
if (!service) return null; if (!service) return null;

View File

@ -1,8 +1,9 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { EventKind, HexKey, Lists, RequestBuilder, FlatNoteStore, ReplaceableNoteStore } from "@snort/system"; import { EventKind, HexKey, Lists, RequestBuilder, FlatNoteStore, ReplaceableNoteStore } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { unwrap, findTag, chunks } from "SnortUtils"; import { unwrap, findTag, chunks } from "SnortUtils";
import useRequestBuilder from "Hooks/useRequestBuilder"; import { System } from "index";
type BadgeAwards = { type BadgeAwards = {
pubkeys: string[]; pubkeys: string[];
@ -17,7 +18,7 @@ export default function useProfileBadges(pubkey?: HexKey) {
return b; return b;
}, [pubkey]); }, [pubkey]);
const profileBadges = useRequestBuilder<ReplaceableNoteStore>(ReplaceableNoteStore, sub); const profileBadges = useRequestBuilder<ReplaceableNoteStore>(System, ReplaceableNoteStore, sub);
const profile = useMemo(() => { const profile = useMemo(() => {
if (profileBadges.data) { if (profileBadges.data) {
@ -57,7 +58,7 @@ export default function useProfileBadges(pubkey?: HexKey) {
return b; return b;
}, [profile, ds]); }, [profile, ds]);
const awards = useRequestBuilder<FlatNoteStore>(FlatNoteStore, awardsSub); const awards = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, awardsSub);
const result = useMemo(() => { const result = useMemo(() => {
if (awards.data) { if (awards.data) {

View File

@ -1,8 +1,9 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { NostrPrefix, RequestBuilder, ReplaceableNoteStore, NostrLink } from "@snort/system"; import { NostrPrefix, RequestBuilder, ReplaceableNoteStore, NostrLink } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import useRequestBuilder from "Hooks/useRequestBuilder";
import { unwrap } from "SnortUtils"; import { unwrap } from "SnortUtils";
import { System } from "index";
export default function useEventFeed(link: NostrLink) { export default function useEventFeed(link: NostrLink) {
const sub = useMemo(() => { const sub = useMemo(() => {
@ -24,5 +25,5 @@ export default function useEventFeed(link: NostrLink) {
return b; return b;
}, [link]); }, [link]);
return useRequestBuilder<ReplaceableNoteStore>(ReplaceableNoteStore, sub); return useRequestBuilder<ReplaceableNoteStore>(System, ReplaceableNoteStore, sub);
} }

View File

@ -1,7 +1,7 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { HexKey, EventKind, PubkeyReplaceableNoteStore, RequestBuilder } from "@snort/system"; import { HexKey, EventKind, PubkeyReplaceableNoteStore, RequestBuilder } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import useRequestBuilder from "Hooks/useRequestBuilder"; import { System } from "index";
export default function useFollowersFeed(pubkey?: HexKey) { export default function useFollowersFeed(pubkey?: HexKey) {
const sub = useMemo(() => { const sub = useMemo(() => {
@ -11,7 +11,7 @@ export default function useFollowersFeed(pubkey?: HexKey) {
return b; return b;
}, [pubkey]); }, [pubkey]);
const followersFeed = useRequestBuilder<PubkeyReplaceableNoteStore>(PubkeyReplaceableNoteStore, sub); const followersFeed = useRequestBuilder<PubkeyReplaceableNoteStore>(System, PubkeyReplaceableNoteStore, sub);
const followers = useMemo(() => { const followers = useMemo(() => {
const contactLists = followersFeed.data?.filter( const contactLists = followersFeed.data?.filter(

View File

@ -1,8 +1,9 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { HexKey, TaggedRawEvent, EventKind, PubkeyReplaceableNoteStore, RequestBuilder } from "@snort/system"; 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 useLogin from "Hooks/useLogin";
import { System } from "index";
export default function useFollowsFeed(pubkey?: HexKey) { export default function useFollowsFeed(pubkey?: HexKey) {
const { publicKey, follows } = useLogin(); const { publicKey, follows } = useLogin();
@ -15,7 +16,7 @@ export default function useFollowsFeed(pubkey?: HexKey) {
return b; return b;
}, [isMe, pubkey]); }, [isMe, pubkey]);
const contactFeed = useRequestBuilder<PubkeyReplaceableNoteStore>(PubkeyReplaceableNoteStore, sub); const contactFeed = useRequestBuilder<PubkeyReplaceableNoteStore>(System, PubkeyReplaceableNoteStore, sub);
return useMemo(() => { return useMemo(() => {
if (isMe) { if (isMe) {
return follows.item; return follows.item;

View File

@ -1,13 +1,13 @@
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { TaggedRawEvent, Lists, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system"; import { TaggedRawEvent, Lists, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system";
import debug from "debug"; import debug from "debug";
import { useRequestBuilder } from "@snort/system-react";
import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils"; import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils";
import { makeNotification, sendNotification } from "Notifications"; import { makeNotification, sendNotification } from "Notifications";
import useEventPublisher from "Feed/EventPublisher"; import useEventPublisher from "Feed/EventPublisher";
import { getMutedKeys } from "Feed/MuteList"; import { getMutedKeys } from "Feed/MuteList";
import useModeration from "Hooks/useModeration"; import useModeration from "Hooks/useModeration";
import useRequestBuilder from "Hooks/useRequestBuilder";
import { DmCache } from "Cache"; import { DmCache } from "Cache";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { addSubscription, setBlocked, setBookmarked, setFollows, setMuted, setPinned, setRelays, setTags } from "Login"; import { addSubscription, setBlocked, setBookmarked, setFollows, setMuted, setPinned, setRelays, setTags } from "Login";
@ -15,6 +15,7 @@ import { SnortPubKey } from "Const";
import { SubscriptionEvent } from "Subscription"; import { SubscriptionEvent } from "Subscription";
import useRelaysFeedFollows from "./RelaysFeedFollows"; import useRelaysFeedFollows from "./RelaysFeedFollows";
import { UserRelays } from "Cache"; import { UserRelays } from "Cache";
import { System } from "index";
/** /**
* Managed loading data for the current logged in user * Managed loading data for the current logged in user
@ -61,7 +62,7 @@ export default function useLoginFeed() {
return b; return b;
}, [pubKey]); }, [pubKey]);
const loginFeed = useRequestBuilder<FlatNoteStore>(FlatNoteStore, subLogin); const loginFeed = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, subLogin);
// update relays and follow lists // update relays and follow lists
useEffect(() => { useEffect(() => {
@ -153,7 +154,7 @@ export default function useLoginFeed() {
} }
} }
const listsFeed = useRequestBuilder<FlatNoteStore>(FlatNoteStore, subLists); const listsFeed = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, subLists);
useEffect(() => { useEffect(() => {
if (listsFeed.data) { if (listsFeed.data) {

View File

@ -7,10 +7,11 @@ import {
ParameterizedReplaceableNoteStore, ParameterizedReplaceableNoteStore,
RequestBuilder, RequestBuilder,
} from "@snort/system"; } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { getNewest } from "SnortUtils"; import { getNewest } from "SnortUtils";
import useRequestBuilder from "Hooks/useRequestBuilder";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { System } from "index";
export default function useMutedFeed(pubkey?: HexKey) { export default function useMutedFeed(pubkey?: HexKey) {
const { publicKey, muted } = useLogin(); const { publicKey, muted } = useLogin();
@ -23,7 +24,11 @@ export default function useMutedFeed(pubkey?: HexKey) {
return b; return b;
}, [pubkey]); }, [pubkey]);
const mutedFeed = useRequestBuilder<ParameterizedReplaceableNoteStore>(ParameterizedReplaceableNoteStore, sub); const mutedFeed = useRequestBuilder<ParameterizedReplaceableNoteStore>(
System,
ParameterizedReplaceableNoteStore,
sub
);
const mutedList = useMemo(() => { const mutedList = useMemo(() => {
if (pubkey && mutedFeed.data) { if (pubkey && mutedFeed.data) {

View File

@ -1,7 +1,7 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { HexKey, FullRelaySettings, EventKind, RequestBuilder, ReplaceableNoteStore } from "@snort/system"; import { HexKey, FullRelaySettings, EventKind, RequestBuilder, ReplaceableNoteStore } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import useRequestBuilder from "Hooks/useRequestBuilder"; import { System } from "index";
export default function useRelaysFeed(pubkey?: HexKey) { export default function useRelaysFeed(pubkey?: HexKey) {
const sub = useMemo(() => { const sub = useMemo(() => {
@ -11,7 +11,7 @@ export default function useRelaysFeed(pubkey?: HexKey) {
return b; return b;
}, [pubkey]); }, [pubkey]);
const relays = useRequestBuilder<ReplaceableNoteStore>(ReplaceableNoteStore, sub); const relays = useRequestBuilder<ReplaceableNoteStore>(System, ReplaceableNoteStore, sub);
if (!relays.data?.content) { if (!relays.data?.content) {
return [] as FullRelaySettings[]; return [] as FullRelaySettings[];

View File

@ -8,11 +8,12 @@ import {
PubkeyReplaceableNoteStore, PubkeyReplaceableNoteStore,
RequestBuilder, RequestBuilder,
} from "@snort/system"; } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import debug from "debug"; import debug from "debug";
import { sanitizeRelayUrl } from "SnortUtils"; import { sanitizeRelayUrl } from "SnortUtils";
import useRequestBuilder from "Hooks/useRequestBuilder";
import { UserRelays } from "Cache"; import { UserRelays } from "Cache";
import { System } from "index";
interface RelayList { interface RelayList {
pubkey: string; pubkey: string;
@ -79,7 +80,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array<RelayList
}); });
} }
const relays = useRequestBuilder<PubkeyReplaceableNoteStore>(PubkeyReplaceableNoteStore, sub); const relays = useRequestBuilder<PubkeyReplaceableNoteStore>(System, PubkeyReplaceableNoteStore, sub);
const notesRelays = relays.data?.filter(a => a.kind === EventKind.Relays) ?? []; const notesRelays = relays.data?.filter(a => a.kind === EventKind.Relays) ?? [];
const notesContactLists = relays.data?.filter(a => a.kind === EventKind.ContactList) ?? []; const notesContactLists = relays.data?.filter(a => a.kind === EventKind.ContactList) ?? [];
return useMemo(() => { return useMemo(() => {

View File

@ -1,9 +1,10 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { u256, EventKind, NostrLink, FlatNoteStore, RequestBuilder } from "@snort/system"; import { u256, EventKind, NostrLink, FlatNoteStore, RequestBuilder } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { appendDedupe } from "SnortUtils"; import { appendDedupe } from "SnortUtils";
import useRequestBuilder from "Hooks/useRequestBuilder";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { System } from "index";
interface RelayTaggedEventId { interface RelayTaggedEventId {
id: u256; id: u256;
@ -54,7 +55,7 @@ export default function useThreadFeed(link: NostrLink) {
return sub; return sub;
}, [trackingEvents, trackingATags, allEvents, pref]); }, [trackingEvents, trackingATags, allEvents, pref]);
const store = useRequestBuilder<FlatNoteStore>(FlatNoteStore, sub); const store = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, sub);
useEffect(() => { useEffect(() => {
setTrackingATags([]); setTrackingATags([]);

View File

@ -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 { EventKind, u256, FlatNoteStore, RequestBuilder } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils"; import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils";
import useRequestBuilder from "Hooks/useRequestBuilder";
import useTimelineWindow from "Hooks/useTimelineWindow"; import useTimelineWindow from "Hooks/useTimelineWindow";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { System } from "index";
export interface TimelineFeedOptions { export interface TimelineFeedOptions {
method: "TIME_RANGE" | "LIMIT_UNTIL"; method: "TIME_RANGE" | "LIMIT_UNTIL";
@ -108,7 +109,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
return rb?.builder ?? null; return rb?.builder ?? null;
}, [until, since, options.method, pref, createBuilder]); }, [until, since, options.method, pref, createBuilder]);
const main = useRequestBuilder<FlatNoteStore>(FlatNoteStore, sub); const main = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, sub);
const subRealtime = useMemo(() => { const subRealtime = useMemo(() => {
const rb = createBuilder(); const rb = createBuilder();
@ -122,7 +123,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
return rb?.builder ?? null; return rb?.builder ?? null;
}, [pref.autoShowLatest, createBuilder]); }, [pref.autoShowLatest, createBuilder]);
const latest = useRequestBuilder<FlatNoteStore>(FlatNoteStore, subRealtime); const latest = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, subRealtime);
useEffect(() => { useEffect(() => {
// clear store if changing relays // clear store if changing relays
@ -168,7 +169,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
return rb.numFilters > 0 ? rb : null; return rb.numFilters > 0 ? rb : null;
}, [main.data, pref, subject.type]); }, [main.data, pref, subject.type]);
const related = useRequestBuilder<FlatNoteStore>(FlatNoteStore, subNext); const related = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, subNext);
return { return {
main: main.data, main: main.data,

View File

@ -1,8 +1,9 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { HexKey, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system"; import { HexKey, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { parseZap } from "Element/Zap"; import { parseZap } from "Element/Zap";
import useRequestBuilder from "Hooks/useRequestBuilder"; import { System } from "index";
export default function useZapsFeed(pubkey?: HexKey) { export default function useZapsFeed(pubkey?: HexKey) {
const sub = useMemo(() => { const sub = useMemo(() => {
@ -12,7 +13,7 @@ export default function useZapsFeed(pubkey?: HexKey) {
return b; return b;
}, [pubkey]); }, [pubkey]);
const zapsFeed = useRequestBuilder<FlatNoteStore>(FlatNoteStore, sub); const zapsFeed = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, sub);
const zaps = useMemo(() => { const zaps = useMemo(() => {
if (zapsFeed.data) { if (zapsFeed.data) {

View File

@ -7,9 +7,10 @@ import {
ParameterizedReplaceableNoteStore, ParameterizedReplaceableNoteStore,
RequestBuilder, RequestBuilder,
} from "@snort/system"; } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import useRequestBuilder from "Hooks/useRequestBuilder";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { System } from "index";
export default function useNotelistSubscription(pubkey: HexKey | undefined, l: Lists, defaultIds: HexKey[]) { export default function useNotelistSubscription(pubkey: HexKey | undefined, l: Lists, defaultIds: HexKey[]) {
const { preferences, publicKey } = useLogin(); const { preferences, publicKey } = useLogin();
@ -23,7 +24,11 @@ export default function useNotelistSubscription(pubkey: HexKey | undefined, l: L
return rb; return rb;
}, [pubkey]); }, [pubkey]);
const listStore = useRequestBuilder<ParameterizedReplaceableNoteStore>(ParameterizedReplaceableNoteStore, sub); const listStore = useRequestBuilder<ParameterizedReplaceableNoteStore>(
System,
ParameterizedReplaceableNoteStore,
sub
);
const etags = useMemo(() => { const etags = useMemo(() => {
if (isMe) return defaultIds; if (isMe) return defaultIds;
// there should only be a single event here because we only load 1 pubkey // 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; return s;
}, [etags, pubkey, preferences]); }, [etags, pubkey, preferences]);
const store = useRequestBuilder<FlatNoteStore>(FlatNoteStore, esub); const store = useRequestBuilder<FlatNoteStore>(System, FlatNoteStore, esub);
return store.data ?? []; return store.data ?? [];
} }

View File

@ -1,10 +0,0 @@
import { useSyncExternalStore } from "react";
import { SystemSnapshot } from "@snort/system";
import { System } from "index";
export default function useSystemState() {
return useSyncExternalStore<SystemSnapshot>(
cb => System.hook(cb),
() => System.snapshot()
);
}

View File

@ -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<MetadataCache | undefined>(
h => UserCache.hook(h, pubKey),
() => UserCache.getFromCache(pubKey)
);
useEffect(() => {
if (pubKey) {
ProfileLoader.TrackMetadata(pubKey);
return () => ProfileLoader.UntrackMetadata(pubKey);
}
}, [pubKey]);
return user;
}

View File

@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { Outlet, useLocation, useNavigate } from "react-router-dom";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { useUserProfile } from "@snort/system-react";
import messages from "./messages"; import messages from "./messages";
@ -18,7 +19,6 @@ import { useDmCache } from "Hooks/useDmsCache";
import { mapPlanName } from "./subscribe"; import { mapPlanName } from "./subscribe";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import Avatar from "Element/Avatar"; import Avatar from "Element/Avatar";
import { useUserProfile } from "Hooks/useUserProfile";
import { profileLink } from "SnortUtils"; import { profileLink } from "SnortUtils";
import { getCurrentSubscription } from "Subscription"; import { getCurrentSubscription } from "Subscription";
import Toaster from "Toaster"; import Toaster from "Toaster";
@ -147,7 +147,7 @@ const AccountHeader = () => {
const { isMuted } = useModeration(); const { isMuted } = useModeration();
const { publicKey, latestNotification, readNotifications } = useLogin(); const { publicKey, latestNotification, readNotifications } = useLogin();
const dms = useDmCache(); const dms = useDmCache();
const profile = useUserProfile(publicKey); const profile = useUserProfile(System, publicKey);
const hasNotifications = useMemo( const hasNotifications = useMemo(
() => latestNotification > readNotifications, () => latestNotification > readNotifications,

View File

@ -2,6 +2,7 @@ import React, { useMemo, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { HexKey, NostrEvent, NostrPrefix } from "@snort/system"; import { HexKey, NostrEvent, NostrPrefix } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import UnreadCount from "Element/UnreadCount"; import UnreadCount from "Element/UnreadCount";
import ProfileImage, { getDisplayName } from "Element/ProfileImage"; import ProfileImage, { getDisplayName } from "Element/ProfileImage";
@ -14,12 +15,12 @@ import usePageWidth from "Hooks/usePageWidth";
import NoteTime from "Element/NoteTime"; import NoteTime from "Element/NoteTime";
import DmWindow from "Element/DmWindow"; import DmWindow from "Element/DmWindow";
import Avatar from "Element/Avatar"; import Avatar from "Element/Avatar";
import { useUserProfile } from "Hooks/useUserProfile";
import Icon from "Icons/Icon"; import Icon from "Icons/Icon";
import Text from "Element/Text"; import Text from "Element/Text";
import "./MessagesPage.css"; import "./MessagesPage.css";
import messages from "./messages"; import messages from "./messages";
import { System } from "index";
const TwoCol = 768; const TwoCol = 768;
const ThreeCol = 1500; const ThreeCol = 1500;
@ -126,7 +127,7 @@ export default function MessagesPage() {
} }
function ProfileDmActions({ pubkey }: { pubkey: string }) { function ProfileDmActions({ pubkey }: { pubkey: string }) {
const profile = useUserProfile(pubkey); const profile = useUserProfile(System, pubkey);
const { block, unblock, isBlocked } = useModeration(); const { block, unblock, isBlocked } = useModeration();
const blocked = isBlocked(pubkey); const blocked = isBlocked(pubkey);

View File

@ -4,6 +4,7 @@ import { useIntl, FormattedMessage } from "react-intl";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { encodeTLV, EventKind, HexKey, NostrPrefix, tryParseNostrLink } from "@snort/system"; import { encodeTLV, EventKind, HexKey, NostrPrefix, tryParseNostrLink } from "@snort/system";
import { LNURL } from "@snort/shared"; import { LNURL } from "@snort/shared";
import { useUserProfile } from "@snort/system-react";
import { getReactions, unwrap } from "SnortUtils"; import { getReactions, unwrap } from "SnortUtils";
import { formatShort } from "Number"; import { formatShort } from "Number";
@ -19,7 +20,6 @@ import useBookmarkFeed from "Feed/BookmarkFeed";
import useFollowersFeed from "Feed/FollowersFeed"; import useFollowersFeed from "Feed/FollowersFeed";
import useFollowsFeed from "Feed/FollowsFeed"; import useFollowsFeed from "Feed/FollowsFeed";
import useProfileBadges from "Feed/BadgesFeed"; import useProfileBadges from "Feed/BadgesFeed";
import { useUserProfile } from "Hooks/useUserProfile";
import useModeration from "Hooks/useModeration"; import useModeration from "Hooks/useModeration";
import useZapsFeed from "Feed/ZapsFeed"; import useZapsFeed from "Feed/ZapsFeed";
import { default as ZapElement } from "Element/Zap"; import { default as ZapElement } from "Element/Zap";
@ -47,6 +47,7 @@ import { getNip05PubKey } from "Pages/LoginPage";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import messages from "./messages"; import messages from "./messages";
import { System } from "index";
const NOTES = 0; const NOTES = 0;
const REACTIONS = 1; const REACTIONS = 1;
@ -104,7 +105,7 @@ export default function ProfilePage() {
const params = useParams(); const params = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [id, setId] = useState<string>(); const [id, setId] = useState<string>();
const user = useUserProfile(id); const user = useUserProfile(System, id);
const loginPubKey = useLogin().publicKey; const loginPubKey = useLogin().publicKey;
const isMe = loginPubKey === id; const isMe = loginPubKey === id;
const [showLnQr, setShowLnQr] = useState<boolean>(false); const [showLnQr, setShowLnQr] = useState<boolean>(false);

View File

@ -2,6 +2,7 @@ import "./ZapPool.css";
import { useMemo, useSyncExternalStore } from "react"; import { useMemo, useSyncExternalStore } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl"; import { FormattedMessage, FormattedNumber } from "react-intl";
import { useUserProfile } from "@snort/system-react";
import { SnortPubKey } from "Const"; import { SnortPubKey } from "Const";
import ProfilePreview from "Element/ProfilePreview"; import ProfilePreview from "Element/ProfilePreview";
@ -9,7 +10,6 @@ import useLogin from "Hooks/useLogin";
import { UploaderServices } from "Upload"; import { UploaderServices } from "Upload";
import { bech32ToHex, getRelayName, unwrap } from "SnortUtils"; import { bech32ToHex, getRelayName, unwrap } from "SnortUtils";
import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController"; import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController";
import { useUserProfile } from "Hooks/useUserProfile";
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
import { useWallet } from "Wallet"; import { useWallet } from "Wallet";
import { System } from "index"; import { System } from "index";
@ -35,7 +35,7 @@ const DataProviders = [
function ZapTarget({ target }: { target: ZapPoolRecipient }) { function ZapTarget({ target }: { target: ZapPoolRecipient }) {
const login = useLogin(); const login = useLogin();
const profile = useUserProfile(target.pubkey); const profile = useUserProfile(System, target.pubkey);
const hasAddress = profile?.lud16 || profile?.lud06; const hasAddress = profile?.lud16 || profile?.lud06;
const defaultZapMount = Math.ceil(login.preferences.defaultZapAmount * (target.split / 100)); const defaultZapMount = Math.ceil(login.preferences.defaultZapAmount * (target.split / 100));
return ( return (

View File

@ -1,20 +1,21 @@
import { useState } from "react"; import { useState } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useUserProfile } from "@snort/system-react";
import Logo from "Element/Logo"; import Logo from "Element/Logo";
import { Nip5Services } from "Pages/Verification"; import { Nip5Services } from "Pages/Verification";
import Nip5Service from "Element/Nip5Service"; import Nip5Service from "Element/Nip5Service";
import ProfileImage from "Element/ProfileImage"; import ProfileImage from "Element/ProfileImage";
import { useUserProfile } from "Hooks/useUserProfile";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import messages from "./messages"; import messages from "./messages";
import { System } from "index";
export default function GetVerified() { export default function GetVerified() {
const navigate = useNavigate(); const navigate = useNavigate();
const { publicKey } = useLogin(); const { publicKey } = useLogin();
const user = useUserProfile(publicKey); const user = useUserProfile(System, publicKey);
const [isVerified, setIsVerified] = useState(false); const [isVerified, setIsVerified] = useState(false);
const name = user?.name || "nostrich"; const name = user?.name || "nostrich";
const [nip05, setNip05] = useState(`${name}@snort.social`); const [nip05, setNip05] = useState(`${name}@snort.social`);

View File

@ -2,11 +2,11 @@ import { useEffect, useState } from "react";
import { useIntl, FormattedMessage } from "react-intl"; import { useIntl, FormattedMessage } from "react-intl";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { mapEventToProfile } from "@snort/system"; import { mapEventToProfile } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import Logo from "Element/Logo"; import Logo from "Element/Logo";
import useEventPublisher from "Feed/EventPublisher"; import useEventPublisher from "Feed/EventPublisher";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { useUserProfile } from "Hooks/useUserProfile";
import { UserCache } from "Cache"; import { UserCache } from "Cache";
import AvatarEditor from "Element/AvatarEditor"; import AvatarEditor from "Element/AvatarEditor";
import { DISCOVER } from "."; import { DISCOVER } from ".";
@ -16,7 +16,7 @@ import messages from "./messages";
export default function ProfileSetup() { export default function ProfileSetup() {
const login = useLogin(); const login = useLogin();
const myProfile = useUserProfile(login.publicKey); const myProfile = useUserProfile(System, login.publicKey);
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [picture, setPicture] = useState(""); const [picture, setPicture] = useState("");
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();

View File

@ -4,10 +4,10 @@ import { useEffect, useState } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { mapEventToProfile } from "@snort/system"; import { mapEventToProfile } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { System } from "index"; import { System } from "index";
import useEventPublisher from "Feed/EventPublisher"; import useEventPublisher from "Feed/EventPublisher";
import { useUserProfile } from "Hooks/useUserProfile";
import { openFile } from "SnortUtils"; import { openFile } from "SnortUtils";
import useFileUpload from "Upload"; import useFileUpload from "Upload";
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
@ -26,7 +26,7 @@ export interface ProfileSettingsProps {
export default function ProfileSettings(props: ProfileSettingsProps) { export default function ProfileSettings(props: ProfileSettingsProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const { publicKey: id } = useLogin(); const { publicKey: id } = useLogin();
const user = useUserProfile(id ?? ""); const user = useUserProfile(System, id ?? "");
const publisher = useEventPublisher(); const publisher = useEventPublisher();
const uploader = useFileUpload(); const uploader = useFileUpload();

View File

@ -1,17 +1,19 @@
import useLogin from "Hooks/useLogin";
import { useUserProfile } from "Hooks/useUserProfile";
import Icon from "Icons/Icon";
import { useState } from "react"; 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 { UITask } from "Tasks";
import { DonateTask } from "./DonateTask"; import { DonateTask } from "./DonateTask";
import { Nip5Task } from "./Nip5Task"; import { Nip5Task } from "./Nip5Task";
import { System } from "index";
const AllTasks: Array<UITask> = [new Nip5Task(), new DonateTask()]; const AllTasks: Array<UITask> = [new Nip5Task(), new DonateTask()];
AllTasks.forEach(a => a.load()); AllTasks.forEach(a => a.load());
export const TaskList = () => { export const TaskList = () => {
const publicKey = useLogin().publicKey; const publicKey = useLogin().publicKey;
const user = useUserProfile(publicKey); const user = useUserProfile(System, publicKey);
const [, setTick] = useState<number>(0); const [, setTick] = useState<number>(0);
function muteTask(t: UITask) { function muteTask(t: UITask) {

View File

@ -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 <div>
Post by: {profile.name ?? profile.display_name}
<p>
{ev.content}
</p>
</div>
}
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<FlatNoteStore>(System, FlatNoteStore, sub);
return (
<>
{data.data.map(a => <Note ev={a} />)}
</>
)
}
export function MyApp() {
return (
<UserPosts pubkey="63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed" />
)
}
```

View File

@ -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 <div>
Post by: {profile.name ?? profile.display_name}
<p>
{ev.content}
</p>
</div>
}
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<FlatNoteStore>(System, FlatNoteStore, sub);
return (
<>
{data.data.map(a => <Note ev={a} />)}
</>
)
}
export function MyApp() {
return (
<UserPosts pubkey="63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed" />
)
}

View File

@ -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"
}
}

View File

@ -0,0 +1,3 @@
export * from "./useRequestBuilder";
export * from "./useSystemState";
export * from "./useUserProfile";

View File

@ -1,15 +1,18 @@
import { useSyncExternalStore } from "react"; import { useSyncExternalStore } from "react";
import { RequestBuilder, EmptySnapshot, NoteStore, StoreSnapshot } from "@snort/system"; import { RequestBuilder, EmptySnapshot, NoteStore, StoreSnapshot, SystemInterface } from "@snort/system";
import { unwrap } from "SnortUtils"; import { unwrap } from "@snort/shared";
import { System } from "index";
/**
* Send a query to the relays and wait for data
*/
const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TStore["getSnapshotData"]>>( const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TStore["getSnapshotData"]>>(
type: { new (): TStore }, system: SystemInterface,
type: { new(): TStore },
rb: RequestBuilder | null rb: RequestBuilder | null
) => { ) => {
const subscribe = (onChanged: () => void) => { const subscribe = (onChanged: () => void) => {
if (rb) { if (rb) {
const q = System.Query<TStore>(type, rb); const q = system.Query<TStore>(type, rb);
const release = q.feed.hook(onChanged); const release = q.feed.hook(onChanged);
q.uncancel(); q.uncancel();
return () => { return () => {
@ -22,7 +25,7 @@ const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TSto
}; };
}; };
const getState = (): StoreSnapshot<TSnapshot> => { const getState = (): StoreSnapshot<TSnapshot> => {
const q = System.GetQuery(rb?.id ?? ""); const q = system.GetQuery(rb?.id ?? "");
if (q) { if (q) {
return unwrap(q).feed?.snapshot as StoreSnapshot<TSnapshot>; return unwrap(q).feed?.snapshot as StoreSnapshot<TSnapshot>;
} }
@ -34,4 +37,4 @@ const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TSto
); );
}; };
export default useRequestBuilder; export { useRequestBuilder };

View File

@ -0,0 +1,10 @@
import { useSyncExternalStore } from "react";
import { SystemSnapshot } from "@snort/system";
import { ExternalStore } from "@snort/shared";
export function useSystemState(system: ExternalStore<SystemSnapshot>) {
return useSyncExternalStore<SystemSnapshot>(
cb => system.hook(cb),
() => system.snapshot()
);
}

View File

@ -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<MetadataCache | undefined>(
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)
);
}

View File

@ -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"]
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@snort/system", "name": "@snort/system",
"version": "1.0.4", "version": "1.0.5",
"description": "Snort nostr system package", "description": "Snort nostr system package",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@ -1,13 +1,13 @@
import debug from "debug"; 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 { NostrEvent, TaggedRawEvent } from "./Nostr";
import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection";
import { Query } from "./Query"; import { Query } from "./Query";
import { RelayCache } from "./GossipModel"; import { RelayCache } from "./GossipModel";
import { NoteStore } from "./NoteCollection"; import { NoteStore } from "./NoteCollection";
import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder"; import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder";
import { SystemInterface, SystemSnapshot } from "."; import { MetadataCache, ProfileLoaderService, SystemInterface, SystemSnapshot, UserProfileCache, UserRelaysCache } from ".";
/** /**
* Manages nostr content retrieval system * Manages nostr content retrieval system
@ -35,13 +35,36 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
*/ */
#relayCache: RelayCache; #relayCache: RelayCache;
constructor(props: { authHandler?: AuthHandler, relayCache: RelayCache }) { /**
* Storage class for user profiles
*/
#profileCache: FeedCache<MetadataCache>;
/**
* Profile loading service
*/
#profileLoader: ProfileLoaderService;
constructor(props: {
authHandler?: AuthHandler,
relayCache?: RelayCache,
profileCache?: FeedCache<MetadataCache>
}) {
super(); super();
this.#handleAuth = props.authHandler; 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(); this.#cleanup();
} }
/**
* Profile loader service allows you to request profiles
*/
get ProfileLoader() {
return this.#profileLoader;
}
get Sockets(): ConnectionStateSnapshot[] { get Sockets(): ConnectionStateSnapshot[] {
return [...this.#sockets.values()].map(a => a.snapshot()); return [...this.#sockets.values()].map(a => a.snapshot());
} }

View File

@ -22,6 +22,10 @@ export class ProfileLoaderService {
this.#FetchMetadata(); this.#FetchMetadata();
} }
get Cache() {
return this.#cache;
}
/** /**
* Request profile metadata for a set of pubkeys * Request profile metadata for a set of pubkeys
*/ */

View File

@ -27,7 +27,7 @@ export class SystemWorker extends ExternalStore<SystemSnapshot> implements Syste
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
Query<T extends NoteStore>(type: new () => T, req: RequestBuilder | null): Query | undefined { Query<T extends NoteStore>(type: new () => T, req: RequestBuilder | null): Query {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }

View File

@ -33,7 +33,7 @@ export interface SystemInterface {
HandleAuth?: AuthHandler; HandleAuth?: AuthHandler;
get Sockets(): Array<ConnectionStateSnapshot>; get Sockets(): Array<ConnectionStateSnapshot>;
GetQuery(id: string): Query | undefined; GetQuery(id: string): Query | undefined;
Query<T extends NoteStore>(type: { new(): T }, req: RequestBuilder | null): Query | undefined; Query<T extends NoteStore>(type: { new(): T }, req: RequestBuilder | null): Query;
ConnectToRelay(address: string, options: RelaySettings): Promise<void>; ConnectToRelay(address: string, options: RelaySettings): Promise<void>;
DisconnectRelay(address: string): void; DisconnectRelay(address: string): void;
BroadcastEvent(ev: NostrEvent): void; BroadcastEvent(ev: NostrEvent): void;

View File

@ -8707,7 +8707,7 @@ react-twitter-embed@^4.0.4:
react@^18.2.0: react@^18.2.0:
version "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== integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"