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';
|
2022-11-22 18:43:43 +00:00
|
|
|
import * as Errors from '../../types/errors';
|
2022-07-21 16:26:11 +00:00
|
|
|
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';
|
2022-07-21 16:26:11 +00:00
|
|
|
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';
|
2023-04-10 16:31:45 +00:00
|
|
|
import type { HydratedBodyRangeMention } from '../../types/BodyRange';
|
|
|
|
import { applyRangesForText, BodyRange } from '../../types/BodyRange';
|
2022-07-21 16:26:11 +00:00
|
|
|
|
|
|
|
function renderMentions(
|
|
|
|
message: ForwardMessagePropsType,
|
|
|
|
conversationSelector: GetConversationByIdType
|
|
|
|
): string | undefined {
|
|
|
|
const { text } = message;
|
|
|
|
|
|
|
|
if (!text) {
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bodyRanges = processBodyRanges(message, {
|
|
|
|
conversationSelector,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (bodyRanges && bodyRanges.length) {
|
2023-04-10 16:31:45 +00:00
|
|
|
return applyRangesForText({
|
|
|
|
mentions: bodyRanges.filter<HydratedBodyRangeMention>(
|
|
|
|
BodyRange.isMention
|
|
|
|
),
|
|
|
|
spoilers: [],
|
|
|
|
text,
|
|
|
|
});
|
2022-07-21 16:26:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2022-07-21 16:26:11 +00:00
|
|
|
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) {
|
2022-11-22 18:43:43 +00:00
|
|
|
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,
|
2022-10-06 20:22:59 +00:00
|
|
|
{ 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}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|