Skip to content

Conversation

@barbaraperic
Copy link
Collaborator

This commit introduces a new UploadButton component that allows users to upload files with error handling for file size limits. The component utilizes Radix UI for form control and includes a button with a loading state during uploads. It also provides user feedback for file size errors.

CleanShot.2025-11-05.at.11.46.18.mp4

@vercel
Copy link

vercel bot commented Nov 5, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
filecoin-pin-website Ready Ready Preview Comment Nov 7, 2025 11:11am

@FilOzzy FilOzzy added team/filecoin-pin "Filecoin Pin" project is a stakeholder for this work. team/fs-wg FOC working group is a stakeholder for this work, and thus wants to track it on their project board. labels Nov 5, 2025
@FilOzzy FilOzzy added this to FS Nov 5, 2025
@github-project-automation github-project-automation bot moved this to 📌 Triage in FS Nov 5, 2025
@barbaraperic barbaraperic changed the title Add UploadButton component for file uploads [UXIT-3464] Add compact Upload component Nov 5, 2025
@github-actions
Copy link

github-actions bot commented Nov 5, 2025

Filecoin Pin Upload

IPFS Artifacts:

Onchain verification:

Payment:

  • Current Filecoin Pay balance: 48.5533 USDFC
  • Amount deposited to Filecoin Pay by this workflow: 0.0000 USDFC
  • Data Set Storage runway: 2 year(s) 9 month(s) 25 day(s)

More details

@barbaraperic barbaraperic marked this pull request as ready for review November 5, 2025 14:35
Copy link
Collaborator

@CharlyMartin CharlyMartin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks great @barbaraperic 👏

My only comment would be: centralize the logic file state management in a hook, if possible.

Currently, DragNDrop and UploadButton each use their own state and functions to manage files, but the logic is similar.

I would create a useFileState hook (name TBD) to abstract all of it, such as below, and call it in each file picker component.

import { useRef, useState } from 'react'
import { formatFileSize } from '@/utils/format-file-size'

type UseFileStateProps = {
  maxSize: number
  onUpload: (file: File) => void
}

export function useFileState({ maxSize, onUpload }: UseFileStateProps) {
  const fileInputRef = useRef<HTMLInputElement>(null)
  const [error, setError] = useState<string | null>(null)

  function handleButtonClick() {
    fileInputRef.current?.click()
  }

  function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0]

    if (file) {
      if (file.size > maxSize) {
        setError(`File is too large. Maximum size is ${formatFileSize(maxSize)}.`)
        if (fileInputRef.current) fileInputRef.current.value = ''
        return
      }

      setError(null)
      onUpload(file)
      if (fileInputRef.current) fileInputRef.current.value = ''
    }
  }

  function clearFile() {
    if (fileInputRef.current) fileInputRef.current.value = ''
  }

  return {
    error,
    fileInputRef,
    handleButtonClick,
    handleFileChange,
    clearFile,
  }
}

(I haven't tested this)

@github-project-automation github-project-automation bot moved this from 📌 Triage to ✔️ Approved by reviewer in FS Nov 6, 2025
Comment on lines +47 to +53
<input
accept={accept.join(',')}
className="hidden"
multiple={false}
onChange={handleFileChange}
ref={fileInputRef}
type="file"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe if you pass size here, the user will only be able to pick files with an appropriate size, hence rendering the use setError(File is too large. Maximum size is ${formatFileSize(maxSize)}.) unnecessary.

To be tested, maybe both are needed!

Suggested change
<input
accept={accept.join(',')}
className="hidden"
multiple={false}
onChange={handleFileChange}
ref={fileInputRef}
type="file"
<input
accept={accept.join(',')}
className="hidden"
multiple={false}
onChange={handleFileChange}
ref={fileInputRef}
type="file"
size={maxSize}

https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/file#size

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like that section is under “Getting information on selected files,” which is all about the File API, not HTML attribute? 👀

Copy link
Collaborator

@SgtPooki SgtPooki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not up to date on the mocks. are we supposed to remove the drag-n-drop functionality?

This commit introduces a new UploadButton component that allows users to upload files with error handling for file size limits. The component utilizes Radix UI for form control and includes a button with a loading state during uploads. It also provides user feedback for file size errors.
…and UploadButton components

This commit updates the DragNDrop and UploadButton components to use a centralized MAX_FILE_SIZE constant instead of hardcoded values for maximum file size. Additionally, it improves the structure of the UploadButton component by adjusting the layout and error message handling for better user experience.
@barbaraperic
Copy link
Collaborator Author

It looks great @barbaraperic 👏

My only comment would be: centralize the logic file state management in a hook, if possible.

Currently, DragNDrop and UploadButton each use their own state and functions to manage files, but the logic is similar.

I would create a useFileState hook (name TBD) to abstract all of it, such as below, and call it in each file picker component.

import { useRef, useState } from 'react'
import { formatFileSize } from '@/utils/format-file-size'

type UseFileStateProps = {
  maxSize: number
  onUpload: (file: File) => void
}

export function useFileState({ maxSize, onUpload }: UseFileStateProps) {
  const fileInputRef = useRef<HTMLInputElement>(null)
  const [error, setError] = useState<string | null>(null)

  function handleButtonClick() {
    fileInputRef.current?.click()
  }

  function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0]

    if (file) {
      if (file.size > maxSize) {
        setError(`File is too large. Maximum size is ${formatFileSize(maxSize)}.`)
        if (fileInputRef.current) fileInputRef.current.value = ''
        return
      }

      setError(null)
      onUpload(file)
      if (fileInputRef.current) fileInputRef.current.value = ''
    }
  }

  function clearFile() {
    if (fileInputRef.current) fileInputRef.current.value = ''
  }

  return {
    error,
    fileInputRef,
    handleButtonClick,
    handleFileChange,
    clearFile,
  }
}

(I haven't tested this)

Hm I honestly feel like they are too different for a shared hook...

State Management

  • DragNDrop: Manages file state internally, has a two-step flow (select and upload)
  • UploadButton: No file state, immediate upload on selection

User Flow

  • DragNDrop: Select file - Review - Click upload button
  • UploadButton: Click button - Select file - Immediate upload

Validation

  • DragNDrop: Validation happens in FilePicker component
  • UploadButton: Validation happens inline in handleFileChange

UI

  • DragNDrop: Shows selected file, has cancel/upload buttons
  • UploadButton: Only shows error state

@barbaraperic barbaraperic merged commit 15af919 into main Nov 10, 2025
4 checks passed
@github-project-automation github-project-automation bot moved this from ✔️ Approved by reviewer to 🎉 Done in FS Nov 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team/filecoin-pin "Filecoin Pin" project is a stakeholder for this work. team/fs-wg FOC working group is a stakeholder for this work, and thus wants to track it on their project board.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants