2023-01-03 19:55:46 +00:00
|
|
|
// Copyright 2019 Signal Messenger, LLC
|
2020-10-30 20:34:04 +00:00
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2024-03-13 20:44:13 +00:00
|
|
|
import React, { useCallback, useMemo, memo } from 'react';
|
2024-03-12 16:29:31 +00:00
|
|
|
import { useSelector } from 'react-redux';
|
2021-10-26 19:15:33 +00:00
|
|
|
import { CompositionArea } from '../../components/CompositionArea';
|
2024-03-12 16:29:31 +00:00
|
|
|
import { useContactNameData } from '../../components/conversation/ContactName';
|
2023-09-14 17:04:48 +00:00
|
|
|
import type {
|
|
|
|
DraftBodyRanges,
|
|
|
|
HydratedBodyRangesType,
|
|
|
|
} from '../../types/BodyRange';
|
2024-03-12 16:29:31 +00:00
|
|
|
import { hydrateRanges } from '../../types/BodyRange';
|
|
|
|
import { strictAssert } from '../../util/assert';
|
|
|
|
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
|
2022-11-16 21:41:38 +00:00
|
|
|
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
2024-03-12 16:29:31 +00:00
|
|
|
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
|
|
|
import { isSignalConversation } from '../../util/isSignalConversation';
|
2023-09-01 17:03:44 +00:00
|
|
|
import {
|
2024-03-12 16:29:31 +00:00
|
|
|
getErrorDialogAudioRecorderType,
|
|
|
|
getRecordingState,
|
|
|
|
} from '../selectors/audioRecorder';
|
|
|
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
|
|
|
import { getComposerStateForConversationIdSelector } from '../selectors/composer';
|
2021-06-17 17:15:10 +00:00
|
|
|
import {
|
|
|
|
getConversationSelector,
|
2021-07-20 20:18:35 +00:00
|
|
|
getGroupAdminsSelector,
|
2023-07-26 22:23:32 +00:00
|
|
|
getHasPanelOpen,
|
2023-05-12 00:27:19 +00:00
|
|
|
getLastEditableMessageId,
|
2023-03-20 22:23:53 +00:00
|
|
|
getSelectedMessageIds,
|
2021-06-17 17:15:10 +00:00
|
|
|
isMissingRequiredProfileSharing,
|
|
|
|
} from '../selectors/conversations';
|
2024-03-12 16:29:31 +00:00
|
|
|
import { selectRecentEmojis } from '../selectors/emojis';
|
|
|
|
import {
|
|
|
|
getDefaultConversationColor,
|
|
|
|
getEmojiSkinTone,
|
|
|
|
getShowStickerPickerHint,
|
|
|
|
getShowStickersIntroduction,
|
|
|
|
getTextFormattingEnabled,
|
|
|
|
} from '../selectors/items';
|
2021-06-25 16:08:16 +00:00
|
|
|
import { getPropsForQuote } from '../selectors/message';
|
2019-05-16 22:32:11 +00:00
|
|
|
import {
|
2019-05-24 01:27:42 +00:00
|
|
|
getBlessedStickerPacks,
|
2019-05-16 22:32:11 +00:00
|
|
|
getInstalledStickerPacks,
|
2019-05-24 01:27:42 +00:00
|
|
|
getKnownStickerPacks,
|
2019-05-16 22:32:11 +00:00
|
|
|
getReceivedStickerPacks,
|
|
|
|
getRecentStickers,
|
2024-03-12 16:29:31 +00:00
|
|
|
getRecentlyInstalledStickerPack,
|
2019-05-16 22:32:11 +00:00
|
|
|
} from '../selectors/stickers';
|
2024-03-12 16:29:31 +00:00
|
|
|
import {
|
|
|
|
getIntl,
|
|
|
|
getPlatform,
|
|
|
|
getTheme,
|
|
|
|
getUserConversationId,
|
|
|
|
} from '../selectors/user';
|
2023-03-02 20:55:40 +00:00
|
|
|
import type { SmartCompositionRecordingProps } from './CompositionRecording';
|
|
|
|
import { SmartCompositionRecording } from './CompositionRecording';
|
|
|
|
import type { SmartCompositionRecordingDraftProps } from './CompositionRecordingDraft';
|
|
|
|
import { SmartCompositionRecordingDraft } from './CompositionRecordingDraft';
|
2024-03-12 16:29:31 +00:00
|
|
|
import { useItemsActions } from '../ducks/items';
|
|
|
|
import { useComposerActions } from '../ducks/composer';
|
|
|
|
import { useConversationsActions } from '../ducks/conversations';
|
|
|
|
import { useAudioRecorderActions } from '../ducks/audioRecorder';
|
|
|
|
import { useEmojisActions } from '../ducks/emojis';
|
|
|
|
import { useGlobalModalActions } from '../ducks/globalModals';
|
|
|
|
import { useStickersActions } from '../ducks/stickers';
|
|
|
|
import { useToastActions } from '../ducks/toast';
|
2024-06-13 23:22:07 +00:00
|
|
|
import { isShowingAnyModal } from '../selectors/globalModals';
|
2019-08-07 00:40:25 +00:00
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
function renderSmartCompositionRecording(
|
|
|
|
recProps: SmartCompositionRecordingProps
|
|
|
|
) {
|
|
|
|
return <SmartCompositionRecording {...recProps} />;
|
|
|
|
}
|
2021-10-05 16:47:06 +00:00
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
function renderSmartCompositionRecordingDraft(
|
|
|
|
draftProps: SmartCompositionRecordingDraftProps
|
|
|
|
) {
|
|
|
|
return <SmartCompositionRecordingDraft {...draftProps} />;
|
|
|
|
}
|
2019-08-07 00:40:25 +00:00
|
|
|
|
2024-03-13 20:44:13 +00:00
|
|
|
export const SmartCompositionArea = memo(function SmartCompositionArea({
|
|
|
|
id,
|
|
|
|
}: {
|
|
|
|
id: string;
|
|
|
|
}) {
|
2024-03-12 16:29:31 +00:00
|
|
|
const conversationSelector = useSelector(getConversationSelector);
|
2021-06-25 16:08:16 +00:00
|
|
|
const conversation = conversationSelector(id);
|
2024-03-12 16:29:31 +00:00
|
|
|
strictAssert(conversation, `Conversation id ${id} not found!`);
|
2019-08-07 00:40:25 +00:00
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
const i18n = useSelector(getIntl);
|
|
|
|
const theme = useSelector(getTheme);
|
|
|
|
const skinTone = useSelector(getEmojiSkinTone);
|
|
|
|
const recentEmojis = useSelector(selectRecentEmojis);
|
|
|
|
const selectedMessageIds = useSelector(getSelectedMessageIds);
|
|
|
|
const isFormattingEnabled = useSelector(getTextFormattingEnabled);
|
|
|
|
const lastEditableMessageId = useSelector(getLastEditableMessageId);
|
|
|
|
const receivedPacks = useSelector(getReceivedStickerPacks);
|
|
|
|
const installedPacks = useSelector(getInstalledStickerPacks);
|
|
|
|
const blessedPacks = useSelector(getBlessedStickerPacks);
|
|
|
|
const knownPacks = useSelector(getKnownStickerPacks);
|
|
|
|
const platform = useSelector(getPlatform);
|
|
|
|
const shouldHidePopovers = useSelector(getHasPanelOpen);
|
|
|
|
const installedPack = useSelector(getRecentlyInstalledStickerPack);
|
|
|
|
const recentStickers = useSelector(getRecentStickers);
|
|
|
|
const showStickersIntroduction = useSelector(getShowStickersIntroduction);
|
|
|
|
const showStickerPickerHint = useSelector(getShowStickerPickerHint);
|
|
|
|
const recordingState = useSelector(getRecordingState);
|
|
|
|
const errorDialogAudioRecorderType = useSelector(
|
|
|
|
getErrorDialogAudioRecorderType
|
2019-05-16 22:32:11 +00:00
|
|
|
);
|
2024-06-13 23:22:07 +00:00
|
|
|
const hasGlobalModalOpen = useSelector(isShowingAnyModal);
|
|
|
|
const hasPanelOpen = useSelector(getHasPanelOpen);
|
2024-03-12 16:29:31 +00:00
|
|
|
const getGroupAdmins = useSelector(getGroupAdminsSelector);
|
|
|
|
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
|
|
|
const composerStateForConversationIdSelector = useSelector(
|
|
|
|
getComposerStateForConversationIdSelector
|
2021-09-13 02:36:41 +00:00
|
|
|
);
|
2023-04-20 16:31:59 +00:00
|
|
|
const composerState = composerStateForConversationIdSelector(id);
|
2024-03-12 16:29:31 +00:00
|
|
|
const { announcementsOnly, areWeAdmin, draftEditMessage, draftBodyRanges } =
|
|
|
|
conversation;
|
2021-06-25 16:08:16 +00:00
|
|
|
const {
|
|
|
|
attachments: draftAttachments,
|
2022-12-08 23:56:17 +00:00
|
|
|
focusCounter,
|
2022-12-08 07:43:48 +00:00
|
|
|
isDisabled,
|
2021-06-25 16:08:16 +00:00
|
|
|
linkPreviewLoading,
|
|
|
|
linkPreviewResult,
|
2022-12-08 07:43:48 +00:00
|
|
|
messageCompositionId,
|
2023-04-05 22:06:16 +00:00
|
|
|
sendCounter,
|
2021-06-25 16:08:16 +00:00
|
|
|
shouldSendHighQualityAttachments,
|
2023-04-20 16:31:59 +00:00
|
|
|
} = composerState;
|
|
|
|
|
2024-06-13 23:22:07 +00:00
|
|
|
const isActive = useMemo(() => {
|
|
|
|
return !hasGlobalModalOpen && !hasPanelOpen;
|
|
|
|
}, [hasGlobalModalOpen, hasPanelOpen]);
|
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
const groupAdmins = useMemo(() => {
|
|
|
|
return getGroupAdmins(id);
|
|
|
|
}, [getGroupAdmins, id]);
|
|
|
|
|
|
|
|
const addedBy = useMemo(() => {
|
|
|
|
if (conversation.type === 'group') {
|
|
|
|
return getAddedByForOurPendingInvitation(conversation);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}, [conversation]);
|
|
|
|
|
|
|
|
const conversationName = useContactNameData(conversation);
|
|
|
|
strictAssert(conversationName, 'conversationName is required');
|
|
|
|
const addedByName = useContactNameData(addedBy);
|
|
|
|
|
|
|
|
const hydratedDraftBodyRanges = useMemo(() => {
|
|
|
|
return hydrateRanges(draftBodyRanges, conversationSelector);
|
|
|
|
}, [conversationSelector, draftBodyRanges]);
|
|
|
|
|
|
|
|
const convertDraftBodyRangesIntoHydrated = useCallback(
|
|
|
|
(
|
|
|
|
bodyRanges: DraftBodyRanges | undefined
|
|
|
|
): HydratedBodyRangesType | undefined => {
|
|
|
|
return hydrateRanges(bodyRanges, conversationSelector);
|
|
|
|
},
|
|
|
|
[conversationSelector]
|
|
|
|
);
|
|
|
|
|
2023-04-20 16:31:59 +00:00
|
|
|
let { quotedMessage } = composerState;
|
|
|
|
if (!quotedMessage && draftEditMessage?.quote) {
|
|
|
|
quotedMessage = {
|
|
|
|
conversationId: id,
|
|
|
|
quote: draftEditMessage.quote,
|
|
|
|
};
|
|
|
|
}
|
2021-06-25 16:08:16 +00:00
|
|
|
|
2024-03-15 14:38:26 +00:00
|
|
|
const ourConversationId = useSelector(getUserConversationId);
|
|
|
|
const defaultConversationColor = useSelector(getDefaultConversationColor);
|
|
|
|
|
|
|
|
const quotedMessageProps = useMemo(() => {
|
2024-03-12 16:29:31 +00:00
|
|
|
return quotedMessage
|
2021-08-11 23:06:20 +00:00
|
|
|
? getPropsForQuote(quotedMessage, {
|
2021-06-25 16:08:16 +00:00
|
|
|
conversationSelector,
|
2024-03-15 14:38:26 +00:00
|
|
|
ourConversationId,
|
|
|
|
defaultConversationColor,
|
2021-08-11 23:06:20 +00:00
|
|
|
})
|
2024-03-12 16:29:31 +00:00
|
|
|
: undefined;
|
2024-03-15 14:38:26 +00:00
|
|
|
}, [
|
|
|
|
quotedMessage,
|
|
|
|
conversationSelector,
|
|
|
|
ourConversationId,
|
|
|
|
defaultConversationColor,
|
|
|
|
]);
|
2024-03-12 16:29:31 +00:00
|
|
|
|
|
|
|
const { putItem, removeItem } = useItemsActions();
|
|
|
|
|
|
|
|
const onSetSkinTone = useCallback(
|
|
|
|
(tone: number) => {
|
|
|
|
putItem('skinTone', tone);
|
2023-03-02 20:55:40 +00:00
|
|
|
},
|
2024-03-12 16:29:31 +00:00
|
|
|
[putItem]
|
|
|
|
);
|
2023-03-20 22:23:53 +00:00
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
const clearShowIntroduction = useCallback(() => {
|
|
|
|
removeItem('showStickersIntroduction');
|
|
|
|
}, [removeItem]);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
const clearShowPickerHint = useCallback(() => {
|
|
|
|
removeItem('showStickerPickerHint');
|
|
|
|
}, [removeItem]);
|
2019-06-27 20:35:21 +00:00
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
const {
|
|
|
|
onTextTooLong,
|
|
|
|
onCloseLinkPreview,
|
|
|
|
addAttachment,
|
|
|
|
removeAttachment,
|
|
|
|
onClearAttachments,
|
|
|
|
processAttachments,
|
|
|
|
setMediaQualitySetting,
|
|
|
|
setQuoteByMessageId,
|
|
|
|
cancelJoinRequest,
|
|
|
|
sendStickerMessage,
|
|
|
|
sendEditedMessage,
|
|
|
|
sendMultiMediaMessage,
|
|
|
|
setComposerFocus,
|
|
|
|
} = useComposerActions();
|
|
|
|
const {
|
|
|
|
pushPanelForConversation,
|
|
|
|
discardEditMessage,
|
|
|
|
acceptConversation,
|
|
|
|
blockAndReportSpam,
|
|
|
|
blockConversation,
|
|
|
|
reportSpam,
|
|
|
|
deleteConversation,
|
|
|
|
toggleSelectMode,
|
|
|
|
scrollToMessage,
|
|
|
|
setMessageToEdit,
|
|
|
|
showConversation,
|
|
|
|
} = useConversationsActions();
|
|
|
|
const { cancelRecording, completeRecording, startRecording, errorRecording } =
|
|
|
|
useAudioRecorderActions();
|
|
|
|
const { onUseEmoji } = useEmojisActions();
|
|
|
|
const { showGV2MigrationDialog, toggleForwardMessagesModal } =
|
|
|
|
useGlobalModalActions();
|
|
|
|
const { clearInstalledStickerPack } = useStickersActions();
|
|
|
|
const { showToast } = useToastActions();
|
2024-03-13 18:00:41 +00:00
|
|
|
const { onEditorStateChange } = useComposerActions();
|
2019-05-16 22:32:11 +00:00
|
|
|
|
2024-03-12 16:29:31 +00:00
|
|
|
return (
|
|
|
|
<CompositionArea
|
|
|
|
// Base
|
|
|
|
conversationId={id}
|
|
|
|
draftEditMessage={draftEditMessage ?? null}
|
|
|
|
focusCounter={focusCounter}
|
|
|
|
getPreferredBadge={getPreferredBadge}
|
|
|
|
i18n={i18n}
|
|
|
|
isDisabled={isDisabled}
|
|
|
|
isFormattingEnabled={isFormattingEnabled}
|
2024-06-13 23:22:07 +00:00
|
|
|
isActive={isActive}
|
2024-03-12 16:29:31 +00:00
|
|
|
lastEditableMessageId={lastEditableMessageId ?? null}
|
|
|
|
messageCompositionId={messageCompositionId}
|
|
|
|
platform={platform}
|
|
|
|
sendCounter={sendCounter}
|
|
|
|
shouldHidePopovers={shouldHidePopovers}
|
|
|
|
theme={theme}
|
|
|
|
convertDraftBodyRangesIntoHydrated={convertDraftBodyRangesIntoHydrated}
|
|
|
|
onTextTooLong={onTextTooLong}
|
|
|
|
pushPanelForConversation={pushPanelForConversation}
|
|
|
|
discardEditMessage={discardEditMessage}
|
|
|
|
onCloseLinkPreview={onCloseLinkPreview}
|
2024-03-13 18:00:41 +00:00
|
|
|
onEditorStateChange={onEditorStateChange}
|
2024-03-12 16:29:31 +00:00
|
|
|
// AudioCapture
|
|
|
|
errorDialogAudioRecorderType={errorDialogAudioRecorderType ?? null}
|
|
|
|
recordingState={recordingState}
|
|
|
|
cancelRecording={cancelRecording}
|
|
|
|
completeRecording={completeRecording}
|
|
|
|
startRecording={startRecording}
|
|
|
|
errorRecording={errorRecording}
|
|
|
|
// AttachmentsList
|
|
|
|
draftAttachments={draftAttachments}
|
|
|
|
addAttachment={addAttachment}
|
|
|
|
removeAttachment={removeAttachment}
|
|
|
|
onClearAttachments={onClearAttachments}
|
|
|
|
processAttachments={processAttachments}
|
|
|
|
// MediaEditor
|
|
|
|
imageToBlurHash={imageToBlurHash}
|
|
|
|
// MediaQualitySelector
|
|
|
|
shouldSendHighQualityAttachments={
|
|
|
|
shouldSendHighQualityAttachments !== undefined
|
|
|
|
? shouldSendHighQualityAttachments
|
|
|
|
: window.storage.get('sent-media-quality') === 'high'
|
|
|
|
}
|
|
|
|
setMediaQualitySetting={setMediaQualitySetting}
|
|
|
|
// StagedLinkPreview
|
|
|
|
linkPreviewLoading={linkPreviewLoading}
|
|
|
|
linkPreviewResult={linkPreviewResult ?? null}
|
|
|
|
// Quote
|
|
|
|
quotedMessageId={quotedMessage?.quote?.messageId ?? null}
|
|
|
|
quotedMessageProps={quotedMessageProps ?? null}
|
|
|
|
quotedMessageAuthorAci={quotedMessage?.quote?.authorAci ?? null}
|
|
|
|
quotedMessageSentAt={quotedMessage?.quote?.id ?? null}
|
|
|
|
setQuoteByMessageId={setQuoteByMessageId}
|
|
|
|
// Emojis
|
|
|
|
recentEmojis={recentEmojis}
|
|
|
|
skinTone={skinTone}
|
|
|
|
onPickEmoji={onUseEmoji}
|
|
|
|
// Stickers
|
|
|
|
receivedPacks={receivedPacks}
|
|
|
|
installedPack={installedPack}
|
|
|
|
blessedPacks={blessedPacks}
|
|
|
|
knownPacks={knownPacks}
|
|
|
|
installedPacks={installedPacks}
|
|
|
|
recentStickers={recentStickers}
|
|
|
|
showIntroduction={showStickersIntroduction}
|
|
|
|
showPickerHint={showStickerPickerHint}
|
|
|
|
// Message Requests
|
|
|
|
acceptedMessageRequest={conversation.acceptedMessageRequest ?? null}
|
|
|
|
removalStage={conversation.removalStage ?? null}
|
|
|
|
addedByName={addedByName}
|
|
|
|
conversationName={conversationName}
|
|
|
|
conversationType={conversation.type}
|
|
|
|
isBlocked={conversation.isBlocked ?? false}
|
|
|
|
isReported={conversation.isReported ?? false}
|
|
|
|
isHidden={conversation.removalStage != null}
|
|
|
|
isSMSOnly={Boolean(isConversationSMSOnly(conversation))}
|
|
|
|
isSignalConversation={isSignalConversation(conversation)}
|
|
|
|
isFetchingUUID={conversation.isFetchingUUID ?? null}
|
|
|
|
isMissingMandatoryProfileSharing={isMissingRequiredProfileSharing(
|
|
|
|
conversation
|
|
|
|
)}
|
|
|
|
acceptConversation={acceptConversation}
|
|
|
|
blockAndReportSpam={blockAndReportSpam}
|
|
|
|
blockConversation={blockConversation}
|
|
|
|
reportSpam={reportSpam}
|
|
|
|
deleteConversation={deleteConversation}
|
|
|
|
// Groups
|
|
|
|
groupVersion={conversation.groupVersion ?? null}
|
|
|
|
isGroupV1AndDisabled={conversation.isGroupV1AndDisabled ?? null}
|
|
|
|
left={conversation.left ?? null}
|
|
|
|
announcementsOnly={announcementsOnly ?? null}
|
|
|
|
areWeAdmin={areWeAdmin ?? null}
|
|
|
|
areWePending={conversation.areWePending ?? null}
|
|
|
|
areWePendingApproval={conversation.areWePendingApproval ?? null}
|
|
|
|
groupAdmins={groupAdmins}
|
|
|
|
draftText={conversation.draftText ?? null}
|
|
|
|
draftBodyRanges={hydratedDraftBodyRanges ?? null}
|
|
|
|
renderSmartCompositionRecording={renderSmartCompositionRecording}
|
|
|
|
renderSmartCompositionRecordingDraft={
|
|
|
|
renderSmartCompositionRecordingDraft
|
|
|
|
}
|
|
|
|
showGV2MigrationDialog={showGV2MigrationDialog}
|
|
|
|
cancelJoinRequest={cancelJoinRequest}
|
|
|
|
sortedGroupMembers={conversation.sortedGroupMembers ?? null}
|
|
|
|
// Select Mode
|
|
|
|
selectedMessageIds={selectedMessageIds}
|
|
|
|
toggleSelectMode={toggleSelectMode}
|
|
|
|
toggleForwardMessagesModal={toggleForwardMessagesModal}
|
|
|
|
// Dispatch
|
|
|
|
onSetSkinTone={onSetSkinTone}
|
|
|
|
clearShowIntroduction={clearShowIntroduction}
|
|
|
|
clearInstalledStickerPack={clearInstalledStickerPack}
|
|
|
|
clearShowPickerHint={clearShowPickerHint}
|
|
|
|
showToast={showToast}
|
|
|
|
sendStickerMessage={sendStickerMessage}
|
|
|
|
sendEditedMessage={sendEditedMessage}
|
|
|
|
sendMultiMediaMessage={sendMultiMediaMessage}
|
|
|
|
scrollToMessage={scrollToMessage}
|
|
|
|
setComposerFocus={setComposerFocus}
|
|
|
|
setMessageToEdit={setMessageToEdit}
|
|
|
|
showConversation={showConversation}
|
|
|
|
/>
|
|
|
|
);
|
2024-03-13 20:44:13 +00:00
|
|
|
});
|