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 @@ -7,7 +7,10 @@ import { storeEstablishedHolderActionType } from './holder-actions.js'; import blobService from '../facts/blob-service.js'; import type { CallSingleKeyserverEndpoint } from '../keyserver-conn/call-single-keyserver-endpoint.js'; -import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js'; +import { + extractKeyserverIDFromID, + extractKeyserverIDFromIDOptional, +} from '../keyserver-conn/keyserver-call-utils.js'; import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js'; import type { CallKeyserverEndpoint } from '../keyserver-conn/keyserver-conn-types.js'; import { type PerformHTTPMultipartUpload } from '../keyserver-conn/multipart-upload.js'; @@ -327,9 +330,122 @@ return useKeyserverCall(blobUploadAction); } +export type ThickThreadMediaMetadataInput = { + +blobHash: string, + +encryptionKey: string, + +mimeType: string, + +dimensions: ?Dimensions, + +filename?: ?string, + +thumbHash?: ?string, + +loop?: boolean, +}; + +export type MediaMetadataReassignmentAction = (input: { + +mediaMetadataInput: ThickThreadMediaMetadataInput, + +keyserverOrThreadID: string, +}) => Promise; + +const reassignThickThreadMediaForThinThread = + ( + callKeyserverEndpoint: CallKeyserverEndpoint, + authMetadata: AuthMetadata, + ): MediaMetadataReassignmentAction => + async input => { + const { mediaMetadataInput, keyserverOrThreadID } = input; + const { encryptionKey, loop, dimensions, thumbHash, mimeType } = + mediaMetadataInput; + const blobHolder = generateBlobHolder(); + const blobHash = toBase64URL(mediaMetadataInput.blobHash); + const defaultHeaders = createDefaultHTTPRequestHeaders(authMetadata); + + let filename = mediaMetadataInput.filename; + if (!filename) { + const basename = Math.random().toString(36).slice(-10); + const extension = mediaConfig[mimeType]?.extension; + filename = extension ? `${basename}.${extension}` : basename; + } + + // 1. Assign new holder for blob with given blobHash + try { + const assignHolderEndpoint = blobService.httpEndpoints.ASSIGN_HOLDER; + const assignHolderResponse = await fetch( + makeBlobServiceEndpointURL(assignHolderEndpoint), + { + method: assignHolderEndpoint.method, + body: JSON.stringify({ + holder: blobHolder, + blob_hash: blobHash, + }), + headers: { + ...defaultHeaders, + 'content-type': 'application/json', + }, + }, + ); + handleHTTPResponseError(assignHolderResponse); + } catch (e) { + throw new Error( + `Failed to assign holder: ${ + getMessageForException(e) ?? 'unknown error' + }`, + ); + } + + // 2. Upload media metadata to keyserver + const keyserverID = extractKeyserverIDFromID(keyserverOrThreadID); + const requests = { + [keyserverID]: { + blobHash, + blobHolder, + encryptionKey, + filename, + mimeType, + loop, + thumbHash, + ...dimensions, + }, + }; + const responses = await callKeyserverEndpoint( + 'upload_media_metadata', + requests, + ); + const response = responses[keyserverID]; + + return { + id: response.id, + uri: response.uri, + mediaType: response.mediaType, + dimensions: response.dimensions, + loop: response.loop, + }; + }; + +function useMediaMetadataReassignment(): MediaMetadataReassignmentAction { + const identityContext = React.useContext(IdentityClientContext); + invariant(identityContext, 'Identity context should be set'); + const { getAuthMetadata } = identityContext; + + const thickThreadMediaReassignmentAction = React.useCallback( + ( + callSingleKeyserverEndpoint: CallSingleKeyserverEndpoint, + ): MediaMetadataReassignmentAction => + async input => { + const authMetadata = await getAuthMetadata(); + const authenticatedAction = reassignThickThreadMediaForThinThread( + callSingleKeyserverEndpoint, + authMetadata, + ); + return authenticatedAction(input); + }, + [getAuthMetadata], + ); + return useKeyserverCall(thickThreadMediaReassignmentAction); +} + export { uploadMultimedia, useBlobServiceUpload, + useMediaMetadataReassignment, updateMultimediaMessageMediaActionType, useDeleteUpload, };