import { useArk } from "@lume/ark"; import { CheckIcon, ChevronDownIcon, LoaderIcon } from "@lume/icons"; import { useStorage } from "@lume/storage"; import { onboardingAtom } from "@lume/utils"; import NDK, { NDKEvent, NDKKind, NDKNip46Signer, NDKPrivateKeySigner, } from "@nostr-dev-kit/ndk"; import * as Select from "@radix-ui/react-select"; import { downloadDir } from "@tauri-apps/api/path"; import { Window } from "@tauri-apps/api/window"; import { save } from "@tauri-apps/plugin-dialog"; import { writeTextFile } from "@tauri-apps/plugin-fs"; import { useSetAtom } from "jotai"; import { getPublicKey, nip19 } from "nostr-tools"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { useLoaderData, useNavigate } from "react-router-dom"; import { toast } from "sonner"; const Item = ({ event }: { event: NDKEvent }) => { const domain = JSON.parse(event.content).nip05.replace("_@", ""); return ( @{domain} ); }; export function CreateAccountScreen() { const ark = useArk(); const storage = useStorage(); const navigate = useNavigate(); const services = useLoaderData() as NDKEvent[]; const setOnboarding = useSetAtom(onboardingAtom); const [serviceId, setServiceId] = useState(services?.[0]?.id); const [loading, setIsLoading] = useState(false); const { register, handleSubmit, formState: { isValid }, } = useForm(); const getDomainName = (id: string) => { const event = services.find((ev) => ev.id === id); return JSON.parse(event.content).nip05.replace("_@", "") as string; }; const generateNostrKeys = async () => { const signer = NDKPrivateKeySigner.generate(); const pubkey = getPublicKey(signer.privateKey); const npub = nip19.npubEncode(pubkey); const nsec = nip19.nsecEncode(signer.privateKey); ark.updateNostrSigner({ signer }); const downloadPath = await downloadDir(); const fileName = `nostr_keys_${new Date().getTime().toString(36)}.txt`; const filePath = await save({ defaultPath: `${downloadPath}/${fileName}`, }); if (filePath) { await writeTextFile( filePath, `Nostr account, generated by Lume (lume.nu)\nPublic key: ${npub}\nPrivate key: ${nsec}`, ); } // else { user cancel action } await storage.createAccount({ pubkey: pubkey, privkey: signer.privateKey, }); setOnboarding(true); return navigate("/auth/onboarding", { replace: true }); }; const onSubmit = async (data: { username: string; email: string }) => { try { setIsLoading(true); const domain = getDomainName(serviceId); const service = services.find((ev) => ev.id === serviceId); // generate ndk for nsecbunker const localSigner = NDKPrivateKeySigner.generate(); const bunker = new NDK({ explicitRelayUrls: [ "wss://relay.nsecbunker.com/", "wss://nostr.vulpem.com/", ], }); await bunker.connect(2000); // generate tmp remote singer for create account const remoteSigner = new NDKNip46Signer( bunker, service.pubkey, localSigner, ); // handle auth url request let authWindow: Window; remoteSigner.addListener("authUrl", (authUrl: string) => { authWindow = new Window(`auth-${serviceId}`, { url: authUrl, title: domain, titleBarStyle: "overlay", width: 415, height: 600, center: true, closable: false, }); }); // create new account const account = await remoteSigner.createAccount( data.username, domain, data.email, ); if (!account) { authWindow.close(); setIsLoading(false); return toast.error("Failed to create new account, try again later"); } authWindow.close(); // add account to storage await storage.createSetting("nsecbunker", "1"); const dbAccount = await storage.createAccount({ pubkey: account, privkey: localSigner.privateKey, }); ark.account = dbAccount; // get final signer with newly created account const finalSigner = new NDKNip46Signer(bunker, account, localSigner); await finalSigner.blockUntilReady(); // update main ndk instance signer ark.updateNostrSigner({ signer: finalSigner }); // remove default nsecbunker profile and contact list await ark.createEvent({ kind: NDKKind.Metadata, content: "", tags: [] }); await ark.createEvent({ kind: NDKKind.Contacts, content: "", tags: [] }); setOnboarding(true); setIsLoading(false); return navigate("/auth/onboarding", { replace: true }); } catch (e) { setIsLoading(false); toast.error(String(e)); } }; return (

Let's get you set up on Nostr.

With an account on Nostr, you'll be able to travel across all nostr clients, all your data are synced.

{!services ? (
) : (
@{getDomainName(serviceId)} Public handles {services.map((service) => ( ))}
Or

If you are using this option, please make sure keep your keys in safe place. You{" "} cannot recover if it lost, all your data will be{" "} lost forever.

)}
); }