feat: Added top menu

This commit is contained in:
florian 2024-05-09 17:32:14 +02:00
parent b7c27ddc8a
commit b7949c38a4
6 changed files with 126 additions and 127 deletions

View File

@ -37,11 +37,13 @@ const ensureDecrypted = async (dvm: NDKUser, event: NDKEvent) => {
return event; return event;
}; };
const NPUB_DVM_THUMBNAIL_CREATION = 'npub1q8cv87l47fql2xer2uyw509y5n5s9f53h76hvf9377efdptmsvusxf3n8s';
const FileEventEditor = ({ data }: { data: FileEventData }) => { const FileEventEditor = ({ data }: { data: FileEventData }) => {
const [fileEventData, setFileEventData] = useState(data); const [fileEventData, setFileEventData] = useState(data);
const [thumbnailRequestEventId, setThumbnailRequestEventId] = useState<string | undefined>(); const [thumbnailRequestEventId, setThumbnailRequestEventId] = useState<string | undefined>();
const { ndk, user } = useNDK(); const { ndk, user } = useNDK();
const dvm = ndk.getUser({ npub: 'npub1q8cv87l47fql2xer2uyw509y5n5s9f53h76hvf9377efdptmsvusxf3n8s' }); const dvm = ndk.getUser({ npub: NPUB_DVM_THUMBNAIL_CREATION });
const thumbnailDvmFilter = useMemo( const thumbnailDvmFilter = useMemo(
() => ({ kinds: [6204 as NDKKind], '#e': [thumbnailRequestEventId || ''] }), () => ({ kinds: [6204 as NDKKind], '#e': [thumbnailRequestEventId || ''] }),

View File

@ -6,38 +6,11 @@
@apply flex flex-col self-center md:w-10/12 w-full min-h-[80vh] px-4 md:px-0; @apply flex flex-col self-center md:w-10/12 w-full min-h-[80vh] px-4 md:px-0;
} }
.title {
@apply text-neutral-content text-4xl flex flex-row items-center gap-2 p-4 md:w-10/12 w-full self-center;
}
.title img {
@apply w-10;
}
.title a.action {
@apply flex flex-col text-sm items-center opacity-50 hover:opacity-100 cursor-pointer px-2;
}
.title a.logo {
@apply flex flex-row flex-grow items-center gap-2 cursor-pointer;
}
.title span {
@apply flex-grow;
}
.title svg {
@apply w-7;
}
.avatar {
@apply flex-shrink;
}
.avatar img {
@apply w-10 h-10 rounded-full;
}
.footer { .footer {
@apply justify-center gap-1 text-base-content pt-12 pb-6; @apply justify-center gap-1 text-base-content pt-12 pb-6;
} }
.btn svg {
@apply w-8;
}

View File

@ -1,7 +1,7 @@
import { Outlet, useNavigate } from 'react-router-dom'; import { Outlet, useNavigate } from 'react-router-dom';
import { useNDK } from '../../utils/ndk'; import { useNDK } from '../../utils/ndk';
import './Layout.css'; import './Layout.css';
import { ArrowUpOnSquareIcon } from '@heroicons/react/24/outline'; import { ArrowUpOnSquareIcon, MagnifyingGlassIcon, ServerStackIcon } from '@heroicons/react/24/outline';
import { useEffect } from 'react'; import { useEffect } from 'react';
import ThemeSwitcher from '../ThemeSwitcher'; import ThemeSwitcher from '../ThemeSwitcher';
@ -15,22 +15,36 @@ export const Layout = () => {
return ( return (
<div className="main"> <div className="main">
<div className="title"> <div className="navbar bg-base-300">
<a className="logo" onClick={() => navigate('/')}> <div className="navbar-start">
<img src="/bouquet.png" /> <span>bouquet</span> <button className="btn btn-ghost text-xl">
</a> <a className="logo" onClick={() => navigate('/')}>
<div> <img className="w-8" src="/bouquet.png" />{' '}
</a>
<span>bouquet</span>
</button>
</div>
<div className="navbar-center">
<button className=" btn btn-ghost" onClick={() => navigate('/upload')}>
<ArrowUpOnSquareIcon /> Upload
</button>
<button className=" btn btn-ghost" onClick={() => navigate('/')}>
<MagnifyingGlassIcon /> Browse
</button>
<button className=" btn btn-ghost" onClick={() => navigate('/transfer')}>
<ServerStackIcon /> Sync
</button>
</div>
<div className="navbar-end">
<ThemeSwitcher /> <ThemeSwitcher />
<div className="tooltip tooltip-bottom" data-tip="Upload"> <div className="avatar px-4">
<button className=" btn btn-square btn-ghost" onClick={() => navigate('/upload')}> <div className="w-12 rounded-full">
<ArrowUpOnSquareIcon /> <img src={user?.profile?.image} />
</button> </div>
</div> </div>
</div> </div>
<div className="avatar">
<img src={user?.profile?.image} />
</div>
</div> </div>
<div className="content">{<Outlet />}</div> <div className="content">{<Outlet />}</div>
<div className="footer"> <div className="footer">
made with 💜 by{' '} made with 💜 by{' '}

View File

@ -14,8 +14,8 @@ const ThemeSwitcher = () => {
<div className="tooltip tooltip-bottom" data-tip="Switch theme"> <div className="tooltip tooltip-bottom" data-tip="Switch theme">
<label className="swap swap-rotate"> <label className="swap swap-rotate">
<input onClick={toggleTheme} type="checkbox" /> <input onClick={toggleTheme} type="checkbox" />
<MoonIcon className="tooltip swap-on" /> <MoonIcon className="w-8 tooltip swap-on" />
<SunIcon className="tooltip swap-off" /> <SunIcon className="w-8 *:tooltip swap-off" />
</label> </label>
</div> </div>
); );

View File

@ -41,6 +41,7 @@ const router = createBrowserRouter(
<Route element={<Layout />}> <Route element={<Layout />}>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/transfer/:source" element={<Transfer />} /> <Route path="/transfer/:source" element={<Transfer />} />
<Route path="/transfer" element={<Transfer />} />
<Route path="/upload" element={<Upload />} /> <Route path="/upload" element={<Upload />} />
<Route path="/check/:source" element={<Check />} /> <Route path="/check/:source" element={<Check />} />
</Route> </Route>

View File

@ -27,7 +27,8 @@ type TransferStatus = {
}; };
export const Transfer = () => { export const Transfer = () => {
const { source: transferSource } = useParams(); const { source } = useParams();
const [transferSource, setTransferSource] = useState(source);
const navigate = useNavigate(); const navigate = useNavigate();
const { serverInfo } = useServerInfo(); const { serverInfo } = useServerInfo();
const [transferTarget, setTransferTarget] = useState<string | undefined>(); const [transferTarget, setTransferTarget] = useState<string | undefined>();
@ -100,88 +101,96 @@ export const Transfer = () => {
return { ...stats, fullSize: transferJobs?.reduce((acc, b) => acc + b.size, 0) || 0 }; return { ...stats, fullSize: transferJobs?.reduce((acc, b) => acc + b.size, 0) || 0 };
}, [transferLog, transferJobs]); }, [transferLog, transferJobs]);
return ( return transferSource ? (
transferSource && ( <>
<> <ServerList
<ServerList servers={Object.values(serverInfo).filter(s => s.name == transferSource)}
servers={Object.values(serverInfo).filter(s => s.name == transferSource)} onCancel={() => closeTransferMode()}
onCancel={() => closeTransferMode()} title={
title={
<>
<ArrowUpOnSquareIcon /> Transfer Source
</>
}
></ServerList>
<ServerList
servers={Object.values(serverInfo)
.filter(s => s.name != transferSource)
.sort()}
selectedServer={transferTarget}
setSelectedServer={setTransferTarget}
title={
<>
<ArrowDownOnSquareIcon /> Transfer Target
</>
}
></ServerList>
{transferTarget && transferJobs && transferJobs.length > 0 ? (
<> <>
<div className=" bg-base-200 rounded-xl p-4 text-neutral-content gap-4 flex flex-col my-4"> <ArrowUpOnSquareIcon /> Transfer Source
<div className="message"> </>
{transferJobs.length} object{transferJobs.length > 1 ? 's' : ''} to transfer{' '} }
{!started && ( ></ServerList>
<button <ServerList
className="action-button" servers={Object.values(serverInfo)
onClick={() => performTransfer(transferSource, transferTarget, transferJobs)} .filter(s => s.name != transferSource)
> .sort()}
<ArrowUpOnSquareIcon /> selectedServer={transferTarget}
Start setSelectedServer={setTransferTarget}
</button> title={
)} <>
</div> <ArrowDownOnSquareIcon /> Transfer Target
<div className="w-5/6"> </>
<ProgressBar }
value={transferStatus.size} ></ServerList>
max={transferStatus.fullSize} {transferTarget && transferJobs && transferJobs.length > 0 ? (
description={ <>
formatFileSize(transferStatus.size) + <div className=" bg-base-200 rounded-xl p-4 text-neutral-content gap-4 flex flex-col my-4">
' / ' + <div className="message">
formatFileSize(transferStatus.fullSize) + {transferJobs.length} object{transferJobs.length > 1 ? 's' : ''} to transfer{' '}
' transferred' {!started && (
} <button
/> className="action-button"
{<div className="message"></div>} onClick={() => performTransfer(transferSource, transferTarget, transferJobs)}
<div className="error-log"> >
{Object.values(transferLog) <ArrowUpOnSquareIcon />
.filter(b => b.status == 'error') Start
.map(t => ( </button>
<div> )}
<span> </div>
<DocumentIcon /> <div className="w-5/6">
</span> <ProgressBar
<span>{t.sha256}</span> value={transferStatus.size}
<span>{formatFileSize(t.size)}</span> max={transferStatus.fullSize}
<span>{t.status && (t.status == 'error' ? <ExclamationTriangleIcon /> : '')}</span> description={
<span>{t.message}</span> formatFileSize(transferStatus.size) + ' / ' + formatFileSize(transferStatus.fullSize) + ' transferred'
</div> }
))} />
</div> {<div className="message"></div>}
<div className="error-log">
{Object.values(transferLog)
.filter(b => b.status == 'error')
.map(t => (
<div>
<span>
<DocumentIcon />
</span>
<span>{t.sha256}</span>
<span>{formatFileSize(t.size)}</span>
<span>{t.status && (t.status == 'error' ? <ExclamationTriangleIcon /> : '')}</span>
<span>{t.message}</span>
</div>
))}
</div> </div>
</div> </div>
{!started && <BlobList blobs={transferJobs}></BlobList>}
</>
) : (
<div className="message">
{transferTarget ? (
<>
<CheckBadgeIcon /> no missing objects to transfer
</>
) : (
<>choose a transfer target above</>
)}
</div> </div>
)} {!started && <BlobList blobs={transferJobs}></BlobList>}
</> </>
) ) : (
<div className="message">
{transferTarget ? (
<>
<CheckBadgeIcon /> no missing objects to transfer
</>
) : (
<>choose a transfer target above</>
)}
</div>
)}
</>
) : (
<>
<ServerList
title={
<>
<ArrowUpOnSquareIcon /> Transfer Source
</>
}
servers={Object.values(serverInfo).sort()}
selectedServer={transferSource}
setSelectedServer={s => setTransferSource(s)}
></ServerList>
</>
); );
}; };