import { useState } from 'react' import { trpc } from '@/src/trpc-client' type ContextString = 'review' | 'product_info' | 'notification' | 'store' | 'complaint' | 'profile' interface UploadInput { blob: Blob mimeType: string } interface UploadBatchInput { images: UploadInput[] contextString: ContextString } interface UploadResult { keys: string[] presignedUrls: string[] } export function useUploadToObjectStorage() { const [isUploading, setIsUploading] = useState(false) const [error, setError] = useState(null) const [progress, setProgress] = useState<{ completed: number; total: number } | null>(null) const generateUploadUrls = trpc.common.generateUploadUrls.useMutation() const upload = async (input: UploadBatchInput): Promise => { setIsUploading(true) setError(null) setProgress({ completed: 0, total: input.images.length }) try { const { images, contextString } = input if (images.length === 0) { return { keys: [], presignedUrls: [] } } const mimeTypes = images.map((img) => img.mimeType) const { uploadUrls } = await generateUploadUrls.mutateAsync({ contextString: contextString as any, mimeTypes, }) if (uploadUrls.length !== images.length) { throw new Error(`Expected ${images.length} URLs, got ${uploadUrls.length}`) } const uploadPromises = images.map(async (image, index) => { const presignedUrl = uploadUrls[index] const { blob, mimeType } = image const response = await fetch(presignedUrl, { method: 'PUT', body: blob, headers: { 'Content-Type': mimeType }, }) if (!response.ok) { throw new Error(`Upload ${index + 1} failed with status ${response.status}`) } setProgress((prev) => (prev ? { ...prev, completed: prev.completed + 1 } : null)) return { key: extractKeyFromPresignedUrl(presignedUrl), presignedUrl, } }) const results = await Promise.all(uploadPromises) return { keys: results.map((result) => result.key), presignedUrls: results.map((result) => result.presignedUrl), } } catch (err) { const uploadError = err instanceof Error ? err : new Error('Upload failed') setError(uploadError) throw uploadError } finally { setIsUploading(false) setProgress(null) } } const uploadSingle = async ( blob: Blob, mimeType: string, contextString: ContextString ): Promise<{ key: string; presignedUrl: string }> => { const result = await upload({ images: [{ blob, mimeType }], contextString, }) return { key: result.keys[0], presignedUrl: result.presignedUrls[0], } } return { upload, uploadSingle, isUploading, error, progress, isPending: generateUploadUrls.isPending, } } function extractKeyFromPresignedUrl(url: string): string { const parsedUrl = new URL(url) let rawKey = parsedUrl.pathname.replace(/^\/+/, '') rawKey = rawKey.split('/').slice(1).join('/') return decodeURIComponent(rawKey) }