diff --git a/lib/actions/upload-actions.js b/lib/actions/upload-actions.js --- a/lib/actions/upload-actions.js +++ b/lib/actions/upload-actions.js @@ -2,13 +2,17 @@ import { type PerformHTTPMultipartUpload } from '../keyserver-conn/multipart-upload.js'; import type { Dimensions, UploadMultimediaResult } from '../types/media-types'; -import { type BlobServiceUploadHandler } from '../utils/blob-service-upload.js'; +import { + type BlobServiceUploadHandler, + type PlaintextMediaUploadHandler, +} from '../utils/blob-service-upload.js'; export type MultimediaUploadCallbacks = Partial<{ +onProgress: (percent: number) => void, +abortHandler: (abort: () => void) => void, +performHTTPMultipartUpload: PerformHTTPMultipartUpload, +blobServiceUploadHandler: BlobServiceUploadHandler, + +plaintextMediaUploadHandler: PlaintextMediaUploadHandler, +timeout: ?number, }>; @@ -65,4 +69,16 @@ +keyserverOrThreadIDForMetadata: string, }) => Promise; +export type PlaintextMediaUploadInput = { + +uploadInput: BlobServiceUploadFile, + +dimensions: ?Dimensions, + +loop?: boolean, + +thumbHash?: ?string, +}; + +export type PlaintextMediaUploadAction = (input: { + +mediaInput: PlaintextMediaUploadInput, + +callbacks?: MultimediaUploadCallbacks, +}) => Promise; + export { updateMultimediaMessageMediaActionType }; diff --git a/lib/hooks/upload-hooks.js b/lib/hooks/upload-hooks.js --- a/lib/hooks/upload-hooks.js +++ b/lib/hooks/upload-hooks.js @@ -9,6 +9,7 @@ BlobServiceUploadAction, DeleteUploadInput, MediaMetadataReassignmentAction, + PlaintextMediaUploadAction, } from '../actions/upload-actions.js'; import blobService from '../facts/blob-service.js'; import type { CallSingleKeyserverEndpoint } from '../keyserver-conn/call-single-keyserver-endpoint.js'; @@ -23,9 +24,13 @@ type AuthMetadata, IdentityClientContext, } from '../shared/identity-client-context.js'; +import type { UploadMultimediaResult } from '../types/media-types.js'; import type { Dispatch } from '../types/redux-types.js'; import { toBase64URL } from '../utils/base64.js'; -import { blobServiceUploadHandler } from '../utils/blob-service-upload.js'; +import { + blobServiceUploadHandler, + plaintextMediaUploadHandler, +} from '../utils/blob-service-upload.js'; import { assignBlobHolder, generateBlobHolder, @@ -224,6 +229,77 @@ return useKeyserverCall(blobUploadAction); } +function usePlaintextMediaUpload(): PlaintextMediaUploadAction { + const identityContext = React.useContext(IdentityClientContext); + invariant(identityContext, 'Identity context should be set'); + const { getAuthMetadata } = identityContext; + + return React.useCallback( + async (input): Promise => { + const { mediaInput, callbacks } = input; + const { uploadInput, dimensions, thumbHash, loop } = mediaInput; + + const authMetadata = await getAuthMetadata(); + const uploadEndpoint = blobService.httpEndpoints.UPLOAD_MEDIA; + + let mediaUploadCallback = plaintextMediaUploadHandler; + if (callbacks && callbacks.plaintextMediaUploadHandler) { + mediaUploadCallback = callbacks.plaintextMediaUploadHandler; + } + + try { + const customMetadata = JSON.stringify({ + dimensions, + thumbHash, + loop, + }); + const response = await mediaUploadCallback( + makeBlobServiceEndpointURL(uploadEndpoint), + uploadInput, + authMetadata, + customMetadata, + { ...callbacks }, + ); + + const mimeType = + uploadInput.type === 'file' + ? uploadInput.file.type + : uploadInput.mimeType; + const mediaType = + mediaConfig[response.contentType ?? mimeType]?.mediaType; + if (mediaType !== 'photo' && mediaType !== 'video') { + throw new Error( + `mediaType for ${mediaType} should be photo or video`, + ); + } + + if (!dimensions) { + throw new Error('dimensions are required for plaintext uploads'); + } + return { + id: response.mediaID, + uri: makeBlobServiceEndpointURL(blobService.httpEndpoints.GET_MEDIA, { + mediaID: response.mediaID, + }), + mediaType, + dimensions, + loop: loop ?? false, + }; + } catch (e) { + if (errorMessageIsInvalidCSAT(e)) { + throw e; + } + throw new Error( + `Failed to upload Farcaster media: ${ + getMessageForException(e) ?? 'unknown error' + }`, + ); + } + }, + [getAuthMetadata], + ); +} + const reassignThickThreadMediaForThinThread = ( callKeyserverEndpoint: CallKeyserverEndpoint, @@ -321,4 +397,9 @@ return useKeyserverCall(thickThreadMediaReassignmentAction); } -export { useMediaMetadataReassignment, useBlobServiceUpload, useDeleteUpload }; +export { + useMediaMetadataReassignment, + useBlobServiceUpload, + useDeleteUpload, + usePlaintextMediaUpload, +};