Repository: h2oai/wave
File: ui/src/file_upload.tsx (TypeScript)

Code before:

        try {
          const { responseText } = await new Promise<XMLHttpRequest>((resolve, reject) => {
            xhrRef.current = new XMLHttpRequest()
            const xhr = xhrRef.current
            xhr.open("POST", wave.uploadURL)
            xhr.upload.onprogress = e => setPercentComplete(e.loaded / e.total)
            xhr.send(formData)
            xhr.onreadystatechange = () => {
              if (xhr.readyState !== XMLHttpRequest.DONE) return
              xhr.status >= 200 && xhr.status < 300 ? resolve(xhr) : reject(xhr)
            }
            xhr.onabort = () => isAborted.current = true
          })
          const { files } = JSON.parse(responseText)
          wave.args[name] = files

          if (!compact) wave.push()
          setSuccessMsg(`Successfully uploaded files: ${files.map(({ name }: File) => name).join(',')}.`)
        }
        catch ({ responseText }) {
          if (isAborted.current) {
            isAborted.current = false
            return
          }
          setError(responseText as S || 'There was an error when uploading file.')
        }
        finally {
          setFiles([])
          setFileNames('')
        }
      },
      isFileTypeAllowed = (fileName: S) => !fileExtensions || fileExtensions.some(ext => fileName.toLowerCase().endsWith(ext.toLowerCase())),
      validateFiles = (fileArr: File[]) => {
        if (!multiple && fileArr.length > 1) return 'Cannot upload multiple files. Input is not set to multiple mode.'

        const notAllowedFiles = fileArr.filter(({ name }) => !isFileTypeAllowed(name))
        if (notAllowedFiles.length) {
          return `Not allowed extension for files: ${notAllowedFiles.map(({ name }) => name).join(', ')}.
          Allowed file extensions: ${fileExtensions?.join(', ')}.`
        }

        if (maxFileSizeBytes) {
          const maxSizePerFileExceededFiles = fileArr.filter(({ size }) => size > maxFileSizeBytes)
          if (maxSizePerFileExceededFiles.length) {
            return `Max file size exceeded for files: ${maxSizePerFileExceededFiles.map(({ name }) => name).join(', ')}.
            Allowed size per file: ${max_file_size}Mb.`
          }
        }

        if (maxSizeBytes) {

Review comment (suggestion):
Should be cleaner and type-safe.
```suggestion
        catch (err: unknown) {
          let errMsg = 'There was an error when uploading file.'
          if (err instanceof XMLHttpRequest) {
            if (err.status === 0) return // Aborted.
            errMsg = err.responseText
          }
          setError(errMsg)
```

Code after:
        try {
          const { responseText } = await new Promise<XMLHttpRequest>((resolve, reject) => {
            const xhr = new XMLHttpRequest()
            rejectXhr.current = () => {
              reject()
              xhr.abort()
              setPercentComplete(0)
            }
            xhr.open("POST", wave.uploadURL)
            xhr.upload.onprogress = e => setPercentComplete(e.loaded / e.total)
            xhr.send(formData)
            xhr.onreadystatechange = () => {
              if (xhr.readyState !== XMLHttpRequest.DONE) return
              xhr.status >= 200 && xhr.status < 300 ? resolve(xhr) : reject(xhr)
            }
          })
          const { files } = JSON.parse(responseText)
          wave.args[name] = files

          if (!compact) wave.push()
          setSuccessMsg(`Successfully uploaded files: ${files.map(({ name }: File) => name).join(',')}.`)
        }
        catch (err: unknown) {
          if (err) setError(err instanceof XMLHttpRequest ? err.responseText : 'There was an error when uploading file.')
          setFileNames('')
        }
        finally {
          setFiles([])
        }
      },
      isFileTypeAllowed = (fileName: S) => !fileExtensions || fileExtensions.some(ext => fileName.toLowerCase().endsWith(ext.toLowerCase())),
      validateFiles = (fileArr: File[]) => {
        if (!multiple && fileArr.length > 1) return 'Cannot upload multiple files. Input is not set to multiple mode.'

        const notAllowedFiles = fileArr.filter(({ name }) => !isFileTypeAllowed(name))
        if (notAllowedFiles.length) {
          return `Not allowed extension for files: ${notAllowedFiles.map(({ name }) => name).join(', ')}.
          Allowed file extensions: ${fileExtensions?.join(', ')}.`
        }

        if (maxFileSizeBytes) {
          const maxSizePerFileExceededFiles = fileArr.filter(({ size }) => size > maxFileSizeBytes)
          if (maxSizePerFileExceededFiles.length) {
            return `Max file size exceeded for files: ${maxSizePerFileExceededFiles.map(({ name }) => name).join(', ')}.
            Allowed size per file: ${max_file_size}Mb.`
          }
        }

        if (maxSizeBytes) {
          const totalSize = fileArr.reduce((total, { size }) => total + size, 0)
          if (totalSize > maxSizeBytes) {