mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-28 18:38:12 -04:00
* 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
237 lines
5.7 KiB
TypeScript
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])
|
|
});
|
|
}}
|
|
/>
|
|
));
|
|
};
|