spacedrive/packages/interface/src/App.tsx

213 lines
6.8 KiB
TypeScript
Raw Normal View History

2022-04-17 18:44:34 +00:00
import React, { useContext, useEffect, useState } from 'react';
import {
BrowserRouter,
Location,
Outlet,
Route,
Routes,
useLocation,
useNavigate
} from 'react-router-dom';
import { Sidebar } from './components/file/Sidebar';
import { SettingsScreen } from './screens/Settings';
import { ExplorerScreen } from './screens/Explorer';
import { useCoreEvents } from './hooks/useCoreEvents';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
import { OverviewScreen } from './screens/Overview';
2022-04-24 16:50:22 +00:00
import { DebugScreen } from './screens/Debug';
2022-04-17 18:44:34 +00:00
import { Modal } from './components/layout/Modal';
import GeneralSettings from './screens/settings/GeneralSettings';
import SlideUp from './components/transitions/SlideUp';
import SecuritySettings from './screens/settings/SecuritySettings';
import LocationSettings from './screens/settings/LocationSettings';
import { RedirectPage } from './screens/Redirect';
import { QueryClient, QueryClientProvider } from 'react-query';
import { BaseTransport, ClientProvider, setTransport } from '@sd/client';
2022-04-20 05:55:44 +00:00
import { Button } from '@sd/ui';
2022-04-17 18:44:34 +00:00
import { CoreEvent } from '@sd/core';
2022-04-19 03:35:19 +00:00
import clsx from 'clsx';
import './style.scss';
2022-04-24 16:50:22 +00:00
import { ContentScreen } from './screens/Content';
2022-04-26 23:44:08 +00:00
import LibrarySettings from './screens/settings/LibrarySettings';
2022-04-17 18:44:34 +00:00
const queryClient = new QueryClient();
export const AppPropsContext = React.createContext<AppProps | null>(null);
export type Platform = 'browser' | 'macOS' | 'windows' | 'linux';
2022-04-17 18:44:34 +00:00
export interface AppProps {
transport: BaseTransport;
platform: Platform;
2022-04-17 18:44:34 +00:00
convertFileSrc: (url: string) => string;
2022-04-19 00:34:39 +00:00
openDialog: (options: { directory?: boolean }) => Promise<string | string[]>;
2022-04-17 18:44:34 +00:00
onClose?: () => void;
onMinimize?: () => void;
onFullscreen?: () => void;
}
function AppLayout() {
const appPropsContext = useContext(AppPropsContext);
const [isWindowRounded, setIsWindowRounded] = useState(false);
2022-04-19 03:35:19 +00:00
const [hasWindowBorder, setHasWindowBorder] = useState(true);
2022-04-17 18:44:34 +00:00
useEffect(() => {
if (appPropsContext?.platform === 'macOS') {
setIsWindowRounded(true);
}
2022-04-19 03:35:19 +00:00
if (appPropsContext?.platform === 'browser') {
setHasWindowBorder(false);
}
2022-04-17 18:44:34 +00:00
}, []);
return (
<div
2022-04-19 03:35:19 +00:00
className={clsx(
'flex flex-row h-screen overflow-hidden text-gray-900 bg-white select-none dark:text-white dark:bg-gray-650',
isWindowRounded && 'rounded-xl',
hasWindowBorder && 'border border-gray-200 dark:border-gray-500'
)}
2022-04-17 18:44:34 +00:00
>
<Sidebar />
<div className="flex flex-col w-full min-h-full">
{/* <TopBar /> */}
<div className="relative flex w-full">
<Outlet />
</div>
</div>
</div>
);
}
function SettingsRoutes({ modal = false }) {
return (
<SlideUp>
<Routes>
<Route
path={modal ? '/settings' : '/'}
element={modal ? <Modal children={<SettingsScreen />} /> : <SettingsScreen />}
>
<Route index element={<GeneralSettings />} />
<Route path="general" element={<GeneralSettings />} />
<Route path="security" element={<SecuritySettings />} />
<Route path="appearance" element={<></>} />
<Route path="locations" element={<LocationSettings />} />
2022-04-26 23:44:08 +00:00
<Route path="library" element={<LibrarySettings />} />
2022-04-17 18:44:34 +00:00
<Route path="media" element={<></>} />
<Route path="keys" element={<></>} />
<Route path="tags" element={<></>} />
2022-04-26 18:34:36 +00:00
<Route path="sync" element={<></>} />
<Route path="contacts" element={<></>} />
2022-04-17 18:44:34 +00:00
</Route>
</Routes>
</SlideUp>
);
}
function Router() {
let location = useLocation();
let state = location.state as { backgroundLocation?: Location };
useEffect(() => {
console.log({ url: location.pathname });
}, [state]);
return (
<>
<Routes location={state?.backgroundLocation || location}>
<Route path="/" element={<AppLayout />}>
<Route index element={<RedirectPage to="/overview" />} />
<Route path="overview" element={<OverviewScreen />} />
2022-04-24 16:50:22 +00:00
<Route path="content" element={<ContentScreen />} />
<Route path="debug" element={<DebugScreen />} />
2022-04-17 18:44:34 +00:00
<Route path="settings/*" element={<SettingsRoutes />} />
<Route path="explorer/:id" element={<ExplorerScreen />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
{state?.backgroundLocation && <SettingsRoutes modal />}
</>
);
}
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div
data-tauri-drag-region
role="alert"
className="flex flex-col items-center justify-center w-screen h-screen p-4 border border-gray-200 rounded-lg dark:border-gray-650 bg-gray-50 dark:bg-gray-650 dark:text-white"
>
<p className="m-3 text-sm font-bold text-gray-400">APP CRASHED</p>
<h1 className="text-2xl font-bold">We're past the event horizon...</h1>
<pre className="m-2">Error: {error.message}</pre>
<div className="flex flex-row space-x-2">
<Button variant="primary" className="mt-2" onClick={resetErrorBoundary}>
Reload
</Button>
<Button className="mt-2" onClick={resetErrorBoundary}>
Send report
</Button>
</div>
</div>
);
}
function NotFound() {
const navigate = useNavigate();
return (
<div
data-tauri-drag-region
role="alert"
className="flex flex-col items-center justify-center w-full h-full p-4 rounded-lg dark:text-white"
>
<p className="m-3 mt-20 text-sm font-semibold text-gray-500 uppercase">Error: 404</p>
<h1 className="text-4xl font-bold">You chose nothingness.</h1>
<div className="flex flex-row space-x-2">
<Button variant="primary" className="mt-4" onClick={() => navigate(-1)}>
Go Back
</Button>
</div>
</div>
);
}
function AppContainer() {
useCoreEvents();
return (
<BrowserRouter>
<Router />
</BrowserRouter>
);
}
export function bindCoreEvent() {}
2022-04-17 18:44:34 +00:00
export default function App(props: AppProps) {
// @ts-ignore: TODO: This is a hack and a better solution should probably be found. This exists so that the queryClient can be accessed within the subpackage '@sd/client'. Refer to <ClientProvider /> for where this is used.
if (window.ReactQueryClient === undefined) {
// @ts-ignore
window.ReactQueryClient = queryClient;
}
setTransport(props.transport);
console.log('App props', props);
2022-04-17 18:44:34 +00:00
return (
<>
{/* @ts-ignore */}
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => {}}>
{/* @ts-ignore */}
<QueryClientProvider client={queryClient} contextSharing={false}>
<AppPropsContext.Provider value={props}>
<ClientProvider>
<AppContainer />
</ClientProvider>
</AppPropsContext.Provider>
</QueryClientProvider>
</ErrorBoundary>
</>
);
}