1
1
import type { As , Component , Props , Options , HTMLProps } from 'ariakit-react-utils'
2
- import type { ChangeEvent } from 'react'
2
+ import type { ChangeEvent , FormEventHandler } from 'react'
3
3
import type { AnyLink , CARMetadata , ProgressStatus } from '@w3ui/core'
4
4
5
5
import React , {
@@ -43,7 +43,7 @@ export interface UploaderContextState {
43
43
* A callback that can be passed to an `onSubmit` handler to
44
44
* upload `file` to web3.storage via the w3up API.
45
45
*/
46
- handleUploadSubmit ?: ( e : Event ) => Promise < void >
46
+ handleUploadSubmit ?: FormEventHandler < HTMLFormElement >
47
47
/**
48
48
* The CID of a successful upload
49
49
*/
@@ -158,7 +158,7 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent(
158
158
const [ uploadAsCAR , setUploadAsCAR ] = useState ( defaultUploadAsCAR )
159
159
const [ dataCID , setDataCID ] = useState < AnyLink > ( )
160
160
const [ status , setStatus ] = useState ( UploadStatus . Idle )
161
- const [ error , setError ] = useState ( )
161
+ const [ error , setError ] = useState < Error > ( )
162
162
const [ storedDAGShards , setStoredDAGShards ] = useState < UploaderContextState [ 'storedDAGShards' ] > ( [ ] )
163
163
const [ uploadProgress , setUploadProgress ] = useState < UploadProgress > ( { } )
164
164
@@ -167,42 +167,55 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent(
167
167
setStatus ( UploadStatus . Idle )
168
168
}
169
169
170
- const handleUploadSubmit = async ( e : Event ) : Promise < void > => {
170
+ const handleUploadSubmit : FormEventHandler < HTMLFormElement > = ( e ) => {
171
171
e . preventDefault ( )
172
- // file !== undefined should be unecessary but is here to make tsc happy
173
- if ( ( client !== undefined ) && ( files !== undefined ) && ( file !== undefined ) ) {
174
- try {
175
- setError ( undefined )
176
- setStatus ( UploadStatus . Uploading )
177
- const storedShards : CARMetadata [ ] = [ ]
178
- setStoredDAGShards ( storedShards )
179
- const uploadOptions = {
180
- onShardStored ( meta : CARMetadata ) {
181
- storedShards . push ( meta )
182
- setStoredDAGShards ( [ ...storedShards ] )
183
- } ,
184
- onUploadProgress ( status : ProgressStatus ) {
185
- setUploadProgress ( statuses => ( { ...statuses , [ status . url ?? '' ] : status } ) )
186
- }
187
- }
188
- const cid = files . length > 1
189
- ? await client . uploadDirectory ( files , uploadOptions )
190
- : ( uploadAsCAR
191
- ? await client . uploadCAR ( file , uploadOptions )
192
- : ( wrapInDirectory
193
- ? await client . uploadDirectory ( files , uploadOptions )
194
- : await client . uploadFile ( file , uploadOptions ) ) )
172
+ if ( ( client === undefined ) ) {
173
+ // eslint-disable-next-line no-console
174
+ console . error ( 'No client available for upload. Ignoring upload attempt.' )
175
+ return
176
+ }
177
+
178
+ // The application should only attempt to submit once files are selected.
179
+ if ( ( files === undefined ) || ( file === undefined ) ) {
180
+ // eslint-disable-next-line no-console
181
+ console . error ( 'No no files given to upload. Ignoring upload attempt.' )
182
+ return
183
+ }
195
184
196
- setDataCID ( cid )
197
- setStatus ( UploadStatus . Succeeded )
198
- if ( onUploadComplete !== undefined ) {
199
- onUploadComplete ( { file, files, dataCID : cid } )
185
+ const doUpload = async ( ) : Promise < void > => {
186
+ setError ( undefined )
187
+ setStatus ( UploadStatus . Uploading )
188
+ const storedShards : CARMetadata [ ] = [ ]
189
+ setStoredDAGShards ( storedShards )
190
+ const uploadOptions = {
191
+ onShardStored ( meta : CARMetadata ) {
192
+ storedShards . push ( meta )
193
+ setStoredDAGShards ( [ ...storedShards ] )
194
+ } ,
195
+ onUploadProgress ( status : ProgressStatus ) {
196
+ setUploadProgress ( statuses => ( { ...statuses , [ status . url ?? '' ] : status } ) )
200
197
}
201
- } catch ( error_ : any ) {
202
- setError ( error_ )
203
- setStatus ( UploadStatus . Failed )
198
+ }
199
+ const cid = files . length > 1
200
+ ? await client . uploadDirectory ( files , uploadOptions )
201
+ : ( uploadAsCAR
202
+ ? await client . uploadCAR ( file , uploadOptions )
203
+ : ( wrapInDirectory
204
+ ? await client . uploadDirectory ( files , uploadOptions )
205
+ : await client . uploadFile ( file , uploadOptions ) ) )
206
+
207
+ setDataCID ( cid )
208
+ setStatus ( UploadStatus . Succeeded )
209
+ if ( onUploadComplete !== undefined ) {
210
+ onUploadComplete ( { file, files, dataCID : cid } )
204
211
}
205
212
}
213
+
214
+ doUpload ( ) . catch ( ( error_ : unknown ) => {
215
+ const error = ( error_ instanceof Error ) ? error_ : new Error ( String ( error_ ) )
216
+ setError ( error )
217
+ setStatus ( UploadStatus . Failed )
218
+ } )
206
219
}
207
220
208
221
const uploaderContextValue =
@@ -307,7 +320,7 @@ export type UploaderFormProps<T extends As = 'form'> = Props<UploaderFormOptions
307
320
*/
308
321
export const UploaderForm : Component < UploaderFormProps > = createComponent ( ( props ) => {
309
322
const [ { handleUploadSubmit } ] = useContext ( UploaderContext )
310
- return createElement ( 'form' , { ...props , onSubmit : handleUploadSubmit } )
323
+ return createElement ( 'form' , { ...props , onSubmit : handleUploadSubmit satisfies React . ComponentProps < 'form' > [ 'onSubmit' ] } )
311
324
} )
312
325
313
326
/**
0 commit comments