UNPKG

@shopify/shop-minis-react

Version:

React component library for Shopify Shop Minis with Tailwind CSS v4 support (source-only, requires TypeScript)

136 lines (115 loc) 3.19 kB
import {useCallback} from 'react' import {useShopActions} from '../../internal/useShopActions' import {UploadTarget} from '../../types' export interface UploadImageParams { /** * The MIME type of the image. */ mimeType: string /** * The size of the image in bytes. */ fileSize: number /** * The URI of the image to upload. */ uri: string } export interface UploadedImage { /** * The ID of the uploaded image. */ id: string /** * The URL of the uploaded image. */ imageUrl?: string /** * The resource URL of the uploaded image. */ resourceUrl?: string } interface UseImageUploadReturns { /** * Upload an image attached to the current user. */ uploadImage: (params: UploadImageParams[]) => Promise<UploadedImage[]> } const uploadFileToGCS = async ( image: UploadImageParams, target: UploadTarget ) => { const formData = new FormData() target.parameters.forEach(({name, value}) => { formData.append(name, value) }) // Append the actual file data last formData.append('file', new Blob([image.uri], {type: image.mimeType})) const uploadResponse = await fetch(target.url, { method: 'POST', body: formData, }) if (!uploadResponse.ok) { console.error('Failed to upload image', { response: await uploadResponse.text(), }) return {error: 'Failed to upload image'} } return {} } export const useImageUpload = (): UseImageUploadReturns => { const {createImageUploadLink, completeImageUpload} = useShopActions() const uploadImage = useCallback( async (params: UploadImageParams[]) => { if (params.length > 1) { throw new Error('Multiple image upload is not supported yet') } const links = await createImageUploadLink({ input: params.map(image => ({ mimeType: image.mimeType, fileSize: image.fileSize, })), }) if (!links.ok) { throw new Error(links.error.message) } // Upload single file to GCS // TODO: Upload multiple files to GCS const {error: uploadError} = await uploadFileToGCS( params[0], links?.data?.targets?.[0]! ) if (uploadError) { throw new Error(uploadError) } // 10 second polling for image upload let count = 0 while (count < 30) { const result = await completeImageUpload({ resourceUrls: links?.data?.targets?.map(target => target.resourceUrl) || [], }) if (!result.ok) { throw new Error(result.error.message) } // TODO: Add support for multiple files if (result.data?.files?.[0]?.fileStatus === 'READY') { return [ { id: result.data.files[0].id, imageUrl: result.data.files[0].image?.url, resourceUrl: links?.data?.targets?.[0]?.resourceUrl, }, ] } await new Promise(resolve => setTimeout(resolve, 1000)) count++ } throw new Error('Image upload completion timed out') }, [createImageUploadLink, completeImageUpload] ) return { uploadImage, } }