Files
kandev/apps/web/hooks/use-file-operations.ts
T
João Salavisa ce82e2a4ab fix(i18n): externalize remaining frontend copy (#2616)
Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-08-13 11:11:46 +01:00

131 lines
3.9 KiB
TypeScript

import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { getWebSocketClient } from "@/lib/ws/connection";
import type { WebSocketClient } from "@/lib/ws/client";
import { createFile, deleteFile, renameFile, requestFileContent } from "@/lib/ws/workspace-files";
import { triggerFileDownload } from "@/lib/utils/file-download";
import { useToast } from "@/components/toast-provider";
type ToastFn = ReturnType<typeof useToast>["toast"];
const ERROR_VARIANT = "error" as const;
type DownloadResult = { ok: true } | { ok: false; error?: string };
/**
* Fetch a file from the workspace and trigger a browser download.
* Extracted from the hook so it can be unit-tested without React.
*/
export async function downloadFileContent(
client: WebSocketClient,
sessionId: string,
path: string,
unknownError: string,
): Promise<DownloadResult> {
try {
const response = await requestFileContent(client, sessionId, path);
if (response.error) return { ok: false, error: response.error };
triggerFileDownload({
fileName: path,
content: response.content,
isBinary: !!response.is_binary,
});
return { ok: true };
} catch (error) {
return { ok: false, error: error instanceof Error ? error.message : unknownError };
}
}
async function runFileOp<T extends { success: boolean; error?: string }>(
op: () => Promise<T>,
title: string,
unknownError: string,
toast: ToastFn,
): Promise<boolean> {
try {
const response = await op();
if (!response.success) {
toast({ title, description: response.error || unknownError, variant: ERROR_VARIANT });
return false;
}
return true;
} catch (error) {
const description = error instanceof Error ? error.message : unknownError;
toast({ title, description, variant: ERROR_VARIANT });
return false;
}
}
export function useFileOperations(sessionId: string | null) {
const { toast } = useToast();
const { t } = useTranslation("task");
const unknownError = t("common:anUnknownErrorOccurred");
const handleCreateFile = useCallback(
async (path: string): Promise<boolean> => {
const client = getWebSocketClient();
if (!client || !sessionId) return false;
return runFileOp(
() => createFile(client, sessionId, path),
t("task:failedToCreateFile"),
unknownError,
toast,
);
},
[sessionId, t, toast, unknownError],
);
const handleDeleteFile = useCallback(
async (path: string): Promise<boolean> => {
const client = getWebSocketClient();
if (!client || !sessionId) return false;
return runFileOp(
() => deleteFile(client, sessionId, path),
t("task:failedToDeleteItem"),
unknownError,
toast,
);
},
[sessionId, t, toast, unknownError],
);
const handleRenameFile = useCallback(
async (oldPath: string, newPath: string): Promise<boolean> => {
const client = getWebSocketClient();
if (!client || !sessionId) return false;
return runFileOp(
() => renameFile(client, sessionId, oldPath, newPath),
t("task:failedToRenameItem"),
unknownError,
toast,
);
},
[sessionId, t, toast, unknownError],
);
const handleDownloadFile = useCallback(
async (path: string): Promise<boolean> => {
const client = getWebSocketClient();
if (!client || !sessionId) return false;
const result = await downloadFileContent(client, sessionId, path, unknownError);
if (!result.ok) {
toast({
title: t("task:failedToDownloadFile"),
description: result.error || unknownError,
variant: ERROR_VARIANT,
});
return false;
}
return true;
},
[sessionId, t, toast, unknownError],
);
return {
createFile: handleCreateFile,
deleteFile: handleDeleteFile,
renameFile: handleRenameFile,
downloadFile: handleDownloadFile,
};
}