import React, { useCallback } from 'react'; import { translate } from '@aws-amplify/ui'; import { humanFileSize } from '@aws-amplify/ui'; import { FileState, TrackerProps } from '../types'; import { View, Image, Text, Button, TextField, Loader, ComponentClassNames, VisuallyHidden, } from '../../../../primitives'; import { IconClose, IconEdit, IconFile, } from '../../../../primitives/Icon/internal'; import { UploadMessage } from './UploadMessage'; export function UploadTracker({ errorMessage, file, fileState, hasImage, name, onCancel, onCancelEdit, onPause, onResume, onSaveEdit, onStartEdit, percentage, isResumable, showImage, }: TrackerProps): JSX.Element | null { const [tempName, setTempName] = React.useState(name); const inputRef = React.useRef<HTMLInputElement>(null); const url = URL.createObjectURL(file); // Focus the input after pressing the edit button React.useEffect(() => { if (fileState === FileState.EDITING && inputRef.current) { inputRef.current.focus(); } }, [fileState]); const { size } = file; const icon = hasImage ? <Image alt={file.name} src={url} /> : <IconFile />; const isDeterminate = isResumable ? percentage > 0 : true; const showEditButton = fileState === FileState.INIT || (fileState === FileState.ERROR && errorMessage === translate('Extension not allowed')); const DisplayView = useCallback( () => ( <> <View className={ComponentClassNames.FileUploaderFileMain}> <Text className={ComponentClassNames.FileUploaderFileName}> {name} </Text> </View> {showEditButton ? ( <Button onClick={onStartEdit} size="small" variation="link"> <VisuallyHidden>Edit file name {file.name}</VisuallyHidden> <IconEdit aria-hidden fontSize="medium" /> </Button> ) : null} <Text as="span" className={ComponentClassNames.FileUploaderFileSize}> {humanFileSize(size, true)} </Text> </> ), [file.name, name, onStartEdit, showEditButton, size] ); const Actions = useCallback(() => { switch (fileState) { case FileState.EDITING: return ( <> <Button size="small" onClick={() => { setTempName(name); onCancelEdit?.(); }} > Cancel </Button> <Button size="small" variation="primary" onClick={() => { onSaveEdit(tempName); }} > Save </Button> </> ); case FileState.RESUME: case FileState.LOADING: if (!isResumable) return null; return ( <Button onClick={onPause} size="small" variation="link"> {translate('pause')} </Button> ); case FileState.PAUSED: return ( <Button onClick={onResume} size="small" variation="link"> {translate('Resume')} </Button> ); case FileState.SUCCESS: return null; default: return ( <Button size="small" onClick={onCancel}> <VisuallyHidden>Remove file name {file.name}</VisuallyHidden> <IconClose aria-hidden fontSize="medium" /> </Button> ); } }, [ file.name, fileState, isResumable, name, onCancel, onCancelEdit, onPause, onResume, onSaveEdit, tempName, ]); if (!file) return null; return ( <View className={ComponentClassNames.FileUploaderFile}> <View className={ComponentClassNames.FileUploaderFileWrapper}> {showImage ? ( <View className={ComponentClassNames.FileUploaderFileImage}> {icon} </View> ) : null} {/* Main View */} {fileState === FileState.EDITING ? ( // Wrapping this text field in a form with onSubmit will allow keyboard // users to press enter to save changes. <View as="form" flex="1" onSubmit={() => { onSaveEdit(tempName); }} > <TextField maxLength={1024} ref={inputRef} label="file name" size="small" variation="quiet" labelHidden onChange={(e: React.ChangeEvent<HTMLInputElement>) => { setTempName(e.target.value); }} value={tempName} /> </View> ) : ( <DisplayView /> )} <Actions /> {fileState === FileState.LOADING ? ( <Loader className={ComponentClassNames.FileUploaderLoader} variation="linear" percentage={percentage} isDeterminate={isDeterminate} isPercentageTextHidden /> ) : null} </View> <UploadMessage fileState={fileState} errorMessage={errorMessage} percentage={percentage} /> </View> ); }