import { LoaderIcon } from "@lume/icons"; import { NDKCacheAdapterTauri } from "@lume/ndk-cache-tauri"; import { LumeStorage } from "@lume/storage"; import { QUOTES, delay, sendNativeNotification } from "@lume/utils"; import NDK, { NDKNip46Signer, NDKPrivateKeySigner, NDKRelay, NDKRelayAuthPolicies, } from "@nostr-dev-kit/ndk"; import { ndkAdapter } from "@nostr-fetch/adapter-ndk"; import { platform } from "@tauri-apps/plugin-os"; import { relaunch } from "@tauri-apps/plugin-process"; import Database from "@tauri-apps/plugin-sql"; import { check } from "@tauri-apps/plugin-updater"; import Markdown from "markdown-to-jsx"; import { NostrFetcher, normalizeRelayUrl, normalizeRelayUrlSet, } from "nostr-fetch"; import { PropsWithChildren, useEffect, useState } from "react"; import { createContext, useContextSelector } from "use-context-selector"; import { Ark } from "./ark"; type Context = { storage: LumeStorage; ark: Ark; }; const LumeContext = createContext({ storage: undefined, ark: undefined, }); const LumeProvider = ({ children }: PropsWithChildren) => { const [context, setContext] = useState(undefined); const [isNewVersion, setIsNewVersion] = useState(false); async function initNostrSigner({ storage, nsecbunker, }: { storage: LumeStorage; nsecbunker?: boolean; }) { try { if (!storage.account) return null; // NIP-46 Signer if (nsecbunker) { const localSignerPrivkey = await storage.loadPrivkey( `${storage.account.id}-nsecbunker`, ); if (!localSignerPrivkey) return null; const localSigner = new NDKPrivateKeySigner(localSignerPrivkey); const bunker = new NDK({ explicitRelayUrls: normalizeRelayUrlSet([ "wss://relay.nsecbunker.com/", "wss://nostr.vulpem.com/", ]), }); await bunker.connect(3000); const remoteSigner = new NDKNip46Signer( bunker, storage.account.pubkey, localSigner, ); await remoteSigner.blockUntilReady(); return remoteSigner; } // Privkey Signer const userPrivkey = await storage.loadPrivkey(storage.account.pubkey); if (!userPrivkey) { return null; } return new NDKPrivateKeySigner(userPrivkey); } catch (e) { console.error(e); return null; } } async function init() { const platformName = await platform(); const sqliteAdapter = await Database.load("sqlite:lume_v2.db"); const storage = new LumeStorage(sqliteAdapter, platformName); await storage.init(); // check for new update if (storage.settings.autoupdate) { const update = await check(); // install new version if (update) { setIsNewVersion(true); await update.downloadAndInstall(); await relaunch(); } } const explicitRelayUrls = normalizeRelayUrlSet([ "wss://bostr.nokotaro.com/", "wss://nostr.mutinywallet.com", ]); if (storage.settings.depot) { await storage.launchDepot(); await delay(2000); explicitRelayUrls.push(normalizeRelayUrl("ws://localhost:6090")); } // #TODO: user should config outbox relays const outboxRelayUrls = normalizeRelayUrlSet(["wss://purplepag.es"]); // #TODO: user should config blacklist relays // No need to connect depot tunnel url const blacklistRelayUrls = storage.settings.tunnelUrl.length ? [storage.settings.tunnelUrl, `${storage.settings.tunnelUrl}/`] : []; const cacheAdapter = new NDKCacheAdapterTauri(storage); const ndk = new NDK({ cacheAdapter, explicitRelayUrls, outboxRelayUrls, blacklistRelayUrls, enableOutboxModel: !storage.settings.lowPowerMode, autoConnectUserRelays: !storage.settings.lowPowerMode, autoFetchUserMutelist: !storage.settings.lowPowerMode, // clientName: 'Lume', // clientNip89: '', }); // add signer const signer = await initNostrSigner({ storage, nsecbunker: storage.settings.bunker, }); if (signer) ndk.signer = signer; // connect await ndk.connect(3000); // auth ndk.relayAuthDefaultPolicy = async (relay: NDKRelay, challenge: string) => { const signIn = NDKRelayAuthPolicies.signIn({ ndk, signer }); const event = await signIn(relay, challenge); if (event) { sendNativeNotification( `You've sign in sucessfully to relay: ${relay.url}`, ); return event; } }; // update account's metadata if (storage.account) { const user = ndk.getUser({ pubkey: storage.account.pubkey }); ndk.activeUser = user; const contacts = await user.follows(); storage.account.contacts = [...contacts].map((user) => user.pubkey); } // init nostr fetcher const fetcher = NostrFetcher.withCustomPool(ndkAdapter(ndk)); // ark utils const ark = new Ark({ storage, ndk, fetcher }); // update context setContext({ ark, storage }); } useEffect(() => { if (!context && !isNewVersion) init(); }, []); if (!context) { return (
TIP:
{QUOTES[Math.floor(Math.random() * QUOTES.length)]}

{isNewVersion ? "Found a new version, updating..." : "Starting..."}

); } return ( {children} ); }; const useArk = () => { const context = useContextSelector(LumeContext, (state) => state.ark); if (context === undefined) { throw new Error("Please import Ark Provider to use useArk() hook"); } return context; }; const useStorage = () => { const context = useContextSelector(LumeContext, (state) => state.storage); if (context === undefined) { throw new Error("Please import Ark Provider to use useStorage() hook"); } return context; }; export { LumeProvider, useArk, useStorage };