feat: upload file dialog err handling & new flow

- add basic err handling to upload file dialog.
- rework the upload flow. now, on all successful uploads, the dialog
won't auto disappear. if some fails, the dialog will allow for retry.
This commit is contained in:
2025-10-12 23:48:21 +00:00
parent b17de812b9
commit 2ed8be94f1
5 changed files with 345 additions and 50 deletions

View File

@@ -1,19 +1,39 @@
import { atom } from "jotai"
import { atomFamily } from "jotai/utils"
type FileUpload = {
id: string
export enum FileUploadStatusKind {
InProgress = "InProgress",
Error = "Error",
Success = "Success",
}
export type FileUploadInProgress = {
kind: FileUploadStatusKind.InProgress
progress: number
}
export const fileUploadsAtom = atom<Record<string, FileUpload>>({})
export type FileUploadError = {
kind: FileUploadStatusKind.Error
error: unknown
}
export const fileUploadAtomFamily = atomFamily((id: string) =>
export type FileUploadSuccess = {
kind: FileUploadStatusKind.Success
}
export type FileUploadStatus =
| FileUploadInProgress
| FileUploadError
| FileUploadSuccess
export const fileUploadsAtom = atom<Record<string, FileUploadStatus>>({})
export const fileUploadStatusAtomFamily = atomFamily((id: string) =>
atom(
(get) => get(fileUploadsAtom)[id],
(get, set, progress: number) => {
(get, set, status: FileUploadStatus) => {
const fileUploads = { ...get(fileUploadsAtom) }
fileUploads[id] = { id, progress }
fileUploads[id] = status
set(fileUploadsAtom, fileUploads)
},
),
@@ -22,12 +42,56 @@ export const fileUploadAtomFamily = atomFamily((id: string) =>
export const clearFileUploadAtom = atom(null, (get, set, id: string) => {
const fileUploads = { ...get(fileUploadsAtom) }
delete fileUploads[id]
fileUploadAtomFamily.remove(id)
fileUploadStatusAtomFamily.remove(id)
set(fileUploadsAtom, fileUploads)
})
export const clearFileUploadStatusesAtom = atom(
null,
(get, set, ids: string[]) => {
const fileUploads = { ...get(fileUploadsAtom) }
for (const id of ids) {
if (fileUploads[id]) {
delete fileUploads[id]
}
fileUploadStatusAtomFamily.remove(id)
}
set(fileUploadsAtom, fileUploads)
},
)
export const fileUploadCountAtom = atom(
(get) => Object.keys(get(fileUploadsAtom)).length,
)
export const hasFileUploadsAtom = atom((get) => get(fileUploadCountAtom) > 0)
export const inProgressFileUploadCountAtom = atom((get) => {
const statuses = get(fileUploadsAtom)
let count = 0
for (const status in statuses) {
if (statuses[status]?.kind === FileUploadStatusKind.InProgress) {
count += 1
}
}
return count
})
export const successfulFileUploadCountAtom = atom((get) => {
const statuses = get(fileUploadsAtom)
let count = 0
for (const status in statuses) {
if (statuses[status]?.kind === FileUploadStatusKind.Success) {
count += 1
}
}
return count
})
export const hasFileUploadsErrorAtom = atom((get) => {
const statuses = get(fileUploadsAtom)
for (const status in statuses) {
if (statuses[status]?.kind === FileUploadStatusKind.Error) {
return true
}
}
return false
})