From cbbf5eaf5042e6a5e7e0724e0d2b30340c4a0a91 Mon Sep 17 00:00:00 2001 From: reya Date: Thu, 28 Mar 2024 15:12:43 +0700 Subject: [PATCH] feat: add create account flow --- apps/desktop2/package.json | 1 + .../src/components/avatarUploader.tsx | 20 +- apps/desktop2/src/routes/auth/create.tsx | 25 --- apps/desktop2/src/routes/auth/new/backup.tsx | 186 ++++++++++++++++++ apps/desktop2/src/routes/auth/new/profile.tsx | 147 ++++++++++++++ .../desktop2/src/routes/auth/privkey.lazy.tsx | 97 +++++---- .../src/routes/auth/settings.lazy.tsx | 164 +++++++++++++++ apps/desktop2/src/routes/landing/index.tsx | 2 +- apps/desktop2/src/routes/newsfeed.lazy.tsx | 33 ++-- packages/ark/src/ark.ts | 44 +++++ packages/icons/src/check.tsx | 30 ++- packages/types/index.d.ts | 12 +- packages/ui/src/container.tsx | 27 ++- packages/utils/src/formater.ts | 15 ++ pnpm-lock.yaml | 30 +++ src-tauri/src/main.rs | 4 - src-tauri/src/nostr/keys.rs | 3 - src-tauri/src/nostr/metadata.rs | 26 ++- 18 files changed, 714 insertions(+), 152 deletions(-) delete mode 100644 apps/desktop2/src/routes/auth/create.tsx create mode 100644 apps/desktop2/src/routes/auth/new/backup.tsx create mode 100644 apps/desktop2/src/routes/auth/new/profile.tsx create mode 100644 apps/desktop2/src/routes/auth/settings.lazy.tsx diff --git a/apps/desktop2/package.json b/apps/desktop2/package.json index 853e4e7b..79c3591a 100644 --- a/apps/desktop2/package.json +++ b/apps/desktop2/package.json @@ -18,6 +18,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-switch": "^1.0.3", "@tanstack/query-sync-storage-persister": "^5.28.4", "@tanstack/react-query": "^5.28.4", "@tanstack/react-query-persist-client": "^5.28.4", diff --git a/apps/desktop2/src/components/avatarUploader.tsx b/apps/desktop2/src/components/avatarUploader.tsx index 0cd2714e..475d024f 100644 --- a/apps/desktop2/src/components/avatarUploader.tsx +++ b/apps/desktop2/src/components/avatarUploader.tsx @@ -1,17 +1,19 @@ import { useArk } from "@lume/ark"; import { LoaderIcon } from "@lume/icons"; -import { Dispatch, SetStateAction, useState } from "react"; -import { useTranslation } from "react-i18next"; +import { cn } from "@lume/utils"; +import { Dispatch, ReactNode, SetStateAction, useState } from "react"; import { toast } from "sonner"; export function AvatarUploader({ setPicture, + children, + className, }: { setPicture: Dispatch>; + children: ReactNode; + className?: string; }) { const ark = useArk(); - - const [t] = useTranslation(); const [loading, setLoading] = useState(false); const uploadAvatar = async () => { @@ -32,15 +34,9 @@ export function AvatarUploader({ - ) : ( - t("user.avatarButton") - )} + {loading ? : children} ); } diff --git a/apps/desktop2/src/routes/auth/create.tsx b/apps/desktop2/src/routes/auth/create.tsx deleted file mode 100644 index 1a2b1399..00000000 --- a/apps/desktop2/src/routes/auth/create.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { LoaderIcon } from "@lume/icons"; -import { createFileRoute } from "@tanstack/react-router"; - -export const Route = createFileRoute("/auth/create")({ - component: Screen, - loader: ({ context }) => { - return context.ark.create_keys(); - }, - pendingComponent: Pending, -}); - -function Screen() { - return
; -} - -function Pending() { - return ( -
- -

Creating account

-
- ); -} diff --git a/apps/desktop2/src/routes/auth/new/backup.tsx b/apps/desktop2/src/routes/auth/new/backup.tsx new file mode 100644 index 00000000..f9c71ff0 --- /dev/null +++ b/apps/desktop2/src/routes/auth/new/backup.tsx @@ -0,0 +1,186 @@ +import { displayNsec } from "@lume/utils"; +import { createFileRoute, useNavigate } from "@tanstack/react-router"; +import { invoke } from "@tauri-apps/api/core"; +import { writeText } from "@tauri-apps/plugin-clipboard-manager"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import * as Checkbox from "@radix-ui/react-checkbox"; +import { CheckIcon } from "@lume/icons"; + +export const Route = createFileRoute("/auth/new/backup")({ + component: Screen, +}); + +function Screen() { + // @ts-ignore, magic!!! + const { account } = Route.useSearch(); + const { t } = useTranslation(); + + const [key, setKey] = useState(null); + const [passphase, setPassphase] = useState(""); + const [copied, setCopied] = useState(false); + const [confirm, setConfirm] = useState({ c1: false, c2: false, c3: false }); + + const navigate = useNavigate(); + + const submit = async () => { + try { + if (key) { + if (!confirm.c1 || !confirm.c2 || !confirm.c3) { + return toast.warning("You need to confirm before continue"); + } else { + return navigate({ + to: "/auth/settings", + search: { account, new: true }, + }); + } + } + + const encrypted: string = await invoke("get_encrypted_key", { + npub: account, + password: passphase, + }); + + setKey(encrypted); + } catch (e) { + toast.error(String(e)); + } + }; + + const copyKey = async () => { + try { + await writeText(key); + setCopied(true); + } catch (e) { + toast.error(e); + } + }; + + return ( +
+
+

Backup your sign in keys

+

+ It's use for login to Lume or other Nostr clients. You will lost + access to your account if you lose this key. +

+
+
+
+ +
+ setPassphase(e.target.value)} + className="h-11 w-full resize-none rounded-lg border-transparent bg-neutral-100 placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-100 dark:bg-neutral-900 dark:focus:ring-blue-900" + /> +
+
+ {key ? ( + <> +
+ +
+ + +
+
+
+
Before you continue:
+
+
+ + setConfirm((state) => ({ ...state, c1: !state.c1 })) + } + className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-neutral-900" + id="confirm1" + > + + + + + +
+
+ + setConfirm((state) => ({ ...state, c2: !state.c2 })) + } + className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-neutral-900" + id="confirm2" + > + + + + + +
+
+ + setConfirm((state) => ({ ...state, c3: !state.c3 })) + } + className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-neutral-900" + id="confirm3" + > + + + + + +
+
+
+ + ) : null} +
+ +
+
+
+ ); +} diff --git a/apps/desktop2/src/routes/auth/new/profile.tsx b/apps/desktop2/src/routes/auth/new/profile.tsx new file mode 100644 index 00000000..e0aef933 --- /dev/null +++ b/apps/desktop2/src/routes/auth/new/profile.tsx @@ -0,0 +1,147 @@ +import { AvatarUploader } from "@/components/avatarUploader"; +import { useArk } from "@lume/ark"; +import { LoaderIcon, PlusIcon } from "@lume/icons"; +import { Metadata } from "@lume/types"; +import { createFileRoute, useNavigate } from "@tanstack/react-router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; + +export const Route = createFileRoute("/auth/new/profile")({ + component: Screen, + loader: ({ context }) => { + return context.ark.create_keys(); + }, +}); + +function Screen() { + const ark = useArk(); + const keys = Route.useLoaderData(); + const navigate = useNavigate(); + + const { t } = useTranslation(); + const { register, handleSubmit } = useForm(); + + const [picture, setPicture] = useState(""); + const [loading, setLoading] = useState(false); + + const onSubmit = async (data: { + name: string; + about: string; + website: string; + }) => { + setLoading(true); + + try { + // Save account keys + const save = await ark.save_account(keys.nsec); + + // Then create profile + if (save) { + const profile: Metadata = { ...data, picture }; + const eventId = await ark.create_profile(profile); + + if (eventId) { + navigate({ + to: "/auth/new/backup", + search: { account: keys.npub }, + replace: true, + }); + } + } + } catch (e) { + setLoading(false); + toast.error(String(e)); + } + }; + + return ( +
+
+

Let's set up your profile.

+
+
+
+ {picture ? ( + avatar + ) : null} + + + +
+
+
+
+ + +
+
+ + +
+
+ +