feat: redesign navigation bar

This commit is contained in:
reya 2024-01-10 13:57:44 +07:00
parent f2504071cd
commit a5ad4fe05c
17 changed files with 274 additions and 197 deletions

View File

@ -1,4 +1,4 @@
import { LumeProvider } from "@lume/ark"; import { ColumnProvider, LumeProvider } from "@lume/ark";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import Router from "./router"; import Router from "./router";
@ -16,7 +16,9 @@ export default function App() {
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<Toaster position="top-center" theme="system" closeButton /> <Toaster position="top-center" theme="system" closeButton />
<LumeProvider> <LumeProvider>
<Router /> <ColumnProvider>
<Router />
</ColumnProvider>
</LumeProvider> </LumeProvider>
</QueryClientProvider> </QueryClientProvider>
); );

View File

@ -140,6 +140,8 @@ export function CreateAccountScreen() {
return toast.error("Failed to create new account, try again later"); return toast.error("Failed to create new account, try again later");
} }
authWindow.close();
// add account to storage // add account to storage
await storage.createSetting("nsecbunker", "1"); await storage.createSetting("nsecbunker", "1");
await storage.createAccount({ await storage.createAccount({
@ -162,8 +164,6 @@ export function CreateAccountScreen() {
setOnboarding(true); setOnboarding(true);
setIsLoading(false); setIsLoading(false);
authWindow.close();
return navigate("/auth/onboarding"); return navigate("/auth/onboarding");
} catch (e) { } catch (e) {
setIsLoading(false); setIsLoading(false);

View File

@ -111,8 +111,10 @@ export class Ark {
public async getUserProfile(pubkey?: string) { public async getUserProfile(pubkey?: string) {
try { try {
// get clean pubkey without any special characters // get clean pubkey without any special characters
let hexstring = pubkey.replace(/[^a-zA-Z0-9]/g, "").replace("nostr:", "");
const currentUserPubkey = this.#storage.account.pubkey; const currentUserPubkey = this.#storage.account.pubkey;
let hexstring = pubkey
? pubkey.replace(/[^a-zA-Z0-9]/g, "").replace("nostr:", "")
: currentUserPubkey;
if ( if (
hexstring.startsWith("npub1") || hexstring.startsWith("npub1") ||
@ -127,7 +129,7 @@ export class Ark {
} }
const user = this.ndk.getUser({ const user = this.ndk.getUser({
pubkey: pubkey ? hexstring : currentUserPubkey, pubkey: hexstring,
}); });
const profile = await user.fetchProfile({ const profile = await user.fetchProfile({
@ -144,8 +146,10 @@ export class Ark {
public async getUserContacts(pubkey?: string) { public async getUserContacts(pubkey?: string) {
try { try {
// get clean pubkey without any special characters // get clean pubkey without any special characters
let hexstring = pubkey.replace(/[^a-zA-Z0-9]/g, "").replace("nostr:", "");
const currentUserPubkey = this.#storage.account.pubkey; const currentUserPubkey = this.#storage.account.pubkey;
let hexstring = pubkey
? pubkey.replace(/[^a-zA-Z0-9]/g, "").replace("nostr:", "")
: currentUserPubkey;
if ( if (
hexstring.startsWith("npub1") || hexstring.startsWith("npub1") ||
@ -160,7 +164,7 @@ export class Ark {
} }
const user = this.ndk.getUser({ const user = this.ndk.getUser({
pubkey: pubkey ? hexstring : currentUserPubkey, pubkey: hexstring,
}); });
const contacts = [...(await user.follows())].map((user) => user.pubkey); const contacts = [...(await user.follows())].map((user) => user.pubkey);

View File

@ -41,7 +41,7 @@ export function ColumnLiveWidget({
className="inline-flex items-center justify-center h-8 gap-1 pl-2 pr-2.5 text-sm font-semibold rounded-full w-max bg-neutral-950 dark:bg-neutral-50 hover:bg-neutral-900 dark:hover:bg-neutral-100 text-neutral-50 dark:text-neutral-950" className="inline-flex items-center justify-center h-8 gap-1 pl-2 pr-2.5 text-sm font-semibold rounded-full w-max bg-neutral-950 dark:bg-neutral-50 hover:bg-neutral-900 dark:hover:bg-neutral-100 text-neutral-50 dark:text-neutral-950"
> >
<ChevronUpIcon className="w-4 h-4" /> <ChevronUpIcon className="w-4 h-4" />
{events.length} {events.length === 1 ? "new event" : "new events"} {events.length} {events.length === 1 ? "new note" : "new notes"}
</button> </button>
</div> </div>
); );

View File

@ -104,3 +104,5 @@ export * from "./src/column";
export * from "./src/addMedia"; export * from "./src/addMedia";
export * from "./src/check"; export * from "./src/check";
export * from "./src/popperFilled"; export * from "./src/popperFilled";
export * from "./src/composeFilled";
export * from "./src/settingsFilled";

View File

@ -1,19 +1,24 @@
import { SVGProps } from 'react'; import { SVGProps } from "react";
export function ComposeIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) { export function ComposeIcon(
return ( props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
<svg ) {
xmlns="http://www.w3.org/2000/svg" return (
width="24" <svg
height="24" xmlns="http://www.w3.org/2000/svg"
fill="none" width="24"
viewBox="0 0 24 24" height="24"
{...props} fill="none"
> viewBox="0 0 24 24"
<path {...props}
fill="currentColor" >
d="M19.121 3.707a3 3 0 00-4.242 0l-12 12A3 3 0 002 17.828V20a1 1 0 001 1h2.172a3 3 0 002.12-.879l12-12a3 3 0 000-4.243l-.17-.171zM21.67 18.749a7.14 7.14 0 01-.93.827c-.58.43-1.503.968-2.574.968-1.006 0-1.878-.463-2.503-.796l-.066-.035c-.723-.384-1.168-.598-1.61-.598-1.057 0-1.732.558-2.26 1.116a1 1 0 11-1.454-1.373c.654-.692 1.824-1.743 3.714-1.743.986 0 1.85.46 2.468.79l.08.042c.717.38 1.172.598 1.631.598.429 0 .923-.234 1.384-.576a5.148 5.148 0 00.694-.624 1.01 1.01 0 011.41-.102c.476.412.418 1.076.017 1.506z" <path
></path> stroke="currentColor"
</svg> strokeLinecap="round"
); strokeLinejoin="round"
strokeWidth="2"
d="M20 14c0 2.8 0 4.2-.545 5.27a5 5 0 01-2.185 2.185C16.2 22 14.8 22 12 22h-2c-2.8 0-4.2 0-5.27-.545a5 5 0 01-2.185-2.185C2 18.2 2 16.8 2 14v-2c0-2.8 0-4.2.545-5.27A5 5 0 014.73 4.545C5.8 4 7.2 4 10 4m-1.938 9.502c.008-.351.013-.527.055-.691.038-.146.097-.286.177-.414.09-.144.213-.268.46-.517l9.396-9.45a1.46 1.46 0 011.828-.196A5.87 5.87 0 0121.7 3.946l.075.116c.376.605.253 1.371-.24 1.867l-9.322 9.375c-.257.257-.385.386-.534.478a1.476 1.476 0 01-.429.178c-.17.04-.351.04-.714.04L8 15.996l.062-2.495z"
/>
</svg>
);
} }

View File

@ -0,0 +1,25 @@
import { SVGProps } from "react";
export function ComposeFilledIcon(
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
>
<path
fill="currentColor"
d="M22.537 3.398a6.87 6.87 0 00-2.017-2.004 2.46 2.46 0 00-3.079.331l-9.396 9.45-.047.047c-.2.2-.402.403-.554.648a2.49 2.49 0 00-.295.691c-.072.28-.079.566-.086.85L7 15.97a1 1 0 00.998 1.025l2.538.004h.068c.293.001.59.002.88-.068.254-.06.497-.161.72-.298.253-.156.462-.367.668-.574h.001l.05-.05 9.322-9.376c.791-.795 1.021-2.067.381-3.098-.024-.04-.054-.085-.073-.113l-.005-.008-.01-.017z"
/>
<path
fill="currentColor"
d="M5.184 5.436c.37-.189.842-.308 1.613-.371L10 5a1 1 0 100-2h-.044c-1.363 0-2.447 0-3.321.071-.896.074-1.66.227-2.359.583a6 6 0 00-2.622 2.622c-.356.7-.51 1.463-.583 2.359C1 9.509 1 10.593 1 11.956v2.088c0 1.363 0 2.447.071 3.322.074.895.227 1.659.583 2.358a6 6 0 002.622 2.622c.7.356 1.463.51 2.359.583C7.509 23 8.593 23 9.956 23h2.088c1.363 0 2.447 0 3.321-.071.896-.074 1.66-.227 2.359-.583a5.998 5.998 0 002.622-2.622c.356-.7.51-1.463.583-2.358.071-.875.071-1.96.071-3.322V14a1 1 0 10-2 0c0 1.417 0 2.419-.065 3.203-.063.771-.182 1.243-.371 1.613a4 4 0 01-1.748 1.748c-.37.189-.841.308-1.613.371C14.419 21 13.417 21 12 21h-2c-1.417 0-2.419 0-3.203-.065-.771-.063-1.243-.182-1.613-.371a4 4 0 01-1.748-1.748c-.189-.37-.308-.841-.371-1.613C3 16.419 3 15.417 3 14v-2c0-1.417 0-2.419.065-3.203.063-.771.182-1.243.371-1.613a4 4 0 011.748-1.748z"
/>
</svg>
);
}

View File

@ -1,18 +1,20 @@
export function NwcIcon(props: JSX.IntrinsicElements["svg"]) { export function NwcIcon(props: JSX.IntrinsicElements["svg"]) {
return ( return (
<svg <svg
{...props}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="25" height="24"
fill="none" fill="none"
viewBox="0 0 24 25" viewBox="0 0 24 24"
stroke="currentColor" {...props}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
> >
<path d="M2 15v-3.5c0-2.8 0-4.2.545-5.27A5 5 0 014.73 4.045C5.8 3.5 7.2 3.5 10 3.5h3.5c1.398 0 2.097 0 2.648.228a3 3 0 011.624 1.624c.207.5.226 1.123.228 2.28M2 15c0 1.33 0 2.495.38 3.413a5 5 0 002.707 2.706c.918.381 2.083.381 4.413.381h5c2.33 0 3.495 0 4.413-.38a5 5 0 002.706-2.707C22 17.495 22 16.33 22 15c0-2.33 0-3.495-.38-4.413a5 5 0 00-2.707-2.706A4.062 4.062 0 0018 7.63M2 15c0-2.33 0-3.495.38-4.413A5 5 0 015.088 7.88C6.005 7.5 7.17 7.5 9.5 7.5h5c1.634 0 2.695 0 3.5.131M14 12.5h3" /> <path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M13 4H9.222M13 4a4 4 0 010 8H9.222c-.206 0-.31 0-.396-.008a2 2 0 01-1.818-1.818C7 10.087 7 9.984 7 9.778V6.222c0-.206 0-.31.008-.396A2 2 0 019 4m4 0V2M9.222 4H9m.222 0H9m1.2 16H15a4 4 0 100-8h-4.8c-1.12 0-1.68 0-2.108.218a2 2 0 00-.874.874C7 13.52 7 14.08 7 15.2v1.6c0 1.12 0 1.68.218 2.108a2 2 0 00.874.874C8.52 20 9.08 20 10.2 20zm0 0H9M7 4v16M7 4H5m2 0h2M5 20h2m2 2v-2M9 2v2m4 18v-2m-6 0h2"
/>
</svg> </svg>
); );
} }

View File

@ -0,0 +1,21 @@
import { SVGProps } from "react";
export function SettingsFilledIcon(
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
>
<path
fill="currentColor"
d="M10.899 2.116a5.3 5.3 0 012.203 0c.823.175 1.58.611 2.695 1.256l1.78 1.027c1.117.644 1.874 1.08 2.437 1.705a5.294 5.294 0 011.102 1.907c.26.8.26 1.674.26 2.961v2.056c0 1.287 0 2.16-.26 2.96a5.294 5.294 0 01-1.102 1.908c-.563.625-1.32 1.061-2.436 1.705l-1.781 1.027c-1.116.645-1.872 1.081-2.696 1.256a5.3 5.3 0 01-2.202 0c-.823-.175-1.58-.611-2.696-1.256l-1.78-1.027c-1.117-.644-1.874-1.08-2.437-1.705a5.294 5.294 0 01-1.102-1.907c-.26-.8-.26-1.674-.259-2.961v-2.056c0-1.287 0-2.16.26-2.96a5.295 5.295 0 011.101-1.908C4.55 5.48 5.306 5.043 6.422 4.4l1.781-1.027C9.32 2.727 10.076 2.29 10.9 2.116zM12 9.9a2.1 2.1 0 100 4.2 2.1 2.1 0 000-4.2z"
/>
</svg>
);
}

View File

@ -6,7 +6,7 @@ export function HomeRoute({ id }: { id: string }) {
return ( return (
<div className="pb-5 overflow-y-auto"> <div className="pb-5 overflow-y-auto">
<WindowVirtualizer> <WindowVirtualizer>
<div className="px-3"> <div className="px-3 mt-3">
<ThreadNote eventId={id} /> <ThreadNote eventId={id} />
<ReplyList eventId={id} title="All replies" className="mt-5" /> <ReplyList eventId={id} title="All replies" className="mt-5" />
</div> </div>

View File

@ -17,33 +17,30 @@ export function ActiveAccount() {
)}`; )}`;
return ( return (
<div className="flex flex-col gap-1 rounded-xl bg-black/10 p-1 ring-1 ring-transparent hover:bg-black/20 hover:ring-blue-500 dark:bg-white/10 dark:hover:bg-white/20"> <Link to="/settings/" className="relative inline-block">
<Link to="/settings/" className="relative inline-block"> <Avatar.Root>
<Avatar.Root> <Avatar.Image
<Avatar.Image src={user?.picture || user?.image}
src={user?.picture || user?.image} alt={storage.account.pubkey}
alt={storage.account.pubkey} loading="lazy"
loading="lazy" decoding="async"
decoding="async" style={{ contentVisibility: "auto" }}
style={{ contentVisibility: "auto" }} className="aspect-square h-auto w-full rounded-xl object-cover"
className="aspect-square h-auto w-full rounded-lg object-cover"
/>
<Avatar.Fallback delayMs={150}>
<img
src={svgURI}
alt={storage.account.pubkey}
className="aspect-square h-auto w-full rounded-lg bg-black dark:bg-white"
/>
</Avatar.Fallback>
</Avatar.Root>
<span
className={twMerge(
"absolute bottom-0 right-0 block h-2 w-2 rounded-full ring-2 ring-neutral-100 dark:ring-neutral-900",
isOnline ? "bg-teal-500" : "bg-red-500",
)}
/> />
</Link> <Avatar.Fallback delayMs={150}>
<AccountMoreActions /> <img
</div> src={svgURI}
alt={storage.account.pubkey}
className="aspect-square h-auto w-full rounded-xl bg-black dark:bg-white"
/>
</Avatar.Fallback>
</Avatar.Root>
<span
className={twMerge(
"absolute bottom-0 right-0 block h-2 w-2 rounded-full ring-2 ring-neutral-100 dark:ring-neutral-900",
isOnline ? "bg-teal-500" : "bg-red-500",
)}
/>
</Link>
); );
} }

View File

@ -1,7 +1,7 @@
import { MentionNote, useArk, useStorage } from "@lume/ark"; import { MentionNote, useArk, useColumnContext, useStorage } from "@lume/ark";
import { TrashIcon } from "@lume/icons"; import { LoaderIcon, TrashIcon } from "@lume/icons";
import { NDKCacheUserProfile } from "@lume/types"; import { NDKCacheUserProfile } from "@lume/types";
import { cn, editorValueAtom } from "@lume/utils"; import { COL_TYPES, cn, editorValueAtom } from "@lume/utils";
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
@ -195,10 +195,13 @@ export function EditorForm() {
const [target, setTarget] = useState<Range | undefined>(); const [target, setTarget] = useState<Range | undefined>();
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [loading, setLoading] = useState(false);
const [editor] = useState(() => const [editor] = useState(() =>
withMentions(withNostrEvent(withImages(withReact(createEditor())))), withMentions(withNostrEvent(withImages(withReact(createEditor())))),
); );
const { addColumn } = useColumnContext();
const filters = contacts const filters = contacts
?.filter((c) => c?.name?.toLowerCase().startsWith(search.toLowerCase())) ?.filter((c) => c?.name?.toLowerCase().startsWith(search.toLowerCase()))
?.slice(0, 10); ?.slice(0, 10);
@ -234,19 +237,35 @@ export function EditorForm() {
}; };
const submit = async () => { const submit = async () => {
const event = new NDKEvent(ark.ndk); try {
event.kind = NDKKind.Text; setLoading(true);
event.content = serialize(editor.children);
const publish = await event.publish(); const event = new NDKEvent(ark.ndk);
event.kind = NDKKind.Text;
event.content = serialize(editor.children);
if (!publish) toast.error("Failed to publish event, try again later."); const publish = await event.publish();
toast.success( if (publish) {
`Event has been published successfully to ${publish.size} relays.`, toast.success(
); `Event has been published successfully to ${publish.size} relays.`,
);
reset(); // add current post as column thread
addColumn({
kind: COL_TYPES.thread,
content: event.id,
title: "Thread",
});
setLoading(false);
return reset();
}
} catch (e) {
setLoading(false);
toast.error(String(e));
}
}; };
useEffect(() => { useEffect(() => {
@ -300,7 +319,31 @@ export function EditorForm() {
setTarget(null); setTarget(null);
}} }}
> >
<div className="py-6 overflow-y-auto px-7"> <div className="flex items-center justify-between h-16 px-3 border-b shrink-0 border-neutral-100 dark:border-neutral-900 bg-neutral-50 dark:bg-neutral-950">
<div>
<h3 className="font-semibold text-neutral-700 dark:text-neutral-500">
New Post
</h3>
</div>
<div className="flex items-center">
<div className="inline-flex items-center gap-2">
<EditorAddMedia />
</div>
<div className="w-px h-6 mx-3 bg-neutral-200 dark:bg-neutral-800" />
<button
type="button"
onClick={submit}
className="inline-flex items-center justify-center w-20 pb-[2px] font-semibold border-t rounded-lg border-neutral-900 dark:border-neutral-800 h-9 bg-neutral-950 text-neutral-50 dark:bg-neutral-900 hover:bg-neutral-900 dark:hover:bg-neutral-800"
>
{loading ? (
<LoaderIcon className="size-4 animate-spin" />
) : (
"Post"
)}
</button>
</div>
</div>
<div className="py-6 h-full overflow-y-auto px-7">
<Editable <Editable
key={JSON.stringify(editorValue)} key={JSON.stringify(editorValue)}
autoFocus={false} autoFocus={false}
@ -335,22 +378,6 @@ export function EditorForm() {
</Portal> </Portal>
)} )}
</div> </div>
<div className="flex items-center justify-between h-16 px-3 border-t shrink-0 border-neutral-100 dark:border-neutral-900 bg-neutral-50 dark:bg-neutral-950">
<div />
<div className="flex items-center">
<div className="inline-flex items-center gap-2">
<EditorAddMedia />
</div>
<div className="w-px h-6 mx-3 bg-neutral-200 dark:bg-neutral-800" />
<button
type="button"
onClick={submit}
className="inline-flex items-center justify-center w-20 pb-[2px] font-semibold border-t rounded-lg border-neutral-900 dark:border-neutral-800 h-9 bg-neutral-950 text-neutral-50 dark:bg-neutral-900 hover:bg-neutral-900 dark:hover:bg-neutral-800"
>
Post
</button>
</div>
</div>
</Slate> </Slate>
</div> </div>
); );

View File

@ -1,14 +1,13 @@
import { ColumnProvider } from "@lume/ark";
import { Outlet } from "react-router-dom"; import { Outlet } from "react-router-dom";
import { OnboardingModal } from "../onboarding/modal"; import { OnboardingModal } from "../onboarding/modal";
export function HomeLayout() { export function HomeLayout() {
return ( return (
<ColumnProvider> <>
<OnboardingModal /> <OnboardingModal />
<div className="h-full w-full rounded-xl overflow-hidden bg-white shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:bg-black dark:shadow-[inset_0_0_0.5px_1px_hsla(0,0%,100%,0.075),0_0_0_1px_hsla(0,0%,0%,0.05),0_0.3px_0.4px_hsla(0,0%,0%,0.02),0_0.9px_1.5px_hsla(0,0%,0%,0.045),0_3.5px_6px_hsla(0,0%,0%,0.09)]"> <div className="h-full w-full rounded-xl overflow-hidden bg-white shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:bg-black dark:shadow-[inset_0_0_0.5px_1px_hsla(0,0%,100%,0.075),0_0_0_1px_hsla(0,0%,0%,0.05),0_0.3px_0.4px_hsla(0,0%,0%,0.02),0_0.9px_1.5px_hsla(0,0%,0%,0.045),0_3.5px_6px_hsla(0,0%,0%,0.09)]">
<Outlet /> <Outlet />
</div> </div>
</ColumnProvider> </>
); );
} }

View File

@ -1,34 +1,52 @@
import { import {
ComposeFilledIcon,
ComposeIcon,
DepotFilledIcon, DepotFilledIcon,
DepotIcon, DepotIcon,
HomeFilledIcon, HomeFilledIcon,
HomeIcon, HomeIcon,
NwcFilledIcon, NwcFilledIcon,
NwcIcon, NwcIcon,
PlusIcon,
RelayFilledIcon, RelayFilledIcon,
RelayIcon, RelayIcon,
SettingsFilledIcon,
SettingsIcon,
} from "@lume/icons"; } from "@lume/icons";
import { cn, editorAtom } from "@lume/utils"; import { cn, editorAtom } from "@lume/utils";
import { useSetAtom } from "jotai"; import { useAtom } from "jotai";
import { useHotkeys } from "react-hotkeys-hook"; import { useHotkeys } from "react-hotkeys-hook";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { ActiveAccount } from "./account/active"; import { ActiveAccount } from "./account/active";
export function Navigation() { export function Navigation() {
const setIsEditorOpen = useSetAtom(editorAtom); const [isEditorOpen, setIsEditorOpen] = useAtom(editorAtom);
useHotkeys("meta+n", () => setIsEditorOpen((state) => !state), []); useHotkeys("meta+n", () => setIsEditorOpen((state) => !state), []);
return ( return (
<div className="flex flex-col justify-between w-20 h-full px-4 py-3 shrink-0"> <div className="flex flex-col justify-between w-20 h-full px-4 py-3 shrink-0">
<div className="flex flex-col flex-1 gap-5"> <div className="flex flex-col flex-1">
<NavLink <div className="flex flex-col gap-3">
to="/" <ActiveAccount />
preventScrollReset={true} <button
className="inline-flex flex-col items-center justify-center" type="button"
> onClick={() => setIsEditorOpen((prev) => !prev)}
{({ isActive }) => ( className="flex items-center justify-center h-auto w-full text-black aspect-square rounded-xl bg-black/5 hover:bg-blue-500 hover:text-white dark:bg-white/5 dark:text-white dark:hover:bg-blue-500"
<> >
{isEditorOpen ? (
<ComposeFilledIcon className="size-5" />
) : (
<ComposeIcon className="size-5" />
)}
</button>
</div>
<div className="my-5 w-2/3 mx-auto h-px bg-black/10 dark:bg-white/10" />
<div className="flex flex-col gap-2">
<NavLink
to="/"
preventScrollReset={true}
className="inline-flex flex-col items-center justify-center"
>
{({ isActive }) => (
<div <div
className={cn( className={cn(
"inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl", "inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl",
@ -38,31 +56,19 @@ export function Navigation() {
)} )}
> >
{isActive ? ( {isActive ? (
<HomeFilledIcon className="text-black size-6 dark:text-white" /> <HomeFilledIcon className="size-6" />
) : ( ) : (
<HomeIcon className="size-6" /> <HomeIcon className="size-6" />
)} )}
</div> </div>
<div )}
className={cn( </NavLink>
"text-sm", <NavLink
isActive to="/relays"
? "font-semibold text-black dark:text-white" preventScrollReset={true}
: "font-medium text-black/50 dark:text-white/50", className="inline-flex flex-col items-center justify-center"
)} >
> {({ isActive }) => (
Home
</div>
</>
)}
</NavLink>
<NavLink
to="/relays"
preventScrollReset={true}
className="inline-flex flex-col items-center justify-center"
>
{({ isActive }) => (
<>
<div <div
className={cn( className={cn(
"inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl", "inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl",
@ -77,26 +83,14 @@ export function Navigation() {
<RelayIcon className="size-6" /> <RelayIcon className="size-6" />
)} )}
</div> </div>
<div )}
className={cn( </NavLink>
"text-sm", <NavLink
isActive to="/depot"
? "font-semibold text-black dark:text-white" preventScrollReset={true}
: "font-medium text-black/50 dark:text-white/50", className="inline-flex flex-col items-center justify-center"
)} >
> {({ isActive }) => (
Relays
</div>
</>
)}
</NavLink>
<NavLink
to="/depot"
preventScrollReset={true}
className="inline-flex flex-col items-center justify-center"
>
{({ isActive }) => (
<>
<div <div
className={cn( className={cn(
"inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl", "inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl",
@ -111,26 +105,14 @@ export function Navigation() {
<DepotIcon className="size-6" /> <DepotIcon className="size-6" />
)} )}
</div> </div>
<div )}
className={cn( </NavLink>
"text-sm", <NavLink
isActive to="/nwc"
? "font-semibold text-black dark:text-white" preventScrollReset={true}
: "font-medium text-black/50 dark:text-white/50", className="inline-flex flex-col items-center justify-center"
)} >
> {({ isActive }) => (
Depot
</div>
</>
)}
</NavLink>
<NavLink
to="/nwc"
preventScrollReset={true}
className="inline-flex flex-col items-center justify-center"
>
{({ isActive }) => (
<>
<div <div
className={cn( className={cn(
"inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl", "inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl",
@ -139,36 +121,36 @@ export function Navigation() {
: "text-black/50 dark:text-neutral-400", : "text-black/50 dark:text-neutral-400",
)} )}
> >
{isActive ? ( <NwcIcon className="size-6" />
<NwcFilledIcon className="text-black size-6 dark:text-white" />
) : (
<NwcIcon className="size-6" />
)}
</div> </div>
<div )}
className={cn( </NavLink>
"text-sm", </div>
isActive </div>
? "font-semibold text-black dark:text-white" <div className="flex flex-col">
: "font-medium text-black/50 dark:text-white/50", <NavLink
)} to="/settings"
> preventScrollReset={true}
Wallet className="inline-flex flex-col items-center justify-center"
</div> >
</> {({ isActive }) => (
<div
className={cn(
"inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl",
isActive
? "bg-black/10 text-black dark:bg-white/10 dark:text-white"
: "text-black/50 dark:text-neutral-400",
)}
>
{isActive ? (
<SettingsFilledIcon className="size-6" />
) : (
<SettingsIcon className="size-6" />
)}
</div>
)} )}
</NavLink> </NavLink>
</div> </div>
<div className="flex flex-col gap-3 p-1 shrink-0">
<button
type="button"
onClick={() => setIsEditorOpen((prev) => !prev)}
className="flex items-center justify-center w-full h-auto text-black aspect-square rounded-xl bg-black/10 hover:bg-blue-500 hover:text-white dark:bg-white/10 dark:text-white dark:hover:bg-blue-500"
>
<PlusIcon className="w-5 h-5" />
</button>
<ActiveAccount />
</div>
</div> </div>
); );
} }

View File

@ -1,21 +1,33 @@
import { CheckIcon } from "@lume/icons"; import { useStorage } from "@lume/ark";
import { CheckIcon, LoaderIcon } from "@lume/icons";
import { onboardingAtom } from "@lume/utils"; import { onboardingAtom } from "@lume/utils";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useSetAtom } from "jotai"; import { useSetAtom } from "jotai";
import { useState } from "react";
export function OnboardingFinishScreen() { export function OnboardingFinishScreen() {
const storage = useStorage();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const setOnboarding = useSetAtom(onboardingAtom); const setOnboarding = useSetAtom(onboardingAtom);
const [loading, setLoading] = useState(false);
const finish = async () => { const finish = async () => {
setLoading(true);
const queryCache = queryClient.getQueryCache(); const queryCache = queryClient.getQueryCache();
const queryKeys = queryCache.getAll().map((cache) => cache.queryKey); const queryKeys = queryCache.getAll().map((cache) => cache.queryKey);
await queryClient.refetchQueries({
queryKey: ["user", storage.account.pubkey],
});
for (const key of queryKeys) { for (const key of queryKeys) {
await queryClient.refetchQueries({ queryKey: key }); await queryClient.refetchQueries({ queryKey: key });
} }
setLoading(false);
setOnboarding(false); setOnboarding(false);
}; };
@ -39,7 +51,7 @@ export function OnboardingFinishScreen() {
onClick={finish} onClick={finish}
className="inline-flex items-center justify-center gap-2 w-44 font-medium h-11 rounded-xl bg-blue-100 text-blue-500 hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-500 dark:hover:bg-blue-800" className="inline-flex items-center justify-center gap-2 w-44 font-medium h-11 rounded-xl bg-blue-100 text-blue-500 hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-500 dark:hover:bg-blue-800"
> >
Close {loading ? <LoaderIcon className="size-4 animate-spin" /> : "Close"}
</button> </button>
<a <a
href="https://github.com/luminous-devs/lume/issues" href="https://github.com/luminous-devs/lume/issues"

View File

@ -82,7 +82,7 @@ export function OnboardingFollowScreen() {
} }
} catch (e) { } catch (e) {
setLoading(false); setLoading(false);
toast.error(e); toast.error(String(e));
} }
}; };

View File

@ -56,8 +56,7 @@ export function OnboardingProfileSettingsScreen() {
} }
} catch (e) { } catch (e) {
setLoading(false); setLoading(false);
console.log(e); toast.error(String(e));
toast.error("Cannot publish your profile, please try again later.");
} }
}; };