[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
235
components/sftp/hooks/useSftpViewPaneCallbacks.ts
Normal file
235
components/sftp/hooks/useSftpViewPaneCallbacks.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import type { MutableRefObject } from "react";
|
||||
import type { SftpStateApi } from "../../../application/state/useSftpState";
|
||||
import type { RemoteFile, SftpFilenameEncoding } from "../../../types";
|
||||
import type { SftpPaneCallbacks } from "../SftpContext";
|
||||
import type { SftpPane } from "../../../application/state/sftp/types";
|
||||
import { useSftpViewPaneActions } from "./useSftpViewPaneActions";
|
||||
import { useSftpViewFileOps } from "./useSftpViewFileOps";
|
||||
import type { FileOpenerType, SystemAppInfo } from "../../../lib/sftpFileUtils";
|
||||
import { formatFileSize, formatDate } from '../../../application/state/sftp/utils';
|
||||
import { isSessionError } from "../../../application/state/sftp/errors";
|
||||
import { filterHiddenFiles } from "../utils";
|
||||
|
||||
interface UseSftpViewPaneCallbacksParams {
|
||||
sftpRef: MutableRefObject<SftpStateApi>;
|
||||
behaviorRef: MutableRefObject<string>;
|
||||
autoSyncRef: MutableRefObject<boolean>;
|
||||
getOpenerForFileRef: MutableRefObject<
|
||||
(fileName: string) => { openerType?: FileOpenerType; systemApp?: SystemAppInfo } | null
|
||||
>;
|
||||
setOpenerForExtension: (
|
||||
extension: string,
|
||||
openerType: FileOpenerType,
|
||||
systemApp?: SystemAppInfo,
|
||||
) => void;
|
||||
t: (key: string, vars?: Record<string, string | number>) => string;
|
||||
listSftp?: (sftpId: string, path: string, encoding?: SftpFilenameEncoding) => Promise<RemoteFile[]>;
|
||||
showSaveDialog?: (defaultPath: string, filters?: Array<{ name: string; extensions: string[] }>) => Promise<string | null>;
|
||||
selectDirectory?: (title?: string, defaultPath?: string) => Promise<string | null>;
|
||||
getSftpIdForConnection?: (connectionId: string) => string | undefined;
|
||||
listLocalFiles: (path: string) => Promise<RemoteFile[]>;
|
||||
mkdirLocal?: (path: string) => Promise<void>;
|
||||
deleteLocalFile?: (path: string) => Promise<void>;
|
||||
listDrives: () => Promise<string[]>;
|
||||
}
|
||||
|
||||
export const useSftpViewPaneCallbacks = ({
|
||||
sftpRef,
|
||||
behaviorRef,
|
||||
autoSyncRef,
|
||||
getOpenerForFileRef,
|
||||
setOpenerForExtension,
|
||||
t,
|
||||
listSftp,
|
||||
showSaveDialog,
|
||||
selectDirectory,
|
||||
getSftpIdForConnection,
|
||||
listLocalFiles,
|
||||
listDrives,
|
||||
}: UseSftpViewPaneCallbacksParams) => {
|
||||
const paneActions = useSftpViewPaneActions({ sftpRef, t });
|
||||
const fileOps = useSftpViewFileOps({
|
||||
sftpRef,
|
||||
behaviorRef,
|
||||
autoSyncRef,
|
||||
getOpenerForFileRef,
|
||||
setOpenerForExtension,
|
||||
t,
|
||||
showSaveDialog,
|
||||
selectDirectory,
|
||||
getSftpIdForConnection,
|
||||
});
|
||||
|
||||
const listLocalFilesRef = useRef(listLocalFiles);
|
||||
const listSftpRef = useRef(listSftp);
|
||||
const getSftpIdForConnectionRef = useRef(getSftpIdForConnection);
|
||||
|
||||
useEffect(() => {
|
||||
listLocalFilesRef.current = listLocalFiles;
|
||||
listSftpRef.current = listSftp;
|
||||
getSftpIdForConnectionRef.current = getSftpIdForConnection;
|
||||
}, [listLocalFiles, listSftp, getSftpIdForConnection]);
|
||||
|
||||
const makeListDirectory = (side: "left" | "right", getPane: () => SftpPane) =>
|
||||
async (path: string) => {
|
||||
const pane = getPane();
|
||||
if (!pane.connection) return [];
|
||||
const toSize = (raw: string) => parseInt(raw) || 0;
|
||||
const toTs = (raw: string) => new Date(raw).getTime();
|
||||
const normalizeEntries = (rawFiles: RemoteFile[]) =>
|
||||
filterHiddenFiles(
|
||||
rawFiles.map(f => {
|
||||
const s = toSize(f.size);
|
||||
const ms = toTs(f.lastModified);
|
||||
return {
|
||||
name: f.name,
|
||||
type: f.type as 'file' | 'directory' | 'symlink',
|
||||
size: s,
|
||||
sizeFormatted: formatFileSize(s),
|
||||
lastModified: ms,
|
||||
lastModifiedFormatted: formatDate(ms),
|
||||
permissions: f.permissions,
|
||||
owner: f.owner,
|
||||
linkTarget: f.linkTarget as 'file' | 'directory' | null | undefined,
|
||||
hidden: f.hidden,
|
||||
};
|
||||
}),
|
||||
pane.showHiddenFiles,
|
||||
);
|
||||
if (pane.connection.isLocal) {
|
||||
return normalizeEntries(await listLocalFilesRef.current(path));
|
||||
}
|
||||
const sftpId = getSftpIdForConnectionRef.current?.(pane.connection.id);
|
||||
if (!sftpId) {
|
||||
const error = new Error("SFTP session not found");
|
||||
sftpRef.current.reportSessionError(side, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
let rawFiles: RemoteFile[] | undefined;
|
||||
try {
|
||||
rawFiles = await listSftpRef.current?.(sftpId, path, pane.filenameEncoding);
|
||||
} catch (err) {
|
||||
if (isSessionError(err)) {
|
||||
sftpRef.current.reportSessionError(side, err as Error);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!rawFiles) return [];
|
||||
return normalizeEntries(rawFiles);
|
||||
};
|
||||
|
||||
/* eslint-disable react-hooks/exhaustive-deps -- Handlers use refs, so they are stable */
|
||||
const leftCallbacks = useMemo<SftpPaneCallbacks>(
|
||||
() => ({
|
||||
onConnect: paneActions.onConnectLeft,
|
||||
onDisconnect: paneActions.onDisconnectLeft,
|
||||
onPrepareSelection: paneActions.onPrepareSelectionLeft,
|
||||
onNavigateTo: paneActions.onNavigateToLeft,
|
||||
onNavigateUp: paneActions.onNavigateUpLeft,
|
||||
onRefresh: paneActions.onRefreshLeft,
|
||||
onRefreshTab: paneActions.onRefreshTabLeft,
|
||||
onSetFilenameEncoding: paneActions.onSetFilenameEncodingLeft,
|
||||
onOpenEntry: fileOps.onOpenEntryLeft,
|
||||
onToggleSelection: paneActions.onToggleSelectionLeft,
|
||||
onRangeSelect: paneActions.onRangeSelectLeft,
|
||||
onClearSelection: paneActions.onClearSelectionLeft,
|
||||
onSetFilter: paneActions.onSetFilterLeft,
|
||||
onCreateDirectory: paneActions.onCreateDirectoryLeft,
|
||||
onCreateDirectoryAtPath: paneActions.onCreateDirectoryAtPathLeft,
|
||||
onCreateFile: paneActions.onCreateFileLeft,
|
||||
onCreateFileAtPath: paneActions.onCreateFileAtPathLeft,
|
||||
onDeleteFiles: paneActions.onDeleteFilesLeft,
|
||||
onDeleteFilesAtPath: paneActions.onDeleteFilesAtPathLeft,
|
||||
onRenameFile: paneActions.onRenameFileLeft,
|
||||
onRenameFileAtPath: paneActions.onRenameFileAtPathLeft,
|
||||
onMoveEntriesToPath: paneActions.onMoveEntriesToPathLeft,
|
||||
onCopyToOtherPane: paneActions.onCopyToOtherPaneLeft,
|
||||
onReceiveFromOtherPane: paneActions.onReceiveFromOtherPaneLeft,
|
||||
onEditPermissions: fileOps.onEditPermissionsLeft,
|
||||
onEditFile: fileOps.onEditFileLeft,
|
||||
onOpenFile: fileOps.onOpenFileLeft,
|
||||
onOpenFileWithSystemDefault: fileOps.onOpenFileWithSystemDefaultLeft,
|
||||
onOpenFileWith: fileOps.onOpenFileWithLeft,
|
||||
onDownloadFile: fileOps.onDownloadFileLeft,
|
||||
onDownloadFiles: fileOps.onDownloadFilesLeft,
|
||||
onExtractArchive: fileOps.onExtractArchiveLeft,
|
||||
onUploadExternalFiles: fileOps.onUploadExternalFilesLeft,
|
||||
onUploadExternalFileList: fileOps.onUploadExternalFileListLeft,
|
||||
onUploadExternalFolder: fileOps.onUploadExternalFolderLeft,
|
||||
onListDirectory: makeListDirectory("left", () => sftpRef.current.leftPane),
|
||||
onListDrives: listDrives,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const rightCallbacks = useMemo<SftpPaneCallbacks>(
|
||||
() => ({
|
||||
onConnect: paneActions.onConnectRight,
|
||||
onDisconnect: paneActions.onDisconnectRight,
|
||||
onPrepareSelection: paneActions.onPrepareSelectionRight,
|
||||
onNavigateTo: paneActions.onNavigateToRight,
|
||||
onNavigateUp: paneActions.onNavigateUpRight,
|
||||
onRefresh: paneActions.onRefreshRight,
|
||||
onRefreshTab: paneActions.onRefreshTabRight,
|
||||
onSetFilenameEncoding: paneActions.onSetFilenameEncodingRight,
|
||||
onOpenEntry: fileOps.onOpenEntryRight,
|
||||
onToggleSelection: paneActions.onToggleSelectionRight,
|
||||
onRangeSelect: paneActions.onRangeSelectRight,
|
||||
onClearSelection: paneActions.onClearSelectionRight,
|
||||
onSetFilter: paneActions.onSetFilterRight,
|
||||
onCreateDirectory: paneActions.onCreateDirectoryRight,
|
||||
onCreateDirectoryAtPath: paneActions.onCreateDirectoryAtPathRight,
|
||||
onCreateFile: paneActions.onCreateFileRight,
|
||||
onCreateFileAtPath: paneActions.onCreateFileAtPathRight,
|
||||
onDeleteFiles: paneActions.onDeleteFilesRight,
|
||||
onDeleteFilesAtPath: paneActions.onDeleteFilesAtPathRight,
|
||||
onRenameFile: paneActions.onRenameFileRight,
|
||||
onRenameFileAtPath: paneActions.onRenameFileAtPathRight,
|
||||
onMoveEntriesToPath: paneActions.onMoveEntriesToPathRight,
|
||||
onCopyToOtherPane: paneActions.onCopyToOtherPaneRight,
|
||||
onReceiveFromOtherPane: paneActions.onReceiveFromOtherPaneRight,
|
||||
onEditPermissions: fileOps.onEditPermissionsRight,
|
||||
onEditFile: fileOps.onEditFileRight,
|
||||
onOpenFile: fileOps.onOpenFileRight,
|
||||
onOpenFileWithSystemDefault: fileOps.onOpenFileWithSystemDefaultRight,
|
||||
onOpenFileWith: fileOps.onOpenFileWithRight,
|
||||
onDownloadFile: fileOps.onDownloadFileRight,
|
||||
onDownloadFiles: fileOps.onDownloadFilesRight,
|
||||
onExtractArchive: fileOps.onExtractArchiveRight,
|
||||
onUploadExternalFiles: fileOps.onUploadExternalFilesRight,
|
||||
onUploadExternalFileList: fileOps.onUploadExternalFileListRight,
|
||||
onUploadExternalFolder: fileOps.onUploadExternalFolderRight,
|
||||
onListDirectory: makeListDirectory("right", () => sftpRef.current.rightPane),
|
||||
onListDrives: listDrives,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
/* eslint-enable react-hooks/exhaustive-deps */
|
||||
|
||||
return {
|
||||
leftCallbacks,
|
||||
rightCallbacks,
|
||||
dragCallbacks: paneActions.dragCallbacks,
|
||||
draggedFiles: paneActions.draggedFiles,
|
||||
permissionsState: fileOps.permissionsState,
|
||||
setPermissionsState: fileOps.setPermissionsState,
|
||||
showTextEditor: fileOps.showTextEditor,
|
||||
setShowTextEditor: fileOps.setShowTextEditor,
|
||||
textEditorTarget: fileOps.textEditorTarget,
|
||||
setTextEditorTarget: fileOps.setTextEditorTarget,
|
||||
textEditorContent: fileOps.textEditorContent,
|
||||
setTextEditorContent: fileOps.setTextEditorContent,
|
||||
loadingTextContent: fileOps.loadingTextContent,
|
||||
showFileOpenerDialog: fileOps.showFileOpenerDialog,
|
||||
setShowFileOpenerDialog: fileOps.setShowFileOpenerDialog,
|
||||
fileOpenerTarget: fileOps.fileOpenerTarget,
|
||||
setFileOpenerTarget: fileOps.setFileOpenerTarget,
|
||||
handleSaveTextFile: fileOps.handleSaveTextFile,
|
||||
onPromoteToTab: fileOps.onPromoteToTab,
|
||||
handleFileOpenerSelect: fileOps.handleFileOpenerSelect,
|
||||
handleSelectSystemApp: fileOps.handleSelectSystemApp,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user