diff --git a/packages/react/src/Uploader.tsx b/packages/react/src/Uploader.tsx index 010ea63d..744ffde9 100644 --- a/packages/react/src/Uploader.tsx +++ b/packages/react/src/Uploader.tsx @@ -1,5 +1,5 @@ import type { As, Component, Props, Options, HTMLProps } from 'ariakit-react-utils' -import type { ChangeEvent } from 'react' +import type { ChangeEvent, FormEventHandler } from 'react' import type { AnyLink, CARMetadata, ProgressStatus } from '@w3ui/core' import React, { @@ -43,7 +43,7 @@ export interface UploaderContextState { * A callback that can be passed to an `onSubmit` handler to * upload `file` to web3.storage via the w3up API. */ - handleUploadSubmit?: (e: Event) => Promise + handleUploadSubmit?: FormEventHandler /** * The CID of a successful upload */ @@ -158,7 +158,7 @@ export const UploaderRoot: Component = createComponent( const [uploadAsCAR, setUploadAsCAR] = useState(defaultUploadAsCAR) const [dataCID, setDataCID] = useState() const [status, setStatus] = useState(UploadStatus.Idle) - const [error, setError] = useState() + const [error, setError] = useState() const [storedDAGShards, setStoredDAGShards] = useState([]) const [uploadProgress, setUploadProgress] = useState({}) @@ -167,42 +167,55 @@ export const UploaderRoot: Component = createComponent( setStatus(UploadStatus.Idle) } - const handleUploadSubmit = async (e: Event): Promise => { + const handleUploadSubmit: FormEventHandler = (e) => { e.preventDefault() - // file !== undefined should be unecessary but is here to make tsc happy - if ((client !== undefined) && (files !== undefined) && (file !== undefined)) { - try { - setError(undefined) - setStatus(UploadStatus.Uploading) - const storedShards: CARMetadata[] = [] - setStoredDAGShards(storedShards) - const uploadOptions = { - onShardStored (meta: CARMetadata) { - storedShards.push(meta) - setStoredDAGShards([...storedShards]) - }, - onUploadProgress (status: ProgressStatus) { - setUploadProgress(statuses => ({ ...statuses, [status.url ?? '']: status })) - } - } - const cid = files.length > 1 - ? await client.uploadDirectory(files, uploadOptions) - : (uploadAsCAR - ? await client.uploadCAR(file, uploadOptions) - : (wrapInDirectory - ? await client.uploadDirectory(files, uploadOptions) - : await client.uploadFile(file, uploadOptions))) + if ((client === undefined)) { + // eslint-disable-next-line no-console + console.error('No client available for upload. Ignoring upload attempt.') + return + } + + // The application should only attempt to submit once files are selected. + if ((files === undefined) || (file === undefined)) { + // eslint-disable-next-line no-console + console.error('No no files given to upload. Ignoring upload attempt.') + return + } - setDataCID(cid) - setStatus(UploadStatus.Succeeded) - if (onUploadComplete !== undefined) { - onUploadComplete({ file, files, dataCID: cid }) + const doUpload = async (): Promise => { + setError(undefined) + setStatus(UploadStatus.Uploading) + const storedShards: CARMetadata[] = [] + setStoredDAGShards(storedShards) + const uploadOptions = { + onShardStored (meta: CARMetadata) { + storedShards.push(meta) + setStoredDAGShards([...storedShards]) + }, + onUploadProgress (status: ProgressStatus) { + setUploadProgress(statuses => ({ ...statuses, [status.url ?? '']: status })) } - } catch (error_: any) { - setError(error_) - setStatus(UploadStatus.Failed) + } + const cid = files.length > 1 + ? await client.uploadDirectory(files, uploadOptions) + : (uploadAsCAR + ? await client.uploadCAR(file, uploadOptions) + : (wrapInDirectory + ? await client.uploadDirectory(files, uploadOptions) + : await client.uploadFile(file, uploadOptions))) + + setDataCID(cid) + setStatus(UploadStatus.Succeeded) + if (onUploadComplete !== undefined) { + onUploadComplete({ file, files, dataCID: cid }) } } + + doUpload().catch((error_: unknown) => { + const error = (error_ instanceof Error) ? error_ : new Error(String(error_)) + setError(error) + setStatus(UploadStatus.Failed) + }) } const uploaderContextValue = @@ -307,7 +320,7 @@ export type UploaderFormProps = Props = createComponent((props) => { const [{ handleUploadSubmit }] = useContext(UploaderContext) - return createElement('form', { ...props, onSubmit: handleUploadSubmit }) + return createElement('form', { ...props, onSubmit: handleUploadSubmit satisfies React.ComponentProps<'form'>['onSubmit'] }) }) /**