Files
spacedrive/interface/app/$libraryId/Explorer/ContextMenu/SharedItems.tsx
Oscar Beaumont 2ca88a5d6b Refactor P2P Hooks (#2193)
* Basic AF server

* wip

* Add autonat to relay server

* Add autonat client + fixes

* Deploy script

* wip

* Debug view

* wip

* wip

* relay all events

* wip

* fix

* wip

* libp2p man spoke

* dctur

* Relay config file

* Advertise relay server

* Dynamic relay configuration

* wip

* p2p relay config

* cleanup

* push instances into p2p state

* fix

* Fix up TS

* refactor p2p hooks

* fix backend state

* Skip self

* a

* b

* Relay config in debug query

* Fix method name

* tsc is broken on my machine

* a

* Incorrect typecasts. Can we just ban them

* fix types
2024-03-13 06:43:22 +00:00

237 lines
5.7 KiB
TypeScript

import { FileX, Share as ShareIcon } from '@phosphor-icons/react';
import { useMemo } from 'react';
import { useBridgeMutation, useDiscoveredPeers, useSelector } from '@sd/client';
import { ContextMenu, ModifierKeys } from '@sd/ui';
import { Menu } from '~/components/Menu';
import { useLocale, useOperatingSystem } from '~/hooks';
import { useKeybindFactory } from '~/hooks/useKeybindFactory';
import { isNonEmpty } from '~/util';
import { usePlatform, type Platform } from '~/util/Platform';
import { useExplorerContext } from '../Context';
import { getQuickPreviewStore } from '../QuickPreview/store';
import { RevealInNativeExplorerBase } from '../RevealInNativeExplorer';
import { explorerStore } from '../store';
import { getPaths } from '../useExplorerDnd';
import { useViewItemDoubleClick } from '../View/ViewItem';
import { Conditional, ConditionalItem } from './ConditionalItem';
import { useContextMenuContext } from './context';
import OpenWith from './OpenWith';
export const OpenOrDownload = new ConditionalItem({
useCondition: () => {
const { selectedFilePaths, selectedEphemeralPaths } = useContextMenuContext();
const { openFilePaths, openEphemeralFiles } = usePlatform();
if (
!openFilePaths ||
!openEphemeralFiles ||
(!isNonEmpty(selectedFilePaths) && !isNonEmpty(selectedEphemeralPaths))
)
return null;
return { openFilePaths, openEphemeralFiles, selectedFilePaths, selectedEphemeralPaths };
},
Component: () => {
const keybind = useKeybindFactory();
const { platform } = usePlatform();
const { doubleClick } = useViewItemDoubleClick();
const os = useOperatingSystem(true);
const { t } = useLocale();
if (platform === 'web') return <Menu.Item label={t('download')} />;
else
return (
<>
<Menu.Item
label={t('open')}
keybind={keybind(os === 'windows' ? [] : [ModifierKeys.Control], [
os === 'windows' ? 'Enter' : 'O'
])}
onClick={() => doubleClick()}
/>
<Conditional items={[OpenWith]} />
</>
);
}
});
export const OpenQuickView = () => {
const keybind = useKeybindFactory();
const { t } = useLocale();
return (
<ContextMenu.Item
label={t('quick_view')}
keybind={keybind([], [' '])}
onClick={() => (getQuickPreviewStore().open = true)}
/>
);
};
export const Details = new ConditionalItem({
useCondition: () => {
const showInspector = useSelector(explorerStore, (s) => s.showInspector);
if (showInspector) return null;
return {};
},
Component: () => {
const keybind = useKeybindFactory();
const { t } = useLocale();
return (
<ContextMenu.Item
label={t('details')}
keybind={keybind([ModifierKeys.Control], ['I'])}
// icon={Sidebar}
onClick={() => (explorerStore.showInspector = true)}
/>
);
}
});
export const Rename = new ConditionalItem({
useCondition: () => {
const { selectedItems } = useContextMenuContext();
const settings = useExplorerContext().useSettingsSnapshot();
if (settings.layoutMode === 'media' || selectedItems.length > 1) return null;
return {};
},
Component: () => {
const keybind = useKeybindFactory();
const os = useOperatingSystem(true);
const { t } = useLocale();
return (
<ContextMenu.Item
label={t('rename')}
keybind={keybind([], [os === 'windows' ? 'F2' : 'Enter'])}
onClick={() => (explorerStore.isRenaming = true)}
/>
);
}
});
export const RevealInNativeExplorer = new ConditionalItem({
useCondition: () => {
const { selectedItems } = useContextMenuContext();
const items = useMemo(() => {
const array: Parameters<NonNullable<Platform['revealItems']>>[1] = [];
for (const item of selectedItems) {
switch (item.type) {
case 'Path': {
array.push({
FilePath: { id: item.item.id }
});
break;
}
case 'Object': {
// this isn't good but it's the current behaviour
const filePath = item.item.file_paths[0];
if (filePath)
array.push({
FilePath: {
id: filePath.id
}
});
else return [];
break;
}
case 'Location': {
array.push({
Location: {
id: item.item.id
}
});
break;
}
case 'NonIndexedPath': {
array.push({
Ephemeral: {
path: item.item.path
}
});
break;
}
}
}
return array;
}, [selectedItems]);
if (!isNonEmpty(items)) return null;
return { items };
},
Component: ({ items }) => <RevealInNativeExplorerBase items={items} />
});
export const Deselect = new ConditionalItem({
useCondition: () => {
const cutCopyState = useSelector(explorerStore, (s) => s.cutCopyState);
if (cutCopyState.type === 'Idle') return null;
return {};
},
Component: () => {
const { t } = useLocale();
return (
<ContextMenu.Item
label={t('deselect')}
icon={FileX}
onClick={() => {
explorerStore.cutCopyState = {
type: 'Idle'
};
}}
/>
);
}
});
export const Share = () => {
const { t } = useLocale();
return (
<Menu.SubMenu label={t('share')} icon={ShareIcon}>
<Menu.SubMenu label="Spacedrop">
<SpacedropNodes />
</Menu.SubMenu>
</Menu.SubMenu>
);
};
const SpacedropNodes = () => {
const { t } = useLocale();
const explorer = useExplorerContext();
const discoveredPeers = useDiscoveredPeers();
const spacedrop = useBridgeMutation('p2p.spacedrop');
if (discoveredPeers.size === 0) {
return <p className="p-1 text-center text-sm">{t('no_nodes_found')}</p>;
}
return Array.from(discoveredPeers).map(([id, peer]) => (
<Menu.Item
key={id}
label={peer.metadata.name}
disabled={spacedrop.isLoading}
onClick={async () => {
spacedrop.mutateAsync({
identity: id,
file_path: await getPaths([...explorer.selectedItems])
});
}}
/>
));
};