Files
teleport/web/packages/shared/components/FileTransfer/FileTransfer.tsx
T
cb642ce2e3 Upgrade to React 19 (#54460)
* update package.json

* codemod scoped-jsx

* refobject-defaults

* Fix some tests

* Lint

* Move import after license comment

* Fix test types for a wrapper of Button

I couldn't get it to work when destructuring rest props, e.g.:

    const { size, children, ...buttonProps } = props;

so for now I just removed it altogether. I don't know why it works
in ForwardedAsWrapper, I suppose it has to do either with the css prop
passed to ButtonPrimary? Or maybe the forwardedAs prop accepted by
ForwardedAsWrapper?

* revert most ReactElement<any>

* Fix tests

* Remove extra useEffect from useTable

* Pass null to useRef when dealing with DOM elements

* Bring back just `ReactElement` without `<any>` where possible

* Remove defaultProps

* pnpm dedupe

* revert test and fix stories

* Import JSX in StatusInfo.tsx

---------

Co-authored-by: Grzegorz Zdunek <grzegorz.zdunek@goteleport.com>
Co-authored-by: Rafał Cieślak <rafal.cieslak@goteleport.com>
Co-authored-by: Lisa Kim <lisa@goteleport.com>
2025-05-09 14:05:15 +00:00

141 lines
4.0 KiB
TypeScript

/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type { JSX } from 'react';
import { FileTransferContainer } from './FileTransferContainer';
import { useFileTransferContext } from './FileTransferContextProvider';
import {
FileTransferDialogDirection,
FileTransferListeners,
FileTransferStateless,
} from './FileTransferStateless';
interface FileTransferProps {
transferHandlers: TransferHandlers;
// errorText is any general error that isn't related to a specific transfer
errorText?: string;
/**
* `beforeClose` is called when an attempt to close the dialog was made
* and there is a file transfer in progress.
* Returning `true` will close the dialog, returning `false` will not.
*/
beforeClose?(): Promise<boolean> | boolean;
afterClose?(): void;
FileTransferRequestsComponent?: JSX.Element;
}
/**
* Both `getDownloader` and `getUploader` can return a promise containing `FileTransferListeners` function or nothing.
* In the latter case, the file will not be added to the list and the download will not start.
*/
export interface TransferHandlers {
getDownloader: (
sourcePath: string,
abortController: AbortController
) => Promise<FileTransferListeners | undefined>;
getUploader: (
destinationPath: string,
file: File,
abortController: AbortController
) => Promise<FileTransferListeners | undefined>;
}
export function FileTransfer(props: FileTransferProps) {
const { openedDialog, closeDialog } = useFileTransferContext();
async function handleCloseDialog(
isAnyTransferInProgress: boolean
): Promise<void> {
const runCloseCallbacks = () => {
closeDialog();
props.afterClose?.();
};
if (!isAnyTransferInProgress || !props.beforeClose) {
runCloseCallbacks();
return;
}
if (await props.beforeClose()) {
runCloseCallbacks();
}
}
return (
<FileTransferContainer>
{props.FileTransferRequestsComponent}
{openedDialog && (
<FileTransferDialog
errorText={props.errorText}
openedDialog={openedDialog}
transferHandlers={props.transferHandlers}
onCloseDialog={handleCloseDialog}
/>
)}
</FileTransferContainer>
);
}
export function FileTransferDialog(
props: Pick<FileTransferProps, 'transferHandlers' | 'errorText'> & {
openedDialog: FileTransferDialogDirection;
onCloseDialog(isAnyTransferInProgress: boolean): void;
}
) {
const { filesStore } = useFileTransferContext();
function handleAddDownload(sourcePath: string): void {
filesStore.start({
name: sourcePath,
runFileTransfer: abortController =>
props.transferHandlers.getDownloader(sourcePath, abortController),
});
}
function handleAddUpload(destinationPath: string, file: File): void {
filesStore.start({
name: file.name,
runFileTransfer: abortController =>
props.transferHandlers.getUploader(
destinationPath,
file,
abortController
),
});
}
function handleClose(): void {
props.onCloseDialog(filesStore.isAnyTransferInProgress());
}
return (
<FileTransferStateless
errorText={props.errorText}
openedDialog={props.openedDialog}
files={filesStore.files}
onCancel={filesStore.cancel}
onClose={handleClose}
onAddUpload={handleAddUpload}
onAddDownload={handleAddDownload}
/>
);
}