put back quote a message logic with hook
This commit is contained in:
parent
9a380b716b
commit
a54345a42e
|
@ -43,7 +43,7 @@ import autoBind from 'auto-bind';
|
|||
import { AudioPlayerWithEncryptedFile } from './H5AudioPlayer';
|
||||
import { ClickToTrustSender } from './message/ClickToTrustSender';
|
||||
import { getMessageById } from '../../data/data';
|
||||
import { deleteMessagesById } from '../../interactions/conversationInteractions';
|
||||
import { deleteMessagesById, replyToMessage } from '../../interactions/conversationInteractions';
|
||||
import { connect } from 'react-redux';
|
||||
import { StateType } from '../../state/reducer';
|
||||
import { getSelectedMessageIds } from '../../state/selectors/conversations';
|
||||
|
@ -56,6 +56,7 @@ import {
|
|||
} from '../../state/ducks/conversations';
|
||||
import { saveAttachmentToDisk } from '../../util/attachmentsUtil';
|
||||
import { LightBoxOptions } from '../session/conversation/SessionConversation';
|
||||
import { pushUnblockToSend } from '../../session/utils/Toast';
|
||||
|
||||
// Same as MIN_WIDTH in ImageGrid.tsx
|
||||
const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = 200;
|
||||
|
@ -944,9 +945,12 @@ class MessageInner extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private onReplyPrivate(e: any) {
|
||||
if (this.props && this.props.onReply) {
|
||||
this.props.onReply(this.props.timestamp);
|
||||
if (this.props.isBlocked) {
|
||||
pushUnblockToSend();
|
||||
return;
|
||||
}
|
||||
|
||||
void replyToMessage(this.props.id);
|
||||
}
|
||||
|
||||
private async onAddModerator() {
|
||||
|
|
|
@ -45,6 +45,9 @@ import {
|
|||
getItemById,
|
||||
hasLinkPreviewPopupBeenDisplayed,
|
||||
} from '../../../data/data';
|
||||
import { getQuotedMessage } from '../../../state/selectors/conversations';
|
||||
import { connect } from 'react-redux';
|
||||
import { StateType } from '../../../state/reducer';
|
||||
|
||||
export interface ReplyingToMessageProps {
|
||||
convoId: string;
|
||||
|
@ -80,10 +83,7 @@ interface Props {
|
|||
selectedConversationKey: string;
|
||||
selectedConversation: ReduxConversationType | undefined;
|
||||
isPublic: boolean;
|
||||
|
||||
quotedMessageProps?: ReplyingToMessageProps;
|
||||
removeQuotedMessage: () => void;
|
||||
|
||||
stagedAttachments: Array<StagedAttachmentType>;
|
||||
clearAttachments: () => any;
|
||||
removeAttachment: (toRemove: AttachmentType) => void;
|
||||
|
@ -135,7 +135,7 @@ const getDefaultState = () => {
|
|||
};
|
||||
};
|
||||
|
||||
export class SessionCompositionBox extends React.Component<Props, State> {
|
||||
class SessionCompositionBoxInner extends React.Component<Props, State> {
|
||||
private readonly textarea: React.RefObject<any>;
|
||||
private readonly fileInput: React.RefObject<HTMLInputElement>;
|
||||
private emojiPanel: any;
|
||||
|
@ -188,7 +188,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
|
|||
|
||||
return (
|
||||
<Flex flexDirection="column">
|
||||
{this.renderQuotedMessage()}
|
||||
<SessionQuotedMessageComposition />
|
||||
{this.renderStagedLinkPreview()}
|
||||
{this.renderAttachmentsStaged()}
|
||||
<div className="composition-container">
|
||||
|
@ -665,19 +665,6 @@ export class SessionCompositionBox extends React.Component<Props, State> {
|
|||
});
|
||||
}
|
||||
|
||||
private renderQuotedMessage() {
|
||||
const { quotedMessageProps, removeQuotedMessage } = this.props;
|
||||
if (quotedMessageProps?.id) {
|
||||
return (
|
||||
<SessionQuotedMessageComposition
|
||||
quotedMessageProps={quotedMessageProps}
|
||||
removeQuotedMessage={removeQuotedMessage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
|
||||
private onClickAttachment(attachment: AttachmentType) {
|
||||
this.setState({ showCaptionEditor: attachment });
|
||||
}
|
||||
|
@ -839,6 +826,8 @@ export class SessionCompositionBox extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
const { quotedMessageProps } = this.props;
|
||||
|
||||
console.warn('quotedMessageProps', quotedMessageProps);
|
||||
const { stagedLinkPreview } = this.state;
|
||||
|
||||
// Send message
|
||||
|
@ -999,3 +988,13 @@ export class SessionCompositionBox extends React.Component<Props, State> {
|
|||
this.linkPreviewAbortController?.abort();
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: StateType) => {
|
||||
return {
|
||||
quotedMessageProps: getQuotedMessage(state),
|
||||
};
|
||||
};
|
||||
|
||||
const smart = connect(mapStateToProps);
|
||||
|
||||
export const SessionCompositionBox = smart(SessionCompositionBoxInner);
|
||||
|
|
|
@ -21,6 +21,7 @@ import { SessionFileDropzone } from './SessionFileDropzone';
|
|||
import {
|
||||
fetchMessagesForConversation,
|
||||
PropsForMessage,
|
||||
quoteMessage,
|
||||
ReduxConversationType,
|
||||
resetSelectedMessageIds,
|
||||
showLightBox,
|
||||
|
@ -44,10 +45,6 @@ interface State {
|
|||
|
||||
stagedAttachments: Array<StagedAttachmentType>;
|
||||
isDraggingFile: boolean;
|
||||
|
||||
// quoted message
|
||||
quotedMessageTimestamp?: number;
|
||||
quotedMessageProps?: any;
|
||||
}
|
||||
|
||||
export interface LightBoxOptions {
|
||||
|
@ -151,8 +148,6 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
showRecordingView: false,
|
||||
stagedAttachments: [],
|
||||
isDraggingFile: false,
|
||||
quotedMessageProps: undefined,
|
||||
quotedMessageTimestamp: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +169,7 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
// ~~~~~~~~~~~~~~ RENDER METHODS ~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
public render() {
|
||||
const { showRecordingView, quotedMessageProps, isDraggingFile, stagedAttachments } = this.state;
|
||||
const { showRecordingView, isDraggingFile, stagedAttachments } = this.state;
|
||||
|
||||
const {
|
||||
selectedConversation,
|
||||
|
@ -210,6 +205,8 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
(this.messageContainerRef
|
||||
.current as any).scrollTop = this.messageContainerRef.current?.scrollHeight;
|
||||
}
|
||||
|
||||
window.inboxStore?.dispatch(quoteMessage(undefined));
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -231,7 +228,7 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
{lightBoxOptions?.media && this.renderLightBox(lightBoxOptions)}
|
||||
|
||||
<div className="conversation-messages">
|
||||
<SessionMessagesList {...this.getMessagesListProps()} />
|
||||
<SessionMessagesList messageContainerRef={this.messageContainerRef} />
|
||||
|
||||
{showRecordingView && <div className="conversation-messages__blocking-overlay" />}
|
||||
{isDraggingFile && <SessionFileDropzone />}
|
||||
|
@ -249,10 +246,6 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
stagedAttachments={stagedAttachments}
|
||||
onLoadVoiceNoteView={this.onLoadVoiceNoteView}
|
||||
onExitVoiceNoteView={this.onExitVoiceNoteView}
|
||||
quotedMessageProps={quotedMessageProps}
|
||||
removeQuotedMessage={() => {
|
||||
void this.replyToMessage(undefined);
|
||||
}}
|
||||
clearAttachments={this.clearAttachments}
|
||||
removeAttachment={this.removeAttachment}
|
||||
onChoseAttachments={this.onChoseAttachments}
|
||||
|
@ -292,13 +285,6 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
public getMessagesListProps(): SessionMessageListProps {
|
||||
return {
|
||||
messageContainerRef: this.messageContainerRef,
|
||||
replyToMessage: this.replyToMessage,
|
||||
};
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~ MICROPHONE METHODS ~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -315,37 +301,6 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
});
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~ MESSAGE QUOTE ~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
private async replyToMessage(quotedMessageTimestamp?: number) {
|
||||
if (this.props.selectedConversation?.isBlocked) {
|
||||
pushUnblockToSend();
|
||||
return;
|
||||
}
|
||||
if (!_.isEqual(this.state.quotedMessageTimestamp, quotedMessageTimestamp)) {
|
||||
const { messagesProps, selectedConversationKey } = this.props;
|
||||
const conversationModel = getConversationController().getOrThrow(selectedConversationKey);
|
||||
|
||||
let quotedMessageProps = null;
|
||||
if (quotedMessageTimestamp) {
|
||||
const quotedMessage = messagesProps.find(
|
||||
m =>
|
||||
m.propsForMessage.timestamp === quotedMessageTimestamp ||
|
||||
m.propsForMessage.serverTimestamp === quotedMessageTimestamp
|
||||
);
|
||||
|
||||
if (quotedMessage) {
|
||||
const quotedMessageModel = await getMessageById(quotedMessage.propsForMessage.id);
|
||||
if (quotedMessageModel) {
|
||||
quotedMessageProps = await conversationModel.makeQuote(quotedMessageModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setState({ quotedMessageTimestamp, quotedMessageProps });
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~ KEYBOARD NAVIGATION ~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -52,8 +52,6 @@ interface State {
|
|||
|
||||
export type SessionMessageListProps = {
|
||||
messageContainerRef: React.RefObject<any>;
|
||||
|
||||
replyToMessage: (messageId: number) => Promise<void>;
|
||||
};
|
||||
|
||||
type Props = SessionMessageListProps & {
|
||||
|
@ -138,10 +136,6 @@ const GenericMessageItem = (props: {
|
|||
|
||||
console.warn('FIXME audric');
|
||||
|
||||
if (!props.messageProps) {
|
||||
debugger;
|
||||
}
|
||||
|
||||
// const onQuoteClick = props.messageProps.propsForMessage.quote
|
||||
// ? this.scrollToQuoteMessage
|
||||
// : async () => {};
|
||||
|
@ -152,7 +146,6 @@ const GenericMessageItem = (props: {
|
|||
multiSelectMode,
|
||||
// isQuotedMessageToAnimate: messageId === this.state.animateQuotedMessageId,
|
||||
// nextMessageToPlay: this.state.nextMessageToPlay,
|
||||
onReply: props.replyToMessage,
|
||||
// playNextMessage: this.playNextMessage,
|
||||
// onQuoteClick,
|
||||
};
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import React, { useContext } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Flex } from '../../basic/Flex';
|
||||
import { SessionIcon, SessionIconButton, SessionIconSize, SessionIconType } from '../icon';
|
||||
import { ReplyingToMessageProps } from './SessionCompositionBox';
|
||||
import styled, { DefaultTheme, ThemeContext } from 'styled-components';
|
||||
import { getAlt, isAudio, isImageAttachment } from '../../../types/Attachment';
|
||||
import styled, { useTheme } from 'styled-components';
|
||||
import { getAlt, isAudio } from '../../../types/Attachment';
|
||||
import { Image } from '../../conversation/Image';
|
||||
import { AUDIO_MP3 } from '../../../types/MIME';
|
||||
|
||||
// tslint:disable: react-unused-props-and-state
|
||||
interface Props {
|
||||
quotedMessageProps: ReplyingToMessageProps;
|
||||
removeQuotedMessage: any;
|
||||
}
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getQuotedMessage } from '../../../state/selectors/conversations';
|
||||
import { quoteMessage } from '../../../state/ducks/conversations';
|
||||
|
||||
const QuotedMessageComposition = styled.div`
|
||||
width: 100%;
|
||||
|
@ -41,11 +37,13 @@ const ReplyingTo = styled.div`
|
|||
color: ${props => props.theme.colors.textColor};
|
||||
`;
|
||||
|
||||
export const SessionQuotedMessageComposition = (props: Props) => {
|
||||
const { quotedMessageProps, removeQuotedMessage } = props;
|
||||
const theme = useContext(ThemeContext);
|
||||
export const SessionQuotedMessageComposition = () => {
|
||||
const theme = useTheme();
|
||||
const quotedMessageProps = useSelector(getQuotedMessage);
|
||||
|
||||
const { text: body, attachments } = quotedMessageProps;
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { text: body, attachments } = quotedMessageProps || {};
|
||||
const hasAttachments = attachments && attachments.length > 0;
|
||||
|
||||
let hasImageAttachment = false;
|
||||
|
@ -61,6 +59,14 @@ export const SessionQuotedMessageComposition = (props: Props) => {
|
|||
const hasAudioAttachment =
|
||||
hasAttachments && attachments && attachments.length > 0 && isAudio(attachments);
|
||||
|
||||
const removeQuotedMessage = useCallback(() => {
|
||||
dispatch(quoteMessage(undefined));
|
||||
}, []);
|
||||
|
||||
if (!quotedMessageProps?.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<QuotedMessageComposition theme={theme}>
|
||||
<Flex
|
||||
|
|
|
@ -34,6 +34,7 @@ import {
|
|||
} from '../data/data';
|
||||
import {
|
||||
conversationReset,
|
||||
quoteMessage,
|
||||
resetSelectedMessageIds,
|
||||
SortedMessageModelProps,
|
||||
} from '../state/ducks/conversations';
|
||||
|
@ -43,6 +44,7 @@ import { FSv2 } from '../fileserver';
|
|||
import { fromBase64ToArray, toHex } from '../session/utils/String';
|
||||
import { SessionButtonColor } from '../components/session/SessionButton';
|
||||
import { perfEnd, perfStart } from '../session/utils/Performance';
|
||||
import { ReplyingToMessageProps } from '../components/session/conversation/SessionCompositionBox';
|
||||
|
||||
export const getCompleteUrlForV2ConvoId = async (convoId: string) => {
|
||||
if (convoId.match(openGroupV2ConversationIdRegex)) {
|
||||
|
@ -532,3 +534,22 @@ export async function deleteMessagesById(
|
|||
void doDelete();
|
||||
}
|
||||
}
|
||||
|
||||
export async function replyToMessage(messageId: string) {
|
||||
const quotedMessageModel = await getMessageById(messageId);
|
||||
if (!quotedMessageModel) {
|
||||
window.log.warn('Failed to find message to reply to');
|
||||
return;
|
||||
}
|
||||
const conversationModel = getConversationController().getOrThrow(
|
||||
quotedMessageModel.get('conversationId')
|
||||
);
|
||||
|
||||
const quotedMessageProps = await conversationModel.makeQuote(quotedMessageModel);
|
||||
|
||||
if (quotedMessageProps) {
|
||||
window.inboxStore?.dispatch(quoteMessage(quotedMessageProps));
|
||||
} else {
|
||||
window.inboxStore?.dispatch(quoteMessage(undefined));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil';
|
|||
import { getOpenGroupV2FromConversationId } from '../opengroup/utils/OpenGroupUtils';
|
||||
import { createTaskWithTimeout } from '../session/utils/TaskWithTimeout';
|
||||
import { perfEnd, perfStart } from '../session/utils/Performance';
|
||||
import { ReplyingToMessageProps } from '../components/session/conversation/SessionCompositionBox';
|
||||
|
||||
export enum ConversationTypeEnum {
|
||||
GROUP = 'group',
|
||||
|
@ -566,17 +567,24 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
return [];
|
||||
}
|
||||
|
||||
public async makeQuote(quotedMessage: MessageModel) {
|
||||
public async makeQuote(quotedMessage: MessageModel): Promise<ReplyingToMessageProps | null> {
|
||||
const attachments = quotedMessage.get('attachments');
|
||||
const preview = quotedMessage.get('preview');
|
||||
|
||||
const body = quotedMessage.get('body');
|
||||
const quotedAttachments = await this.getQuoteAttachment(attachments, preview);
|
||||
|
||||
if (!quotedMessage.get('sent_at')) {
|
||||
window.log.warn('tried to make a quote without a sent_at timestamp');
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
author: quotedMessage.getSource(),
|
||||
id: quotedMessage.get('sent_at'),
|
||||
id: `${quotedMessage.get('sent_at')}` || '',
|
||||
text: body,
|
||||
attachments: quotedAttachments,
|
||||
timestamp: quotedMessage.get('sent_at') || 0,
|
||||
convoId: this.id,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -529,6 +529,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
const conversation = this.getConversation();
|
||||
|
||||
const isGroup = !!conversation && !conversation.isPrivate();
|
||||
const isBlocked = conversation?.isBlocked() || false;
|
||||
const isPublic = !!this.get('isPublic');
|
||||
const isPublicOpenGroupV2 = isOpenGroupV2(this.getConversation()?.id || '');
|
||||
|
||||
|
@ -568,6 +569,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
expirationLength,
|
||||
expirationTimestamp,
|
||||
isPublic,
|
||||
isBlocked,
|
||||
isOpenGroupV2: isPublicOpenGroupV2,
|
||||
isKickedFromGroup: conversation?.get('isKickedFromGroup'),
|
||||
isTrustedForAttachmentDownload,
|
||||
|
@ -774,9 +776,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
conversationType: ConversationTypeEnum.PRIVATE,
|
||||
multiSelectMode: false,
|
||||
firstMessageOfSeries: false,
|
||||
onReply: noop,
|
||||
// tslint:disable-next-line: no-async-without-await no-empty
|
||||
onQuoteClick: async () => {},
|
||||
},
|
||||
errors,
|
||||
contacts: sortedContacts || [],
|
||||
|
|
|
@ -237,6 +237,7 @@ export interface MessageRegularProps {
|
|||
expirationTimestamp?: number;
|
||||
convoId: string;
|
||||
isPublic?: boolean;
|
||||
isBlocked: boolean;
|
||||
isOpenGroupV2?: boolean;
|
||||
isKickedFromGroup: boolean;
|
||||
// whether or not to show check boxes
|
||||
|
@ -245,8 +246,6 @@ export interface MessageRegularProps {
|
|||
isUnread: boolean;
|
||||
isQuotedMessageToAnimate?: boolean;
|
||||
isTrustedForAttachmentDownload: boolean;
|
||||
|
||||
onReply: (messagId: number) => void;
|
||||
onQuoteClick: (options: QuoteClickOptions) => Promise<void>;
|
||||
|
||||
playableMessageIndex?: number;
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from '../../models/messageType';
|
||||
import { NotificationForConvoOption } from '../../components/conversation/ConversationHeader';
|
||||
import { LightBoxOptions } from '../../components/session/conversation/SessionConversation';
|
||||
import { ReplyingToMessageProps } from '../../components/session/conversation/SessionCompositionBox';
|
||||
|
||||
export type MessageModelProps = {
|
||||
propsForMessage: PropsForMessage;
|
||||
|
@ -181,6 +182,7 @@ export type PropsForMessage = {
|
|||
isSenderAdmin: boolean;
|
||||
isDeletable: boolean;
|
||||
isExpired: boolean;
|
||||
isBlocked: boolean;
|
||||
};
|
||||
|
||||
export type LastMessageType = {
|
||||
|
@ -235,6 +237,7 @@ export type ConversationsStateType = {
|
|||
showRightPanel: boolean;
|
||||
selectedMessageIds: Array<string>;
|
||||
lightBox?: LightBoxOptions;
|
||||
quotedMessage?: ReplyingToMessageProps;
|
||||
};
|
||||
|
||||
async function getMessages(
|
||||
|
@ -708,6 +711,8 @@ const conversationsSlice = createSlice({
|
|||
state.selectedMessageIds = [];
|
||||
state.selectedConversation = action.payload.id;
|
||||
state.messages = [];
|
||||
state.quotedMessage = undefined;
|
||||
state.lightBox = undefined;
|
||||
return state;
|
||||
},
|
||||
showLightBox(
|
||||
|
@ -717,6 +722,13 @@ const conversationsSlice = createSlice({
|
|||
state.lightBox = action.payload;
|
||||
return state;
|
||||
},
|
||||
quoteMessage(
|
||||
state: ConversationsStateType,
|
||||
action: PayloadAction<ReplyingToMessageProps | undefined>
|
||||
) {
|
||||
state.quotedMessage = action.payload;
|
||||
return state;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder: any) => {
|
||||
// Add reducers for additional action types here, and handle loading state as needed
|
||||
|
@ -762,4 +774,5 @@ export const {
|
|||
resetSelectedMessageIds,
|
||||
toggleSelectedMessageId,
|
||||
showLightBox,
|
||||
quoteMessage,
|
||||
} = actions;
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
ConversationHeaderTitleProps,
|
||||
} from '../../components/conversation/ConversationHeader';
|
||||
import { LightBoxOptions } from '../../components/session/conversation/SessionConversation';
|
||||
import { ReplyingToMessageProps } from '../../components/session/conversation/SessionCompositionBox';
|
||||
|
||||
export const getConversations = (state: StateType): ConversationsStateType => state.conversations;
|
||||
|
||||
|
@ -289,3 +290,8 @@ export const getLightBoxOptions = createSelector(
|
|||
getConversations,
|
||||
(state: ConversationsStateType): LightBoxOptions | undefined => state.lightBox
|
||||
);
|
||||
|
||||
export const getQuotedMessage = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): ReplyingToMessageProps | undefined => state.quotedMessage
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue