@snort/system-react package

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

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

@ -0,0 +1,40 @@
import { useSyncExternalStore } from "react";
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 = <TStore extends NoteStore, TSnapshot = ReturnType<TStore["getSnapshotData"]>>(
system: SystemInterface,
type: { new(): TStore },
rb: RequestBuilder | null
) => {
const subscribe = (onChanged: () => void) => {
if (rb) {
const q = system.Query<TStore>(type, rb);
const release = q.feed.hook(onChanged);
q.uncancel();
return () => {
q.cancel();
release();
};
}
return () => {
// noop
};
};
const getState = (): StoreSnapshot<TSnapshot> => {
const q = system.GetQuery(rb?.id ?? "");
if (q) {
return unwrap(q).feed?.snapshot as StoreSnapshot<TSnapshot>;
}
return EmptySnapshot as StoreSnapshot<TSnapshot>;
};
return useSyncExternalStore<StoreSnapshot<TSnapshot>>(
v => subscribe(v),
() => getState()
);
};
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"]
}