Support Channel direct messages (#5990)

This commit is contained in:
zubiden 2025-06-18 17:41:05 +02:00 committed by Alexander Zinchuk
parent 8f9310c7d5
commit a85fec6c65
59 changed files with 803 additions and 303 deletions

View File

@ -98,6 +98,7 @@ export interface GramJsAppConfig extends LimitsConfig {
stars_stargift_resale_amount_max?: number; stars_stargift_resale_amount_max?: number;
stars_stargift_resale_amount_min?: number; stars_stargift_resale_amount_min?: number;
stars_stargift_resale_commission_permille?: number; stars_stargift_resale_commission_permille?: number;
poll_answers_max?: number;
} }
function buildEmojiSounds(appConfig: GramJsAppConfig) { function buildEmojiSounds(appConfig: GramJsAppConfig) {
@ -198,5 +199,6 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
starsStargiftResaleAmountMin: appConfig.stars_stargift_resale_amount_min, starsStargiftResaleAmountMin: appConfig.stars_stargift_resale_amount_min,
starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max, starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max,
starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille, starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille,
pollMaxAnswers: appConfig.poll_answers_max,
}; };
} }

View File

@ -110,6 +110,9 @@ function buildApiChatFieldsFromPeerEntity(
isJoinToSend: channel?.joinToSend, isJoinToSend: channel?.joinToSend,
isJoinRequest: channel?.joinRequest, isJoinRequest: channel?.joinRequest,
isForum: channel?.forum, isForum: channel?.forum,
isMonoforum: channel?.monoforum,
linkedMonoforumId: channel?.linkedMonoforumId && buildApiPeerId(channel.linkedMonoforumId, 'channel'),
areChannelMessagesAllowed: channel?.broadcastMessagesAllowed,
areStoriesHidden, areStoriesHidden,
maxStoryId, maxStoryId,
hasStories: Boolean(maxStoryId) && !storiesUnavailable, hasStories: Boolean(maxStoryId) && !storiesUnavailable,

View File

@ -426,11 +426,12 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
} }
if (action instanceof GramJs.MessageActionPaidMessagesPrice) { if (action instanceof GramJs.MessageActionPaidMessagesPrice) {
const { const {
stars, stars, broadcastMessagesAllowed,
} = action; } = action;
return { return {
mediaType: 'action', mediaType: 'action',
type: 'paidMessagesPrice', type: 'paidMessagesPrice',
isAllowedInChannel: broadcastMessagesAllowed,
stars: stars.toJSNumber(), stars: stars.toJSNumber(),
}; };
} }

View File

@ -287,6 +287,7 @@ export function buildMessageDraft(draft: GramJs.TypeDraftMessage): ApiDraft | un
replyToMsgId: replyTo.replyToMsgId, replyToMsgId: replyTo.replyToMsgId,
replyToTopId: replyTo.topMsgId, replyToTopId: replyTo.topMsgId,
replyToPeerId: replyTo.replyToPeerId && getApiChatIdFromMtpPeer(replyTo.replyToPeerId), replyToPeerId: replyTo.replyToPeerId && getApiChatIdFromMtpPeer(replyTo.replyToPeerId),
monoforumPeerId: replyTo.monoforumPeerId && getApiChatIdFromMtpPeer(replyTo.monoforumPeerId),
quoteText: replyTo.quoteText ? buildMessageTextContent(replyTo.quoteText, replyTo.quoteEntities) : undefined, quoteText: replyTo.quoteText ? buildMessageTextContent(replyTo.quoteText, replyTo.quoteEntities) : undefined,
quoteOffset: replyTo.quoteOffset, quoteOffset: replyTo.quoteOffset,
} satisfies ApiInputMessageReplyInfo : undefined; } satisfies ApiInputMessageReplyInfo : undefined;

View File

@ -3,7 +3,7 @@ import { Api as GramJs } from '../../../lib/gramjs';
import type { ApiEmojiStatusType, ApiPeerColor } from '../../types'; import type { ApiEmojiStatusType, ApiPeerColor } from '../../types';
import { CHANNEL_ID_LENGTH } from '../../../config'; import { CHANNEL_ID_BASE } from '../../../config';
import { numberToHexColor } from '../../../util/colors'; import { numberToHexColor } from '../../../util/colors';
type TypePeerOrInput = GramJs.TypePeer | GramJs.TypeInputPeer | GramJs.TypeInputUser | GramJs.TypeInputChannel; type TypePeerOrInput = GramJs.TypePeer | GramJs.TypeInputPeer | GramJs.TypeInputUser | GramJs.TypeInputChannel;
@ -26,13 +26,10 @@ export function buildApiPeerId(id: BigInt.BigInteger, type: 'user' | 'chat' | 'c
} }
if (type === 'channel') { if (type === 'channel') {
// Simulates TDLib https://github.com/tdlib/td/blob/d7203eb719304866a7eb7033ef03d421459335b8/td/telegram/DialogId.cpp#L54 return id.add(CHANNEL_ID_BASE).negate().toString();
// But using only string operations. Should be fine until channel ids reach 10^12
// Example: 12345678 -> -1000012345678
return `-1${id.toString().padStart(CHANNEL_ID_LENGTH - 2, '0')}`;
} }
return `-${id.toString()}`; return id.negate().toString();
} }
export function getApiChatIdFromMtpPeer(peer: TypePeerOrInput) { export function getApiChatIdFromMtpPeer(peer: TypePeerOrInput) {

View File

@ -37,23 +37,22 @@ import {
ApiMessageEntityTypes, ApiMessageEntityTypes,
} from '../../types'; } from '../../types';
import { CHANNEL_ID_LENGTH, DEFAULT_STATUS_ICON_ID } from '../../../config'; import { CHANNEL_ID_BASE, DEFAULT_STATUS_ICON_ID } from '../../../config';
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
import { deserializeBytes } from '../helpers/misc'; import { deserializeBytes } from '../helpers/misc';
import localDb from '../localDb'; import localDb from '../localDb';
function checkIfChannelId(id: string) { export function getEntityTypeById(peerId: string) {
return id.length === CHANNEL_ID_LENGTH && id.startsWith('-1'); const n = Number(peerId);
} if (n > 0) {
export function getEntityTypeById(chatOrUserId: string) {
if (!chatOrUserId.startsWith('-')) {
return 'user'; return 'user';
} else if (checkIfChannelId(chatOrUserId)) {
return 'channel';
} else {
return 'chat';
} }
if (n < -CHANNEL_ID_BASE) {
return 'channel';
}
return 'chat';
} }
export function buildPeer(chatOrUserId: string): GramJs.TypePeer { export function buildPeer(chatOrUserId: string): GramJs.TypePeer {
@ -569,11 +568,13 @@ export function buildMtpPeerId(id: string, type: 'user' | 'chat' | 'channel') {
return BigInt(id); return BigInt(id);
} }
const n = Number(id);
if (type === 'channel') { if (type === 'channel') {
return BigInt(id.slice(2)); // Slice "-1", zeroes are trimmed when converting to BigInt return BigInt(-n - CHANNEL_ID_BASE);
} }
return BigInt(id.slice(1)); return BigInt(n * -1);
} }
export function buildInputGroupCall(groupCall: Partial<ApiGroupCall>) { export function buildInputGroupCall(groupCall: Partial<ApiGroupCall>) {
@ -843,12 +844,13 @@ export function buildInputReplyTo(replyInfo: ApiInputReplyInfo) {
if (replyInfo.type === 'message') { if (replyInfo.type === 'message') {
const { const {
replyToMsgId, replyToTopId, replyToPeerId, quoteText, quoteOffset, replyToMsgId, replyToTopId, replyToPeerId, quoteText, quoteOffset, monoforumPeerId,
} = replyInfo; } = replyInfo;
return new GramJs.InputReplyToMessage({ return new GramJs.InputReplyToMessage({
replyToMsgId, replyToMsgId,
topMsgId: replyToTopId, topMsgId: replyToTopId,
replyToPeerId: replyToPeerId ? buildInputPeerFromLocalDb(replyToPeerId)! : undefined, replyToPeerId: replyToPeerId ? buildInputPeerFromLocalDb(replyToPeerId)! : undefined,
monoforumPeerId: monoforumPeerId ? buildInputPeerFromLocalDb(monoforumPeerId)! : undefined,
quoteText: quoteText?.text, quoteText: quoteText?.text,
quoteEntities: quoteText?.entities?.map(buildMtpMessageEntity), quoteEntities: quoteText?.entities?.map(buildMtpMessageEntity),
quoteOffset, quoteOffset,

View File

@ -299,6 +299,10 @@ export async function fetchSavedChats({
const chats: ApiChat[] = []; const chats: ApiChat[] = [];
dialogs.forEach((dialog) => { dialogs.forEach((dialog) => {
if (dialog instanceof GramJs.MonoForumDialog) {
return;
}
const peerEntity = peersByKey[getPeerKey(dialog.peer)]; const peerEntity = peersByKey[getPeerKey(dialog.peer)];
const chat = buildApiChatFromSavedDialog(dialog, peerEntity); const chat = buildApiChatFromSavedDialog(dialog, peerEntity);
const chatId = getApiChatIdFromMtpPeer(dialog.peer); const chatId = getApiChatIdFromMtpPeer(dialog.peer);
@ -596,7 +600,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
async function getFullChannelInfo( async function getFullChannelInfo(
chat: ApiChat, chat: ApiChat,
): Promise<FullChatData | undefined> { ): Promise<FullChatData | undefined> {
const { id, adminRights } = chat; const { id, adminRights, isMonoforum } = chat;
const accessHash = chat.accessHash!; const accessHash = chat.accessHash!;
const result = await invokeRequest(new GramJs.channels.GetFullChannel({ const result = await invokeRequest(new GramJs.channels.GetFullChannel({
channel: buildInputChannel(id, accessHash), channel: buildInputChannel(id, accessHash),
@ -653,12 +657,14 @@ async function getFullChannelInfo(
? exportedInvite.link ? exportedInvite.link
: undefined; : undefined;
const { members, userStatusesById } = (canViewParticipants && await fetchMembers({ chat })) || {}; const canLoadParticipants = canViewParticipants && !isMonoforum;
const { members, userStatusesById } = (canLoadParticipants && await fetchMembers({ chat })) || {};
const { members: kickedMembers, userStatusesById: bannedStatusesById } = ( const { members: kickedMembers, userStatusesById: bannedStatusesById } = (
canViewParticipants && adminRights && await fetchMembers({ chat, memberFilter: 'kicked' }) canLoadParticipants && adminRights && await fetchMembers({ chat, memberFilter: 'kicked' })
) || {}; ) || {};
const { members: adminMembers, userStatusesById: adminStatusesById } = ( const { members: adminMembers, userStatusesById: adminStatusesById } = (
canViewParticipants && await fetchMembers({ chat, memberFilter: 'admin' }) canLoadParticipants && await fetchMembers({ chat, memberFilter: 'admin' })
) || {}; ) || {};
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
const memberInfoRequest = !chat.isNotJoined && chat.type === 'chatTypeChannel' const memberInfoRequest = !chat.isNotJoined && chat.type === 'chatTypeChannel'
@ -710,7 +716,7 @@ async function getFullChannelInfo(
chatId: buildApiPeerId(migratedFromChatId, 'chat'), chatId: buildApiPeerId(migratedFromChatId, 'chat'),
maxMessageId: migratedFromMaxId, maxMessageId: migratedFromMaxId,
} : undefined, } : undefined,
canViewMembers: canViewParticipants, canViewMembers: canLoadParticipants,
canViewStatistics: canViewStats, canViewStatistics: canViewStats,
canViewMonetization, canViewMonetization,
isPreHistoryHidden: hiddenPrehistory, isPreHistoryHidden: hiddenPrehistory,

View File

@ -980,8 +980,8 @@ export async function sendMessageAction({
}: { }: {
peer: ApiPeer; threadId?: ThreadId; action: ApiSendMessageAction; peer: ApiPeer; threadId?: ThreadId; action: ApiSendMessageAction;
}) { }) {
const gramAction = buildSendMessageAction(action); const mtpAction = buildSendMessageAction(action);
if (!gramAction) { if (!mtpAction) {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Unsupported message action', action); console.warn('Unsupported message action', action);
@ -993,7 +993,7 @@ export async function sendMessageAction({
const result = await invokeRequest(new GramJs.messages.SetTyping({ const result = await invokeRequest(new GramJs.messages.SetTyping({
peer: buildInputPeer(peer.id, peer.accessHash), peer: buildInputPeer(peer.id, peer.accessHash),
topMsgId: Number(threadId), topMsgId: Number(threadId),
action: gramAction, action: mtpAction,
}), { }), {
shouldThrow: true, shouldThrow: true,
abortControllerChatId: peer.id, abortControllerChatId: peer.id,

View File

@ -47,6 +47,9 @@ export interface ApiChat {
emojiStatus?: ApiEmojiStatusType; emojiStatus?: ApiEmojiStatusType;
isForum?: boolean; isForum?: boolean;
isForumAsMessages?: true; isForumAsMessages?: true;
isMonoforum?: boolean;
linkedMonoforumId?: string;
areChannelMessagesAllowed?: boolean;
boostLevel?: number; boostLevel?: number;
botVerificationIconId?: string; botVerificationIconId?: string;
hasAutoTranslation?: true; hasAutoTranslation?: true;

View File

@ -278,6 +278,7 @@ export interface ApiMessageActionPaidMessagesRefunded extends ActionMediaType {
export interface ApiMessageActionPaidMessagesPrice extends ActionMediaType { export interface ApiMessageActionPaidMessagesPrice extends ActionMediaType {
type: 'paidMessagesPrice'; type: 'paidMessagesPrice';
stars: number; stars: number;
isAllowedInChannel?: boolean;
} }
export interface ApiMessageActionUnsupported extends ActionMediaType { export interface ApiMessageActionUnsupported extends ActionMediaType {

View File

@ -379,6 +379,7 @@ export interface ApiInputMessageReplyInfo {
replyToMsgId: number; replyToMsgId: number;
replyToTopId?: number; replyToTopId?: number;
replyToPeerId?: string; replyToPeerId?: string;
monoforumPeerId?: string;
quoteText?: ApiFormattedText; quoteText?: ApiFormattedText;
quoteOffset?: number; quoteOffset?: number;
} }

View File

@ -250,6 +250,7 @@ export interface ApiAppConfig {
starsStargiftResaleAmountMin?: number; starsStargiftResaleAmountMin?: number;
starsStargiftResaleAmountMax?: number; starsStargiftResaleAmountMax?: number;
starsStargiftResaleCommissionPermille?: number; starsStargiftResaleCommissionPermille?: number;
pollMaxAnswers?: number;
} }
export interface ApiConfig { export interface ApiConfig {

View File

@ -1918,7 +1918,8 @@
"SectionDescriptionStarsForForMessages" = "You will receive {percent} of the selected fee (~{amount}) for each incoming message."; "SectionDescriptionStarsForForMessages" = "You will receive {percent} of the selected fee (~{amount}) for each incoming message.";
"SubtitlePrivacyAddUsers" = "Add Users"; "SubtitlePrivacyAddUsers" = "Add Users";
"PrivacyPaidMessagesValue" = "Paid"; "PrivacyPaidMessagesValue" = "Paid";
"FirstMessageInPaidMessagesChat" = "**{user}** charges {amount} for each message."; "MessagesPlaceholderPaidUser" = "**{peer}** charges {amount} for each message.";
"MessagesPlaceholderPaidChannel" = "**{peer}** charges {amount} per message to its admin.";
"ButtonBuyStars" = "Buy Stars"; "ButtonBuyStars" = "Buy Stars";
"ComposerPlaceholderPaidMessage" = "Message for {amount}"; "ComposerPlaceholderPaidMessage" = "Message for {amount}";
"ComposerPlaceholderPaidReply" = "Reply for {amount}"; "ComposerPlaceholderPaidReply" = "Reply for {amount}";
@ -1965,8 +1966,12 @@
"FrozenAccountAppealSubtitle" = "Appeal via {botLink} before {date} or your account will be deleted."; "FrozenAccountAppealSubtitle" = "Appeal via {botLink} before {date} or your account will be deleted.";
"ButtonAppeal" = "Submit an Appeal"; "ButtonAppeal" = "Submit an Appeal";
"ButtonUnderstood" = "Understood"; "ButtonUnderstood" = "Understood";
"ActionPaidMessageGroupPrice" = "Messages now cost **{stars}** in this group"; "ActionPaidMessagePrice" = "{peer} changed price to **{amount}** per message";
"ActionPaidMessageGroupPriceFree" = "Messages are now free in this group"; "ActionPaidMessagePriceYou" = "You changed price to **{amount}** per message";
"ActionPaidMessagePriceFree" = "{peer} made messages free";
"ActionPaidMessagePriceFreeYou" = "You made messages free";
"ActionMessageChannelFree" = "{peer} now accepts private messages";
"ActionMessageChannelDisabled" = "{peer} no longer accepts private messages";
"ApiMessageActionPaidMessagesRefundedOutgoing" = "You refunded **{stars}** to {user}"; "ApiMessageActionPaidMessagesRefundedOutgoing" = "You refunded **{stars}** to {user}";
"ApiMessageActionPaidMessagesRefundedIncoming" = "{user} refunded **{stars}** to you"; "ApiMessageActionPaidMessagesRefundedIncoming" = "{user} refunded **{stars}** to you";
"NotificationTitleNotSupportedInFrozenAccount" = "Your account is frozen"; "NotificationTitleNotSupportedInFrozenAccount" = "Your account is frozen";
@ -2012,4 +2017,8 @@
"ValueGiftSortByNumber" = "Number"; "ValueGiftSortByNumber" = "Number";
"ResellGiftsNoFound" = "No gifts found"; "ResellGiftsNoFound" = "No gifts found";
"ResellGiftsClearFilters" = "Clear Filters"; "ResellGiftsClearFilters" = "Clear Filters";
"MonoforumBadge" = "DIRECT";
"MonoforumStatus" = "Channel messages";
"MonoforumComposerPlaceholder" = "Choose a message to reply";
"ChannelSendMessage" = "Direct Messages";
"AutomaticTranslation" = "Automatic Translation"; "AutomaticTranslation" = "Automatic Translation";

View File

@ -150,4 +150,13 @@
&.premium-gradient-bg > .inner { &.premium-gradient-bg > .inner {
background-image: var(--premium-gradient); background-image: var(--premium-gradient);
} }
&.message-bubble {
--radius: 0;
mask-image: url("../../assets/icons/forumTopic/blue.svg");
mask-position: center;
mask-repeat: no-repeat;
mask-size: 100%;
}
} }

View File

@ -85,6 +85,7 @@ type OwnProps = {
storyViewerMode?: 'full' | 'single-peer' | 'disabled'; storyViewerMode?: 'full' | 'single-peer' | 'disabled';
loopIndefinitely?: boolean; loopIndefinitely?: boolean;
noPersonalPhoto?: boolean; noPersonalPhoto?: boolean;
asMessageBubble?: boolean;
observeIntersection?: ObserveFn; observeIntersection?: ObserveFn;
onClick?: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>, hasMedia: boolean) => void; onClick?: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>, hasMedia: boolean) => void;
onContextMenu?: (e: React.MouseEvent) => void; onContextMenu?: (e: React.MouseEvent) => void;
@ -111,6 +112,7 @@ const Avatar: FC<OwnProps> = ({
storyViewerMode = 'single-peer', storyViewerMode = 'single-peer',
loopIndefinitely, loopIndefinitely,
noPersonalPhoto, noPersonalPhoto,
asMessageBubble,
onClick, onClick,
onContextMenu, onContextMenu,
}) => { }) => {
@ -261,6 +263,7 @@ const Avatar: FC<OwnProps> = ({
isReplies && 'replies-bot-account', isReplies && 'replies-bot-account',
isPremiumGradient && 'premium-gradient-bg', isPremiumGradient && 'premium-gradient-bg',
isRoundedRect && 'forum', isRoundedRect && 'forum',
asMessageBubble && 'message-bubble',
(photo || webPhoto) && 'force-fit', (photo || webPhoto) && 'force-fit',
((withStory && realPeer?.hasStories) || forPremiumPromo) && 'with-story-circle', ((withStory && realPeer?.hasStories) || forPremiumPromo) && 'with-story-circle',
withStorySolid && realPeer?.hasStories && 'with-story-solid', withStorySolid && realPeer?.hasStories && 'with-story-solid',

View File

@ -300,6 +300,7 @@ type StateProps =
isAccountFrozen?: boolean; isAccountFrozen?: boolean;
isAppConfigLoaded?: boolean; isAppConfigLoaded?: boolean;
insertingPeerIdMention?: string; insertingPeerIdMention?: string;
pollMaxAnswers?: number;
}; };
enum MainButtonState { enum MainButtonState {
@ -413,10 +414,6 @@ const Composer: FC<OwnProps & StateProps> = ({
shouldPlayEffect, shouldPlayEffect,
maxMessageLength, maxMessageLength,
isSilentPosting, isSilentPosting,
onDropHide,
onFocus,
onBlur,
onForward,
isPaymentMessageConfirmDialogOpen, isPaymentMessageConfirmDialogOpen,
starsBalance, starsBalance,
isStarsBalanceModalOpen, isStarsBalanceModalOpen,
@ -424,6 +421,11 @@ const Composer: FC<OwnProps & StateProps> = ({
isAccountFrozen, isAccountFrozen,
isAppConfigLoaded, isAppConfigLoaded,
insertingPeerIdMention, insertingPeerIdMention,
pollMaxAnswers,
onDropHide,
onFocus,
onBlur,
onForward,
}) => { }) => {
const { const {
sendMessage, sendMessage,
@ -481,9 +483,12 @@ const Composer: FC<OwnProps & StateProps> = ({
const canMediaBeReplaced = editingMessage && canEditMedia(editingMessage); const canMediaBeReplaced = editingMessage && canEditMedia(editingMessage);
const isMonoforum = chat?.isMonoforum;
const { emojiSet, members: groupChatMembers, botCommands: chatBotCommands } = chatFullInfo || {}; const { emojiSet, members: groupChatMembers, botCommands: chatBotCommands } = chatFullInfo || {};
const chatEmojiSetId = emojiSet?.id; const chatEmojiSetId = emojiSet?.id;
const canSchedule = !paidMessagesStars && !isMonoforum;
const isSentStoryReactionHeart = sentStoryReaction && isSameReaction(sentStoryReaction, HEART_REACTION); const isSentStoryReactionHeart = sentStoryReaction && isSameReaction(sentStoryReaction, HEART_REACTION);
useEffect(processMessageInputForCustomEmoji, [getHtml]); useEffect(processMessageInputForCustomEmoji, [getHtml]);
@ -510,10 +515,10 @@ const Composer: FC<OwnProps & StateProps> = ({
}, [chatId]); }, [chatId]);
useEffect(() => { useEffect(() => {
if (isAppConfigLoaded && chatId && isReady && !isInStoryViewer) { if (isAppConfigLoaded && chatId && isReady && !isInStoryViewer && !isMonoforum) {
loadScheduledHistory({ chatId }); loadScheduledHistory({ chatId });
} }
}, [isReady, chatId, threadId, isInStoryViewer, isAppConfigLoaded]); }, [isReady, chatId, threadId, isInStoryViewer, isAppConfigLoaded, isMonoforum]);
useEffect(() => { useEffect(() => {
const isChannelWithProfiles = isChannel && chat?.areProfilesShown; const isChannelWithProfiles = isChannel && chat?.areProfilesShown;
@ -541,10 +546,11 @@ const Composer: FC<OwnProps & StateProps> = ({
() => getAllowedAttachmentOptions(chat, () => getAllowedAttachmentOptions(chat,
chatFullInfo, chatFullInfo,
isChatWithBot, isChatWithBot,
isChatWithSelf,
isInStoryViewer, isInStoryViewer,
paidMessagesStars, paidMessagesStars,
isInScheduledList), isInScheduledList),
[chat, chatFullInfo, isChatWithBot, isInStoryViewer, paidMessagesStars, isInScheduledList], [chat, chatFullInfo, isChatWithBot, isChatWithSelf, isInStoryViewer, paidMessagesStars, isInScheduledList],
); );
const isNeedPremium = isContactRequirePremium && isInStoryViewer; const isNeedPremium = isContactRequirePremium && isInStoryViewer;
@ -804,7 +810,7 @@ const Composer: FC<OwnProps & StateProps> = ({
getHtml, getHtml,
setHtml, setHtml,
editedMessage: editingMessage, editedMessage: editingMessage,
isDisabled: isInStoryViewer || Boolean(requestedDraft), isDisabled: isInStoryViewer || Boolean(requestedDraft) || isMonoforum,
}); });
const resetComposer = useLastCallback((shouldPreserveInput = false) => { const resetComposer = useLastCallback((shouldPreserveInput = false) => {
@ -1847,8 +1853,8 @@ const Composer: FC<OwnProps & StateProps> = ({
shouldForceAsFile={shouldForceAsFile} shouldForceAsFile={shouldForceAsFile}
isForCurrentMessageList={isForCurrentMessageList} isForCurrentMessageList={isForCurrentMessageList}
isForMessage={isInMessageList} isForMessage={isInMessageList}
shouldSchedule={!paidMessagesStars && isInScheduledList} shouldSchedule={canSchedule && isInScheduledList}
canSchedule={!paidMessagesStars} canSchedule={canSchedule}
forceDarkTheme={isInStoryViewer} forceDarkTheme={isInStoryViewer}
onCaptionUpdate={onCaptionUpdate} onCaptionUpdate={onCaptionUpdate}
onSendSilent={handleSendSilentAttachments} onSendSilent={handleSendSilentAttachments}
@ -1869,6 +1875,7 @@ const Composer: FC<OwnProps & StateProps> = ({
isOpen={pollModal.isOpen} isOpen={pollModal.isOpen}
isQuiz={pollModal.isQuiz} isQuiz={pollModal.isQuiz}
shouldBeAnonymous={isChannel} shouldBeAnonymous={isChannel}
maxOptionsCount={pollMaxAnswers}
onClear={closePollModal} onClear={closePollModal}
onSend={handlePollSend} onSend={handlePollSend}
/> />
@ -2304,7 +2311,7 @@ const Composer: FC<OwnProps & StateProps> = ({
{canShowCustomSendMenu && ( {canShowCustomSendMenu && (
<CustomSendMenu <CustomSendMenu
isOpen={isCustomSendMenuOpen} isOpen={isCustomSendMenuOpen}
canSchedule={!paidMessagesStars && isInMessageList && !isViewOnceEnabled} canSchedule={canSchedule && isInMessageList && !isViewOnceEnabled}
canScheduleUntilOnline={canScheduleUntilOnline && !isViewOnceEnabled} canScheduleUntilOnline={canScheduleUntilOnline && !isViewOnceEnabled}
onSendSilent={!isChatWithSelf ? handleSendSilent : undefined} onSendSilent={!isChatWithSelf ? handleSendSilent : undefined}
onSendSchedule={!isInScheduledList ? handleSendScheduled : undefined} onSendSchedule={!isInScheduledList ? handleSendScheduled : undefined}
@ -2344,6 +2351,7 @@ export default memo(withGlobal<OwnProps>(
(global, { (global, {
chatId, threadId, storyId, messageListType, isMobile, type, chatId, threadId, storyId, messageListType, isMobile, type,
}): StateProps => { }): StateProps => {
const appConfig = global.appConfig;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const chatBot = !isSystemBot(chatId) ? selectBot(global, chatId) : undefined; const chatBot = !isSystemBot(chatId) ? selectBot(global, chatId) : undefined;
const isChatWithBot = Boolean(chatBot); const isChatWithBot = Boolean(chatBot);
@ -2514,6 +2522,7 @@ export default memo(withGlobal<OwnProps>(
isAccountFrozen, isAccountFrozen,
isAppConfigLoaded, isAppConfigLoaded,
insertingPeerIdMention, insertingPeerIdMention,
pollMaxAnswers: appConfig?.pollMaxAnswers,
}; };
}, },
)(Composer)); )(Composer));

View File

@ -90,11 +90,11 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
contactName, contactName,
willDeleteForCurrentUserOnly, willDeleteForCurrentUserOnly,
willDeleteForAll, willDeleteForAll,
onConfirm,
chatBot, chatBot,
adminMembersById, adminMembersById,
canBanUsers, canBanUsers,
linkedChatId, linkedChatId,
onConfirm,
}) => { }) => {
const { const {
closeDeleteMessageModal, closeDeleteMessageModal,
@ -130,7 +130,11 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
const global = getGlobal(); const global = getGlobal();
const senderArray = getSendersFromSelectedMessages(global, chat.id, messageIds); const senderArray = getSendersFromSelectedMessages(global, chat.id, messageIds);
return senderArray ? unique(senderArray) return senderArray ? unique(senderArray)
.filter((peer) => peer?.id !== chat?.id && peer?.id !== linkedChatId) : MEMO_EMPTY_ARRAY; .filter((peer) => (
peer?.id !== chat?.id
&& peer?.id !== linkedChatId
&& peer?.id !== chat?.linkedMonoforumId
)) : MEMO_EMPTY_ARRAY;
}, [chat, isChannel, linkedChatId, messageIds]); }, [chat, isChannel, linkedChatId, messageIds]);
const buildNestedOptionListWithAvatars = useLastCallback(() => { const buildNestedOptionListWithAvatars = useLastCallback(() => {
@ -144,16 +148,24 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
}); });
const peerListToDeleteAll = useMemo(() => { const peerListToDeleteAll = useMemo(() => {
return peerList.filter((peer) => peer.id !== linkedChatId && peer.id !== currentUserId); return peerList.filter((peer) => (
}, [peerList, currentUserId, linkedChatId]); peer.id !== linkedChatId
&& peer.id !== chat?.linkedMonoforumId
&& peer.id !== currentUserId
));
}, [peerList, currentUserId, linkedChatId, chat?.linkedMonoforumId]);
const peerListToReportSpam = useMemo(() => { const peerListToReportSpam = useMemo(() => {
return peerList.filter((peer) => peer.id !== currentUserId && peer.id !== linkedChatId); return peerList.filter((peer) => (
}, [peerList, currentUserId, linkedChatId]); peer.id !== currentUserId
&& peer.id !== linkedChatId
&& peer.id !== chat?.linkedMonoforumId
));
}, [peerList, currentUserId, linkedChatId, chat?.linkedMonoforumId]);
const peerListToBan = useMemo(() => { const peerListToBan = useMemo(() => {
const isCurrentUserInList = peerList.some((peer) => peer.id === currentUserId); const isCurrentUserInList = peerList.some((peer) => peer.id === currentUserId);
const shouldReturnEmpty = !canBanUsers || isCurrentUserInList; const shouldReturnEmpty = !canBanUsers || isCurrentUserInList || chat?.isMonoforum;
if (shouldReturnEmpty) { if (shouldReturnEmpty) {
return MEMO_EMPTY_ARRAY; return MEMO_EMPTY_ARRAY;
@ -163,7 +175,7 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
const isAdmin = adminMembersById?.[peer.id]; const isAdmin = adminMembersById?.[peer.id];
return isCreator || !isAdmin; return isCreator || !isAdmin;
}); });
}, [peerList, isCreator, currentUserId, canBanUsers, adminMembersById]); }, [peerList, isCreator, currentUserId, canBanUsers, adminMembersById, chat?.isMonoforum]);
const shouldShowAdditionalOptions = useMemo(() => { const shouldShowAdditionalOptions = useMemo(() => {
return Boolean(peerListToDeleteAll.length || peerListToReportSpam.length || peerListToBan.length); return Boolean(peerListToDeleteAll.length || peerListToReportSpam.length || peerListToBan.length);
@ -184,11 +196,12 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
label: oldLang('ReportSpamTitle'), label: oldLang('ReportSpamTitle'),
nestedOptions: messageIds && peerList.length >= 2 ? [ nestedOptions: messageIds && peerList.length >= 2 ? [
...buildNestedOptionListWithAvatars().filter((opt) => opt.value !== linkedChatId ...buildNestedOptionListWithAvatars().filter((opt) => opt.value !== linkedChatId
&& opt.value !== chat?.linkedMonoforumId
&& opt.value !== currentUserId), && opt.value !== currentUserId),
] : undefined, ] : undefined,
}, },
]; ];
}, [messageIds, peerList, oldLang, linkedChatId, currentUserId]); }, [messageIds, peerList, oldLang, linkedChatId, chat?.linkedMonoforumId, currentUserId]);
const ACTION_DELETE_OPTION: IRadioOption[] = useMemo(() => { const ACTION_DELETE_OPTION: IRadioOption[] = useMemo(() => {
return [ return [
@ -199,11 +212,12 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
: oldLang('DeleteAllFrom', Object.values(peerNames)[0]), : oldLang('DeleteAllFrom', Object.values(peerNames)[0]),
nestedOptions: messageIds && peerList.length >= 2 ? [ nestedOptions: messageIds && peerList.length >= 2 ? [
...buildNestedOptionListWithAvatars().filter((opt) => opt.value !== linkedChatId ...buildNestedOptionListWithAvatars().filter((opt) => opt.value !== linkedChatId
&& opt.value !== chat?.linkedMonoforumId
&& opt.value !== currentUserId), && opt.value !== currentUserId),
] : undefined, ] : undefined,
}, },
]; ];
}, [messageIds, peerList, oldLang, peerNames, linkedChatId, currentUserId]); }, [messageIds, peerList, oldLang, peerNames, linkedChatId, chat?.linkedMonoforumId, currentUserId]);
const ACTION_BAN_OPTION: IRadioOption[] = useMemo(() => { const ACTION_BAN_OPTION: IRadioOption[] = useMemo(() => {
return [ return [
@ -279,7 +293,7 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
if (isSchedule) { if (isSchedule) {
deleteScheduledMessages({ messageIds }); deleteScheduledMessages({ messageIds });
} else if (shouldShowOption) { } else if (shouldShowOption) {
if (peerIdsToReportSpam) { if (peerIdsToReportSpam?.length) {
const global = getGlobal(); const global = getGlobal();
const peerIdList = peerIdsToReportSpam.filter((option) => !Number.isNaN(Number(option))); const peerIdList = peerIdsToReportSpam.filter((option) => !Number.isNaN(Number(option)));
const messageList = messageIds.reduce<Record<string, number[]>>((acc, msgId) => { const messageList = messageIds.reduce<Record<string, number[]>>((acc, msgId) => {
@ -296,24 +310,24 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
handleReportSpam(messageList); handleReportSpam(messageList);
} }
if (peerIdsToDeleteAll) { if (peerIdsToDeleteAll?.length) {
const peerIdList = peerIdsToDeleteAll.filter((option) => !Number.isNaN(Number(option))); const peerIdList = peerIdsToDeleteAll.filter((option) => !Number.isNaN(Number(option)));
handleDeleteAllPeerMessages(peerIdList); handleDeleteAllPeerMessages(peerIdList);
} }
if (peerIdsToBan && !havePermissionChanged) { if (peerIdsToBan?.length && !havePermissionChanged) {
const peerIdList = peerIdsToBan.filter((option) => !Number.isNaN(Number(option))); const peerIdList = peerIdsToBan.filter((option) => !Number.isNaN(Number(option)));
handleDeleteMember(peerIdList); handleDeleteMember(peerIdList);
const filteredMessageIdList = filterMessageIdByPeerId(peerIdList, messageIds); const filteredMessageIdList = filterMessageIdByPeerId(peerIdList, messageIds);
handleDeleteMessages(filteredMessageIdList); handleDeleteMessages(filteredMessageIdList);
} }
if (peerIdsToBan && havePermissionChanged) { if (peerIdsToBan?.length && havePermissionChanged) {
const peerIdList = peerIdsToBan.filter((option) => !Number.isNaN(Number(option))); const peerIdList = peerIdsToBan.filter((option) => !Number.isNaN(Number(option)));
handleUpdateChatMemberBannedRights(peerIdList); handleUpdateChatMemberBannedRights(peerIdList);
} }
if (!peerIdsToReportSpam || !peerIdsToDeleteAll || !peerIdsToBan) { if (!peerIdsToReportSpam?.length || !peerIdsToDeleteAll?.length || !peerIdsToBan?.length) {
deleteMessages({ messageIds, shouldDeleteForAll }); deleteMessages({ messageIds, shouldDeleteForAll });
} }
} else { } else {
@ -426,21 +440,19 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
<p className={styles.actionTitle}>{oldLang('DeleteAdditionalActions')}</p> <p className={styles.actionTitle}>{oldLang('DeleteAdditionalActions')}</p>
{renderAdditionalActionOptions()} {renderAdditionalActionOptions()}
{renderPartiallyRestrictedUser()} {renderPartiallyRestrictedUser()}
{ {peerIdsToBan?.length && canBanUsers ? (
peerIdsToBan && canBanUsers ? ( <ListItem
<ListItem narrow
narrow buttonClassName={styles.button}
buttonClassName={styles.button} onClick={toggleAdditionalOptions}
onClick={toggleAdditionalOptions} >
> {oldLang(isAdditionalOptionsVisible ? 'DeleteToggleBanUsers' : 'DeleteToggleRestrictUsers')}
{oldLang(isAdditionalOptionsVisible ? 'DeleteToggleBanUsers' : 'DeleteToggleRestrictUsers')} <Icon
<Icon name={isAdditionalOptionsVisible ? 'up' : 'down'}
name={isAdditionalOptionsVisible ? 'up' : 'down'} className={buildClassName(styles.button, 'ml-2')}
className={buildClassName(styles.button, 'ml-2')} />
/> </ListItem>
</ListItem> ) : setIsAdditionalOptionsVisible(false)}
) : setIsAdditionalOptionsVisible(false)
}
</> </>
)} )}
{(canDeleteForAll || chatBot || !shouldShowOption) && ( {(canDeleteForAll || chatBot || !shouldShowOption) && (

View File

@ -38,3 +38,15 @@
display: flex; display: flex;
align-items: center; align-items: center;
} }
.monoforumBadge {
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-size: 0.625rem;
font-weight: 500;
line-height: 1;
color: var(--color-white);
background-color: var(--color-gray);
}

View File

@ -24,6 +24,7 @@ import { copyTextToClipboard } from '../../util/clipboard';
import stopEvent from '../../util/stopEvent'; import stopEvent from '../../util/stopEvent';
import renderText from './helpers/renderText'; import renderText from './helpers/renderText';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback'; import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang'; import useOldLang from '../../hooks/useOldLang';
@ -44,12 +45,14 @@ type OwnProps = {
emojiStatusSize?: number; emojiStatusSize?: number;
isSavedMessages?: boolean; isSavedMessages?: boolean;
isSavedDialog?: boolean; isSavedDialog?: boolean;
isMonoforum?: boolean;
monoforumBadgeClassName?: string;
noLoopLimit?: boolean; noLoopLimit?: boolean;
canCopyTitle?: boolean; canCopyTitle?: boolean;
iconElement?: React.ReactNode; iconElement?: React.ReactNode;
statusSparklesColor?: string;
onEmojiStatusClick?: NoneToVoidFunction; onEmojiStatusClick?: NoneToVoidFunction;
observeIntersection?: ObserveFn; observeIntersection?: ObserveFn;
statusSparklesColor?: string;
}; };
const FullNameTitle: FC<OwnProps> = ({ const FullNameTitle: FC<OwnProps> = ({
@ -65,15 +68,20 @@ const FullNameTitle: FC<OwnProps> = ({
canCopyTitle, canCopyTitle,
iconElement, iconElement,
statusSparklesColor, statusSparklesColor,
isMonoforum,
monoforumBadgeClassName,
onEmojiStatusClick, onEmojiStatusClick,
observeIntersection, observeIntersection,
}) => { }) => {
const lang = useOldLang();
const { showNotification } = getActions(); const { showNotification } = getActions();
const oldLang = useOldLang();
const lang = useLang();
const realPeer = 'id' in peer ? peer : undefined; const realPeer = 'id' in peer ? peer : undefined;
const customPeer = 'isCustomPeer' in peer ? peer : undefined; const customPeer = 'isCustomPeer' in peer ? peer : undefined;
const isUser = realPeer && isApiPeerUser(realPeer); const isUser = realPeer && isApiPeerUser(realPeer);
const title = realPeer && (isUser ? getUserFullName(realPeer) : getChatTitle(lang, realPeer)); const title = realPeer && (isUser ? getUserFullName(realPeer) : getChatTitle(oldLang, realPeer));
const isPremium = (isUser && realPeer.isPremium) || customPeer?.isPremium; const isPremium = (isUser && realPeer.isPremium) || customPeer?.isPremium;
const canShowEmojiStatus = withEmojiStatus && !isSavedMessages; const canShowEmojiStatus = withEmojiStatus && !isSavedMessages;
const emojiStatus = realPeer?.emojiStatus const emojiStatus = realPeer?.emojiStatus
@ -91,27 +99,27 @@ const FullNameTitle: FC<OwnProps> = ({
const specialTitle = useMemo(() => { const specialTitle = useMemo(() => {
if (customPeer) { if (customPeer) {
return renderText(customPeer.title || lang(customPeer.titleKey!)); return renderText(customPeer.title || oldLang(customPeer.titleKey!));
} }
if (isSavedMessages) { if (isSavedMessages) {
return lang(isSavedDialog ? 'MyNotes' : 'SavedMessages'); return oldLang(isSavedDialog ? 'MyNotes' : 'SavedMessages');
} }
if (isAnonymousForwardsChat(realPeer!.id)) { if (isAnonymousForwardsChat(realPeer!.id)) {
return lang('AnonymousForward'); return oldLang('AnonymousForward');
} }
if (isChatWithRepliesBot(realPeer!.id)) { if (isChatWithRepliesBot(realPeer!.id)) {
return lang('RepliesTitle'); return oldLang('RepliesTitle');
} }
if (isChatWithVerificationCodesBot(realPeer!.id)) { if (isChatWithVerificationCodesBot(realPeer!.id)) {
return lang('VerifyCodesNotifications'); return oldLang('VerifyCodesNotifications');
} }
return undefined; return undefined;
}, [customPeer, isSavedDialog, isSavedMessages, lang, realPeer]); }, [customPeer, isSavedDialog, isSavedMessages, oldLang, realPeer]);
const botVerificationIconId = realPeer?.botVerificationIconId; const botVerificationIconId = realPeer?.botVerificationIconId;
return ( return (
@ -164,6 +172,11 @@ const FullNameTitle: FC<OwnProps> = ({
</Transition> </Transition>
)} )}
{canShowEmojiStatus && !emojiStatus && isPremium && <StarIcon />} {canShowEmojiStatus && !emojiStatus && isPremium && <StarIcon />}
{isMonoforum && (
<div className={buildClassName(styles.monoforumBadge, monoforumBadgeClassName)}>
{lang('MonoforumBadge')}
</div>
)}
</> </>
)} )}
{iconElement} {iconElement}

View File

@ -19,6 +19,7 @@ import {
selectChat, selectChat,
selectChatMessages, selectChatMessages,
selectChatOnlineCount, selectChatOnlineCount,
selectMonoforumChannel,
selectThreadInfo, selectThreadInfo,
selectThreadMessagesCount, selectThreadMessagesCount,
selectTopic, selectTopic,
@ -28,6 +29,7 @@ import buildClassName from '../../util/buildClassName';
import { REM } from './helpers/mediaDimensions'; import { REM } from './helpers/mediaDimensions';
import renderText from './helpers/renderText'; import renderText from './helpers/renderText';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback'; import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang'; import useOldLang from '../../hooks/useOldLang';
@ -63,20 +65,21 @@ type OwnProps = {
withStory?: boolean; withStory?: boolean;
storyViewerOrigin?: StoryViewerOrigin; storyViewerOrigin?: StoryViewerOrigin;
isSavedDialog?: boolean; isSavedDialog?: boolean;
withMonoforumStatus?: boolean;
onClick?: VoidFunction; onClick?: VoidFunction;
onEmojiStatusClick?: NoneToVoidFunction; onEmojiStatusClick?: NoneToVoidFunction;
}; };
type StateProps = type StateProps = {
{ chat?: ApiChat;
chat?: ApiChat; threadInfo?: ApiThreadInfo;
threadInfo?: ApiThreadInfo; topic?: ApiTopic;
topic?: ApiTopic; onlineCount?: number;
onlineCount?: number; areMessagesLoaded: boolean;
areMessagesLoaded: boolean; messagesCount?: number;
messagesCount?: number; self?: ApiUser;
self?: ApiUser; monoforumChannel?: ApiChat;
}; };
const GroupChatInfo: FC<OwnProps & StateProps> = ({ const GroupChatInfo: FC<OwnProps & StateProps> = ({
typingStatus, typingStatus,
@ -93,7 +96,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
withChatType, withChatType,
threadInfo, threadInfo,
noRtl, noRtl,
chat, chat: realChat,
onlineCount, onlineCount,
areMessagesLoaded, areMessagesLoaded,
topic, topic,
@ -105,6 +108,8 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
emojiStatusSize, emojiStatusSize,
isSavedDialog, isSavedDialog,
self, self,
withMonoforumStatus,
monoforumChannel,
onClick, onClick,
onEmojiStatusClick, onEmojiStatusClick,
}) => { }) => {
@ -114,7 +119,10 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
loadMoreProfilePhotos, loadMoreProfilePhotos,
} = getActions(); } = getActions();
const lang = useOldLang(); const chat = !withMonoforumStatus && monoforumChannel ? monoforumChannel : realChat;
const oldLang = useOldLang();
const lang = useLang();
const isSuperGroup = chat && isChatSuperGroup(chat); const isSuperGroup = chat && isChatSuperGroup(chat);
const isTopic = Boolean(chat?.isForum && threadInfo && topic); const isTopic = Boolean(chat?.isForum && threadInfo && topic);
@ -148,6 +156,24 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
} }
function renderStatusOrTyping() { function renderStatusOrTyping() {
if (withUpdatingStatus && !areMessagesLoaded && !isRestricted) {
return (
<DotAnimation className="status" content={oldLang('Updating')} />
);
}
if (withMonoforumStatus) {
return (
<span className="status" dir="auto">
{lang('MonoforumStatus')}
</span>
);
}
if (realChat?.isMonoforum) {
return undefined;
}
if (status) { if (status) {
return withDots ? ( return withDots ? (
<DotAnimation className="status" content={status} /> <DotAnimation className="status" content={status} />
@ -159,12 +185,6 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
); );
} }
if (withUpdatingStatus && !areMessagesLoaded && !isRestricted) {
return (
<DotAnimation className="status" content={lang('Updating')} />
);
}
if (!chat) { if (!chat) {
return undefined; return undefined;
} }
@ -182,7 +202,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
activeKey={messagesCount !== undefined ? 1 : 2} activeKey={messagesCount !== undefined ? 1 : 2}
className="message-count-transition" className="message-count-transition"
> >
{messagesCount !== undefined && lang('messages', messagesCount, 'i')} {messagesCount !== undefined && oldLang('messages', messagesCount, 'i')}
</Transition> </Transition>
</span> </span>
); );
@ -190,12 +210,12 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
if (withChatType) { if (withChatType) {
return ( return (
<span className="status" dir="auto">{lang(getChatTypeString(chat))}</span> <span className="status" dir="auto">{oldLang(getChatTypeString(chat))}</span>
); );
} }
const groupStatus = getGroupStatus(lang, chat); const groupStatus = getGroupStatus(oldLang, chat);
const onlineStatus = onlineCount ? `, ${lang('OnlineCount', onlineCount, 'i')}` : undefined; const onlineStatus = onlineCount ? `, ${oldLang('OnlineCount', onlineCount, 'i')}` : undefined;
return ( return (
<span className="status"> <span className="status">
@ -211,7 +231,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
className={ className={
buildClassName('ChatInfo', className) buildClassName('ChatInfo', className)
} }
dir={!noRtl && lang.isRtl ? 'rtl' : undefined} dir={!noRtl && oldLang.isRtl ? 'rtl' : undefined}
onClick={onClick} onClick={onClick}
> >
{!noAvatar && !isTopic && ( {!noAvatar && !isTopic && (
@ -231,6 +251,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
size={avatarSize} size={avatarSize}
peer={chat} peer={chat}
withStory={withStory} withStory={withStory}
asMessageBubble={Boolean(monoforumChannel)}
storyViewerOrigin={storyViewerOrigin} storyViewerOrigin={storyViewerOrigin}
storyViewerMode="single-peer" storyViewerMode="single-peer"
isSavedDialog={isSavedDialog} isSavedDialog={isSavedDialog}
@ -251,6 +272,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
: ( : (
<FullNameTitle <FullNameTitle
peer={chat} peer={chat}
isMonoforum={!withMonoforumStatus && Boolean(monoforumChannel)}
emojiStatusSize={emojiStatusSize} emojiStatusSize={emojiStatusSize}
withEmojiStatus={!noEmojiStatus} withEmojiStatus={!noEmojiStatus}
isSavedDialog={isSavedDialog} isSavedDialog={isSavedDialog}
@ -272,6 +294,7 @@ export default memo(withGlobal<OwnProps>(
const topic = threadId ? selectTopic(global, chatId, threadId) : undefined; const topic = threadId ? selectTopic(global, chatId, threadId) : undefined;
const messagesCount = topic && selectThreadMessagesCount(global, chatId, threadId!); const messagesCount = topic && selectThreadMessagesCount(global, chatId, threadId!);
const self = selectUser(global, global.currentUserId!); const self = selectUser(global, global.currentUserId!);
const monoforumChannel = selectMonoforumChannel(global, chatId);
return { return {
chat, chat,
@ -281,6 +304,7 @@ export default memo(withGlobal<OwnProps>(
areMessagesLoaded, areMessagesLoaded,
messagesCount, messagesCount,
self, self,
monoforumChannel,
}; };
}, },
)(GroupChatInfo)); )(GroupChatInfo));

View File

@ -27,6 +27,7 @@ import { MEMO_EMPTY_ARRAY } from '../../util/memo';
import renderText from './helpers/renderText'; import renderText from './helpers/renderText';
import useIntervalForceUpdate from '../../hooks/schedulers/useIntervalForceUpdate'; import useIntervalForceUpdate from '../../hooks/schedulers/useIntervalForceUpdate';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback'; import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang'; import useOldLang from '../../hooks/useOldLang';
import usePreviousDeprecated from '../../hooks/usePreviousDeprecated'; import usePreviousDeprecated from '../../hooks/usePreviousDeprecated';
@ -45,6 +46,7 @@ type OwnProps = {
peerId: string; peerId: string;
forceShowSelf?: boolean; forceShowSelf?: boolean;
canPlayVideo: boolean; canPlayVideo: boolean;
isForMonoforum?: boolean;
}; };
type StateProps = type StateProps =
@ -81,6 +83,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
emojiStatusSlug, emojiStatusSlug,
profilePhotos, profilePhotos,
peerId, peerId,
isForMonoforum,
}) => { }) => {
const { const {
openMediaViewer, openMediaViewer,
@ -91,7 +94,8 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
openUniqueGiftBySlug, openUniqueGiftBySlug,
} = getActions(); } = getActions();
const lang = useOldLang(); const oldLang = useOldLang();
const lang = useLang();
useIntervalForceUpdate(user ? STATUS_UPDATE_INTERVAL : undefined); useIntervalForceUpdate(user ? STATUS_UPDATE_INTERVAL : undefined);
@ -100,7 +104,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
const prevAvatarOwnerId = usePreviousDeprecated(avatarOwnerId); const prevAvatarOwnerId = usePreviousDeprecated(avatarOwnerId);
const [hasSlideAnimation, setHasSlideAnimation] = useState(true); const [hasSlideAnimation, setHasSlideAnimation] = useState(true);
// slideOptimized doesn't work well when animation is dynamically disabled // slideOptimized doesn't work well when animation is dynamically disabled
const slideAnimation = hasSlideAnimation ? (lang.isRtl ? 'slideRtl' : 'slide') : 'none'; const slideAnimation = hasSlideAnimation ? (oldLang.isRtl ? 'slideRtl' : 'slide') : 'none';
const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0); const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0);
const isFirst = photos.length <= 1 || currentPhotoIndex === 0; const isFirst = photos.length <= 1 || currentPhotoIndex === 0;
@ -217,9 +221,9 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
letterClassName={styles.topicIconTitle} letterClassName={styles.topicIconTitle}
noLoopLimit noLoopLimit
/> />
<h3 className={styles.topicTitle} dir={lang.isRtl ? 'rtl' : undefined}>{renderText(topic!.title)}</h3> <h3 className={styles.topicTitle} dir={oldLang.isRtl ? 'rtl' : undefined}>{renderText(topic!.title)}</h3>
<p className={styles.topicMessagesCounter}> <p className={styles.topicMessagesCounter}>
{messagesCount ? lang('Chat.Title.Topic', messagesCount, 'i') : lang('lng_forum_no_messages')} {messagesCount ? oldLang('Chat.Title.Topic', messagesCount, 'i') : oldLang('lng_forum_no_messages')}
</p> </p>
</div> </div>
); );
@ -265,6 +269,14 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
const isSystemBotChat = isSystemBot(peerId); const isSystemBotChat = isSystemBot(peerId);
if (isAnonymousForwards || isSystemBotChat) return undefined; if (isAnonymousForwards || isSystemBotChat) return undefined;
if (isForMonoforum) {
return (
<span className={buildClassName(styles.status, 'status')} dir="auto">
{lang('MonoforumStatus')}
</span>
);
}
if (user) { if (user) {
return ( return (
<div <div
@ -275,11 +287,11 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
)} )}
> >
<span className={styles.userStatus} dir="auto"> <span className={styles.userStatus} dir="auto">
{getUserStatus(lang, user, userStatus)} {getUserStatus(oldLang, user, userStatus)}
</span> </span>
{userStatus?.isReadDateRestrictedByMe && ( {userStatus?.isReadDateRestrictedByMe && (
<span className={styles.getStatus} onClick={handleOpenGetReadDateModal}> <span className={styles.getStatus} onClick={handleOpenGetReadDateModal}>
<span>{lang('StatusHiddenShow')}</span> <span>{oldLang('StatusHiddenShow')}</span>
</span> </span>
)} )}
</div> </div>
@ -290,8 +302,8 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
<span className={buildClassName(styles.status, 'status')} dir="auto"> <span className={buildClassName(styles.status, 'status')} dir="auto">
{ {
isChatChannel(chat!) isChatChannel(chat!)
? lang('Subscribers', chat!.membersCount ?? 0, 'i') ? oldLang('Subscribers', chat!.membersCount ?? 0, 'i')
: lang('Members', chat!.membersCount ?? 0, 'i') : oldLang('Members', chat!.membersCount ?? 0, 'i')
} }
</span> </span>
); );
@ -304,7 +316,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
return ( return (
<div <div
className={buildClassName('ProfileInfo')} className={buildClassName('ProfileInfo')}
dir={lang.isRtl ? 'rtl' : undefined} dir={oldLang.isRtl ? 'rtl' : undefined}
> >
<div className={styles.photoWrapper}> <div className={styles.photoWrapper}>
{renderPhotoTabs()} {renderPhotoTabs()}
@ -315,7 +327,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
)} )}
> >
<div className={styles.fallbackPhotoContents}> <div className={styles.fallbackPhotoContents}>
{lang(profilePhotos.personalPhoto.isVideo ? 'UserInfo.CustomVideo' : 'UserInfo.CustomPhoto')} {oldLang(profilePhotos.personalPhoto.isVideo ? 'UserInfo.CustomVideo' : 'UserInfo.CustomPhoto')}
</div> </div>
</div> </div>
)} )}
@ -333,7 +345,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
size="mini" size="mini"
/> />
)} )}
{lang(profilePhotos.fallbackPhoto.isVideo ? 'UserInfo.PublicVideo' : 'UserInfo.PublicPhoto')} {oldLang(profilePhotos.fallbackPhoto.isVideo ? 'UserInfo.PublicVideo' : 'UserInfo.PublicPhoto')}
</div> </div>
</div> </div>
)} )}
@ -344,24 +356,24 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
{!isFirst && ( {!isFirst && (
<button <button
type="button" type="button"
dir={lang.isRtl ? 'rtl' : undefined} dir={oldLang.isRtl ? 'rtl' : undefined}
className={buildClassName(styles.navigation, styles.navigation_prev)} className={buildClassName(styles.navigation, styles.navigation_prev)}
aria-label={lang('AccDescrPrevious')} aria-label={oldLang('AccDescrPrevious')}
onClick={selectPreviousMedia} onClick={selectPreviousMedia}
/> />
)} )}
{!isLast && ( {!isLast && (
<button <button
type="button" type="button"
dir={lang.isRtl ? 'rtl' : undefined} dir={oldLang.isRtl ? 'rtl' : undefined}
className={buildClassName(styles.navigation, styles.navigation_next)} className={buildClassName(styles.navigation, styles.navigation_next)}
aria-label={lang('Next')} aria-label={oldLang('Next')}
onClick={selectNextMedia} onClick={selectNextMedia}
/> />
)} )}
</div> </div>
<div className={styles.info} dir={lang.isRtl ? 'rtl' : 'auto'}> <div className={styles.info} dir={oldLang.isRtl ? 'rtl' : 'auto'}>
{(user || chat) && ( {(user || chat) && (
<FullNameTitle <FullNameTitle
peer={(user || chat)!} peer={(user || chat)!}

View File

@ -12,7 +12,7 @@ import {
} from '../../global/helpers'; } from '../../global/helpers';
import { filterPeersByQuery } from '../../global/helpers/peers'; import { filterPeersByQuery } from '../../global/helpers/peers';
import { import {
filterChatIdsByType, selectChat, selectChatFullInfo, selectUser, filterChatIdsByType, selectChat, selectChatFullInfo, selectIsMonoforumAdmin, selectUser,
} from '../../global/selectors'; } from '../../global/selectors';
import { unique } from '../../util/iteratees'; import { unique } from '../../util/iteratees';
import sortChatIds from './helpers/sortChatIds'; import sortChatIds from './helpers/sortChatIds';
@ -77,9 +77,15 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
const user = selectUser(global, id); const user = selectUser(global, id);
if (user && !isDeletedUser(user)) return true; if (user && !isDeletedUser(user)) return true;
const chatFullInfo = selectChatFullInfo(global, id); if (!chat) return false;
return chat && (!chatFullInfo || getCanPostInChat(chat, undefined, undefined, chatFullInfo)); if (chat.isMonoforum && selectIsMonoforumAdmin(global, id)) {
return false;
}
const chatFullInfo = selectChatFullInfo(global, id);
// TODO: Handle bulk check with API call
return !chatFullInfo || getCanPostInChat(chat, undefined, undefined, chatFullInfo);
}); });
const sorted = sortChatIds( const sorted = sortChatIds(

View File

@ -10,6 +10,7 @@ import type { MessageList } from '../../types';
import { EMOJI_SIZE_MODAL, STICKER_SIZE_MODAL, TME_LINK_PREFIX } from '../../config'; import { EMOJI_SIZE_MODAL, STICKER_SIZE_MODAL, TME_LINK_PREFIX } from '../../config';
import { getAllowedAttachmentOptions, getCanPostInChat } from '../../global/helpers'; import { getAllowedAttachmentOptions, getCanPostInChat } from '../../global/helpers';
import { import {
selectBot,
selectCanScheduleUntilOnline, selectCanScheduleUntilOnline,
selectChat, selectChat,
selectChatFullInfo, selectChatFullInfo,
@ -261,7 +262,12 @@ export default memo(withGlobal<OwnProps>(
const { chatId, threadId } = currentMessageList || {}; const { chatId, threadId } = currentMessageList || {};
const chat = chatId && selectChat(global, chatId); const chat = chatId && selectChat(global, chatId);
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined; const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
const sendOptions = chat ? getAllowedAttachmentOptions(chat, chatFullInfo) : undefined; const chatBot = chatId && selectBot(global, chatId);
const isSavedMessages = chatId ? selectIsChatWithSelf(global, chatId) : undefined;
const sendOptions = chat
? getAllowedAttachmentOptions(chat, chatFullInfo, Boolean(chatBot), isSavedMessages)
: undefined;
const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined; const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined;
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId); const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
const topic = chatId && threadId ? selectTopic(global, chatId, threadId) : undefined; const topic = chatId && threadId ? selectTopic(global, chatId, threadId) : undefined;
@ -269,7 +275,6 @@ export default memo(withGlobal<OwnProps>(
chat && threadId && getCanPostInChat(chat, topic, isMessageThread, chatFullInfo) chat && threadId && getCanPostInChat(chat, topic, isMessageThread, chatFullInfo)
&& sendOptions?.canSendStickers, && sendOptions?.canSendStickers,
); );
const isSavedMessages = Boolean(chatId) && selectIsChatWithSelf(global, chatId);
const stickerSetInfo = fromSticker ? fromSticker.stickerSetInfo const stickerSetInfo = fromSticker ? fromSticker.stickerSetInfo
: stickerSetShortName ? { shortName: stickerSetShortName } : undefined; : stickerSetShortName ? { shortName: stickerSetShortName } : undefined;

View File

@ -14,7 +14,7 @@ import {
getCanPostInChat, getGroupStatus, getUserStatus, isUserOnline, getCanPostInChat, getGroupStatus, getUserStatus, isUserOnline,
} from '../../../global/helpers'; } from '../../../global/helpers';
import { isApiPeerChat } from '../../../global/helpers/peers'; import { isApiPeerChat } from '../../../global/helpers/peers';
import { selectPeer, selectTopics, selectUserStatus } from '../../../global/selectors'; import { selectMonoforumChannel, selectPeer, selectTopics, selectUserStatus } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { REM } from '../helpers/mediaDimensions'; import { REM } from '../helpers/mediaDimensions';
import renderText from '../helpers/renderText'; import renderText from '../helpers/renderText';
@ -47,12 +47,12 @@ export type OwnProps = {
searchPlaceholder: string; searchPlaceholder: string;
search: string; search: string;
className?: string; className?: string;
isLowStackPriority?: boolean;
loadMore?: NoneToVoidFunction; loadMore?: NoneToVoidFunction;
onSearchChange: (search: string) => void; onSearchChange: (search: string) => void;
onSelectChatOrUser: (chatOrUserId: string, threadId?: ThreadId) => void; onSelectChatOrUser: (chatOrUserId: string, threadId?: ThreadId) => void;
onClose: NoneToVoidFunction; onClose: NoneToVoidFunction;
onCloseAnimationEnd?: NoneToVoidFunction; onCloseAnimationEnd?: NoneToVoidFunction;
isLowStackPriority?: boolean;
}; };
const CHAT_LIST_SLIDE = 0; const CHAT_LIST_SLIDE = 0;
@ -77,7 +77,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
}) => { }) => {
const { loadTopics } = getActions(); const { loadTopics } = getActions();
const lang = useOldLang(); const oldLang = useOldLang();
const containerRef = useRef<HTMLDivElement>(); const containerRef = useRef<HTMLDivElement>();
const topicContainerRef = useRef<HTMLDivElement>(); const topicContainerRef = useRef<HTMLDivElement>();
const searchRef = useRef<HTMLInputElement>(); const searchRef = useRef<HTMLInputElement>();
@ -181,23 +181,28 @@ const ChatOrUserPicker: FC<OwnProps> = ({
const renderChatItem = useCallback((id: string, index: number) => { const renderChatItem = useCallback((id: string, index: number) => {
const global = getGlobal(); const global = getGlobal();
const peer = selectPeer(global, id); let peer = selectPeer(global, id);
if (!peer) { if (!peer) {
return undefined; return undefined;
} }
const monoforumChannel = selectMonoforumChannel(global, id);
if (monoforumChannel) {
peer = monoforumChannel;
}
const isSelf = peer && !isApiPeerChat(peer) ? peer.isSelf : undefined; const isSelf = peer && !isApiPeerChat(peer) ? peer.isSelf : undefined;
function getSubtitle() { function getSubtitle() {
if (!peer) return undefined; if (!peer) return undefined;
if (peer.id === currentUserId) return [lang('SavedMessagesInfo')]; if (peer.id === currentUserId) return [oldLang('SavedMessagesInfo')];
if (isApiPeerChat(peer)) { if (isApiPeerChat(peer)) {
return [getGroupStatus(lang, peer)]; return [getGroupStatus(oldLang, peer)];
} }
const userStatus = selectUserStatus(global, peer.id); const userStatus = selectUserStatus(global, peer.id);
return [ return [
getUserStatus(lang, peer, userStatus), getUserStatus(oldLang, peer, userStatus),
buildClassName(isUserOnline(peer, userStatus, true) && 'online'), buildClassName(isUserOnline(peer, userStatus, true) && 'online'),
]; ];
} }
@ -208,10 +213,20 @@ const ChatOrUserPicker: FC<OwnProps> = ({
<PickerItem <PickerItem
key={id} key={id}
className={ITEM_CLASS_NAME} className={ITEM_CLASS_NAME}
title={<FullNameTitle peer={peer} isSavedMessages={isSelf} />} title={(
<div className="title-wrapper">
<FullNameTitle
className="item-title"
peer={peer}
isMonoforum={Boolean(monoforumChannel)}
isSavedMessages={isSelf}
/>
</div>
)}
avatarElement={( avatarElement={(
<Avatar <Avatar
peer={peer} peer={peer}
asMessageBubble={Boolean(monoforumChannel)}
isSavedMessages={isSelf} isSavedMessages={isSelf}
size="medium" size="medium"
/> />
@ -224,13 +239,13 @@ const ChatOrUserPicker: FC<OwnProps> = ({
onClick={() => handleClick(id)} onClick={() => handleClick(id)}
/> />
); );
}, [currentUserId, lang, viewportOffset]); }, [currentUserId, oldLang, viewportOffset]);
function renderTopicList() { function renderTopicList() {
return ( return (
<> <>
<div className="modal-header" dir={lang.isRtl ? 'rtl' : undefined}> <div className="modal-header" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button round color="translucent" size="smaller" ariaLabel={lang('Back')} onClick={handleHeaderBackClick}> <Button round color="translucent" size="smaller" ariaLabel={oldLang('Back')} onClick={handleHeaderBackClick}>
<Icon name="arrow-left" /> <Icon name="arrow-left" />
</Button> </Button>
<InputText <InputText
@ -276,12 +291,12 @@ const ChatOrUserPicker: FC<OwnProps> = ({
function renderChatList() { function renderChatList() {
return ( return (
<> <>
<div className="modal-header" dir={lang.isRtl ? 'rtl' : undefined}> <div className="modal-header" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
round round
color="translucent" color="translucent"
size="smaller" size="smaller"
ariaLabel={lang('Close')} ariaLabel={oldLang('Close')}
onClick={onClose} onClick={onClose}
> >
<Icon name="close" /> <Icon name="close" />
@ -308,7 +323,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
{viewportIds.map(renderChatItem)} {viewportIds.map(renderChatItem)}
</InfiniteScroll> </InfiniteScroll>
) : viewportIds && !viewportIds.length ? ( ) : viewportIds && !viewportIds.length ? (
<p className="no-results">{lang('lng_blocked_list_not_found')}</p> <p className="no-results">{oldLang('lng_blocked_list_not_found')}</p>
) : ( ) : (
<Loading /> <Loading />
)} )}

View File

@ -438,6 +438,11 @@
.avatar-badge-wrapper .ChatBadge:not(.pinned).muted { .avatar-badge-wrapper .ChatBadge:not(.pinned).muted {
background: var(--color-gray); background: var(--color-gray);
} }
.monoforum-badge {
color: var(--color-chat-active);
background-color: #fff;
}
} }
} }

View File

@ -35,6 +35,7 @@ import {
selectIsCurrentUserFrozen, selectIsCurrentUserFrozen,
selectIsForumPanelClosed, selectIsForumPanelClosed,
selectIsForumPanelOpen, selectIsForumPanelOpen,
selectMonoforumChannel,
selectNotifyDefaults, selectNotifyDefaults,
selectNotifyException, selectNotifyException,
selectOutgoingStatus, selectOutgoingStatus,
@ -94,6 +95,7 @@ type OwnProps = {
type StateProps = { type StateProps = {
chat?: ApiChat; chat?: ApiChat;
monoforumChannel?: ApiChat;
lastMessageStory?: ApiTypeStory; lastMessageStory?: ApiTypeStory;
listedTopicIds?: number[]; listedTopicIds?: number[];
topics?: Record<number, ApiTopic>; topics?: Record<number, ApiTopic>;
@ -128,6 +130,7 @@ const Chat: FC<OwnProps & StateProps> = ({
topics, topics,
observeIntersection, observeIntersection,
chat, chat,
monoforumChannel,
lastMessageStory, lastMessageStory,
isMuted, isMuted,
user, user,
@ -177,7 +180,7 @@ const Chat: FC<OwnProps & StateProps> = ({
const [shouldRenderMuteModal, markRenderMuteModal, unmarkRenderMuteModal] = useFlag(); const [shouldRenderMuteModal, markRenderMuteModal, unmarkRenderMuteModal] = useFlag();
const [shouldRenderChatFolderModal, markRenderChatFolderModal, unmarkRenderChatFolderModal] = useFlag(); const [shouldRenderChatFolderModal, markRenderChatFolderModal, unmarkRenderChatFolderModal] = useFlag();
const { isForum, isForumAsMessages } = chat || {}; const { isForum, isForumAsMessages, isMonoforum } = chat || {};
useEnsureMessage(isSavedDialog ? currentUserId : chatId, lastMessageId, lastMessage); useEnsureMessage(isSavedDialog ? currentUserId : chatId, lastMessageId, lastMessage);
@ -355,11 +358,12 @@ const Chat: FC<OwnProps & StateProps> = ({
> >
<div className={buildClassName('status', 'status-clickable')}> <div className={buildClassName('status', 'status-clickable')}>
<Avatar <Avatar
peer={peer} peer={isMonoforum ? monoforumChannel : peer}
isSavedMessages={user?.isSelf} isSavedMessages={user?.isSelf}
isSavedDialog={isSavedDialog} isSavedDialog={isSavedDialog}
size={isPreview ? 'medium' : 'large'} size={isPreview ? 'medium' : 'large'}
withStory={!user?.isSelf} asMessageBubble={isMonoforum}
withStory={!user?.isSelf && !isMonoforum}
withStoryGap={isAvatarOnlineShown || Boolean(chat.subscriptionUntil)} withStoryGap={isAvatarOnlineShown || Boolean(chat.subscriptionUntil)}
storyViewerOrigin={StoryViewerOrigin.ChatList} storyViewerOrigin={StoryViewerOrigin.ChatList}
storyViewerMode="single-peer" storyViewerMode="single-peer"
@ -387,7 +391,9 @@ const Chat: FC<OwnProps & StateProps> = ({
<div className="info"> <div className="info">
<div className="info-row"> <div className="info-row">
<FullNameTitle <FullNameTitle
peer={peer} peer={isMonoforum ? monoforumChannel! : peer}
isMonoforum={isMonoforum}
monoforumBadgeClassName="monoforum-badge"
withEmojiStatus withEmojiStatus
isSavedMessages={chatId === user?.id && user?.isSelf} isSavedMessages={chatId === user?.id && user?.isSelf}
isSavedDialog={isSavedDialog} isSavedDialog={isSavedDialog}
@ -489,6 +495,8 @@ export default memo(withGlobal<OwnProps>(
const lastMessageStory = storyData && selectPeerStory(global, storyData.peerId, storyData.id); const lastMessageStory = storyData && selectPeerStory(global, storyData.peerId, storyData.id);
const isAccountFrozen = selectIsCurrentUserFrozen(global); const isAccountFrozen = selectIsCurrentUserFrozen(global);
const monoforumChannel = selectMonoforumChannel(global, chatId);
return { return {
chat, chat,
isMuted: getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id)), isMuted: getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id)),
@ -515,6 +523,7 @@ export default memo(withGlobal<OwnProps>(
isSynced: global.isSynced, isSynced: global.isSynced,
lastMessageStory, lastMessageStory,
isAccountFrozen, isAccountFrozen,
monoforumChannel,
}; };
}, },
)(Chat)); )(Chat));

View File

@ -74,6 +74,7 @@ interface StateProps {
canLeave?: boolean; canLeave?: boolean;
canEnterVoiceChat?: boolean; canEnterVoiceChat?: boolean;
canCreateVoiceChat?: boolean; canCreateVoiceChat?: boolean;
channelMonoforumId?: string;
pendingJoinRequests?: number; pendingJoinRequests?: number;
shouldJoinToSend?: boolean; shouldJoinToSend?: boolean;
shouldSendJoinRequest?: boolean; shouldSendJoinRequest?: boolean;
@ -110,6 +111,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
canLeave, canLeave,
canEnterVoiceChat, canEnterVoiceChat,
canCreateVoiceChat, canCreateVoiceChat,
channelMonoforumId,
pendingJoinRequests, pendingJoinRequests,
isRightColumnShown, isRightColumnShown,
isForForum, isForForum,
@ -451,6 +453,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
pendingJoinRequests={pendingJoinRequests} pendingJoinRequests={pendingJoinRequests}
onJoinRequestsClick={handleJoinRequestsClick} onJoinRequestsClick={handleJoinRequestsClick}
withForumActions={isForForum} withForumActions={isForForum}
channelMonoforumId={channelMonoforumId}
onSubscribeChannel={handleSubscribeClick} onSubscribeChannel={handleSubscribeClick}
onSearchClick={handleSearchClick} onSearchClick={handleSearchClick}
onAsMessagesClick={handleAsMessagesClick} onAsMessagesClick={handleAsMessagesClick}
@ -499,7 +502,7 @@ export default memo(withGlobal<OwnProps>(
const canStartBot = !canRestartBot && Boolean(selectIsChatBotNotStarted(global, chatId)); const canStartBot = !canRestartBot && Boolean(selectIsChatBotNotStarted(global, chatId));
const canUnblock = isUserBlocked && !bot; const canUnblock = isUserBlocked && !bot;
const canSubscribe = Boolean( const canSubscribe = Boolean(
(isMainThread || chat.isForum) && (isChannel || isSuperGroup) && chat.isNotJoined, (isMainThread || chat.isForum) && (isChannel || isSuperGroup) && chat.isNotJoined && !chat.isMonoforum,
); );
const canSearch = isMainThread || isDiscussionThread; const canSearch = isMainThread || isDiscussionThread;
const canCall = ARE_CALLS_SUPPORTED && isUserId(chat.id) && !isChatWithSelf && !bot && !chat.isSupport const canCall = ARE_CALLS_SUPPORTED && isUserId(chat.id) && !isChatWithSelf && !bot && !chat.isSupport
@ -508,12 +511,12 @@ export default memo(withGlobal<OwnProps>(
const canLeave = isSavedDialog || (isMainThread && !canSubscribe); const canLeave = isSavedDialog || (isMainThread && !canSubscribe);
const canEnterVoiceChat = ARE_CALLS_SUPPORTED && isMainThread && chat.isCallActive; const canEnterVoiceChat = ARE_CALLS_SUPPORTED && isMainThread && chat.isCallActive;
const canCreateVoiceChat = ARE_CALLS_SUPPORTED && isMainThread && !chat.isCallActive const canCreateVoiceChat = ARE_CALLS_SUPPORTED && isMainThread && !chat.isCallActive
&& (chat.adminRights?.manageCall || (chat.isCreator && isChatBasicGroup(chat))); && (chat.adminRights?.manageCall || (chat.isCreator && isChatBasicGroup(chat))) && !chat.isMonoforum;
const canViewStatistics = isMainThread && chatFullInfo?.canViewStatistics; const canViewStatistics = isMainThread && chatFullInfo?.canViewStatistics;
const canViewMonetization = isMainThread && chatFullInfo?.canViewMonetization; const canViewMonetization = isMainThread && chatFullInfo?.canViewMonetization;
const canViewBoosts = isMainThread const canViewBoosts = isMainThread && !chat.isMonoforum
&& (isSuperGroup || isChannel) && (canViewStatistics || getHasAdminRight(chat, 'postStories')); && (isSuperGroup || isChannel) && (canViewStatistics || getHasAdminRight(chat, 'postStories'));
const canShowBoostModal = !canViewBoosts && (isSuperGroup || isChannel); const canShowBoostModal = !canViewBoosts && (isSuperGroup || isChannel) && !chat.isMonoforum;
const pendingJoinRequests = isMainThread ? chatFullInfo?.requestsPending : undefined; const pendingJoinRequests = isMainThread ? chatFullInfo?.requestsPending : undefined;
const shouldJoinToSend = Boolean(chat?.isNotJoined && chat.isJoinToSend); const shouldJoinToSend = Boolean(chat?.isNotJoined && chat.isJoinToSend);
const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest); const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest);
@ -523,6 +526,8 @@ export default memo(withGlobal<OwnProps>(
const canTranslate = selectCanTranslateChat(global, chatId) && !fullInfo?.isTranslationDisabled; const canTranslate = selectCanTranslateChat(global, chatId) && !fullInfo?.isTranslationDisabled;
const isAccountFrozen = selectIsCurrentUserFrozen(global); const isAccountFrozen = selectIsCurrentUserFrozen(global);
const channelMonoforumId = isChatChannel(chat) ? chat.linkedMonoforumId : undefined;
return { return {
noMenu: false, noMenu: false,
isChannel, isChannel,
@ -552,6 +557,7 @@ export default memo(withGlobal<OwnProps>(
detectedChatLanguage: chat.detectedLanguage, detectedChatLanguage: chat.detectedLanguage,
canUnblock, canUnblock,
isAccountFrozen, isAccountFrozen,
channelMonoforumId,
}; };
}, },
)(HeaderActions)); )(HeaderActions));

View File

@ -17,6 +17,7 @@ import {
getCanManageTopic, getCanManageTopic,
getHasAdminRight, getHasAdminRight,
getIsSavedDialog, getIsSavedDialog,
isChatAdmin,
isChatChannel, isChatChannel,
isChatGroup, isChatGroup,
isSystemBot, isSystemBot,
@ -94,6 +95,7 @@ export type OwnProps = {
canCreateVoiceChat?: boolean; canCreateVoiceChat?: boolean;
pendingJoinRequests?: number; pendingJoinRequests?: number;
canTranslate?: boolean; canTranslate?: boolean;
channelMonoforumId?: string;
onSubscribeChannel: () => void; onSubscribeChannel: () => void;
onSearchClick: () => void; onSearchClick: () => void;
onAsMessagesClick: () => void; onAsMessagesClick: () => void;
@ -176,14 +178,15 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
isChatWithSelf, isChatWithSelf,
savedDialog, savedDialog,
canShowBoostModal, canShowBoostModal,
disallowedGifts,
isAccountFrozen,
channelMonoforumId,
onJoinRequestsClick, onJoinRequestsClick,
onSubscribeChannel, onSubscribeChannel,
onSearchClick, onSearchClick,
onAsMessagesClick, onAsMessagesClick,
onClose, onClose,
onCloseAnimationEnd, onCloseAnimationEnd,
disallowedGifts,
isAccountFrozen,
}) => { }) => {
const { const {
updateChatMutedState, updateChatMutedState,
@ -488,6 +491,11 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
closeMenu(); closeMenu();
}); });
const handleSendChannelMessage = useLastCallback(() => {
openChat({ id: channelMonoforumId });
closeMenu();
});
useEffect(disableScrolling, []); useEffect(disableScrolling, []);
const botButtons = useMemo(() => { const botButtons = useMemo(() => {
@ -585,6 +593,14 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
<MenuSeparator /> <MenuSeparator />
</> </>
)} )}
{channelMonoforumId && (
<MenuItem
icon="message"
onClick={handleSendChannelMessage}
>
{lang('ChannelSendMessage')}
</MenuItem>
)}
{isViewGroupInfoShown && ( {isViewGroupInfoShown && (
<MenuItem <MenuItem
icon="info" icon="info"
@ -835,7 +851,7 @@ export default memo(withGlobal<OwnProps>(
const isMainThread = threadId === MAIN_THREAD_ID; const isMainThread = threadId === MAIN_THREAD_ID;
const isChatWithSelf = selectIsChatWithSelf(global, chatId); const isChatWithSelf = selectIsChatWithSelf(global, chatId);
const { chatId: currentChatId, threadId: currentThreadId } = selectCurrentMessageList(global) || {}; const { chatId: currentChatId, threadId: currentThreadId } = selectCurrentMessageList(global) || {};
const canReportChat = isMainThread && !user && (isChatChannel(chat) || isChatGroup(chat)); const canReportChat = isMainThread && !user && (isChatChannel(chat) || isChatGroup(chat)) && !isChatAdmin(chat);
const chatBot = !isSystemBot(chatId) ? selectBot(global, chatId) : undefined; const chatBot = !isSystemBot(chatId) ? selectBot(global, chatId) : undefined;
const userFullInfo = isPrivate ? selectUserFullInfo(global, chatId) : undefined; const userFullInfo = isPrivate ? selectUserFullInfo(global, chatId) : undefined;

View File

@ -28,6 +28,7 @@ import {
isAnonymousForwardsChat, isAnonymousForwardsChat,
isChatChannel, isChatChannel,
isChatGroup, isChatGroup,
isChatMonoforum,
isSystemBot, isSystemBot,
} from '../../global/helpers'; } from '../../global/helpers';
import { import {
@ -48,6 +49,7 @@ import {
selectIsInSelectMode, selectIsInSelectMode,
selectIsViewportNewest, selectIsViewportNewest,
selectLastScrollOffset, selectLastScrollOffset,
selectMonoforumChannel,
selectPerformanceSettingsValue, selectPerformanceSettingsValue,
selectScrollOffset, selectScrollOffset,
selectTabState, selectTabState,
@ -107,6 +109,7 @@ type StateProps = {
isChatLoaded?: boolean; isChatLoaded?: boolean;
isChannelChat?: boolean; isChannelChat?: boolean;
isGroupChat?: boolean; isGroupChat?: boolean;
isChatMonoforum?: boolean;
isChatWithSelf?: boolean; isChatWithSelf?: boolean;
isSystemBotChat?: boolean; isSystemBotChat?: boolean;
isAnonymousForwards?: boolean; isAnonymousForwards?: boolean;
@ -139,6 +142,7 @@ type StateProps = {
isChatProtected?: boolean; isChatProtected?: boolean;
hasCustomGreeting?: boolean; hasCustomGreeting?: boolean;
isAppConfigLoaded?: boolean; isAppConfigLoaded?: boolean;
monoforumChannelId?: string;
canTranslate?: boolean; canTranslate?: boolean;
translationLanguage?: string; translationLanguage?: string;
shouldAutoTranslate?: boolean; shouldAutoTranslate?: boolean;
@ -169,6 +173,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
isChannelWithAvatars, isChannelWithAvatars,
canPost, canPost,
isSynced, isSynced,
isChatMonoforum,
isReady, isReady,
isChatWithSelf, isChatWithSelf,
isSystemBotChat, isSystemBotChat,
@ -203,6 +208,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
isChatProtected, isChatProtected,
isAccountFrozen, isAccountFrozen,
hasCustomGreeting, hasCustomGreeting,
monoforumChannelId,
isAppConfigLoaded, isAppConfigLoaded,
canTranslate, canTranslate,
translationLanguage, translationLanguage,
@ -695,7 +701,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
isChatProtected && 'hide-on-print', isChatProtected && 'hide-on-print',
); );
const hasMessages = (messageIds && messageGroups) || lastMessage; const hasMessages = Boolean((messageIds && messageGroups) || lastMessage);
useEffect(() => { useEffect(() => {
if (hasMessages) return; if (hasMessages) return;
@ -716,12 +722,12 @@ const MessageList: FC<OwnProps & StateProps> = ({
{restrictionReason ? restrictionReason.text : `This is a private ${isChannelChat ? 'channel' : 'chat'}`} {restrictionReason ? restrictionReason.text : `This is a private ${isChannelChat ? 'channel' : 'chat'}`}
</span> </span>
</div> </div>
) : paidMessagesStars && isPrivate && !hasMessages && !hasCustomGreeting ? ( ) : paidMessagesStars && !hasMessages && !hasCustomGreeting ? (
<RequirementToContactMessage paidMessagesStars={paidMessagesStars} userId={chatId} /> <RequirementToContactMessage paidMessagesStars={paidMessagesStars} peerId={monoforumChannelId || chatId} />
) : isContactRequirePremium && !hasMessages ? ( ) : isContactRequirePremium && !hasMessages ? (
<RequirementToContactMessage userId={chatId} /> <RequirementToContactMessage peerId={chatId} />
) : (isBot || isNonContact) && !hasMessages ? ( ) : (isBot || isNonContact) && !hasMessages ? (
<MessageListAccountInfo chatId={chatId} /> <MessageListAccountInfo chatId={chatId} hasMessages={hasMessages} />
) : shouldRenderGreeting ? ( ) : shouldRenderGreeting ? (
<ContactGreeting key={chatId} userId={chatId} /> <ContactGreeting key={chatId} userId={chatId} />
) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? ( ) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? (
@ -738,6 +744,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
chatId={chatId} chatId={chatId}
isComments={isComments} isComments={isComments}
isChannelChat={isChannelChat} isChannelChat={isChannelChat}
isChatMonoforum={isChatMonoforum}
isSavedDialog={isSavedDialog} isSavedDialog={isSavedDialog}
messageIds={messageIds || [lastMessage!.id]} messageIds={messageIds || [lastMessage!.id]}
messageGroups={messageGroups || groupMessages([lastMessage!])} messageGroups={messageGroups || groupMessages([lastMessage!])}
@ -820,6 +827,7 @@ export default memo(withGlobal<OwnProps>(
const hasCustomGreeting = Boolean(userFullInfo?.businessIntro); const hasCustomGreeting = Boolean(userFullInfo?.businessIntro);
const isAppConfigLoaded = global.isAppConfigLoaded; const isAppConfigLoaded = global.isAppConfigLoaded;
const monoforumChannelId = selectMonoforumChannel(global, chatId)?.id;
const canTranslate = selectCanTranslateChat(global, chatId) && !chatFullInfo?.isTranslationDisabled; const canTranslate = selectCanTranslateChat(global, chatId) && !chatFullInfo?.isTranslationDisabled;
const shouldAutoTranslate = chat?.hasAutoTranslation; const shouldAutoTranslate = chat?.hasAutoTranslation;
const translationLanguage = selectTranslationLanguage(global); const translationLanguage = selectTranslationLanguage(global);
@ -830,6 +838,7 @@ export default memo(withGlobal<OwnProps>(
isRestricted, isRestricted,
restrictionReason, restrictionReason,
isChannelChat: isChatChannel(chat), isChannelChat: isChatChannel(chat),
isChatMonoforum: isChatMonoforum(chat),
isGroupChat: isChatGroup(chat), isGroupChat: isChatGroup(chat),
isChannelWithAvatars: chat.areProfilesShown, isChannelWithAvatars: chat.areProfilesShown,
isCreator: chat.isCreator, isCreator: chat.isCreator,
@ -860,6 +869,7 @@ export default memo(withGlobal<OwnProps>(
isAccountFrozen, isAccountFrozen,
hasCustomGreeting, hasCustomGreeting,
isAppConfigLoaded, isAppConfigLoaded,
monoforumChannelId,
canTranslate, canTranslate,
translationLanguage, translationLanguage,
shouldAutoTranslate, shouldAutoTranslate,

View File

@ -46,7 +46,7 @@ import styles from './MessageListAccountInfo.module.scss';
type OwnProps = { type OwnProps = {
chatId: string; chatId: string;
isInMessageList?: boolean; hasMessages?: boolean;
}; };
type StateProps = { type StateProps = {
@ -66,6 +66,7 @@ const MessageListAccountInfo: FC<OwnProps & StateProps> = ({
phoneCodeList, phoneCodeList,
commonChats, commonChats,
userFullInfo, userFullInfo,
hasMessages,
}) => { }) => {
const { loadCommonChats, openChatWithInfo } = getActions(); const { loadCommonChats, openChatWithInfo } = getActions();
const oldLang = useOldLang(); const oldLang = useOldLang();
@ -170,7 +171,9 @@ const MessageListAccountInfo: FC<OwnProps & StateProps> = ({
return ( return (
<div className={buildClassName(styles.root, 'empty')}> <div className={buildClassName(styles.root, 'empty')}>
{isLoadingFullUser && isChatInfoEmpty && <span>{oldLang('Loading')}</span>} {isLoadingFullUser && isChatInfoEmpty && <span>{oldLang('Loading')}</span>}
{(isBotInfoEmpty && isChatInfoEmpty) && !isLoadingFullUser && <span>{oldLang('NoMessages')}</span>} {(isBotInfoEmpty && isChatInfoEmpty) && !isLoadingFullUser && !hasMessages && (
<span>{oldLang('NoMessages')}</span>
)}
{botInfo && ( {botInfo && (
<div <div
className={buildClassName(styles.chatInfo, styles.botBackground)} className={buildClassName(styles.chatInfo, styles.botBackground)}

View File

@ -50,6 +50,7 @@ interface OwnProps {
isUnread: boolean; isUnread: boolean;
withUsers: boolean; withUsers: boolean;
isChannelChat: boolean | undefined; isChannelChat: boolean | undefined;
isChatMonoforum?: boolean;
isEmptyThread?: boolean; isEmptyThread?: boolean;
isComments?: boolean; isComments?: boolean;
noAvatars: boolean; noAvatars: boolean;
@ -87,6 +88,7 @@ const MessageListContent: FC<OwnProps> = ({
isEmptyThread, isEmptyThread,
withUsers, withUsers,
isChannelChat, isChannelChat,
isChatMonoforum,
noAvatars, noAvatars,
containerRef, containerRef,
anchorIdRef, anchorIdRef,
@ -261,7 +263,7 @@ const MessageListContent: FC<OwnProps> = ({
// Service notifications saved in cache in previous versions may share the same `previousLocalId` // Service notifications saved in cache in previous versions may share the same `previousLocalId`
const key = isServiceNotificationMessage(message) ? `${message.date}_${originalId}` : originalId; const key = isServiceNotificationMessage(message) ? `${message.date}_${originalId}` : originalId;
const noComments = hasLinkedChat === false || !isChannelChat; const noComments = hasLinkedChat === false || !isChannelChat || Boolean(isChatMonoforum);
return compact([ return compact([
message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider, message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider,
@ -377,7 +379,7 @@ const MessageListContent: FC<OwnProps> = ({
<div className="messages-container" teactFastList> <div className="messages-container" teactFastList>
{withHistoryTriggers && <div ref={backwardsTriggerRef} key="backwards-trigger" className="backwards-trigger" />} {withHistoryTriggers && <div ref={backwardsTriggerRef} key="backwards-trigger" className="backwards-trigger" />}
{shouldRenderAccountInfo {shouldRenderAccountInfo
&& <MessageListAccountInfo key={`account_info_${chatId}`} chatId={chatId} />} && <MessageListAccountInfo key={`account_info_${chatId}`} chatId={chatId} hasMessages />}
{dateGroups.flat()} {dateGroups.flat()}
{withHistoryTriggers && ( {withHistoryTriggers && (
<div <div

View File

@ -51,6 +51,7 @@ import {
selectIsChatBotNotStarted, selectIsChatBotNotStarted,
selectIsCurrentUserFrozen, selectIsCurrentUserFrozen,
selectIsInSelectMode, selectIsInSelectMode,
selectIsMonoforumAdmin,
selectIsRightColumnShown, selectIsRightColumnShown,
selectIsUserBlocked, selectIsUserBlocked,
selectPeerPaidMessagesStars, selectPeerPaidMessagesStars,
@ -76,6 +77,7 @@ import useAppLayout from '../../hooks/useAppLayout';
import useCustomBackground from '../../hooks/useCustomBackground'; import useCustomBackground from '../../hooks/useCustomBackground';
import useForceUpdate from '../../hooks/useForceUpdate'; import useForceUpdate from '../../hooks/useForceUpdate';
import useHistoryBack from '../../hooks/useHistoryBack'; import useHistoryBack from '../../hooks/useHistoryBack';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback'; import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang'; import useOldLang from '../../hooks/useOldLang';
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation'; import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
@ -163,6 +165,7 @@ type StateProps = {
paidMessagesStars?: number; paidMessagesStars?: number;
isAccountFrozen?: boolean; isAccountFrozen?: boolean;
freezeAppealChat?: ApiChat; freezeAppealChat?: ApiChat;
shouldBlockSendInMonoforum?: boolean;
}; };
function isImage(item: DataTransferItem) { function isImage(item: DataTransferItem) {
@ -226,6 +229,7 @@ function MiddleColumn({
paidMessagesStars, paidMessagesStars,
isAccountFrozen, isAccountFrozen,
freezeAppealChat, freezeAppealChat,
shouldBlockSendInMonoforum,
}: OwnProps & StateProps) { }: OwnProps & StateProps) {
const { const {
openChat, openChat,
@ -247,7 +251,8 @@ function MiddleColumn({
const { width: windowWidth } = useWindowSize(); const { width: windowWidth } = useWindowSize();
const { isTablet, isDesktop } = useAppLayout(); const { isTablet, isDesktop } = useAppLayout();
const lang = useOldLang(); const oldLang = useOldLang();
const lang = useLang();
const [dropAreaState, setDropAreaState] = useState(DropAreaState.None); const [dropAreaState, setDropAreaState] = useState(DropAreaState.None);
const [isScrollDownNeeded, setIsScrollDownShown] = useState(false); const [isScrollDownNeeded, setIsScrollDownShown] = useState(false);
const isScrollDownShown = isScrollDownNeeded && (!isMobile || !hasActiveMiddleSearch); const isScrollDownShown = isScrollDownNeeded && (!isMobile || !hasActiveMiddleSearch);
@ -412,7 +417,8 @@ function MiddleColumn({
joinChannel({ chatId: chatId! }); joinChannel({ chatId: chatId! });
if (renderingShouldSendJoinRequest) { if (renderingShouldSendJoinRequest) {
showNotification({ showNotification({
message: isChannel ? lang('RequestToJoinChannelSentDescription') : lang('RequestToJoinGroupSentDescription'), message: isChannel
? oldLang('RequestToJoinChannelSentDescription') : oldLang('RequestToJoinGroupSentDescription'),
}); });
} }
}); });
@ -451,14 +457,17 @@ function MiddleColumn({
); );
const messageSendingRestrictionReason = getMessageSendingRestrictionReason( const messageSendingRestrictionReason = getMessageSendingRestrictionReason(
lang, currentUserBannedRights, defaultBannedRights, oldLang, currentUserBannedRights, defaultBannedRights,
);
const forumComposerPlaceholder = getForumComposerPlaceholder(
oldLang, chat, threadId, topics, Boolean(draftReplyInfo),
); );
const forumComposerPlaceholder = getForumComposerPlaceholder(lang, chat, threadId, topics, Boolean(draftReplyInfo));
const composerRestrictionMessage = messageSendingRestrictionReason const composerRestrictionMessage = messageSendingRestrictionReason
?? forumComposerPlaceholder || forumComposerPlaceholder
?? (isContactRequirePremium ? <PremiumRequiredPlaceholder userId={chatId!} /> : undefined) || (shouldBlockSendInMonoforum ? lang('MonoforumComposerPlaceholder') : undefined)
?? (isAccountFrozen && freezeAppealChat?.id !== chatId ? <FrozenAccountPlaceholder /> : undefined); || (isContactRequirePremium ? <PremiumRequiredPlaceholder userId={chatId!} /> : undefined)
|| (isAccountFrozen && freezeAppealChat?.id !== chatId ? <FrozenAccountPlaceholder /> : undefined);
// CSS Variables calculation doesn't work properly with transforms, so we calculate transform values in JS // CSS Variables calculation doesn't work properly with transforms, so we calculate transform values in JS
const { const {
@ -587,7 +596,7 @@ function MiddleColumn({
/> />
)} )}
{isPinnedMessageList && canUnpin && ( {isPinnedMessageList && canUnpin && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> <div className="middle-column-footer-button-container" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
size="tiny" size="tiny"
fluid fluid
@ -596,12 +605,12 @@ function MiddleColumn({
onClick={handleOpenUnpinModal} onClick={handleOpenUnpinModal}
> >
<Icon name="unpin" /> <Icon name="unpin" />
<span>{lang('Chat.Pinned.UnpinAll', pinnedMessagesCount, 'i')}</span> <span>{oldLang('Chat.Pinned.UnpinAll', pinnedMessagesCount, 'i')}</span>
</Button> </Button>
</div> </div>
)} )}
{canShowOpenChatButton && ( {canShowOpenChatButton && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> <div className="middle-column-footer-button-container" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
size="tiny" size="tiny"
fluid fluid
@ -609,7 +618,7 @@ function MiddleColumn({
className="composer-button open-chat-button" className="composer-button open-chat-button"
onClick={handleOpenChatFromSaved} onClick={handleOpenChatFromSaved}
> >
<span>{lang('SavedOpenChat')}</span> <span>{oldLang('SavedOpenChat')}</span>
</Button> </Button>
</div> </div>
)} )}
@ -625,7 +634,7 @@ function MiddleColumn({
{( {(
isMobile && (renderingCanSubscribe || (renderingShouldJoinToSend && !renderingShouldSendJoinRequest)) isMobile && (renderingCanSubscribe || (renderingShouldJoinToSend && !renderingShouldSendJoinRequest))
) && ( ) && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> <div className="middle-column-footer-button-container" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
size="tiny" size="tiny"
fluid fluid
@ -633,12 +642,12 @@ function MiddleColumn({
className="composer-button join-subscribe-button" className="composer-button join-subscribe-button"
onClick={handleSubscribeClick} onClick={handleSubscribeClick}
> >
{lang(renderingIsChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')} {oldLang(renderingIsChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')}
</Button> </Button>
</div> </div>
)} )}
{isMobile && renderingShouldSendJoinRequest && ( {isMobile && renderingShouldSendJoinRequest && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> <div className="middle-column-footer-button-container" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
size="tiny" size="tiny"
fluid fluid
@ -646,12 +655,12 @@ function MiddleColumn({
className="composer-button join-subscribe-button" className="composer-button join-subscribe-button"
onClick={handleSubscribeClick} onClick={handleSubscribeClick}
> >
{lang('ChannelJoinRequest')} {oldLang('ChannelJoinRequest')}
</Button> </Button>
</div> </div>
)} )}
{isMobile && renderingCanStartBot && ( {isMobile && renderingCanStartBot && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> <div className="middle-column-footer-button-container" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
size="tiny" size="tiny"
fluid fluid
@ -659,12 +668,12 @@ function MiddleColumn({
className="composer-button join-subscribe-button" className="composer-button join-subscribe-button"
onClick={handleStartBot} onClick={handleStartBot}
> >
{lang('BotStart')} {oldLang('BotStart')}
</Button> </Button>
</div> </div>
)} )}
{isMobile && renderingCanRestartBot && ( {isMobile && renderingCanRestartBot && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> <div className="middle-column-footer-button-container" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
size="tiny" size="tiny"
fluid fluid
@ -672,12 +681,12 @@ function MiddleColumn({
className="composer-button join-subscribe-button" className="composer-button join-subscribe-button"
onClick={handleRestartBot} onClick={handleRestartBot}
> >
{lang('BotRestart')} {oldLang('BotRestart')}
</Button> </Button>
</div> </div>
)} )}
{isMobile && renderingCanUnblock && ( {isMobile && renderingCanUnblock && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> <div className="middle-column-footer-button-container" dir={oldLang.isRtl ? 'rtl' : undefined}>
<Button <Button
size="tiny" size="tiny"
fluid fluid
@ -685,7 +694,7 @@ function MiddleColumn({
className="composer-button join-subscribe-button" className="composer-button join-subscribe-button"
onClick={handleUnblock} onClick={handleUnblock}
> >
{lang('Unblock')} {oldLang('Unblock')}
</Button> </Button>
</div> </div>
)} )}
@ -788,7 +797,8 @@ export default memo(withGlobal<OwnProps>(
const isMainThread = messageListType === 'thread' && threadId === MAIN_THREAD_ID; const isMainThread = messageListType === 'thread' && threadId === MAIN_THREAD_ID;
const isChannel = Boolean(chat && isChatChannel(chat)); const isChannel = Boolean(chat && isChatChannel(chat));
const canSubscribe = Boolean( const canSubscribe = Boolean(
chat && isMainThread && (isChannel || isChatSuperGroup(chat)) && chat.isNotJoined && !chat.joinRequests, chat && isMainThread && (isChannel || isChatSuperGroup(chat)) && chat.isNotJoined && !chat.joinRequests
&& !chat.isMonoforum,
); );
const shouldJoinToSend = Boolean(chat?.isNotJoined && chat.isJoinToSend); const shouldJoinToSend = Boolean(chat?.isNotJoined && chat.isJoinToSend);
const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest); const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest);
@ -803,6 +813,8 @@ export default memo(withGlobal<OwnProps>(
const shouldBlockSendInForum = chat?.isForum const shouldBlockSendInForum = chat?.isForum
? threadId === MAIN_THREAD_ID && !draftReplyInfo && (selectTopic(global, chatId, GENERAL_TOPIC_ID)?.isClosed) ? threadId === MAIN_THREAD_ID && !draftReplyInfo && (selectTopic(global, chatId, GENERAL_TOPIC_ID)?.isClosed)
: false; : false;
const isMonoforumAdmin = selectIsMonoforumAdmin(global, chatId);
const shouldBlockSendInMonoforum = Boolean(chat?.isMonoforum && !draftReplyInfo && isMonoforumAdmin);
const topics = selectTopics(global, chatId); const topics = selectTopics(global, chatId);
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId); const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
@ -840,6 +852,7 @@ export default memo(withGlobal<OwnProps>(
&& !isBotNotStarted && !isBotNotStarted
&& !(shouldJoinToSend && chat?.isNotJoined) && !(shouldJoinToSend && chat?.isNotJoined)
&& !shouldBlockSendInForum && !shouldBlockSendInForum
&& !shouldBlockSendInMonoforum
&& !isSavedDialog && !isSavedDialog
&& (!isAccountFrozen || freezeAppealChat?.id === chatId), && (!isAccountFrozen || freezeAppealChat?.id === chatId),
isPinnedMessageList, isPinnedMessageList,
@ -864,6 +877,7 @@ export default memo(withGlobal<OwnProps>(
paidMessagesStars, paidMessagesStars,
isAccountFrozen, isAccountFrozen,
freezeAppealChat, freezeAppealChat,
shouldBlockSendInMonoforum,
}; };
}, },
)(MiddleColumn)); )(MiddleColumn));

View File

@ -261,6 +261,8 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
const savedMessagesStatus = isSavedDialog ? lang('SavedMessages') : undefined; const savedMessagesStatus = isSavedDialog ? lang('SavedMessages') : undefined;
const realChatId = isSavedDialog ? String(threadId) : chatId; const realChatId = isSavedDialog ? String(threadId) : chatId;
const displayChatId = chat?.isMonoforum ? chat.linkedMonoforumId! : realChatId;
return ( return (
<> <>
{(isLeftColumnHideable || currentTransitionKey > 0) && renderBackButton(shouldShowCloseButton, !isSavedDialog)} {(isLeftColumnHideable || currentTransitionKey > 0) && renderBackButton(shouldShowCloseButton, !isSavedDialog)}
@ -272,10 +274,10 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
onTouchStart={handleLongPressTouchStart} onTouchStart={handleLongPressTouchStart}
onTouchEnd={handleLongPressTouchEnd} onTouchEnd={handleLongPressTouchEnd}
> >
{isUserId(realChatId) ? ( {isUserId(displayChatId) ? (
<PrivateChatInfo <PrivateChatInfo
key={realChatId} key={displayChatId}
userId={realChatId} userId={displayChatId}
typingStatus={typingStatus} typingStatus={typingStatus}
status={connectionStatusText || savedMessagesStatus} status={connectionStatusText || savedMessagesStatus}
withDots={Boolean(connectionStatusText)} withDots={Boolean(connectionStatusText)}
@ -291,10 +293,11 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
/> />
) : ( ) : (
<GroupChatInfo <GroupChatInfo
key={realChatId} key={displayChatId}
chatId={realChatId} chatId={displayChatId}
threadId={!isSavedDialog ? threadId : undefined} threadId={!isSavedDialog ? threadId : undefined}
typingStatus={typingStatus} typingStatus={typingStatus}
withMonoforumStatus={chat?.isMonoforum}
status={connectionStatusText || savedMessagesStatus} status={connectionStatusText || savedMessagesStatus}
withDots={Boolean(connectionStatusText)} withDots={Boolean(connectionStatusText)}
withMediaViewer={threadId === MAIN_THREAD_ID} withMediaViewer={threadId === MAIN_THREAD_ID}

View File

@ -1,8 +1,10 @@
import { memo } from '../../lib/teact/teact'; import { memo } from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global'; import { getActions, withGlobal } from '../../global';
import { getUserFirstOrLastName } from '../../global/helpers'; import type { ApiPeer } from '../../api/types';
import { selectTheme, selectThemeValues, selectUser } from '../../global/selectors';
import { getPeerTitle, isApiPeerUser } from '../../global/helpers/peers';
import { selectPeer, selectTheme, selectThemeValues } from '../../global/selectors';
import { formatStarsAsIcon } from '../../util/localization/format'; import { formatStarsAsIcon } from '../../util/localization/format';
import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets'; import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets';
import renderText from '../common/helpers/renderText'; import renderText from '../common/helpers/renderText';
@ -19,17 +21,18 @@ import Button from '../ui/Button';
import styles from './RequirementToContactMessage.module.scss'; import styles from './RequirementToContactMessage.module.scss';
type OwnProps = { type OwnProps = {
peerId: string;
userId: string;
paidMessagesStars?: number; paidMessagesStars?: number;
}; };
type StateProps = { type StateProps = {
patternColor?: string; patternColor?: string;
userName?: string; peer?: ApiPeer;
}; };
function RequirementToContactMessage({ patternColor, userName, paidMessagesStars }: OwnProps & StateProps) { function RequirementToContactMessage({
patternColor, peer, paidMessagesStars,
}: OwnProps & StateProps) {
const oldLang = useOldLang(); const oldLang = useOldLang();
const lang = useLang(); const lang = useLang();
const { openPremiumModal, openStarsBalanceModal } = getActions(); const { openPremiumModal, openStarsBalanceModal } = getActions();
@ -40,6 +43,8 @@ function RequirementToContactMessage({ patternColor, userName, paidMessagesStars
openStarsBalanceModal({}); openStarsBalanceModal({});
}); });
if (!peer) return undefined;
return ( return (
<div className={styles.root}> <div className={styles.root}>
<div className={styles.inner}> <div className={styles.inner}>
@ -55,8 +60,8 @@ function RequirementToContactMessage({ patternColor, userName, paidMessagesStars
<span className={styles.description}> <span className={styles.description}>
{ {
paidMessagesStars paidMessagesStars
? lang('FirstMessageInPaidMessagesChat', { ? lang(isApiPeerUser(peer) ? 'MessagesPlaceholderPaidUser' : 'MessagesPlaceholderPaidChannel', {
user: userName, peer: getPeerTitle(lang, peer),
amount: formatStarsAsIcon(lang, amount: formatStarsAsIcon(lang,
paidMessagesStars, paidMessagesStars,
{ {
@ -68,7 +73,7 @@ function RequirementToContactMessage({ patternColor, userName, paidMessagesStars
withNodes: true, withNodes: true,
withMarkdown: true, withMarkdown: true,
}) })
: renderText(oldLang('MessageLockedPremium', userName), ['simple_markdown']) : renderText(oldLang('MessageLockedPremium', getPeerTitle(lang, peer)), ['simple_markdown'])
} }
</span> </span>
<Button <Button
@ -95,14 +100,14 @@ function RequirementToContactMessage({ patternColor, userName, paidMessagesStars
} }
export default memo( export default memo(
withGlobal<OwnProps>((global, { userId }): StateProps => { withGlobal<OwnProps>((global, { peerId: userId }): StateProps => {
const theme = selectTheme(global); const theme = selectTheme(global);
const { patternColor } = selectThemeValues(global, theme) || {}; const { patternColor } = selectThemeValues(global, theme) || {};
const user = selectUser(global, userId); const peer = selectPeer(global, userId);
return { return {
patternColor, patternColor,
userName: getUserFirstOrLastName(user), peer,
}; };
})(RequirementToContactMessage), })(RequirementToContactMessage),
); );

View File

@ -1,6 +1,5 @@
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import type { ElementRef, FC } from '../../../lib/teact/teact'; import type { ElementRef } from '../../../lib/teact/teact';
import type React from '../../../lib/teact/teact';
import { import {
memo, useEffect, useRef, useState, memo, useEffect, useRef, useState,
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
@ -28,19 +27,25 @@ export type OwnProps = {
isOpen: boolean; isOpen: boolean;
shouldBeAnonymous?: boolean; shouldBeAnonymous?: boolean;
isQuiz?: boolean; isQuiz?: boolean;
maxOptionsCount?: number;
onSend: (pollSummary: ApiNewPoll) => void; onSend: (pollSummary: ApiNewPoll) => void;
onClear: () => void; onClear: () => void;
}; };
const MAX_LIST_HEIGHT = 320; const MAX_LIST_HEIGHT = 320;
const MAX_OPTIONS_COUNT = 10; const FALLBACK_MAX_OPTIONS_COUNT = 12;
const MAX_OPTION_LENGTH = 100; const MAX_OPTION_LENGTH = 100;
const MAX_QUESTION_LENGTH = 255; const MAX_QUESTION_LENGTH = 255;
const MAX_SOLUTION_LENGTH = 200; const MAX_SOLUTION_LENGTH = 200;
const PollModal: FC<OwnProps> = ({ const PollModal = ({
isOpen, isQuiz, shouldBeAnonymous, onSend, onClear, isOpen,
}) => { isQuiz,
shouldBeAnonymous,
maxOptionsCount = FALLBACK_MAX_OPTIONS_COUNT,
onSend,
onClear,
}: OwnProps) => {
const questionInputRef = useRef<HTMLInputElement>(); const questionInputRef = useRef<HTMLInputElement>();
const optionsListRef = useRef<HTMLDivElement>(); const optionsListRef = useRef<HTMLDivElement>();
@ -170,7 +175,7 @@ const PollModal: FC<OwnProps> = ({
const updateOption = useLastCallback((index: number, text: string) => { const updateOption = useLastCallback((index: number, text: string) => {
const newOptions = [...options]; const newOptions = [...options];
newOptions[index] = text; newOptions[index] = text;
if (newOptions[newOptions.length - 1].trim().length && newOptions.length < MAX_OPTIONS_COUNT) { if (newOptions[newOptions.length - 1].trim().length && newOptions.length < maxOptionsCount) {
addNewOption(newOptions); addNewOption(newOptions);
} else { } else {
setOptions(newOptions); setOptions(newOptions);
@ -265,7 +270,7 @@ const PollModal: FC<OwnProps> = ({
<div className="option-wrapper"> <div className="option-wrapper">
<InputText <InputText
maxLength={MAX_OPTION_LENGTH} maxLength={MAX_OPTION_LENGTH}
label={index !== options.length - 1 || options.length === MAX_OPTIONS_COUNT label={index !== options.length - 1 || options.length === maxOptionsCount
? lang('OptionHint') ? lang('OptionHint')
: lang('CreatePoll.AddOption')} : lang('CreatePoll.AddOption')}
error={getOptionsError(index)} error={getOptionsError(index)}

View File

@ -717,13 +717,21 @@ const ActionMessageText = ({
return action.message; return action.message;
case 'paidMessagesPrice': { case 'paidMessagesPrice': {
const { stars } = action; const { stars, isAllowedInChannel } = action;
if (stars === 0) { if (stars === 0) {
return lang('ActionPaidMessageGroupPriceFree'); if (isChannel) {
return lang(
isAllowedInChannel ? 'ActionMessageChannelFree' : 'ActionMessageChannelDisabled',
{ peer: chatLink },
{ withNodes: true },
);
}
return translateWithYou(lang, 'ActionPaidMessagePriceFree', isOutgoing, { peer: senderLink });
} }
return lang('ActionPaidMessageGroupPrice', { return translateWithYou(lang, 'ActionPaidMessagePrice', isOutgoing, {
stars: formatStarsAsText(lang, stars), peer: senderLink,
}, { withNodes: true, withMarkdown: true }); amount: formatStarsAsText(lang, stars),
}, { withMarkdown: true });
} }
case 'paidMessagesRefunded': { case 'paidMessagesRefunded': {

View File

@ -65,6 +65,7 @@ import {
selectPollFromMessage, selectPollFromMessage,
selectRequestedChatTranslationLanguage, selectRequestedChatTranslationLanguage,
selectRequestedMessageTranslationLanguage, selectRequestedMessageTranslationLanguage,
selectSavedDialogIdFromMessage,
selectStickerSet, selectStickerSet,
selectThreadInfo, selectThreadInfo,
selectTopic, selectTopic,
@ -156,6 +157,7 @@ type StateProps = {
isWithPaidReaction?: boolean; isWithPaidReaction?: boolean;
userFullName?: string; userFullName?: string;
canGift?: boolean; canGift?: boolean;
savedDialogId?: string;
}; };
const selection = window.getSelection(); const selection = window.getSelection();
@ -223,6 +225,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
userFullName, userFullName,
canGift, canGift,
className, className,
savedDialogId,
onClose, onClose,
onCloseAnimationEnd, onCloseAnimationEnd,
}) => { }) => {
@ -416,6 +419,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
replyToMsgId: message.id, replyToMsgId: message.id,
quoteText, quoteText,
quoteOffset: selectionQuoteOffset, quoteOffset: selectionQuoteOffset,
monoforumPeerId: savedDialogId,
replyToPeerId: undefined, replyToPeerId: undefined,
}); });
} }
@ -813,6 +817,7 @@ export default memo(withGlobal<OwnProps>(
const hasTtl = hasMessageTtl(message); const hasTtl = hasMessageTtl(message);
const canShowSeenBy = Boolean(!isLocal const canShowSeenBy = Boolean(!isLocal
&& chat && chat
&& !chat.isMonoforum
&& !isMessageUnread && !isMessageUnread
&& seenByMaxChatMembers && seenByMaxChatMembers
&& seenByExpiresAt && seenByExpiresAt
@ -849,6 +854,8 @@ export default memo(withGlobal<OwnProps>(
const canGift = selectCanGift(global, message.chatId); const canGift = selectCanGift(global, message.chatId);
const savedDialogId = selectSavedDialogIdFromMessage(global, message);
return { return {
threadId, threadId,
chat, chat,
@ -904,6 +911,7 @@ export default memo(withGlobal<OwnProps>(
story, story,
userFullName, userFullName,
canGift, canGift,
savedDialogId,
}; };
}, },
)(ContextMenuContainer)); )(ContextMenuContainer));

View File

@ -38,11 +38,11 @@ export function translateWithYou<K extends LangKey>(
options?: { options?: {
pluralValue?: number; pluralValue?: number;
asText?: boolean; asText?: boolean;
isMarkdown?: boolean; withMarkdown?: boolean;
renderTextFilters?: string[]; renderTextFilters?: string[];
}, },
): TeactNode { ): TeactNode {
const { pluralValue, asText, isMarkdown, renderTextFilters } = options || {}; const { pluralValue, asText, withMarkdown, renderTextFilters } = options || {};
const translationKey = isYou ? (`${key}You` as LangKey) : key; const translationKey = isYou ? (`${key}You` as LangKey) : key;
return lang( return lang(
@ -50,7 +50,7 @@ export function translateWithYou<K extends LangKey>(
translationKey, translationKey,
variables, variables,
{ {
withNodes: !asText, isMarkdown, pluralValue, renderTextFilters, withNodes: !asText, withMarkdown, pluralValue, renderTextFilters,
}, },
); );
} }

View File

@ -26,6 +26,7 @@ import {
selectForwardedSender, selectForwardedSender,
selectIsChatWithSelf, selectIsChatWithSelf,
selectIsCurrentUserPremium, selectIsCurrentUserPremium,
selectMonoforumChannel,
selectSender, selectSender,
selectTabState, selectTabState,
} from '../../../global/selectors'; } from '../../../global/selectors';
@ -67,6 +68,7 @@ export type OwnProps = {
type StateProps = { type StateProps = {
isActive?: boolean; isActive?: boolean;
chat?: ApiChat; chat?: ApiChat;
monoforumChat?: ApiChat;
threadId?: ThreadId; threadId?: ThreadId;
requestedQuery?: string; requestedQuery?: string;
savedTags?: Record<ApiReactionKey, ApiSavedReactionTag>; savedTags?: Record<ApiReactionKey, ApiSavedReactionTag>;
@ -97,6 +99,7 @@ const runDebouncedForSearch = debounce((cb) => cb(), 200, false);
const MiddleSearch: FC<StateProps> = ({ const MiddleSearch: FC<StateProps> = ({
isActive, isActive,
chat, chat,
monoforumChat,
threadId, threadId,
requestedQuery, requestedQuery,
savedTags, savedTags,
@ -645,7 +648,7 @@ const MiddleSearch: FC<StateProps> = ({
{!isMobile && ( {!isMobile && (
<Avatar <Avatar
className={styles.avatar} className={styles.avatar}
peer={chat} peer={monoforumChat || chat}
size="medium" size="medium"
isSavedMessages={isSavedMessages} isSavedMessages={isSavedMessages}
/> />
@ -793,8 +796,11 @@ export default memo(withGlobal<OwnProps>(
const savedTags = isSavedMessages && !isSavedDialog ? global.savedReactionTags?.byKey : undefined; const savedTags = isSavedMessages && !isSavedDialog ? global.savedReactionTags?.byKey : undefined;
const monoforumChat = selectMonoforumChannel(global, chatId);
return { return {
chat, chat,
monoforumChat,
requestedQuery, requestedQuery,
totalCount, totalCount,
threadId, threadId,

View File

@ -77,7 +77,8 @@ const GifSearch: FC<OwnProps & StateProps> = ({
observe: observeIntersection, observe: observeIntersection,
} = useIntersectionObserver({ rootRef: containerRef, debounceMs: INTERSECTION_DEBOUNCE }); } = useIntersectionObserver({ rootRef: containerRef, debounceMs: INTERSECTION_DEBOUNCE });
const canSendGifs = canPostInChat && getAllowedAttachmentOptions(chat, chatFullInfo, isChatWithBot).canSendGifs; const canSendGifs = canPostInChat
&& getAllowedAttachmentOptions(chat, chatFullInfo, isChatWithBot, isSavedMessages).canSendGifs;
const handleGifClick = useCallback((gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => { const handleGifClick = useCallback((gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => {
if (canSendGifs) { if (canSendGifs) {

View File

@ -49,6 +49,7 @@ import {
selectCurrentSharedMediaSearch, selectCurrentSharedMediaSearch,
selectIsCurrentUserPremium, selectIsCurrentUserPremium,
selectIsRightColumnShown, selectIsRightColumnShown,
selectMonoforumChannel,
selectPeerStories, selectPeerStories,
selectSimilarBotsIds, selectSimilarBotsIds,
selectSimilarChannelIds, selectSimilarChannelIds,
@ -114,11 +115,12 @@ type OwnProps = {
threadId?: ThreadId; threadId?: ThreadId;
profileState: ProfileState; profileState: ProfileState;
isMobile?: boolean; isMobile?: boolean;
onProfileStateChange: (state: ProfileState) => void;
isActive: boolean; isActive: boolean;
onProfileStateChange: (state: ProfileState) => void;
}; };
type StateProps = { type StateProps = {
monoforumChannel?: ApiChat;
theme: ThemeKey; theme: ThemeKey;
isChannel?: boolean; isChannel?: boolean;
isBot?: boolean; isBot?: boolean;
@ -183,6 +185,7 @@ const Profile: FC<OwnProps & StateProps> = ({
threadId, threadId,
profileState, profileState,
theme, theme,
monoforumChannel,
isChannel, isChannel,
isBot, isBot,
currentUserId, currentUserId,
@ -376,6 +379,9 @@ const Profile: FC<OwnProps & StateProps> = ({
const handleLoadGifts = useCallback(() => { const handleLoadGifts = useCallback(() => {
loadPeerSavedGifts({ peerId: chatId }); loadPeerSavedGifts({ peerId: chatId });
}, [chatId]); }, [chatId]);
const handleLoadMoreMembers = useCallback(() => {
loadMoreMembers({ chatId });
}, [chatId, loadMoreMembers]);
useEffectWithPrevDeps(([prevGifts]) => { useEffectWithPrevDeps(([prevGifts]) => {
if (!gifts || !prevGifts) { if (!gifts || !prevGifts) {
@ -397,7 +403,7 @@ const Profile: FC<OwnProps & StateProps> = ({
}, [gifts, startViewTransition]); }, [gifts, startViewTransition]);
const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds({ const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds({
loadMoreMembers, loadMoreMembers: handleLoadMoreMembers,
searchMessages: searchSharedMediaMessages, searchMessages: searchSharedMediaMessages,
loadStories: handleLoadPeerStories, loadStories: handleLoadPeerStories,
loadStoriesArchive: handleLoadStoriesArchive, loadStoriesArchive: handleLoadStoriesArchive,
@ -872,7 +878,12 @@ const Profile: FC<OwnProps & StateProps> = ({
onScroll={handleScroll} onScroll={handleScroll}
> >
{!noProfileInfo && !isSavedMessages && ( {!noProfileInfo && !isSavedMessages && (
renderProfileInfo(profileId, isRightColumnShown && canRenderContent, isSavedDialog) renderProfileInfo(
monoforumChannel?.id || profileId,
isRightColumnShown && canRenderContent,
isSavedDialog,
Boolean(monoforumChannel),
)
)} )}
{!isRestricted && ( {!isRestricted && (
<div <div
@ -915,10 +926,10 @@ const Profile: FC<OwnProps & StateProps> = ({
); );
}; };
function renderProfileInfo(profileId: string, isReady: boolean, isSavedDialog?: boolean) { function renderProfileInfo(profileId: string, isReady: boolean, isSavedDialog?: boolean, isForMonoforum?: boolean) {
return ( return (
<div className="profile-info"> <div className="profile-info">
<ProfileInfo peerId={profileId} canPlayVideo={isReady} /> <ProfileInfo peerId={profileId} canPlayVideo={isReady} isForMonoforum={isForMonoforum} />
<ChatExtra chatOrUserId={profileId} isSavedDialog={isSavedDialog} /> <ChatExtra chatOrUserId={profileId} isSavedDialog={isSavedDialog} />
</div> </div>
); );
@ -949,7 +960,8 @@ export default memo(withGlobal<OwnProps>(
const isGroup = chat && isChatGroup(chat); const isGroup = chat && isChatGroup(chat);
const isChannel = chat && isChatChannel(chat); const isChannel = chat && isChatChannel(chat);
const isBot = user && isUserBot(user); const isBot = user && isUserBot(user);
const hasMembersTab = !isTopicInfo && !isSavedDialog && (isGroup || (isChannel && isChatAdmin(chat))); const hasMembersTab = !isTopicInfo && !isSavedDialog
&& (isGroup || (isChannel && isChatAdmin(chat))) && !chat?.isMonoforum;
const members = chatFullInfo?.members; const members = chatFullInfo?.members;
const adminMembersById = chatFullInfo?.adminMembersById; const adminMembersById = chatFullInfo?.adminMembersById;
const areMembersHidden = hasMembersTab && chat const areMembersHidden = hasMembersTab && chat
@ -984,6 +996,8 @@ export default memo(withGlobal<OwnProps>(
const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog; const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog;
const peerGifts = selectTabState(global).savedGifts.giftsByPeerId[chatId]; const peerGifts = selectTabState(global).savedGifts.giftsByPeerId[chatId];
const monoforumChannel = selectMonoforumChannel(global, chatId);
return { return {
theme: selectTheme(global), theme: selectTheme(global),
isChannel, isChannel,
@ -1025,6 +1039,7 @@ export default memo(withGlobal<OwnProps>(
limitSimilarPeers: selectPremiumLimit(global, 'recommendedChannels'), limitSimilarPeers: selectPremiumLimit(global, 'recommendedChannels'),
...(hasMembersTab && members && { members, adminMembersById }), ...(hasMembersTab && members && { members, adminMembersById }),
...(hasCommonChatsTab && user && { commonChatIds: commonChats?.ids }), ...(hasCommonChatsTab && user && { commonChatIds: commonChats?.ids }),
monoforumChannel,
}; };
}, },
)(Profile)); )(Profile));

View File

@ -423,6 +423,7 @@ const RightColumn: FC<OwnProps & StateProps> = ({
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { isMobile }): StateProps => { (global, { isMobile }): StateProps => {
const { chatId, threadId } = selectCurrentMessageList(global) || {}; const { chatId, threadId } = selectCurrentMessageList(global) || {};
const areActiveChatsLoaded = selectAreActiveChatsLoaded(global); const areActiveChatsLoaded = selectAreActiveChatsLoaded(global);
const { const {
management, shouldSkipHistoryAnimations, nextProfileTab, shouldCloseRightColumn, management, shouldSkipHistoryAnimations, nextProfileTab, shouldCloseRightColumn,

View File

@ -173,12 +173,12 @@ const RightHeader: FC<OwnProps & StateProps> = ({
isInsideTopic, isInsideTopic,
canEditTopic, canEditTopic,
isSavedMessages, isSavedMessages,
onClose,
onScreenSelect,
canEditBot, canEditBot,
giftProfileFilter, giftProfileFilter,
canUseGiftFilter, canUseGiftFilter,
canUseGiftAdminFilter, canUseGiftAdminFilter,
onClose,
onScreenSelect,
}) => { }) => {
const { const {
setStickerSearchQuery, setStickerSearchQuery,

View File

@ -1,7 +1,6 @@
import type { FC } from '../../../lib/teact/teact'; import type { FC } from '../../../lib/teact/teact';
import { import {
memo, useCallback, memo, useMemo, useState,
useMemo, useState,
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global'; import { getActions, withGlobal } from '../../../global';
@ -10,6 +9,7 @@ import type { ApiChat, ApiChatMember } from '../../../api/types';
import { filterPeersByQuery } from '../../../global/helpers/peers'; import { filterPeersByQuery } from '../../../global/helpers/peers';
import { selectChatFullInfo } from '../../../global/selectors'; import { selectChatFullInfo } from '../../../global/selectors';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang'; import useOldLang from '../../../hooks/useOldLang';
import ChatOrUserPicker from '../../common/pickers/ChatOrUserPicker'; import ChatOrUserPicker from '../../common/pickers/ChatOrUserPicker';
@ -52,10 +52,14 @@ const RemoveGroupUserModal: FC<OwnProps & StateProps> = ({
return filterPeersByQuery({ ids: availableMemberIds, query: search, type: 'user' }); return filterPeersByQuery({ ids: availableMemberIds, query: search, type: 'user' });
}, [chatMembers, currentUserId, search]); }, [chatMembers, currentUserId, search]);
const handleRemoveUser = useCallback((userId: string) => { const handleLoadMore = useLastCallback(() => {
loadMoreMembers({ chatId: chat.id });
});
const handleRemoveUser = useLastCallback((userId: string) => {
deleteChatMember({ chatId: chat.id, userId }); deleteChatMember({ chatId: chat.id, userId });
onClose(); onClose();
}, [chat.id, deleteChatMember, onClose]); });
return ( return (
<ChatOrUserPicker <ChatOrUserPicker
@ -64,7 +68,7 @@ const RemoveGroupUserModal: FC<OwnProps & StateProps> = ({
searchPlaceholder={lang('ChannelBlockUser')} searchPlaceholder={lang('ChannelBlockUser')}
search={search} search={search}
onSearchChange={setSearch} onSearchChange={setSearch}
loadMore={loadMoreMembers} loadMore={handleLoadMore}
onSelectChatOrUser={handleRemoveUser} onSelectChatOrUser={handleRemoveUser}
onClose={onClose} onClose={onClose}
/> />

View File

@ -343,7 +343,7 @@ export const REPLIES_USER_ID = '1271266957'; // TODO For Test connection ID must
export const VERIFICATION_CODES_USER_ID = '489000'; export const VERIFICATION_CODES_USER_ID = '489000';
export const ANONYMOUS_USER_ID = '2666000'; export const ANONYMOUS_USER_ID = '2666000';
export const RESTRICTED_EMOJI_SET_ID = '7173162320003080'; export const RESTRICTED_EMOJI_SET_ID = '7173162320003080';
export const CHANNEL_ID_LENGTH = 14; // 14 symbols, based on TDLib's `ZERO_CHANNEL_ID = -1000000000000` export const CHANNEL_ID_BASE = 10 ** 12;
export const DEFAULT_GIF_SEARCH_BOT_USERNAME = 'gif'; export const DEFAULT_GIF_SEARCH_BOT_USERNAME = 'gif';
export const ALL_FOLDER_ID = 0; export const ALL_FOLDER_ID = 0;
export const ARCHIVED_FOLDER_ID = 1; export const ARCHIVED_FOLDER_ID = 1;

View File

@ -52,6 +52,7 @@ import {
isChatArchived, isChatArchived,
isChatBasicGroup, isChatBasicGroup,
isChatChannel, isChatChannel,
isChatMonoforum,
isChatSuperGroup, isChatSuperGroup,
isUserBot, isUserBot,
} from '../../helpers'; } from '../../helpers';
@ -2130,11 +2131,11 @@ addActionHandler('resetOpenChatWithDraft', (global, actions, payload): ActionRet
}); });
addActionHandler('loadMoreMembers', async (global, actions, payload): Promise<void> => { addActionHandler('loadMoreMembers', async (global, actions, payload): Promise<void> => {
const { chatId } = payload || {};
if (selectIsCurrentUserFrozen(global)) return; if (selectIsCurrentUserFrozen(global)) return;
const { tabId = getCurrentTabId() } = payload || {}; const chat = selectChat(global, chatId);
const { chatId } = selectCurrentMessageList(global, tabId) || {}; if (!chat || isChatBasicGroup(chat) || isChatMonoforum(chat)) {
const chat = chatId ? selectChat(global, chatId) : undefined;
if (!chat || isChatBasicGroup(chat)) {
return; return;
} }

View File

@ -581,12 +581,17 @@ addActionHandler('saveDraft', (global, actions, payload): ActionReturnType => {
const { const {
chatId, threadId, text, chatId, threadId, text,
} = payload; } = payload;
if (!text) { const chat = selectChat(global, chatId);
if (!text || !chat) {
return; return;
} }
const currentDraft = selectDraft(global, chatId, threadId); const currentDraft = selectDraft(global, chatId, threadId);
if (chat.isMonoforum && !currentDraft?.replyInfo) {
return; // Monoforum doesn't support drafts outside threads
}
const newDraft: ApiDraft = { const newDraft: ApiDraft = {
text, text,
replyInfo: currentDraft?.replyInfo, replyInfo: currentDraft?.replyInfo,
@ -654,8 +659,12 @@ addActionHandler('resetDraftReplyInfo', (global, actions, payload): ActionReturn
return; return;
} }
const { chatId, threadId } = currentMessageList; const { chatId, threadId } = currentMessageList;
const chat = selectChat(global, chatId);
const currentDraft = selectDraft(global, chatId, threadId); const currentDraft = selectDraft(global, chatId, threadId);
if (chat?.isMonoforum && !currentDraft?.replyInfo) {
return; // Monoforum doesn't support drafts outside threads
}
const newDraft: ApiDraft | undefined = !currentDraft?.text ? undefined : { const newDraft: ApiDraft | undefined = !currentDraft?.text ? undefined : {
...currentDraft, ...currentDraft,
replyInfo: undefined, replyInfo: undefined,
@ -671,7 +680,11 @@ addActionHandler('saveEffectInDraft', (global, actions, payload): ActionReturnTy
chatId, threadId, effectId, chatId, threadId, effectId,
} = payload; } = payload;
const chat = selectChat(global, chatId);
const currentDraft = selectDraft(global, chatId, threadId); const currentDraft = selectDraft(global, chatId, threadId);
if (chat?.isMonoforum && !currentDraft?.replyInfo) {
return; // Monoforum doesn't support drafts outside threads
}
const newDraft = { const newDraft = {
...currentDraft, ...currentDraft,
@ -986,7 +999,7 @@ addActionHandler('sendMessageAction', async (global, actions, payload): Promise<
if (selectIsChatWithSelf(global, chatId)) return; if (selectIsChatWithSelf(global, chatId)) return;
const chat = selectChat(global, chatId)!; const chat = selectChat(global, chatId)!;
if (!chat) return; if (!chat || chat.isMonoforum) return;
const user = selectUser(global, chatId); const user = selectUser(global, chatId);
if (user && (isUserBot(user) || isDeletedUser(user))) return; if (user && (isUserBot(user) || isDeletedUser(user))) return;
@ -2140,6 +2153,7 @@ addActionHandler('openChatOrTopicWithReplyInDraft', (global, actions, payload):
replyToMsgId: replyingInfo.messageId, replyToMsgId: replyingInfo.messageId,
replyToTopId: replyingInfo.toThreadId, replyToTopId: replyingInfo.toThreadId,
replyToPeerId: currentChatId, replyToPeerId: currentChatId,
monoforumPeerId: replyingInfo.toThreadId,
quoteText: replyingInfo.quoteText, quoteText: replyingInfo.quoteText,
quoteOffset: replyingInfo.quoteOffset, quoteOffset: replyingInfo.quoteOffset,
} as ApiInputMessageReplyInfo; } as ApiInputMessageReplyInfo;

View File

@ -30,6 +30,8 @@ import {
selectChatMessage, selectChatMessage,
selectCurrentChat, selectCurrentChat,
selectCurrentMessageList, selectCurrentMessageList,
selectIsChatWithBot,
selectIsChatWithSelf,
selectIsCurrentUserPremium, selectIsCurrentUserPremium,
selectIsTrustedBot, selectIsTrustedBot,
selectPeerPaidMessagesStars, selectPeerPaidMessagesStars,
@ -344,11 +346,13 @@ addActionHandler('showAllowedMessageTypesNotification', (global, actions, payloa
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) return; if (!chat) return;
const chatFullInfo = selectChatFullInfo(global, chatId); const chatFullInfo = selectChatFullInfo(global, chatId);
const isSavedMessages = chatId ? selectIsChatWithSelf(global, chatId) : undefined;
const isChatWithBot = chatId ? selectIsChatWithBot(global, chat) : undefined;
const { const {
canSendPlainText, canSendPhotos, canSendVideos, canSendDocuments, canSendAudios, canSendPlainText, canSendPhotos, canSendVideos, canSendDocuments, canSendAudios,
canSendStickers, canSendRoundVideos, canSendVoices, canSendStickers, canSendRoundVideos, canSendVoices,
} = getAllowedAttachmentOptions(chat, chatFullInfo); } = getAllowedAttachmentOptions(chat, chatFullInfo, isChatWithBot, isSavedMessages);
const allowedContent = compact([ const allowedContent = compact([
canSendPlainText ? 'Chat.SendAllowedContentTypeText' : undefined, canSendPlainText ? 'Chat.SendAllowedContentTypeText' : undefined,
canSendPhotos ? 'Chat.SendAllowedContentTypePhoto' : undefined, canSendPhotos ? 'Chat.SendAllowedContentTypePhoto' : undefined,

View File

@ -47,6 +47,10 @@ export function isChatChannel(chat: ApiChat) {
return chat.type === 'chatTypeChannel'; return chat.type === 'chatTypeChannel';
} }
export function isChatMonoforum(chat: ApiChat) {
return chat.isMonoforum;
}
export function isCommonBoxChat(chat: ApiChat) { export function isCommonBoxChat(chat: ApiChat) {
return chat.type === 'chatTypePrivate' || chat.type === 'chatTypeBasicGroup'; return chat.type === 'chatTypePrivate' || chat.type === 'chatTypeBasicGroup';
} }
@ -152,7 +156,8 @@ export function getCanPostInChat(
} }
if (chat.isRestricted || chat.isForbidden || chat.migratedTo if (chat.isRestricted || chat.isForbidden || chat.migratedTo
|| (!isMessageThread && chat.isNotJoined) || isSystemBot(chat.id) || isAnonymousForwardsChat(chat.id)) { || (chat.isNotJoined && !isChatMonoforum(chat) && !isMessageThread)
|| isSystemBot(chat.id) || isAnonymousForwardsChat(chat.id)) {
return false; return false;
} }
@ -190,6 +195,7 @@ export function getAllowedAttachmentOptions(
chat?: ApiChat, chat?: ApiChat,
chatFullInfo?: ApiChatFullInfo, chatFullInfo?: ApiChatFullInfo,
isChatWithBot = false, isChatWithBot = false,
isSavedMessages = false,
isStoryReply = false, isStoryReply = false,
paidMessagesStars?: number, paidMessagesStars?: number,
isInScheduledList = false, isInScheduledList = false,
@ -215,9 +221,9 @@ export function getAllowedAttachmentOptions(
return { return {
canAttachMedia: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendMedia', chatFullInfo), canAttachMedia: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendMedia', chatFullInfo),
canAttachPolls: !isStoryReply canAttachPolls: !isStoryReply && !chat.isMonoforum
&& (isAdmin || !isUserRightBanned(chat, 'sendPolls', chatFullInfo)) && (isAdmin || !isUserRightBanned(chat, 'sendPolls', chatFullInfo))
&& (!isUserId(chat.id) || isChatWithBot), && (!isUserId(chat.id) || isChatWithBot || isSavedMessages),
canSendStickers: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendStickers', chatFullInfo), canSendStickers: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendStickers', chatFullInfo),
canSendGifs: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendGifs', chatFullInfo), canSendGifs: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendGifs', chatFullInfo),
canAttachEmbedLinks: !isStoryReply && (isAdmin || !isUserRightBanned(chat, 'embedLinks', chatFullInfo)), canAttachEmbedLinks: !isStoryReply && (isAdmin || !isUserRightBanned(chat, 'embedLinks', chatFullInfo)),

View File

@ -349,3 +349,28 @@ export function selectChatLastMessage<T extends GlobalState>(
const realChatId = listType === 'saved' ? global.currentUserId! : chatId; const realChatId = listType === 'saved' ? global.currentUserId! : chatId;
return global.messages.byChatId[realChatId]?.byId[id]; return global.messages.byChatId[realChatId]?.byId[id];
} }
export function selectIsMonoforumAdmin<T extends GlobalState>(
global: T, chatId: string,
) {
const chat = selectChat(global, chatId);
if (!chat?.isMonoforum) return;
const channel = selectMonoforumChannel(global, chatId);
if (!channel) return;
return Boolean(chat.isCreator || chat.adminRights || channel.isCreator || channel.adminRights);
}
/**
* Only selects monoforum channel for monoforum chats.
* Returns `undefined` for other chats, including channels that have linked monoforum.
*/
export function selectMonoforumChannel<T extends GlobalState>(
global: T, chatId: string,
) {
const chat = selectChat(global, chatId);
if (!chat) return;
return chat.isMonoforum ? selectChat(global, chat.linkedMonoforumId!) : undefined;
}

View File

@ -72,7 +72,7 @@ export function selectCanManage<T extends GlobalState>(
chatId: string, chatId: string,
) { ) {
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat || chat.isRestricted) return false; if (!chat || chat.isRestricted || chat.isMonoforum) return false;
const isPrivate = isUserId(chat.id); const isPrivate = isUserId(chat.id);
const user = isPrivate ? selectUser(global, chatId) : undefined; const user = isPrivate ? selectUser(global, chatId) : undefined;

View File

@ -72,6 +72,7 @@ import {
selectChat, selectChat,
selectChatFullInfo, selectChatFullInfo,
selectChatLastMessageId, selectChatLastMessageId,
selectIsChatWithBot,
selectIsChatWithSelf, selectIsChatWithSelf,
selectRequestedChatTranslationLanguage, selectRequestedChatTranslationLanguage,
} from './chats'; } from './chats';
@ -651,6 +652,7 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
const { content } = message; const { content } = message;
const isDocumentSticker = isMessageDocumentSticker(message); const isDocumentSticker = isMessageDocumentSticker(message);
const isBoostMessage = message.content.action?.type === 'boostApply'; const isBoostMessage = message.content.action?.type === 'boostApply';
const isMonoforum = chat.isMonoforum;
const hasChatPinPermission = (chat.isCreator const hasChatPinPermission = (chat.isCreator
|| (!isChannel && !isUserRightBanned(chat, 'pinMessages')) || (!isChannel && !isUserRightBanned(chat, 'pinMessages'))
@ -726,7 +728,7 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
const canFaveSticker = !isAction && hasSticker && !hasFavoriteSticker; const canFaveSticker = !isAction && hasSticker && !hasFavoriteSticker;
const canUnfaveSticker = !isAction && hasFavoriteSticker; const canUnfaveSticker = !isAction && hasFavoriteSticker;
const canCopy = !isAction; const canCopy = !isAction;
const canCopyLink = !isLocal && !isAction && (isChannel || isSuperGroup); const canCopyLink = !isLocal && !isAction && (isChannel || isSuperGroup) && !isMonoforum;
const canSelect = !isLocal && !isAction; const canSelect = !isLocal && !isAction;
const canDownload = Boolean(content.webPage?.document || content.webPage?.video || content.webPage?.photo const canDownload = Boolean(content.webPage?.document || content.webPage?.video || content.webPage?.photo
@ -1462,7 +1464,10 @@ export function selectForwardsCanBeSentToChat<T extends GlobalState>(
const chatFullInfo = selectChatFullInfo(global, toChatId); const chatFullInfo = selectChatFullInfo(global, toChatId);
const chatMessages = selectChatMessages(global, fromChatId!); const chatMessages = selectChatMessages(global, fromChatId!);
const options = getAllowedAttachmentOptions(chat, chatFullInfo);
const isSavedMessages = toChatId ? selectIsChatWithSelf(global, toChatId) : undefined;
const isChatWithBot = toChatId ? selectIsChatWithBot(global, chat) : undefined;
const options = getAllowedAttachmentOptions(chat, chatFullInfo, isChatWithBot, isSavedMessages);
return !messageIds!.some((messageId) => сheckMessageSendingDenied(chatMessages[messageId], options)); return !messageIds!.some((messageId) => сheckMessageSendingDenied(chatMessages[messageId], options));
} }
function сheckMessageSendingDenied(message: ApiMessage, options: IAllowedAttachmentOptions) { function сheckMessageSendingDenied(message: ApiMessage, options: IAllowedAttachmentOptions) {

View File

@ -354,7 +354,9 @@ export interface ActionPayloads {
} & WithTabId; } & WithTabId;
openThreadWithInfo: ActionPayloads['openThread'] & WithTabId; openThreadWithInfo: ActionPayloads['openThread'] & WithTabId;
openLinkedChat: { id: string } & WithTabId; openLinkedChat: { id: string } & WithTabId;
loadMoreMembers: WithTabId | undefined; loadMoreMembers: {
chatId: string;
};
setActiveChatFolder: { setActiveChatFolder: {
activeChatFolder: number; activeChatFolder: number;
} & WithTabId; } & WithTabId;

View File

@ -12,5 +12,5 @@ for (const tl of Object.values(Api)) {
} }
} }
export const LAYER = 203; export const LAYER = 204;
export { tlobjects }; export { tlobjects };

File diff suppressed because one or more lines are too long

View File

@ -80,7 +80,7 @@ userStatusLastMonth#65899777 flags:# by_me:flags.0?true = UserStatus;
chatEmpty#29562865 id:long = Chat; chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat; chatForbidden#6592a1a7 id:long title:string = Chat;
channel#7482147e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long = Chat; channel#fe685355 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true broadcast_messages_allowed:flags2.16?true monoforum:flags2.17?true forum_tabs:flags2.19?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long linked_monoforum_id:flags2.18?long = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull; chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
channelFull#52d6806b flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int = ChatFull; channelFull#52d6806b flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int = ChatFull;
@ -160,7 +160,7 @@ messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long trans
messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction; messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction;
messageActionStarGiftUnique#2e3ae60e flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_stars:flags.8?long can_transfer_at:flags.9?int can_resell_at:flags.10?int = MessageAction; messageActionStarGiftUnique#2e3ae60e flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_stars:flags.8?long can_transfer_at:flags.9?int can_resell_at:flags.10?int = MessageAction;
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction; messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
messageActionPaidMessagesPrice#bcd71419 stars:long = MessageAction; messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction; messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog; dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -279,7 +279,7 @@ updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update; updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update; updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update; updateDraftMessage#edfc111e flags:# peer:Peer top_msg_id:flags.0?int saved_peer_id:flags.1?Peer draft:DraftMessage = Update;
updateReadFeaturedStickers#571d2742 = Update; updateReadFeaturedStickers#571d2742 = Update;
updateRecentStickers#9a422c20 = Update; updateRecentStickers#9a422c20 = Update;
updateConfig#a229dd06 = Update; updateConfig#a229dd06 = Update;
@ -295,10 +295,10 @@ updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
updateLangPackTooLong#46560264 lang_code:string = Update; updateLangPackTooLong#46560264 lang_code:string = Update;
updateLangPack#56022f4d difference:LangPackDifference = Update; updateLangPack#56022f4d difference:LangPackDifference = Update;
updateFavedStickers#e511996d = Update; updateFavedStickers#e511996d = Update;
updateChannelReadMessagesContents#ea29055d flags:# channel_id:long top_msg_id:flags.0?int messages:Vector<int> = Update; updateChannelReadMessagesContents#25f324f7 flags:# channel_id:long top_msg_id:flags.0?int saved_peer_id:flags.1?Peer messages:Vector<int> = Update;
updateContactsReset#7084a7be = Update; updateContactsReset#7084a7be = Update;
updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update; updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update;
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update; updateDialogUnreadMark#b658f23e flags:# unread:flags.0?true peer:DialogPeer saved_peer_id:flags.1?Peer = Update;
updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update; updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update; updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update; updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
@ -332,7 +332,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update; updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update; updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update; updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update; updateMessageReactions#1e297bfa flags:# peer:Peer msg_id:int top_msg_id:flags.0?int saved_peer_id:flags.1?Peer reactions:MessageReactions = Update;
updateAttachMenuBots#17b7a20b = Update; updateAttachMenuBots#17b7a20b = Update;
updateWebViewResultSent#1592b79d query_id:long = Update; updateWebViewResultSent#1592b79d query_id:long = Update;
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update; updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
@ -380,6 +380,8 @@ updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Updat
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update; updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update; updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update; updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference; updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference; updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
@ -1234,8 +1236,9 @@ storyViewPublicForward#9083670b flags:# blocked:flags.0?true blocked_my_stories_
storyViewPublicRepost#bd74cf49 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true peer_id:Peer story:StoryItem = StoryView; storyViewPublicRepost#bd74cf49 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true peer_id:Peer story:StoryItem = StoryView;
stories.storyViewsList#59d78fc5 flags:# count:int views_count:int forwards_count:int reactions_count:int views:Vector<StoryView> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryViewsList; stories.storyViewsList#59d78fc5 flags:# count:int views_count:int forwards_count:int reactions_count:int views:Vector<StoryView> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryViewsList;
stories.storyViews#de9eed1d views:Vector<StoryViews> users:Vector<User> = stories.StoryViews; stories.storyViews#de9eed1d views:Vector<StoryViews> users:Vector<User> = stories.StoryViews;
inputReplyToMessage#22c0f6d5 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity> quote_offset:flags.4?int = InputReplyTo; inputReplyToMessage#b07038b0 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity> quote_offset:flags.4?int monoforum_peer_id:flags.5?InputPeer = InputReplyTo;
inputReplyToStory#5881323a peer:InputPeer story_id:int = InputReplyTo; inputReplyToStory#5881323a peer:InputPeer story_id:int = InputReplyTo;
inputReplyToMonoForum#69d66c45 monoforum_peer_id:InputPeer = InputReplyTo;
exportedStoryLink#3fc9053b link:string = ExportedStoryLink; exportedStoryLink#3fc9053b link:string = ExportedStoryLink;
storiesStealthMode#712e27fd flags:# active_until_date:flags.0?int cooldown_until_date:flags.1?int = StoriesStealthMode; storiesStealthMode#712e27fd flags:# active_until_date:flags.0?int cooldown_until_date:flags.1?int = StoriesStealthMode;
mediaAreaCoordinates#cfc9e002 flags:# x:double y:double w:double h:double rotation:double radius:flags.0?double = MediaAreaCoordinates; mediaAreaCoordinates#cfc9e002 flags:# x:double y:double w:double h:double rotation:double radius:flags.0?double = MediaAreaCoordinates;
@ -1280,6 +1283,7 @@ storyReactionPublicForward#bbab2643 message:Message = StoryReaction;
storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction; storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction;
stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList; stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;
savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog; savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;
monoForumDialog#64407ea7 flags:# unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_reactions_count:int draft:flags.1?DraftMessage = SavedDialog;
messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs; messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs; messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
messages.savedDialogsNotModified#c01f6fe8 count:int = messages.SavedDialogs; messages.savedDialogsNotModified#c01f6fe8 count:int = messages.SavedDialogs;
@ -1527,7 +1531,7 @@ messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#fbf2340a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; messages.sendMessage#fbf2340a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
messages.sendMedia#a550cd78 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; messages.sendMedia#a550cd78 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
messages.forwardMessages#bb9fa475 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long = Updates; messages.forwardMessages#38f0188c flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int reply_to:flags.22?InputReplyTo schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings; messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult; messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
@ -1578,7 +1582,7 @@ messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?in
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
messages.sendMultiMedia#1bf89d74 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; messages.sendMultiMedia#1bf89d74 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool; messages.markDialogUnread#8c5006f8 flags:# unread:flags.0?true parent_peer:flags.1?InputPeer peer:InputDialogPeer = Bool;
messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates; messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates;
messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates; messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines; messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
@ -1599,7 +1603,7 @@ messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
messages.unpinAllMessages#ee22b9a8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; messages.unpinAllMessages#62dd747 flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer = messages.AffectedHistory;
messages.deleteChat#5bd0ee50 chat_id:long = Bool; messages.deleteChat#5bd0ee50 chat_id:long = Bool;
messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites;
messages.editExportedChatInvite#bdca2f75 flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int request_needed:flags.3?Bool title:flags.4?string = messages.ExportedChatInvite; messages.editExportedChatInvite#bdca2f75 flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int request_needed:flags.3?Bool title:flags.4?string = messages.ExportedChatInvite;
@ -1618,8 +1622,8 @@ messages.setChatAvailableReactions#864b2581 flags:# peer:InputPeer available_rea
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions; messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool; messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<TextWithEntities> to_lang:string = messages.TranslatedText; messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<TextWithEntities> to_lang:string = messages.TranslatedText;
messages.getUnreadReactions#3223495b flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; messages.getUnreadReactions#bd7f90ac flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; messages.readReactions#9ec44f93 flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer = messages.AffectedHistory;
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots; messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot; messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool; messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool;
@ -1639,9 +1643,9 @@ messages.getExtendedMedia#84f80814 peer:InputPeer id:Vector<int> = Updates;
messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool; messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool;
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp; messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult; messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult;
messages.getSavedDialogs#5381d21a flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs; messages.getSavedDialogs#1e91fc99 flags:# exclude_pinned:flags.0?true parent_peer:flags.1?InputPeer offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs;
messages.getSavedHistory#3d9a414d peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.getSavedHistory#998ab009 flags:# parent_peer:flags.0?InputPeer peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.deleteSavedHistory#6e98102b flags:# peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory; messages.deleteSavedHistory#4dc5085f flags:# parent_peer:flags.0?InputPeer peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory;
messages.getPinnedSavedDialogs#d63d94e0 = messages.SavedDialogs; messages.getPinnedSavedDialogs#d63d94e0 = messages.SavedDialogs;
messages.toggleSavedDialogPin#ac81bbde flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; messages.toggleSavedDialogPin#ac81bbde flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.getSavedReactionTags#3637e05b flags:# peer:flags.0?InputPeer hash:long = messages.SavedReactionTags; messages.getSavedReactionTags#3637e05b flags:# peer:flags.0?InputPeer hash:long = messages.SavedReactionTags;
@ -1715,7 +1719,7 @@ channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool; channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool; channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool; channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool;
channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates; channels.toggleForum#3ff75734 channel:InputChannel enabled:Bool tabs:Bool = Updates;
channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates; channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics; channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector<int> = messages.ForumTopics; channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector<int> = messages.ForumTopics;
@ -1726,7 +1730,7 @@ channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = U
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates; channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats; channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats;
channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
channels.updatePaidMessagesPrice#fc84653f channel:InputChannel send_paid_messages_stars:long = Updates; channels.updatePaidMessagesPrice#4b12327b flags:# broadcast_messages_allowed:flags.0?true channel:InputChannel send_paid_messages_stars:long = Updates;
channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates; channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates;
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool; bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
bots.canSendMessage#1359f4e6 bot:InputUser = Bool; bots.canSendMessage#1359f4e6 bot:InputUser = Bool;

View File

@ -99,7 +99,7 @@ userStatusLastMonth#65899777 flags:# by_me:flags.0?true = UserStatus;
chatEmpty#29562865 id:long = Chat; chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat; chatForbidden#6592a1a7 id:long title:string = Chat;
channel#7482147e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long = Chat; channel#fe685355 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true broadcast_messages_allowed:flags2.16?true monoforum:flags2.17?true forum_tabs:flags2.19?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long linked_monoforum_id:flags2.18?long = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull; chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
@ -186,7 +186,7 @@ messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long trans
messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction; messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction;
messageActionStarGiftUnique#2e3ae60e flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_stars:flags.8?long can_transfer_at:flags.9?int can_resell_at:flags.10?int = MessageAction; messageActionStarGiftUnique#2e3ae60e flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_stars:flags.8?long can_transfer_at:flags.9?int can_resell_at:flags.10?int = MessageAction;
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction; messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
messageActionPaidMessagesPrice#bcd71419 stars:long = MessageAction; messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction; messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog; dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
@ -332,7 +332,7 @@ updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update; updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update; updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update; updateDraftMessage#edfc111e flags:# peer:Peer top_msg_id:flags.0?int saved_peer_id:flags.1?Peer draft:DraftMessage = Update;
updateReadFeaturedStickers#571d2742 = Update; updateReadFeaturedStickers#571d2742 = Update;
updateRecentStickers#9a422c20 = Update; updateRecentStickers#9a422c20 = Update;
updateConfig#a229dd06 = Update; updateConfig#a229dd06 = Update;
@ -348,10 +348,10 @@ updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
updateLangPackTooLong#46560264 lang_code:string = Update; updateLangPackTooLong#46560264 lang_code:string = Update;
updateLangPack#56022f4d difference:LangPackDifference = Update; updateLangPack#56022f4d difference:LangPackDifference = Update;
updateFavedStickers#e511996d = Update; updateFavedStickers#e511996d = Update;
updateChannelReadMessagesContents#ea29055d flags:# channel_id:long top_msg_id:flags.0?int messages:Vector<int> = Update; updateChannelReadMessagesContents#25f324f7 flags:# channel_id:long top_msg_id:flags.0?int saved_peer_id:flags.1?Peer messages:Vector<int> = Update;
updateContactsReset#7084a7be = Update; updateContactsReset#7084a7be = Update;
updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update; updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update;
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update; updateDialogUnreadMark#b658f23e flags:# unread:flags.0?true peer:DialogPeer saved_peer_id:flags.1?Peer = Update;
updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update; updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update; updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update; updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
@ -385,7 +385,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update; updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update; updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update; updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update; updateMessageReactions#1e297bfa flags:# peer:Peer msg_id:int top_msg_id:flags.0?int saved_peer_id:flags.1?Peer reactions:MessageReactions = Update;
updateAttachMenuBots#17b7a20b = Update; updateAttachMenuBots#17b7a20b = Update;
updateWebViewResultSent#1592b79d query_id:long = Update; updateWebViewResultSent#1592b79d query_id:long = Update;
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update; updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
@ -433,6 +433,8 @@ updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Updat
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update; updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update; updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update; updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1638,8 +1640,9 @@ stories.storyViewsList#59d78fc5 flags:# count:int views_count:int forwards_count
stories.storyViews#de9eed1d views:Vector<StoryViews> users:Vector<User> = stories.StoryViews; stories.storyViews#de9eed1d views:Vector<StoryViews> users:Vector<User> = stories.StoryViews;
inputReplyToMessage#22c0f6d5 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity> quote_offset:flags.4?int = InputReplyTo; inputReplyToMessage#b07038b0 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity> quote_offset:flags.4?int monoforum_peer_id:flags.5?InputPeer = InputReplyTo;
inputReplyToStory#5881323a peer:InputPeer story_id:int = InputReplyTo; inputReplyToStory#5881323a peer:InputPeer story_id:int = InputReplyTo;
inputReplyToMonoForum#69d66c45 monoforum_peer_id:InputPeer = InputReplyTo;
exportedStoryLink#3fc9053b link:string = ExportedStoryLink; exportedStoryLink#3fc9053b link:string = ExportedStoryLink;
@ -1712,6 +1715,7 @@ storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction;
stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList; stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;
savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog; savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;
monoForumDialog#64407ea7 flags:# unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_reactions_count:int draft:flags.1?DraftMessage = SavedDialog;
messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs; messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs; messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
@ -2177,7 +2181,7 @@ messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#fbf2340a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; messages.sendMessage#fbf2340a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
messages.sendMedia#a550cd78 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; messages.sendMedia#a550cd78 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
messages.forwardMessages#bb9fa475 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long = Updates; messages.forwardMessages#38f0188c flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int reply_to:flags.22?InputReplyTo schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings; messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult; messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
@ -2259,8 +2263,8 @@ messages.sendMultiMedia#1bf89d74 flags:# silent:flags.5?true background:flags.6?
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>; messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool; messages.markDialogUnread#8c5006f8 flags:# unread:flags.0?true parent_peer:flags.1?InputPeer peer:InputDialogPeer = Bool;
messages.getDialogUnreadMarks#22e24e22 = Vector<DialogPeer>; messages.getDialogUnreadMarks#21202222 flags:# parent_peer:flags.0?InputPeer = Vector<DialogPeer>;
messages.clearAllDrafts#7e58ee9c = Bool; messages.clearAllDrafts#7e58ee9c = Bool;
messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates; messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates;
messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates; messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
@ -2290,7 +2294,7 @@ messages.getOldFeaturedStickers#7ed094a1 offset:int limit:int hash:long = messag
messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
messages.unpinAllMessages#ee22b9a8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; messages.unpinAllMessages#62dd747 flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer = messages.AffectedHistory;
messages.deleteChat#5bd0ee50 chat_id:long = Bool; messages.deleteChat#5bd0ee50 chat_id:long = Bool;
messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages; messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages;
messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed;
@ -2321,8 +2325,8 @@ messages.setChatAvailableReactions#864b2581 flags:# peer:InputPeer available_rea
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions; messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool; messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<TextWithEntities> to_lang:string = messages.TranslatedText; messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<TextWithEntities> to_lang:string = messages.TranslatedText;
messages.getUnreadReactions#3223495b flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; messages.getUnreadReactions#bd7f90ac flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; messages.readReactions#9ec44f93 flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer = messages.AffectedHistory;
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages; messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots; messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot; messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
@ -2354,9 +2358,9 @@ messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult; messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult;
messages.setChatWallPaper#8ffacae1 flags:# for_both:flags.3?true revert:flags.4?true peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates; messages.setChatWallPaper#8ffacae1 flags:# for_both:flags.3?true revert:flags.4?true peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates;
messages.searchEmojiStickerSets#92b4494c flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.searchEmojiStickerSets#92b4494c flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSavedDialogs#5381d21a flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs; messages.getSavedDialogs#1e91fc99 flags:# exclude_pinned:flags.0?true parent_peer:flags.1?InputPeer offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs;
messages.getSavedHistory#3d9a414d peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.getSavedHistory#998ab009 flags:# parent_peer:flags.0?InputPeer peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.deleteSavedHistory#6e98102b flags:# peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory; messages.deleteSavedHistory#4dc5085f flags:# parent_peer:flags.0?InputPeer peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory;
messages.getPinnedSavedDialogs#d63d94e0 = messages.SavedDialogs; messages.getPinnedSavedDialogs#d63d94e0 = messages.SavedDialogs;
messages.toggleSavedDialogPin#ac81bbde flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; messages.toggleSavedDialogPin#ac81bbde flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.reorderPinnedSavedDialogs#8b716587 flags:# force:flags.0?true order:Vector<InputDialogPeer> = Bool; messages.reorderPinnedSavedDialogs#8b716587 flags:# force:flags.0?true order:Vector<InputDialogPeer> = Bool;
@ -2391,6 +2395,8 @@ messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage; messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers; messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers;
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool; messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
messages.getSavedDialogsByID#6f6f9c96 flags:# parent_peer:flags.1?InputPeer ids:Vector<InputPeer> = messages.SavedDialogs;
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
updates.getState#edd4882a = updates.State; updates.getState#edd4882a = updates.State;
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference; updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
@ -2479,7 +2485,7 @@ channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool; channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool; channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool; channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool;
channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates; channels.toggleForum#3ff75734 channel:InputChannel enabled:Bool tabs:Bool = Updates;
channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates; channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics; channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector<int> = messages.ForumTopics; channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector<int> = messages.ForumTopics;
@ -2498,8 +2504,9 @@ channels.setBoostsToUnblockRestrictions#ad399cee channel:InputChannel boosts:int
channels.setEmojiStickers#3cd930b7 channel:InputChannel stickerset:InputStickerSet = Bool; channels.setEmojiStickers#3cd930b7 channel:InputChannel stickerset:InputStickerSet = Bool;
channels.restrictSponsoredMessages#9ae91519 channel:InputChannel restricted:Bool = Updates; channels.restrictSponsoredMessages#9ae91519 channel:InputChannel restricted:Bool = Updates;
channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
channels.updatePaidMessagesPrice#fc84653f channel:InputChannel send_paid_messages_stars:long = Updates; channels.updatePaidMessagesPrice#4b12327b flags:# broadcast_messages_allowed:flags.0?true channel:InputChannel send_paid_messages_stars:long = Updates;
channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates; channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates;
channels.getMessageAuthor#ece2a0e6 channel:InputChannel id:int = User;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;

View File

@ -1509,7 +1509,7 @@ export interface LangPair {
'FrozenAccountAppealTitle': undefined; 'FrozenAccountAppealTitle': undefined;
'ButtonAppeal': undefined; 'ButtonAppeal': undefined;
'ButtonUnderstood': undefined; 'ButtonUnderstood': undefined;
'ActionPaidMessageGroupPriceFree': undefined; 'ActionPaidMessagePriceFreeYou': undefined;
'NotificationTitleNotSupportedInFrozenAccount': undefined; 'NotificationTitleNotSupportedInFrozenAccount': undefined;
'NotificationMessageNotSupportedInFrozenAccount': undefined; 'NotificationMessageNotSupportedInFrozenAccount': undefined;
'GiftRibbonSale': undefined; 'GiftRibbonSale': undefined;
@ -1530,6 +1530,10 @@ export interface LangPair {
'ValueGiftSortByNumber': undefined; 'ValueGiftSortByNumber': undefined;
'ResellGiftsNoFound': undefined; 'ResellGiftsNoFound': undefined;
'ResellGiftsClearFilters': undefined; 'ResellGiftsClearFilters': undefined;
'MonoforumBadge': undefined;
'MonoforumStatus': undefined;
'MonoforumComposerPlaceholder': undefined;
'ChannelSendMessage': undefined;
'AutomaticTranslation': undefined; 'AutomaticTranslation': undefined;
} }
@ -2393,8 +2397,12 @@ export interface LangPairWithVariables<V = LangVariable> {
'percent': V; 'percent': V;
'amount': V; 'amount': V;
}; };
'FirstMessageInPaidMessagesChat': { 'MessagesPlaceholderPaidUser': {
'user': V; 'peer': V;
'amount': V;
};
'MessagesPlaceholderPaidChannel': {
'peer': V;
'amount': V; 'amount': V;
}; };
'ComposerPlaceholderPaidMessage': { 'ComposerPlaceholderPaidMessage': {
@ -2434,8 +2442,21 @@ export interface LangPairWithVariables<V = LangVariable> {
'botLink': V; 'botLink': V;
'date': V; 'date': V;
}; };
'ActionPaidMessageGroupPrice': { 'ActionPaidMessagePrice': {
'stars': V; 'peer': V;
'amount': V;
};
'ActionPaidMessagePriceYou': {
'amount': V;
};
'ActionPaidMessagePriceFree': {
'peer': V;
};
'ActionMessageChannelFree': {
'peer': V;
};
'ActionMessageChannelDisabled': {
'peer': V;
}; };
'ApiMessageActionPaidMessagesRefundedOutgoing': { 'ApiMessageActionPaidMessagesRefundedOutgoing': {
'stars': V; 'stars': V;

View File

@ -1,15 +1,17 @@
import { CHANNEL_ID_LENGTH } from '../../config'; import { CHANNEL_ID_BASE } from '../../config';
export function isUserId(entityId: string) { export function isUserId(entityId: string) {
return !entityId.startsWith('-'); return !entityId.startsWith('-');
} }
export function isChannelId(entityId: string) { export function isChannelId(entityId: string) {
return entityId.length === CHANNEL_ID_LENGTH && entityId.startsWith('-1'); const n = Number(entityId);
return n < -CHANNEL_ID_BASE;
} }
export function toChannelId(mtpId: string) { export function toChannelId(mtpId: string) {
return `-1${mtpId.padStart(CHANNEL_ID_LENGTH - 2, '0')}`; const n = Number(mtpId);
return String(-CHANNEL_ID_BASE - n);
} }
export function getCleanPeerId(peerId: string) { export function getCleanPeerId(peerId: string) {