Skip to content

Commit

Permalink
Merge pull request #169 from lumehq/v4/onboarding
Browse files Browse the repository at this point in the history
Add basic onboarding dialog to v4
  • Loading branch information
reyamir authored Mar 6, 2024
2 parents 86183d7 + e93cbf7 commit 32d770e
Show file tree
Hide file tree
Showing 20 changed files with 447 additions and 717 deletions.
1 change: 1 addition & 0 deletions apps/desktop2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@lume/utils": "workspace:^",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-popover": "^1.0.7",
"@tanstack/query-sync-storage-persister": "^5.24.1",
Expand Down
25 changes: 8 additions & 17 deletions apps/desktop2/src/components/accounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { User } from "@lume/ui";
import { useNavigate, useParams, useSearch } from "@tanstack/react-router";
import { useEffect, useState } from "react";
import * as Popover from "@radix-ui/react-popover";
import { Link } from "@tanstack/react-router";
import { useTranslation } from "react-i18next";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { BackupDialog } from "./backup";
import { LoginDialog } from "./login";

export function Accounts() {
const ark = useArk();
Expand Down Expand Up @@ -63,7 +63,6 @@ function Active({ pubkey }: { pubkey: string }) {
const [open, setOpen] = useState(true);
// @ts-ignore, magic !!!
const { guest } = useSearch({ strict: false });
const { t } = useTranslation();

if (guest) {
return (
Expand All @@ -84,25 +83,17 @@ function Active({ pubkey }: { pubkey: string }) {
side="bottom"
>
<div>
<h1 className="mb-1 font-semibold">You're using guest account</h1>
<h1 className="mb-1 font-semibold">
You're using random account
</h1>
<p className="text-sm text-neutral-500 dark:text-neutral-600">
You can continue by claim and backup this account, or you can
import your own account key.
import your own account.
</p>
</div>
<div className="flex flex-col gap-2">
<Link
to="/backup"
className="inline-flex h-9 w-full items-center justify-center rounded-lg bg-white text-sm font-medium leading-tight text-neutral-900 hover:bg-neutral-100"
>
Claim & Backup
</Link>
<Link
to="/login"
className="inline-flex h-9 w-full items-center justify-center rounded-lg bg-neutral-900 text-sm font-medium leading-tight text-neutral-100 hover:bg-neutral-800"
>
{t("welcome.login")}
</Link>
<BackupDialog />
<LoginDialog />
</div>
<Popover.Arrow className="fill-black dark:fill-white" />
</Popover.Content>
Expand Down
121 changes: 121 additions & 0 deletions apps/desktop2/src/components/backup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { ArrowRightIcon, CancelIcon } from "@lume/icons";
import * as Dialog from "@radix-ui/react-dialog";
import { Link, useParams } from "@tanstack/react-router";
import { invoke } from "@tauri-apps/api/core";
import { useState } from "react";
import { toast } from "sonner";

export function BackupDialog() {
// @ts-ignore, magic!!!
const { account } = useParams({ strict: false });

const [key, setKey] = useState(null);
const [passphase, setPassphase] = useState("");
const [loading, setLoading] = useState(false);

const encryptKey = async () => {
try {
setLoading(true);

const encrypted: string = await invoke("get_encrypted_key", {
npub: account,
password: passphase,
});

if (encrypted) {
setKey(encrypted);
}

setLoading(false);
} catch (e) {
setLoading(false);
toast.error(String(e));
}
};

return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-full items-center justify-center rounded-lg bg-neutral-900 text-sm font-medium leading-tight text-neutral-100 hover:bg-neutral-800"
>
Claim & Backup
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/30 backdrop-blur dark:bg-white/30" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Dialog.Close className="absolute right-5 top-5 flex w-12 flex-col items-center justify-center gap-1 text-white">
<CancelIcon className="size-8" />
<span className="text-sm font-medium uppercase text-neutral-400 dark:text-neutral-600">
Esc
</span>
</Dialog.Close>
<div className="relative flex h-min w-full max-w-xl flex-col gap-8 rounded-xl bg-white p-5 dark:bg-black">
<div className="flex flex-col">
<h3 className="text-lg font-semibold">
This is your account key
</h3>
<p>
It's use for login to Lume or other Nostr clients. You will lost
access to your account if you lose this key.
</p>
</div>
<div className="flex flex-col gap-5">
<div className="flex flex-col gap-2">
<label htmlFor="nsec">Set a passphase to secure your key</label>
<div className="relative">
<input
name="passphase"
type="password"
value={passphase}
onChange={(e) => 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"
/>
</div>
</div>
{key ? (
<div className="flex flex-col gap-2">
<label htmlFor="nsec">
Copy this key and keep it in safe place
</label>
<input
name="nsec"
type="text"
value={key}
readOnly
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"
/>
</div>
) : null}
</div>
<div className="flex flex-col gap-3">
{!key ? (
<button
type="button"
onClick={encryptKey}
disabled={loading}
className="inline-flex h-11 w-full items-center justify-between gap-1.5 rounded-lg bg-blue-500 px-5 font-medium text-white hover:bg-blue-600"
>
<div className="size-5" />
<div>Submit</div>
<ArrowRightIcon className="size-5" />
</button>
) : (
<Link
to="/$account/home"
params={{ account }}
search={{ guest: false }}
className="inline-flex h-11 w-full items-center justify-center gap-1.5 rounded-lg bg-blue-500 px-5 font-medium text-white hover:bg-blue-600"
>
I've safely store my account key
</Link>
)}
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
120 changes: 120 additions & 0 deletions apps/desktop2/src/components/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { useArk } from "@lume/ark";
import { ArrowRightIcon, CancelIcon } from "@lume/icons";
import * as Dialog from "@radix-ui/react-dialog";
import { useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import { toast } from "sonner";

export function LoginDialog() {
const ark = useArk();
const navigate = useNavigate();

const [nsec, setNsec] = useState("");
const [passphase, setPassphase] = useState("");
const [loading, setLoading] = useState(false);

const login = async () => {
try {
setLoading(true);

const save = await ark.save_account(nsec, passphase);

if (save) {
navigate({ to: "/", search: { guest: false } });
}
} catch (e) {
setLoading(false);
toast.error(String(e));
}
};

return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-full items-center justify-center rounded-lg bg-neutral-900 text-sm font-medium leading-tight text-neutral-100 hover:bg-neutral-800"
>
Add account
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/30 backdrop-blur dark:bg-white/30" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Dialog.Close className="absolute right-5 top-5 flex w-12 flex-col items-center justify-center gap-1 text-white">
<CancelIcon className="size-8" />
<span className="text-sm font-medium uppercase text-neutral-400 dark:text-neutral-600">
Esc
</span>
</Dialog.Close>
<div className="relative flex h-min w-full max-w-xl flex-col gap-8 rounded-xl bg-white p-5 dark:bg-black">
<div className="flex flex-col gap-1.5">
<h3 className="text-lg font-semibold">Add new account with</h3>
<div className="flex h-11 items-center overflow-hidden rounded-lg bg-neutral-100 p-1 dark:bg-neutral-900">
<button
type="button"
className="h-full flex-1 rounded-md bg-white text-sm font-medium dark:bg-black"
>
nsec
</button>
<button
type="button"
className="flex h-full flex-1 flex-col items-center justify-center rounded-md text-sm font-medium"
>
<span className="leading-tight">nsecBunker</span>
<span className="text-xs font-normal leading-tight text-neutral-700 dark:text-neutral-300">
coming soon
</span>
</button>
<button
type="button"
className="flex h-full flex-1 flex-col items-center justify-center rounded-md text-sm font-medium"
>
<span className="leading-tight">Address</span>
<span className="text-xs font-normal leading-tight text-neutral-700 dark:text-neutral-300">
coming soon
</span>
</button>
</div>
</div>
<div className="flex flex-col gap-5">
<div className="flex flex-col gap-2">
<label htmlFor="nsec">
Enter sign in key start with nsec or ncrypto
</label>
<input
name="nsec"
type="text"
value={nsec}
onChange={(e) => setNsec(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"
/>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="nsec">Passphase (optional)</label>
<input
name="passphase"
type="password"
value={passphase}
onChange={(e) => 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"
/>
</div>
</div>
<div className="flex flex-col gap-3">
<button
type="button"
onClick={login}
className="inline-flex h-11 w-full items-center justify-between gap-1.5 rounded-lg bg-blue-500 px-5 font-medium text-white hover:bg-blue-600"
>
<div className="size-5" />
<div>Add account</div>
<ArrowRightIcon className="size-5" />
</button>
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
7 changes: 7 additions & 0 deletions apps/desktop2/src/routes/$account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
HomeFilledIcon,
HomeIcon,
HorizontalDotsIcon,
SettingsIcon,
SpaceFilledIcon,
SpaceIcon,
} from "@lume/icons";
Expand Down Expand Up @@ -43,6 +44,12 @@ function App() {
<ComposeFilledIcon className="size-4" />
New post
</button>
<button
type="button"
className="inline-flex size-8 items-center justify-center rounded-full bg-neutral-200 text-neutral-800 hover:bg-neutral-400 dark:bg-neutral-800 dark:text-neutral-200 dark:hover:bg-neutral-600"
>
<HorizontalDotsIcon className="size-5" />
</button>
</div>
</div>
<Box>
Expand Down
20 changes: 0 additions & 20 deletions apps/desktop2/src/routes/backup.lazy.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion apps/desktop2/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router";
import { useState } from "react";

export const Route = createFileRoute("/")({
beforeLoad: async ({ location, context }) => {
beforeLoad: async ({ context }) => {
const ark = context.ark;
const accounts = await ark.get_all_accounts();

Expand Down
14 changes: 0 additions & 14 deletions apps/desktop2/src/routes/login.tsx

This file was deleted.

Loading

0 comments on commit 32d770e

Please sign in to comment.