Messages: Handle 100+ messages forward (#5329)
This commit is contained in:
parent
ed761133bc
commit
5e4924189a
@ -228,14 +228,21 @@ export function buildApiUrlAuthResult(result: GramJs.TypeUrlAuthResult): ApiUrlA
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiConfig(config: GramJs.Config): ApiConfig {
|
export function buildApiConfig(config: GramJs.Config): ApiConfig {
|
||||||
const defaultReaction = config.reactionsDefault && buildApiReaction(config.reactionsDefault);
|
const {
|
||||||
|
testMode, expires, gifSearchUsername, chatSizeMax, autologinToken, reactionsDefault,
|
||||||
|
messageLengthMax, editTimeLimit, forwardedCountMax,
|
||||||
|
} = config;
|
||||||
|
const defaultReaction = reactionsDefault && buildApiReaction(reactionsDefault);
|
||||||
return {
|
return {
|
||||||
isTestServer: config.testMode,
|
isTestServer: testMode,
|
||||||
expiresAt: config.expires,
|
expiresAt: expires,
|
||||||
gifSearchUsername: config.gifSearchUsername,
|
gifSearchUsername,
|
||||||
defaultReaction,
|
defaultReaction,
|
||||||
maxGroupSize: config.chatSizeMax,
|
maxGroupSize: chatSizeMax,
|
||||||
autologinToken: config.autologinToken,
|
autologinToken,
|
||||||
|
maxMessageLength: messageLengthMax,
|
||||||
|
editTimeLimit,
|
||||||
|
maxForwardedCount: forwardedCountMax,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1963,7 +1963,10 @@ function handleMultipleLocalMessagesUpdate(
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
handleGramJsUpdate(otherUpdates);
|
// Illegal monkey patching. Easier than creating mock update object
|
||||||
|
update.updates = otherUpdates;
|
||||||
|
|
||||||
|
handleGramJsUpdate(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLocalMessageUpdate(
|
function handleLocalMessageUpdate(
|
||||||
|
|||||||
@ -249,6 +249,9 @@ export interface ApiConfig {
|
|||||||
maxGroupSize: number;
|
maxGroupSize: number;
|
||||||
autologinToken?: string;
|
autologinToken?: string;
|
||||||
isTestServer?: boolean;
|
isTestServer?: boolean;
|
||||||
|
maxMessageLength: number;
|
||||||
|
editTimeLimit: number;
|
||||||
|
maxForwardedCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiPeerColorSet = string[];
|
export type ApiPeerColorSet = string[];
|
||||||
|
|||||||
@ -44,6 +44,7 @@ import { MAIN_THREAD_ID } from '../../api/types';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
BASE_EMOJI_KEYWORD_LANG,
|
BASE_EMOJI_KEYWORD_LANG,
|
||||||
|
DEFAULT_MAX_MESSAGE_LENGTH,
|
||||||
EDITABLE_INPUT_MODAL_ID,
|
EDITABLE_INPUT_MODAL_ID,
|
||||||
HEART_REACTION,
|
HEART_REACTION,
|
||||||
MAX_UPLOAD_FILEPART_SIZE,
|
MAX_UPLOAD_FILEPART_SIZE,
|
||||||
@ -270,6 +271,7 @@ type StateProps =
|
|||||||
areEffectsSupported?: boolean;
|
areEffectsSupported?: boolean;
|
||||||
canPlayEffect?: boolean;
|
canPlayEffect?: boolean;
|
||||||
shouldPlayEffect?: boolean;
|
shouldPlayEffect?: boolean;
|
||||||
|
maxMessageLength: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MainButtonState {
|
enum MainButtonState {
|
||||||
@ -291,7 +293,6 @@ const SCREEN_WIDTH_TO_HIDE_PLACEHOLDER = 600; // px
|
|||||||
|
|
||||||
const MOBILE_KEYBOARD_HIDE_DELAY_MS = 100;
|
const MOBILE_KEYBOARD_HIDE_DELAY_MS = 100;
|
||||||
const SELECT_MODE_TRANSITION_MS = 200;
|
const SELECT_MODE_TRANSITION_MS = 200;
|
||||||
const MESSAGE_MAX_LENGTH = 4096;
|
|
||||||
const SENDING_ANIMATION_DURATION = 350;
|
const SENDING_ANIMATION_DURATION = 350;
|
||||||
const MOUNT_ANIMATION_DURATION = 430;
|
const MOUNT_ANIMATION_DURATION = 430;
|
||||||
|
|
||||||
@ -384,6 +385,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
areEffectsSupported,
|
areEffectsSupported,
|
||||||
canPlayEffect,
|
canPlayEffect,
|
||||||
shouldPlayEffect,
|
shouldPlayEffect,
|
||||||
|
maxMessageLength,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
@ -871,7 +873,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const validateTextLength = useLastCallback((text: string, isAttachmentModal?: boolean) => {
|
const validateTextLength = useLastCallback((text: string, isAttachmentModal?: boolean) => {
|
||||||
const maxLength = isAttachmentModal ? captionLimit : MESSAGE_MAX_LENGTH;
|
const maxLength = isAttachmentModal ? captionLimit : maxMessageLength;
|
||||||
if (text?.length > maxLength) {
|
if (text?.length > maxLength) {
|
||||||
const extraLength = text.length - maxLength;
|
const extraLength = text.length - maxLength;
|
||||||
showDialog({
|
showDialog({
|
||||||
@ -2145,6 +2147,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const effect = effectId ? global.availableEffectById[effectId] : undefined;
|
const effect = effectId ? global.availableEffectById[effectId] : undefined;
|
||||||
const effectReactions = global.reactions.effectReactions;
|
const effectReactions = global.reactions.effectReactions;
|
||||||
|
|
||||||
|
const maxMessageLength = global.config?.maxMessageLength || DEFAULT_MAX_MESSAGE_LENGTH;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availableReactions: global.reactions.availableReactions,
|
availableReactions: global.reactions.availableReactions,
|
||||||
topReactions: type === 'story' ? global.reactions.topReactions : undefined,
|
topReactions: type === 'story' ? global.reactions.topReactions : undefined,
|
||||||
@ -2220,6 +2224,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
areEffectsSupported,
|
areEffectsSupported,
|
||||||
canPlayEffect,
|
canPlayEffect,
|
||||||
shouldPlayEffect,
|
shouldPlayEffect,
|
||||||
|
maxMessageLength,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(Composer));
|
)(Composer));
|
||||||
|
|||||||
@ -16,8 +16,6 @@ import getFilesFromDataTransferItems from '../helpers/getFilesFromDataTransferIt
|
|||||||
|
|
||||||
import useOldLang from '../../../../hooks/useOldLang';
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
const MAX_MESSAGE_LENGTH = 4096;
|
|
||||||
|
|
||||||
const TYPE_HTML = 'text/html';
|
const TYPE_HTML = 'text/html';
|
||||||
const DOCUMENT_TYPE_WORD = 'urn:schemas-microsoft-com:office:word';
|
const DOCUMENT_TYPE_WORD = 'urn:schemas-microsoft-com:office:word';
|
||||||
const NAMESPACE_PREFIX_WORD = 'xmlns:w';
|
const NAMESPACE_PREFIX_WORD = 'xmlns:w';
|
||||||
@ -49,7 +47,7 @@ const useClipboardPaste = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pastedText = e.clipboardData.getData('text').substring(0, MAX_MESSAGE_LENGTH);
|
const pastedText = e.clipboardData.getData('text');
|
||||||
const html = e.clipboardData.getData('text/html');
|
const html = e.clipboardData.getData('text/html');
|
||||||
|
|
||||||
let pastedFormattedText = html ? parseHtmlAsFormattedText(
|
let pastedFormattedText = html ? parseHtmlAsFormattedText(
|
||||||
|
|||||||
@ -44,6 +44,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
selectActiveDownloads,
|
selectActiveDownloads,
|
||||||
selectAllowedMessageActionsSlow,
|
selectAllowedMessageActionsSlow,
|
||||||
|
selectCanForwardMessage,
|
||||||
selectCanPlayAnimatedEmojis,
|
selectCanPlayAnimatedEmojis,
|
||||||
selectCanScheduleUntilOnline,
|
selectCanScheduleUntilOnline,
|
||||||
selectCanTranslateMessage,
|
selectCanTranslateMessage,
|
||||||
@ -732,7 +733,6 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
canDelete,
|
canDelete,
|
||||||
canReport,
|
canReport,
|
||||||
canEdit,
|
canEdit,
|
||||||
canForward,
|
|
||||||
canFaveSticker,
|
canFaveSticker,
|
||||||
canUnfaveSticker,
|
canUnfaveSticker,
|
||||||
canCopy,
|
canCopy,
|
||||||
@ -743,6 +743,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
canRevote,
|
canRevote,
|
||||||
canClosePoll,
|
canClosePoll,
|
||||||
} = (threadId && selectAllowedMessageActionsSlow(global, message, threadId)) || {};
|
} = (threadId && selectAllowedMessageActionsSlow(global, message, threadId)) || {};
|
||||||
|
const canForward = selectCanForwardMessage(global, message);
|
||||||
|
|
||||||
const userStatus = isPrivate ? selectUserStatus(global, chat.id) : undefined;
|
const userStatus = isPrivate ? selectUserStatus(global, chat.id) : undefined;
|
||||||
const isOwn = isOwnMessage(message);
|
const isOwn = isOwnMessage(message);
|
||||||
|
|||||||
@ -364,7 +364,7 @@ export const DEFAULT_LIMITS: Record<ApiLimitType, readonly [number, number]> = {
|
|||||||
dialogFiltersChats: [100, 200],
|
dialogFiltersChats: [100, 200],
|
||||||
dialogFilters: [10, 20],
|
dialogFilters: [10, 20],
|
||||||
dialogFolderPinned: [5, 10],
|
dialogFolderPinned: [5, 10],
|
||||||
captionLength: [1024, 2048],
|
captionLength: [1024, 4096],
|
||||||
channels: [500, 1000],
|
channels: [500, 1000],
|
||||||
channelsPublic: [10, 20],
|
channelsPublic: [10, 20],
|
||||||
aboutLength: [70, 140],
|
aboutLength: [70, 140],
|
||||||
@ -373,6 +373,7 @@ export const DEFAULT_LIMITS: Record<ApiLimitType, readonly [number, number]> = {
|
|||||||
recommendedChannels: [10, 100],
|
recommendedChannels: [10, 100],
|
||||||
savedDialogsPinned: [5, 100],
|
savedDialogsPinned: [5, 100],
|
||||||
};
|
};
|
||||||
|
export const DEFAULT_MAX_MESSAGE_LENGTH = 4096;
|
||||||
|
|
||||||
export const ONE_TIME_MEDIA_TTL_SECONDS = 2147483647;
|
export const ONE_TIME_MEDIA_TTL_SECONDS = 2147483647;
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,7 @@ import {
|
|||||||
isMessageLocal,
|
isMessageLocal,
|
||||||
isServiceNotificationMessage,
|
isServiceNotificationMessage,
|
||||||
isUserBot,
|
isUserBot,
|
||||||
|
splitMessagesForForwarding,
|
||||||
} from '../../helpers';
|
} from '../../helpers';
|
||||||
import { isApiPeerUser } from '../../helpers/peers';
|
import { isApiPeerUser } from '../../helpers/peers';
|
||||||
import {
|
import {
|
||||||
@ -99,6 +100,7 @@ import {
|
|||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
import { updateTabState } from '../../reducers/tabs';
|
import { updateTabState } from '../../reducers/tabs';
|
||||||
import {
|
import {
|
||||||
|
selectCanForwardMessage,
|
||||||
selectChat,
|
selectChat,
|
||||||
selectChatFullInfo,
|
selectChatFullInfo,
|
||||||
selectChatLastMessageId,
|
selectChatLastMessageId,
|
||||||
@ -1146,23 +1148,29 @@ addActionHandler('forwardMessages', (global, actions, payload): ActionReturnType
|
|||||||
const lastMessageId = selectChatLastMessageId(global, toChat.id);
|
const lastMessageId = selectChatLastMessageId(global, toChat.id);
|
||||||
|
|
||||||
const [realMessages, serviceMessages] = partition(messages, (m) => !isServiceNotificationMessage(m));
|
const [realMessages, serviceMessages] = partition(messages, (m) => !isServiceNotificationMessage(m));
|
||||||
if (realMessages.length) {
|
const forwardableRealMessages = realMessages.filter((message) => selectCanForwardMessage(global, message));
|
||||||
|
if (forwardableRealMessages.length) {
|
||||||
|
const messageBatches = global.config?.maxForwardedCount
|
||||||
|
? splitMessagesForForwarding(forwardableRealMessages, global.config.maxForwardedCount)
|
||||||
|
: [forwardableRealMessages];
|
||||||
(async () => {
|
(async () => {
|
||||||
await rafPromise(); // Wait one frame for any previous `sendMessage` to be processed
|
await rafPromise(); // Wait one frame for any previous `sendMessage` to be processed
|
||||||
callApi('forwardMessages', {
|
messageBatches.forEach((batch) => {
|
||||||
fromChat,
|
callApi('forwardMessages', {
|
||||||
toChat,
|
fromChat,
|
||||||
toThreadId,
|
toChat,
|
||||||
messages: realMessages,
|
toThreadId,
|
||||||
isSilent,
|
messages: batch,
|
||||||
scheduledAt,
|
isSilent,
|
||||||
sendAs,
|
scheduledAt,
|
||||||
withMyScore,
|
sendAs,
|
||||||
noAuthors,
|
withMyScore,
|
||||||
noCaptions,
|
noAuthors,
|
||||||
isCurrentUserPremium,
|
noCaptions,
|
||||||
wasDrafted: Boolean(draft),
|
isCurrentUserPremium,
|
||||||
lastMessageId,
|
wasDrafted: Boolean(draft),
|
||||||
|
lastMessageId,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,8 +50,10 @@ import {
|
|||||||
import { updateTabState } from '../../reducers/tabs';
|
import { updateTabState } from '../../reducers/tabs';
|
||||||
import {
|
import {
|
||||||
selectAllowedMessageActionsSlow,
|
selectAllowedMessageActionsSlow,
|
||||||
|
selectCanForwardMessage,
|
||||||
selectChat,
|
selectChat,
|
||||||
selectChatLastMessageId,
|
selectChatLastMessageId,
|
||||||
|
selectChatMessage,
|
||||||
selectChatMessages,
|
selectChatMessages,
|
||||||
selectChatScheduledMessages,
|
selectChatScheduledMessages,
|
||||||
selectCurrentChat,
|
selectCurrentChat,
|
||||||
@ -610,7 +612,16 @@ addActionHandler('openForwardMenuForSelectedMessages', (global, actions, payload
|
|||||||
|
|
||||||
const { chatId: fromChatId, messageIds } = tabState.selectedMessages;
|
const { chatId: fromChatId, messageIds } = tabState.selectedMessages;
|
||||||
|
|
||||||
actions.openForwardMenu({ fromChatId, messageIds, tabId });
|
const forwardableMessageIds = messageIds.filter((id) => {
|
||||||
|
const message = selectChatMessage(global, fromChatId, id);
|
||||||
|
return message && selectCanForwardMessage(global, message);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!forwardableMessageIds.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.openForwardMenu({ fromChatId, messageIds: forwardableMessageIds, tabId });
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('cancelMediaDownload', (global, actions, payload): ActionReturnType => {
|
addActionHandler('cancelMediaDownload', (global, actions, payload): ActionReturnType => {
|
||||||
|
|||||||
@ -408,3 +408,34 @@ export function getMessageLink(peer: ApiPeer, topicId?: ThreadId, messageId?: nu
|
|||||||
const messagePart = messageId ? `/${messageId}` : '';
|
const messagePart = messageId ? `/${messageId}` : '';
|
||||||
return `${TME_LINK_PREFIX}${chatPart}${topicPart}${messagePart}`;
|
return `${TME_LINK_PREFIX}${chatPart}${topicPart}${messagePart}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function splitMessagesForForwarding(messages: ApiMessage[], limit: number): ApiMessage[][] {
|
||||||
|
const result: ApiMessage[][] = [];
|
||||||
|
let currentArr: ApiMessage[] = [];
|
||||||
|
|
||||||
|
// Group messages by `groupedId`
|
||||||
|
messages.reduce<ApiMessage[][]>((acc, message) => {
|
||||||
|
const lastGroup = acc[acc.length - 1];
|
||||||
|
if (message.groupedId && lastGroup?.[0]?.groupedId === message.groupedId) {
|
||||||
|
lastGroup.push(message);
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.push([message]);
|
||||||
|
return acc;
|
||||||
|
}, []).forEach((batch) => {
|
||||||
|
// Fit them into `limit` size
|
||||||
|
if (currentArr.length + batch.length > limit) {
|
||||||
|
result.push(currentArr);
|
||||||
|
currentArr = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
currentArr.push(...batch);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (currentArr.length) {
|
||||||
|
result.push(currentArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import type {
|
|||||||
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types';
|
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ANONYMOUS_USER_ID, GENERAL_TOPIC_ID, SERVICE_NOTIFICATIONS_USER_ID,
|
ANONYMOUS_USER_ID, API_GENERAL_ID_LIMIT, GENERAL_TOPIC_ID, SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
} from '../../config';
|
} from '../../config';
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
import { findLast } from '../../util/iteratees';
|
import { findLast } from '../../util/iteratees';
|
||||||
@ -81,8 +81,6 @@ import {
|
|||||||
selectBot, selectIsCurrentUserPremium, selectUser, selectUserStatus,
|
selectBot, selectIsCurrentUserPremium, selectUser, selectUserStatus,
|
||||||
} from './users';
|
} from './users';
|
||||||
|
|
||||||
const MESSAGE_EDIT_ALLOWED_TIME = 172800; // 48 hours
|
|
||||||
|
|
||||||
export function selectCurrentMessageList<T extends GlobalState>(
|
export function selectCurrentMessageList<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
@ -607,6 +605,28 @@ export function selectCanReplyToMessage<T extends GlobalState>(global: T, messag
|
|||||||
return !messageTopic || !messageTopic.isClosed || messageTopic.isOwner || getHasAdminRight(chat, 'manageTopics');
|
return !messageTopic || !messageTopic.isClosed || messageTopic.isOwner || getHasAdminRight(chat, 'manageTopics');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectCanForwardMessage<T extends GlobalState>(global: T, message: ApiMessage) {
|
||||||
|
const isLocal = isMessageLocal(message);
|
||||||
|
const isServiceNotification = isServiceNotificationMessage(message);
|
||||||
|
const isAction = isActionMessage(message);
|
||||||
|
const hasTtl = hasMessageTtl(message);
|
||||||
|
const { content } = message;
|
||||||
|
const story = content.storyData
|
||||||
|
? selectPeerStory(global, content.storyData.peerId, content.storyData.id)
|
||||||
|
: (content.webPage?.story
|
||||||
|
? selectPeerStory(global, content.webPage.story.peerId, content.webPage.story.id)
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
const isChatProtected = selectIsChatProtected(global, message.chatId);
|
||||||
|
const isStoryForwardForbidden = story && ('isDeleted' in story || ('noForwards' in story && story.noForwards));
|
||||||
|
const canForward = (
|
||||||
|
!isLocal && !isAction && !isChatProtected && !isStoryForwardForbidden
|
||||||
|
&& (message.isForwardingAllowed || isServiceNotification) && !hasTtl
|
||||||
|
);
|
||||||
|
|
||||||
|
return canForward;
|
||||||
|
}
|
||||||
|
|
||||||
// This selector is slow and not to be used within lists (e.g. Message component)
|
// This selector is slow and not to be used within lists (e.g. Message component)
|
||||||
export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
||||||
global: T, message: ApiMessage, threadId: ThreadId,
|
global: T, message: ApiMessage, threadId: ThreadId,
|
||||||
@ -651,7 +671,7 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
|||||||
const isMessageEditable = (
|
const isMessageEditable = (
|
||||||
(
|
(
|
||||||
canEditMessagesIndefinitely
|
canEditMessagesIndefinitely
|
||||||
|| getServerTime() - message.date < MESSAGE_EDIT_ALLOWED_TIME
|
|| getServerTime() - message.date < (global.config?.editTimeLimit || Infinity)
|
||||||
) && !(
|
) && !(
|
||||||
content.sticker || content.contact || content.pollId || content.action
|
content.sticker || content.contact || content.pollId || content.action
|
||||||
|| (content.video?.isRound) || content.location || content.invoice || content.giveaway || content.giveawayResults
|
|| (content.video?.isRound) || content.location || content.invoice || content.giveaway || content.giveawayResults
|
||||||
@ -702,20 +722,6 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
|||||||
|
|
||||||
const canEdit = !isLocal && !isAction && isMessageEditable && hasMessageEditRight;
|
const canEdit = !isLocal && !isAction && isMessageEditable && hasMessageEditRight;
|
||||||
|
|
||||||
const story = content.storyData
|
|
||||||
? selectPeerStory(global, content.storyData.peerId, content.storyData.id)
|
|
||||||
: (content.webPage?.story
|
|
||||||
? selectPeerStory(global, content.webPage.story.peerId, content.webPage.story.id)
|
|
||||||
: undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
const isChatProtected = selectIsChatProtected(global, message.chatId);
|
|
||||||
const isStoryForwardForbidden = story && ('isDeleted' in story || ('noForwards' in story && story.noForwards));
|
|
||||||
const canForward = (
|
|
||||||
!isLocal && !isAction && !isChatProtected && !isStoryForwardForbidden
|
|
||||||
&& (message.isForwardingAllowed || isServiceNotification) && !hasTtl
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasSticker = Boolean(message.content.sticker);
|
const hasSticker = Boolean(message.content.sticker);
|
||||||
const hasFavoriteSticker = hasSticker && selectIsStickerFavorite(global, message.content.sticker!);
|
const hasFavoriteSticker = hasSticker && selectIsStickerFavorite(global, message.content.sticker!);
|
||||||
const canFaveSticker = !isAction && hasSticker && !hasFavoriteSticker;
|
const canFaveSticker = !isAction && hasSticker && !hasFavoriteSticker;
|
||||||
@ -743,7 +749,6 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
|||||||
canReport,
|
canReport,
|
||||||
canDelete,
|
canDelete,
|
||||||
canDeleteForAll,
|
canDeleteForAll,
|
||||||
canForward,
|
|
||||||
canFaveSticker,
|
canFaveSticker,
|
||||||
canUnfaveSticker,
|
canUnfaveSticker,
|
||||||
canCopy,
|
canCopy,
|
||||||
@ -765,7 +770,6 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
|||||||
canReport,
|
canReport,
|
||||||
canDelete,
|
canDelete,
|
||||||
canDeleteForAll,
|
canDeleteForAll,
|
||||||
canForward,
|
|
||||||
canFaveSticker,
|
canFaveSticker,
|
||||||
canUnfaveSticker,
|
canUnfaveSticker,
|
||||||
canCopy,
|
canCopy,
|
||||||
@ -790,6 +794,8 @@ export function selectCanDeleteSelectedMessages<T extends GlobalState>(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedMessageIds.length > API_GENERAL_ID_LIMIT) return {};
|
||||||
|
|
||||||
const messageActions = selectedMessageIds
|
const messageActions = selectedMessageIds
|
||||||
.map((id) => chatMessages[id] && selectAllowedMessageActionsSlow(global, chatMessages[id], threadId))
|
.map((id) => chatMessages[id] && selectAllowedMessageActionsSlow(global, chatMessages[id], threadId))
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user