Refactor file upload to use generated upload mutation
Replaced direct SDK file upload calls with generated React Query mutations for better progress tracking and error handling. Streamlined logic for obtaining presigned URLs and uploading files, ensuring consistency and maintainability. Removed redundant code and improved state management during the upload process.
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
import { useState, useCallback, useEffect } from "react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import {
|
||||
generatePresignedUrl,
|
||||
uploadFile as uploadFileSdk,
|
||||
PresignedUrlResponse,
|
||||
} from "@/client";
|
||||
import { generatePresignedUrl, PresignedUrlResponse } from "@/client";
|
||||
import { uploadFileMutation } from "@/client/@tanstack/react-query.gen";
|
||||
|
||||
export interface FileUploadState {
|
||||
file?: File;
|
||||
@@ -41,6 +38,7 @@ export function usePresignedUpload(options: UsePresignedUploadOptions = {}) {
|
||||
|
||||
const [uploadState, setUploadState] = useState<FileUploadState>(initialState);
|
||||
|
||||
// Use the presigned URL mutation
|
||||
const presignedUrlMutation = useMutation({
|
||||
mutationFn: async (file: File): Promise<PresignedUrlResponse> => {
|
||||
const { data } = await generatePresignedUrl({
|
||||
@@ -54,23 +52,29 @@ export function usePresignedUpload(options: UsePresignedUploadOptions = {}) {
|
||||
if (!data || !data.upload_url || !data.file_url) {
|
||||
throw new Error("Invalid response obtaining presigned URLs");
|
||||
}
|
||||
|
||||
console.log("Presigned URL response: ", data);
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
// Use the generated upload file mutation
|
||||
const uploadMutation = useMutation(
|
||||
uploadFileMutation({
|
||||
onUploadProgress: (progressEvent: any) => {
|
||||
const progress =
|
||||
progressEvent.loaded && progressEvent.total
|
||||
? Math.round((progressEvent.loaded / progressEvent.total) * 100)
|
||||
: 0;
|
||||
setUploadState((prev) => ({ ...prev, progress }));
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const uploadFileToPresignedUrl = useCallback(
|
||||
async (file: File, uploadUrl: string): Promise<void> => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
// Use the SDK's uploadFile method instead of direct XMLHttpRequest
|
||||
|
||||
// Create form data for the file
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
// Get the token from the upload URL
|
||||
// Assuming the uploadUrl format has a token part after the last '/'
|
||||
const token = uploadUrl.split("/").pop();
|
||||
|
||||
if (!token) {
|
||||
@@ -78,35 +82,31 @@ export function usePresignedUpload(options: UsePresignedUploadOptions = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a progress tracker
|
||||
const onProgress = (progressEvent: any) => {
|
||||
const progress =
|
||||
progressEvent.loaded && progressEvent.total
|
||||
? Math.round((progressEvent.loaded / progressEvent.total) * 100)
|
||||
: 0;
|
||||
setUploadState((prev) => ({ ...prev, progress }));
|
||||
};
|
||||
|
||||
// Call the SDK's upload method
|
||||
uploadFileSdk({
|
||||
path: { token },
|
||||
body: { file },
|
||||
onUploadProgress: onProgress,
|
||||
})
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch((error: any) => {
|
||||
reject(
|
||||
new Error(`Upload failed: ${error.message || "Unknown error"}`),
|
||||
);
|
||||
});
|
||||
// Use the generated mutation instead of direct SDK call
|
||||
uploadMutation.mutate(
|
||||
{
|
||||
path: { token },
|
||||
body: { file },
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
resolve();
|
||||
},
|
||||
onError: (error: any) => {
|
||||
reject(
|
||||
new Error(
|
||||
`Upload failed: ${error.message || "Unknown error"}`,
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
reject(new Error(`Network error during file upload: ${error}`));
|
||||
}
|
||||
});
|
||||
},
|
||||
[],
|
||||
[uploadMutation],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -149,50 +149,48 @@ export function usePresignedUpload(options: UsePresignedUploadOptions = {}) {
|
||||
fileUrl: undefined,
|
||||
});
|
||||
},
|
||||
[reset, fileType, onUploadError],
|
||||
[fileType, onUploadError, reset],
|
||||
);
|
||||
|
||||
const uploadFile = useCallback(async () => {
|
||||
if (!uploadState.file) {
|
||||
const error = "File not selected.";
|
||||
setUploadState((prev) => ({ ...prev, error }));
|
||||
onUploadError?.(error);
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadState((prev) => ({
|
||||
...prev,
|
||||
isUploading: true,
|
||||
progress: 0,
|
||||
error: undefined,
|
||||
}));
|
||||
const { file } = uploadState;
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
const { upload_url, file_url } = await presignedUrlMutation.mutateAsync(
|
||||
uploadState.file,
|
||||
);
|
||||
setUploadState((prev) => ({
|
||||
...prev,
|
||||
isUploading: true,
|
||||
error: undefined,
|
||||
}));
|
||||
|
||||
await uploadFileToPresignedUrl(uploadState.file, upload_url);
|
||||
// First get the presigned URL
|
||||
const presignedData = await presignedUrlMutation.mutateAsync(file);
|
||||
|
||||
// Then upload to that URL
|
||||
await uploadFileToPresignedUrl(file, presignedData.upload_url);
|
||||
|
||||
// Use the file_url directly from the presigned URL response
|
||||
const fileUrl = presignedData.file_url;
|
||||
|
||||
setUploadState((prev) => ({
|
||||
...prev,
|
||||
isUploading: false,
|
||||
fileUrl: file_url,
|
||||
progress: 100,
|
||||
fileUrl,
|
||||
}));
|
||||
|
||||
onUploadSuccess?.(file_url);
|
||||
} catch (error: unknown) {
|
||||
const message =
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Unexpected error during upload.";
|
||||
onUploadSuccess?.(fileUrl);
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.message || "Unknown error during file upload";
|
||||
|
||||
setUploadState((prev) => ({
|
||||
...prev,
|
||||
isUploading: false,
|
||||
error: message,
|
||||
progress: 0,
|
||||
error: errorMessage,
|
||||
}));
|
||||
onUploadError?.(message);
|
||||
|
||||
onUploadError?.(errorMessage);
|
||||
}
|
||||
}, [
|
||||
uploadState.file,
|
||||
@@ -207,6 +205,5 @@ export function usePresignedUpload(options: UsePresignedUploadOptions = {}) {
|
||||
selectFile,
|
||||
uploadFile,
|
||||
reset,
|
||||
isPresignedUrlLoading: presignedUrlMutation.isPending,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user