Channels: Support reply in another chat (#4798)

This commit is contained in:
Alexander Zinchuk 2024-08-06 20:06:31 +02:00
parent 23b6e5e66d
commit 8d34fb3c20
11 changed files with 92 additions and 23 deletions

View File

@ -113,7 +113,7 @@ const ForwardRecipientPicker: FC<OwnProps & StateProps> = ({
<RecipientPicker
isOpen={isOpen}
className={renderingIsStory ? 'component-theme-dark' : undefined}
searchPlaceholder={lang('ForwardTo')}
searchPlaceholder={lang(isForwarding ? 'ForwardTo' : 'ReplyToDialog')}
onSelectRecipient={handleSelectRecipient}
onClose={handleClose}
onCloseAnimationEnd={unmarkIsShown}

View File

@ -201,8 +201,8 @@ export default memo(withGlobal<OwnProps>(
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 {

View File

@ -400,8 +400,9 @@ export default memo(withGlobal<OwnProps>(
}): StateProps => {
const {
forwardMessages: {
fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions, isModalShown,
fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions,
},
isShareMessageModalShown: isModalShown,
shouldPreventComposerAnimation,
} = selectTabState(global);

View File

@ -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<OwnProps & StateProps> = ({
isInSavedMessages,
onClose,
onCloseAnimationEnd,
isChannel,
canPostMessagesInChannel,
}) => {
const {
openThread,
@ -194,6 +199,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
setEditingId,
pinMessage,
openForwardMenu,
openReplyMenu,
faveSticker,
unfaveSticker,
toggleMessageSelection,
@ -357,11 +363,16 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
});
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<OwnProps>(
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<OwnProps>(
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
isReactionPickerOpen: selectIsReactionPickerOpen(global),
isInSavedMessages,
isChannel,
canPostMessagesInChannel,
};
},
)(ContextMenuContainer));

View File

@ -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);
});

View File

@ -61,6 +61,7 @@ addActionHandler('processOpenChatOrThread', (global, actions, payload): ActionRe
contentToBeScheduled: undefined,
...(chatId !== selectTabState(global, tabId).forwardMessages.toChatId && {
forwardMessages: {},
isShareMessageModalShown: false,
}),
}, tabId);
}

View File

@ -28,6 +28,7 @@ addActionHandler('openMediaViewer', (global, actions, payload): ActionReturnType
withDynamicLoading,
},
forwardMessages: {},
isShareMessageModalShown: false,
}, tabId);
});

View File

@ -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);
});

View File

@ -349,8 +349,12 @@ export const INITIAL_TAB_STATE: TabState = {
isMuted: false,
},
isShareMessageModalShown: false,
forwardMessages: {},
replyingMessage: {},
pollResults: {},
payment: {},

View File

@ -655,9 +655,13 @@ export function selectAllowedMessageActions<T extends GlobalState>(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<T extends GlobalState>(
global: T,
...[tabId = getCurrentTabId()]: TabArgs<T>
) {
const { forwardMessages } = selectTabState(global, tabId);
return Boolean(forwardMessages.isModalShown);
const { isShareMessageModalShown } = selectTabState(global, tabId);
return Boolean(isShareMessageModalShown);
}
export function selectCommonBoxChatId<T extends GlobalState>(global: T, messageId: number) {

View File

@ -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;