diff --git a/src/components/main/ForwardRecipientPicker.tsx b/src/components/main/ForwardRecipientPicker.tsx index 9d054ea22..b8fae3e1c 100644 --- a/src/components/main/ForwardRecipientPicker.tsx +++ b/src/components/main/ForwardRecipientPicker.tsx @@ -113,7 +113,7 @@ const ForwardRecipientPicker: FC = ({ ( const { messageIds: selectedMessageIds } = tabState.selectedMessages || {}; const hasProtectedMessage = chatId ? selectHasProtectedMessage(global, chatId, selectedMessageIds) : false; const canForward = !isSchedule && chatId ? selectCanForwardMessages(global, chatId, selectedMessageIds) : false; - const isForwardModalOpen = tabState.forwardMessages.isModalShown; - const isAnyModalOpen = Boolean(isForwardModalOpen || tabState.requestedDraft + const isShareMessageModalOpen = tabState.isShareMessageModalShown; + const isAnyModalOpen = Boolean(isShareMessageModalOpen || tabState.requestedDraft || tabState.requestedAttachBotInChat || tabState.requestedAttachBotInstall); return { diff --git a/src/components/middle/composer/ComposerEmbeddedMessage.tsx b/src/components/middle/composer/ComposerEmbeddedMessage.tsx index 04d435a5c..d114c8903 100644 --- a/src/components/middle/composer/ComposerEmbeddedMessage.tsx +++ b/src/components/middle/composer/ComposerEmbeddedMessage.tsx @@ -400,8 +400,9 @@ export default memo(withGlobal( }): StateProps => { const { forwardMessages: { - fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions, isModalShown, + fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions, }, + isShareMessageModalShown: isModalShown, shouldPreventComposerAnimation, } = selectTabState(global); diff --git a/src/components/middle/message/ContextMenuContainer.tsx b/src/components/middle/message/ContextMenuContainer.tsx index aedecd07f..90fb0d6c0 100644 --- a/src/components/middle/message/ContextMenuContainer.tsx +++ b/src/components/middle/message/ContextMenuContainer.tsx @@ -19,6 +19,7 @@ import { MAIN_THREAD_ID } from '../../../api/types'; import { PREVIEW_AVATAR_COUNT, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config'; import { areReactionsEmpty, + getHasAdminRight, getIsDownloading, getMessageDownloadableMedia, getMessageVideo, @@ -127,6 +128,8 @@ type StateProps = { canPlayAnimatedEmojis?: boolean; isReactionPickerOpen?: boolean; isInSavedMessages?: boolean; + isChannel?: boolean; + canPostMessagesInChannel?: boolean; }; const selection = window.getSelection(); @@ -187,6 +190,8 @@ const ContextMenuContainer: FC = ({ isInSavedMessages, onClose, onCloseAnimationEnd, + isChannel, + canPostMessagesInChannel, }) => { const { openThread, @@ -194,6 +199,7 @@ const ContextMenuContainer: FC = ({ setEditingId, pinMessage, openForwardMenu, + openReplyMenu, faveSticker, unfaveSticker, toggleMessageSelection, @@ -357,11 +363,16 @@ const ContextMenuContainer: FC = ({ }); const handleReply = useLastCallback(() => { - updateDraftReplyInfo({ - replyToMsgId: message.id, - quoteText: canQuoteSelection && selectionRange ? getSelectionAsFormattedText(selectionRange) : undefined, - replyToPeerId: undefined, - }); + const quoteText = canQuoteSelection && selectionRange ? getSelectionAsFormattedText(selectionRange) : undefined; + if (isChannel && !canPostMessagesInChannel) { + openReplyMenu({ fromChatId: message.chatId, messageId: message.id, quoteText }); + } else { + updateDraftReplyInfo({ + replyToMsgId: message.id, + quoteText, + replyToPeerId: undefined, + }); + } closeMenu(); }); @@ -705,6 +716,7 @@ export default memo(withGlobal( const isPinned = messageListType === 'pinned'; const isScheduled = messageListType === 'scheduled'; const isChannel = chat && isChatChannel(chat); + const canPostMessagesInChannel = isChannel && getHasAdminRight(chat, 'postMessages'); const isLocal = isMessageLocal(message); const hasTtl = hasMessageTtl(message); const canShowSeenBy = Boolean(!isLocal @@ -787,6 +799,8 @@ export default memo(withGlobal( canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global), isReactionPickerOpen: selectIsReactionPickerOpen(global), isInSavedMessages, + isChannel, + canPostMessagesInChannel, }; }, )(ContextMenuContainer)); diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index d12009567..0ae921a5e 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -1121,6 +1121,7 @@ addActionHandler('forwardMessages', (global, actions, payload): ActionReturnType global = getGlobal(); global = updateTabState(global, { forwardMessages: {}, + isShareMessageModalShown: false, }, tabId); setGlobal(global); }); @@ -1827,11 +1828,12 @@ addActionHandler('openChatOrTopicWithReplyInDraft', (global, actions, payload): global = getGlobal(); + const tabState = selectTabState(global, tabId); + const replyingInfo = tabState.replyingMessage; + global = updateTabState(global, { - forwardMessages: { - ...selectTabState(global, tabId).forwardMessages, - isModalShown: false, - }, + isShareMessageModalShown: false, + replyingMessage: {}, }, tabId); setGlobal(global); @@ -1843,7 +1845,16 @@ addActionHandler('openChatOrTopicWithReplyInDraft', (global, actions, payload): const threadId = topicId || MAIN_THREAD_ID; const currentChatId = currentChat.id; - const currentReplyInfo = selectDraft(global, currentChatId, currentThreadId)?.replyInfo; + const newReplyInfo = { + type: 'message', + replyToMsgId: replyingInfo.messageId, + replyToTopId: replyingInfo.toThreadId, + replyToPeerId: currentChatId, + quoteText: replyingInfo.quoteText, + } as ApiInputMessageReplyInfo; + + const currentReplyInfo = replyingInfo.messageId + ? newReplyInfo : selectDraft(global, currentChatId, currentThreadId)?.replyInfo; if (!currentReplyInfo) return; if (!selectReplyCanBeSentToChat(global, toChatId, currentChatId, currentReplyInfo)) { @@ -1896,8 +1907,8 @@ addActionHandler('setForwardChatOrTopic', async (global, actions, payload): Prom ...selectTabState(global, tabId).forwardMessages, toChatId: chatId, toThreadId: topicId, - isModalShown: false, }, + isShareMessageModalShown: false, }, tabId); setGlobal(global); actions.openThread({ chatId, threadId: topicId || MAIN_THREAD_ID, tabId }); @@ -1947,6 +1958,7 @@ addActionHandler('forwardStory', (global, actions, payload): ActionReturnType => global = getGlobal(); global = updateTabState(global, { forwardMessages: {}, + isShareMessageModalShown: false, }, tabId); setGlobal(global); }); diff --git a/src/global/actions/ui/chats.ts b/src/global/actions/ui/chats.ts index 7ce642751..5ba0ebf21 100644 --- a/src/global/actions/ui/chats.ts +++ b/src/global/actions/ui/chats.ts @@ -61,6 +61,7 @@ addActionHandler('processOpenChatOrThread', (global, actions, payload): ActionRe contentToBeScheduled: undefined, ...(chatId !== selectTabState(global, tabId).forwardMessages.toChatId && { forwardMessages: {}, + isShareMessageModalShown: false, }), }, tabId); } diff --git a/src/global/actions/ui/mediaViewer.ts b/src/global/actions/ui/mediaViewer.ts index 9b096b185..b81b9db78 100644 --- a/src/global/actions/ui/mediaViewer.ts +++ b/src/global/actions/ui/mediaViewer.ts @@ -28,6 +28,7 @@ addActionHandler('openMediaViewer', (global, actions, payload): ActionReturnType withDynamicLoading, }, forwardMessages: {}, + isShareMessageModalShown: false, }, tabId); }); diff --git a/src/global/actions/ui/messages.ts b/src/global/actions/ui/messages.ts index eeb37e4ac..566cea8fc 100644 --- a/src/global/actions/ui/messages.ts +++ b/src/global/actions/ui/messages.ts @@ -515,6 +515,20 @@ addActionHandler('setShouldPreventComposerAnimation', (global, actions, payload) }, tabId); }); +addActionHandler('openReplyMenu', (global, actions, payload): ActionReturnType => { + const { + fromChatId, messageId, quoteText, tabId = getCurrentTabId(), + } = payload; + return updateTabState(global, { + replyingMessage: { + fromChatId, + messageId, + quoteText, + }, + isShareMessageModalShown: true, + }, tabId); +}); + addActionHandler('openForwardMenu', (global, actions, payload): ActionReturnType => { const { fromChatId, messageIds, storyId, groupedId, withMyScore, tabId = getCurrentTabId(), @@ -528,9 +542,9 @@ addActionHandler('openForwardMenu', (global, actions, payload): ActionReturnType fromChatId, messageIds: groupedMessageIds || messageIds, storyId, - isModalShown: true, withMyScore, }, + isShareMessageModalShown: true, }, tabId); }); @@ -540,10 +554,10 @@ addActionHandler('changeRecipient', (global, actions, payload): ActionReturnType forwardMessages: { ...selectTabState(global, tabId).forwardMessages, toChatId: undefined, - isModalShown: true, noAuthors: false, noCaptions: false, }, + isShareMessageModalShown: true, }, tabId); }); @@ -575,7 +589,9 @@ addActionHandler('exitForwardMode', (global, actions, payload): ActionReturnType const { tabId = getCurrentTabId() } = payload || {}; global = updateTabState(global, { + isShareMessageModalShown: false, forwardMessages: {}, + replyingMessage: {}, }, tabId); setGlobal(global); }); diff --git a/src/global/initialState.ts b/src/global/initialState.ts index 3a49d21af..535fa9332 100644 --- a/src/global/initialState.ts +++ b/src/global/initialState.ts @@ -349,8 +349,12 @@ export const INITIAL_TAB_STATE: TabState = { isMuted: false, }, + isShareMessageModalShown: false, + forwardMessages: {}, + replyingMessage: {}, + pollResults: {}, payment: {}, diff --git a/src/global/selectors/messages.ts b/src/global/selectors/messages.ts index 586621be0..923d8ed04 100644 --- a/src/global/selectors/messages.ts +++ b/src/global/selectors/messages.ts @@ -655,9 +655,13 @@ export function selectAllowedMessageActions(global: T, me const threadInfo = selectThreadInfo(global, message.chatId, threadId); const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId); - const canReply = !isLocal && !isServiceNotification && !chat.isForbidden - && getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo) - && (!messageTopic || !messageTopic.isClosed || messageTopic.isOwner || getHasAdminRight(chat, 'manageTopics')); + const canPostInChat = getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo); + const canReply = (() => { + if (isLocal || isServiceNotification) return false; + if (isChatChannel(chat)) return true; + if (!canPostInChat || chat.isForbidden) return false; + return !messageTopic || !messageTopic.isClosed || messageTopic.isOwner || getHasAdminRight(chat, 'manageTopics'); + })(); const hasPinPermission = isPrivate || ( chat.isCreator @@ -945,8 +949,8 @@ export function selectIsForwardModalOpen( global: T, ...[tabId = getCurrentTabId()]: TabArgs ) { - const { forwardMessages } = selectTabState(global, tabId); - return Boolean(forwardMessages.isModalShown); + const { isShareMessageModalShown } = selectTabState(global, tabId); + return Boolean(isShareMessageModalShown); } export function selectCommonBoxChatId(global: T, messageId: number) { diff --git a/src/global/types.ts b/src/global/types.ts index 7fa3182e1..b042f40ea 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -491,8 +491,17 @@ export type TabState = { loadingMessageId: number; }; + isShareMessageModalShown?: boolean; + + replyingMessage: { + fromChatId?: string; + messageId?: number; + quoteText?: ApiFormattedText; + toChatId?: string; + toThreadId?: ThreadId; + }; + forwardMessages: { - isModalShown?: boolean; fromChatId?: string; messageIds?: number[]; storyId?: number; @@ -2625,6 +2634,13 @@ export interface ActionPayloads { shouldPreventComposerAnimation: boolean; } & WithTabId; + // Replies + openReplyMenu: { + fromChatId: string; + messageId?: number; + quoteText?: ApiFormattedText; + } & WithTabId; + // Forwards openForwardMenu: { fromChatId: string;