Compare commits

...
Author SHA1 Message Date
Peter Steinberger afa9a6db13 fix: preserve folder upload mode across refresh (#551) (thanks @MunemHashmi) 2026-03-07 18:32:58 +00:00
Munem Hashmi f5944c529e fix(ui): persist folder upload input across hydration and re-renders (#58)
Replace the useEffect + useRef approach for setting webkitdirectory/
directory attributes with a ref callback that sets the attributes
every time the input element is mounted. This ensures folder selection
mode persists after page refresh, where React hydration could strip
the non-standard attributes.

Also removes the @ts-expect-error JSX props since the attributes are
now set imperatively via the ref callback.
2026-03-07 18:31:42 +00:00
2 changed files with 11 additions and 9 deletions
+1
View File
@@ -28,6 +28,7 @@
- Search/listing performance: cut embedding hydration and badge read bandwidth via `embeddingSkillMap` + denormalized skill badges; shift stat-doc sync to low-frequency cron (#441) (thanks @sethconvex).
### Fixed
- Upload: keep folder-picking enabled after page refresh by reapplying `webkitdirectory`/`directory` on the file input ref (#551) (thanks @MunemHashmi).
- Skills hard-delete: delete `commentReports` rows during moderation cleanup to avoid orphaned report records.
- Comments: hide entries authored by deleted/deactivated users in `comments:listBySkill`.
- Admin API: `POST /api/v1/users/reclaim` now performs non-destructive root-slug owner transfer
+10 -9
View File
@@ -77,6 +77,13 @@ export function Upload() {
const [error, setError] = useState<string | null>(null)
const [isDragging, setIsDragging] = useState(false)
const fileInputRef = useRef<HTMLInputElement | null>(null)
const setFileInputRef = (node: HTMLInputElement | null) => {
fileInputRef.current = node
if (node) {
node.setAttribute('webkitdirectory', '')
node.setAttribute('directory', '')
}
}
const validationRef = useRef<HTMLDivElement | null>(null)
const navigate = useNavigate()
const maxBytes = 50 * 1024 * 1024
@@ -272,11 +279,8 @@ export function Upload() {
slugCollision,
])
useEffect(() => {
if (!fileInputRef.current) return
fileInputRef.current.setAttribute('webkitdirectory', '')
fileInputRef.current.setAttribute('directory', '')
}, [])
// webkitdirectory/directory attributes are set via the ref callback (setFileInputRef)
// to ensure they persist across hydration and re-renders (#58)
if (!isAuthenticated) {
return (
@@ -448,15 +452,12 @@ export function Upload() {
}}
>
<input
ref={fileInputRef}
ref={setFileInputRef}
className="upload-file-input"
id="upload-files"
data-testid="upload-input"
type="file"
multiple
// @ts-expect-error - non-standard attribute to allow folder selection
webkitdirectory=""
directory=""
onChange={(event) => {
const picked = Array.from(event.target.files ?? [])
void applyExpandedFiles(picked)