vfs:copy/move/delete
This commit is contained in:
parent
dd159af703
commit
1a1646dad5
@ -7,13 +7,18 @@ export interface VfsPathRef {
|
||||
}
|
||||
|
||||
export type VfsCopyConflictMode = 'auto' | 'manual';
|
||||
export type VfsCopyConflictStrategy = 'error' | 'overwrite' | 'skip' | 'rename';
|
||||
export type VfsCopyConflictStrategy = 'error' | 'overwrite' | 'skip' | 'rename' | 'if_newer';
|
||||
export type VfsTransferOperation = 'copy' | 'move' | 'delete';
|
||||
|
||||
export interface VfsCopyRequest {
|
||||
operation?: VfsTransferOperation;
|
||||
source: VfsPathRef;
|
||||
destination: VfsPathRef;
|
||||
destination?: VfsPathRef;
|
||||
conflictMode?: VfsCopyConflictMode;
|
||||
conflictStrategy?: VfsCopyConflictStrategy;
|
||||
includePatterns?: string[];
|
||||
excludePatterns?: string[];
|
||||
excludeDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface VfsCopyConflict {
|
||||
|
||||
@ -21,6 +21,10 @@ export const useStream = () => {
|
||||
return context;
|
||||
};
|
||||
|
||||
export const useOptionalStream = () => {
|
||||
return useContext(StreamContext);
|
||||
};
|
||||
|
||||
interface StreamProviderProps {
|
||||
children: ReactNode;
|
||||
url?: string;
|
||||
|
||||
@ -24,6 +24,8 @@ import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { IMAGE_EXTS, VIDEO_EXTS, CODE_EXTS } from '@/modules/storage/helpers';
|
||||
import { T } from '@/i18n';
|
||||
import { useOptionalStream } from '@/contexts/StreamContext';
|
||||
import type { AppEvent } from '@/types-server';
|
||||
|
||||
import { useVfsAdapter } from '@/modules/storage/hooks/useVfsAdapter';
|
||||
import { useSelection } from '@/modules/storage/hooks/useSelection';
|
||||
@ -238,6 +240,56 @@ const FileBrowserPanel: React.FC<FileBrowserPanelProps> = ({
|
||||
}
|
||||
});
|
||||
|
||||
const stream = useOptionalStream();
|
||||
const refreshTimerRef = useRef<number | null>(null);
|
||||
const scheduleScopedRefresh = useCallback(() => {
|
||||
if (refreshTimerRef.current) {
|
||||
window.clearTimeout(refreshTimerRef.current);
|
||||
}
|
||||
refreshTimerRef.current = window.setTimeout(() => {
|
||||
fetchDir(currentPath || '/');
|
||||
refreshTimerRef.current = null;
|
||||
}, 250);
|
||||
}, [currentPath, fetchDir]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!stream) return;
|
||||
const normalize = (p?: string) => (p || '').replace(/^\/+|\/+$/g, '');
|
||||
const unsubscribe = stream.subscribe((event: AppEvent) => {
|
||||
if (event.kind !== 'system') return;
|
||||
if (event.type !== 'vfs-copy' && event.type !== 'vfs-index') return;
|
||||
|
||||
if (event.type === 'vfs-index') {
|
||||
const evMount = String(event.data?.mount || '');
|
||||
const evTarget = normalize(String(event.data?.targetPath || ''));
|
||||
const here = normalize(currentPath);
|
||||
if (evMount === mount && (evTarget === '' || here === evTarget || here.startsWith(`${evTarget}/`) || evTarget.startsWith(`${here}/`))) {
|
||||
scheduleScopedRefresh();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// vfs-copy
|
||||
const srcMount = String(event.data?.sourceMount || '');
|
||||
const dstMount = String(event.data?.destinationMount || '');
|
||||
const srcPath = normalize(String(event.data?.sourcePath || ''));
|
||||
const dstPath = normalize(String(event.data?.destinationPath || ''));
|
||||
const here = normalize(currentPath);
|
||||
const mountMatches = mount === srcMount || mount === dstMount;
|
||||
const pathMatches = here === '' || here === srcPath || here === dstPath || srcPath.startsWith(`${here}/`) || dstPath.startsWith(`${here}/`);
|
||||
if (mountMatches && pathMatches) {
|
||||
scheduleScopedRefresh();
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
if (refreshTimerRef.current) {
|
||||
window.clearTimeout(refreshTimerRef.current);
|
||||
refreshTimerRef.current = null;
|
||||
}
|
||||
unsubscribe();
|
||||
};
|
||||
}, [stream, mount, currentPath, scheduleScopedRefresh]);
|
||||
|
||||
// ── View Mode & Zoom ─────────────────────────────────────────
|
||||
|
||||
const displayNodes = useMemo(() => [...sorted, ...(uploads as INode[])], [sorted, uploads]);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user