From 6e489f1c49519cb6089d9915d41c07d1cadf24b8 Mon Sep 17 00:00:00 2001 From: reya Date: Mon, 25 Mar 2024 14:22:06 +0700 Subject: [PATCH] wip: onboarding --- apps/desktop2/package.json | 2 + apps/desktop2/src/app.css | 4 +- .../src/components/avatarUploader.tsx | 46 +++++ apps/desktop2/src/routes/$account.home.tsx | 4 - apps/desktop2/src/routes/$account.tsx | 2 +- apps/desktop2/src/routes/auth.lazy.tsx | 16 ++ apps/desktop2/src/routes/auth/create.tsx | 25 +++ .../src/routes/auth/create/index.lazy.tsx | 108 ------------ .../src/routes/auth/create/managed.lazy.tsx | 5 - .../src/routes/auth/create/self.lazy.tsx | 161 ------------------ apps/desktop2/src/routes/auth/import.lazy.tsx | 101 ----------- .../desktop2/src/routes/auth/privkey.lazy.tsx | 102 +++++++++++ apps/desktop2/src/routes/auth/remote.lazy.tsx | 9 + apps/desktop2/src/routes/index.tsx | 5 +- apps/desktop2/src/routes/landing/index.tsx | 75 +++++--- apps/desktop2/src/routes/newsfeed.lazy.tsx | 14 +- packages/ark/src/ark.ts | 2 +- packages/icons/index.ts | 2 + packages/icons/src/key.tsx | 13 ++ packages/icons/src/remote.tsx | 13 ++ pnpm-lock.yaml | 6 + src-tauri/src/nostr/keys.rs | 6 +- 22 files changed, 299 insertions(+), 422 deletions(-) create mode 100644 apps/desktop2/src/components/avatarUploader.tsx create mode 100644 apps/desktop2/src/routes/auth.lazy.tsx create mode 100644 apps/desktop2/src/routes/auth/create.tsx delete mode 100644 apps/desktop2/src/routes/auth/create/index.lazy.tsx delete mode 100644 apps/desktop2/src/routes/auth/create/managed.lazy.tsx delete mode 100644 apps/desktop2/src/routes/auth/create/self.lazy.tsx delete mode 100644 apps/desktop2/src/routes/auth/import.lazy.tsx create mode 100644 apps/desktop2/src/routes/auth/privkey.lazy.tsx create mode 100644 apps/desktop2/src/routes/auth/remote.lazy.tsx create mode 100644 packages/icons/src/key.tsx create mode 100644 packages/icons/src/remote.tsx diff --git a/apps/desktop2/package.json b/apps/desktop2/package.json index 8be687bb..853e4e7b 100644 --- a/apps/desktop2/package.json +++ b/apps/desktop2/package.json @@ -24,11 +24,13 @@ "@tanstack/react-router": "^1.20.0", "i18next": "^23.10.1", "i18next-resources-to-backend": "^1.2.0", + "minidenticons": "^4.2.1", "nanoid": "^5.0.6", "nostr-tools": "^2.3.1", "react": "^18.2.0", "react-currency-input-field": "^3.8.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.51.0", "react-hotkeys-hook": "^4.5.0", "react-i18next": "^14.1.0", "slate": "^0.102.0", diff --git a/apps/desktop2/src/app.css b/apps/desktop2/src/app.css index da5e45e1..0d1730e6 100644 --- a/apps/desktop2/src/app.css +++ b/apps/desktop2/src/app.css @@ -7,11 +7,11 @@ } *::-webkit-scrollbar-track { - @apply bg-white dark:bg-black; + @apply bg-transparent; } *::-webkit-scrollbar-thumb { - @apply bg-black dark:bg-white rounded; + @apply rounded bg-black dark:bg-white; } @layer utilities { diff --git a/apps/desktop2/src/components/avatarUploader.tsx b/apps/desktop2/src/components/avatarUploader.tsx new file mode 100644 index 00000000..0cd2714e --- /dev/null +++ b/apps/desktop2/src/components/avatarUploader.tsx @@ -0,0 +1,46 @@ +import { useArk } from "@lume/ark"; +import { LoaderIcon } from "@lume/icons"; +import { Dispatch, SetStateAction, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; + +export function AvatarUploader({ + setPicture, +}: { + setPicture: Dispatch>; +}) { + const ark = useArk(); + + const [t] = useTranslation(); + const [loading, setLoading] = useState(false); + + const uploadAvatar = async () => { + // start loading + setLoading(true); + try { + const image = await ark.upload(); + setPicture(image); + } catch (e) { + toast.error(String(e)); + } + + // stop loading + setLoading(false); + }; + + return ( + + ) : ( + t("user.avatarButton") + )} + + ); +} diff --git a/apps/desktop2/src/routes/$account.home.tsx b/apps/desktop2/src/routes/$account.home.tsx index ee372b17..e6e4b243 100644 --- a/apps/desktop2/src/routes/$account.home.tsx +++ b/apps/desktop2/src/routes/$account.home.tsx @@ -7,7 +7,6 @@ import { UnlistenFn } from "@tauri-apps/api/event"; import { getCurrent } from "@tauri-apps/api/window"; import { useEffect, useRef, useState } from "react"; import { VList, VListHandle } from "virtua"; -import { useHotkeys } from "react-hotkeys-hook"; export const Route = createFileRoute("/$account/home")({ component: Screen, @@ -27,9 +26,6 @@ function Screen() { const [selectedIndex, setSelectedIndex] = useState(-1); const [isScroll, setIsScroll] = useState(false); - useHotkeys("left", () => goLeft()); - useHotkeys("right", () => goRight()); - const goLeft = () => { const prevIndex = Math.max(selectedIndex - 1, 0); setSelectedIndex(prevIndex); diff --git a/apps/desktop2/src/routes/$account.tsx b/apps/desktop2/src/routes/$account.tsx index 391a26c9..52231828 100644 --- a/apps/desktop2/src/routes/$account.tsx +++ b/apps/desktop2/src/routes/$account.tsx @@ -1,4 +1,4 @@ -import { ComposeFilledIcon, HorizontalDotsIcon, PlusIcon } from "@lume/icons"; +import { ComposeFilledIcon, PlusIcon } from "@lume/icons"; import { Outlet, createFileRoute } from "@tanstack/react-router"; import { cn } from "@lume/utils"; import { Accounts } from "@/components/accounts"; diff --git a/apps/desktop2/src/routes/auth.lazy.tsx b/apps/desktop2/src/routes/auth.lazy.tsx new file mode 100644 index 00000000..d7a0cd4e --- /dev/null +++ b/apps/desktop2/src/routes/auth.lazy.tsx @@ -0,0 +1,16 @@ +import { Box, Container } from "@lume/ui"; +import { Outlet, createLazyFileRoute } from "@tanstack/react-router"; + +export const Route = createLazyFileRoute("/auth")({ + component: Screen, +}); + +function Screen() { + return ( + + + + + + ); +} diff --git a/apps/desktop2/src/routes/auth/create.tsx b/apps/desktop2/src/routes/auth/create.tsx new file mode 100644 index 00000000..1a2b1399 --- /dev/null +++ b/apps/desktop2/src/routes/auth/create.tsx @@ -0,0 +1,25 @@ +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/create/index.lazy.tsx b/apps/desktop2/src/routes/auth/create/index.lazy.tsx deleted file mode 100644 index fed2597e..00000000 --- a/apps/desktop2/src/routes/auth/create/index.lazy.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { LoaderIcon } from "@lume/icons"; -import { cn } from "@lume/utils"; -import { createLazyFileRoute, useNavigate } from "@tanstack/react-router"; -import { useState } from "react"; -import { useTranslation } from "react-i18next"; - -export const Route = createLazyFileRoute("/auth/create/")({ - component: Screen, -}); - -function Screen() { - const navigate = useNavigate(); - - const [t] = useTranslation(); - const [method, setMethod] = useState<"self" | "managed">("self"); - const [loading, setLoading] = useState(false); - - const next = () => { - setLoading(true); - - if (method === "self") { - navigate({ to: "/auth/create/self" }); - } else { - navigate({ to: "/auth/create/managed" }); - } - }; - - return ( -
-
-
-

{t("signup.title")}

-

- {t("signup.subtitle")} -

-
-
- - -
- - {method === "managed" ? ( -
-

- Attention: -

-

- You're chosing Managed by Provider, this feature still in - "Beta". -

-

- Some functions still missing or not work as expected, you - shouldn't create your main account with this method -

- - Learn more - -
- ) : null} -
-
-
-
- ); -} diff --git a/apps/desktop2/src/routes/auth/create/managed.lazy.tsx b/apps/desktop2/src/routes/auth/create/managed.lazy.tsx deleted file mode 100644 index 9258c283..00000000 --- a/apps/desktop2/src/routes/auth/create/managed.lazy.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { createLazyFileRoute } from '@tanstack/react-router' - -export const Route = createLazyFileRoute('/auth/create/managed')({ - component: () =>
Hello /auth/create/managed!
-}) \ No newline at end of file diff --git a/apps/desktop2/src/routes/auth/create/self.lazy.tsx b/apps/desktop2/src/routes/auth/create/self.lazy.tsx deleted file mode 100644 index 547045f1..00000000 --- a/apps/desktop2/src/routes/auth/create/self.lazy.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { useArk } from "@lume/ark"; -import { CheckIcon, EyeOffIcon, EyeOnIcon, LoaderIcon } from "@lume/icons"; -import { Keys } from "@lume/types"; -import * as Checkbox from "@radix-ui/react-checkbox"; -import { createLazyFileRoute, useNavigate } from "@tanstack/react-router"; -import { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; - -export const Route = createLazyFileRoute("/auth/create/self")({ - component: Create, -}); - -function Create() { - const ark = useArk(); - const navigate = useNavigate(); - - const [t] = useTranslation(); - const [loading, setLoading] = useState(false); - const [showKey, setShowKey] = useState(false); - const [confirm, setConfirm] = useState({ c1: false, c2: false, c3: false }); - const [keys, setKeys] = useState(null); - - const submit = async () => { - setLoading(true); - try { - await ark.save_account(keys); - navigate({ - to: "/$account/home/local", - params: { account: keys.npub }, - search: { onboarding: true }, - replace: true, - }); - } catch (e) { - setLoading(false); - toast.error(e); - } - }; - - useEffect(() => { - async function genKeys() { - const res = await ark.create_keys(); - setKeys(res); - } - genKeys(); - }, []); - - return ( -
-
-
-

- {t("signupWithSelfManage.title")} -

-

- {t("signupWithSelfManage.subtitle")} -

-
-
-
-
- {keys ? ( - - ) : null} - -
-
-
- - setConfirm((state) => ({ ...state, c1: !state.c1 })) - } - className="flex size-7 appearance-none items-center justify-center rounded-lg bg-neutral-100 outline-none dark:bg-neutral-900" - id="confirm1" - > - - - - - -
-
- - setConfirm((state) => ({ ...state, c3: !state.c3 })) - } - className="flex size-7 appearance-none items-center justify-center rounded-lg bg-neutral-100 outline-none dark:bg-neutral-900" - id="confirm3" - > - - - - - -
-
- - setConfirm((state) => ({ ...state, c2: !state.c2 })) - } - className="flex size-7 appearance-none items-center justify-center rounded-lg bg-neutral-100 outline-none dark:bg-neutral-900" - id="confirm2" - > - - - - - -
-
-
- -
-
-
- ); -} diff --git a/apps/desktop2/src/routes/auth/import.lazy.tsx b/apps/desktop2/src/routes/auth/import.lazy.tsx deleted file mode 100644 index 3de95d31..00000000 --- a/apps/desktop2/src/routes/auth/import.lazy.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { useArk } from "@lume/ark"; -import { LoaderIcon } from "@lume/icons"; -import { createLazyFileRoute, useNavigate } from "@tanstack/react-router"; -import { invoke } from "@tauri-apps/api/core"; -import { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; - -export const Route = createLazyFileRoute("/auth/import")({ - component: Import, -}); - -function Import() { - const ark = useArk(); - const navigate = useNavigate(); - - const [t] = useTranslation(); - const [key, setKey] = useState(""); - const [password, setPassword] = useState(""); - const [loading, setLoading] = useState(false); - - const submit = async () => { - if (!key.startsWith("nsec1")) return; - if (key.length < 30) return; - - setLoading(true); - - try { - const npub: string = await invoke("get_public_key", { nsec: key }); - await ark.save_account({ - npub, - nsec: key, - }); - navigate({ - to: "/$account/home/local", - params: { account: npub }, - search: { onboarding: true }, - replace: true, - }); - } catch (e) { - setLoading(false); - toast.error(e); - } - }; - - const isNip05 = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(key); - const isNip49 = key.startsWith("ncryptsec"); - - return ( -
-
-
-

{t("login.title")}

-

- {t("login.subtitle")} -

-
-
-
-
- setKey(e.target.value)} - className="h-12 w-full resize-none rounded-xl border-transparent bg-neutral-100 pl-3 pr-10 placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-100 dark:bg-neutral-900 dark:focus:ring-blue-900" - /> -
- {isNip05 || isNip49 ? ( -
- - setPassword(e.target.value)} - className="h-12 w-full resize-none rounded-xl border-transparent bg-neutral-100 pl-3 pr-10 placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-100 dark:bg-neutral-900 dark:focus:ring-blue-900" - /> -
- ) : null} -
- -
-
-
- ); -} diff --git a/apps/desktop2/src/routes/auth/privkey.lazy.tsx b/apps/desktop2/src/routes/auth/privkey.lazy.tsx new file mode 100644 index 00000000..da74390b --- /dev/null +++ b/apps/desktop2/src/routes/auth/privkey.lazy.tsx @@ -0,0 +1,102 @@ +import { useArk } from "@lume/ark"; +import { LoaderIcon } from "@lume/icons"; +import { createLazyFileRoute, useNavigate } from "@tanstack/react-router"; +import { invoke } from "@tauri-apps/api/core"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; + +export const Route = createLazyFileRoute("/auth/privkey")({ + component: Screen, +}); + +function Screen() { + const ark = useArk(); + const navigate = useNavigate(); + + const [t] = useTranslation(); + const [key, setKey] = useState(""); + const [password, setPassword] = useState(""); + const [loading, setLoading] = useState(false); + + const submit = async () => { + if (!key.startsWith("nsec1")) + return toast.warning( + "You need to enter a valid private key starts with nsec or ncryptsec", + ); + if (key.length < 30) + return toast.warning("You need to enter a valid private key"); + + setLoading(true); + + try { + const npub = await ark.save_account(key, password); + navigate({ + to: "/$account/home", + params: { account: npub }, + search: { onboarding: true }, + replace: true, + }); + } catch (e) { + toast.error(e); + } + + setLoading(false); + }; + + return ( +
+
+
+

{t("login.title")}

+

+ {t("login.subtitle")} +

+
+
+
+
+ + setKey(e.target.value)} + className="h-11 w-full 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" + /> +
+
+ + setPassword(e.target.value)} + className="h-11 w-full 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" + /> +
+
+ +
+
+
+ ); +} diff --git a/apps/desktop2/src/routes/auth/remote.lazy.tsx b/apps/desktop2/src/routes/auth/remote.lazy.tsx new file mode 100644 index 00000000..74bd94bd --- /dev/null +++ b/apps/desktop2/src/routes/auth/remote.lazy.tsx @@ -0,0 +1,9 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; + +export const Route = createLazyFileRoute("/auth/remote")({ + component: Screen, +}); + +function Screen() { + return
#todo
; +} diff --git a/apps/desktop2/src/routes/index.tsx b/apps/desktop2/src/routes/index.tsx index 2769ad37..9778a9e2 100644 --- a/apps/desktop2/src/routes/index.tsx +++ b/apps/desktop2/src/routes/index.tsx @@ -13,11 +13,8 @@ export const Route = createFileRoute("/")({ switch (accounts.length) { // Guest account case 0: - const guest = await ark.create_guest_account(); throw redirect({ - to: "/$account/home", - params: { account: guest }, - search: { guest: true }, + to: "/landing", replace: true, }); // Only 1 account, skip account selection screen diff --git a/apps/desktop2/src/routes/landing/index.tsx b/apps/desktop2/src/routes/landing/index.tsx index 4ad9f6c4..1fe75584 100644 --- a/apps/desktop2/src/routes/landing/index.tsx +++ b/apps/desktop2/src/routes/landing/index.tsx @@ -1,3 +1,4 @@ +import { KeyIcon, RemoteIcon } from "@lume/icons"; import { Link, createFileRoute } from "@tanstack/react-router"; import { useTranslation } from "react-i18next"; @@ -6,12 +7,16 @@ export const Route = createFileRoute("/landing/")({ }); function Screen() { - const context = Route.useRouteContext(); const { t } = useTranslation(); + const context = Route.useRouteContext(); return ( -
-
+
+
+
@@ -19,39 +24,59 @@ function Screen() { src={`/heading-${context.locale}.png`} srcSet={`/heading-${context.locale}@2x.png 2x`} alt="lume" - className="w-2/3" + className="w-3/4 xl:w-2/3" /> -

+

{t("welcome.title")}

-
+
{t("welcome.signup")} - - {t("welcome.login")} - +
+
+ Or +
+
+
+ + + Continue with Remote Signing + + + + Continue with Private Key + +
-
-

- {t("welcome.footer")}{" "} - - here - -

-
+
+
+ ); diff --git a/apps/desktop2/src/routes/newsfeed.lazy.tsx b/apps/desktop2/src/routes/newsfeed.lazy.tsx index 681217d7..8521e973 100644 --- a/apps/desktop2/src/routes/newsfeed.lazy.tsx +++ b/apps/desktop2/src/routes/newsfeed.lazy.tsx @@ -40,11 +40,13 @@ export function Screen() { - {isLoading || isRefetching ? ( + {isLoading ? (
- +
- ) : !data.length ? ( + ) : !data ? (
@@ -61,13 +63,11 @@ export function Screen() { )}
- {!isLoading && hasNextPage ? ( + {data?.length && hasNextPage ? (