mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
feat: redesign navigation bar
This commit is contained in:
parent
f2504071cd
commit
a5ad4fe05c
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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";
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
25
packages/icons/src/composeFilled.tsx
Normal file
25
packages/icons/src/composeFilled.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
21
packages/icons/src/settingsFilled.tsx
Normal file
21
packages/icons/src/settingsFilled.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -82,7 +82,7 @@ export function OnboardingFollowScreen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(e);
|
toast.error(String(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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.");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user