Populate blurHash when sending stories
This commit is contained in:
parent
6be69a7ba8
commit
9bad2301fd
8 changed files with 67 additions and 12 deletions
|
@ -60,6 +60,8 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
quotedMessageProps: overrideProps.quotedMessageProps,
|
quotedMessageProps: overrideProps.quotedMessageProps,
|
||||||
onClickQuotedMessage: action('onClickQuotedMessage'),
|
onClickQuotedMessage: action('onClickQuotedMessage'),
|
||||||
setQuotedMessage: action('setQuotedMessage'),
|
setQuotedMessage: action('setQuotedMessage'),
|
||||||
|
// MediaEditor
|
||||||
|
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
|
||||||
// MediaQualitySelector
|
// MediaQualitySelector
|
||||||
onSelectMediaQuality: action('onSelectMediaQuality'),
|
onSelectMediaQuality: action('onSelectMediaQuality'),
|
||||||
shouldSendHighQualityAttachments: Boolean(
|
shouldSendHighQualityAttachments: Boolean(
|
||||||
|
|
|
@ -13,6 +13,7 @@ import type {
|
||||||
import type { ErrorDialogAudioRecorderType } from '../state/ducks/audioRecorder';
|
import type { ErrorDialogAudioRecorderType } from '../state/ducks/audioRecorder';
|
||||||
import { RecordingState } from '../state/ducks/audioRecorder';
|
import { RecordingState } from '../state/ducks/audioRecorder';
|
||||||
import type { HandleAttachmentsProcessingArgsType } from '../util/handleAttachmentsProcessing';
|
import type { HandleAttachmentsProcessingArgsType } from '../util/handleAttachmentsProcessing';
|
||||||
|
import type { imageToBlurHash } from '../util/imageToBlurHash';
|
||||||
import { Spinner } from './Spinner';
|
import { Spinner } from './Spinner';
|
||||||
import type {
|
import type {
|
||||||
Props as EmojiButtonProps,
|
Props as EmojiButtonProps,
|
||||||
|
@ -56,7 +57,6 @@ import {
|
||||||
useKeyboardShortcuts,
|
useKeyboardShortcuts,
|
||||||
} from '../hooks/useKeyboardShortcuts';
|
} from '../hooks/useKeyboardShortcuts';
|
||||||
import { MediaEditor } from './MediaEditor';
|
import { MediaEditor } from './MediaEditor';
|
||||||
import { IMAGE_PNG } from '../types/MIME';
|
|
||||||
import { isImageTypeSupported } from '../util/GoogleChrome';
|
import { isImageTypeSupported } from '../util/GoogleChrome';
|
||||||
import * as KeyboardLayout from '../services/keyboardLayout';
|
import * as KeyboardLayout from '../services/keyboardLayout';
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ export type OwnProps = Readonly<{
|
||||||
groupAdmins: Array<ConversationType>;
|
groupAdmins: Array<ConversationType>;
|
||||||
groupVersion?: 1 | 2;
|
groupVersion?: 1 | 2;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
|
imageToBlurHash: typeof imageToBlurHash;
|
||||||
isFetchingUUID?: boolean;
|
isFetchingUUID?: boolean;
|
||||||
isGroupV1AndDisabled?: boolean;
|
isGroupV1AndDisabled?: boolean;
|
||||||
isMissingMandatoryProfileSharing?: boolean;
|
isMissingMandatoryProfileSharing?: boolean;
|
||||||
|
@ -174,6 +175,7 @@ export const CompositionArea = ({
|
||||||
conversationId,
|
conversationId,
|
||||||
i18n,
|
i18n,
|
||||||
onSendMessage,
|
onSendMessage,
|
||||||
|
imageToBlurHash,
|
||||||
processAttachments,
|
processAttachments,
|
||||||
removeAttachment,
|
removeAttachment,
|
||||||
theme,
|
theme,
|
||||||
|
@ -594,12 +596,14 @@ export const CompositionArea = ({
|
||||||
<MediaEditor
|
<MediaEditor
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
imageSrc={attachmentToEdit.url}
|
imageSrc={attachmentToEdit.url}
|
||||||
|
imageToBlurHash={imageToBlurHash}
|
||||||
isSending={false}
|
isSending={false}
|
||||||
onClose={() => setAttachmentToEdit(undefined)}
|
onClose={() => setAttachmentToEdit(undefined)}
|
||||||
onDone={data => {
|
onDone={({ data, contentType, blurHash }) => {
|
||||||
const newAttachment = {
|
const newAttachment = {
|
||||||
...attachmentToEdit,
|
...attachmentToEdit,
|
||||||
contentType: IMAGE_PNG,
|
contentType,
|
||||||
|
blurHash,
|
||||||
data,
|
data,
|
||||||
size: data.byteLength,
|
size: data.byteLength,
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,7 @@ const getDefaultProps = (): PropsType => ({
|
||||||
onClose: action('onClose'),
|
onClose: action('onClose'),
|
||||||
onDone: action('onDone'),
|
onDone: action('onDone'),
|
||||||
isSending: false,
|
isSending: false,
|
||||||
|
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
|
||||||
|
|
||||||
// StickerButtonProps
|
// StickerButtonProps
|
||||||
installedPacks,
|
installedPacks,
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { get, has, noop } from 'lodash';
|
||||||
|
|
||||||
import type { LocalizerType } from '../types/Util';
|
import type { LocalizerType } from '../types/Util';
|
||||||
import { ThemeType } from '../types/Util';
|
import { ThemeType } from '../types/Util';
|
||||||
|
import type { MIMEType } from '../types/MIME';
|
||||||
|
import { IMAGE_PNG } from '../types/MIME';
|
||||||
import type { Props as StickerButtonProps } from './stickers/StickerButton';
|
import type { Props as StickerButtonProps } from './stickers/StickerButton';
|
||||||
import type { ImageStateType } from '../mediaEditor/ImageStateType';
|
import type { ImageStateType } from '../mediaEditor/ImageStateType';
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ import { Slider } from './Slider';
|
||||||
import { StickerButton } from './stickers/StickerButton';
|
import { StickerButton } from './stickers/StickerButton';
|
||||||
import { Theme } from '../util/theme';
|
import { Theme } from '../util/theme';
|
||||||
import { canvasToBytes } from '../util/canvasToBytes';
|
import { canvasToBytes } from '../util/canvasToBytes';
|
||||||
|
import type { imageToBlurHash } from '../util/imageToBlurHash';
|
||||||
import { useFabricHistory } from '../mediaEditor/useFabricHistory';
|
import { useFabricHistory } from '../mediaEditor/useFabricHistory';
|
||||||
import { usePortal } from '../hooks/usePortal';
|
import { usePortal } from '../hooks/usePortal';
|
||||||
import { useUniqueId } from '../hooks/useUniqueId';
|
import { useUniqueId } from '../hooks/useUniqueId';
|
||||||
|
@ -41,13 +44,21 @@ import { AddNewLines } from './conversation/AddNewLines';
|
||||||
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
|
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
|
||||||
import { Spinner } from './Spinner';
|
import { Spinner } from './Spinner';
|
||||||
|
|
||||||
|
export type MediaEditorResultType = Readonly<{
|
||||||
|
data: Uint8Array;
|
||||||
|
contentType: MIMEType;
|
||||||
|
blurHash: string;
|
||||||
|
caption?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
doneButtonLabel?: string;
|
doneButtonLabel?: string;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
imageSrc: string;
|
imageSrc: string;
|
||||||
isSending: boolean;
|
isSending: boolean;
|
||||||
|
imageToBlurHash: typeof imageToBlurHash;
|
||||||
onClose: () => unknown;
|
onClose: () => unknown;
|
||||||
onDone: (data: Uint8Array, caption?: string | undefined) => unknown;
|
onDone: (result: MediaEditorResultType) => unknown;
|
||||||
} & Pick<StickerButtonProps, 'installedPacks' | 'recentStickers'> &
|
} & Pick<StickerButtonProps, 'installedPacks' | 'recentStickers'> &
|
||||||
(
|
(
|
||||||
| {
|
| {
|
||||||
|
@ -1115,6 +1126,7 @@ export const MediaEditor = ({
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
|
|
||||||
let data: Uint8Array;
|
let data: Uint8Array;
|
||||||
|
let blurHash: string;
|
||||||
try {
|
try {
|
||||||
const renderFabricCanvas = await cloneFabricCanvas(
|
const renderFabricCanvas = await cloneFabricCanvas(
|
||||||
fabricCanvas
|
fabricCanvas
|
||||||
|
@ -1151,6 +1163,12 @@ export const MediaEditor = ({
|
||||||
const renderedCanvas = renderFabricCanvas.toCanvasElement();
|
const renderedCanvas = renderFabricCanvas.toCanvasElement();
|
||||||
|
|
||||||
data = await canvasToBytes(renderedCanvas);
|
data = await canvasToBytes(renderedCanvas);
|
||||||
|
|
||||||
|
const blob = new Blob([data], {
|
||||||
|
type: IMAGE_PNG,
|
||||||
|
});
|
||||||
|
|
||||||
|
blurHash = await props.imageToBlurHash(blob);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
onTryClose();
|
onTryClose();
|
||||||
throw err;
|
throw err;
|
||||||
|
@ -1158,7 +1176,12 @@ export const MediaEditor = ({
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDone(data, caption !== '' ? caption : undefined);
|
onDone({
|
||||||
|
contentType: IMAGE_PNG,
|
||||||
|
data,
|
||||||
|
caption: caption !== '' ? caption : undefined,
|
||||||
|
blurHash,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
theme={Theme.Dark}
|
theme={Theme.Dark}
|
||||||
variant={ButtonVariant.Primary}
|
variant={ButtonVariant.Primary}
|
||||||
|
|
|
@ -37,6 +37,7 @@ export default {
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
i18n: { defaultValue: i18n },
|
i18n: { defaultValue: i18n },
|
||||||
|
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
|
||||||
installedPacks: {
|
installedPacks: {
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,8 +14,9 @@ import type { LocalizerType } from '../types/Util';
|
||||||
import type { Props as StickerButtonProps } from './stickers/StickerButton';
|
import type { Props as StickerButtonProps } from './stickers/StickerButton';
|
||||||
import type { PropsType as SendStoryModalPropsType } from './SendStoryModal';
|
import type { PropsType as SendStoryModalPropsType } from './SendStoryModal';
|
||||||
import type { UUIDStringType } from '../types/UUID';
|
import type { UUIDStringType } from '../types/UUID';
|
||||||
|
import type { imageToBlurHash } from '../util/imageToBlurHash';
|
||||||
|
|
||||||
import { IMAGE_JPEG, TEXT_ATTACHMENT } from '../types/MIME';
|
import { TEXT_ATTACHMENT } from '../types/MIME';
|
||||||
import { isVideoAttachment } from '../types/Attachment';
|
import { isVideoAttachment } from '../types/Attachment';
|
||||||
import { SendStoryModal } from './SendStoryModal';
|
import { SendStoryModal } from './SendStoryModal';
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ export type PropsType = {
|
||||||
conversationIds: Array<string>,
|
conversationIds: Array<string>,
|
||||||
attachment: AttachmentType
|
attachment: AttachmentType
|
||||||
) => unknown;
|
) => unknown;
|
||||||
|
imageToBlurHash: typeof imageToBlurHash;
|
||||||
processAttachment: (
|
processAttachment: (
|
||||||
file: File
|
file: File
|
||||||
) => Promise<void | InMemoryAttachmentDraftType>;
|
) => Promise<void | InMemoryAttachmentDraftType>;
|
||||||
|
@ -80,6 +82,7 @@ export const StoryCreator = ({
|
||||||
groupStories,
|
groupStories,
|
||||||
hasFirstStoryPostExperience,
|
hasFirstStoryPostExperience,
|
||||||
i18n,
|
i18n,
|
||||||
|
imageToBlurHash,
|
||||||
installedPacks,
|
installedPacks,
|
||||||
isSending,
|
isSending,
|
||||||
linkPreview,
|
linkPreview,
|
||||||
|
@ -107,6 +110,7 @@ export const StoryCreator = ({
|
||||||
const [draftAttachment, setDraftAttachment] = useState<
|
const [draftAttachment, setDraftAttachment] = useState<
|
||||||
AttachmentType | undefined
|
AttachmentType | undefined
|
||||||
>();
|
>();
|
||||||
|
const [isReadyToSend, setIsReadyToSend] = useState(false);
|
||||||
const [attachmentUrl, setAttachmentUrl] = useState<string | undefined>();
|
const [attachmentUrl, setAttachmentUrl] = useState<string | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -123,11 +127,16 @@ export const StoryCreator = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDraftAttachment(attachment);
|
||||||
if (isVideoAttachment(attachment)) {
|
if (isVideoAttachment(attachment)) {
|
||||||
setDraftAttachment(attachment);
|
setAttachmentUrl(undefined);
|
||||||
|
setIsReadyToSend(true);
|
||||||
} else if (attachment && has(attachment, 'data')) {
|
} else if (attachment && has(attachment, 'data')) {
|
||||||
url = URL.createObjectURL(new Blob([get(attachment, 'data')]));
|
url = URL.createObjectURL(new Blob([get(attachment, 'data')]));
|
||||||
setAttachmentUrl(url);
|
setAttachmentUrl(url);
|
||||||
|
|
||||||
|
// Needs editing in MediaEditor
|
||||||
|
setIsReadyToSend(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,12 +151,17 @@ export const StoryCreator = ({
|
||||||
}, [file, processAttachment]);
|
}, [file, processAttachment]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sendStoryModalOpenStateChanged(Boolean(draftAttachment));
|
if (draftAttachment === undefined) {
|
||||||
|
sendStoryModalOpenStateChanged(false);
|
||||||
|
setIsReadyToSend(false);
|
||||||
|
} else {
|
||||||
|
sendStoryModalOpenStateChanged(true);
|
||||||
|
}
|
||||||
}, [draftAttachment, sendStoryModalOpenStateChanged]);
|
}, [draftAttachment, sendStoryModalOpenStateChanged]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{draftAttachment && (
|
{draftAttachment && isReadyToSend && (
|
||||||
<SendStoryModal
|
<SendStoryModal
|
||||||
draftAttachment={draftAttachment}
|
draftAttachment={draftAttachment}
|
||||||
candidateConversations={candidateConversations}
|
candidateConversations={candidateConversations}
|
||||||
|
@ -182,7 +196,7 @@ export const StoryCreator = ({
|
||||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{attachmentUrl && (
|
{draftAttachment && !isReadyToSend && attachmentUrl && (
|
||||||
<MediaEditor
|
<MediaEditor
|
||||||
doneButtonLabel={i18n('next2')}
|
doneButtonLabel={i18n('next2')}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
@ -192,13 +206,17 @@ export const StoryCreator = ({
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
supportsCaption
|
supportsCaption
|
||||||
renderCompositionTextArea={renderCompositionTextArea}
|
renderCompositionTextArea={renderCompositionTextArea}
|
||||||
onDone={(data, caption) => {
|
imageToBlurHash={imageToBlurHash}
|
||||||
|
onDone={({ contentType, data, blurHash, caption }) => {
|
||||||
setDraftAttachment({
|
setDraftAttachment({
|
||||||
contentType: IMAGE_JPEG,
|
...draftAttachment,
|
||||||
|
contentType,
|
||||||
data,
|
data,
|
||||||
size: data.byteLength,
|
size: data.byteLength,
|
||||||
|
blurHash,
|
||||||
caption,
|
caption,
|
||||||
});
|
});
|
||||||
|
setIsReadyToSend(true);
|
||||||
}}
|
}}
|
||||||
recentStickers={recentStickers}
|
recentStickers={recentStickers}
|
||||||
/>
|
/>
|
||||||
|
@ -216,6 +234,7 @@ export const StoryCreator = ({
|
||||||
textAttachment,
|
textAttachment,
|
||||||
size: textAttachment.text?.length || 0,
|
size: textAttachment.text?.length || 0,
|
||||||
});
|
});
|
||||||
|
setIsReadyToSend(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { CompositionArea } from '../../components/CompositionArea';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
||||||
import { dropNull } from '../../util/dropNull';
|
import { dropNull } from '../../util/dropNull';
|
||||||
|
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
||||||
|
|
||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
import { selectRecentEmojis } from '../selectors/emojis';
|
import { selectRecentEmojis } from '../selectors/emojis';
|
||||||
|
@ -89,6 +90,8 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||||
recordingState: state.audioRecorder.recordingState,
|
recordingState: state.audioRecorder.recordingState,
|
||||||
// AttachmentsList
|
// AttachmentsList
|
||||||
draftAttachments,
|
draftAttachments,
|
||||||
|
// MediaEditor
|
||||||
|
imageToBlurHash,
|
||||||
// MediaQualitySelector
|
// MediaQualitySelector
|
||||||
shouldSendHighQualityAttachments,
|
shouldSendHighQualityAttachments,
|
||||||
// StagedLinkPreview
|
// StagedLinkPreview
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { getHasSetMyStoriesPrivacy } from '../selectors/items';
|
||||||
import { getLinkPreview } from '../selectors/linkPreviews';
|
import { getLinkPreview } from '../selectors/linkPreviews';
|
||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
import { processAttachment } from '../../util/processAttachment';
|
import { processAttachment } from '../../util/processAttachment';
|
||||||
|
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
||||||
import { useConversationsActions } from '../ducks/conversations';
|
import { useConversationsActions } from '../ducks/conversations';
|
||||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||||
import { useLinkPreviewActions } from '../ducks/linkPreviews';
|
import { useLinkPreviewActions } from '../ducks/linkPreviews';
|
||||||
|
@ -91,6 +92,7 @@ export function SmartStoryCreator(): JSX.Element | null {
|
||||||
groupStories={groupStories}
|
groupStories={groupStories}
|
||||||
hasFirstStoryPostExperience={!hasSetMyStoriesPrivacy}
|
hasFirstStoryPostExperience={!hasSetMyStoriesPrivacy}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
imageToBlurHash={imageToBlurHash}
|
||||||
installedPacks={installedPacks}
|
installedPacks={installedPacks}
|
||||||
isSending={isSending}
|
isSending={isSending}
|
||||||
linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)}
|
linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)}
|
||||||
|
|
Loading…
Reference in a new issue