diff --git a/web/avatars/avatar-hooks.react.js b/web/avatars/avatar-hooks.react.js index d7b01a252..72c54c496 100644 --- a/web/avatars/avatar-hooks.react.js +++ b/web/avatars/avatar-hooks.react.js @@ -1,82 +1,103 @@ // @flow import * as React from 'react'; import { uploadMultimedia, useBlobServiceUpload, } from 'lib/actions/upload-actions.js'; import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js'; import type { UpdateUserAvatarRequest } from 'lib/types/avatar-types.js'; import { authoritativeKeyserverID } from '../authoritative-keyserver.js'; import { encryptFile } from '../media/encryption-utils.js'; import { generateThumbHash } from '../media/image-utils.js'; import { validateFile } from '../media/media-utils.js'; // TODO: flip the switch const useBlobServiceUploads = false; -function useUploadAvatarMedia(): File => Promise { +type AvatarMediaUploadOptions = { + +uploadMetadataToKeyserver?: boolean, +}; + +function useUploadAvatarMedia( + options: AvatarMediaUploadOptions = {}, +): File => Promise { + const { uploadMetadataToKeyserver = true } = options; + const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia); const callBlobServiceUpload = useBlobServiceUpload(); const uploadAvatarMedia = React.useCallback( async (file: File): Promise => { const validatedFile = await validateFile(file); const { result } = validatedFile; if (!result.success) { throw new Error('Avatar media validation failed.'); } const { file: fixedFile, dimensions } = result; const uploadExtras = { ...dimensions, loop: false, }; - if (!useBlobServiceUploads) { + const useBlobService = + !uploadMetadataToKeyserver || useBlobServiceUploads; + if (!useBlobService) { const { id } = await callUploadMultimedia(fixedFile, uploadExtras); return { type: 'image', uploadID: id }; } const encryptionResponse = await encryptFile(fixedFile); const { result: encryptionResult } = encryptionResponse; if (!encryptionResult.success) { throw new Error('Avatar media encryption failed.'); } const { file: encryptedFile, sha256Hash: blobHash, encryptionKey, } = encryptionResult; const { result: thumbHashResult } = await generateThumbHash( fixedFile, encryptionKey, ); const thumbHash = thumbHashResult.success ? thumbHashResult.thumbHash : null; - const { id } = await callBlobServiceUpload({ + const { id, uri } = await callBlobServiceUpload({ uploadInput: { blobInput: { type: 'file', file: encryptedFile, }, blobHash, encryptionKey, dimensions, loop: false, thumbHash, }, - keyserverOrThreadID: authoritativeKeyserverID, + keyserverOrThreadID: uploadMetadataToKeyserver + ? authoritativeKeyserverID + : null, callbacks: {}, }); - return { type: 'encrypted_image', uploadID: id }; + if (uploadMetadataToKeyserver) { + return { type: 'encrypted_image', uploadID: id }; + } + + return { + type: 'non_keyserver_image', + blobURI: uri, + thumbHash, + encryptionKey, + }; }, - [callBlobServiceUpload, callUploadMultimedia], + [callBlobServiceUpload, callUploadMultimedia, uploadMetadataToKeyserver], ); return uploadAvatarMedia; } export { useUploadAvatarMedia }; diff --git a/web/avatars/edit-thread-avatar-menu.react.js b/web/avatars/edit-thread-avatar-menu.react.js index d2ec10eb1..1c4a72cc4 100644 --- a/web/avatars/edit-thread-avatar-menu.react.js +++ b/web/avatars/edit-thread-avatar-menu.react.js @@ -1,124 +1,127 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { EditThreadAvatarContext } from 'lib/components/base-edit-thread-avatar-provider.react.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import SWMansionIcon from 'lib/components/swmansion-icon.react.js'; import type { ThreadInfo, RawThreadInfo, } from 'lib/types/minimally-encoded-thread-permissions-types.js'; +import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; import { useUploadAvatarMedia } from './avatar-hooks.react.js'; import css from './edit-avatar-menu.css'; import ThreadEmojiAvatarSelectionModal from './thread-emoji-avatar-selection-modal.react.js'; import MenuItem from '../components/menu-item.react.js'; import Menu from '../components/menu.react.js'; import { allowedMimeTypeString } from '../media/file-utils.js'; const editIcon = (
); type Props = { +threadInfo: RawThreadInfo | ThreadInfo, }; function EditThreadAvatarMenu(props: Props): React.Node { const { threadInfo } = props; const editThreadAvatarContext = React.useContext(EditThreadAvatarContext); invariant(editThreadAvatarContext, 'editThreadAvatarContext should be set'); const { baseSetThreadAvatar } = editThreadAvatarContext; const removeThreadAvatar = React.useCallback( () => baseSetThreadAvatar(threadInfo.id, { type: 'remove' }), [baseSetThreadAvatar, threadInfo.id], ); const removeMenuItem = React.useMemo( () => ( ), [removeThreadAvatar], ); const imageInputRef = React.useRef(); const onImageMenuItemClicked = React.useCallback( () => imageInputRef.current?.click(), [], ); - const uploadAvatarMedia = useUploadAvatarMedia(); + const uploadAvatarMedia = useUploadAvatarMedia({ + uploadMetadataToKeyserver: !threadTypeIsThick(threadInfo.type), + }); const onImageSelected = React.useCallback( async (event: SyntheticEvent) => { const { target } = event; invariant(target instanceof HTMLInputElement, 'target not input'); const uploadResult = await uploadAvatarMedia(target.files[0]); await baseSetThreadAvatar(threadInfo.id, uploadResult); }, [baseSetThreadAvatar, threadInfo.id, uploadAvatarMedia], ); const imageMenuItem = React.useMemo( () => ( ), [onImageMenuItemClicked], ); const { pushModal } = useModalContext(); const openEmojiSelectionModal = React.useCallback( () => pushModal(), [pushModal, threadInfo], ); const emojiMenuItem = React.useMemo( () => ( ), [openEmojiSelectionModal], ); const menuItems = React.useMemo(() => { const items = [emojiMenuItem, imageMenuItem]; if (threadInfo.avatar) { items.push(removeMenuItem); } return items; }, [emojiMenuItem, imageMenuItem, removeMenuItem, threadInfo.avatar]); return (
{menuItems}
); } export default EditThreadAvatarMenu;