Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/components/content-tab-import-export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useI18n } from '@wordpress/react-i18n';
import { useEffect, useRef, useState } from 'react';
import Button from 'src/components/button';
import { ClearAction } from 'src/components/clear-action';
import { ErrorIcon } from 'src/components/error-icon';
import ProgressBar from 'src/components/progress-bar';
import { Tooltip } from 'src/components/tooltip';
import { ACCEPTED_IMPORT_FILE_TYPES } from 'src/constants';
Expand Down Expand Up @@ -164,6 +165,14 @@ const InitialImportButton = ( {
);
};

const isValidImportFile = ( file: File ): boolean => {
const fileName = file.name.toLowerCase();
return (
ACCEPTED_IMPORT_FILE_TYPES.some( ( ext ) => fileName.endsWith( ext ) ) ||
fileName.endsWith( '.sql' )
);
};

const ImportSite = ( {
selectedSite,
isThisSiteSyncing,
Expand All @@ -178,6 +187,7 @@ const ImportSite = ( {
const { [ selectedSite.id ]: currentProgress } = importState;
const isSiteExporting =
exportState[ selectedSite?.id ] && exportState[ selectedSite?.id ].progress < 100;
const [ fileError, setFileError ] = useState< string | null >( null );

const importConfirmation = useConfirmationDialog( {
message: sprintf( __( 'Overwrite %s?' ), selectedSite.name ),
Expand All @@ -192,9 +202,25 @@ const ImportSite = ( {
if ( isImporting ) {
return;
}
if ( ! isValidImportFile( file ) ) {
setFileError(
__(
'This file type is not supported. Please use a .zip, .gz, .tar, .tar.gz, .wpress, or .sql file.'
)
);
return;
}
setFileError( null );
void importConfirmation( () => importFile( file, selectedSite ) );
},
} );

useEffect( () => {
if ( isDraggingOver && fileError ) {
setFileError( null );
}
}, [ isDraggingOver, fileError ] );

const inputFileRef = useRef< HTMLInputElement >( null );
const openFileSelector = async () => {
inputFileRef.current?.click();
Expand Down Expand Up @@ -304,6 +330,12 @@ const ImportSite = ( {
) }
</div>
</InitialImportButton>
{ fileError && (
<div className="flex items-start gap-1 text-xs text-red-500 mt-2">
<ErrorIcon className="shrink-0 mt-px fill-current" />
<span className="text-left">{ fileError }</span>
</div>
) }
</div>
<input
ref={ inputFileRef }
Expand Down
37 changes: 29 additions & 8 deletions src/modules/add-site/components/import-backup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { download } from '@wordpress/icons';
import { useI18n } from '@wordpress/react-i18n';
import { useCallback, useRef, useState } from 'react';
import Button from 'src/components/button';
import { ErrorIcon } from 'src/components/error-icon';
import { ACCEPTED_IMPORT_FILE_TYPES } from 'src/constants';
import { cx } from 'src/lib/cx';
import { getIpcApi } from 'src/lib/get-ipc-api';
Expand Down Expand Up @@ -46,6 +47,10 @@ interface ImportBackupProps {
selectedFile?: File | null;
}

const isValidBackupFile = ( file: File ): boolean => {
return ACCEPTED_IMPORT_FILE_TYPES.some( ( ext ) => file.name.toLowerCase().endsWith( ext ) );
};

export default function ImportBackup( {
onFileSelect,
selectedFile: initialFile,
Expand All @@ -54,11 +59,13 @@ export default function ImportBackup( {
const locale = useI18nLocale();
const [ isDragging, setIsDragging ] = useState( false );
const [ selectedFile, setSelectedFile ] = useState< File | null >( initialFile || null );
const [ fileError, setFileError ] = useState< string | null >( null );
const fileInputRef = useRef< HTMLInputElement >( null );

const handleFileSelection = useCallback(
( file: File ) => {
setSelectedFile( file );
setFileError( null );
onFileSelect( file );
},
[ onFileSelect ]
Expand All @@ -68,6 +75,7 @@ export default function ImportBackup( {
e.preventDefault();
e.stopPropagation();
setIsDragging( true );
setFileError( null );
}, [] );

const handleDragLeave = useCallback( ( e: React.DragEvent ) => {
Expand All @@ -83,17 +91,22 @@ export default function ImportBackup( {
setIsDragging( false );

const files = Array.from( e.dataTransfer.files );
const backupFile = files.find( ( file ) => {
return ACCEPTED_IMPORT_FILE_TYPES.some( ( ext ) =>
file.name.toLowerCase().endsWith( ext )
);
} );
if ( files.length === 0 ) {
return;
}

if ( backupFile ) {
handleFileSelection( backupFile );
const file = files[ 0 ];
if ( isValidBackupFile( file ) ) {
handleFileSelection( file );
} else {
setFileError(
__(
'This file type is not supported. Please use a .zip, .gz, .tar, .tar.gz, or .wpress file.'
)
);
}
},
[ handleFileSelection ]
[ handleFileSelection, __ ]
);

const handleFileInputChange = useCallback(
Expand All @@ -114,6 +127,7 @@ export default function ImportBackup( {
( e: React.MouseEvent ) => {
e.stopPropagation();
setSelectedFile( null );
setFileError( null );
onFileSelect();
if ( fileInputRef.current ) {
fileInputRef.current.value = '';
Expand Down Expand Up @@ -210,6 +224,13 @@ export default function ImportBackup( {
className="hidden"
aria-label={ __( 'Select backup file' ) }
/>

{ fileError && (
<div className="flex items-start justify-center gap-1 text-xs text-red-500 mt-2 max-w-[400px] mx-auto">
<ErrorIcon className="shrink-0 mt-px fill-current" />
<span className="text-left">{ fileError }</span>
</div>
) }
</VStack>
);
}