Skip to content

Commit

Permalink
feat: improve nostr subscription and state management
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Aug 31, 2024
1 parent 2bfac22 commit b12a2d3
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 43 deletions.
33 changes: 19 additions & 14 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,21 +267,26 @@ export default function App() {
{store.isLoggedIn && (
<div className="flex flex-wrap gap-2">
{jim.recommendedByUsers.map((user) => (
<div key={user.user.pubkey} className="avatar">
<div className="w-8 rounded-lg">
<img
title={
user.user.profile?.displayName ||
user.user.profile?.name ||
user.user.npub
}
src={
user.user.profile?.image ||
`https://api.dicebear.com/9.x/pixel-art/svg?seed=${user.user.pubkey}`
}
/>
<a
key={user.user.pubkey}
href={`https://nostrudel.ninja/#/u/${user.user.npub}`}
>
<div className="avatar">
<div className="w-8 rounded-lg">
<img
title={
user.user.profile?.displayName ||
user.user.profile?.name ||
user.user.npub
}
src={
user.user.profile?.image ||
`https://api.dicebear.com/9.x/pixel-art/svg?seed=${user.user.pubkey}`
}
/>
</div>
</div>
</div>
</a>
))}
</div>
)}
Expand Down
106 changes: 77 additions & 29 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import NDK, {
NDKKind,
NDKNip07Signer,
NDKSigner,
NDKSubscription,
NDKUser,
} from "@nostr-dev-kit/ndk";
import { create } from "zustand";
Expand Down Expand Up @@ -51,19 +52,48 @@ type Jim = {
};

type Store = {
readonly _jims: Partial<Jim>[];
readonly jims: Jim[];
readonly isLoggedIn: boolean;
readonly hasLoaded: boolean;
setJims(jims: Jim[]): void;
updateJim(jim: Partial<Jim>): void;
setLoaded(hasLoaded: boolean): void;
login(): Promise<boolean>;
};

export const useStore = create<Store>((set, get) => ({
isLoggedIn: false,
_jims: [],
jims: [],
hasLoaded: false,
setJims: (jims) => set({ jims }),
updateJim: (jim: Partial<Jim>) => {
const currentJims = get()._jims;
const currentJim: Partial<Jim> =
currentJims.find((current) => current.eventId === jim.eventId) || {};

const updatedJim: Partial<Jim> = {
...currentJim,
...jim,
recommendedByUsers: [
...(currentJim.recommendedByUsers || []),
...(jim.recommendedByUsers || []),
].filter(
(v, i, a) =>
a.findIndex((current) => current.user.npub === v.user.npub) === i,
),
};

const _jims = [
...currentJims.filter((current) => current.eventId !== jim.eventId),
updatedJim,
];
const jims = _jims.filter((jim) => !!jim.info) as Jim[];
jims.sort(
(a, b) => b.recommendedByUsers.length - a.recommendedByUsers.length,
);

set({ _jims, jims });
},
setLoaded: (hasLoaded) => set({ hasLoaded }),
login: async () => {
if (get().isLoggedIn || !get().hasLoaded) {
Expand All @@ -83,19 +113,36 @@ export const useStore = create<Store>((set, get) => ({
}));

(async () => {
await ndk.connect();
console.log("Connecting to relays");
await ndk.connect(5000);
console.log("Loading Jims");
await loadJims();
useStore.getState().setLoaded(true);
})();

let jimInstanceEventsSub: NDKSubscription | undefined;
async function loadJims() {
const jimInstanceEvents = await ndk.fetchEvents({
if (jimInstanceEventsSub) {
jimInstanceEventsSub.stop();
}
console.log("Fetching jim instance events");
jimInstanceEventsSub = ndk.subscribe({
kinds: [JIM_INSTANCE_KIND as NDKKind],
});

const jims: Jim[] = [];
console.log("jim instance events", jimInstanceEvents);
for (const event of jimInstanceEvents) {
jimInstanceEventsSub.on("event", async (event) => {
if (
useStore
.getState()
.jims.some((existing) => existing.eventId === event.id && existing.info)
) {
// only update the event
useStore.getState().updateJim({
event,
eventId: event.id,
});
}

const url = event.dTag;
if (url && !url.endsWith("/")) {
let info: Jim["info"];
Expand All @@ -107,7 +154,7 @@ async function loadJims() {
info = await response.json();
} catch (error) {
console.error("failed to fetch jim info", url, error);
continue;
return;
}
let reserves: Jim["reserves"];
try {
Expand All @@ -118,39 +165,39 @@ async function loadJims() {
reserves = await response.json();
} catch (error) {
console.error("failed to fetch jim reserves", url, error);
continue;
return;
}

jims.push({
const jim: Partial<Jim> = {
eventId: event.id,
url,
recommendedByUsers: [],
event,
info,
reserves,
});
useStore.getState().setJims(jims);
};
useStore.getState().updateJim(jim);
}
}
});

// load recommendations

const jimRecommendationEvents = await ndk.fetchEvents({
const jimRecommendationSub = ndk.subscribe({
kinds: [38000],
"#k": [JIM_INSTANCE_KIND.toString()],
});

console.log("jim recommendation events", jimRecommendationEvents);
for (const recommendationEvent of jimRecommendationEvents) {
const jim = jims.find((j) => j.eventId === recommendationEvent.dTag);
if (jim) {
jim.recommendedByUsers.push({
user: recommendationEvent.author,
mutual: false,
});
}
}
useStore.getState().setJims(jims);
jimRecommendationSub.on("event", (recommendationEvent: NDKEvent) => {
const jimEventId = recommendationEvent.dTag;
useStore.getState().updateJim({
eventId: jimEventId,
recommendedByUsers: [
{
user: recommendationEvent.author,
mutual: false,
},
],
});
});
}

async function loadMutualRecommendations(signer: NDKSigner) {
Expand All @@ -170,10 +217,11 @@ async function loadMutualRecommendations(signer: NDKSigner) {
await recommendedByUser.user.fetchProfile();
}
}
useStore.getState().updateJim({
eventId: jim.eventId,
recommendedByUsers: jim.recommendedByUsers,
});
}
useStore.getState().setJims(jims);

// TODO: sort jims

useStore.getState().setLoaded(true);
}

0 comments on commit b12a2d3

Please sign in to comment.