mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-02 11:13:29 +00:00
Eng 142 fix interface hot reload (#252)
* fix interface hot reloading * AppPropsContext exsiting in App.tsx was the issue :D * type export fix
This commit is contained in:
parent
c0184ec9f0
commit
6978928db9
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -5,6 +5,7 @@
|
|||
"consts",
|
||||
"countup",
|
||||
"creationdate",
|
||||
"fontsource",
|
||||
"ipfs",
|
||||
"Keepsafe",
|
||||
"pathctx",
|
||||
|
@ -13,6 +14,7 @@
|
|||
"quicktime",
|
||||
"repr",
|
||||
"Roadmap",
|
||||
"subpackage",
|
||||
"svgr",
|
||||
"tailwindcss",
|
||||
"titlebar",
|
||||
|
|
|
@ -65,7 +65,6 @@ function App() {
|
|||
|
||||
return (
|
||||
<SpacedriveInterface
|
||||
useMemoryRouter
|
||||
transport={new Transport()}
|
||||
platform={platform}
|
||||
convertFileSrc={function (url: string): string {
|
||||
|
|
|
@ -10,7 +10,6 @@ export default defineConfig({
|
|||
port: 8001
|
||||
},
|
||||
plugins: [
|
||||
//@ts-ignore
|
||||
react({
|
||||
jsxRuntime: 'classic'
|
||||
}),
|
||||
|
|
|
@ -72,7 +72,6 @@ function App() {
|
|||
{/* <header className="App-header"></header> */}
|
||||
<SpacedriveInterface
|
||||
demoMode
|
||||
useMemoryRouter={true}
|
||||
transport={new Transport()}
|
||||
platform={'browser'}
|
||||
convertFileSrc={function (url: string): string {
|
||||
|
|
|
@ -1,213 +1,27 @@
|
|||
import '@fontsource/inter/variable.css';
|
||||
import { BaseTransport, ClientProvider, setTransport } from '@sd/client';
|
||||
import { Button } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
|
||||
import React from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import {
|
||||
Location,
|
||||
MemoryRouter,
|
||||
Outlet,
|
||||
Route,
|
||||
Routes,
|
||||
useLocation,
|
||||
useNavigate
|
||||
} from 'react-router-dom';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import { Sidebar } from './components/file/Sidebar';
|
||||
import { Modal } from './components/layout/Modal';
|
||||
import SlideUp from './components/transitions/SlideUp';
|
||||
import { AppProps, AppPropsContext } from './AppPropsContext';
|
||||
import { AppRouter } from './AppRouter';
|
||||
import { ErrorFallback } from './ErrorFallback';
|
||||
import { useCoreEvents } from './hooks/useCoreEvents';
|
||||
import { ContentScreen } from './screens/Content';
|
||||
import { DebugScreen } from './screens/Debug';
|
||||
import { ExplorerScreen } from './screens/Explorer';
|
||||
import { OverviewScreen } from './screens/Overview';
|
||||
import { PhotosScreen } from './screens/Photos';
|
||||
import { RedirectPage } from './screens/Redirect';
|
||||
import { SettingsScreen } from './screens/Settings';
|
||||
import { TagScreen } from './screens/Tag';
|
||||
import AppearanceSettings from './screens/settings/AppearanceSettings';
|
||||
import ContactsSettings from './screens/settings/ContactsSettings';
|
||||
import ExperimentalSettings from './screens/settings/ExperimentalSettings';
|
||||
import GeneralSettings from './screens/settings/GeneralSettings';
|
||||
import KeysSettings from './screens/settings/KeysSetting';
|
||||
import LibrarySettings from './screens/settings/LibrarySettings';
|
||||
import LocationSettings from './screens/settings/LocationSettings';
|
||||
import SecuritySettings from './screens/settings/SecuritySettings';
|
||||
import SharingSettings from './screens/settings/SharingSettings';
|
||||
import SyncSettings from './screens/settings/SyncSettings';
|
||||
import TagsSettings from './screens/settings/TagsSettings';
|
||||
import './style.scss';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export const AppPropsContext = React.createContext<AppProps | null>(null);
|
||||
|
||||
export type Platform = 'browser' | 'macOS' | 'windows' | 'linux';
|
||||
|
||||
export interface AppProps {
|
||||
transport: BaseTransport;
|
||||
platform: Platform;
|
||||
convertFileSrc: (url: string) => string;
|
||||
openDialog: (options: { directory?: boolean }) => Promise<string | string[] | null>;
|
||||
onClose?: () => void;
|
||||
onMinimize?: () => void;
|
||||
onFullscreen?: () => void;
|
||||
onOpen?: (path: string) => void;
|
||||
isFocused?: boolean;
|
||||
useMemoryRouter: boolean;
|
||||
demoMode?: boolean;
|
||||
}
|
||||
|
||||
function AppLayout() {
|
||||
const appProps = useContext(AppPropsContext);
|
||||
|
||||
const isWindowRounded = appProps?.platform === 'macOS';
|
||||
const hasWindowBorder = appProps?.platform !== 'browser' && appProps?.platform !== 'windows';
|
||||
|
||||
return (
|
||||
<div
|
||||
onContextMenu={(e) => {
|
||||
// TODO: allow this on some UI text at least
|
||||
// disable default browser context menu
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}}
|
||||
className={clsx(
|
||||
'flex flex-row h-screen overflow-hidden text-gray-900 select-none dark:text-white',
|
||||
isWindowRounded && 'rounded-xl',
|
||||
hasWindowBorder && 'border border-gray-200 dark:border-gray-500'
|
||||
)}
|
||||
>
|
||||
<Sidebar />
|
||||
<div className="flex flex-col w-full min-h-full">
|
||||
{/* <TopBar /> */}
|
||||
|
||||
<div className="relative flex w-full min-h-full bg-white dark:bg-gray-650">
|
||||
<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="appearance" element={<AppearanceSettings />} />
|
||||
<Route path="contacts" element={<ContactsSettings />} />
|
||||
<Route path="experimental" element={<ExperimentalSettings />} />
|
||||
<Route path="general" element={<GeneralSettings />} />
|
||||
<Route path="keys" element={<KeysSettings />} />
|
||||
<Route path="library" element={<LibrarySettings />} />
|
||||
<Route path="security" element={<SecuritySettings />} />
|
||||
<Route path="locations" element={<LocationSettings />} />
|
||||
<Route path="sharing" element={<SharingSettings />} />
|
||||
<Route path="sync" element={<SyncSettings />} />
|
||||
<Route path="tags" element={<TagsSettings />} />
|
||||
</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 />} />
|
||||
<Route path="content" element={<ContentScreen />} />
|
||||
<Route path="photos" element={<PhotosScreen />} />
|
||||
<Route path="debug" element={<DebugScreen />} />
|
||||
<Route path="settings/*" element={<SettingsRoutes />} />
|
||||
<Route path="explorer/:id" element={<ExplorerScreen />} />
|
||||
<Route path="tag/:id" element={<TagScreen />} />
|
||||
<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 MemoryRouterContainer() {
|
||||
function RouterContainer() {
|
||||
useCoreEvents();
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Router />
|
||||
<AppRouter />
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
function BrowserRouterContainer() {
|
||||
useCoreEvents();
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Router />
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export function bindCoreEvent() {}
|
||||
|
||||
export default function App(props: AppProps) {
|
||||
// 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'.
|
||||
|
@ -218,13 +32,11 @@ export default function App(props: AppProps) {
|
|||
|
||||
return (
|
||||
<>
|
||||
{/* @ts-ignore */}
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => {}}>
|
||||
{/* @ts-ignore */}
|
||||
<QueryClientProvider client={queryClient} contextSharing={false}>
|
||||
<AppPropsContext.Provider value={Object.assign({ isFocused: true }, props)}>
|
||||
<ClientProvider>
|
||||
{props.useMemoryRouter ? <MemoryRouterContainer /> : <BrowserRouterContainer />}
|
||||
<RouterContainer />
|
||||
</ClientProvider>
|
||||
</AppPropsContext.Provider>
|
||||
</QueryClientProvider>
|
||||
|
|
35
packages/interface/src/AppLayout.tsx
Normal file
35
packages/interface/src/AppLayout.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import clsx from 'clsx';
|
||||
import React, { useContext } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import { AppPropsContext } from './AppPropsContext';
|
||||
import { Sidebar } from './components/file/Sidebar';
|
||||
|
||||
export function AppLayout() {
|
||||
const appProps = useContext(AppPropsContext);
|
||||
|
||||
const isWindowRounded = appProps?.platform === 'macOS';
|
||||
const hasWindowBorder = appProps?.platform !== 'browser' && appProps?.platform !== 'windows';
|
||||
|
||||
return (
|
||||
<div
|
||||
onContextMenu={(e) => {
|
||||
// TODO: allow this on some UI text at least / disable default browser context menu
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}}
|
||||
className={clsx(
|
||||
'flex flex-row h-screen overflow-hidden text-gray-900 select-none dark:text-white',
|
||||
isWindowRounded && 'rounded-xl',
|
||||
hasWindowBorder && 'border border-gray-200 dark:border-gray-500'
|
||||
)}
|
||||
>
|
||||
<Sidebar />
|
||||
<div className="flex flex-col w-full min-h-full">
|
||||
<div className="relative flex w-full min-h-full bg-white dark:bg-gray-650">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
19
packages/interface/src/AppPropsContext.tsx
Normal file
19
packages/interface/src/AppPropsContext.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { BaseTransport } from '@sd/client';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const AppPropsContext = createContext<AppProps | null>(null);
|
||||
|
||||
export type Platform = 'browser' | 'macOS' | 'windows' | 'linux';
|
||||
|
||||
export interface AppProps {
|
||||
transport: BaseTransport;
|
||||
platform: Platform;
|
||||
convertFileSrc: (url: string) => string;
|
||||
openDialog: (options: { directory?: boolean }) => Promise<string | string[] | null>;
|
||||
onClose?: () => void;
|
||||
onMinimize?: () => void;
|
||||
onFullscreen?: () => void;
|
||||
onOpen?: (path: string) => void;
|
||||
isFocused?: boolean;
|
||||
demoMode?: boolean;
|
||||
}
|
64
packages/interface/src/AppRouter.tsx
Normal file
64
packages/interface/src/AppRouter.tsx
Normal file
|
@ -0,0 +1,64 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Route, Routes, useLocation } from 'react-router-dom';
|
||||
|
||||
import { AppLayout } from './AppLayout';
|
||||
import { NotFound } from './NotFound';
|
||||
import { ContentScreen } from './screens/Content';
|
||||
import { DebugScreen } from './screens/Debug';
|
||||
import { ExplorerScreen } from './screens/Explorer';
|
||||
import { OverviewScreen } from './screens/Overview';
|
||||
import { PhotosScreen } from './screens/Photos';
|
||||
import { RedirectPage } from './screens/Redirect';
|
||||
import { SettingsScreen } from './screens/Settings';
|
||||
import { TagScreen } from './screens/Tag';
|
||||
import AppearanceSettings from './screens/settings/AppearanceSettings';
|
||||
import ContactsSettings from './screens/settings/ContactsSettings';
|
||||
import ExperimentalSettings from './screens/settings/ExperimentalSettings';
|
||||
import GeneralSettings from './screens/settings/GeneralSettings';
|
||||
import KeysSettings from './screens/settings/KeysSetting';
|
||||
import LibrarySettings from './screens/settings/LibrarySettings';
|
||||
import LocationSettings from './screens/settings/LocationSettings';
|
||||
import SecuritySettings from './screens/settings/SecuritySettings';
|
||||
import SharingSettings from './screens/settings/SharingSettings';
|
||||
import SyncSettings from './screens/settings/SyncSettings';
|
||||
import TagsSettings from './screens/settings/TagsSettings';
|
||||
|
||||
export function AppRouter() {
|
||||
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 />} />
|
||||
<Route path="content" element={<ContentScreen />} />
|
||||
<Route path="photos" element={<PhotosScreen />} />
|
||||
<Route path="debug" element={<DebugScreen />} />
|
||||
<Route path={'settings'} element={<SettingsScreen />}>
|
||||
<Route index element={<GeneralSettings />} />
|
||||
<Route path="appearance" element={<AppearanceSettings />} />
|
||||
<Route path="contacts" element={<ContactsSettings />} />
|
||||
<Route path="experimental" element={<ExperimentalSettings />} />
|
||||
<Route path="general" element={<GeneralSettings />} />
|
||||
<Route path="keys" element={<KeysSettings />} />
|
||||
<Route path="library" element={<LibrarySettings />} />
|
||||
<Route path="security" element={<SecuritySettings />} />
|
||||
<Route path="locations" element={<LocationSettings />} />
|
||||
<Route path="sharing" element={<SharingSettings />} />
|
||||
<Route path="sync" element={<SyncSettings />} />
|
||||
<Route path="tags" element={<TagsSettings />} />
|
||||
</Route>
|
||||
<Route path="explorer/:id" element={<ExplorerScreen />} />
|
||||
<Route path="tag/:id" element={<TagScreen />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
}
|
25
packages/interface/src/ErrorFallback.tsx
Normal file
25
packages/interface/src/ErrorFallback.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { Button } from '@sd/ui';
|
||||
import React from 'react';
|
||||
import { FallbackProps } from 'react-error-boundary';
|
||||
|
||||
export 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>
|
||||
);
|
||||
}
|
22
packages/interface/src/NotFound.tsx
Normal file
22
packages/interface/src/NotFound.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { Button } from '@sd/ui';
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
export 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>
|
||||
);
|
||||
}
|
|
@ -25,7 +25,7 @@ export function Device(props: DeviceProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="w-full bg-gray-50 dark:bg-gray-600 border rounded-md border-gray-100 dark:border-gray-550">
|
||||
<div className="w-full border border-gray-100 rounded-md bg-gray-50 dark:bg-gray-600 dark:border-gray-550">
|
||||
<div className="flex flex-row items-center px-4 pt-2 pb-2">
|
||||
<DotsSixVertical weight="bold" className="mr-3 opacity-30" />
|
||||
{props.type === 'phone' && <DeviceMobileCamera weight="fill" size={20} className="mr-2" />}
|
||||
|
@ -44,7 +44,7 @@ export function Device(props: DeviceProps) {
|
|||
</span>
|
||||
<div className="flex flex-grow" />
|
||||
{props.runningJob && (
|
||||
<div className="flex flex-row ml-5 bg-opacity-50 rounded-md bg-gray-300 dark:bg-gray-550">
|
||||
<div className="flex flex-row ml-5 bg-gray-300 bg-opacity-50 rounded-md dark:bg-gray-550">
|
||||
<Rings
|
||||
stroke="#2599FF"
|
||||
strokeOpacity={4}
|
||||
|
|
|
@ -2,38 +2,15 @@ import { DotsVerticalIcon } from '@heroicons/react/solid';
|
|||
import { useBridgeQuery } from '@sd/client';
|
||||
import { FilePath } from '@sd/core';
|
||||
import clsx from 'clsx';
|
||||
import byteSize from 'pretty-bytes';
|
||||
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
|
||||
import { useKey, useWindowSize } from 'rooks';
|
||||
import create from 'zustand';
|
||||
|
||||
import { AppPropsContext } from '../../App';
|
||||
import { AppPropsContext } from '../../AppPropsContext';
|
||||
import { useExplorerState } from '../../hooks/useExplorerState';
|
||||
import FileThumb from './FileThumb';
|
||||
|
||||
type ExplorerState = {
|
||||
selectedRowIndex: number;
|
||||
setSelectedRowIndex: (index: number) => void;
|
||||
locationId: number;
|
||||
setLocationId: (index: number) => void;
|
||||
newThumbnails: Record<string, boolean>;
|
||||
addNewThumbnail: (cas_id: string) => void;
|
||||
};
|
||||
|
||||
export const useExplorerState = create<ExplorerState>((set) => ({
|
||||
selectedRowIndex: 1,
|
||||
setSelectedRowIndex: (index) => set((state) => ({ ...state, selectedRowIndex: index })),
|
||||
locationId: -1,
|
||||
setLocationId: (id: number) => set((state) => ({ ...state, locationId: id })),
|
||||
newThumbnails: {},
|
||||
addNewThumbnail: (cas_id: string) =>
|
||||
set((state) => ({
|
||||
...state,
|
||||
newThumbnails: { ...state.newThumbnails, [cas_id]: true }
|
||||
}))
|
||||
}));
|
||||
|
||||
interface IColumn {
|
||||
column: string;
|
||||
key: string;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { FilePath } from '@sd/core';
|
|||
import clsx from 'clsx';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { AppPropsContext } from '../../App';
|
||||
import { AppPropsContext } from '../../AppPropsContext';
|
||||
import icons from '../../assets/icons';
|
||||
import { Folder } from '../icons/Folder';
|
||||
|
||||
|
@ -17,7 +17,7 @@ export default function FileThumb(props: {
|
|||
const { data: client } = useBridgeQuery('NodeGetState');
|
||||
|
||||
if (props.file.is_dir) {
|
||||
return <Folder className="max-w-[20px]" />;
|
||||
return <Folder size={100} />;
|
||||
}
|
||||
|
||||
if (client?.data_path && (props.file.file?.has_thumbnail || props.hasThumbnailOverride)) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Transition } from '@headlessui/react';
|
||||
import { ShareIcon } from '@heroicons/react/solid';
|
||||
import { FilePath } from '@sd/core';
|
||||
import { FilePath, LocationResource } from '@sd/core';
|
||||
import { Button, TextArea } from '@sd/ui';
|
||||
import moment from 'moment';
|
||||
import { Heart, Link } from 'phosphor-react';
|
||||
|
@ -16,7 +16,7 @@ interface MetaItemProps {
|
|||
|
||||
const MetaItem = (props: MetaItemProps) => {
|
||||
return (
|
||||
<div className="flex flex-col px-3 py-1 meta-item">
|
||||
<div data-tip={props.value} className="flex flex-col px-3 py-1 meta-item">
|
||||
<h5 className="text-xs font-bold">{props.title}</h5>
|
||||
{typeof props.value === 'string' ? (
|
||||
<p className="text-xs text-gray-600 break-all truncate dark:text-gray-300">{props.value}</p>
|
||||
|
@ -29,12 +29,15 @@ const MetaItem = (props: MetaItemProps) => {
|
|||
|
||||
const Divider = () => <div className="w-full my-1 h-[1px] bg-gray-100 dark:bg-gray-550" />;
|
||||
|
||||
export const Inspector = (props: { selectedFile?: FilePath; locationId: number }) => {
|
||||
// const { selectedRowIndex } = useExplorerState();
|
||||
// const isOpen = !!props.selectedFile;
|
||||
|
||||
export const Inspector = (props: {
|
||||
locationId: number;
|
||||
location?: LocationResource;
|
||||
selectedFile?: FilePath;
|
||||
}) => {
|
||||
const file_path = props.selectedFile;
|
||||
|
||||
let full_path = `${props.location?.path}/${file_path?.materialized_path}`;
|
||||
|
||||
return (
|
||||
<Transition
|
||||
show={true}
|
||||
|
@ -45,9 +48,9 @@ export const Inspector = (props: { selectedFile?: FilePath; locationId: number }
|
|||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-64"
|
||||
>
|
||||
<div className="top-0 right-0 h-full m-2 border border-gray-100 rounded-lg w-60 dark:border-gray-850 ">
|
||||
<div className="top-0 right-0 m-2 border border-gray-100 w-60 dark:border-gray-850 custom-scroll page-scroll">
|
||||
{!!file_path && (
|
||||
<div className="flex flex-col h-full overflow-hidden bg-white rounded-lg select-text dark:bg-gray-600 bg-opacity-70">
|
||||
<div className="flex flex-col overflow-x-hidden bg-white rounded-lg select-text dark:bg-gray-600 bg-opacity-70">
|
||||
<div className="flex items-center justify-center w-full h-64 overflow-hidden rounded-t-lg bg-gray-50 dark:bg-gray-900">
|
||||
<FileThumb
|
||||
hasThumbnailOverride={false}
|
||||
|
@ -72,7 +75,7 @@ export const Inspector = (props: { selectedFile?: FilePath; locationId: number }
|
|||
<MetaItem title="Unique Content ID" value={file_path.file.cas_id as string} />
|
||||
)}
|
||||
<Divider />
|
||||
<MetaItem title="Uri" value={file_path?.materialized_path as string} />
|
||||
<MetaItem title="Uri" value={full_path} />
|
||||
<Divider />
|
||||
<MetaItem
|
||||
title="Date Created"
|
||||
|
|
|
@ -7,7 +7,7 @@ import { CirclesFour, Code, Planet } from 'phosphor-react';
|
|||
import React, { useContext } from 'react';
|
||||
import { NavLink, NavLinkProps } from 'react-router-dom';
|
||||
|
||||
import { AppPropsContext } from '../../App';
|
||||
import { AppPropsContext } from '../../AppPropsContext';
|
||||
import { useNodeStore } from '../device/Stores';
|
||||
import { Folder } from '../icons/Folder';
|
||||
import RunningJobsWidget from '../jobs/RunningJobsWidget';
|
||||
|
@ -244,10 +244,7 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
|
|||
<Button
|
||||
noPadding
|
||||
variant={isActive ? 'default' : 'default'}
|
||||
className={clsx(
|
||||
'px-[4px] mb-1'
|
||||
// isActive && '!bg-gray-550'
|
||||
)}
|
||||
className={clsx('px-[4px] mb-1')}
|
||||
>
|
||||
<CogIcon className="w-5 h-5" />
|
||||
</Button>
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
import React, { DetailedHTMLProps, HTMLAttributes } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useExplorerState } from '../file/FileList';
|
||||
import { useExplorerState } from '../../hooks/useExplorerState';
|
||||
import { Shortcut } from '../primitive/Shortcut';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useContext, useEffect } from 'react';
|
|||
import { useQueryClient } from 'react-query';
|
||||
|
||||
import { AppPropsContext } from '../App';
|
||||
import { useExplorerState } from '../components/file/FileList';
|
||||
import { useExplorerState } from './useExplorerState';
|
||||
|
||||
export function useCoreEvents() {
|
||||
const client = useQueryClient();
|
||||
|
|
23
packages/interface/src/hooks/useExplorerState.ts
Normal file
23
packages/interface/src/hooks/useExplorerState.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import create from 'zustand';
|
||||
|
||||
type ExplorerState = {
|
||||
selectedRowIndex: number;
|
||||
setSelectedRowIndex: (index: number) => void;
|
||||
locationId: number;
|
||||
setLocationId: (index: number) => void;
|
||||
newThumbnails: Record<string, boolean>;
|
||||
addNewThumbnail: (cas_id: string) => void;
|
||||
};
|
||||
|
||||
export const useExplorerState = create<ExplorerState>((set) => ({
|
||||
selectedRowIndex: 1,
|
||||
setSelectedRowIndex: (index) => set((state) => ({ ...state, selectedRowIndex: index })),
|
||||
locationId: -1,
|
||||
setLocationId: (id: number) => set((state) => ({ ...state, locationId: id })),
|
||||
newThumbnails: {},
|
||||
addNewThumbnail: (cas_id: string) =>
|
||||
set((state) => ({
|
||||
...state,
|
||||
newThumbnails: { ...state.newThumbnails, [cas_id]: true }
|
||||
}))
|
||||
}));
|
|
@ -1,5 +1,5 @@
|
|||
import App from './App';
|
||||
import { AppProps, Platform } from './App';
|
||||
import { AppProps, Platform } from './AppPropsContext';
|
||||
|
||||
export type { AppProps, Platform };
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// import { useBridgeCommand, useBridgeQuery } from '@sd/client';
|
||||
import React from 'react';
|
||||
|
||||
export const ContentScreen: React.FC<{}> = (props) => {
|
||||
|
|
|
@ -2,45 +2,36 @@ import { useBridgeCommand, useBridgeQuery } from '@sd/client';
|
|||
import { Button } from '@sd/ui';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { AppPropsContext } from '../App';
|
||||
import { AppPropsContext } from '../AppPropsContext';
|
||||
import CodeBlock from '../components/primitive/Codeblock';
|
||||
|
||||
export const DebugScreen: React.FC<{}> = (props) => {
|
||||
const appPropsContext = useContext(AppPropsContext);
|
||||
const { data: client } = useBridgeQuery('NodeGetState');
|
||||
const { data: jobs } = useBridgeQuery('JobGetRunning');
|
||||
const { data: jobHistory } = useBridgeQuery('JobGetHistory');
|
||||
// const { mutate: purgeDB } = useBridgeCommand('PurgeDatabase', {
|
||||
// onMutate: () => {
|
||||
// alert('Database purged');
|
||||
// }
|
||||
// });
|
||||
const { mutate: identifyFiles } = useBridgeCommand('IdentifyUniqueFiles');
|
||||
return (
|
||||
<div className="flex flex-col w-full h-screen p-5 custom-scroll page-scroll">
|
||||
<div className="flex flex-col space-y-5 pb-7">
|
||||
<h1 className="text-lg font-bold ">Developer Debugger</h1>
|
||||
<div className="flex flex-row pb-4 space-x-2">
|
||||
<Button
|
||||
className="w-40"
|
||||
variant="gray"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (client && appPropsContext?.onOpen) {
|
||||
appPropsContext.onOpen(client.data_path);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Open data folder
|
||||
</Button>
|
||||
|
||||
const appPropsContext = useContext(AppPropsContext);
|
||||
const { data: client } = useBridgeQuery('NodeGetState');
|
||||
const { data: jobs } = useBridgeQuery('JobGetRunning');
|
||||
const { data: jobHistory } = useBridgeQuery('JobGetHistory');
|
||||
// const { mutate: purgeDB } = useBridgeCommand('PurgeDatabase', {
|
||||
// onMutate: () => {
|
||||
// alert('Database purged');
|
||||
// }
|
||||
// });
|
||||
const { mutate: identifyFiles } = useBridgeCommand('IdentifyUniqueFiles');
|
||||
return (
|
||||
<div className="flex flex-col w-full h-screen p-5 custom-scroll page-scroll">
|
||||
<div className="flex flex-col space-y-5 pb-7">
|
||||
<h1 className="text-lg font-bold ">Developer Debugger</h1>
|
||||
<div className="flex flex-row pb-4 space-x-2">
|
||||
<Button
|
||||
className="w-40"
|
||||
variant="gray"
|
||||
size="sm"
|
||||
onClick={() => identifyFiles(undefined)}
|
||||
onClick={() => {
|
||||
if (client && appPropsContext?.onOpen) {
|
||||
appPropsContext.onOpen(client.data_path);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Identify unique files
|
||||
Open data folder
|
||||
</Button>
|
||||
</div>
|
||||
<h1 className="text-sm font-bold ">Running Jobs</h1>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useBridgeQuery } from '@sd/client';
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { FileList, useExplorerState } from '../components/file/FileList';
|
||||
import { FileList } from '../components/file/FileList';
|
||||
import { Inspector } from '../components/file/Inspector';
|
||||
import { TopBar } from '../components/layout/TopBar';
|
||||
import { useExplorerState } from '../hooks/useExplorerState';
|
||||
|
||||
export const ExplorerScreen: React.FC<{}> = () => {
|
||||
let [searchParams] = useSearchParams();
|
||||
|
@ -13,22 +14,20 @@ export const ExplorerScreen: React.FC<{}> = () => {
|
|||
let { id } = useParams();
|
||||
let location_id = Number(id);
|
||||
|
||||
let [limit, setLimit] = React.useState(100);
|
||||
|
||||
useEffect(() => {
|
||||
console.log({ location_id, path, limit });
|
||||
}, [location_id, path]);
|
||||
const [limit, setLimit] = React.useState(100);
|
||||
|
||||
const { selectedRowIndex } = useExplorerState();
|
||||
|
||||
// Current Location
|
||||
const { data: currentLocation } = useBridgeQuery('SysGetLocation', { id: location_id });
|
||||
|
||||
// Current Directory
|
||||
const { data: currentDir } = useBridgeQuery(
|
||||
'LibGetExplorerDir',
|
||||
{ location_id: location_id!, path, limit },
|
||||
{ enabled: !!location_id }
|
||||
);
|
||||
|
||||
console.log({ currentDir });
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full h-full">
|
||||
<TopBar />
|
||||
|
@ -36,6 +35,7 @@ export const ExplorerScreen: React.FC<{}> = () => {
|
|||
<FileList location_id={location_id} path={path} limit={limit} />
|
||||
{currentDir?.contents && (
|
||||
<Inspector
|
||||
location={currentLocation}
|
||||
selectedFile={currentDir.contents[selectedRowIndex]}
|
||||
locationId={location_id}
|
||||
/>
|
||||
|
|
|
@ -10,7 +10,7 @@ import Skeleton from 'react-loading-skeleton';
|
|||
import 'react-loading-skeleton/dist/skeleton.css';
|
||||
import create from 'zustand';
|
||||
|
||||
import { AppPropsContext } from '../App';
|
||||
import { AppPropsContext } from '../AppPropsContext';
|
||||
import { Device } from '../components/device/Device';
|
||||
import Dialog from '../components/layout/Dialog';
|
||||
|
||||
|
|
|
@ -1,25 +1,19 @@
|
|||
// import { dummyIFile, FileList } from '../components/file/FileList';
|
||||
import {
|
||||
CloudIcon,
|
||||
CogIcon,
|
||||
KeyIcon,
|
||||
LockClosedIcon,
|
||||
PhotographIcon,
|
||||
TagIcon,
|
||||
TerminalIcon,
|
||||
UsersIcon
|
||||
} from '@heroicons/react/outline';
|
||||
import clsx from 'clsx';
|
||||
import { Book, Database, HardDrive, PaintBrush } from 'phosphor-react';
|
||||
import { Database, HardDrive, PaintBrush } from 'phosphor-react';
|
||||
import React from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import { SidebarLink } from '../components/file/Sidebar';
|
||||
|
||||
//@ts-ignore
|
||||
// import { Spline } from 'react-spline';
|
||||
// import WINDOWS_SCENE from '../assets/spline/scene.json';
|
||||
|
||||
const Icon = ({ component: Icon, ...props }: any) => (
|
||||
<Icon weight="bold" {...props} className={clsx('w-4 h-4 mr-2', props.className)} />
|
||||
);
|
||||
|
|
|
@ -9,10 +9,7 @@ export const TagScreen: React.FC<{}> = () => {
|
|||
|
||||
return (
|
||||
<div className="w-full p-5">
|
||||
<p className="px-5 py-3 mb-3 text-sm text-gray-400 rounded-md bg-gray-50 dark:text-gray-400 dark:bg-gray-600">
|
||||
<b>Note: </b>This is a pre-alpha build of Spacedrive, many features are yet to be
|
||||
functional.
|
||||
</p>
|
||||
<h1>{id}</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,32 +15,6 @@ export default function GeneralSettings() {
|
|||
title="General Settings"
|
||||
description="Basic settings related to this client."
|
||||
/>
|
||||
|
||||
{/* <InputContainer
|
||||
title="Test scan directory"
|
||||
description="This will create a job to scan the directory you specify to the database."
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<Input
|
||||
value={tempWatchDir}
|
||||
className="flex-grow"
|
||||
onChange={(e) => setTempWatchDir(e.target.value)}
|
||||
placeholder="/users/jamie/Desktop"
|
||||
/>
|
||||
<Button
|
||||
className="ml-2"
|
||||
variant="primary"
|
||||
onClick={() =>
|
||||
createLocation({
|
||||
path: tempWatchDir
|
||||
})
|
||||
}
|
||||
>
|
||||
Scan Now
|
||||
</Button>
|
||||
</div>
|
||||
</InputContainer> */}
|
||||
|
||||
<InputContainer title="Volumes" description="A list of volumes running on this device.">
|
||||
<div className="flex flex-row space-x-2">
|
||||
<div className="flex flex-grow">
|
||||
|
|
Loading…
Reference in a new issue