diff --git a/apps/desktop/src/router.tsx b/apps/desktop/src/router.tsx
index 3fd3b071..a840b26f 100644
--- a/apps/desktop/src/router.tsx
+++ b/apps/desktop/src/router.tsx
@@ -207,9 +207,6 @@ export default function Router() {
},
{
path: "create",
- loader: async () => {
- return await ark.getOAuthServices();
- },
async lazy() {
const { CreateAccountScreen } = await import(
"./routes/auth/create"
@@ -217,6 +214,27 @@ export default function Router() {
return { Component: CreateAccountScreen };
},
},
+ {
+ path: "create-keys",
+ async lazy() {
+ const { CreateAccountKeys } = await import(
+ "./routes/auth/create-keys"
+ );
+ return { Component: CreateAccountKeys };
+ },
+ },
+ {
+ path: "create-address",
+ loader: async () => {
+ return await ark.getOAuthServices();
+ },
+ async lazy() {
+ const { CreateAccountAddress } = await import(
+ "./routes/auth/create-address"
+ );
+ return { Component: CreateAccountAddress };
+ },
+ },
{
path: "login",
async lazy() {
diff --git a/apps/desktop/src/routes/auth/create-address.tsx b/apps/desktop/src/routes/auth/create-address.tsx
new file mode 100644
index 00000000..8a525bdc
--- /dev/null
+++ b/apps/desktop/src/routes/auth/create-address.tsx
@@ -0,0 +1,261 @@
+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 { UnlistenFn } from "@tauri-apps/api/event";
+import { Window } from "@tauri-apps/api/window";
+import { useSetAtom } from "jotai";
+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 CreateAccountAddress() {
+ const ark = useArk();
+ const storage = useStorage();
+ const services = useLoaderData() as NDKEvent[];
+ const setOnboarding = useSetAtom(onboardingAtom);
+ const navigate = useNavigate();
+
+ 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 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 unlisten: UnlistenFn;
+ let authWindow: Window;
+ let account: string = undefined;
+
+ remoteSigner.addListener("authUrl", async (authUrl: string) => {
+ authWindow = new Window(`auth-${serviceId}`, {
+ url: authUrl,
+ title: domain,
+ titleBarStyle: "overlay",
+ width: 600,
+ height: 650,
+ center: true,
+ closable: false,
+ });
+ unlisten = await authWindow.onCloseRequested(() => {
+ if (!account) {
+ setIsLoading(false);
+ unlisten();
+
+ return authWindow.close();
+ }
+ });
+ });
+
+ // create new account
+ account = await remoteSigner.createAccount(
+ data.username,
+ domain,
+ data.email,
+ );
+
+ if (!account) {
+ unlisten();
+ setIsLoading(false);
+
+ authWindow.close();
+
+ return toast.error("Failed to create new account, try again later");
+ }
+
+ unlisten();
+ 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: [] });
+
+ setIsLoading(false);
+ setOnboarding({ open: true, newUser: true });
+
+ return navigate("/auth/onboarding", { replace: true });
+ } catch (e) {
+ setIsLoading(false);
+ toast.error(String(e));
+ }
+ };
+
+ return (
+
+
+
+
Create Account
+
+ {!services ? (
+
+
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/apps/desktop/src/routes/auth/create-keys.tsx b/apps/desktop/src/routes/auth/create-keys.tsx
new file mode 100644
index 00000000..4090075c
--- /dev/null
+++ b/apps/desktop/src/routes/auth/create-keys.tsx
@@ -0,0 +1,91 @@
+import { useArk } from "@lume/ark";
+import { useStorage } from "@lume/storage";
+import { onboardingAtom } from "@lume/utils";
+import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
+import { desktopDir } from "@tauri-apps/api/path";
+import { save } from "@tauri-apps/plugin-dialog";
+import { writeTextFile } from "@tauri-apps/plugin-fs";
+import { useSetAtom } from "jotai";
+import { nanoid } from "nanoid";
+import { getPublicKey, nip19 } from "nostr-tools";
+import { useNavigate } from "react-router-dom";
+import { toast } from "sonner";
+
+export function CreateAccountKeys() {
+ const ark = useArk();
+ const storage = useStorage();
+ const setOnboarding = useSetAtom(onboardingAtom);
+ const navigate = useNavigate();
+
+ 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 desktopDir();
+ const fileName = `nostr_keys_${nanoid(4)}.txt`;
+ const filePath = await save({
+ defaultPath: `${downloadPath}/${fileName}`,
+ });
+
+ if (!filePath) {
+ return toast.info("You need to save account keys before continue.");
+ }
+
+ await writeTextFile(
+ filePath,
+ `Nostr Account\nGenerated by Lume (lume.nu)\n---\nPublic key: ${npub}\nPrivate key: ${nsec}`,
+ );
+
+ await storage.createAccount({
+ pubkey: pubkey,
+ privkey: signer.privateKey,
+ });
+
+ setOnboarding({ open: true, newUser: true });
+
+ return navigate("/auth/onboarding", { replace: true });
+ };
+
+ return (
+
+ );
+}
diff --git a/apps/desktop/src/routes/auth/create.tsx b/apps/desktop/src/routes/auth/create.tsx
index 83ed6973..9a1a7c3f 100644
--- a/apps/desktop/src/routes/auth/create.tsx
+++ b/apps/desktop/src/routes/auth/create.tsx
@@ -1,342 +1,74 @@
-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 { UnlistenFn } from "@tauri-apps/api/event";
-import { desktopDir } 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 { nanoid } from "nanoid";
-import { getPublicKey, nip19 } from "nostr-tools";
+import { LoaderIcon } from "@lume/icons";
+import { cn } from "@lume/utils";
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}
-
-
-
-
- );
-};
+import { Link, useNavigate } from "react-router-dom";
export function CreateAccountScreen() {
- const ark = useArk();
- const storage = useStorage();
- const services = useLoaderData() as NDKEvent[];
- const setOnboarding = useSetAtom(onboardingAtom);
const navigate = useNavigate();
- const [serviceId, setServiceId] = useState(services?.[0]?.id);
- const [loading, setIsLoading] = useState(false);
+ const [method, setMethod] = useState<"self" | "managed">("self");
+ const [loading, setLoading] = useState(false);
- const {
- register,
- handleSubmit,
- formState: { isValid },
- } = useForm();
+ const next = () => {
+ setLoading(true);
- 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 desktopDir();
- const fileName = `nostr_keys_${nanoid(4)}.txt`;
- const filePath = await save({
- defaultPath: `${downloadPath}/${fileName}`,
- });
-
- if (!filePath) {
- return toast.info("You need to save account keys before continue.");
- }
-
- await writeTextFile(
- filePath,
- `Nostr Account\nGenerated by Lume (lume.nu)\n---\nPublic key: ${npub}\nPrivate key: ${nsec}`,
- );
-
- await storage.createAccount({
- pubkey: pubkey,
- privkey: signer.privateKey,
- });
-
- setOnboarding({ open: true, newUser: 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 unlisten: UnlistenFn;
- let authWindow: Window;
- let account: string = undefined;
-
- remoteSigner.addListener("authUrl", async (authUrl: string) => {
- authWindow = new Window(`auth-${serviceId}`, {
- url: authUrl,
- title: domain,
- titleBarStyle: "overlay",
- width: 600,
- height: 650,
- center: true,
- closable: false,
- });
- unlisten = await authWindow.onCloseRequested(() => {
- if (!account) {
- setIsLoading(false);
- unlisten();
-
- return authWindow.close();
- }
- });
- });
-
- // create new account
- account = await remoteSigner.createAccount(
- data.username,
- domain,
- data.email,
- );
-
- if (!account) {
- unlisten();
- setIsLoading(false);
-
- authWindow.close();
-
- return toast.error("Failed to create new account, try again later");
- }
-
- unlisten();
- 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: [] });
-
- setIsLoading(false);
- setOnboarding({ open: true, newUser: true });
-
- return navigate("/auth/onboarding", { replace: true });
- } catch (e) {
- setIsLoading(false);
- toast.error(String(e));
+ if (method === "self") {
+ navigate("/auth/create-keys");
+ } else {
+ navigate("/auth/create-address");
}
};
return (
-
+
Let's get you set up on Nostr.
+
+ Choose one of methods below to create your account
+
+
+
+
setMethod("managed")}
+ className={cn(
+ "flex flex-col items-start px-4 py-3.5 bg-neutral-900 rounded-xl hover:bg-neutral-800",
+ method === "managed" ? "ring-1 ring-teal-500" : "",
+ )}
+ >
+ Managed by Provider
+
+ A 3rd party provider will handle your sign in keys for you.
+
+
+
setMethod("self")}
+ className={cn(
+ "flex flex-col items-start px-4 py-3.5 bg-neutral-900 rounded-xl hover:bg-neutral-800",
+ method === "self" ? "ring-1 ring-teal-500" : "",
+ )}
+ >
+ Self-Managed
+
+ You create your keys and keep them safe.
+
+
+
+ {loading ? (
+
+ ) : (
+ "Continue"
+ )}
+
- {!services ? (
-
-
-
- ) : (
-
-
-
-
-
-
-
-
- Or manage your own keys
-
-
-
-
- Mostly compatible with other Nostr clients
-
-
-
-
- Generate Nostr Keys
-
-
- If you are using this option, please make sure to store your
- keys safely. You{" "}
- cannot recover them if
- they're lost, and will be{" "}
- unable to access your
- account.
-
-
-
-
- )}
);
diff --git a/apps/desktop/src/routes/auth/login-key.tsx b/apps/desktop/src/routes/auth/login-key.tsx
index 871d26de..3a65e074 100644
--- a/apps/desktop/src/routes/auth/login-key.tsx
+++ b/apps/desktop/src/routes/auth/login-key.tsx
@@ -50,7 +50,7 @@ export function LoginWithKey() {
return (
-
+
Enter your Private Key
diff --git a/apps/desktop/src/routes/auth/login-nsecbunker.tsx b/apps/desktop/src/routes/auth/login-nsecbunker.tsx
index 4679ab76..20480e3e 100644
--- a/apps/desktop/src/routes/auth/login-nsecbunker.tsx
+++ b/apps/desktop/src/routes/auth/login-nsecbunker.tsx
@@ -66,7 +66,7 @@ export function LoginWithNsecbunker() {
return (
-
+
Enter your nsecbunker token
diff --git a/apps/desktop/src/routes/auth/login-oauth.tsx b/apps/desktop/src/routes/auth/login-oauth.tsx
index 87d88496..228932fa 100644
--- a/apps/desktop/src/routes/auth/login-oauth.tsx
+++ b/apps/desktop/src/routes/auth/login-oauth.tsx
@@ -128,9 +128,9 @@ export function LoginWithOAuth() {
return (
-
+
-
Enter your NIP-05 address
+ Enter your Nostr Address