// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useCallback, useMemo, memo } from 'react';
import { useSelector } from 'react-redux';
import { CompositionArea } from '../../components/CompositionArea';
import { useContactNameData } from '../../components/conversation/ContactName';
import type {
DraftBodyRanges,
HydratedBodyRangesType,
} from '../../types/BodyRange';
import { hydrateRanges } from '../../types/BodyRange';
import { strictAssert } from '../../util/assert';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
import { imageToBlurHash } from '../../util/imageToBlurHash';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
import { isSignalConversation } from '../../util/isSignalConversation';
import {
getErrorDialogAudioRecorderType,
getRecordingState,
} from '../selectors/audioRecorder';
import { getPreferredBadgeSelector } from '../selectors/badges';
import { getComposerStateForConversationIdSelector } from '../selectors/composer';
import {
getConversationSelector,
getGroupAdminsSelector,
getHasPanelOpen,
getLastEditableMessageId,
getSelectedMessageIds,
isMissingRequiredProfileSharing,
} from '../selectors/conversations';
import { selectRecentEmojis } from '../selectors/emojis';
import {
getDefaultConversationColor,
getEmojiSkinTone,
getShowStickerPickerHint,
getShowStickersIntroduction,
getTextFormattingEnabled,
} from '../selectors/items';
import { getPropsForQuote } from '../selectors/message';
import {
getBlessedStickerPacks,
getInstalledStickerPacks,
getKnownStickerPacks,
getReceivedStickerPacks,
getRecentStickers,
getRecentlyInstalledStickerPack,
} from '../selectors/stickers';
import {
getIntl,
getPlatform,
getTheme,
getUserConversationId,
} from '../selectors/user';
import type { SmartCompositionRecordingProps } from './CompositionRecording';
import { SmartCompositionRecording } from './CompositionRecording';
import type { SmartCompositionRecordingDraftProps } from './CompositionRecordingDraft';
import { SmartCompositionRecordingDraft } from './CompositionRecordingDraft';
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';
import { isShowingAnyModal } from '../selectors/globalModals';
function renderSmartCompositionRecording(
recProps: SmartCompositionRecordingProps
) {
return ;
}
function renderSmartCompositionRecordingDraft(
draftProps: SmartCompositionRecordingDraftProps
) {
return ;
}
export const SmartCompositionArea = memo(function SmartCompositionArea({
id,
}: {
id: string;
}) {
const conversationSelector = useSelector(getConversationSelector);
const conversation = conversationSelector(id);
strictAssert(conversation, `Conversation id ${id} not found!`);
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
);
const hasGlobalModalOpen = useSelector(isShowingAnyModal);
const hasPanelOpen = useSelector(getHasPanelOpen);
const getGroupAdmins = useSelector(getGroupAdminsSelector);
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const composerStateForConversationIdSelector = useSelector(
getComposerStateForConversationIdSelector
);
const composerState = composerStateForConversationIdSelector(id);
const { announcementsOnly, areWeAdmin, draftEditMessage, draftBodyRanges } =
conversation;
const {
attachments: draftAttachments,
focusCounter,
isDisabled,
linkPreviewLoading,
linkPreviewResult,
messageCompositionId,
sendCounter,
shouldSendHighQualityAttachments,
} = composerState;
const isActive = useMemo(() => {
return !hasGlobalModalOpen && !hasPanelOpen;
}, [hasGlobalModalOpen, hasPanelOpen]);
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]
);
let { quotedMessage } = composerState;
if (!quotedMessage && draftEditMessage?.quote) {
quotedMessage = {
conversationId: id,
quote: draftEditMessage.quote,
};
}
const ourConversationId = useSelector(getUserConversationId);
const defaultConversationColor = useSelector(getDefaultConversationColor);
const quotedMessageProps = useMemo(() => {
return quotedMessage
? getPropsForQuote(quotedMessage, {
conversationSelector,
ourConversationId,
defaultConversationColor,
})
: undefined;
}, [
quotedMessage,
conversationSelector,
ourConversationId,
defaultConversationColor,
]);
const { putItem, removeItem } = useItemsActions();
const onSetSkinTone = useCallback(
(tone: number) => {
putItem('skinTone', tone);
},
[putItem]
);
const clearShowIntroduction = useCallback(() => {
removeItem('showStickersIntroduction');
}, [removeItem]);
const clearShowPickerHint = useCallback(() => {
removeItem('showStickerPickerHint');
}, [removeItem]);
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();
const { onEditorStateChange } = useComposerActions();
return (
);
});