diff --git a/apps/mobile/src/App.tsx b/apps/mobile/src/App.tsx index 25c1715dd..81ddcf4b6 100644 --- a/apps/mobile/src/App.tsx +++ b/apps/mobile/src/App.tsx @@ -12,12 +12,14 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { MenuProvider } from 'react-native-popup-menu'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { useDeviceContext } from 'twrnc'; +import { proxy, useSnapshot } from 'valtio'; import { + ClientContextProvider, LibraryContextProvider, getDebugState, queryClient, rspc, - useCurrentLibrary, + useClientContext, useInvalidateQuery } from '@sd/client'; import { GlobalModals } from './components/modal/GlobalModals'; @@ -25,6 +27,7 @@ import { reactNativeLink } from './lib/rspcReactNativeTransport'; import { tw } from './lib/tailwind'; import RootNavigator from './navigation'; import OnboardingNavigator from './navigation/OnboardingNavigator'; +import { currentLibraryStore } from './utils/nav'; dayjs.extend(advancedFormat); dayjs.extend(relativeTime); @@ -35,33 +38,47 @@ const NavigatorTheme: Theme = { colors: { ...DefaultTheme.colors, // Default screen background - background: tw.color('app') + background: tw.color('app')! } }; +function AppNavigation() { + const { library } = useClientContext(); + + // TODO: Make sure library has actually been loaded by this point - precache with useCachedLibraries? + // if (library === undefined) throw new Error("Tried to render AppNavigation before libraries fetched!") + + return ( + + {!library ? ( + + ) : ( + + + + + )} + + ); +} + function AppContainer() { // Enables dark mode, and screen size breakpoints, etc. for tailwind useDeviceContext(tw, { withDeviceColorScheme: false }); useInvalidateQuery(); - const { library } = useCurrentLibrary(); + const { id } = useSnapshot(currentLibraryStore); + return ( - - {!library ? ( - - ) : ( - <> - - - - )} - + + + @@ -85,13 +102,7 @@ export default function App() { return ( - { - console.log('TODO'); - }} - > - - + ); } diff --git a/apps/mobile/src/components/dialog/CreateLibraryDialog.tsx b/apps/mobile/src/components/dialog/CreateLibraryDialog.tsx index d16948c0c..0852632a8 100644 --- a/apps/mobile/src/components/dialog/CreateLibraryDialog.tsx +++ b/apps/mobile/src/components/dialog/CreateLibraryDialog.tsx @@ -1,7 +1,8 @@ import { useState } from 'react'; -import { queryClient, useBridgeMutation, useCurrentLibrary } from '@sd/client'; +import { queryClient, useBridgeMutation } from '@sd/client'; import Dialog from '~/components/layout/Dialog'; import { Input } from '~/components/primitive/Input'; +import { currentLibraryStore } from '~/utils/nav'; type Props = { onSubmit?: () => void; @@ -14,8 +15,6 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props const [libName, setLibName] = useState(''); const [isOpen, setIsOpen] = useState(false); - const { switchLibrary } = useCurrentLibrary(); - const { mutate: createLibrary, isLoading: createLibLoading } = useBridgeMutation( 'library.create', { @@ -27,7 +26,7 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props queryClient.setQueryData(['library.list'], (libraries: any) => [...(libraries || []), lib]); // Switch to the new library - switchLibrary(lib.uuid); + currentLibraryStore.id = lib.uuid; onSubmit?.(); }, diff --git a/apps/mobile/src/components/drawer/DrawerLibraryManager.tsx b/apps/mobile/src/components/drawer/DrawerLibraryManager.tsx index d7cc6befd..da0c1c85a 100644 --- a/apps/mobile/src/components/drawer/DrawerLibraryManager.tsx +++ b/apps/mobile/src/components/drawer/DrawerLibraryManager.tsx @@ -4,8 +4,9 @@ import { MotiView } from 'moti'; import { CaretDown, Gear, Lock, Plus } from 'phosphor-react-native'; import { useEffect, useState } from 'react'; import { Alert, Pressable, Text, View } from 'react-native'; -import { useCurrentLibrary } from '~/../../../packages/client/src'; +import { useClientContext } from '@sd/client'; import { tw, twStyle } from '~/lib/tailwind'; +import { currentLibraryStore } from '~/utils/nav'; import { AnimatedHeight } from '../animation/layout'; import CreateLibraryDialog from '../dialog/CreateLibraryDialog'; import Divider from '../primitive/Divider'; @@ -19,7 +20,7 @@ const DrawerLibraryManager = () => { if (!isDrawerOpen) setDropdownClosed(true); }, [isDrawerOpen]); - const { library: currentLibrary, libraries, switchLibrary } = useCurrentLibrary(); + const { library: currentLibrary, libraries } = useClientContext(); const navigation = useNavigation(); @@ -34,7 +35,7 @@ const DrawerLibraryManager = () => { : 'border-b-app-box border-sidebar-line bg-sidebar-button rounded-t-md' )} > - {currentLibrary?.config.name} + {currentLibrary.config.name} { - + {/* Libraries */} - {libraries?.map((library) => ( - switchLibrary(library.uuid)}> - - { + console.log('library', library); + return ( + (currentLibraryStore.id = library.uuid)}> + - {library.config.name} - - - - ))} + + {library.config.name} + + + + ); + })} {/* Menu */} {/* Create Library */} diff --git a/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx b/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx index ad8c4ecb0..5df271be8 100644 --- a/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx +++ b/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx @@ -2,7 +2,7 @@ import { Trash } from 'phosphor-react-native'; import React from 'react'; import { Controller, useForm } from 'react-hook-form'; import { Alert, Text, View } from 'react-native'; -import { useBridgeMutation, useCurrentLibrary } from '@sd/client'; +import { useBridgeMutation, useLibraryContext } from '@sd/client'; import { Button } from '~/components/primitive/Button'; import { Input } from '~/components/primitive/Input'; import { Switch } from '~/components/primitive/Switch'; @@ -15,7 +15,7 @@ import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator'; const LibraryGeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'LibraryGeneralSettings'>) => { - const { library } = useCurrentLibrary(); + const { library } = useLibraryContext(); const form = useForm({ defaultValues: { name: library.config.name, description: library.config.description } diff --git a/apps/mobile/src/utils/nav.ts b/apps/mobile/src/utils/nav.ts index 7f396e32c..528563ef9 100644 --- a/apps/mobile/src/utils/nav.ts +++ b/apps/mobile/src/utils/nav.ts @@ -3,6 +3,11 @@ import { ParamListBase, getFocusedRouteNameFromRoute } from '@react-navigation/native'; +import { valtioPersist } from '@sd/client'; + +export const currentLibraryStore = valtioPersist('sdActiveLibrary', { + id: null as string | null +}); export const getActiveRouteFromState = function (state: any) { if (!state.routes || state.routes.length === 0 || state.index >= state.routes.length) { diff --git a/packages/client/src/hooks/index.ts b/packages/client/src/hooks/index.ts index 98fc5a722..1df2f5f5c 100644 --- a/packages/client/src/hooks/index.ts +++ b/packages/client/src/hooks/index.ts @@ -1,2 +1,3 @@ -export * from './useCurrentLibrary'; +export * from './useClientContext'; +export * from './useLibraryContext'; export * from './useOnlineLocations'; diff --git a/packages/client/src/hooks/useClientContext.tsx b/packages/client/src/hooks/useClientContext.tsx new file mode 100644 index 000000000..c05092ea2 --- /dev/null +++ b/packages/client/src/hooks/useClientContext.tsx @@ -0,0 +1,73 @@ +import { PropsWithChildren, createContext, useContext, useMemo } from 'react'; +import { LibraryConfigWrapped } from '../core'; +import { useBridgeQuery } from '../rspc'; +import { valtioPersist } from '../stores'; + +// The name of the localStorage key for caching library data +const libraryCacheLocalStorageKey = 'sd-library-list'; + +export const useCachedLibraries = () => + useBridgeQuery(['library.list'], { + keepPreviousData: true, + initialData: () => { + const cachedData = localStorage.getItem(libraryCacheLocalStorageKey); + + if (cachedData) { + // If we fail to load cached data, it's fine + try { + return JSON.parse(cachedData); + } catch (e) { + console.error("Error loading cached 'sd-library-list' data", e); + } + } + + return undefined; + }, + onSuccess: (data) => localStorage.setItem(libraryCacheLocalStorageKey, JSON.stringify(data)) + }); + +export interface ClientContext { + currentLibraryId: string | null; + libraries: ReturnType; + library: LibraryConfigWrapped | null | undefined; +} + +const ClientContext = createContext(null!); + +interface ClientContextProviderProps extends PropsWithChildren { + currentLibraryId: string | null; +} + +export const ClientContextProvider = ({ + children, + currentLibraryId +}: ClientContextProviderProps) => { + const libraries = useCachedLibraries(); + + const library = useMemo(() => { + if (libraries.data) return libraries.data.find((l) => l.uuid === currentLibraryId) ?? null; + }, [currentLibraryId, libraries]); + + // Doesn't need to be in a useEffect + currentLibraryCache.id = currentLibraryId; + + return ( + + {children} + + ); +}; + +export const useClientContext = () => { + const ctx = useContext(ClientContext); + + if (ctx === undefined) throw new Error("'ClientContextProvider' not mounted"); + + return ctx; +}; + +export const useCurrentLibraryId = () => useClientContext().currentLibraryId; + +export const currentLibraryCache = valtioPersist('sd-current-library', { + id: null as string | null +}); diff --git a/packages/client/src/hooks/useCurrentLibrary.tsx b/packages/client/src/hooks/useCurrentLibrary.tsx deleted file mode 100644 index 065afa57d..000000000 --- a/packages/client/src/hooks/useCurrentLibrary.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from 'react'; -import { subscribe, useSnapshot } from 'valtio'; -import { useBridgeQuery } from '../rspc'; -import { valtioPersist } from '../stores'; - -// The name of the localStorage key for caching library data -const libraryCacheLocalStorageKey = 'sd-library-list'; - -type OnNoLibraryFunc = () => void | Promise; - -// Keep this private and use `useCurrentLibrary` hook to access or mutate it -const currentLibraryUuidStore = valtioPersist('sdActiveLibrary', { - id: null as string | null -}); - -const CringeContext = createContext<{ - onNoLibrary: OnNoLibraryFunc; -}>(undefined!); - -export const LibraryContextProvider = ({ - onNoLibrary, - children -}: PropsWithChildren<{ onNoLibrary: OnNoLibraryFunc }>) => { - return {children}; -}; - -export function getLibraryIdRaw(): string | null { - return currentLibraryUuidStore.id; -} - -export function onLibraryChange(func: (newLibraryId: string | null) => void) { - subscribe(currentLibraryUuidStore, () => func(currentLibraryUuidStore.id)); -} - -// this is a hook to get the current library loaded into the UI. It takes care of a bunch of invariants under the hood. -export const useCurrentLibrary = () => { - const currentLibraryUuid = useSnapshot(currentLibraryUuidStore).id; - const ctx = useContext(CringeContext); - if (ctx === undefined) - throw new Error( - "The 'LibraryContextProvider' was not mounted and you attempted do use the 'useCurrentLibrary' hook. Please add the provider in your component tree." - ); - const { data: libraries, isLoading } = useBridgeQuery(['library.list'], { - keepPreviousData: true, - initialData: () => { - const cachedData = localStorage.getItem(libraryCacheLocalStorageKey); - - if (cachedData) { - // If we fail to load cached data, it's fine - try { - return JSON.parse(cachedData); - } catch (e) { - console.error("Error loading cached 'sd-library-list' data", e); - } - } - - return undefined; - }, - onSuccess: (data) => { - localStorage.setItem(libraryCacheLocalStorageKey, JSON.stringify(data)); - - // Redirect to the onboarding flow if the user doesn't have any libraries - if (!data?.length) { - ctx.onNoLibrary(); - } - } - }); - - const switchLibrary = useCallback((libraryUuid: string) => { - currentLibraryUuidStore.id = libraryUuid; - }, []); - - // memorize library to avoid re-running find function - const library = useMemo(() => { - const current = libraries?.find((l: any) => l.uuid === currentLibraryUuid); - // switch to first library if none set - if (libraries && !current && libraries[0]?.uuid) { - switchLibrary(libraries[0]?.uuid); - } - - return current; - }, [libraries, currentLibraryUuid, switchLibrary]); // TODO: This runs when the 'libraries' change causing the whole app to re-render which is cringe. - - return { - library, - libraries, - isLoading, - switchLibrary - }; -}; diff --git a/packages/client/src/hooks/useLibraryContext.tsx b/packages/client/src/hooks/useLibraryContext.tsx new file mode 100644 index 000000000..dca1295ac --- /dev/null +++ b/packages/client/src/hooks/useLibraryContext.tsx @@ -0,0 +1,30 @@ +import { PropsWithChildren, createContext, useContext } from 'react'; +import { LibraryConfigWrapped } from '../core'; +import { ClientContext, useClientContext } from './useClientContext'; + +export interface LibraryContext { + library: LibraryConfigWrapped; + libraries: ClientContext['libraries']; +} + +const LibraryContext = createContext(null!); + +interface LibraryContextProviderProps extends PropsWithChildren { + library: LibraryConfigWrapped; +} + +export const LibraryContextProvider = ({ children, library }: LibraryContextProviderProps) => { + const { libraries } = useClientContext(); + + return ( + {children} + ); +}; + +export const useLibraryContext = () => { + const ctx = useContext(LibraryContext); + + if (ctx === undefined) throw new Error("'LibraryContextProvider' not mounted"); + + return ctx; +}; diff --git a/packages/client/src/rspc.ts b/packages/client/src/rspc.ts index edf66bd37..54a2bba0a 100644 --- a/packages/client/src/rspc.ts +++ b/packages/client/src/rspc.ts @@ -2,7 +2,7 @@ import { ProcedureDef } from '@rspc/client'; import { internal_createReactHooksFactory } from '@rspc/react'; import { QueryClient } from '@tanstack/react-query'; import { LibraryArgs, Procedures } from './core'; -import { getLibraryIdRaw } from './index'; +import { currentLibraryCache } from './hooks'; import { normiCustomHooks } from './normi'; type NonLibraryProcedure = @@ -24,6 +24,10 @@ type StripLibraryArgsFromInput = T extends any : never : never; +let getLibraryId: () => string | null; + +export const setLibraryIdGetter = (g: typeof getLibraryId) => (getLibraryId = g); + export const hooks = internal_createReactHooksFactory(); const nonLibraryHooks = hooks.createHooks< @@ -50,16 +54,16 @@ const libraryHooks = hooks.createHooks< customHooks: normiCustomHooks({ contextSharing: true }, () => { return { mapQueryKey: (keyAndInput) => { - const library_id = getLibraryIdRaw(); - if (library_id === null) + const libraryId = currentLibraryCache.id; + if (libraryId === null) throw new Error('Attempted to do library operation with no library set!'); - return [keyAndInput[0], { library_id, arg: keyAndInput[1] || null }]; + return [keyAndInput[0], { library_id: libraryId, arg: keyAndInput[1] || null }]; }, doMutation: (keyAndInput, next) => { - const library_id = getLibraryIdRaw(); - if (library_id === null) + const libraryId = currentLibraryCache.id; + if (libraryId === null) throw new Error('Attempted to do library operation with no library set!'); - return next([keyAndInput[0], { library_id, arg: keyAndInput[1] || null }]); + return next([keyAndInput[0], { library_id: libraryId, arg: keyAndInput[1] || null }]); } }; }) diff --git a/packages/interface/src/App.tsx b/packages/interface/src/App.tsx index d5a2ceeb8..6da5b55f8 100644 --- a/packages/interface/src/App.tsx +++ b/packages/interface/src/App.tsx @@ -11,8 +11,8 @@ import advancedFormat from 'dayjs/plugin/advancedFormat'; import duration from 'dayjs/plugin/duration'; import relativeTime from 'dayjs/plugin/relativeTime'; import { ErrorBoundary } from 'react-error-boundary'; -import { MemoryRouter, useLocation, useNavigate } from 'react-router-dom'; -import { LibraryContextProvider, queryClient, useDebugState } from '@sd/client'; +import { MemoryRouter } from 'react-router-dom'; +import { queryClient, useDebugState } from '@sd/client'; import { Dialogs } from '@sd/ui'; import { AppRouter } from './AppRouter'; import { ErrorFallback } from './ErrorFallback'; @@ -35,8 +35,9 @@ export default function SpacedriveInterface() { - + + ); @@ -56,20 +57,3 @@ function Devtools() { /> ) : null; } - -// This can't go in `` cause it needs the router context but it can't go in `` because that requires this context -function AppRouterWrapper() { - const navigate = useNavigate(); - const { pathname } = useLocation(); - return ( - { - // only redirect to onboarding flow if path doesn't already include onboarding - if (!pathname.includes('onboarding')) navigate('/onboarding'); - }} - > - - - - ); -} diff --git a/packages/interface/src/AppLayout.tsx b/packages/interface/src/AppLayout.tsx index 30d682d63..1155bcb5d 100644 --- a/packages/interface/src/AppLayout.tsx +++ b/packages/interface/src/AppLayout.tsx @@ -1,19 +1,19 @@ import clsx from 'clsx'; import { Suspense } from 'react'; -import { Outlet } from 'react-router-dom'; -import { useCurrentLibrary } from '@sd/client'; +import { Navigate, Outlet } from 'react-router-dom'; +import { ClientContextProvider, LibraryContextProvider, useClientContext } from '@sd/client'; import { Sidebar } from '~/components/layout/Sidebar'; import { Toasts } from '~/components/primitive/Toasts'; import { useOperatingSystem } from '~/hooks/useOperatingSystem'; +import { useLibraryId } from './util'; + +function AppLayout() { + const { libraries, library } = useClientContext(); -export function AppLayout() { - const { libraries } = useCurrentLibrary(); const os = useOperatingSystem(); - // This will ensure nothing is rendered while the `useCurrentLibrary` hook navigates to the onboarding page. This prevents requests with an invalid library id being sent to the backend - if (libraries?.length === 0) { - return null; - } + if (library === null && libraries.data) + return ; return (
- }> - - + {library ? ( + + }> + + + + ) : ( +

Please select or create a library in the sidebar.

+ )}
); } + +export default () => { + const currentLibraryId = useLibraryId(); + + return ( + + + + ); +}; diff --git a/packages/interface/src/AppRouter.tsx b/packages/interface/src/AppRouter.tsx index 508a21f4e..685a4c76f 100644 --- a/packages/interface/src/AppRouter.tsx +++ b/packages/interface/src/AppRouter.tsx @@ -1,44 +1,42 @@ -import { Navigate, Route, Routes } from 'react-router-dom'; -import { useCurrentLibrary, useInvalidateQuery } from '@sd/client'; -import { AppLayout } from '~/AppLayout'; +import { Navigate, useRoutes } from 'react-router-dom'; +import { currentLibraryCache, useCachedLibraries, useInvalidateQuery } from '@sd/client'; +import AppLayout from '~/AppLayout'; import { useKeybindHandler } from '~/hooks/useKeyboardHandler'; import screens from '~/screens'; -import { lazyEl } from '~/util'; -import OnboardingRoot, { ONBOARDING_SCREENS } from './components/onboarding/OnboardingRoot'; +import OnboardingRoot, { ONBOARDING_ROUTES } from './components/onboarding/OnboardingRoot'; -const NotFound = lazyEl(() => import('./NotFound')); +function Index() { + const libraries = useCachedLibraries(); + + if (libraries.status !== 'success') return null; + + if (libraries.data.length === 0) return ; + + const currentLibrary = libraries.data.find((l) => l.uuid === currentLibraryCache.id); + + const libraryId = currentLibrary ? currentLibrary.uuid : libraries.data[0].uuid; + + return ; +} export function AppRouter() { - const { library } = useCurrentLibrary(); - useKeybindHandler(); useInvalidateQuery(); - return ( - - }> - } /> - {ONBOARDING_SCREENS.map(({ key, component: ScreenComponent }, index) => ( - } /> - ))} - - - }> - {/* As we are caching the libraries in localStore so this *shouldn't* result is visual problems unless something else is wrong */} - {library === undefined ? ( - Please select or create a library in the sidebar. - } - /> - ) : ( - <> - {screens} - - - )} - - - ); + return useRoutes([ + { + index: true, + element: + }, + { + path: 'onboarding', + element: , + children: ONBOARDING_ROUTES + }, + { + path: ':libraryId', + element: , + children: screens + } + ]); } diff --git a/packages/interface/src/components/explorer/Explorer.tsx b/packages/interface/src/components/explorer/Explorer.tsx index a2fb07af8..854da1fb8 100644 --- a/packages/interface/src/components/explorer/Explorer.tsx +++ b/packages/interface/src/components/explorer/Explorer.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from 'react'; -import { ExplorerData, rspc, useCurrentLibrary } from '@sd/client'; +import { ExplorerData, rspc, useLibraryContext } from '@sd/client'; import { useExplorerStore } from '~/hooks/useExplorerStore'; import { Inspector } from '../explorer/Inspector'; import { ExplorerContextMenu } from './ExplorerContextMenu'; @@ -12,7 +12,7 @@ interface Props { export default function Explorer(props: Props) { const expStore = useExplorerStore(); - const { library } = useCurrentLibrary(); + const { library } = useLibraryContext(); const [scrollSegments, setScrollSegments] = useState<{ [key: string]: number }>({}); const [separateTopBar, setSeparateTopBar] = useState(false); diff --git a/packages/interface/src/components/explorer/ExplorerContextMenu.tsx b/packages/interface/src/components/explorer/ExplorerContextMenu.tsx index cb968615d..27cf8b767 100644 --- a/packages/interface/src/components/explorer/ExplorerContextMenu.tsx +++ b/packages/interface/src/components/explorer/ExplorerContextMenu.tsx @@ -19,8 +19,8 @@ import { import { PropsWithChildren, useMemo } from 'react'; import { ExplorerItem, - getLibraryIdRaw, isObject, + useLibraryContext, useLibraryMutation, useLibraryQuery } from '@sd/client'; @@ -28,6 +28,7 @@ import { ContextMenu as CM, dialogManager } from '@sd/ui'; import { getExplorerStore, useExplorerStore } from '~/hooks/useExplorerStore'; import { useOperatingSystem } from '~/hooks/useOperatingSystem'; import { useExplorerParams } from '~/screens/LocationExplorer'; +import { useLibraryId } from '~/util'; import { usePlatform } from '~/util/Platform'; import { showAlertDialog } from '~/util/dialog'; import { DecryptFileDialog } from '../dialog/DecryptFileDialog'; @@ -211,18 +212,15 @@ export interface FileItemContextMenuProps extends PropsWithChildren { } export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps) { + const { library } = useLibraryContext(); const store = useExplorerStore(); const params = useExplorerParams(); const platform = usePlatform(); const objectData = data ? (isObject(data) ? data.item : data.item.object) : null; - const isUnlockedQuery = useLibraryQuery(['keys.isUnlocked']); - const isUnlocked = - isUnlockedQuery.data !== undefined && isUnlockedQuery.data === true ? true : false; - - const mountedUuids = useLibraryQuery(['keys.listMounted']); - const hasMountedKeys = - mountedUuids.data !== undefined && mountedUuids.data.length > 0 ? true : false; + const keyManagerUnlocked = useLibraryQuery(['keys.isUnlocked']).data ?? false; + const mountedKeys = useLibraryQuery(['keys.listMounted']); + const hasMountedKeys = mountedKeys.data?.length ?? 0 > 0; const copyFiles = useLibraryMutation('files.copyFiles'); @@ -232,10 +230,10 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps { + onClick={() => { // TODO: Replace this with a proper UI window.location.href = platform.getFileUrl( - getLibraryIdRaw()!, + library.uuid, store.locationId!, data.item.id ); @@ -255,7 +253,7 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps { + onClick={() => { copyFiles.mutate({ source_location_id: store.locationId!, source_path_id: data.item.id, @@ -269,7 +267,7 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps { + onClick={() => { getExplorerStore().cutCopyState = { sourceLocationId: store.locationId!, sourcePathId: data.item.id, @@ -283,7 +281,7 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps { + onClick={() => { getExplorerStore().cutCopyState = { sourceLocationId: store.locationId!, sourcePathId: data.item.id, @@ -297,7 +295,7 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps