* feat(app): open changed files from context menu * fix(app): preserve changed-file row behavior * feat(app): add changed files to open chats * feat(app): share file actions across explorer and changes * feat(app): attach workspace files to focused chat * feat(app): drag workspace files into chat * refactor(app): use file actions menu for changed files * fix(app): open changed-file actions on right click * test(app): cover direct file attachment flow * fix(app): enable add to chat on native * fix(app): align workspace file pane rows * refactor(app): simplify workspace file attachments * fix(app): preserve uploaded file draft attachments --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { useCallback, useMemo } from "react";
|
|
import { useHosts } from "@/runtime/host-runtime";
|
|
import { useDownloadStore } from "@/stores/download-store";
|
|
import { useFileExplorerActions } from "@/hooks/use-file-explorer-actions";
|
|
|
|
interface UseFileDownloadParams {
|
|
serverId: string;
|
|
workspaceId?: string | null;
|
|
workspaceRoot: string;
|
|
}
|
|
|
|
/**
|
|
* Returns a stable callback that downloads a single workspace file by its
|
|
* workspace-relative path. Shared by the file explorer tree and the git diff
|
|
* pane so both surfaces download through the same host token + download-store
|
|
* pipeline instead of duplicating the plumbing.
|
|
*/
|
|
export function useFileDownload({
|
|
serverId,
|
|
workspaceId,
|
|
workspaceRoot,
|
|
}: UseFileDownloadParams): (input: { fileName: string; path: string }) => void {
|
|
const daemons = useHosts();
|
|
const daemonProfile = useMemo(
|
|
() => daemons.find((daemon) => daemon.serverId === serverId),
|
|
[daemons, serverId],
|
|
);
|
|
const normalizedWorkspaceRoot = useMemo(() => workspaceRoot.trim(), [workspaceRoot]);
|
|
const workspaceScopeId = useMemo(
|
|
() => workspaceId?.trim() || normalizedWorkspaceRoot,
|
|
[normalizedWorkspaceRoot, workspaceId],
|
|
);
|
|
const { requestFileDownloadToken } = useFileExplorerActions({
|
|
serverId,
|
|
workspaceId,
|
|
workspaceRoot: normalizedWorkspaceRoot,
|
|
});
|
|
const startDownload = useDownloadStore((state) => state.startDownload);
|
|
|
|
return useCallback(
|
|
({ fileName, path }) => {
|
|
if (!workspaceScopeId) {
|
|
return;
|
|
}
|
|
void startDownload({
|
|
serverId,
|
|
scopeId: workspaceScopeId,
|
|
fileName,
|
|
path,
|
|
daemonProfile,
|
|
requestFileDownloadToken: (targetPath) => requestFileDownloadToken(targetPath),
|
|
});
|
|
},
|
|
[daemonProfile, requestFileDownloadToken, serverId, startDownload, workspaceScopeId],
|
|
);
|
|
}
|