From 2c4de5f7050f856a16ec9416cfeb7a8b6c58f5d8 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sat, 3 Oct 2026 21:02:38 +0800 Subject: [PATCH] fix(file-manager): render transfer toasts without plugin hooks (#1546) --- .../file-manager/src/frontend/FileManager.tsx | 12 +++-- .../src/frontend/components/DiffViewer.tsx | 3 +- .../components/DownloadProgressToast.tsx | 5 +- .../src/frontend/components/FileWindow.tsx | 3 +- .../components/LocalTransferProgressToast.tsx | 5 +- .../components/TransferProgressToast.tsx | 5 +- .../src/frontend/hooks/useLocalTransfers.ts | 1 + .../src/frontend/transferProgressMonitor.tsx | 6 +++ .../frontend/progress-toast-context.test.tsx | 54 +++++++++++++++++++ 9 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 plugins/file-manager/tests/frontend/progress-toast-context.test.tsx diff --git a/plugins/file-manager/src/frontend/FileManager.tsx b/plugins/file-manager/src/frontend/FileManager.tsx index c234717ac..9d10f5ff9 100644 --- a/plugins/file-manager/src/frontend/FileManager.tsx +++ b/plugins/file-manager/src/frontend/FileManager.tsx @@ -1416,10 +1416,13 @@ function FileManagerContent({ const { downloadSSHFileStream } = await import("./api/ssh-file-operations-api"); - toast.loading(, { - id: toastId, - duration: Infinity, - }); + toast.loading( + , + { + id: toastId, + duration: Infinity, + }, + ); await downloadSSHFileStream( sshSessionId, @@ -1438,6 +1441,7 @@ function FileManagerContent({ toast.loading( { const toastId = toast.loading( - , + , { duration: Infinity }, ); try { @@ -145,6 +145,7 @@ export function DiffViewer({ ({ loaded, total }) => { toast.loading( 0 ? Math.min(100, Math.round((loaded / total) * 100)) diff --git a/plugins/file-manager/src/frontend/components/FileWindow.tsx b/plugins/file-manager/src/frontend/components/FileWindow.tsx index 2db6bad3e..e727e6527 100644 --- a/plugins/file-manager/src/frontend/components/FileWindow.tsx +++ b/plugins/file-manager/src/frontend/components/FileWindow.tsx @@ -483,7 +483,7 @@ export function FileWindow({ const handleDownload = async () => { const toastId = toast.loading( - , + , { duration: Infinity }, ); try { @@ -495,6 +495,7 @@ export function FileWindow({ ({ loaded, total }) => { toast.loading( void; } export function LocalTransferProgressToast({ + t, status, onCancel, }: LocalTransferProgressToastProps) { - const { t } = useTranslation(); const percent = status.totalBytes > 0 ? Math.min(100, Math.round((status.bytesDone / status.totalBytes) * 100)) diff --git a/plugins/file-manager/src/frontend/components/TransferProgressToast.tsx b/plugins/file-manager/src/frontend/components/TransferProgressToast.tsx index 830b143f8..4a85f1b9e 100644 --- a/plugins/file-manager/src/frontend/components/TransferProgressToast.tsx +++ b/plugins/file-manager/src/frontend/components/TransferProgressToast.tsx @@ -4,7 +4,7 @@ import { getTransferProgressPercent, type TransferProgressResponse, } from "../api/transfer-api"; -import { useTranslation } from "@termix/plugin-sdk/frontend"; +import type { TranslateFn } from "@termix/plugin-sdk/frontend"; import { Archive, ArchiveRestore, @@ -16,6 +16,7 @@ import { import { TransferProgressBar } from "./TransferProgressBar"; interface TransferProgressToastProps { + t: TranslateFn; status: TransferProgressResponse; liveMbPerSec?: number; stalled?: boolean; @@ -25,6 +26,7 @@ interface TransferProgressToastProps { } export function TransferProgressToast({ + t, status, liveMbPerSec, stalled = false, @@ -32,7 +34,6 @@ export function TransferProgressToast({ onCancel, cancelling = false, }: TransferProgressToastProps) { - const { t } = useTranslation(); const percent = getTransferProgressPercent(status); let title = t("transfer.progressTransferring"); diff --git a/plugins/file-manager/src/frontend/hooks/useLocalTransfers.ts b/plugins/file-manager/src/frontend/hooks/useLocalTransfers.ts index 3ea737781..1ac7541c5 100644 --- a/plugins/file-manager/src/frontend/hooks/useLocalTransfers.ts +++ b/plugins/file-manager/src/frontend/hooks/useLocalTransfers.ts @@ -193,6 +193,7 @@ export function useLocalTransfers({ const render = () => { toast.loading( createElement(LocalTransferProgressToast, { + t, status: { ...status, cancelling }, onCancel: () => { cancelled = true; diff --git a/plugins/file-manager/src/frontend/transferProgressMonitor.tsx b/plugins/file-manager/src/frontend/transferProgressMonitor.tsx index ae1b742bf..8c86f3e30 100644 --- a/plugins/file-manager/src/frontend/transferProgressMonitor.tsx +++ b/plugins/file-manager/src/frontend/transferProgressMonitor.tsx @@ -38,6 +38,7 @@ function formatFileSize(bytes?: number): string { } function renderTransferProgressToast( + t: TFunction, toastId: string | number, status: TransferProgressResponse, liveMbPerSec?: number, @@ -47,6 +48,7 @@ function renderTransferProgressToast( ): void { toast.loading( , @@ -279,6 +282,7 @@ export function beginTransferProgressMonitoring( if (cancelling) return; cancelling = true; renderTransferProgressToast( + t, progressToast, { ...initialStatus, transferId }, undefined, @@ -294,6 +298,7 @@ export function beginTransferProgressMonitoring( options.onProgress?.(status); const { rate, stalled } = progressTracker.update(status); renderTransferProgressToast( + t, progressToast, status, rate, @@ -333,6 +338,7 @@ export function beginTransferProgressMonitoring( }); renderTransferProgressToast( + t, progressToast, initialStatus, undefined, diff --git a/plugins/file-manager/tests/frontend/progress-toast-context.test.tsx b/plugins/file-manager/tests/frontend/progress-toast-context.test.tsx new file mode 100644 index 000000000..4032b6644 --- /dev/null +++ b/plugins/file-manager/tests/frontend/progress-toast-context.test.tsx @@ -0,0 +1,54 @@ +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import { afterEach, expect, it, vi } from "vitest"; +vi.mock("@termix/plugin-sdk/frontend", () => ({ + useTranslation: () => { + throw Error("No plugin scope in the global toaster"); + }, +})); +import { DownloadProgressToast } from "../../src/frontend/components/DownloadProgressToast"; +import { TransferProgressToast } from "../../src/frontend/components/TransferProgressToast"; +import { LocalTransferProgressToast } from "../../src/frontend/components/LocalTransferProgressToast"; +afterEach(cleanup); +const t = (key: string) => key; +it("renders download progress outside plugin scope", () => { + render( + , + ); + expect(screen.getByRole("progressbar").getAttribute("aria-valuenow")).toBe( + "50", + ); +}); +it("renders host transfer progress outside plugin scope", () => { + render( + , + ); + expect(screen.getByRole("progressbar")).toBeTruthy(); +}); +it("preserves cancellation on local transfers outside plugin scope", () => { + const cancel = vi.fn(); + render( + , + ); + fireEvent.click(screen.getByRole("button")); + expect(cancel).toHaveBeenCalledOnce(); +});