feat(router): restructure

This commit is contained in:
reya 2023-12-14 13:22:03 +07:00
parent d9ab7893e0
commit 2fcc4dead1
13 changed files with 404 additions and 643 deletions

View File

@ -78,7 +78,7 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.49.0", "react-hook-form": "^7.49.0",
"react-hotkeys-hook": "^4.4.1", "react-hotkeys-hook": "^4.4.1",
"react-router-dom": "^6.20.1", "react-router-dom": "^6.21.0",
"react-string-replace": "^1.1.1", "react-string-replace": "^1.1.1",
"sonner": "^1.2.4", "sonner": "^1.2.4",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",

View File

@ -186,8 +186,8 @@ dependencies:
specifier: ^4.4.1 specifier: ^4.4.1
version: 4.4.1(react-dom@18.2.0)(react@18.2.0) version: 4.4.1(react-dom@18.2.0)(react@18.2.0)
react-router-dom: react-router-dom:
specifier: ^6.20.1 specifier: ^6.21.0
version: 6.20.1(react-dom@18.2.0)(react@18.2.0) version: 6.21.0(react-dom@18.2.0)(react@18.2.0)
react-string-replace: react-string-replace:
specifier: ^1.1.1 specifier: ^1.1.1
version: 1.1.1 version: 1.1.1
@ -1774,8 +1774,8 @@ packages:
type-fest: 2.19.0 type-fest: 2.19.0
dev: false dev: false
/@remix-run/router@1.13.1: /@remix-run/router@1.14.0:
resolution: {integrity: sha512-so+DHzZKsoOcoXrILB4rqDkMDy7NLMErRdOxvzvOKb507YINKUP4Di+shbTZDhSE/pBZ+vr7XGIpcOO0VLSA+Q==} resolution: {integrity: sha512-WOHih+ClN7N8oHk9N4JUiMxQJmRVaOxcg8w7F/oHUXzJt920ekASLI/7cYX8XkntDWRhLZtsk6LbGrkgOAvi5A==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
dev: false dev: false
@ -5272,26 +5272,26 @@ packages:
use-sidecar: 1.1.2(@types/react@18.2.43)(react@18.2.0) use-sidecar: 1.1.2(@types/react@18.2.43)(react@18.2.0)
dev: false dev: false
/react-router-dom@6.20.1(react-dom@18.2.0)(react@18.2.0): /react-router-dom@6.21.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-npzfPWcxfQN35psS7rJgi/EW0Gx6EsNjfdJSAk73U/HqMEJZ2k/8puxfwHFgDQhBGmS3+sjnGbMdMSV45axPQw==} resolution: {integrity: sha512-1dUdVj3cwc1npzJaf23gulB562ESNvxf7E4x8upNJycqyUm5BRRZ6dd3LrlzhtLaMrwOCO8R0zoiYxdaJx4LlQ==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
peerDependencies: peerDependencies:
react: '>=16.8' react: '>=16.8'
react-dom: '>=16.8' react-dom: '>=16.8'
dependencies: dependencies:
'@remix-run/router': 1.13.1 '@remix-run/router': 1.14.0
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
react-router: 6.20.1(react@18.2.0) react-router: 6.21.0(react@18.2.0)
dev: false dev: false
/react-router@6.20.1(react@18.2.0): /react-router@6.21.0(react@18.2.0):
resolution: {integrity: sha512-ccvLrB4QeT5DlaxSFFYi/KR8UMQ4fcD8zBcR71Zp1kaYTC5oJKYAp1cbavzGrogwxca+ubjkd7XjFZKBW8CxPA==} resolution: {integrity: sha512-hGZ0HXbwz3zw52pLZV3j3+ec+m/PQ9cTpBvqjFQmy2XVUWGn5MD+31oXHb6dVTxYzmAeaiUBYjkoNz66n3RGCg==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
peerDependencies: peerDependencies:
react: '>=16.8' react: '>=16.8'
dependencies: dependencies:
'@remix-run/router': 1.13.1 '@remix-run/router': 1.14.0
react: 18.2.0 react: 18.2.0
dev: false dev: false

View File

@ -1,24 +1,17 @@
import { fetch } from '@tauri-apps/plugin-http'; import { fetch } from '@tauri-apps/plugin-http';
import { RouterProvider, createBrowserRouter, defer, redirect } from 'react-router-dom'; import { RouterProvider, createBrowserRouter, defer, redirect } from 'react-router-dom';
import { ChatsScreen } from '@app/chats';
import { ErrorScreen } from '@app/error'; import { ErrorScreen } from '@app/error';
import { useArk } from '@libs/ark'; import { useArk } from '@libs/ark';
import { LoaderIcon } from '@shared/icons'; import { LoaderIcon } from '@shared/icons';
import { AppLayout } from '@shared/layouts/app'; import { AppLayout } from '@shared/layouts/app';
import { AuthLayout } from '@shared/layouts/auth'; import { AuthLayout } from '@shared/layouts/auth';
import { NewLayout } from '@shared/layouts/new'; import { ComposerLayout } from '@shared/layouts/composer';
import { NoteLayout } from '@shared/layouts/note'; import { HomeLayout } from '@shared/layouts/home';
import { SettingsLayout } from '@shared/layouts/settings'; import { SettingsLayout } from '@shared/layouts/settings';
import './app.css';
export default function App() { export default function App() {
const ark = useArk(); const ark = useArk();
const accountLoader = async () => {
if (!ark.account) return redirect('/auth/welcome');
return null;
};
const relayLoader = async ({ params }) => { const relayLoader = async ({ params }) => {
return defer({ return defer({
relay: fetch(`https://${params.url}`, { relay: fetch(`https://${params.url}`, {
@ -32,25 +25,24 @@ export default function App() {
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
path: '/', element: <AppLayout platform={ark.platform} />,
element: <AppLayout />,
errorElement: <ErrorScreen />,
loader: accountLoader,
children: [ children: [
{ {
path: '', path: '/',
element: <HomeLayout />,
errorElement: <ErrorScreen />,
loader: async () => {
if (!ark.account) return redirect('auth/welcome');
return null;
},
children: [
{
index: true,
async lazy() { async lazy() {
const { HomeScreen } = await import('@app/home'); const { HomeScreen } = await import('@app/home');
return { Component: HomeScreen }; return { Component: HomeScreen };
}, },
}, },
{
path: 'users/:pubkey',
async lazy() {
const { UserScreen } = await import('@app/users');
return { Component: UserScreen };
},
},
{ {
path: 'nwc', path: 'nwc',
async lazy() { async lazy() {
@ -74,28 +66,25 @@ export default function App() {
}, },
}, },
{ {
path: 'chats', path: 'users/:pubkey',
element: <ChatsScreen />,
errorElement: <ErrorScreen />,
children: [
{
path: 'chat/:pubkey',
async lazy() { async lazy() {
const { ChatScreen } = await import('@app/chats/chat'); const { UserScreen } = await import('@app/users');
return { Component: ChatScreen }; return { Component: UserScreen };
}, },
}, },
],
},
],
},
{ {
path: '/new', path: 'events/:id',
element: <NewLayout />, async lazy() {
errorElement: <ErrorScreen />, const { TextNoteScreen } = await import('@app/notes/text');
return { Component: TextNoteScreen };
},
},
{
path: 'new',
element: <ComposerLayout />,
children: [ children: [
{ {
path: '', index: true,
async lazy() { async lazy() {
const { NewPostScreen } = await import('@app/new/post'); const { NewPostScreen } = await import('@app/new/post');
return { Component: NewPostScreen }; return { Component: NewPostScreen };
@ -124,29 +113,10 @@ export default function App() {
}, },
], ],
}, },
{
path: '/notes',
element: <NoteLayout />,
errorElement: <ErrorScreen />,
children: [
{
path: 'text/:id',
async lazy() {
const { TextNoteScreen } = await import('@app/notes/text');
return { Component: TextNoteScreen };
},
},
{
path: 'article/:id',
async lazy() {
const { ArticleNoteScreen } = await import('@app/notes/article');
return { Component: ArticleNoteScreen };
},
},
], ],
}, },
{ {
path: '/auth', path: 'auth',
element: <AuthLayout />, element: <AuthLayout />,
errorElement: <ErrorScreen />, errorElement: <ErrorScreen />,
children: [ children: [
@ -202,33 +172,39 @@ export default function App() {
{ {
path: 'tutorials/widget', path: 'tutorials/widget',
async lazy() { async lazy() {
const { TutorialWidgetScreen } = await import('@app/auth/tutorials/widget'); const { TutorialWidgetScreen } = await import(
'@app/auth/tutorials/widget'
);
return { Component: TutorialWidgetScreen }; return { Component: TutorialWidgetScreen };
}, },
}, },
{ {
path: 'tutorials/posting', path: 'tutorials/posting',
async lazy() { async lazy() {
const { TutorialPostingScreen } = await import('@app/auth/tutorials/posting'); const { TutorialPostingScreen } = await import(
'@app/auth/tutorials/posting'
);
return { Component: TutorialPostingScreen }; return { Component: TutorialPostingScreen };
}, },
}, },
{ {
path: 'tutorials/finish', path: 'tutorials/finish',
async lazy() { async lazy() {
const { TutorialFinishScreen } = await import('@app/auth/tutorials/finish'); const { TutorialFinishScreen } = await import(
'@app/auth/tutorials/finish'
);
return { Component: TutorialFinishScreen }; return { Component: TutorialFinishScreen };
}, },
}, },
], ],
}, },
{ {
path: '/settings', path: 'settings',
element: <SettingsLayout />, element: <SettingsLayout />,
errorElement: <ErrorScreen />, errorElement: <ErrorScreen />,
children: [ children: [
{ {
path: '', index: true,
async lazy() { async lazy() {
const { UserSettingScreen } = await import('@app/settings'); const { UserSettingScreen } = await import('@app/settings');
return { Component: UserSettingScreen }; return { Component: UserSettingScreen };
@ -278,6 +254,8 @@ export default function App() {
}, },
], ],
}, },
],
},
]); ]);
return ( return (

View File

@ -1,78 +1,6 @@
import { NDKKind } from '@nostr-dev-kit/ndk'; import { Link } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useArk } from '@libs/ark';
import { LoaderIcon } from '@shared/icons';
import { FETCH_LIMIT } from '@utils/constants';
export function FinishScreen() { export function FinishScreen() {
const ark = useArk();
const [loading, setLoading] = useState(false);
const queryClient = useQueryClient();
const navigate = useNavigate();
const prefetch = async () => {
if (!ark.account.contacts.length) return navigate('/');
try {
setLoading(true);
// prefetch newsfeed
await queryClient.prefetchInfiniteQuery({
queryKey: ['newsfeed'],
initialPageParam: 0,
queryFn: async ({
signal,
pageParam,
}: {
signal: AbortSignal;
pageParam: number;
}) => {
return await ark.getInfiniteEvents({
filter: {
kinds: [NDKKind.Text, NDKKind.Repost],
authors: !ark.account.contacts.length
? [ark.account.pubkey]
: ark.account.contacts,
},
limit: FETCH_LIMIT,
pageParam,
signal,
});
},
});
// prefetch notification
await queryClient.prefetchInfiniteQuery({
queryKey: ['notification'],
initialPageParam: 0,
queryFn: async ({
signal,
pageParam,
}: {
signal: AbortSignal;
pageParam: number;
}) => {
return await ark.getInfiniteEvents({
filter: {
kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap],
'#p': [ark.account.pubkey],
},
limit: FETCH_LIMIT,
pageParam,
signal,
});
},
});
navigate('/');
} catch (e) {
console.error(e);
}
};
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<div className="mx-auto flex w-full max-w-md flex-col gap-10"> <div className="mx-auto flex w-full max-w-md flex-col gap-10">
@ -89,13 +17,12 @@ export function FinishScreen() {
> >
Start tutorial Start tutorial
</Link> </Link>
<button <Link
type="button" to="/"
onClick={prefetch}
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-neutral-100 font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800" className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-neutral-100 font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
> >
{loading ? <LoaderIcon className="h-4 w-4 animate-spin" /> : 'Skip'} Skip
</button> </Link>
<p className="text-center text-sm font-medium text-neutral-500 dark:text-neutral-600"> <p className="text-center text-sm font-medium text-neutral-500 dark:text-neutral-600">
You need to restart app to make changes in previous step take effect or you You need to restart app to make changes in previous step take effect or you
can continue with Lume default settings can continue with Lume default settings

View File

@ -1,78 +1,6 @@
import { NDKKind } from '@nostr-dev-kit/ndk'; import { Link } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useArk } from '@libs/ark';
import { LoaderIcon } from '@shared/icons';
import { FETCH_LIMIT } from '@utils/constants';
export function TutorialFinishScreen() { export function TutorialFinishScreen() {
const ark = useArk();
const [loading, setLoading] = useState(false);
const queryClient = useQueryClient();
const navigate = useNavigate();
const prefetch = async () => {
if (!ark.account.contacts.length) return navigate('/');
try {
setLoading(true);
// prefetch newsfeed
await queryClient.prefetchInfiniteQuery({
queryKey: ['newsfeed'],
initialPageParam: 0,
queryFn: async ({
signal,
pageParam,
}: {
signal: AbortSignal;
pageParam: number;
}) => {
return await ark.getInfiniteEvents({
filter: {
kinds: [NDKKind.Text, NDKKind.Repost],
authors: !ark.account.contacts.length
? [ark.account.pubkey]
: ark.account.contacts,
},
limit: FETCH_LIMIT,
pageParam,
signal,
});
},
});
// prefetch notification
await queryClient.prefetchInfiniteQuery({
queryKey: ['notification'],
initialPageParam: 0,
queryFn: async ({
signal,
pageParam,
}: {
signal: AbortSignal;
pageParam: number;
}) => {
return await ark.getInfiniteEvents({
filter: {
kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap],
'#p': [ark.account.pubkey],
},
limit: FETCH_LIMIT,
pageParam,
signal,
});
},
});
navigate('/');
} catch (e) {
console.error(e);
}
};
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<div className="mx-auto flex w-full max-w-md flex-col gap-10"> <div className="mx-auto flex w-full max-w-md flex-col gap-10">
@ -83,17 +11,12 @@ export function TutorialFinishScreen() {
</h1> </h1>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<button <Link
type="button" to="/"
onClick={prefetch}
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 font-medium text-white hover:bg-blue-600" className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 font-medium text-white hover:bg-blue-600"
> >
{loading ? ( Start using Lume
<LoaderIcon className="h-4 w-4 animate-spin" /> </Link>
) : (
'Start using Lume'
)}
</button>
<Link <Link
to="https://nostr.how/" to="https://nostr.how/"
target="_blank" target="_blank"

View File

@ -4,6 +4,7 @@ import { createRoot } from 'react-dom/client';
import { Toaster } from 'sonner'; import { Toaster } from 'sonner';
import { ArkProvider } from '@libs/ark/provider'; import { ArkProvider } from '@libs/ark/provider';
import App from './app'; import App from './app';
import './app.css';
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
@ -18,7 +19,7 @@ const root = createRoot(container);
root.render( root.render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} /> <ReactQueryDevtools initialIsOpen={false} buttonPosition="top-right" />
<Toaster position="top-center" theme="system" closeButton /> <Toaster position="top-center" theme="system" closeButton />
<ArkProvider> <ArkProvider>
<App /> <App />

View File

@ -1,39 +1,25 @@
import { type Platform } from '@tauri-apps/plugin-os';
import { Outlet, ScrollRestoration } from 'react-router-dom'; import { Outlet, ScrollRestoration } from 'react-router-dom';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
import { useArk } from '@libs/ark';
import { Navigation } from '@shared/navigation';
import { WindowTitleBar } from '@shared/titlebar'; import { WindowTitleBar } from '@shared/titlebar';
export function AppLayout() { export function AppLayout({ platform }: { platform: Platform }) {
const ark = useArk();
return ( return (
<div <div
className={twMerge( className={twMerge(
'flex h-screen w-screen flex-col', 'flex h-screen w-screen flex-col',
ark.platform !== 'macos' ? 'bg-neutral-50 dark:bg-neutral-950' : '' platform !== 'macos' ? 'bg-neutral-50 dark:bg-neutral-950' : ''
)} )}
> >
{ark.platform !== 'macos' ? ( {platform !== 'macos' ? (
<WindowTitleBar platform={ark.platform} /> <WindowTitleBar platform={platform} />
) : ( ) : (
<div data-tauri-drag-region className="h-9" /> <div data-tauri-drag-region className="h-9 shrink-0" />
)} )}
<div className="flex h-full min-h-0 w-full"> <div className="h-full w-full">
<div
data-tauri-drag-region
className={twMerge(
'h-full w-[64px] shrink-0',
ark.platform !== 'macos' ? 'pt-2' : 'pt-0'
)}
>
<Navigation />
</div>
<div className="min-h-0 flex-1 rounded-tl-lg 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 />
<ScrollRestoration /> <ScrollRestoration />
</div> </div>
</div> </div>
</div>
); );
} }

View File

@ -1,22 +1,10 @@
import { Outlet, ScrollRestoration } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { useArk } from '@libs/ark';
import { WindowTitleBar } from '@shared/titlebar';
export function AuthLayout() { export function AuthLayout() {
const ark = useArk();
return ( return (
<div className="flex h-screen w-screen flex-col">
{ark.platform !== 'macos' ? (
<WindowTitleBar platform={ark.platform} />
) : (
<div data-tauri-drag-region className="h-9" />
)}
<div className="h-full w-full px-2.5 pb-2.5 pt-1"> <div className="h-full w-full px-2.5 pb-2.5 pt-1">
<div className="flex h-full min-h-0 w-full rounded-lg bg-white p-3 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="flex h-full min-h-0 w-full rounded-lg bg-white p-3 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 />
<ScrollRestoration />
</div>
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,52 @@
import { NavLink, Outlet, useLocation } from 'react-router-dom';
import { twMerge } from 'tailwind-merge';
export function ComposerLayout() {
const location = useLocation();
return (
<div className="container mx-auto h-full px-8 pt-8">
<div className="mb-8 flex h-10 shrink-0 items-center gap-3">
{location.pathname !== '/new/privkey' ? (
<div className="flex h-10 items-center gap-2 rounded-lg bg-neutral-100 px-0.5 dark:bg-neutral-800">
<NavLink
to="/new/"
end
className={({ isActive }) =>
twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
isActive ? 'bg-white shadow dark:bg-black' : 'bg-transparent'
)
}
>
Post
</NavLink>
<NavLink
to="/new/article"
className={({ isActive }) =>
twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
isActive ? 'bg-white shadow dark:bg-black' : 'bg-transparent'
)
}
>
Article
</NavLink>
<NavLink
to="/new/file"
className={({ isActive }) =>
twMerge(
'inline-flex h-9 w-28 items-center justify-center rounded-lg text-sm font-medium',
isActive ? 'bg-white shadow dark:bg-black' : 'bg-transparent'
)
}
>
File Sharing
</NavLink>
</div>
) : null}
</div>
<Outlet />
</div>
);
}

View File

@ -0,0 +1,13 @@
import { Outlet } from 'react-router-dom';
import { Navigation } from '@shared/navigation';
export function HomeLayout() {
return (
<div className="flex h-full w-full">
<Navigation />
<div className="min-h-0 flex-1 rounded-tl-lg 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 />
</div>
</div>
);
}

View File

@ -1,75 +0,0 @@
import { Link, NavLink, Outlet, useLocation } from 'react-router-dom';
import { twMerge } from 'tailwind-merge';
import { useArk } from '@libs/ark';
import { ArrowLeftIcon } from '@shared/icons';
import { WindowTitleBar } from '@shared/titlebar';
export function NewLayout() {
const ark = useArk();
const location = useLocation();
return (
<div className="flex h-screen w-screen flex-col bg-neutral-50 dark:bg-neutral-950">
{ark.platform !== 'macos' ? (
<WindowTitleBar platform={ark.platform} />
) : (
<div data-tauri-drag-region className="h-9 shrink-0" />
)}
<div data-tauri-drag-region className="h-4 shrink-0" />
<div className="container mx-auto grid flex-1 grid-cols-8 px-4">
<div className="col-span-1">
<Link
to="/"
className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
<ArrowLeftIcon className="h-5 w-5" />
</Link>
</div>
<div className="col-span-6 flex flex-col">
<div className="mb-8 flex h-10 shrink-0 items-center gap-3">
{location.pathname !== '/new/privkey' ? (
<div className="flex h-10 items-center gap-2 rounded-lg bg-neutral-100 px-0.5 dark:bg-neutral-800">
<NavLink
to="/new/"
end
className={({ isActive }) =>
twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
isActive ? 'bg-white shadow dark:bg-black' : 'bg-transparent'
)
}
>
Post
</NavLink>
<NavLink
to="/new/article"
className={({ isActive }) =>
twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
isActive ? 'bg-white shadow dark:bg-black' : 'bg-transparent'
)
}
>
Article
</NavLink>
<NavLink
to="/new/file"
className={({ isActive }) =>
twMerge(
'inline-flex h-9 w-28 items-center justify-center rounded-lg text-sm font-medium',
isActive ? 'bg-white shadow dark:bg-black' : 'bg-transparent'
)
}
>
File Sharing
</NavLink>
</div>
) : null}
</div>
<Outlet />
</div>
<div className="col-span-1" />
</div>
</div>
);
}

View File

@ -1,21 +0,0 @@
import { Outlet, ScrollRestoration } from 'react-router-dom';
import { useArk } from '@libs/ark';
import { WindowTitleBar } from '@shared/titlebar';
export function NoteLayout() {
const ark = useArk();
return (
<div className="flex h-screen w-screen flex-col bg-neutral-50 dark:bg-neutral-950">
{ark.platform !== 'macos' ? (
<WindowTitleBar platform={ark.platform} />
) : (
<div data-tauri-drag-region className="h-9" />
)}
<div className="flex h-full min-h-0 w-full">
<Outlet />
<ScrollRestoration />
</div>
</div>
);
}

View File

@ -1,6 +1,5 @@
import { NavLink, Outlet, ScrollRestoration, useNavigate } from 'react-router-dom'; import { NavLink, Outlet, useNavigate } from 'react-router-dom';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
import { useArk } from '@libs/ark';
import { import {
AdvancedSettingsIcon, AdvancedSettingsIcon,
ArrowLeftIcon, ArrowLeftIcon,
@ -9,26 +8,18 @@ import {
SettingsIcon, SettingsIcon,
UserIcon, UserIcon,
} from '@shared/icons'; } from '@shared/icons';
import { WindowTitleBar } from '@shared/titlebar';
export function SettingsLayout() { export function SettingsLayout() {
const ark = useArk();
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<div className="flex h-screen w-screen flex-col bg-neutral-50 dark:bg-neutral-950"> <div className="flex h-full min-h-0 w-full flex-col gap-8 overflow-y-auto">
{ark.platform !== 'macos' ? (
<WindowTitleBar platform={ark.platform} />
) : (
<div data-tauri-drag-region className="h-9" />
)}
<div className="flex h-full min-h-0 w-full flex-col gap-8 overflow-y-auto pb-10">
<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 className="flex h-20 w-full items-center justify-between border-b border-neutral-200 px-2 pb-2 dark:border-neutral-900">
<div> <div>
<button <button
type="button" type="button"
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-neutral-100 dark:bg-neutral-900" className="inline-flex h-12 w-12 items-center justify-center rounded-xl"
> >
<ArrowLeftIcon className="h-5 w-5" /> <ArrowLeftIcon className="h-5 w-5" />
</button> </button>
@ -41,7 +32,7 @@ export function SettingsLayout() {
twMerge( 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', '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 isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800' ? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: '' : ''
) )
} }
@ -55,7 +46,7 @@ export function SettingsLayout() {
twMerge( 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', '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 isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800' ? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: '' : ''
) )
} }
@ -69,7 +60,7 @@ export function SettingsLayout() {
twMerge( 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', '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 isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800' ? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: '' : ''
) )
} }
@ -83,7 +74,7 @@ export function SettingsLayout() {
twMerge( 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', '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 isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800' ? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: '' : ''
) )
} }
@ -97,7 +88,7 @@ export function SettingsLayout() {
twMerge( 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', '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 isActive
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800' ? 'bg-neutral-50 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900'
: '' : ''
) )
} }
@ -109,8 +100,6 @@ export function SettingsLayout() {
<div /> <div />
</div> </div>
<Outlet /> <Outlet />
<ScrollRestoration />
</div>
</div> </div>
); );
} }