signal-desktop/ts/state/smart/ForwardMessagesModal.tsx

171 lines
5.5 KiB
TypeScript
Raw Normal View History

2023-01-03 19:55:46 +00:00
// Copyright 2021 Signal Messenger, LLC
2021-04-27 22:35:35 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
2023-03-20 22:23:53 +00:00
import React, { useState } from 'react';
2022-07-01 00:52:03 +00:00
import { useSelector } from 'react-redux';
2023-03-20 22:23:53 +00:00
import type {
ForwardMessagePropsType,
ForwardMessagesPropsType,
} from '../ducks/globalModals';
2022-06-17 00:48:57 +00:00
import type { StateType } from '../reducer';
2022-07-01 00:52:03 +00:00
import * as log from '../../logging/log';
2023-03-20 22:23:53 +00:00
import { ForwardMessagesModal } from '../../components/ForwardMessagesModal';
2022-06-17 00:48:57 +00:00
import { LinkPreviewSourceType } from '../../types/LinkPreview';
import * as Errors from '../../types/errors';
import type { GetConversationByIdType } from '../selectors/conversations';
import {
getAllComposableConversations,
getConversationSelector,
} from '../selectors/conversations';
2022-06-17 00:48:57 +00:00
import { getIntl, getTheme, getRegionCode } from '../selectors/user';
import { getLinkPreview } from '../selectors/linkPreviews';
2022-07-01 00:52:03 +00:00
import { getMessageById } from '../../messages/getMessageById';
2022-06-17 00:48:57 +00:00
import { getPreferredBadgeSelector } from '../selectors/badges';
2023-03-20 22:23:53 +00:00
import type {
ForwardMessageData,
MessageForwardDraft,
} from '../../util/maybeForwardMessages';
import { maybeForwardMessages } from '../../util/maybeForwardMessages';
2022-07-01 00:52:03 +00:00
import {
maybeGrabLinkPreview,
resetLinkPreview,
} from '../../services/LinkPreview';
import { useGlobalModalActions } from '../ducks/globalModals';
import { useLinkPreviewActions } from '../ducks/linkPreviews';
import { processBodyRanges } from '../selectors/message';
2022-10-04 23:17:15 +00:00
import { SmartCompositionTextArea } from './CompositionTextArea';
2023-03-20 22:23:53 +00:00
import { useToastActions } from '../ducks/toast';
import type { HydratedBodyRangeMention } from '../../types/BodyRange';
import { applyRangesForText, BodyRange } from '../../types/BodyRange';
function renderMentions(
message: ForwardMessagePropsType,
conversationSelector: GetConversationByIdType
): string | undefined {
const { text } = message;
if (!text) {
return text;
}
const bodyRanges = processBodyRanges(message, {
conversationSelector,
});
if (bodyRanges && bodyRanges.length) {
return applyRangesForText({
mentions: bodyRanges.filter<HydratedBodyRangeMention>(
BodyRange.isMention
),
spoilers: [],
text,
});
}
return text;
}
2021-04-27 22:35:35 +00:00
2023-03-20 22:23:53 +00:00
export function SmartForwardMessagesModal(): JSX.Element | null {
const forwardMessagesProps = useSelector<
2022-07-01 00:52:03 +00:00
StateType,
2023-03-20 22:23:53 +00:00
ForwardMessagesPropsType | undefined
>(state => state.globalModals.forwardMessagesProps);
2022-07-01 00:52:03 +00:00
const candidateConversations = useSelector(getAllComposableConversations);
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const getConversation = useSelector(getConversationSelector);
2022-07-01 00:52:03 +00:00
const i18n = useSelector(getIntl);
const linkPreviewForSource = useSelector(getLinkPreview);
const regionCode = useSelector(getRegionCode);
const theme = useSelector(getTheme);
2021-04-27 22:35:35 +00:00
2022-07-01 00:52:03 +00:00
const { removeLinkPreview } = useLinkPreviewActions();
2023-03-20 22:23:53 +00:00
const { toggleForwardMessagesModal } = useGlobalModalActions();
const { showToast } = useToastActions();
2021-04-27 22:35:35 +00:00
2023-03-20 22:23:53 +00:00
const [drafts, setDrafts] = useState<ReadonlyArray<MessageForwardDraft>>(
() => {
return (
forwardMessagesProps?.messages.map((props): MessageForwardDraft => {
return {
originalMessageId: props.id,
attachments: props.attachments ?? [],
messageBody: renderMentions(props, getConversation),
isSticker: Boolean(props.isSticker),
hasContact: Boolean(props.contact),
previews: props.previews ?? [],
};
}) ?? []
);
}
);
if (!drafts.length) {
2022-07-01 00:52:03 +00:00
return null;
}
2021-04-27 22:35:35 +00:00
2022-07-01 00:52:03 +00:00
function closeModal() {
resetLinkPreview();
2023-03-20 22:23:53 +00:00
toggleForwardMessagesModal();
2022-07-01 00:52:03 +00:00
}
2021-04-27 22:35:35 +00:00
2022-07-01 00:52:03 +00:00
return (
2023-03-20 22:23:53 +00:00
<ForwardMessagesModal
drafts={drafts}
2022-07-01 00:52:03 +00:00
candidateConversations={candidateConversations}
2023-03-20 22:23:53 +00:00
doForwardMessages={async (conversationIds, finalDrafts) => {
2022-07-01 00:52:03 +00:00
try {
2023-03-20 22:23:53 +00:00
const messages = await Promise.all(
finalDrafts.map(async (draft): Promise<ForwardMessageData> => {
const message = await getMessageById(draft.originalMessageId);
if (message == null) {
throw new Error('No message found');
}
return {
draft,
originalMessage: message.attributes,
};
})
);
2022-07-01 00:52:03 +00:00
2023-03-20 22:23:53 +00:00
const didForwardSuccessfully = await maybeForwardMessages(
messages,
conversationIds
2022-07-01 00:52:03 +00:00
);
if (didForwardSuccessfully) {
closeModal();
2023-03-20 22:23:53 +00:00
forwardMessagesProps?.onForward?.();
2022-07-01 00:52:03 +00:00
}
} catch (err) {
log.warn('doForwardMessage', Errors.toLogFormat(err));
2022-07-01 00:52:03 +00:00
}
}}
2023-03-20 22:23:53 +00:00
linkPreviewForSource={linkPreviewForSource}
2022-07-01 00:52:03 +00:00
getPreferredBadge={getPreferredBadge}
i18n={i18n}
onClose={closeModal}
2023-03-20 22:23:53 +00:00
onChange={(updatedDrafts, caretLocation) => {
setDrafts(updatedDrafts);
const isLonelyDraft = updatedDrafts.length === 1;
const lonelyDraft = isLonelyDraft ? updatedDrafts[0] : null;
if (lonelyDraft == null) {
return;
}
const attachmentsLength = lonelyDraft.attachments?.length ?? 0;
if (attachmentsLength === 0) {
2022-07-01 00:52:03 +00:00
maybeGrabLinkPreview(
2023-03-20 22:23:53 +00:00
lonelyDraft.messageBody ?? '',
2022-07-01 00:52:03 +00:00
LinkPreviewSourceType.ForwardMessageModal,
{ caretLocation }
2022-07-01 00:52:03 +00:00
);
}
}}
regionCode={regionCode}
2022-10-04 23:17:15 +00:00
RenderCompositionTextArea={SmartCompositionTextArea}
2022-07-01 00:52:03 +00:00
removeLinkPreview={removeLinkPreview}
2023-03-20 22:23:53 +00:00
showToast={showToast}
2022-07-01 00:52:03 +00:00
theme={theme}
/>
);
}