Skip to content

final PR for uNotes changes and tweaks #281

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "upup-react-file-uploader",
"author": "Devino Solutions",
"license": "MIT",
"version": "1.3.2",
"version": "1.4.2",
"publishConfig": {
"access": "public"
},
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/UpupUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export type UpupUploaderRef = {
files: FileWithParams[]
loading: boolean
progress: number
upload(): Promise<string[] | undefined>
upload(): Promise<FileWithParams[] | undefined>
dynamicUpload(
files: File[] | FileWithParams[],
): Promise<string[] | undefined>
): Promise<FileWithParams[] | undefined>
setFiles(newFiles: File[]): void
dynamicallyReplaceFiles(files: File[] | FileWithParams[]): void
}
Expand Down
16 changes: 12 additions & 4 deletions src/frontend/components/FileList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default memo(function FileList() {
activeAdapter,
files,
upload: { proceedUpload, uploadStatus, totalProgress },
props: { mini, dark, classNames, allowPreview },
props: { mini, dark, classNames, allowPreview, isProcessing },
handleDone,
handleCancel,
} = useRootContext()
Expand Down Expand Up @@ -42,7 +42,10 @@ export default memo(function FileList() {
<div
className={cn(
// Always-on classes
'upup-flex upup-flex-col upup-gap-4',
` ${
isProcessing &&
'upup-pointer-events-none upup-opacity-75'
} upup-flex upup-flex-col upup-gap-4`,
{
'md:upup-grid md:upup-gap-y-6': allowPreview,
'md:upup-grid-cols-3':
Expand Down Expand Up @@ -82,8 +85,13 @@ export default memo(function FileList() {
},
classNames.uploadButton,
)}
onClick={proceedUpload}
disabled={uploadStatus === UploadStatus.ONGOING}
onClick={() => {
proceedUpload()
}}
disabled={
uploadStatus === UploadStatus.ONGOING ||
isProcessing
}
>
Upload {files.size} file{files.size > 1 ? 's' : ''}
</button>
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/components/shared/MainBoxHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default function MainBoxHeader({ handleCancel }: Readonly<Props>) {
props: {
mini,
limit,
isProcessing,
dark,
classNames,
icons: { ContainerAddMoreIcon },
Expand Down Expand Up @@ -49,7 +50,7 @@ export default function MainBoxHeader({ handleCancel }: Readonly<Props>) {
classNames.containerCancelButton,
)}
onClick={handleCancel}
disabled={isUploading}
disabled={isUploading || isProcessing}
>
{cancelText}
</button>
Expand Down Expand Up @@ -77,7 +78,7 @@ export default function MainBoxHeader({ handleCancel }: Readonly<Props>) {
classNames.containerAddMoreButton,
)}
onClick={() => setIsAddingMore(true)}
disabled={isUploading}
disabled={isUploading || isProcessing}
>
<ContainerAddMoreIcon /> Add More
</button>
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/context/RootContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ContextUpload = {
setUploadStatus: Dispatch<SetStateAction<UploadStatus>>
totalProgress: number
filesProgressMap: FilesProgressMap
proceedUpload: () => Promise<string[] | undefined>
proceedUpload: () => Promise<FileWithParams[] | undefined>
}

type ContextProps = Required<
Expand Down Expand Up @@ -66,7 +66,7 @@ export interface IRootContext {
dynamicallyReplaceFiles: (files: File[] | FileWithParams[]) => void
dynamicUpload: (
files: File[] | FileWithParams[],
) => Promise<string[] | undefined>
) => Promise<FileWithParams[] | undefined>
isAddingMore: boolean
setIsAddingMore: Dispatch<SetStateAction<boolean>>

Expand Down
14 changes: 7 additions & 7 deletions src/frontend/hooks/useMainBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function useMainBox() {
activeAdapter,
isAddingMore,
upload: { uploadStatus },
props: { onFilesDragOver, onFilesDragLeave, onFilesDrop },
props: { onFilesDragOver, onFilesDragLeave, onFilesDrop, isProcessing },
setFiles,
} = useRootContext()
const [isDragging, setIsDragging] = useState(false)
Expand All @@ -26,7 +26,7 @@ export default function useMainBox() {

const handleDragOver: DragEventHandler<HTMLDivElement> = useCallback(
e => {
if (disableDragAction) return
if (disableDragAction || isProcessing) return
e.preventDefault()

setIsDragging(true)
Expand All @@ -35,25 +35,25 @@ export default function useMainBox() {
const files = Array.from(e.dataTransfer.files)
onFilesDragOver(files)
},
[disableDragAction, onFilesDragOver],
[disableDragAction, onFilesDragOver, isProcessing],
)

const handleDragLeave: DragEventHandler<HTMLDivElement> = useCallback(
e => {
if (disableDragAction) return
if (disableDragAction || isProcessing) return
e.preventDefault()

setIsDragging(false)

const files = Array.from(e.dataTransfer.files)
onFilesDragLeave(files)
},
[disableDragAction, onFilesDragLeave],
[disableDragAction, onFilesDragLeave, isProcessing],
)

const handleDrop: DragEventHandler<HTMLDivElement> = useCallback(
e => {
if (disableDragAction) return
if (disableDragAction || isProcessing) return
e.preventDefault()

const droppedFiles = Array.from(e.dataTransfer.files)
Expand All @@ -63,7 +63,7 @@ export default function useMainBox() {

setIsDragging(false)
},
[disableDragAction, onFilesDrop, setFiles],
[disableDragAction, onFilesDrop, setFiles, isProcessing],
)

return {
Expand Down
168 changes: 92 additions & 76 deletions src/frontend/hooks/useRootProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,13 @@
return files.length > 0 && 'id' in files[0]
}
async function dynamicUpload(files: File[] | FileWithParams[]) {
dynamicallyReplaceFiles(files)
return await proceedUpload()
const filesToUpload = isFileWithParamsArray(files)
? files
: files.map(file => fileAppendParams(file))
return await proceedUpload(filesToUpload)
}
function dynamicallyReplaceFiles(files: File[] | FileWithParams[]) {
const filesMap = new Map<string, FileWithParams>()

if (isFileWithParamsArray(files)) {
for (const f of files) {
filesMap.set(f.id, f)
Expand Down Expand Up @@ -203,9 +204,8 @@
async (files: FileWithParams[]) => {
try {
return await Promise.all(
files.map(async file => {
const compressed = await compressFile(file)
return compressed
files.map(async oldFile => {
return await compressFile(oldFile)
}),
)
} catch (error) {
Expand All @@ -224,7 +224,6 @@
loaded: 0,
total: b.size,
}

return a
}, {} as FilesProgressMap)
setFilesProgressMap(progressMap)
Expand All @@ -233,82 +232,99 @@
},
[onPrepareFiles],
)
const proceedUpload = async () => {
if (!selectedFilesMap.size) return

setUploadStatus(UploadStatus.ONGOING)
setUploadError('')

const selectedFiles = Array.from(selectedFilesMap.values())

try {
const compressedFiles = shouldCompress
? await compressFiles(selectedFiles)
: selectedFiles

const processedFiles = await handlePrepareFiles(compressedFiles)
const proceedUpload = useCallback(
async (dynamicFiles: FileWithParams[] | undefined = undefined) => {
if (!selectedFilesMap.size && !dynamicFiles) return
setUploadStatus(UploadStatus.ONGOING)
setUploadError('')
const sendEvent = !dynamicFiles
const selectedFiles = dynamicFiles
? dynamicFiles
: Array.from(selectedFilesMap.values())
try {
const compressedFiles = shouldCompress
? await compressFiles(selectedFiles)
: selectedFiles
const processedFiles = await handlePrepareFiles(compressedFiles)
// Initialize SDK
const sdk = new ProviderSDK({
provider,
tokenEndpoint,
constraints: {
multiple,
accept,
maxFileSize: sizeToBytes(
maxFileSize.size,
maxFileSize.unit,
),
},
customProps,
enableAutoCorsConfig,
})

// Initialize SDK
const sdk = new ProviderSDK({
provider,
tokenEndpoint,
constraints: {
multiple,
accept,
maxFileSize: sizeToBytes(
maxFileSize.size,
maxFileSize.unit,
// Upload files
const uploadResults = await Promise.all(
processedFiles.map(file =>
sdk.upload(file, {
onFileUploadStart,
onFileUploadProgress: (file, progress) => {
setFilesProgressMap(prev => ({
...prev,
[file.id]: {
...prev[file.id],
loaded: progress.loaded,
},
}))
onFileUploadProgress(file, progress)
},
onFileUploadComplete,
sendEvent,
onError,
onFilesUploadProgress: (completedFiles: number) =>
onFilesUploadProgress(
completedFiles,
processedFiles.length,
),
}),
),
},
customProps,
enableAutoCorsConfig,
})

// Upload files
const uploadResults = await Promise.all(
processedFiles.map(file =>
sdk.upload(file, {
onFileUploadStart,
onFileUploadProgress: (file, progress) => {
setFilesProgressMap(prev => ({
...prev,
[file.id]: {
...prev[file.id],
loaded: progress.loaded,
},
}))
onFileUploadProgress(file, progress)
},
onFileUploadComplete,
onError,
onFilesUploadProgress: (completedFiles: number) =>
onFilesUploadProgress(
completedFiles,
processedFiles.length,
),
}),
),
)

// Extract keys from results
const uploadedFileKeys = uploadResults.map(result => result.key)
onFilesUploadComplete(uploadedFileKeys)

setUploadStatus(UploadStatus.SUCCESSFUL)
return uploadedFileKeys
} catch (error) {
onError((error as Error).message)
setUploadStatus(UploadStatus.FAILED)
)
const finalFiles = uploadResults.map(result => result.file)
if (sendEvent) onFilesUploadComplete(finalFiles)

// Reset progress map
setFilesProgressMap({})
return
}
}
setUploadStatus(UploadStatus.SUCCESSFUL)
return finalFiles
} catch (error) {
onError((error as Error).message)
setUploadStatus(UploadStatus.FAILED)
setFilesProgressMap({})
return
}
},
[
selectedFilesMap,
shouldCompress,
compressFiles,
handlePrepareFiles,
provider,
tokenEndpoint,
multiple,
accept,
maxFileSize.size,
maxFileSize.unit,
customProps,
enableAutoCorsConfig,
onFilesUploadComplete,
onFileUploadStart,
onFileUploadComplete,
onError,
onFileUploadProgress,
onFilesUploadProgress,
],
)
const handleDone = useCallback(() => {
onDoneClicked()
handleCancel()
}, [])

Check warning on line 327 in src/frontend/hooks/useRootProvider.ts

View workflow job for this annotation

GitHub Actions / Build project

React Hook useCallback has missing dependencies: 'handleCancel' and 'onDoneClicked'. Either include them or remove the dependency array. If 'onDoneClicked' changes too often, find the parent component that defines it and wrap that definition in useCallback

const handleCancel = useCallback(() => {
setUploadStatus(UploadStatus.PENDING)
Expand Down
21 changes: 14 additions & 7 deletions src/frontend/lib/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,20 @@ export const fileAppendParams = (file: File) => {
return file as FileWithParams
}

export async function compressFile(file: FileWithParams) {
const buffer = await file.arrayBuffer()
return fileAppendParams(
new File([pako.gzip(buffer)], file.name + '.gz', {
type: 'application/octet-stream',
}),
)
export async function compressFile(oldFile: FileWithParams) {
const buffer = await oldFile.arrayBuffer()

const compressed = new File([pako.gzip(buffer)], oldFile.name + '.gz', {
type: 'application/octet-stream',
lastModified: oldFile.lastModified,
})
const newFileWithParams = fileAppendParams(compressed)
newFileWithParams.id = oldFile.id
newFileWithParams.thumbnail = oldFile.thumbnail
newFileWithParams.fileHash = oldFile.fileHash
newFileWithParams.key = oldFile.key

return newFileWithParams
}

export function searchDriveFiles<
Expand Down
Loading