From d527078d5c7f80f7d8fab621d979be3f1ab2bc59 Mon Sep 17 00:00:00 2001 From: reya Date: Wed, 24 Jan 2024 15:08:55 +0700 Subject: [PATCH 1/8] feat: redesign column header --- packages/ark/src/components/column/header.tsx | 141 +++++++++--------- .../src/components/column/interestModal.tsx | 4 +- packages/ark/src/components/column/live.tsx | 8 +- packages/icons/index.ts | 1 + packages/icons/src/arrowUp.tsx | 24 +++ packages/icons/src/chevronDown.tsx | 2 +- packages/icons/src/chevronUp.tsx | 42 +++--- packages/lume-column-timeline/src/index.tsx | 2 +- 8 files changed, 123 insertions(+), 101 deletions(-) create mode 100644 packages/icons/src/arrowUp.tsx diff --git a/packages/ark/src/components/column/header.tsx b/packages/ark/src/components/column/header.tsx index 961033bb..c7334123 100644 --- a/packages/ark/src/components/column/header.tsx +++ b/packages/ark/src/components/column/header.tsx @@ -1,5 +1,5 @@ import { - HorizontalDotsIcon, + ChevronDownIcon, MoveLeftIcon, MoveRightIcon, RefreshIcon, @@ -43,78 +43,73 @@ export function ColumnHeader({ }; return ( -
-
-
- {icon ? icon : } -
{title}
-
+ +
+ +
+
+
{title}
+ +
+
+
+ + + + + + {queryKey?.[0] === "foryou-9998" ? ( + + + + ) : null} + + + + + + + + + + + +
-
- - - - - - - - - - {queryKey?.[0] === "foryou-9998" ? ( - - - - ) : null} - - - - - - - - - - - - - -
-
+ ); } diff --git a/packages/ark/src/components/column/interestModal.tsx b/packages/ark/src/components/column/interestModal.tsx index b9f29441..86271b21 100644 --- a/packages/ark/src/components/column/interestModal.tsx +++ b/packages/ark/src/components/column/interestModal.tsx @@ -56,7 +56,7 @@ export function InterestModal({ @@ -64,7 +64,7 @@ export function InterestModal({ children ) : ( <> - + Edit interest )} diff --git a/packages/ark/src/components/column/live.tsx b/packages/ark/src/components/column/live.tsx index ed46ab3f..aa0f7fbd 100644 --- a/packages/ark/src/components/column/live.tsx +++ b/packages/ark/src/components/column/live.tsx @@ -1,4 +1,4 @@ -import { ChevronUpIcon } from "@lume/icons"; +import { ArrowUpIcon, ChevronUpIcon } from "@lume/icons"; import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk"; import { useEffect, useState } from "react"; import { useArk } from "../../hooks/useArk"; @@ -34,14 +34,14 @@ export function ColumnLiveWidget({ if (!events.length) return null; return ( -
+
); diff --git a/packages/icons/index.ts b/packages/icons/index.ts index ee2e00a1..96c9d564 100644 --- a/packages/icons/index.ts +++ b/packages/icons/index.ts @@ -111,3 +111,4 @@ export * from "./src/foryou"; export * from "./src/editInterest"; export * from "./src/newColumn"; export * from "./src/searchFilled"; +export * from "./src/arrowUp"; diff --git a/packages/icons/src/arrowUp.tsx b/packages/icons/src/arrowUp.tsx new file mode 100644 index 00000000..b4f26528 --- /dev/null +++ b/packages/icons/src/arrowUp.tsx @@ -0,0 +1,24 @@ +import { SVGProps } from "react"; + +export function ArrowUpIcon( + props: JSX.IntrinsicAttributes & SVGProps, +) { + return ( + + + + ); +} diff --git a/packages/icons/src/chevronDown.tsx b/packages/icons/src/chevronDown.tsx index 9311f52d..b4d13eea 100644 --- a/packages/icons/src/chevronDown.tsx +++ b/packages/icons/src/chevronDown.tsx @@ -17,7 +17,7 @@ export function ChevronDownIcon( strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" - d="M8 10.14a20.36 20.36 0 003.702 3.893c.175.141.42.141.596 0A20.361 20.361 0 0016 10.14" + d="M6 9a30.618 30.618 0 005.49 5.817c.3.244.72.244 1.02 0A30.617 30.617 0 0018 9" /> ); diff --git a/packages/icons/src/chevronUp.tsx b/packages/icons/src/chevronUp.tsx index d8144fb6..4caceb20 100644 --- a/packages/icons/src/chevronUp.tsx +++ b/packages/icons/src/chevronUp.tsx @@ -1,22 +1,24 @@ -import { SVGProps } from 'react'; +import { SVGProps } from "react"; -export function ChevronUpIcon(props: JSX.IntrinsicAttributes & SVGProps) { - return ( - - - - ); +export function ChevronUpIcon( + props: JSX.IntrinsicAttributes & SVGProps, +) { + return ( + + + + ); } diff --git a/packages/lume-column-timeline/src/index.tsx b/packages/lume-column-timeline/src/index.tsx index 1cc4c821..87946a02 100644 --- a/packages/lume-column-timeline/src/index.tsx +++ b/packages/lume-column-timeline/src/index.tsx @@ -30,7 +30,7 @@ export function Timeline({ column }: { column: IColumn }) { id={column.id} queryKey={[colKey]} title="Timeline" - icon={} + icon={} /> {ark.account.contacts.length ? ( Date: Thu, 25 Jan 2024 08:14:25 +0700 Subject: [PATCH 2/8] feat: improve ui contrast --- apps/desktop/src/app.css | 4 ++ apps/desktop/src/routes/home/index.tsx | 2 +- .../ark/src/components/note/buttons/reply.tsx | 6 +-- packages/ark/src/components/note/menu.tsx | 33 ++++++++++----- .../ark/src/components/note/preview/image.tsx | 6 +-- packages/icons/src/download.tsx | 42 ++++++++++--------- packages/icons/src/relay.tsx | 12 +++--- packages/icons/src/reply.tsx | 14 ++++--- packages/ui/src/navigation.tsx | 8 +--- packages/ui/src/routes/event.tsx | 2 +- 10 files changed, 73 insertions(+), 56 deletions(-) diff --git a/apps/desktop/src/app.css b/apps/desktop/src/app.css index 3f9426fa..fcc160cb 100644 --- a/apps/desktop/src/app.css +++ b/apps/desktop/src/app.css @@ -12,6 +12,10 @@ .prose :where(iframe):not(:where([class~='not-prose'] *)) { @apply w-full h-auto mx-auto aspect-video; } + + .shadow-toolbar { + box-shadow: 0 0 #0000,0 0 #0000,0 8px 24px 0 rgba(0,0,0,.2),0 2px 8px 0 rgba(0,0,0,.08),inset 0 0 0 1px rgba(0,0,0,.2),inset 0 0 0 2px hsla(0,0%,100%,.14) + } } html { diff --git a/apps/desktop/src/routes/home/index.tsx b/apps/desktop/src/routes/home/index.tsx index 21f35982..b17904bf 100644 --- a/apps/desktop/src/routes/home/index.tsx +++ b/apps/desktop/src/routes/home/index.tsx @@ -104,7 +104,7 @@ export function HomeScreen() {
-
+
- Quick reply + View thread diff --git a/packages/ark/src/components/note/menu.tsx b/packages/ark/src/components/note/menu.tsx index 8564ba45..e3c87655 100644 --- a/packages/ark/src/components/note/menu.tsx +++ b/packages/ark/src/components/note/menu.tsx @@ -4,12 +4,14 @@ import { writeText } from "@tauri-apps/plugin-clipboard-manager"; import { nip19 } from "nostr-tools"; import { type EventPointer } from "nostr-tools/lib/types/nip19"; import { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { toast } from "sonner"; import { useNoteContext } from "./provider"; export function NoteMenu() { const event = useNoteContext(); + const navigate = useNavigate(); + const [open, setOpen] = useState(false); const copyID = async () => { @@ -56,12 +58,21 @@ export function NoteMenu() { - + + + + @@ -70,34 +81,34 @@ export function NoteMenu() { View profile - + @@ -106,7 +117,7 @@ export function NoteMenu() { diff --git a/packages/ark/src/components/note/preview/image.tsx b/packages/ark/src/components/note/preview/image.tsx index abb5d1ae..883ac33c 100644 --- a/packages/ark/src/components/note/preview/image.tsx +++ b/packages/ark/src/components/note/preview/image.tsx @@ -48,12 +48,12 @@ export function ImagePreview({ url }: { url: string }) {
diff --git a/packages/icons/src/download.tsx b/packages/icons/src/download.tsx index e8d39fee..2c676e64 100644 --- a/packages/icons/src/download.tsx +++ b/packages/icons/src/download.tsx @@ -1,22 +1,24 @@ -import { SVGProps } from 'react'; +import { SVGProps } from "react"; -export function DownloadIcon(props: JSX.IntrinsicAttributes & SVGProps) { - return ( - - - - ); +export function DownloadIcon( + props: JSX.IntrinsicAttributes & SVGProps, +) { + return ( + + + + ); } diff --git a/packages/icons/src/relay.tsx b/packages/icons/src/relay.tsx index b2b22d40..0e5b070f 100644 --- a/packages/icons/src/relay.tsx +++ b/packages/icons/src/relay.tsx @@ -6,13 +6,15 @@ export function RelayIcon(props: JSX.IntrinsicElements["svg"]) { height="24" fill="none" viewBox="0 0 25 24" - stroke="currentColor" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="2" {...props} > - + ); } diff --git a/packages/icons/src/reply.tsx b/packages/icons/src/reply.tsx index 58542430..19029885 100644 --- a/packages/icons/src/reply.tsx +++ b/packages/icons/src/reply.tsx @@ -1,18 +1,20 @@ export function ReplyIcon(props: JSX.IntrinsicElements["svg"]) { return ( - + ); } diff --git a/packages/ui/src/navigation.tsx b/packages/ui/src/navigation.tsx index 86e81401..b89d0eea 100644 --- a/packages/ui/src/navigation.tsx +++ b/packages/ui/src/navigation.tsx @@ -2,11 +2,11 @@ import { BellFilledIcon, BellIcon, ComposeFilledIcon, - ComposeIcon, DepotFilledIcon, DepotIcon, HomeFilledIcon, HomeIcon, + PlusIcon, SearchFilledIcon, SearchIcon, SettingsFilledIcon, @@ -44,11 +44,7 @@ export function Navigation() { : "bg-black/5 hover:bg-blue-500 dark:bg-white/5 dark:hover:bg-blue-500", )} > - {isEditorOpen ? ( - - ) : ( - - )} +
diff --git a/packages/ui/src/routes/event.tsx b/packages/ui/src/routes/event.tsx index 86cdf3df..9b3843e2 100644 --- a/packages/ui/src/routes/event.tsx +++ b/packages/ui/src/routes/event.tsx @@ -11,7 +11,7 @@ export function EventRoute() { return (
-
+

- 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. + 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/onboarding.tsx b/apps/desktop/src/routes/auth/onboarding.tsx index 75df18ff..033a0365 100644 --- a/apps/desktop/src/routes/auth/onboarding.tsx +++ b/apps/desktop/src/routes/auth/onboarding.tsx @@ -194,7 +194,7 @@ export function OnboardingScreen() {
- Not have API ? + Don't have an API key?
diff --git a/apps/desktop/src/routes/home/index.tsx b/apps/desktop/src/routes/home/index.tsx index b17904bf..2fc80ce4 100644 --- a/apps/desktop/src/routes/home/index.tsx +++ b/apps/desktop/src/routes/home/index.tsx @@ -10,7 +10,7 @@ import { useColumnContext } from "@lume/ark"; import { ArrowLeftIcon, ArrowRightIcon, - NewColumnIcon, + PlusIcon, PlusSquareIcon, } from "@lume/icons"; import { IColumn } from "@lume/types"; @@ -96,9 +96,9 @@ export function HomeScreen() { content: "", }) } - className="size-16 inline-flex items-center justify-center rounded-full bg-blue-500 hover:bg-blue-600 text-white" + className="size-16 inline-flex items-center justify-center hover:bg-neutral-100 dark:hover:bg-neutral-900 rounded-2xl" > - +
diff --git a/packages/ark/src/components/column/interestModal.tsx b/packages/ark/src/components/column/interestModal.tsx index 86271b21..e0a81691 100644 --- a/packages/ark/src/components/column/interestModal.tsx +++ b/packages/ark/src/components/column/interestModal.tsx @@ -40,7 +40,7 @@ export function InterestModal({ ); if (save) { - storage.interests.hashtags = hashtags; + storage.interests = { hashtags, users: [], words: [] }; await queryClient.refetchQueries({ queryKey }); } @@ -86,11 +86,8 @@ export function InterestModal({
- {TOPICS.map((topic, index) => ( -
+ {TOPICS.map((topic) => ( +
[...prev, result]); + if (result) { + setColumns((prev) => [...prev, result]); + vlistRef?.current.scrollToIndex(columns.length); + } }, []); const removeColumn = useCallback(async (id: number) => { diff --git a/packages/ark/src/components/note/buttons/repost.tsx b/packages/ark/src/components/note/buttons/repost.tsx index a4f6065d..8f5218a9 100644 --- a/packages/ark/src/components/note/buttons/repost.tsx +++ b/packages/ark/src/components/note/buttons/repost.tsx @@ -88,12 +88,12 @@ export function NoteRepost() { - + diff --git a/packages/lume-column-activity/package.json b/packages/lume-column-activity/package.json deleted file mode 100644 index 53880406..00000000 --- a/packages/lume-column-activity/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@columns/activity", - "version": "0.0.0", - "private": true, - "main": "./src/index.tsx", - "dependencies": { - "@lume/ark": "workspace:^", - "@lume/icons": "workspace:^", - "@lume/ui": "workspace:^", - "@lume/utils": "workspace:^", - "@nostr-dev-kit/ndk": "^2.3.3", - "@tanstack/react-query": "^5.17.19", - "react": "^18.2.0", - "react-router-dom": "^6.21.3", - "sonner": "^1.3.1", - "virtua": "^0.21.1" - }, - "devDependencies": { - "@lume/tailwindcss": "workspace:^", - "@lume/tsconfig": "workspace:^", - "@lume/types": "workspace:^", - "@types/react": "^18.2.48", - "tailwind": "^4.0.0", - "typescript": "^5.3.3" - } -} diff --git a/packages/lume-column-activity/src/index.tsx b/packages/lume-column-activity/src/index.tsx deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/lume-column-activity/tailwind.config.js b/packages/lume-column-activity/tailwind.config.js deleted file mode 100644 index 49c48c7a..00000000 --- a/packages/lume-column-activity/tailwind.config.js +++ /dev/null @@ -1,8 +0,0 @@ -import sharedConfig from "@lume/tailwindcss"; - -const config = { - content: ["./src/**/*.{js,ts,jsx,tsx}"], - presets: [sharedConfig], -}; - -export default config; diff --git a/packages/lume-column-activity/tsconfig.json b/packages/lume-column-activity/tsconfig.json deleted file mode 100644 index 34a32891..00000000 --- a/packages/lume-column-activity/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "@lume/tsconfig/base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/lume-column-default/src/index.tsx b/packages/lume-column-default/src/index.tsx index a55885ab..728aa267 100644 --- a/packages/lume-column-default/src/index.tsx +++ b/packages/lume-column-default/src/index.tsx @@ -14,19 +14,36 @@ export function Default({ column }: { column: IColumn }) { icon={} />
-
-
+
+ + +
+
+
group
-
+

Group Feeds

-

+

Collective of people you're interested in.

@@ -41,19 +58,21 @@ export function Default({ column }: { column: IColumn }) {
-
-
+
+
antenas
-
+

Antenas

-

+

Keep track to specific content.

diff --git a/packages/ui/src/translateRegisterModal.tsx b/packages/ui/src/translateRegisterModal.tsx index 883f05b1..e114cd8e 100644 --- a/packages/ui/src/translateRegisterModal.tsx +++ b/packages/ui/src/translateRegisterModal.tsx @@ -68,9 +68,9 @@ export function TranslateRegisterModal({ setAPIKey }) {

- Translate Service is provided by{" "} - nostr.wine, you need to - deposit at least 2,500 sats to use translate 50,000 characters + Translation Service is provided by{" "} + nostr.wine. Prices + start at 2,500 sats for 50,000 characters of translated text.

You can learn more about nostr.wine{" "} diff --git a/packages/ui/src/tutorial/manageColumn.tsx b/packages/ui/src/tutorial/manageColumn.tsx index 7a9320fa..111b9dcb 100644 --- a/packages/ui/src/tutorial/manageColumn.tsx +++ b/packages/ui/src/tutorial/manageColumn.tsx @@ -5,13 +5,9 @@ export function TutorialManageColumnScreen() {

- Lume is also provide simple way to customize column after creation. + Once a new column is created, you can click on the title in its header + to find options to customize it

-

- To customize each column, you - can go to header of each column -

-

Click to "Three Dots" icon

tutorial-3
-

Lume is column based, each column is each experience

+

Lume is column based, each column is its own experience.

- To create new column, you can - look into bottom right part of screen + To create a new column, you can + click on the "Plus" icon at bottom right corner of this window.

Click to "Plus" icon

Date: Thu, 25 Jan 2024 15:25:40 +0700 Subject: [PATCH 5/8] wip: improve onboarding --- apps/desktop/src/router.tsx | 24 +- .../src/routes/auth/create-address.tsx | 261 ++++++++++++ apps/desktop/src/routes/auth/create-keys.tsx | 91 +++++ apps/desktop/src/routes/auth/create.tsx | 376 +++--------------- apps/desktop/src/routes/auth/login-key.tsx | 2 +- .../src/routes/auth/login-nsecbunker.tsx | 2 +- apps/desktop/src/routes/auth/login-oauth.tsx | 4 +- apps/desktop/src/routes/auth/login.tsx | 18 +- apps/desktop/src/routes/auth/welcome.tsx | 27 +- 9 files changed, 446 insertions(+), 359 deletions(-) create mode 100644 apps/desktop/src/routes/auth/create-address.tsx create mode 100644 apps/desktop/src/routes/auth/create-keys.tsx 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 ? ( +
+ +
+ ) : ( +
+
+
+ +
+
+ + + + @{getDomainName(serviceId)} + + + + + + + + + + Choose a Provider + + {services.map((service) => ( + + ))} + + + + + +
+ + Use to login to Lume and other Nostr apps. You can choose + provider you trust to manage your account + +
+
+
+
+ + +
+ + Use for recover your account if you lose your password + +
+
+
+ +
+
+ )} +
+
+ ); +} 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 ( +
+
+
+

Create Account

+
+
+
+ + +
+
+ + +
+
+
+
+ ); +} 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 +

+
+
+ + +
- {!services ? ( -
- -
- ) : ( -
-
-
-
- -
-
- - - - - @{getDomainName(serviceId)} - - - - - - - - - - - Choose a Provider - - {services.map((service) => ( - - ))} - - - - - -
- - Use to login to Lume and other Nostr apps. You can choose - provider you trust to manage your account - -
-
-
-
- - -
- - Use for recover your account if you lose your password - -
-
-
- -
-
-
-
-
-
-
-
-
- - Or manage your own keys - -
-
- - Mostly compatible with other Nostr clients - -
-
- -

- 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

-
+
-

- Continue your experience on Nostr -

+

Welcome back, anon!

@@ -15,13 +13,13 @@ export function LoginScreen() { to="/auth/login-oauth" className="inline-flex items-center justify-center w-full h-12 text-lg font-medium text-white bg-blue-500 rounded-xl hover:bg-blue-600" > - Login with Address + Login with Nostr Address - Login with nsecbunker + Login with nsecBunker
@@ -31,7 +29,7 @@ export function LoginScreen() {
- Or (Not recommended) + Or continue with
@@ -43,8 +41,10 @@ export function LoginScreen() { Login with Private Key

- Lume will store your Private Key in{" "} - OS Secure Storage + Lume will put your Private Key in{" "} + Secure Storage depended + on your OS Platform. It will be secured by Password or Biometric + ID

diff --git a/apps/desktop/src/routes/auth/welcome.tsx b/apps/desktop/src/routes/auth/welcome.tsx index a9986055..83789082 100644 --- a/apps/desktop/src/routes/auth/welcome.tsx +++ b/apps/desktop/src/routes/auth/welcome.tsx @@ -1,16 +1,6 @@ -import { LoaderIcon } from "@lume/icons"; -import { useState } from "react"; -import { Link, useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; export function WelcomeScreen() { - const navigate = useNavigate(); - const [loading, setLoading] = useState(false); - - const gotoCreateAccount = () => { - setLoading(true); - navigate("/auth/create"); - }; - return (
@@ -29,17 +19,12 @@ export function WelcomeScreen() {

- + Join Nostr +
-

+

Before joining Nostr, you can take time to learn more about Nostr{" "} Date: Fri, 26 Jan 2024 10:17:23 +0700 Subject: [PATCH 6/8] feat: improve onboarding --- apps/desktop/package.json | 1 + .../src/routes/auth/create-address.tsx | 10 +- apps/desktop/src/routes/auth/create-keys.tsx | 205 +++++++++++++----- apps/desktop/src/routes/auth/create.tsx | 32 ++- apps/desktop/src/routes/auth/login-key.tsx | 2 +- .../src/routes/auth/login-nsecbunker.tsx | 2 +- apps/desktop/src/routes/auth/login-oauth.tsx | 2 +- apps/desktop/src/routes/auth/login.tsx | 2 +- apps/desktop/src/routes/auth/onboarding.tsx | 33 +-- packages/ark/src/ark.ts | 2 +- packages/ui/src/onboarding/interest.tsx | 5 +- packages/ui/src/onboarding/profile.tsx | 12 +- packages/ui/src/routes/suggest.tsx | 8 +- pnpm-lock.yaml | 31 +++ 14 files changed, 222 insertions(+), 125 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index de615d53..785f5cfd 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -25,6 +25,7 @@ "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", + "@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", diff --git a/apps/desktop/src/routes/auth/create-address.tsx b/apps/desktop/src/routes/auth/create-address.tsx index 8a525bdc..c99ac938 100644 --- a/apps/desktop/src/routes/auth/create-address.tsx +++ b/apps/desktop/src/routes/auth/create-address.tsx @@ -124,11 +124,11 @@ export function CreateAccountAddress() { // add account to storage await storage.createSetting("nsecbunker", "1"); - const dbAccount = await storage.createAccount({ + const newAccount = await storage.createAccount({ pubkey: account, privkey: localSigner.privateKey, }); - ark.account = dbAccount; + ark.account = newAccount; // get final signer with newly created account const finalSigner = new NDKNip46Signer(bunker, account, localSigner); @@ -153,9 +153,11 @@ export function CreateAccountAddress() { return (

-
+
-

Create Account

+

+ Let's set up your account on Nostr +

{!services ? (
diff --git a/apps/desktop/src/routes/auth/create-keys.tsx b/apps/desktop/src/routes/auth/create-keys.tsx index 4090075c..46cb4347 100644 --- a/apps/desktop/src/routes/auth/create-keys.tsx +++ b/apps/desktop/src/routes/auth/create-keys.tsx @@ -1,13 +1,16 @@ import { useArk } from "@lume/ark"; +import { CheckIcon, EyeOffIcon, EyeOnIcon, LoaderIcon } from "@lume/icons"; import { useStorage } from "@lume/storage"; import { onboardingAtom } from "@lume/utils"; import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; +import * as Checkbox from "@radix-ui/react-checkbox"; 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 { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { toast } from "sonner"; @@ -17,73 +20,165 @@ export function CreateAccountKeys() { const setOnboarding = useSetAtom(onboardingAtom); const navigate = useNavigate(); - const generateNostrKeys = async () => { - const signer = NDKPrivateKeySigner.generate(); - const pubkey = getPublicKey(signer.privateKey); + const [key, setKey] = useState(""); + const [loading, setLoading] = useState(false); + const [showKey, setShowKey] = useState(false); + const [confirm, setConfirm] = useState({ c1: false, c2: false, c3: false }); - const npub = nip19.npubEncode(pubkey); - const nsec = nip19.nsecEncode(signer.privateKey); + const submit = async () => { + try { + setLoading(true); - ark.updateNostrSigner({ signer }); + const privkey = nip19.decode(key).data as string; + const signer = new NDKPrivateKeySigner(privkey); + const pubkey = getPublicKey(privkey); - const downloadPath = await desktopDir(); - const fileName = `nostr_keys_${nanoid(4)}.txt`; - const filePath = await save({ - defaultPath: `${downloadPath}/${fileName}`, - }); + ark.updateNostrSigner({ signer }); - if (!filePath) { - return toast.info("You need to save account keys before continue."); + 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---\nPrivate key: ${key}`, + ); + + const newAccount = await storage.createAccount({ + pubkey: pubkey, + privkey: privkey, + }); + ark.account = newAccount; + + setLoading(false); + setOnboarding({ open: true, newUser: true }); + + return navigate("/auth/onboarding", { replace: true }); + } catch (e) { + setLoading(false); + toast.error(String(e)); } - - 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 }); }; + useEffect(() => { + const privkey = NDKPrivateKeySigner.generate().privateKey; + setKey(nip19.nsecEncode(privkey)); + }, []); + return (
-
+
-

Create Account

+

+ This is your new Account Key +

+

+ Keep your key in safe place. If you lose this key, you will lose + access to your account. +

-
-
- - -
-
- - +
+
+
+ + +
+
+
+ + setConfirm((state) => ({ ...state, c1: !state.c1 })) + } + className="flex size-7 appearance-none items-center justify-center rounded-lg bg-neutral-900 outline-none" + id="confirm1" + > + + + + + +
+
+ + setConfirm((state) => ({ ...state, c2: !state.c2 })) + } + className="flex size-7 appearance-none items-center justify-center rounded-lg bg-neutral-900 outline-none" + id="confirm2" + > + + + + + +
+
+ + setConfirm((state) => ({ ...state, c3: !state.c3 })) + } + className="flex size-7 appearance-none items-center justify-center rounded-lg bg-neutral-900 outline-none" + id="confirm3" + > + + + + + +
+
+
diff --git a/apps/desktop/src/routes/auth/create.tsx b/apps/desktop/src/routes/auth/create.tsx index 9a1a7c3f..c0b5a301 100644 --- a/apps/desktop/src/routes/auth/create.tsx +++ b/apps/desktop/src/routes/auth/create.tsx @@ -21,29 +21,14 @@ export function CreateAccountScreen() { return (
-
+
-

- Let's get you set up on Nostr. -

+

Let's Get Started

Choose one of methods below to create your account

- +
{isLoading ? ( -
+
) : isError ? ( -
+
Error. Cannot get trending users
) : ( @@ -171,9 +171,9 @@ export function SuggestRoute({ queryKey }: { queryKey: string[] }) { type="button" onClick={submit} disabled={loading} - className="inline-flex items-center justify-center gap-2 px-6 font-medium text-white transform bg-blue-500 rounded-full active:translate-y-1 w-36 h-11 hover:bg-blue-600 focus:outline-none disabled:cursor-not-allowed" + className="inline-flex items-center justify-center gap-2 px-6 font-medium shadow-xl shadow-neutral-500/50 text-white transform bg-blue-500 rounded-full active:translate-y-1 w-36 h-11 hover:bg-blue-600 focus:outline-none disabled:cursor-not-allowed" > - Save + Save & Go Back
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24133739..55152cd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,6 +117,9 @@ importers: '@radix-ui/react-avatar': specifier: ^1.0.4 version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-checkbox': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-collapsible': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) @@ -2138,6 +2141,34 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-checkbox@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.8 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.48)(react@18.2.0) + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} peerDependencies: From bef1f136ad2b1b3a8060fa434f00b0d7701604fb Mon Sep 17 00:00:00 2001 From: reya Date: Fri, 26 Jan 2024 12:43:19 +0700 Subject: [PATCH 7/8] chore: bump version --- src-tauri/tauri.conf.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 54f45f3b..c9c0e4f2 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,8 +8,8 @@ "withGlobalTauri": true }, "package": { - "productName": "Lume 3", - "version": "3.0.0" + "productName": "Lume", + "version": "3.0.0-beta.1" }, "plugins": { "fs": { @@ -62,7 +62,7 @@ "copyright": "", "identifier": "nu.lume", "longDescription": "nostr client for desktop", - "shortDescription": "", + "shortDescription": "nostr client", "targets": "all", "updater": { "active": true, From b0a443c002a43a5b0ef0ba1f38e71177e85a0043 Mon Sep 17 00:00:00 2001 From: reya Date: Fri, 26 Jan 2024 14:15:25 +0700 Subject: [PATCH 8/8] feat: polish --- apps/desktop/src/routes/settings/backup.tsx | 2 +- apps/desktop/src/routes/settings/profile.tsx | 8 +++- packages/ark/src/components/column/header.tsx | 10 ++--- .../src/components/column/interestModal.tsx | 1 + .../ark/src/components/note/buttons/zap.tsx | 21 ++++++---- packages/ark/src/components/note/menu.tsx | 25 +++++++++-- .../components/note/primitives/childReply.tsx | 2 +- .../src/components/note/primitives/reply.tsx | 2 +- .../src/components/note/primitives/repost.tsx | 2 +- .../src/components/note/primitives/text.tsx | 2 +- packages/ark/src/provider.tsx | 17 +------- packages/icons/src/logout.tsx | 2 +- packages/icons/src/user.tsx | 41 ++++++++++--------- packages/ui/src/account/active.tsx | 20 ++++++--- packages/ui/src/account/logout.tsx | 4 +- packages/ui/src/editor/form.tsx | 18 ++++---- packages/ui/src/editor/utils.ts | 17 ++++++-- 17 files changed, 114 insertions(+), 80 deletions(-) diff --git a/apps/desktop/src/routes/settings/backup.tsx b/apps/desktop/src/routes/settings/backup.tsx index 67ba9763..129d46bc 100644 --- a/apps/desktop/src/routes/settings/backup.tsx +++ b/apps/desktop/src/routes/settings/backup.tsx @@ -48,7 +48,7 @@ export function BackupSettingScreen() { diff --git a/apps/desktop/src/routes/settings/profile.tsx b/apps/desktop/src/routes/settings/profile.tsx index 82b31d10..00b224c1 100644 --- a/apps/desktop/src/routes/settings/profile.tsx +++ b/apps/desktop/src/routes/settings/profile.tsx @@ -10,6 +10,7 @@ import { NDKKind, NDKUserProfile } from "@nostr-dev-kit/ndk"; import { useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import { useForm } from "react-hook-form"; +import { toast } from "sonner"; import { AvatarUpload } from "./components/avatarUpload"; import { CoverUpload } from "./components/coverUpload"; @@ -92,6 +93,9 @@ export function ProfileSettingScreen() { return content; }); + // notify + toast.success("You've updated profile successfully."); + // reset state setPicture(null); setBanner(null); @@ -112,7 +116,7 @@ export function ProfileSettingScreen() { className="h-full w-full rounded-t-xl object-cover" /> ) : ( -
+
)}
@@ -236,7 +240,7 @@ export function ProfileSettingScreen() { - + @@ -100,9 +102,24 @@ export function NoteMenu() { to={`/users/${event.pubkey}`} className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - View profile + View author + + +