signal-desktop/ts/components/StoryCreator.tsx

181 lines
5.4 KiB
TypeScript
Raw Normal View History

2022-06-17 00:48:57 +00:00
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2022-08-04 19:23:24 +00:00
import React, { useEffect, useState } from 'react';
2022-06-17 00:48:57 +00:00
import { get, has } from 'lodash';
2022-08-04 19:23:24 +00:00
import type {
AttachmentType,
InMemoryAttachmentDraftType,
} from '../types/Attachment';
2022-08-02 19:31:55 +00:00
import type { ConversationType } from '../state/ducks/conversations';
2022-08-04 19:23:24 +00:00
import type { LinkPreviewSourceType } from '../types/LinkPreview';
2022-06-17 00:48:57 +00:00
import type { LinkPreviewType } from '../types/message/LinkPreviews';
import type { LocalizerType } from '../types/Util';
2022-08-10 18:37:19 +00:00
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
2022-08-04 19:23:24 +00:00
import type { Props as StickerButtonProps } from './stickers/StickerButton';
2022-08-02 19:31:55 +00:00
import type { StoryDistributionListDataType } from '../state/ducks/storyDistributionLists';
import type { UUIDStringType } from '../types/UUID';
2022-06-17 00:48:57 +00:00
2022-08-04 19:23:24 +00:00
import { IMAGE_JPEG, TEXT_ATTACHMENT } from '../types/MIME';
import { isVideoAttachment } from '../types/Attachment';
2022-08-02 19:31:55 +00:00
import { SendStoryModal } from './SendStoryModal';
2022-08-04 19:23:24 +00:00
import { MediaEditor } from './MediaEditor';
import { TextStoryCreator } from './TextStoryCreator';
2022-06-17 00:48:57 +00:00
export type PropsType = {
2022-08-10 18:37:19 +00:00
candidateConversations: Array<ConversationType>;
2022-06-17 00:48:57 +00:00
debouncedMaybeGrabLinkPreview: (
message: string,
source: LinkPreviewSourceType
) => unknown;
2022-08-02 19:31:55 +00:00
distributionLists: Array<StoryDistributionListDataType>;
2022-08-04 19:23:24 +00:00
file?: File;
2022-08-10 18:37:19 +00:00
getPreferredBadge: PreferredBadgeSelectorType;
groupConversations: Array<ConversationType>;
groupStories: Array<ConversationType>;
2022-06-17 00:48:57 +00:00
i18n: LocalizerType;
linkPreview?: LinkPreviewType;
2022-08-02 19:31:55 +00:00
me: ConversationType;
2022-06-17 00:48:57 +00:00
onClose: () => unknown;
2022-08-10 18:37:19 +00:00
onDistributionListCreated: (
name: string,
viewerUuids: Array<UUIDStringType>
) => unknown;
onSelectedStoryList: (memberUuids: Array<string>) => unknown;
2022-08-02 19:31:55 +00:00
onSend: (
listIds: Array<UUIDStringType>,
2022-08-09 03:26:21 +00:00
conversationIds: Array<string>,
2022-08-04 19:23:24 +00:00
attachment: AttachmentType
2022-08-02 19:31:55 +00:00
) => unknown;
2022-08-04 19:23:24 +00:00
processAttachment: (
file: File
) => Promise<void | InMemoryAttachmentDraftType>;
sendStoryModalOpenStateChanged: (isOpen: boolean) => unknown;
2022-08-02 19:31:55 +00:00
signalConnections: Array<ConversationType>;
2022-08-10 18:37:19 +00:00
tagGroupsAsNewGroupStory: (cids: Array<string>) => unknown;
2022-08-04 19:23:24 +00:00
} & Pick<StickerButtonProps, 'installedPacks' | 'recentStickers'>;
2022-06-17 00:48:57 +00:00
export const StoryCreator = ({
2022-08-10 18:37:19 +00:00
candidateConversations,
2022-06-17 00:48:57 +00:00
debouncedMaybeGrabLinkPreview,
2022-08-02 19:31:55 +00:00
distributionLists,
2022-08-04 19:23:24 +00:00
file,
2022-08-10 18:37:19 +00:00
getPreferredBadge,
groupConversations,
groupStories,
2022-06-17 00:48:57 +00:00
i18n,
2022-08-04 19:23:24 +00:00
installedPacks,
2022-06-17 00:48:57 +00:00
linkPreview,
2022-08-02 19:31:55 +00:00
me,
2022-06-17 00:48:57 +00:00
onClose,
2022-08-10 18:37:19 +00:00
onDistributionListCreated,
onSelectedStoryList,
2022-08-02 19:31:55 +00:00
onSend,
2022-08-04 19:23:24 +00:00
processAttachment,
recentStickers,
sendStoryModalOpenStateChanged,
2022-08-02 19:31:55 +00:00
signalConnections,
2022-08-10 18:37:19 +00:00
tagGroupsAsNewGroupStory,
2022-06-17 00:48:57 +00:00
}: PropsType): JSX.Element => {
2022-08-04 19:23:24 +00:00
const [draftAttachment, setDraftAttachment] = useState<
AttachmentType | undefined
>();
const [attachmentUrl, setAttachmentUrl] = useState<string | undefined>();
2022-06-17 00:48:57 +00:00
useEffect(() => {
2022-08-04 19:23:24 +00:00
let url: string | undefined;
let unmounted = false;
2022-06-17 00:48:57 +00:00
2022-08-04 19:23:24 +00:00
async function loadAttachment(): Promise<void> {
if (!file || unmounted) {
return;
}
2022-06-17 00:48:57 +00:00
2022-08-04 19:23:24 +00:00
const attachment = await processAttachment(file);
if (!attachment || unmounted) {
return;
2022-06-17 00:48:57 +00:00
}
2022-08-04 19:23:24 +00:00
if (isVideoAttachment(attachment)) {
setDraftAttachment(attachment);
} else if (attachment && has(attachment, 'data')) {
url = URL.createObjectURL(new Blob([get(attachment, 'data')]));
setAttachmentUrl(url);
2022-06-17 00:48:57 +00:00
}
2022-08-04 19:23:24 +00:00
}
2022-06-17 00:48:57 +00:00
2022-08-04 19:23:24 +00:00
loadAttachment();
2022-06-17 00:48:57 +00:00
return () => {
2022-08-04 19:23:24 +00:00
unmounted = true;
if (url) {
URL.revokeObjectURL(url);
}
2022-06-17 00:48:57 +00:00
};
2022-08-04 19:23:24 +00:00
}, [file, processAttachment]);
2022-08-02 19:31:55 +00:00
useEffect(() => {
sendStoryModalOpenStateChanged(Boolean(draftAttachment));
}, [draftAttachment, sendStoryModalOpenStateChanged]);
2022-06-17 00:48:57 +00:00
return (
2022-08-02 19:31:55 +00:00
<>
2022-08-04 19:23:24 +00:00
{draftAttachment && (
2022-08-02 19:31:55 +00:00
<SendStoryModal
2022-08-10 18:37:19 +00:00
candidateConversations={candidateConversations}
2022-08-02 19:31:55 +00:00
distributionLists={distributionLists}
2022-08-10 18:37:19 +00:00
getPreferredBadge={getPreferredBadge}
groupConversations={groupConversations}
groupStories={groupStories}
2022-08-02 19:31:55 +00:00
i18n={i18n}
me={me}
2022-08-04 19:23:24 +00:00
onClose={() => setDraftAttachment(undefined)}
2022-08-10 18:37:19 +00:00
onDistributionListCreated={onDistributionListCreated}
onSelectedStoryList={onSelectedStoryList}
2022-08-10 18:37:19 +00:00
onSend={(listIds, groupIds) => {
onSend(listIds, groupIds, draftAttachment);
2022-08-04 19:23:24 +00:00
setDraftAttachment(undefined);
2022-08-02 19:31:55 +00:00
onClose();
}}
signalConnections={signalConnections}
2022-08-10 18:37:19 +00:00
tagGroupsAsNewGroupStory={tagGroupsAsNewGroupStory}
2022-08-02 19:31:55 +00:00
/>
)}
2022-08-04 19:23:24 +00:00
{attachmentUrl && (
<MediaEditor
2022-08-10 18:37:19 +00:00
doneButtonLabel={i18n('next2')}
2022-08-04 19:23:24 +00:00
i18n={i18n}
imageSrc={attachmentUrl}
installedPacks={installedPacks}
onClose={onClose}
onDone={data => {
setDraftAttachment({
contentType: IMAGE_JPEG,
data,
size: data.byteLength,
});
}}
recentStickers={recentStickers}
/>
)}
{!file && (
<TextStoryCreator
debouncedMaybeGrabLinkPreview={debouncedMaybeGrabLinkPreview}
i18n={i18n}
linkPreview={linkPreview}
onClose={onClose}
onDone={textAttachment => {
setDraftAttachment({
contentType: TEXT_ATTACHMENT,
textAttachment,
size: textAttachment.text?.length || 0,
});
}}
/>
)}
2022-08-02 19:31:55 +00:00
</>
2022-06-17 00:48:57 +00:00
);
};