feat(ui): update ui consistent

This commit is contained in:
2023-12-19 10:13:52 +07:00
parent ec2ac2dce3
commit d9e8d05db7
26 changed files with 669 additions and 517 deletions

View File

@@ -122,6 +122,69 @@ export default function App() {
},
],
},
{
path: 'settings',
element: <SettingsLayout />,
children: [
{
index: true,
async lazy() {
const { UserSettingScreen } = await import('@app/settings');
return { Component: UserSettingScreen };
},
},
{
path: 'edit-profile',
async lazy() {
const { EditProfileScreen } = await import(
'@app/settings/editProfile'
);
return { Component: EditProfileScreen };
},
},
{
path: 'edit-contact',
async lazy() {
const { EditContactScreen } = await import(
'@app/settings/editContact'
);
return { Component: EditContactScreen };
},
},
{
path: 'general',
async lazy() {
const { GeneralSettingScreen } = await import(
'@app/settings/general'
);
return { Component: GeneralSettingScreen };
},
},
{
path: 'backup',
async lazy() {
const { BackupSettingScreen } = await import('@app/settings/backup');
return { Component: BackupSettingScreen };
},
},
{
path: 'advanced',
async lazy() {
const { AdvancedSettingScreen } = await import(
'@app/settings/advanced'
);
return { Component: AdvancedSettingScreen };
},
},
{
path: 'about',
async lazy() {
const { AboutScreen } = await import('@app/settings/about');
return { Component: AboutScreen };
},
},
],
},
],
},
],
@@ -203,62 +266,6 @@ export default function App() {
},
],
},
{
path: 'settings',
element: <SettingsLayout platform={ark.platform} />,
errorElement: <ErrorScreen />,
children: [
{
index: true,
async lazy() {
const { UserSettingScreen } = await import('@app/settings');
return { Component: UserSettingScreen };
},
},
{
path: 'edit-profile',
async lazy() {
const { EditProfileScreen } = await import('@app/settings/editProfile');
return { Component: EditProfileScreen };
},
},
{
path: 'edit-contact',
async lazy() {
const { EditContactScreen } = await import('@app/settings/editContact');
return { Component: EditContactScreen };
},
},
{
path: 'general',
async lazy() {
const { GeneralSettingScreen } = await import('@app/settings/general');
return { Component: GeneralSettingScreen };
},
},
{
path: 'backup',
async lazy() {
const { BackupSettingScreen } = await import('@app/settings/backup');
return { Component: BackupSettingScreen };
},
},
{
path: 'advanced',
async lazy() {
const { AdvancedSettingScreen } = await import('@app/settings/advanced');
return { Component: AdvancedSettingScreen };
},
},
{
path: 'about',
async lazy() {
const { AboutScreen } = await import('@app/settings/about');
return { Component: AboutScreen };
},
},
],
},
]);
return (

View File

@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { useArk } from '@libs/ark/provider';
import { useArk } from '@libs/ark';
import { NoteChildUser } from './childUser';
export function NoteChild({ eventId, isRoot }: { eventId: string; isRoot?: boolean }) {

View File

@@ -2,7 +2,7 @@ import * as Avatar from '@radix-ui/react-avatar';
import { useQuery } from '@tanstack/react-query';
import { minidenticon } from 'minidenticons';
import { useMemo } from 'react';
import { useArk } from '@libs/ark/provider';
import { useArk } from '@libs/ark';
import { displayNpub } from '@utils/formater';
export function NoteChildUser({ pubkey, subtext }: { pubkey: string; subtext: string }) {

View File

@@ -1,10 +1,22 @@
import { twMerge } from 'tailwind-merge';
import { useRichContent } from '@utils/hooks/useRichContent';
export function NoteContent({ content }: { content: string }) {
export function NoteContent({
content,
className,
}: {
content: string;
className?: string;
}) {
const { parsedContent } = useRichContent(content);
return (
<div className="break-p select-text whitespace-pre-line leading-normal">
<div
className={twMerge(
'break-p select-text whitespace-pre-line leading-normal',
className
)}
>
{parsedContent}
</div>
);

View File

@@ -1,5 +1,6 @@
import { NoteChild } from './child';
import { NoteContent } from './content';
import { NoteMenu } from './menu';
import { NoteReaction } from './reaction';
import { NoteReply } from './reply';
import { NoteRepost } from './repost';
@@ -10,6 +11,7 @@ import { NoteZap } from './zap';
export const Note = {
Root: NoteRoot,
User: NoteUser,
Menu: NoteMenu,
Content: NoteContent,
Reply: NoteReply,
Repost: NoteRepost,

View File

@@ -1,5 +1,5 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useArk } from '@libs/ark/provider';
import { useArk } from '@libs/ark';
import { Note } from '..';
export function TextNote({ event }: { event: NDKEvent }) {
@@ -8,9 +8,12 @@ export function TextNote({ event }: { event: NDKEvent }) {
return (
<Note.Root>
<Note.User pubkey={event.pubkey} time={event.created_at} />
<div className="flex h-14 items-center justify-between gap-2 px-3">
<Note.User pubkey={event.pubkey} time={event.created_at} className="w-full" />
<Note.Menu eventId={event.id} pubkey={event.pubkey} />
</div>
{thread ? (
<div className="w-full px-3">
<div className="mb-2 w-full px-3">
<div className="flex h-min w-full flex-col gap-3 rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900">
{thread.rootEventId ? (
<Note.Child eventId={thread.rootEventId} isRoot />
@@ -25,7 +28,7 @@ export function TextNote({ event }: { event: NDKEvent }) {
</div>
</div>
) : null}
<Note.Content content={event.content} />
<Note.Content content={event.content} className="px-3" />
<div className="flex h-14 items-center justify-between px-3">
<div />
<div className="inline-flex items-center gap-10">

View File

@@ -0,0 +1,64 @@
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { nip19 } from 'nostr-tools';
import { EventPointer } from 'nostr-tools/lib/types/nip19';
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { HorizontalDotsIcon } from '@shared/icons';
export function NoteMenu({ eventId, pubkey }: { eventId: string; pubkey: string }) {
const [open, setOpen] = useState(false);
const copyID = async () => {
await writeText(nip19.neventEncode({ id: eventId, author: pubkey } as EventPointer));
setOpen(false);
};
const copyLink = async () => {
await writeText(
'https://njump.me/' +
nip19.neventEncode({ id: eventId, author: pubkey } as EventPointer)
);
setOpen(false);
};
return (
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
<DropdownMenu.Trigger asChild>
<button type="button" className="inline-flex h-6 w-6 items-center justify-center">
<HorizontalDotsIcon className="h-4 w-4 text-neutral-800 hover:text-blue-500 dark:text-neutral-200" />
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content className="flex w-[200px] flex-col overflow-hidden rounded-xl border border-neutral-200 bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:bg-neutral-900">
<DropdownMenu.Item asChild>
<button
type="button"
onClick={() => copyLink()}
className="inline-flex h-10 items-center px-4 text-sm text-neutral-900 hover:bg-neutral-200 focus:outline-none dark:text-neutral-100 dark:hover:bg-neutral-800"
>
Copy shareable link
</button>
</DropdownMenu.Item>
<DropdownMenu.Item asChild>
<button
type="button"
onClick={() => copyID()}
className="inline-flex h-10 items-center px-4 text-sm text-neutral-900 hover:bg-neutral-200 focus:outline-none dark:text-neutral-100 dark:hover:bg-neutral-800"
>
Copy ID
</button>
</DropdownMenu.Item>
<DropdownMenu.Item asChild>
<Link
to={`/users/${pubkey}`}
className="inline-flex h-10 items-center px-4 text-sm text-neutral-900 hover:bg-neutral-200 focus:outline-none dark:text-neutral-100 dark:hover:bg-neutral-800"
>
View profile
</Link>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
}

View File

@@ -10,7 +10,7 @@ export function NoteRoot({
}) {
return (
<div className={twMerge('h-min w-full p-3', className)}>
<div className="relative flex flex-col gap-2 overflow-hidden rounded-xl bg-neutral-50 dark:bg-neutral-950">
<div className="relative flex flex-col overflow-hidden rounded-xl bg-neutral-50 dark:bg-neutral-950">
{children}
</div>
</div>

View File

@@ -2,6 +2,7 @@ import * as Avatar from '@radix-ui/react-avatar';
import { useQuery } from '@tanstack/react-query';
import { minidenticon } from 'minidenticons';
import { useMemo } from 'react';
import { twMerge } from 'tailwind-merge';
import { useArk } from '@libs/ark';
import { RepostIcon } from '@shared/icons';
import { displayNpub, formatCreatedAt } from '@utils/formater';
@@ -10,10 +11,12 @@ export function NoteUser({
pubkey,
time,
variant = 'text',
className,
}: {
pubkey: string;
time: number;
variant?: 'text' | 'repost';
className?: string;
}) {
const ark = useArk();
const createdAt = useMemo(() => formatCreatedAt(time), [time]);
@@ -48,7 +51,7 @@ export function NoteUser({
if (variant === 'repost') {
if (isLoading) {
return (
<div className="flex gap-3">
<div className={twMerge('flex gap-3', className)}>
<div className="inline-flex w-10 items-center justify-center">
<RepostIcon className="h-5 w-5 text-blue-500" />
</div>
@@ -61,7 +64,7 @@ export function NoteUser({
}
return (
<div className="flex gap-2">
<div className={twMerge('flex gap-2', className)}>
<div className="inline-flex w-10 items-center justify-center">
<RepostIcon className="h-5 w-5 text-blue-500" />
</div>
@@ -95,7 +98,7 @@ export function NoteUser({
if (isLoading) {
return (
<div className="flex items-center gap-3">
<div className={twMerge('flex items-center gap-3', className)}>
<Avatar.Root className="h-9 w-9 shrink-0">
<Avatar.Image
src={fallbackAvatar}
@@ -113,7 +116,7 @@ export function NoteUser({
}
return (
<div className="flex items-center gap-3">
<div className={twMerge('flex items-center gap-3', className)}>
<Avatar.Root className="h-9 w-9 shrink-0">
<Avatar.Image
src={user?.picture || user?.image}
@@ -130,13 +133,11 @@ export function NoteUser({
/>
</Avatar.Fallback>
</Avatar.Root>
<div className="flex h-6 flex-1 items-start gap-2">
<div className="flex h-6 flex-1 items-start justify-between gap-2">
<div className="max-w-[15rem] truncate font-semibold text-neutral-950 dark:text-neutral-50">
{user?.name || user?.display_name || user?.displayName || fallbackName}
</div>
<div className="ml-auto inline-flex items-center gap-3">
<div className="text-neutral-500 dark:text-neutral-400">{createdAt}</div>
</div>
<div className="text-neutral-500 dark:text-neutral-400">{createdAt}</div>
</div>
</div>
);

View File

@@ -4,9 +4,9 @@ import { ReactNode } from 'react';
import {
ArrowLeftIcon,
ArrowRightIcon,
HomeIcon,
HorizontalDotsIcon,
RefreshIcon,
ThreadIcon,
TrashIcon,
} from '@shared/icons';
import { useWidget } from '@utils/hooks/useWidget';
@@ -19,14 +19,14 @@ export function WidgetHeader({
}: {
id: string;
title: string;
queryKey?: string;
queryKey?: string[];
icon?: ReactNode;
}) {
const queryClient = useQueryClient();
const { removeWidget } = useWidget();
const refresh = async () => {
if (queryKey) await queryClient.refetchQueries({ queryKey: [queryKey] });
if (queryKey) await queryClient.refetchQueries({ queryKey });
};
const moveLeft = async () => {
@@ -46,7 +46,7 @@ export function WidgetHeader({
<div className="inline-flex items-center gap-4">
<div className="h-5 w-1 rounded-full bg-blue-500" />
<div className="inline-flex items-center gap-2">
{icon ? icon : <HomeIcon className="h-5 w-5" />}
{icon ? icon : <ThreadIcon className="h-5 w-5" />}
<div className="text-sm font-medium">{title}</div>
</div>
</div>

View File

@@ -18,7 +18,7 @@ export function ActiveAccount() {
encodeURIComponent(minidenticon(ark.account.pubkey, 90, 50));
return (
<div className="flex flex-col gap-1 rounded-lg bg-neutral-100 p-1 ring-1 ring-transparent hover:bg-neutral-200 hover:ring-blue-500 dark:bg-neutral-900 dark:hover:bg-neutral-800">
<div className="flex flex-col gap-1 rounded-lg 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">
<Avatar.Root>
<Avatar.Image

View File

@@ -0,0 +1,18 @@
export function DepotIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
>
<path d="M20 5.7V12m0-6.3c0 1.491-3.582 2.7-8 2.7S4 7.191 4 5.7m16 0C20 4.209 16.418 3 12 3S4 4.209 4 5.7M20 12v6.131C20 19.716 16.418 21 12 21s-8-1.284-8-2.869V12m16 0c0 1.491-3.582 2.7-8 2.7S4 13.491 4 12m0 0V5.7M16 11h.01M16 17h.01" />
</svg>
);
}

View File

@@ -85,3 +85,5 @@ export * from './light';
export * from './dark';
export * from './system';
export * from './announcement';
export * from './depot';
export * from './search';

View File

@@ -1,19 +1,18 @@
import { SVGProps } from 'react';
export function NwcIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
export function NwcIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
>
<path
fill="currentColor"
d="M14.002 2.401c0-1.484-1.925-2.066-2.748-.832L3.188 13.668c-.665.997.05 2.332 1.248 2.332h5.566V21.6c0 1.484 1.925 2.067 2.748.832l8.066-12.099C21.48 9.335 20.766 8 19.568 8h-5.566V2.401z"
></path>
<path d="M2 14.5V11c0-2.8 0-4.2.545-5.27A5 5 0 0 1 4.73 3.545C5.8 3 7.2 3 10 3h3.5c1.398 0 2.097 0 2.648.228a3 3 0 0 1 1.624 1.624c.207.5.226 1.123.228 2.28M2 14.5c0 1.33 0 2.495.38 3.413a5 5 0 0 0 2.707 2.706c.696.289 1.534.359 2.913.376M2 14.5c0-2.33 0-3.495.38-4.413A5 5 0 0 1 5.088 7.38C6.005 7 7.17 7 9.5 7h5c1.634 0 2.695 0 3.5.131M14 12h3m1-4.869c.343.056.639.136.913.25a5 5 0 0 1 2.706 2.706c.289.696.359 1.534.376 2.913m-3.03 3h.037A2.999 2.999 0 0 1 22 19c0 1.657-1.342 3-2.998 3h-.037m-3.93-6h-.037A2.999 2.999 0 0 0 12 19c0 1.657 1.342 3 2.998 3h.037m.962-3h1.999" />
</svg>
);
}

View File

@@ -1,15 +1,18 @@
import { SVGProps } from 'react';
export function PlusIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
export function PlusIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M12 3.75V12M12 12V20.25M12 12H3.75M12 12H20.25"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
>
<path d="M12 19v-7m0 0V5m0 7H5m7 0h7" />
</svg>
);
}

View File

@@ -1,21 +1,18 @@
import { SVGProps } from 'react';
export function RelayIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
export function RelayIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
>
<path
fill="currentColor"
fillRule="evenodd"
d="M4 5a3 3 0 013-3h10a3 3 0 013 3v11c0 .889-.386 1.687-1 2.236V20a2 2 0 01-2 2H7a2 2 0 01-2-2v-1.75-.014c-.614-.55-1-1.348-1-2.236V5zm3-1a1 1 0 00-1 1v11a1 1 0 001 1h10a1 1 0 001-1V5a1 1 0 00-1-1H7zm0 2a1 1 0 011-1h8a1 1 0 011 1v6a1 1 0 01-1 1H8a1 1 0 01-1-1V6zm6 9a1 1 0 011-1h2a1 1 0 110 2h-2a1 1 0 01-1-1z"
clipRule="evenodd"
></path>
<path d="M7 12h10M7 12c-.464 0-.697 0-.892.022a3.5 3.5 0 0 0-3.086 3.086C3 15.303 3 15.536 3 16s0 .697.022.892a3.5 3.5 0 0 0 3.086 3.086C6.303 20 6.536 20 7 20h10c.464 0 .697 0 .892-.022a3.5 3.5 0 0 0 3.086-3.086C21 16.697 21 16.464 21 16s0-.697-.022-.892a3.5 3.5 0 0 0-3.086-3.086C17.697 12 17.464 12 17 12M7 12c-.464 0-.697 0-.892-.022a3.5 3.5 0 0 1-3.086-3.086C3 8.697 3 8.464 3 8s0-.697.022-.892a3.5 3.5 0 0 1 3.086-3.086C6.303 4 6.536 4 7 4h10c.464 0 .697 0 .892.022a3.5 3.5 0 0 1 3.086 3.086C21 7.303 21 7.536 21 8s0 .697-.022.892a3.5 3.5 0 0 1-3.086 3.086C17.697 12 17.464 12 17 12m-4-4h.01M17 8h.01M13 16h.01M17 16h.01" />
</svg>
);
}

View File

@@ -0,0 +1,18 @@
export function SearchIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
>
<path d="m21 21-3.49-3.49m0 0A8.5 8.5 0 1 0 5.49 5.49a8.5 8.5 0 0 0 12.02 12.02Z" />
</svg>
);
}

View File

@@ -1,19 +1,18 @@
import { SVGProps } from 'react';
export function ThreadIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
export function ThreadIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
>
<path
fill="currentColor"
d="M12 19v1a1 1 0 001-1h-1zm8-9a1 1 0 102 0h-2zm0 4a1 1 0 10-2 0h2zm-2 6a1 1 0 102 0h-2zm-2-4a1 1 0 100 2v-2zm6 2a1 1 0 100-2v2zM4 17V7H2v10h2zm8 1H5v2h7v-2zm8-11v3h2V7h-2zM5 6h7V4H5v2zm7 0h7V4h-7v2zm1 13V5h-2v14h2zm5-5v3h2v-3h-2zm0 3v3h2v-3h-2zm-2 1h3v-2h-3v2zm3 0h3v-2h-3v2zm3-11a3 3 0 00-3-3v2a1 1 0 011 1h2zM4 7a1 1 0 011-1V4a3 3 0 00-3 3h2zM2 17a3 3 0 003 3v-2a1 1 0 01-1-1H2z"
></path>
<path d="M16.193 18.866c.143 0 .306.012.632.036l1.938.138c.771.055 1.157.083 1.446-.054.253-.12.457-.324.577-.577.137-.289.11-.675.054-1.446l-.138-1.938a10.8 10.8 0 0 1-.036-.632c-.002-.232-.005-.15.013-.38.01-.143.088-.685.24-1.768A8.1 8.1 0 0 0 5.642 7.5m8.159 8.1a5.4 5.4 0 1 0-10.733.856c.096.6.144.9.152.992.012.139.011.108.01.247 0 .093-.008.204-.023.428l-.1 1.385c-.036.515-.055.772.037.965a.81.81 0 0 0 .385.384c.192.092.45.073.964.037l1.385-.1a6.97 6.97 0 0 1 .428-.024c.14 0 .108-.001.247.011.093.008.393.056.992.152a5.387 5.387 0 0 0 4.893-1.745 5.38 5.38 0 0 0 1.363-3.588Z" />
</svg>
);
}

View File

@@ -1,114 +1,92 @@
import { Platform } from '@tauri-apps/plugin-os';
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
import { NavLink, Outlet } from 'react-router-dom';
import { twMerge } from 'tailwind-merge';
import {
AdvancedSettingsIcon,
ArrowLeftIcon,
InfoIcon,
SecureIcon,
SettingsIcon,
UserIcon,
} from '@shared/icons';
import { WindowTitleBar } from '@shared/titlebar';
export function SettingsLayout({ platform }: { platform: Platform }) {
const navigate = useNavigate();
export function SettingsLayout() {
return (
<div className="flex h-screen w-screen flex-col bg-neutral-50 dark:bg-neutral-950">
{platform !== 'macos' ? (
<WindowTitleBar platform={platform} />
) : (
<div data-tauri-drag-region className="h-9 shrink-0" />
)}
<div className="flex h-full min-h-0 w-full flex-col gap-8 overflow-y-auto">
<div className="flex h-20 w-full items-center justify-between border-b border-neutral-200 px-2 pb-2 dark:border-neutral-900">
<div>
<button
type="button"
onClick={() => navigate(-1)}
className="inline-flex h-12 w-12 items-center justify-center rounded-xl"
>
<ArrowLeftIcon className="h-5 w-5" />
</button>
</div>
<div className="flex items-center gap-0.5">
<NavLink
to="/settings/"
end
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: ''
)
}
>
<UserIcon className="h-6 w-6" />
<p className="text-sm font-medium">User</p>
</NavLink>
<NavLink
to="/settings/general"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: ''
)
}
>
<SettingsIcon className="h-6 w-6" />
<p className="text-sm font-medium">General</p>
</NavLink>
<NavLink
to="/settings/backup"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: ''
)
}
>
<SecureIcon className="h-6 w-6" />
<p className="text-sm font-medium">Backup</p>
</NavLink>
<NavLink
to="/settings/advanced"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: ''
)
}
>
<AdvancedSettingsIcon className="h-6 w-6" />
<p className="text-sm font-medium">Advanced</p>
</NavLink>
<NavLink
to="/settings/about"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: ''
)
}
>
<InfoIcon className="h-6 w-6" />
<p className="text-sm font-medium">About</p>
</NavLink>
</div>
<div />
<div className="flex h-full min-h-0 w-full flex-col gap-8 overflow-y-auto">
<div className="flex h-24 w-full items-center justify-center border-b border-neutral-200 px-2 dark:border-neutral-900">
<div className="flex items-center gap-0.5">
<NavLink
to="/settings/"
end
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900'
: ''
)
}
>
<UserIcon className="h-6 w-6" />
<p className="text-sm font-medium">User</p>
</NavLink>
<NavLink
to="/settings/general"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900'
: ''
)
}
>
<SettingsIcon className="h-6 w-6" />
<p className="text-sm font-medium">General</p>
</NavLink>
<NavLink
to="/settings/backup"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900'
: ''
)
}
>
<SecureIcon className="h-6 w-6" />
<p className="text-sm font-medium">Backup</p>
</NavLink>
<NavLink
to="/settings/advanced"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900'
: ''
)
}
>
<AdvancedSettingsIcon className="h-6 w-6" />
<p className="text-sm font-medium">Advanced</p>
</NavLink>
<NavLink
to="/settings/about"
className={({ isActive }) =>
twMerge(
'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900'
: ''
)
}
>
<InfoIcon className="h-6 w-6" />
<p className="text-sm font-medium">About</p>
</NavLink>
</div>
<Outlet />
</div>
<Outlet />
</div>
);
}

View File

@@ -1,12 +1,16 @@
import { Link, NavLink } from 'react-router-dom';
import { twMerge } from 'tailwind-merge';
import { ActiveAccount } from '@shared/accounts/active';
import { ChatsIcon, ComposeIcon, HomeIcon, NwcIcon, RelayIcon } from '@shared/icons';
import { compactNumber } from '@utils/formater';
import {
DepotIcon,
HomeIcon,
NwcIcon,
PlusIcon,
RelayIcon,
SearchIcon,
} from '@shared/icons';
export function Navigation() {
const newMessages = 0;
return (
<div className="flex h-full w-full flex-col justify-between p-3">
<div className="flex flex-1 flex-col gap-5">
@@ -19,7 +23,7 @@ export function Navigation() {
<>
<div
className={twMerge(
'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
'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'
@@ -27,33 +31,14 @@ export function Navigation() {
>
<HomeIcon className="h-6 w-6" />
</div>
<div className="text-sm font-medium text-black dark:text-white">Home</div>
</>
)}
</NavLink>
<NavLink
to="/chats"
preventScrollReset={true}
className="inline-flex flex-col items-center justify-center"
>
{({ isActive }) => (
<>
<div
className={twMerge(
'relative inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
isActive
? 'bg-black/10 text-black dark:bg-white/10 dark:text-white'
: 'text-black/50 dark:text-neutral-400'
'text-sm text-black dark:text-white',
isActive ? 'font-semibold' : 'font-medium'
)}
>
<ChatsIcon className="h-6 w-6" />
{newMessages > 0 ? (
<div className="absolute right-0 top-0 inline-flex h-5 w-5 items-center justify-center rounded-full bg-blue-500 text-[9px] font-medium text-white">
{compactNumber.format(newMessages)}
</div>
) : null}
Home
</div>
<div className="text-sm font-medium text-black dark:text-white">Chats</div>
</>
)}
</NavLink>
@@ -66,7 +51,7 @@ export function Navigation() {
<>
<div
className={twMerge(
'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
'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'
@@ -74,7 +59,14 @@ export function Navigation() {
>
<RelayIcon className="h-6 w-6" />
</div>
<div className="text-sm font-medium text-black dark:text-white">Relays</div>
<div
className={twMerge(
'text-sm text-black dark:text-white',
isActive ? 'font-semibold' : 'font-medium'
)}
>
Relays
</div>
</>
)}
</NavLink>
@@ -87,15 +79,50 @@ export function Navigation() {
<>
<div
className={twMerge(
'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
'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'
)}
>
<RelayIcon className="h-6 w-6" />
<DepotIcon className="h-6 w-6" />
</div>
<div
className={twMerge(
'text-sm text-black dark:text-white',
isActive ? 'font-semibold' : 'font-medium'
)}
>
Depot
</div>
</>
)}
</NavLink>
<NavLink
to="/nwc"
preventScrollReset={true}
className="inline-flex flex-col items-center justify-center"
>
{({ isActive }) => (
<>
<div
className={twMerge(
'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'
)}
>
<NwcIcon className="h-6 w-6" />
</div>
<div
className={twMerge(
'text-sm text-black dark:text-white',
isActive ? 'font-semibold' : 'font-medium'
)}
>
Wallet
</div>
<div className="text-sm font-medium text-black dark:text-white">Depot</div>
</>
)}
</NavLink>
@@ -103,15 +130,15 @@ export function Navigation() {
<div className="flex shrink-0 flex-col gap-3 p-1">
<Link
to="/new/"
className="flex aspect-square h-auto w-full items-center justify-center rounded-lg bg-neutral-100 text-black hover:bg-blue-500 hover:text-white dark:bg-neutral-900 dark:text-white dark:hover:bg-blue-500"
className="flex aspect-square h-auto w-full items-center justify-center rounded-xl bg-black/10 text-black hover:bg-blue-500 hover:text-white dark:bg-white/10 dark:text-white dark:hover:bg-blue-500"
>
<ComposeIcon className="h-5 w-5" />
<PlusIcon className="h-5 w-5" />
</Link>
<Link
to="/nwc"
className="flex aspect-square h-auto w-full items-center justify-center rounded-lg bg-neutral-100 hover:bg-blue-500 hover:text-white dark:bg-neutral-900 dark:hover:bg-blue-500"
className="flex aspect-square h-auto w-full items-center justify-center rounded-xl bg-black/10 hover:bg-blue-500 hover:text-white dark:bg-white/10 dark:hover:bg-blue-500"
>
<NwcIcon className="h-5 w-5" />
<SearchIcon className="h-5 w-5" />
</Link>
<ActiveAccount />
</div>

View File

@@ -69,6 +69,7 @@ export function NewsfeedWidget() {
<Widget.Root>
<Widget.Header
id="9999"
queryKey={['newsfeed']}
title="Timeline"
icon={<TimelineIcon className="h-5 w-5" />}
/>

View File

@@ -126,6 +126,7 @@ export function NotificationWidget() {
<Widget.Root>
<Widget.Header
id="9998"
queryKey={['notification']}
title="Notification"
icon={<AnnouncementIcon className="h-5 w-5" />}
/>
@@ -138,8 +139,8 @@ export function NotificationWidget() {
</div>
</div>
) : allEvents.length < 1 ? (
<div className="flex h-[400px] w-full flex-col items-center justify-center">
<p className="mb-2 text-4xl">🎉</p>
<div className="my-3 flex w-full items-center justify-center gap-2">
<div>🎉</div>
<p className="text-center font-medium text-neutral-900 dark:text-neutral-100">
Hmm! Nothing new yet.
</p>