diff --git a/app/(app)/(profile)/[npub]/_components/MySubscription.tsx b/app/(app)/(profile)/[npub]/_components/MySubscription.tsx index b8f6d25..2572773 100644 --- a/app/(app)/(profile)/[npub]/_components/MySubscription.tsx +++ b/app/(app)/(profile)/[npub]/_components/MySubscription.tsx @@ -11,7 +11,7 @@ type MySubscription = { export default function MySubscription({ pubkey }: MySubscription) { const { ndk, fetchEvents } = useNDK(); - const { currentUser, mySubscription, follows } = useCurrentUser(); + const { currentUser } = useCurrentUser(); const [subscriptionTiers, setSubscriptionTiers] = useState([]); useEffect(() => { @@ -22,13 +22,14 @@ export default function MySubscription({ pubkey }: MySubscription) { async function handleFetchSubscriptionTiers() { try { - console.log("FETCHING", pubkey); + if (!ndk) { + return "NDK MISING"; + } const events = await fetchEvents({ kinds: [30044 as NDKKind], authors: [pubkey], }); console.log("events", events); - setSubscriptionTiers(events); } catch (err) { console.log("error", err); diff --git a/app/(app)/(profile)/[npub]/page.tsx b/app/(app)/(profile)/[npub]/page.tsx index ba1efe2..d41599e 100644 --- a/app/(app)/(profile)/[npub]/page.tsx +++ b/app/(app)/(profile)/[npub]/page.tsx @@ -1,5 +1,5 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import dynamic from "next/dynamic"; import Image from "next/image"; import { Button } from "@/components/ui/button"; @@ -40,15 +40,27 @@ export default function ProfilePage({ }; }) { const modal = useModal(); - const { currentUser, mySubscription, follows } = useCurrentUser(); + const { currentUser, mySubscription, initSubscriptions } = useCurrentUser(); const [activeTab, setActiveTab] = useState("feed"); + const [delegate, setDelegate] = useState(); const { type, data } = nip19.decode(npub); - const delegate = getTagValues("delegate", mySubscription?.tags ?? []); + const pubkey = data.toString(); + useEffect(() => { + console.log("In affectr"); + if (mySubscription) { + const _delegate = getTagValues("delegate", mySubscription.tags); + console.log("Setting"); + setDelegate(_delegate); + } + }, [mySubscription]); + useEffect(() => { + initSubscriptions(pubkey); + }, []); + console.log("Delegate", delegate); if (type !== "npub") { throw new Error("Invalid list"); } - const pubkey = data.toString(); const { profile } = useProfile(pubkey); return ( diff --git a/app/(app)/_layout/components/AuthActions.tsx b/app/(app)/_layout/components/AuthActions.tsx index 4c8b26d..b144061 100644 --- a/app/(app)/_layout/components/AuthActions.tsx +++ b/app/(app)/_layout/components/AuthActions.tsx @@ -30,6 +30,10 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { useKeyboardShortcut } from "@/lib/hooks/useKeyboardShortcut"; + +import { db } from "@nostr-dev-kit/ndk-cache-dexie"; +import { useLiveQuery } from "dexie-react-hooks"; + const LoginModal = dynamic(() => import("@/components/Modals/Login"), { ssr: false, }); @@ -40,6 +44,16 @@ export default function AuthActions() { const { currentUser, logout, attemptLogin, initSubscriptions } = useCurrentUser(); const { ndk } = useNDK(); + const friends = useLiveQuery( + async () => { + console.log("Calling", db); + if (!db) return; + const friends = await db.users.count(); + return friends; + }, + // specify vars that affect query: + [], + ); useKeyboardShortcut(["shift", "ctrl", "u"], () => { if (currentUser) { @@ -64,6 +78,7 @@ export default function AuthActions() { + {friends} ); } diff --git a/app/(app)/sub/[naddr]/_components/Header.tsx b/app/(app)/sub/[naddr]/_components/Header.tsx index 66425d9..d699f82 100644 --- a/app/(app)/sub/[naddr]/_components/Header.tsx +++ b/app/(app)/sub/[naddr]/_components/Header.tsx @@ -22,6 +22,7 @@ import { btcToSats, formatNumber } from "@/lib/utils"; import { formatDate } from "@/lib/utils/dates"; import { follow } from "@/lib/actions/create"; import { log } from "@/lib/utils"; + const CreateEventModal = dynamic(() => import("@/components/Modals/NewEvent"), { ssr: false, }); diff --git a/app/_providers/ndk/context/instance.ts b/app/_providers/ndk/context/instance.ts index 84f1660..7847f57 100644 --- a/app/_providers/ndk/context/instance.ts +++ b/app/_providers/ndk/context/instance.ts @@ -6,7 +6,10 @@ import NDK, { NDKNip07Signer, NDKNip46Signer, NDKPrivateKeySigner, + type NDKCacheAdapter, } from "@nostr-dev-kit/ndk"; +import NDKCacheAdapterDexie from "@nostr-dev-kit/ndk-cache-dexie"; +import { db } from "@/lib/ndk/db"; export default function NDKInstance(explicitRelayUrls: string[]) { const loaded = useRef(false); @@ -16,21 +19,62 @@ export default function NDKInstance(explicitRelayUrls: string[]) { NDKPrivateKeySigner | NDKNip46Signer | NDKNip07Signer | undefined >(undefined); + // TODO: fully support NIP-11 + async function getExplicitRelays() { + try { + // get relays + const relays = explicitRelayUrls; + const onlineRelays = new Set(relays); + + for (const relay of relays) { + try { + const url = new URL(relay); + const res = await fetch(`https://${url.hostname}`, { + method: "GET", + headers: { + Accept: "application/nostr+json", + }, + }); + + if (!res.ok) { + console.info(`${relay} is not working, skipping...`); + onlineRelays.delete(relay); + } + } catch { + console.warn(`${relay} is not working, skipping...`); + onlineRelays.delete(relay); + } + } + + // return all online relays + return [...onlineRelays]; + } catch (e) { + console.error(e); + } + } + useEffect(() => { async function load() { if (ndk === undefined && loaded.current === false) { loaded.current = true; - await loadNdk(explicitRelayUrls); + await loadNdk(); } } load(); }, []); async function loadNdk( - explicitRelayUrls: string[], signer?: NDKPrivateKeySigner | NDKNip46Signer | NDKNip07Signer, ) { - const ndkInstance = new NDK({ explicitRelayUrls, signer }); + const explicitRelayUrls = await getExplicitRelays(); + const dexieAdapter = new NDKCacheAdapterDexie({ + dbName: "ndkcache", + }); + const ndkInstance = new NDK({ + explicitRelayUrls, + signer, + cacheAdapter: dexieAdapter as unknown as NDKCacheAdapter, + }); if (process.env.NODE_ENV === "development") { ndkInstance.pool.on("connect", () => console.log("✅ connected")); ndkInstance.pool.on("disconnect", () => console.log("❌ disconnected")); @@ -42,6 +86,7 @@ export default function NDKInstance(explicitRelayUrls: string[]) { try { await ndkInstance.connect(); + ndkInstance.signer = signer; _setNDK(ndkInstance); } catch (error) { console.error("ERROR loading NDK NDKInstance", error); @@ -51,7 +96,7 @@ export default function NDKInstance(explicitRelayUrls: string[]) { async function setSigner( signer: NDKPrivateKeySigner | NDKNip46Signer | NDKNip07Signer, ) { - loadNdk(explicitRelayUrls, signer); + loadNdk(signer); } async function fetchEvents(filter: NDKFilter): Promise { diff --git a/bun.lockb b/bun.lockb index d56a77b..e4f644c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/lib/hooks/useCurrentUser.ts b/lib/hooks/useCurrentUser.ts index dc78a95..c4e4bf2 100644 --- a/lib/hooks/useCurrentUser.ts +++ b/lib/hooks/useCurrentUser.ts @@ -7,6 +7,8 @@ import { useNDK } from "@/app/_providers/ndk"; import { nip19 } from "nostr-tools"; import useLists from "./useLists"; import useSubscriptions from "./useSubscriptions"; +import { db } from "@nostr-dev-kit/ndk-cache-dexie"; +import { unixTimeNowInSeconds } from "../nostr/dates"; export default function useCurrentUser() { const { @@ -20,6 +22,7 @@ export default function useCurrentUser() { const { loginWithNip07, getProfile, ndk, fetchEvents } = useNDK(); const { init } = useLists(); const { init: initSubscriptions, mySubscription } = useSubscriptions(); + async function attemptLogin() { try { const shouldReconnect = localStorage.getItem("shouldReconnect"); @@ -67,6 +70,12 @@ export default function useCurrentUser() { const user = ndk.getUser({ hexpubkey: pubkey }); console.log("user", user); await user.fetchProfile(); + + // await db.users.add({ + // profile: user.profile!, + // pubkey: pubkey, + // createdAt: unixTimeNowInSeconds(), + // }); setCurrentUser(user); void init(user.pubkey); } @@ -76,6 +85,7 @@ export default function useCurrentUser() { console.log("fetching follows"); (async () => { const following = await currentUser.follows(); + console.log("Follows", following); setFollows(following); })(); }, [currentUser]); diff --git a/lib/hooks/useSubscriptions.ts b/lib/hooks/useSubscriptions.ts index 5d6b24a..30660f1 100644 --- a/lib/hooks/useSubscriptions.ts +++ b/lib/hooks/useSubscriptions.ts @@ -10,17 +10,21 @@ export default function useSubscriptions() { const { mySubscription, setMySubscription } = subscriptionsStore(); const { fetchEvents, ndk } = useNDK(); async function init(pubkey: string) { + if (!ndk) { + return "NDK MISING"; + } setIsLoading(true); try { const subscriptionLists = await fetchEvents({ kinds: [30044 as NDKKind], authors: [pubkey], }); + console.log("Found subscriptionLists", subscriptionLists); if (subscriptionLists[0]) { setMySubscription(new NDKList(ndk, subscriptionLists[0].rawEvent())); } } catch (err) { - console.log("error in init", err); + console.log("error in subscriptionLists", err); } finally { setIsLoading(false); } diff --git a/lib/ndk/db.ts b/lib/ndk/db.ts new file mode 100644 index 0000000..0649689 --- /dev/null +++ b/lib/ndk/db.ts @@ -0,0 +1,10 @@ +import Dexie from "dexie"; + +export class MySubClassedDexie extends Dexie { + constructor() { + super("myDatabase"); + this.version(1).stores({}); + } +} + +export const db = new MySubClassedDexie(); diff --git a/package.json b/package.json index 4763c22..7d2cda3 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@hookform/resolvers": "^3.3.2", "@noble/hashes": "^1.3.2", "@nostr-dev-kit/ndk": "^2.0.0", + "@nostr-dev-kit/ndk-cache-dexie": "^2.0.3", "@nostr-dev-kit/ndk-react": "^0.1.1", "@radix-ui/react-aspect-ratio": "^1.0.3", "@radix-ui/react-avatar": "^1.0.4", @@ -37,6 +38,8 @@ "crypto": "^1.0.1", "crypto-js": "^4.1.1", "dayjs": "^1.11.10", + "dexie": "^3.2.4", + "dexie-react-hooks": "^1.1.6", "focus-trap-react": "^10.2.3", "framer-motion": "^10.16.4", "jotai": "^2.4.3", diff --git a/types/index.ts b/types/index.ts index 6889a58..0905922 100644 --- a/types/index.ts +++ b/types/index.ts @@ -1,4 +1,5 @@ import { z } from "zod"; +import { type NDKUserProfile } from "@nostr-dev-kit/ndk"; type User = { npub: string; @@ -37,6 +38,15 @@ const EventSchema = z.object({ sig: z.string(), }); +type Account = NDKUserProfile & { + id: string; + pubkey: string; + follows: null | string[]; + circles: null | string[]; + is_active: number; + last_login_at: number; +}; + export { UserSchema, EventSchema }; -export type { User }; +export type { User, Account };