8df5e76d29
Integrates Uppy.js for file uploads, adds new API endpoints for requesting upload URLs, and updates UI components to support image uploads. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 804c1330-3360-45df-814d-221ee0d46866 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/JyUisd3 Replit-Helium-Checkpoint-Created: true
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import type { ReactNode } from "react";
|
|
import Uppy from "@uppy/core";
|
|
import type { UppyFile, UploadResult } from "@uppy/core";
|
|
import DashboardModal from "@uppy/react/dashboard-modal";
|
|
import "@uppy/core/css/style.min.css";
|
|
import "@uppy/dashboard/css/style.min.css";
|
|
import AwsS3 from "@uppy/aws-s3";
|
|
|
|
interface ObjectUploaderProps {
|
|
maxNumberOfFiles?: number;
|
|
maxFileSize?: number;
|
|
/**
|
|
* Function to get upload parameters for each file.
|
|
* IMPORTANT: This receives the file object - use file.name, file.size, file.type
|
|
* to request per-file presigned URLs from your backend.
|
|
*/
|
|
onGetUploadParameters: (
|
|
file: UppyFile<Record<string, unknown>, Record<string, unknown>>
|
|
) => Promise<{
|
|
method: "PUT";
|
|
url: string;
|
|
headers?: Record<string, string>;
|
|
}>;
|
|
onComplete?: (
|
|
result: UploadResult<Record<string, unknown>, Record<string, unknown>>
|
|
) => void;
|
|
buttonClassName?: string;
|
|
children: ReactNode;
|
|
}
|
|
|
|
/**
|
|
* A file upload component that renders as a button and provides a modal interface for
|
|
* file management.
|
|
*
|
|
* Features:
|
|
* - Renders as a customizable button that opens a file upload modal
|
|
* - Provides a modal interface for:
|
|
* - File selection
|
|
* - File preview
|
|
* - Upload progress tracking
|
|
* - Upload status display
|
|
*
|
|
* The component uses Uppy v5 under the hood to handle all file upload functionality.
|
|
* All file management features are automatically handled by the Uppy dashboard modal.
|
|
*
|
|
* @param props - Component props
|
|
* @param props.maxNumberOfFiles - Maximum number of files allowed to be uploaded
|
|
* (default: 1)
|
|
* @param props.maxFileSize - Maximum file size in bytes (default: 10MB)
|
|
* @param props.onGetUploadParameters - Function to get upload parameters for each file.
|
|
* Receives the UppyFile object with file.name, file.size, file.type properties.
|
|
* Use these to request per-file presigned URLs from your backend. Returns method,
|
|
* url, and optional headers for the upload request.
|
|
* @param props.onComplete - Callback function called when upload is complete. Typically
|
|
* used to make post-upload API calls to update server state and set object ACL
|
|
* policies.
|
|
* @param props.buttonClassName - Optional CSS class name for the button
|
|
* @param props.children - Content to be rendered inside the button
|
|
*/
|
|
export function ObjectUploader({
|
|
maxNumberOfFiles = 1,
|
|
maxFileSize = 10485760, // 10MB default
|
|
onGetUploadParameters,
|
|
onComplete,
|
|
buttonClassName,
|
|
children,
|
|
}: ObjectUploaderProps) {
|
|
const onCompleteRef = useRef(onComplete);
|
|
const onGetUploadParametersRef = useRef(onGetUploadParameters);
|
|
useEffect(() => { onCompleteRef.current = onComplete; }, [onComplete]);
|
|
useEffect(() => { onGetUploadParametersRef.current = onGetUploadParameters; }, [onGetUploadParameters]);
|
|
|
|
const [showModal, setShowModal] = useState(false);
|
|
const [uppy] = useState(() =>
|
|
new Uppy({
|
|
restrictions: {
|
|
maxNumberOfFiles,
|
|
maxFileSize,
|
|
},
|
|
autoProceed: false,
|
|
})
|
|
.use(AwsS3, {
|
|
shouldUseMultipart: false,
|
|
getUploadParameters: (file) => onGetUploadParametersRef.current(file),
|
|
})
|
|
.on("complete", (result) => {
|
|
onCompleteRef.current?.(result);
|
|
})
|
|
);
|
|
|
|
return (
|
|
<div>
|
|
<button onClick={() => setShowModal(true)} className={buttonClassName}>
|
|
{children}
|
|
</button>
|
|
|
|
<DashboardModal
|
|
uppy={uppy}
|
|
open={showModal}
|
|
onRequestClose={() => setShowModal(false)}
|
|
proudlyDisplayPoweredByUppy={false}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|