Skip to content

Commit

Permalink
Merge pull request #1492 from nickgros/SWC-4693
Browse files Browse the repository at this point in the history
  • Loading branch information
nickgros authored Jan 9, 2025
2 parents 4431e59 + b59aec3 commit b3e26b5
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,24 +67,27 @@ const mockUseUploadFileEntitiesReturn = {
activeUploadCount: 0,
uploadProgress: [],
initiateUpload: jest.fn(),
isUploadReady: true,
} satisfies UseUploadFileEntitiesReturn

describe('EntityUpload', () => {
function renderComponent(propOverrides: Partial<EntityUploadProps> = {}) {
const user = userEvent.setup()
const ref = createRef<EntityUploadHandle>()
const mockOnUploadReady = jest.fn()
const result = render(
<EntityUpload
ref={ref}
entityId={mockProject.entity.id}
onUploadReady={mockOnUploadReady}
{...propOverrides}
/>,
{
wrapper: createWrapper(),
},
)

return { user, ref, result }
return { user, ref, result, mockOnUploadReady }
}

beforeEach(() => {
Expand Down Expand Up @@ -330,8 +333,10 @@ describe('EntityUpload', () => {
})
})

it('supports upload via programmatic handle', () => {
const { ref } = renderComponent()
it('supports upload via programmatic handle', async () => {
const { ref, mockOnUploadReady } = renderComponent()

await waitFor(() => expect(mockOnUploadReady).toHaveBeenCalled())

const filesToUpload = [
new File(['contents'], 'file1.txt'),
Expand All @@ -350,6 +355,19 @@ describe('EntityUpload', () => {
)
})

it('does not call onUploadReady if not ready for upload', async () => {
const hookReturnValue = {
...mockUseUploadFileEntitiesReturn,
isUploadReady: false,
} satisfies UseUploadFileEntitiesReturn

mockUseUploadFileEntities.mockReturnValue(hookReturnValue)
const { ref, mockOnUploadReady } = renderComponent()

await waitFor(() => expect(ref.current).toBeDefined())
expect(mockOnUploadReady).not.toHaveBeenCalled()
})

it('passes didUploadsExceedStorageLimit = true to ProjectStorageLimitAlert when onStorageLimitExceeded is invoked', async () => {
renderComponent()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import {
import { noop } from 'lodash-es'
import pluralize from 'pluralize'
import {
ForwardedRef,
forwardRef,
MouseEvent,
useEffect,
useImperativeHandle,
useRef,
useState,
MouseEvent,
forwardRef,
ForwardedRef,
} from 'react'
import { FixedSizeList } from 'react-window'
import { SYNAPSE_STORAGE_LOCATION_ID } from '../../synapse-client/index'
Expand Down Expand Up @@ -48,6 +48,8 @@ export type EntityUploadProps = {
entityId: string
/** Callback that is invoked when the state of the uploader changes */
onStateChange?: (state: UploaderState) => void
/** Callback that is invoked when component is ready to upload */
onUploadReady?: () => void
}

// This padding value will be used to manipulate the appearance of a virtualized list of FileUploadProgress components
Expand All @@ -67,7 +69,7 @@ export const EntityUpload = forwardRef(function EntityUpload(
props: EntityUploadProps,
ref: ForwardedRef<EntityUploadHandle>,
) {
const { entityId, onStateChange = noop } = props
const { entityId, onStateChange = noop, onUploadReady = noop } = props

const { data: entity, isLoading: isLoadingEntity } = useGetEntity(entityId)

Expand All @@ -91,6 +93,7 @@ export const EntityUpload = forwardRef(function EntityUpload(
activePrompts,
activeUploadCount,
isPrecheckingUpload,
isUploadReady,
} = useUploadFileEntities(entityId, accessKey, secretKey, () =>
setDidUploadsExceedStorageLimit(true),
)
Expand All @@ -99,6 +102,12 @@ export const EntityUpload = forwardRef(function EntityUpload(
onStateChange(state)
}, [state, onStateChange])

useEffect(() => {
if (isUploadReady) {
onUploadReady()
}
}, [isUploadReady, onUploadReady])

const fileInputRef = useRef<HTMLInputElement>(null)
const folderInputRef = useRef<HTMLInputElement>(null)
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import {
ForwardedRef,
forwardRef,
KeyboardEvent,
useImperativeHandle,
useRef,
useState,
} from 'react'
import { UploaderState } from '../../utils/hooks/useUploadFileEntity/useUploadFileEntities'
import { DialogBase } from '../DialogBase'
import { displayToast } from '../ToastMessage/ToastMessage'
import { EntityUpload, EntityUploadHandle } from './EntityUpload'
import {
EntityUpload,
EntityUploadHandle,
EntityUploadProps,
} from './EntityUpload'
import { LinkToURL, LinkToURLHandle } from './LinkToURL'

export type EntityUploadModalProps = {
entityId: string
open: boolean
onClose: () => void
}
} & Pick<EntityUploadProps, 'onUploadReady'>

enum UploadTab {
UploadFile,
Expand All @@ -27,14 +32,16 @@ export const EntityUploadModal = forwardRef(function EntityUploadModal(
props: EntityUploadModalProps,
ref: ForwardedRef<EntityUploadHandle>,
) {
const { entityId, open, onClose } = props
const { entityId, open, onClose, onUploadReady } = props
const [tabValue, setTabValue] = useState<UploadTab>(UploadTab.UploadFile)

const [uploadState, setUploadState] = useState<UploaderState>('LOADING')

const [isLinkFormValid, setIsLinkFormValid] = useState(false)
const linkToUrlFormRef = useRef<LinkToURLHandle>(null)

const entityUploadRef = useRef<EntityUploadHandle>(null)

const disableClose =
uploadState === 'PROMPT_USER' || uploadState === 'UPLOADING'
const disableCancel = disableClose || uploadState === 'COMPLETE'
Expand All @@ -54,6 +61,14 @@ export const EntityUploadModal = forwardRef(function EntityUploadModal(
}
}

useImperativeHandle(ref, () => ({
handleUploads: (fileList: ArrayLike<File>) => {
// When an upload starts via imperative handle (e.g. via drag-and-drop), switch to the upload tab
setTabValue(UploadTab.UploadFile)
entityUploadRef.current?.handleUploads(fileList)
},
}))

return (
<DialogBase
DialogProps={{
Expand All @@ -62,6 +77,8 @@ export const EntityUploadModal = forwardRef(function EntityUploadModal(
onFinish()
}
},
// SWC-4693 - For drag-and-drop upload to be ready before the modal opens, the modal contents must be mounted
keepMounted: true,
}}
title={'Upload or Link to File'}
open={open}
Expand All @@ -81,9 +98,10 @@ export const EntityUploadModal = forwardRef(function EntityUploadModal(
</Tabs>
<Box display={tabValue === UploadTab.UploadFile ? 'block' : 'none'}>
<EntityUpload
ref={ref}
ref={entityUploadRef}
entityId={entityId}
onStateChange={setUploadState}
onUploadReady={onUploadReady}
/>
</Box>
<Box display={tabValue === UploadTab.LinkToURL ? 'block' : 'none'}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ export type UseUploadFileEntitiesReturn = {
initiateUpload: (args: InitiateUploadArgs) => void
activeUploadCount: number
uploadProgress: FileUploadProgress[]
/** True when files can be uploaded. */
isUploadReady: boolean
}

// Limit the number of concurrent uploads to avoid overwhelming the browser
Expand Down Expand Up @@ -141,6 +143,8 @@ export function useUploadFileEntities(
staleTime: Infinity,
})

const isUploadReady = Boolean(containerOrEntityId && uploadDestination)

const storageLocationId =
uploadDestination?.storageLocationId || SYNAPSE_STORAGE_LOCATION_ID

Expand Down Expand Up @@ -358,10 +362,16 @@ export function useUploadFileEntities(

const initiateUpload = useCallback(
(args: InitiateUploadArgs) => {
if (uploadDestination == null) {
console.error(
'Upload destination was not loaded, or failed to load! Aborting upload.',
)
return
}
if (
willUploadsExceedStorageLimit(
args.map(arg => arg.file),
uploadDestination!.projectStorageLocationUsage,
uploadDestination.projectStorageLocationUsage,
bytesPendingUpload,
)
) {
Expand Down Expand Up @@ -466,6 +476,7 @@ export function useUploadFileEntities(
initiateUpload: initiateUpload,
activePrompts: activePrompts,
uploadProgress: uploadProgress,
isUploadReady,
}
}, [
state,
Expand All @@ -475,5 +486,6 @@ export function useUploadFileEntities(
initiateUpload,
activePrompts,
uploadProgress,
isUploadReady,
])
}

0 comments on commit b3e26b5

Please sign in to comment.