This commit is contained in:
Alexander Zinchuk 2024-03-01 14:02:45 -05:00
parent 2bed347f28
commit 033f8d4791
62 changed files with 883 additions and 581 deletions

View File

@ -66,6 +66,8 @@ export interface GramJsAppConfig extends LimitsConfig {
story_expire_period: number;
story_viewers_expire_period: number;
stories_changelog_user_id?: number;
// Boosts
group_transcribe_level_min?: number;
}
function buildEmojiSounds(appConfig: GramJsAppConfig) {
@ -134,5 +136,6 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
storyExpirePeriod: appConfig.story_expire_period ?? STORY_EXPIRE_PERIOD,
storyViewersExpirePeriod: appConfig.story_viewers_expire_period ?? STORY_VIEWERS_EXPIRE_PERIOD,
storyChangelogUserId: appConfig.stories_changelog_user_id?.toString() ?? SERVICE_NOTIFICATIONS_USER_ID,
groupTranscribeLevelMin: appConfig.group_transcribe_level_min,
};
}

View File

@ -62,8 +62,9 @@ function buildApiChatFieldsFromPeerEntity(
const color = ('color' in peerEntity && peerEntity.color) ? buildApiPeerColor(peerEntity.color) : undefined;
const emojiStatus = ('emojiStatus' in peerEntity && peerEntity.emojiStatus)
? buildApiEmojiStatus(peerEntity.emojiStatus) : undefined;
const boostLevel = ('level' in peerEntity) ? peerEntity.level : undefined;
return omitUndefined({
return omitUndefined<PeerEntityApiChatFields>({
isMin,
hasPrivateLink,
isSignaturesShown,
@ -93,6 +94,7 @@ function buildApiChatFieldsFromPeerEntity(
maxStoryId,
hasStories: Boolean(maxStoryId) && !storiesUnavailable,
emojiStatus,
boostLevel,
});
}

View File

@ -159,7 +159,7 @@ export type UniversalMessage = (
'out' | 'message' | 'entities' | 'fromId' | 'peerId' | 'fwdFrom' | 'replyTo' | 'replyMarkup' | 'post' |
'media' | 'action' | 'views' | 'editDate' | 'editHide' | 'mediaUnread' | 'groupedId' | 'mentioned' | 'viaBotId' |
'replies' | 'fromScheduled' | 'postAuthor' | 'noforwards' | 'reactions' | 'forwards' | 'silent' | 'pinned' |
'savedPeerId'
'savedPeerId' | 'fromBoostsApplied'
)>
);
@ -197,10 +197,11 @@ export function buildApiMessageWithChatId(
const isForwardingAllowed = !mtpMessage.noforwards;
const emojiOnlyCount = getEmojiOnlyCountForMessage(content, groupedId);
const hasComments = mtpMessage.replies?.comments;
const senderBoosts = mtpMessage.fromBoostsApplied;
const savedPeerId = mtpMessage.savedPeerId && getApiChatIdFromMtpPeer(mtpMessage.savedPeerId);
return omitUndefined({
return omitUndefined<ApiMessage>({
id: mtpMessage.id,
chatId,
isOutgoing,
@ -237,7 +238,8 @@ export function buildApiMessageWithChatId(
isForwardingAllowed,
hasComments,
savedPeerId,
} satisfies ApiMessage);
senderBoosts,
});
}
export function buildMessageDraft(draft: GramJs.TypeDraftMessage): ApiDraft | undefined {
@ -288,7 +290,7 @@ function buildApiReplyInfo(replyHeader: GramJs.TypeMessageReplyHeader): ApiReply
if (replyHeader instanceof GramJs.MessageReplyStoryHeader) {
return {
type: 'story',
userId: replyHeader.userId.toString(),
peerId: getApiChatIdFromMtpPeer(replyHeader.peer),
storyId: replyHeader.storyId,
};
}
@ -563,6 +565,20 @@ function buildAction(
amount = action.winnersCount;
pluralValue = action.winnersCount;
}
} else if (action instanceof GramJs.MessageActionBoostApply) {
type = 'chatBoost';
if (action.boosts === 1) {
text = senderId === currentUserId ? 'BoostingBoostsGroupByYouServiceMsg' : 'BoostingBoostsGroupByUserServiceMsg';
translationValues.push('%action_origin%');
} else {
text = senderId === currentUserId ? 'BoostingBoostsGroupByYouServiceMsgCount'
: 'BoostingBoostsGroupByUserServiceMsgCount';
translationValues.push(action.boosts.toString());
if (senderId !== currentUserId) {
translationValues.unshift('%action_origin%');
}
pluralValue = action.boosts;
}
} else {
text = 'ChatList.UnsupportedMessage';
}
@ -901,7 +917,7 @@ function buildReplyInfo(inputInfo: ApiInputReplyInfo, isForum?: boolean): ApiRep
if (inputInfo.type === 'story') {
return {
type: 'story',
userId: inputInfo.userId,
peerId: inputInfo.peerId,
storyId: inputInfo.storyId,
};
}

View File

@ -4,6 +4,7 @@ import type {
ApiMediaArea,
ApiMediaAreaCoordinates,
ApiStealthMode,
ApiStory,
ApiStoryForwardInfo,
ApiStoryView,
ApiStoryViews,
@ -46,7 +47,7 @@ export function buildApiStory(peerId: string, story: GramJs.TypeStoryItem): ApiT
edited, pinned, expireDate, id, date, caption,
entities, media, privacy, views,
public: isPublic, noforwards, closeFriends, contacts, selectedContacts,
mediaAreas, sentReaction, out, fwdFrom,
mediaAreas, sentReaction, out, fwdFrom, fromId,
} = story;
const content: MediaContent = {
@ -57,38 +58,38 @@ export function buildApiStory(peerId: string, story: GramJs.TypeStoryItem): ApiT
content.text = buildMessageTextContent(caption, entities);
}
return {
return omitUndefined<ApiStory>({
id,
peerId,
date,
expireDate,
content,
...(isPublic && { isPublic }),
...(edited && { isEdited: true }),
...(pinned && { isPinned: true }),
...(contacts && { isForContacts: true }),
...(selectedContacts && { isForSelectedContacts: true }),
...(closeFriends && { isForCloseFriends: true }),
...(noforwards && { noForwards: true }),
...(views && { views: buildApiStoryViews(views) }),
...(out && { isOut: true }),
...(privacy && { visibility: buildPrivacyRules(privacy) }),
...(mediaAreas && { mediaAreas: mediaAreas.map(buildApiMediaArea).filter(Boolean) }),
...(sentReaction && { sentReaction: buildApiReaction(sentReaction) }),
...(fwdFrom && { forwardInfo: buildApiStoryForwardInfo(fwdFrom) }),
};
isPublic,
isEdited: edited,
isPinned: pinned,
isForContacts: contacts,
isForSelectedContacts: selectedContacts,
isForCloseFriends: closeFriends,
noForwards: noforwards,
views: views && buildApiStoryViews(views),
isOut: out,
visibility: privacy && buildPrivacyRules(privacy),
mediaAreas: mediaAreas?.map(buildApiMediaArea).filter(Boolean),
sentReaction: sentReaction && buildApiReaction(sentReaction),
forwardInfo: fwdFrom && buildApiStoryForwardInfo(fwdFrom),
fromId: fromId && getApiChatIdFromMtpPeer(fromId),
});
}
function buildApiStoryViews(views: GramJs.TypeStoryViews): ApiStoryViews | undefined {
return {
export function buildApiStoryViews(views: GramJs.TypeStoryViews): ApiStoryViews {
return omitUndefined<ApiStoryViews>({
hasViewers: views.hasViewers,
viewsCount: views.viewsCount,
forwardsCount: views.forwardsCount,
reactionsCount: views.reactionsCount,
...(views?.reactions && { reactions: views.reactions.map(buildReactionCount).filter(Boolean) }),
...(views?.recentViewers && {
recentViewerIds: views.recentViewers.map((viewerId) => buildApiPeerId(viewerId, 'user')),
}),
};
reactions: views.reactions?.map(buildReactionCount).filter(Boolean),
recentViewerIds: views.recentViewers?.map((viewerId) => buildApiPeerId(viewerId, 'user')),
});
}
export function buildApiStoryView(view: GramJs.TypeStoryView): ApiTypeStoryView | undefined {

View File

@ -646,7 +646,7 @@ export function buildInputBotApp(app: ApiBotApp) {
export function buildInputReplyTo(replyInfo: ApiInputReplyInfo) {
if (replyInfo.type === 'story') {
return new GramJs.InputReplyToStory({
userId: buildInputPeerFromLocalDb(replyInfo.userId)!,
peer: buildInputPeerFromLocalDb(replyInfo.peerId)!,
storyId: replyInfo.storyId,
});
}

View File

@ -80,7 +80,8 @@ import { invokeRequest, uploadFile } from './client';
type FullChatData = {
fullInfo: ApiChatFullInfo;
users?: ApiUser[];
users: ApiUser[];
chats: ApiChat[];
userStatusesById: { [userId: string]: ApiUserStatus };
groupCall?: Partial<ApiGroupCall>;
membersCount?: number;
@ -519,6 +520,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
const inviteLink = exportedInvite instanceof GramJs.ChatInviteExported ? exportedInvite.link : undefined;
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
const chats = result.chats.map((chat) => buildApiChatFromPreview(chat)).filter(Boolean);
return {
fullInfo: {
@ -536,6 +538,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
isTranslationDisabled: translationsDisabled,
},
users,
chats,
userStatusesById,
groupCall: call ? {
chatId,
@ -590,6 +593,9 @@ async function getFullChannelInfo(
translationsDisabled,
storiesPinnedAvailable,
viewForumAsMessages,
emojiset,
boostsApplied,
boostsUnrestrict,
} = result.fullChat;
if (chatPhoto instanceof GramJs.Photo) {
@ -608,6 +614,7 @@ async function getFullChannelInfo(
canViewParticipants && await fetchMembers(id, accessHash, 'admin')
) || {};
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
const chats = result.chats.map((chat) => buildApiChatFromPreview(chat)).filter(Boolean);
if (result?.chats?.length > 1) {
updateLocalDb(result);
@ -662,11 +669,15 @@ async function getFullChannelInfo(
recentRequesterIds: recentRequesters?.map((userId) => buildApiPeerId(userId, 'user')),
statisticsDcId: statsDc,
stickerSet: stickerset ? buildStickerSet(stickerset) : undefined,
emojiSet: emojiset ? buildStickerSet(emojiset) : undefined,
areParticipantsHidden: participantsHidden,
isTranslationDisabled: translationsDisabled,
hasPinnedStories: Boolean(storiesPinnedAvailable),
boostsApplied,
boostsToUnrestrict: boostsUnrestrict,
},
users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])],
chats,
userStatusesById: statusesById,
groupCall: call ? {
chatId: id,

View File

@ -21,6 +21,7 @@ import {
buildApiStealthMode,
buildApiStory,
buildApiStoryView,
buildApiStoryViews,
} from '../apiBuilders/stories';
import { buildApiUser } from '../apiBuilders/users';
import {
@ -300,6 +301,33 @@ export async function fetchStoryViewList({
};
}
export async function fetchStoriesViews({
peer,
storyIds,
}: {
peer: ApiPeer;
storyIds: number[];
}) {
const result = await invokeRequest(new GramJs.stories.GetStoriesViews({
peer: buildInputPeer(peer.id, peer.accessHash),
id: storyIds,
}));
if (!result?.views[0]) {
return undefined;
}
addEntitiesToLocalDb(result.users);
const views = buildApiStoryViews(result.views[0]);
const users = result.users.map(buildApiUser).filter(Boolean);
return {
views,
users,
};
}
export async function fetchStoryLink({ peer, storyId }: { peer: ApiPeer ; storyId: number }) {
const result = await invokeRequest(new GramJs.stories.ExportStoryLink({
peer: buildInputPeer(peer.id, peer.accessHash),

View File

@ -52,6 +52,7 @@ export interface ApiChat {
listedTopicIds?: number[];
topicsCount?: number;
orderedPinnedTopicIds?: number[];
boostLevel?: number;
// Calls
isCallActive?: boolean;
@ -128,10 +129,14 @@ export interface ApiChatFullInfo {
requestsPending?: number;
statisticsDcId?: number;
stickerSet?: ApiStickerSet;
emojiSet?: ApiStickerSet;
profilePhoto?: ApiPhoto;
areParticipantsHidden?: boolean;
isTranslationDisabled?: true;
hasPinnedStories?: boolean;
boostsApplied?: number;
boostsToUnrestrict?: number;
}
export interface ApiChatMember {

View File

@ -298,6 +298,7 @@ export interface ApiAction {
| 'topicCreate'
| 'suggestProfilePhoto'
| 'joinedChannel'
| 'chatBoost'
| 'other';
photo?: ApiPhoto;
amount?: number;
@ -356,7 +357,7 @@ export interface ApiMessageReplyInfo {
export interface ApiStoryReplyInfo {
type: 'story';
userId: string;
peerId: string;
storyId: number;
}
@ -370,7 +371,7 @@ export interface ApiInputMessageReplyInfo {
export interface ApiInputStoryReplyInfo {
type: 'story';
userId: string;
peerId: string;
storyId: number;
}
@ -539,6 +540,7 @@ export interface ApiMessage {
hasComments?: boolean;
readDate?: number;
savedPeerId?: string;
senderBoosts?: number;
}
export interface ApiReactions {

View File

@ -197,6 +197,7 @@ export interface ApiAppConfig {
storyExpirePeriod: number;
storyViewersExpirePeriod: number;
storyChangelogUserId: string;
groupTranscribeLevelMin?: number;
}
export interface ApiConfig {

View File

@ -23,9 +23,11 @@ export interface ApiStory {
sentReaction?: ApiReaction;
mediaAreas?: ApiMediaArea[];
forwardInfo?: ApiStoryForwardInfo;
fromId?: string;
}
export interface ApiStoryViews {
hasViewers?: true;
viewsCount?: number;
forwardsCount?: number;
reactionsCount?: number;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="m20.9 10.884.862-5.46c.635-4.018-4.62-6.107-6.918-2.75L6.311 15.146c-1.732 2.533.081 5.97 3.15 5.97H11.1l-.862 5.46c-.635 4.019 4.62 6.108 6.918 2.75l8.533-12.47c1.732-2.533-.081-5.97-3.15-5.97zm-3.807-6.67c.657-.96 2.158-.363 1.977.786l-1.16 7.35a1.09 1.09 0 0 0 1.076 1.26h3.553c.877 0 1.395.982.9 1.705l-8.532 12.471c-.657.96-2.158.362-1.977-.786l.08-.511 1.08-6.838a1.09 1.09 0 0 0-1.076-1.26H9.46a1.09 1.09 0 0 1-.9-1.706z" style="stroke-width:1.36275"/></svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M19.322 13.194c-.69 0-1.216-.616-1.109-1.298l1.5-9.498c.187-1.182-1.36-1.797-2.035-.809L7.1 17.05c-.51.746.023 1.757.926 1.757h4.652c.69 0 1.216.616 1.109 1.298l-1.5 9.498c-.187 1.182 1.36 1.797 2.035.809L24.9 14.95a1.122 1.122 0 0 0-.926-1.757Z" style="stroke-width:1.68365"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M19.341 13.175a1.129 1.129 0 0 1-1.115-1.303l1.512-9.56c.182-1.192-1.374-1.81-2.052-.817L7.038 17.06a1.129 1.129 0 0 0 .934 1.765h4.682c.696 0 1.225.62 1.116 1.306l-1.507 9.56c-.19 1.19 1.366 1.808 2.047.814l10.65-15.562a1.129 1.129 0 0 0-.93-1.768z" style="stroke-width:2.60715"/></svg>

Before

Width:  |  Height:  |  Size: 355 B

After

Width:  |  Height:  |  Size: 359 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="m22.105 16.416-3.054 4.283-1.477 8.992c-.195 1.189 1.425 1.807 2.134.814l11.086-15.562a1.175 1.129 0 0 0-.972-1.768h-4.878a1.175 1.129 0 0 1-1.162-1.303l1.57-9.56c.197-1.192-1.423-1.81-2.132-.817l-6.463 9.073h2.134c3.13 0 4.978 3.366 3.21 5.848zm-3.065-3.233c.855.1 1.33 1.05.825 1.76L8.78 30.505c-.706.993-2.33.375-2.131-.814l1.569-9.56a1.175 1.129 0 0 0-1.162-1.304H2.18a1.175 1.129 0 0 1-.972-1.77L12.294 1.495c.709-.993 2.33-.375 2.134.816l-1.572 9.558c-.111.686.44 1.306 1.162 1.306h4.875z" style="stroke-width:2.66026"/></svg>

After

Width:  |  Height:  |  Size: 604 B

View File

@ -207,6 +207,8 @@ const Avatar: FC<OwnProps> = ({
content = getFirstLetters(text, 2);
}
const isRoundedRect = isForum && !((withStory || withStorySolid) && peer?.hasStories);
const fullClassName = buildClassName(
`Avatar size-${size}`,
className,
@ -216,7 +218,7 @@ const Avatar: FC<OwnProps> = ({
isAnonymousForwards && 'anonymous-forwards',
isDeleted && 'deleted-account',
isReplies && 'replies-bot-account',
isForum && 'forum',
isRoundedRect && 'forum',
((withStory && peer?.hasStories) || forPremiumPromo) && 'with-story-circle',
withStorySolid && peer?.hasStories && 'with-story-solid',
withStorySolid && (peer?.hasUnreadStories || forceUnreadStorySolid) && 'has-unread-story',

View File

@ -2,9 +2,9 @@ import type { FC } from '../../lib/teact/teact';
import React, {
memo, useMemo, useRef, useState,
} from '../../lib/teact/teact';
import { getActions } from '../../global';
import { getActions, getGlobal } from '../../global';
import type { ApiChat, ApiTopic } from '../../api/types';
import type { ApiTopic } from '../../api/types';
import type { ThreadId } from '../../types';
import { CHAT_HEIGHT_PX } from '../../config';
@ -35,7 +35,6 @@ import './ChatOrUserPicker.scss';
export type OwnProps = {
currentUserId?: string;
chatOrUserIds: string[];
chatsById?: Record<string, ApiChat>;
isOpen: boolean;
searchPlaceholder: string;
search: string;
@ -55,7 +54,6 @@ const ChatOrUserPicker: FC<OwnProps> = ({
isOpen,
currentUserId,
chatOrUserIds,
chatsById,
search,
searchPlaceholder,
className,
@ -89,7 +87,11 @@ const ChatOrUserPicker: FC<OwnProps> = ({
useInputFocusOnOpen(topicSearchRef, isOpen && activeKey === TOPIC_LIST_SLIDE);
const [topicIds, topics] = useMemo(() => {
const topicsResult = forumId ? chatsById?.[forumId].topics : undefined;
const global = getGlobal();
const chatsById = global.chats.byId;
const chatFullInfoById = global.chats.fullInfoById;
const topicsResult = forumId ? chatsById[forumId].topics : undefined;
if (!topicsResult) {
return [undefined, undefined];
}
@ -99,7 +101,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
const result = topicsResult
? Object.values(topicsResult).reduce((acc, topic) => {
if (
getCanPostInChat(chatsById![forumId!], topic.id)
getCanPostInChat(chatsById[forumId!], topic.id, undefined, chatFullInfoById[forumId!])
&& (!searchTitle || topic.title.toLowerCase().includes(searchTitle))
) {
acc[topic.id] = topic;
@ -110,7 +112,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
: topicsResult;
return [Object.keys(result).map(Number), result];
}, [chatsById, forumId, topicSearch]);
}, [forumId, topicSearch]);
const handleHeaderBackClick = useLastCallback(() => {
setForumId(undefined);
@ -127,8 +129,10 @@ const ChatOrUserPicker: FC<OwnProps> = ({
const handleKeyDown = useKeyboardListNavigation(containerRef, isOpen, (index) => {
if (viewportIds && viewportIds.length > 0) {
const chatsById = getGlobal().chats.byId;
const chatId = viewportIds[index === -1 ? 0 : index];
const chat = chatsById?.[chatId];
const chat = chatsById[chatId];
if (chat?.isForum) {
if (!chat.topics) loadTopics({ chatId });
setForumId(chatId);
@ -145,6 +149,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
}, '.ListItem-button', true);
const handleClick = useLastCallback((e: React.MouseEvent, chatId: string) => {
const chatsById = getGlobal().chats.byId;
const chat = chatsById?.[chatId];
if (chat?.isForum) {
if (!chat.topics) loadTopics({ chatId });

View File

@ -14,7 +14,6 @@ import type {
ApiBotMenuButton,
ApiChat,
ApiChatFullInfo,
ApiChatMember,
ApiFormattedText,
ApiMessage,
ApiMessageEntity,
@ -191,6 +190,7 @@ type StateProps =
isOnActiveTab: boolean;
editingMessage?: ApiMessage;
chat?: ApiChat;
chatFullInfo?: ApiChatFullInfo;
draft?: ApiDraft;
replyToTopic?: ApiTopic;
currentMessageList?: MessageList;
@ -210,7 +210,6 @@ type StateProps =
canScheduleUntilOnline?: boolean;
stickersForEmoji?: ApiSticker[];
customEmojiForEmoji?: ApiSticker[];
groupChatMembers?: ApiChatMember[];
currentUserId?: string;
currentUser?: ApiUser;
recentEmojis: string[];
@ -224,7 +223,6 @@ type StateProps =
inlineBots?: Record<string, false | InlineBotSettings>;
botCommands?: ApiBotCommand[] | false;
botMenuButton?: ApiBotMenuButton;
chatBotCommands?: ApiBotCommand[];
sendAsUser?: ApiUser;
sendAsChat?: ApiChat;
sendAsId?: string;
@ -293,6 +291,7 @@ const Composer: FC<OwnProps & StateProps> = ({
messageListType,
draft,
chat,
chatFullInfo,
replyToTopic,
isForCurrentMessageList,
isCurrentUserPremium,
@ -312,7 +311,6 @@ const Composer: FC<OwnProps & StateProps> = ({
withScheduledButton,
stickersForEmoji,
customEmojiForEmoji,
groupChatMembers,
topInlineBotIds,
currentUserId,
currentUser,
@ -326,7 +324,6 @@ const Composer: FC<OwnProps & StateProps> = ({
inlineBots,
isInlineBotLoading,
botCommands,
chatBotCommands,
sendAsUser,
sendAsChat,
sendAsId,
@ -405,6 +402,9 @@ const Composer: FC<OwnProps & StateProps> = ({
const canMediaBeReplaced = editingMessage && hasReplaceableMedia(editingMessage);
const { emojiSet, members: groupChatMembers, botCommands: chatBotCommands } = chatFullInfo || {};
const chatEmojiSetId = emojiSet?.id;
const isSentStoryReactionHeart = sentStoryReaction && 'emoticon' in sentStoryReaction
? sentStoryReaction.emoticon === HEART_REACTION.emoticon : false;
@ -457,8 +457,8 @@ const Composer: FC<OwnProps & StateProps> = ({
canSendStickers, canSendGifs, canAttachMedia, canAttachPolls, canAttachEmbedLinks,
canSendVoices, canSendPlainText, canSendAudios, canSendVideos, canSendPhotos, canSendDocuments,
} = useMemo(
() => getAllowedAttachmentOptions(chat, isChatWithBot, isInStoryViewer),
[chat, isChatWithBot, isInStoryViewer],
() => getAllowedAttachmentOptions(chat, chatFullInfo, isChatWithBot, isInStoryViewer),
[chat, chatFullInfo, isChatWithBot, isInStoryViewer],
);
const isComposerBlocked = !canSendPlainText && !editingMessage;
@ -1088,7 +1088,8 @@ const Composer: FC<OwnProps & StateProps> = ({
}, [handleFileSelect, requestedDraftFiles, resetOpenChatWithDraft]);
const handleCustomEmojiSelect = useLastCallback((emoji: ApiSticker, inInputId?: string) => {
if (!emoji.isFree && !isCurrentUserPremium && !isChatWithSelf) {
const emojiSetId = 'id' in emoji.stickerSetInfo && emoji.stickerSetInfo.id;
if (!emoji.isFree && !isCurrentUserPremium && !isChatWithSelf && emojiSetId !== chatEmojiSetId) {
showCustomEmojiPremiumNotification();
return;
}
@ -2027,7 +2028,7 @@ export default memo(withGlobal<OwnProps>(
pollModal: tabState.pollModal,
stickersForEmoji: global.stickers.forEmoji.stickers,
customEmojiForEmoji: global.customEmojis.forEmoji.stickers,
groupChatMembers: chatFullInfo?.members,
chatFullInfo,
topInlineBotIds: global.topInlineBots?.userIds,
currentUserId,
currentUser,
@ -2040,7 +2041,6 @@ export default memo(withGlobal<OwnProps>(
emojiKeywords: emojiKeywords?.keywords,
inlineBots: tabState.inlineBots.byUsername,
isInlineBotLoading: tabState.inlineBots.isLoading,
chatBotCommands: chatFullInfo?.botCommands,
botCommands: chatBotFullInfo ? (chatBotFullInfo.botInfo?.commands || false) : undefined,
botMenuButton: chatBotFullInfo?.botInfo?.menuButton,
sendAsUser,

View File

@ -10,10 +10,8 @@ import type {
import type { StickerSetOrReactionsSetOrRecent } from '../../types';
import {
CHAT_STICKER_SET_ID,
FAVORITE_SYMBOL_SET_ID,
POPULAR_SYMBOL_SET_ID,
PREMIUM_STICKER_SET_ID,
RECENT_SYMBOL_SET_ID,
SLIDE_TRANSITION_DURATION,
STICKER_PICKER_MAX_SHARED_COVERS,
@ -23,6 +21,7 @@ import {
import { isSameReaction } from '../../global/helpers';
import {
selectCanPlayAnimatedEmojis,
selectChatFullInfo,
selectIsAlwaysHighPriorityEmoji,
selectIsChatWithSelf,
selectIsCurrentUserPremium,
@ -44,6 +43,7 @@ import { useStickerPickerObservers } from './hooks/useStickerPickerObservers';
import StickerSetCover from '../middle/composer/StickerSetCover';
import Button from '../ui/Button';
import Loading from '../ui/Loading';
import Icon from './Icon';
import StickerButton from './StickerButton';
import StickerSet from './StickerSet';
@ -73,6 +73,7 @@ type StateProps = {
customEmojisById?: Record<string, ApiSticker>;
recentCustomEmojiIds?: string[];
recentStatusEmojis?: ApiSticker[];
chatEmojiSetId?: string;
topReactions?: ApiReaction[];
recentReactions?: ApiReaction[];
defaultTagReactions?: ApiReaction[];
@ -97,8 +98,6 @@ const STICKER_SET_IDS_WITH_COVER = new Set([
RECENT_SYMBOL_SET_ID,
FAVORITE_SYMBOL_SET_ID,
POPULAR_SYMBOL_SET_ID,
CHAT_STICKER_SET_ID,
PREMIUM_STICKER_SET_ID,
]);
const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
@ -112,6 +111,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
selectedReactionIds,
recentStatusEmojis,
stickerSetsById,
chatEmojiSetId,
topReactions,
recentReactions,
availableReactions,
@ -250,7 +250,12 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
});
}
const setIdsToDisplay = unique((addedCustomEmojiIds || []).concat(customEmojiFeaturedIds || []));
const userSetIds = [...(addedCustomEmojiIds || [])];
if (chatEmojiSetId) {
userSetIds.unshift(chatEmojiSetId);
}
const setIdsToDisplay = unique(userSetIds.concat(customEmojiFeaturedIds || []));
const setsToDisplay = Object.values(pickTruthy(stickerSetsById, setIdsToDisplay));
@ -261,7 +266,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
}, [
addedCustomEmojiIds, isReactionPicker, isStatusPicker, withDefaultTopicIcons, recentCustomEmojis,
customEmojiFeaturedIds, stickerSetsById, topReactions, availableReactions, lang, recentReactions,
defaultStatusIconsId, defaultTopicIconsId, isSavedMessages, defaultTagReactions,
defaultStatusIconsId, defaultTopicIconsId, isSavedMessages, defaultTagReactions, chatEmojiSetId,
]);
const noPopulatedSets = useMemo(() => (
@ -327,7 +332,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
onClick={() => selectStickerSet(isRecent ? 0 : index)}
>
{isRecent ? (
<i className="icon icon-recent" />
<Icon name="recent" />
) : (
<StickerSetCover
stickerSet={stickerSet as ApiStickerSet}
@ -407,6 +412,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
{allSets.map((stickerSet, i) => {
const shouldHideHeader = stickerSet.id === TOP_SYMBOL_SET_ID
|| (stickerSet.id === RECENT_SYMBOL_SET_ID && (withDefaultTopicIcons || isStatusPicker));
const isChatEmojiSet = stickerSet.id === chatEmojiSetId;
return (
<StickerSet
@ -425,6 +431,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
shouldHideHeader={shouldHideHeader}
withDefaultTopicIcon={withDefaultTopicIcons && stickerSet.id === RECENT_SYMBOL_SET_ID}
withDefaultStatusIcon={isStatusPicker && stickerSet.id === RECENT_SYMBOL_SET_ID}
isChatEmojiSet={isChatEmojiSet}
isCurrentUserPremium={isCurrentUserPremium}
selectedReactionIds={selectedReactionIds}
availableReactions={availableReactions}
@ -466,6 +473,7 @@ export default memo(withGlobal<OwnProps>(
} = global;
const isSavedMessages = Boolean(chatId && selectIsChatWithSelf(global, chatId));
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
return {
customEmojisById: !isStatusPicker ? customEmojisById : undefined,
@ -481,6 +489,7 @@ export default memo(withGlobal<OwnProps>(
defaultStatusIconsId: global.defaultStatusIconsId,
topReactions: isReactionPicker ? topReactions : undefined,
recentReactions: isReactionPicker ? recentReactions : undefined,
chatEmojiSetId: chatFullInfo?.emojiSet?.id,
availableReactions: isReactionPicker ? availableReactions : undefined,
defaultTagReactions: isReactionPicker ? defaultTags : undefined,
};

View File

@ -2,7 +2,7 @@ import type { FC } from '../../lib/teact/teact';
import React, { memo, useMemo, useState } from '../../lib/teact/teact';
import { getGlobal, withGlobal } from '../../global';
import type { ApiChat, ApiChatType } from '../../api/types';
import type { ApiChatType } from '../../api/types';
import type { ThreadId } from '../../types';
import { MAIN_THREAD_ID } from '../../api/types';
@ -35,7 +35,6 @@ export type OwnProps = {
type StateProps = {
currentUserId?: string;
chatsById: Record<string, ApiChat>;
activeListIds?: string[];
archivedListIds?: string[];
pinnedIds?: string[];
@ -45,7 +44,6 @@ type StateProps = {
const RecipientPicker: FC<OwnProps & StateProps> = ({
isOpen,
currentUserId,
chatsById,
activeListIds,
archivedListIds,
pinnedIds,
@ -71,6 +69,8 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
// No need for expensive global updates on users, so we avoid them
const global = getGlobal();
const usersById = global.users.byId;
const chatsById = global.chats.byId;
const chatFullInfoById = global.chats.fullInfoById;
const chatIds = [
...(activeListIds || []),
@ -80,7 +80,7 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
const user = usersById[id];
if (user && isDeletedUser(user)) return false;
return chat && getCanPostInChat(chat, MAIN_THREAD_ID);
return chat && getCanPostInChat(chat, MAIN_THREAD_ID, undefined, chatFullInfoById[id]);
});
const sorted = sortChatIds(unique([
@ -89,7 +89,7 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
]), undefined, priorityIds);
return filterChatIdsByType(global, sorted, filter);
}, [pinnedIds, currentUserId, activeListIds, search, archivedListIds, lang, chatsById, contactIds, filter, isOpen]);
}, [pinnedIds, currentUserId, activeListIds, search, archivedListIds, lang, contactIds, filter, isOpen]);
const renderingIds = useCurrentOrPrev(ids, true)!;
@ -98,7 +98,6 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
isOpen={isOpen}
className={className}
chatOrUserIds={renderingIds}
chatsById={chatsById}
searchPlaceholder={searchPlaceholder}
search={search}
onSearchChange={setSearch}
@ -114,7 +113,6 @@ export default memo(withGlobal<OwnProps>(
(global): StateProps => {
const {
chats: {
byId: chatsById,
listIds,
orderedPinnedIds,
},
@ -122,7 +120,6 @@ export default memo(withGlobal<OwnProps>(
} = global;
return {
chatsById,
activeListIds: listIds.active,
archivedListIds: listIds.archived,
pinnedIds: orderedPinnedIds.active,

View File

@ -32,6 +32,7 @@
vertical-align: bottom;
align-self: center;
justify-self: center;
font-weight: 500;
}
.sticker-locked {

View File

@ -38,6 +38,7 @@ type OwnProps<T> = {
canViewSet?: boolean;
isSelected?: boolean;
isCurrentUserPremium?: boolean;
shouldIgnorePremium?: boolean;
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>;
withTranslucentThumb?: boolean;
forcePlayback?: boolean;
@ -76,6 +77,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
observeIntersectionForShowing,
isSelected,
isCurrentUserPremium,
shouldIgnorePremium,
noShowPremium,
sharedCanvasRef,
withTranslucentThumb,
@ -101,7 +103,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
const {
id, isCustomEmoji, hasEffect: isPremium, stickerSetInfo,
} = sticker;
const isLocked = !isCurrentUserPremium && isPremium;
const isLocked = !isCurrentUserPremium && isPremium && !shouldIgnorePremium;
const isIntersecting = useIsIntersecting(ref, observeIntersection);
const shouldLoad = isIntersecting;

View File

@ -52,6 +52,8 @@ type OwnProps = {
selectedReactionIds?: string[];
withDefaultTopicIcon?: boolean;
withDefaultStatusIcon?: boolean;
isChatEmojiSet?: boolean;
isChatStickerSet?: boolean;
isTranslucent?: boolean;
noContextMenus?: boolean;
forcePlayback?: boolean;
@ -91,6 +93,8 @@ const StickerSet: FC<OwnProps> = ({
withDefaultTopicIcon,
selectedReactionIds,
withDefaultStatusIcon,
isChatEmojiSet,
isChatStickerSet,
isTranslucent,
noContextMenus,
forcePlayback,
@ -224,10 +228,11 @@ const StickerSet: FC<OwnProps> = ({
}
}, [shouldRender, loadStickers, stickerSet]);
const isLocked = !isSavedMessages && !isCurrentUserPremium && isPremiumSet;
const isLocked = !isSavedMessages && !isCurrentUserPremium && isPremiumSet && !isChatEmojiSet;
const isInstalled = stickerSet.installedDate && !stickerSet.isArchived;
const canCut = !isInstalled && stickerSet.id !== RECENT_SYMBOL_SET_ID && stickerSet.id !== POPULAR_SYMBOL_SET_ID;
const canCut = !isInstalled && stickerSet.id !== RECENT_SYMBOL_SET_ID && stickerSet.id !== POPULAR_SYMBOL_SET_ID
&& !isChatEmojiSet && !isChatStickerSet;
const [isCut, , expand] = useFlag(canCut);
const itemsBeforeCutout = itemsPerRow * 3 - 1;
const totalItemsCount = withDefaultTopicIcon ? stickerSet.count + 1 : stickerSet.count;
@ -240,7 +245,7 @@ const StickerSet: FC<OwnProps> = ({
const favoriteStickerIdsSet = useMemo(() => (
favoriteStickers ? new Set(favoriteStickers.map(({ id }) => id)) : undefined
), [favoriteStickers]);
const withAddSetButton = !shouldHideHeader && !isRecent && isEmoji && !isPopular
const withAddSetButton = !shouldHideHeader && !isRecent && isEmoji && !isPopular && !isChatEmojiSet
&& (!isInstalled || (!isCurrentUserPremium && !isSavedMessages));
const addSetButtonText = useMemo(() => {
if (isLocked) {
@ -265,6 +270,9 @@ const StickerSet: FC<OwnProps> = ({
<p className={buildClassName('symbol-set-name', withAddSetButton && 'symbol-set-name-external')}>
{isLocked && <i className="symbol-set-locked-icon icon icon-lock-badge" />}
{stickerSet.title}
{(isChatEmojiSet || isChatStickerSet) && (
<span className="symbol-set-chat">{lang(isChatEmojiSet ? 'GroupEmoji' : 'GroupStickers')}</span>
)}
{withAddSetButton && Boolean(stickerSet.stickers) && (
<span className="symbol-set-amount">
{lang(isEmoji ? 'EmojiCount' : 'Stickers', stickerSet.stickers.length, 'i')}
@ -360,6 +368,7 @@ const StickerSet: FC<OwnProps> = ({
canViewSet
noContextMenu={noContextMenus}
isCurrentUserPremium={isCurrentUserPremium}
shouldIgnorePremium={isChatEmojiSet}
sharedCanvasRef={canvasRef}
withTranslucentThumb={isTranslucent}
onClick={onStickerSelect}

View File

@ -12,6 +12,7 @@ import { getAllowedAttachmentOptions, getCanPostInChat } from '../../global/help
import {
selectCanScheduleUntilOnline,
selectChat,
selectChatFullInfo,
selectCurrentMessageList,
selectIsChatWithSelf,
selectIsCurrentUserPremium,
@ -258,11 +259,13 @@ export default memo(withGlobal<OwnProps>(
const currentMessageList = selectCurrentMessageList(global);
const { chatId, threadId } = currentMessageList || {};
const chat = chatId && selectChat(global, chatId);
const sendOptions = chat ? getAllowedAttachmentOptions(chat) : undefined;
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
const sendOptions = chat ? getAllowedAttachmentOptions(chat, chatFullInfo) : undefined;
const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined;
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
const canSendStickers = Boolean(
chat && threadId && getCanPostInChat(chat, threadId, isMessageThread) && sendOptions?.canSendStickers,
chat && threadId && getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo)
&& sendOptions?.canSendStickers,
);
const isSavedMessages = Boolean(chatId) && selectIsChatWithSelf(global, chatId);

View File

@ -14,11 +14,14 @@ export function getBoostProgressInfo(boostInfo: ApiBoostsStatus, freezeOnLevelUp
: (boosts - currentLevelBoosts) / (nextLevelBoosts - currentLevelBoosts);
const remainingBoosts = nextLevelBoosts ? nextLevelBoosts - boosts : 0;
const isMaxLevel = nextLevelBoosts === undefined;
return {
currentLevel,
hasNextLevel,
boosts,
levelProgress,
remainingBoosts,
isMaxLevel,
};
}

View File

@ -7,7 +7,7 @@ import type { TabState } from '../../global/types';
import { MAIN_THREAD_ID } from '../../api/types';
import { getCanPostInChat } from '../../global/helpers';
import { selectChat } from '../../global/selectors';
import { selectChat, selectChatFullInfo } from '../../global/selectors';
import useInterval from '../../hooks/useInterval';
import useLang from '../../hooks/useLang';
@ -93,7 +93,8 @@ export default memo(withGlobal<OwnProps>(
(global, { openedGame }): StateProps => {
const { chatId } = openedGame || {};
const chat = chatId && selectChat(global, chatId);
const canPost = Boolean(chat) && getCanPostInChat(chat, MAIN_THREAD_ID);
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
const canPost = Boolean(chat) && getCanPostInChat(chat, MAIN_THREAD_ID, undefined, chatFullInfo);
return {
canPost,

View File

@ -268,7 +268,6 @@ const Main: FC<OwnProps & StateProps> = ({
loadDefaultTagReactions,
loadFeaturedEmojiStickers,
setIsElectronUpdateAvailable,
loadPremiumSetStickers,
loadAuthorizations,
loadPeerColors,
loadSavedReactionTags,
@ -357,7 +356,6 @@ const Main: FC<OwnProps & StateProps> = ({
if (isMasterTab && isCurrentUserPremium) {
loadDefaultStatusIcons();
loadRecentEmojiStatuses();
loadPremiumSetStickers();
}
}, [isCurrentUserPremium, isMasterTab]);

View File

@ -67,6 +67,7 @@ interface StateProps {
canMute?: boolean;
canViewStatistics?: boolean;
canViewBoosts?: boolean;
canShowBoostModal?: boolean;
canLeave?: boolean;
canEnterVoiceChat?: boolean;
canCreateVoiceChat?: boolean;
@ -100,6 +101,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
canMute,
canViewStatistics,
canViewBoosts,
canShowBoostModal,
canLeave,
canEnterVoiceChat,
canCreateVoiceChat,
@ -433,6 +435,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
canMute={canMute}
canViewStatistics={canViewStatistics}
canViewBoosts={canViewBoosts}
canShowBoostModal={canShowBoostModal}
canLeave={canLeave}
canEnterVoiceChat={canEnterVoiceChat}
canCreateVoiceChat={canCreateVoiceChat}
@ -456,6 +459,7 @@ export default memo(withGlobal<OwnProps>(
}): StateProps => {
const chat = selectChat(global, chatId);
const isChannel = Boolean(chat && isChatChannel(chat));
const isSuperGroup = Boolean(chat && isChatSuperGroup(chat));
const language = selectLanguageCode(global);
const translationLanguage = selectTranslationLanguage(global);
const isPrivate = isUserId(chatId);
@ -486,7 +490,7 @@ export default memo(withGlobal<OwnProps>(
const canStartBot = !canRestartBot && Boolean(selectIsChatBotNotStarted(global, chatId));
const canUnblock = isUserBlocked && !bot;
const canSubscribe = Boolean(
(isMainThread || chat.isForum) && (isChannel || isChatSuperGroup(chat)) && chat.isNotJoined,
(isMainThread || chat.isForum) && (isChannel || isSuperGroup) && chat.isNotJoined,
);
const canSearch = isMainThread || isDiscussionThread;
const canCall = ARE_CALLS_SUPPORTED && isUserId(chat.id) && !isChatWithSelf && !bot && !chat.isSupport
@ -498,6 +502,7 @@ export default memo(withGlobal<OwnProps>(
&& (chat.adminRights?.manageCall || (chat.isCreator && isChatBasicGroup(chat)));
const canViewStatistics = isMainThread && chatFullInfo?.canViewStatistics;
const canViewBoosts = isMainThread && isChannel && (canViewStatistics || getHasAdminRight(chat, 'postStories'));
const canShowBoostModal = !canViewBoosts && (isSuperGroup || isChannel);
const pendingJoinRequests = isMainThread ? chatFullInfo?.requestsPending : undefined;
const shouldJoinToSend = Boolean(chat?.isNotJoined && chat.isJoinToSend);
const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest);
@ -518,6 +523,7 @@ export default memo(withGlobal<OwnProps>(
canMute,
canViewStatistics,
canViewBoosts,
canShowBoostModal,
canLeave,
canEnterVoiceChat,
canCreateVoiceChat,

View File

@ -84,6 +84,7 @@ export type OwnProps = {
canMute?: boolean;
canViewStatistics?: boolean;
canViewBoosts?: boolean;
canShowBoostModal?: boolean;
withForumActions?: boolean;
canLeave?: boolean;
canEnterVoiceChat?: boolean;
@ -166,6 +167,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
isBot,
isChatWithSelf,
savedDialog,
canShowBoostModal,
onJoinRequestsClick,
onSubscribeChannel,
onSearchClick,
@ -195,6 +197,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
blockUser,
unblockUser,
setViewForumAsMessages,
openBoostModal,
} = getActions();
const { isMobile } = useAppLayout();
@ -346,8 +349,12 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleBoostClick = useLastCallback(() => {
openBoostStatistics({ chatId });
setShouldCloseFast(!isRightColumnShown);
if (canViewBoosts) {
openBoostStatistics({ chatId });
setShouldCloseFast(!isRightColumnShown);
} else {
openBoostModal({ chatId });
}
closeMenu();
});
@ -522,6 +529,14 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
{lang(isChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')}
</MenuItem>
)}
{canShowBoostModal && !canViewBoosts && (
<MenuItem
icon="boost-outline"
onClick={handleBoostClick}
>
{lang(isChannel ? 'BoostingBoostChannelMenu' : 'BoostingBoostGroupMenu')}
</MenuItem>
)}
{canAddContact && (
<MenuItem
icon="add-user"
@ -589,7 +604,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
)}
{canViewBoosts && (
<MenuItem
icon="boost"
icon="boost-outline"
onClick={handleBoostClick}
>
{lang('Boosts')}

View File

@ -763,10 +763,11 @@ export default memo(withGlobal<OwnProps>(
const bot = selectBot(global, chatId);
const pinnedIds = selectPinnedIds(global, chatId, threadId);
const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer;
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
const threadInfo = selectThreadInfo(global, chatId, threadId);
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
const canPost = chat && getCanPostInChat(chat, threadId, isMessageThread);
const canPost = chat && getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo);
const isBotNotStarted = selectIsChatBotNotStarted(global, chatId);
const isPinnedMessageList = messageListType === 'pinned';
const isMainThread = messageListType === 'thread' && threadId === MAIN_THREAD_ID;
@ -781,7 +782,7 @@ export default memo(withGlobal<OwnProps>(
const canStartBot = !canRestartBot && isBotNotStarted;
const canUnblock = isUserBlocked && !bot;
const shouldLoadFullChat = Boolean(
chat && isChatGroup(chat) && !selectChatFullInfo(global, chat.id),
chat && isChatGroup(chat) && !chatFullInfo,
);
const draftReplyInfo = selectDraft(global, chatId, threadId)?.replyInfo;
const shouldBlockSendInForum = chat?.isForum

View File

@ -11,7 +11,6 @@ import type { StickerSetOrReactionsSetOrRecent, ThreadId } from '../../../types'
import {
CHAT_STICKER_SET_ID,
FAVORITE_SYMBOL_SET_ID,
PREMIUM_STICKER_SET_ID,
RECENT_SYMBOL_SET_ID,
SLIDE_TRANSITION_DURATION,
STICKER_PICKER_MAX_SHARED_COVERS,
@ -23,7 +22,7 @@ import {
} from '../../../global/selectors';
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
import buildClassName from '../../../util/buildClassName';
import { pickTruthy, uniqueByField } from '../../../util/iteratees';
import { pickTruthy } from '../../../util/iteratees';
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
import { REM } from '../../common/helpers/mediaDimensions';
@ -37,7 +36,6 @@ import { useStickerPickerObservers } from '../../common/hooks/useStickerPickerOb
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
import Avatar from '../../common/Avatar';
import PremiumIcon from '../../common/PremiumIcon';
import StickerButton from '../../common/StickerButton';
import StickerSet from '../../common/StickerSet';
import Button from '../../ui/Button';
@ -65,7 +63,6 @@ type StateProps = {
chat?: ApiChat;
recentStickers: ApiSticker[];
favoriteStickers: ApiSticker[];
premiumStickers: ApiSticker[];
stickerSetsById: Record<string, ApiStickerSet>;
chatStickerSetId?: string;
addedSetIds?: string[];
@ -86,7 +83,6 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
canSendStickers,
recentStickers,
favoriteStickers,
premiumStickers,
addedSetIds,
stickerSetsById,
chatStickerSetId,
@ -140,8 +136,6 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
const defaultSets = [];
const existingAddedSetIds = Object.values(pickTruthy(stickerSetsById, addedSetIds));
if (favoriteStickers.length) {
defaultSets.push({
id: FAVORITE_SYMBOL_SET_ID,
@ -162,46 +156,18 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
});
}
if (isCurrentUserPremium) {
const addedPremiumStickers = existingAddedSetIds
.map(({ stickers }) => stickers?.filter((sticker) => sticker.hasEffect))
.flat()
.filter(Boolean);
const totalPremiumStickers = uniqueByField([...addedPremiumStickers, ...premiumStickers], 'id');
if (totalPremiumStickers.length) {
defaultSets.push({
id: PREMIUM_STICKER_SET_ID,
accessHash: '0',
title: lang('PremiumStickers'),
stickers: totalPremiumStickers,
count: totalPremiumStickers.length,
});
}
}
const userSetIds = [...(addedSetIds || [])];
if (chatStickerSetId) {
const fullSet = stickerSetsById[chatStickerSetId];
if (fullSet) {
defaultSets.push({
id: CHAT_STICKER_SET_ID,
accessHash: fullSet.accessHash,
title: lang('GroupStickers'),
stickers: fullSet.stickers,
count: fullSet.stickers!.length,
});
}
userSetIds.unshift(chatStickerSetId);
}
const existingAddedSetIds = Object.values(pickTruthy(stickerSetsById, userSetIds));
return [
...defaultSets,
...existingAddedSetIds,
];
}, [
addedSetIds, stickerSetsById, favoriteStickers, recentStickers, isCurrentUserPremium, chatStickerSetId, lang,
premiumStickers,
]);
}, [addedSetIds, stickerSetsById, favoriteStickers, recentStickers, chatStickerSetId, lang]);
const noPopulatedSets = useMemo(() => (
areAddedLoaded
@ -266,7 +232,6 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
if (stickerSet.id === RECENT_SYMBOL_SET_ID
|| stickerSet.id === FAVORITE_SYMBOL_SET_ID
|| stickerSet.id === CHAT_STICKER_SET_ID
|| stickerSet.id === PREMIUM_STICKER_SET_ID
|| stickerSet.hasThumbnail
|| !firstSticker
) {
@ -281,9 +246,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => selectStickerSet(index)}
>
{stickerSet.id === PREMIUM_STICKER_SET_ID ? (
<PremiumIcon withGradient big />
) : stickerSet.id === RECENT_SYMBOL_SET_ID ? (
{stickerSet.id === RECENT_SYMBOL_SET_ID ? (
<i className="icon icon-recent" />
) : stickerSet.id === FAVORITE_SYMBOL_SET_ID ? (
<i className="icon icon-favorite" />
@ -374,6 +337,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
isSavedMessages={isSavedMessages}
isCurrentUserPremium={isCurrentUserPremium}
isTranslucent={isTranslucent}
isChatStickerSet={stickerSet.id === chatStickerSetId}
onStickerSelect={handleStickerSelect}
onStickerUnfave={handleStickerUnfave}
onStickerFave={handleStickerFave}
@ -393,7 +357,6 @@ export default memo(withGlobal<OwnProps>(
added,
recent,
favorite,
premiumSet,
} = global.stickers;
const isSavedMessages = selectIsChatWithSelf(global, chatId);
@ -404,7 +367,6 @@ export default memo(withGlobal<OwnProps>(
chat,
recentStickers: recent.stickers,
favoriteStickers: favorite.stickers,
premiumStickers: premiumSet.stickers,
stickerSetsById: setsById,
addedSetIds: added.setIds,
canAnimate: selectShouldLoopStickers(global),

View File

@ -222,6 +222,14 @@
}
}
&-chat {
background-color: var(--color-text-secondary);
color: var(--color-background);
border-radius: 0.5rem;
padding-inline: 0.25rem;
margin-inline-start: 0.5rem;
}
&-locked-icon {
margin-right: 0.25rem;
}

View File

@ -81,6 +81,7 @@ import {
selectIsMessageSelected,
selectMessageIdsByGroupId,
selectOutgoingStatus,
selectPeer,
selectPeerStory,
selectPerformanceSettingsValue,
selectReplySender,
@ -222,7 +223,7 @@ type StateProps = {
replyMessageChat?: ApiChat;
isReplyPrivate?: boolean;
replyStory?: ApiTypeStory;
storySender?: ApiUser;
storySender?: ApiPeer;
outgoingStatus?: ApiMessageOutgoingStatus;
uploadProgress?: number;
isInDocumentGroup: boolean;
@ -279,7 +280,9 @@ type StateProps = {
isConnected: boolean;
isLoadingComments?: boolean;
shouldWarnAboutSvg?: boolean;
senderBoosts?: number;
tags?: Record<ApiReactionKey, ApiSavedReactionTag>;
canTranscribeVoice?: boolean;
};
type MetaPosition =
@ -394,7 +397,9 @@ const Message: FC<OwnProps & StateProps> = ({
isConnected,
getIsMessageListReady,
shouldWarnAboutSvg,
senderBoosts,
tags,
canTranscribeVoice,
onPinnedIntersectionChange,
}) => {
const {
@ -655,7 +660,7 @@ const Message: FC<OwnProps & StateProps> = ({
} = getMessageContent(message);
const { replyToMsgId, replyToPeerId, isQuote } = messageReplyInfo || {};
const { userId: storyReplyUserId, storyId: storyReplyId } = storyReplyInfo || {};
const { peerId: storyReplyPeerId, storyId: storyReplyId } = storyReplyInfo || {};
const detectedLanguage = useTextLanguage(
text?.text,
@ -739,7 +744,7 @@ const Message: FC<OwnProps & StateProps> = ({
);
useEnsureStory(
storyReplyUserId || chatId,
storyReplyPeerId || chatId,
storyReplyId,
replyStory,
);
@ -1132,7 +1137,7 @@ const Message: FC<OwnProps & StateProps> = ({
isTranscriptionError={isTranscriptionError}
canDownload={!isProtected}
onHideTranscription={setTranscriptionHidden}
canTranscribe={isPremium && !hasTtl}
canTranscribe={canTranscribeVoice && !hasTtl}
/>
)}
{document && (
@ -1312,6 +1317,7 @@ const Message: FC<OwnProps & StateProps> = ({
</span>
</>
)}
<div className="title-spacer" />
{forwardInfo?.isLinkedChannelPost ? (
<span className="admin-title" dir="auto">{lang('DiscussChannel')}</span>
) : message.forwardInfo?.postAuthorTitle && isGroup && asForwarded ? (
@ -1325,6 +1331,12 @@ const Message: FC<OwnProps & StateProps> = ({
)}
</span>
) : undefined}
{Boolean(senderBoosts) && (
<span className="sender-boosts" aria-hidden>
<Icon name={senderBoosts > 1 ? 'boosts' : 'boost'} />
{senderBoosts > 1 ? senderBoosts : undefined}
</span>
)}
</div>
);
}
@ -1499,7 +1511,7 @@ export default memo(withGlobal<OwnProps>(
const isThreadTop = message.id === threadId;
const { replyToMsgId, replyToPeerId, replyFrom } = getMessageReplyInfo(message) || {};
const { userId: storyReplyUserId, storyId: storyReplyId } = getStoryReplyInfo(message) || {};
const { peerId: storyReplyPeerId, storyId: storyReplyId } = getStoryReplyInfo(message) || {};
const shouldHideReply = replyToMsgId && replyToMsgId === threadId;
const replyMessage = replyToMsgId ? selectChatMessage(global, replyToPeerId || chatId, replyToMsgId) : undefined;
@ -1512,10 +1524,10 @@ export default memo(withGlobal<OwnProps>(
const isReplyPrivate = !isRepliesChat && !isAnonymousForwards && replyMessageChat && !isChatPublic(replyMessageChat)
&& (replyMessageChat.isNotJoined || replyMessageChat.isRestricted);
const isReplyToTopicStart = replyMessage?.content.action?.type === 'topicCreate';
const replyStory = storyReplyId && storyReplyUserId
? selectPeerStory(global, storyReplyUserId, storyReplyId)
const replyStory = storyReplyId && storyReplyPeerId
? selectPeerStory(global, storyReplyPeerId, storyReplyId)
: undefined;
const storySender = storyReplyUserId ? selectUser(global, storyReplyUserId) : undefined;
const storySender = storyReplyPeerId ? selectPeer(global, storyReplyPeerId) : undefined;
const uploadProgress = selectUploadProgress(global, message);
const isFocused = messageListType === 'thread' && (
@ -1572,6 +1584,14 @@ export default memo(withGlobal<OwnProps>(
const hasActiveReactions = Boolean(reactionMessage && activeReactions[getMessageKey(reactionMessage)]?.length);
const isPremium = selectIsCurrentUserPremium(global);
const senderBoosts = sender && selectIsChatWithSelf(global, sender.id)
? (chatFullInfo?.boostsApplied ?? message.senderBoosts) : message.senderBoosts;
const chatLevel = chat?.boostLevel || 0;
const transcribeMinLevel = global.appConfig?.groupTranscribeLevelMin;
const canTranscribeVoice = isPremium || Boolean(transcribeMinLevel && chatLevel >= transcribeMinLevel);
return {
theme: selectTheme(global),
forceSenderName,
@ -1627,7 +1647,7 @@ export default memo(withGlobal<OwnProps>(
hasUnreadReaction,
isTranscribing: transcriptionId !== undefined && global.transcriptions[transcriptionId]?.isPending,
transcribedText: transcriptionId !== undefined ? global.transcriptions[transcriptionId]?.text : undefined,
isPremium: selectIsCurrentUserPremium(global),
isPremium,
senderAdminMember,
messageTopic,
hasTopicChip,
@ -1652,7 +1672,9 @@ export default memo(withGlobal<OwnProps>(
isResizingContainer,
focusedQuote,
}),
senderBoosts,
tags: global.savedReactionTags?.byKey,
canTranscribeVoice,
};
},
)(Message));

View File

@ -325,19 +325,32 @@
margin-left: 0.25rem;
}
.title-spacer {
flex-grow: 1;
}
.admin-title {
flex: 1;
margin-left: 1rem;
text-align: right;
font-weight: 400;
font-size: 0.75rem;
margin-top: -0.125rem;
color: rgba(var(--color-text-meta-rgb), 0.75);
user-select: none;
.Message.own & {
color: var(--accent-color);
}
}
.sender-boosts {
display: flex;
align-items: center;
font-size: 0.75rem;
margin-top: -0.125rem;
margin-inline-start: 0.125rem;
user-select: none;
}
}
.message-subheader {

View File

@ -27,6 +27,7 @@ export function getWebpageButtonText(type?: string) {
case 'telegram_story':
return 'lng_view_button_story';
case 'telegram_channel_boost':
case 'telegram_group_boost':
return 'lng_view_button_boost';
default:
return undefined;

View File

@ -27,6 +27,12 @@
.description {
padding: 0.5rem;
line-height: 1.25;
text-align: center;
text-wrap: balance;
}
.bold {
font-weight: 500;
}
.chip {

View File

@ -1,11 +1,12 @@
import React, { memo, useMemo } from '../../../lib/teact/teact';
import React, { memo, useEffect, useMemo } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { ApiChat, ApiMyBoost } from '../../../api/types';
import type { ApiChat, ApiChatFullInfo, ApiMyBoost } from '../../../api/types';
import type { TabState } from '../../../global/types';
import { getChatTitle } from '../../../global/helpers';
import { selectChat, selectIsCurrentUserPremium } from '../../../global/selectors';
import { getChatTitle, isChatAdmin, isChatChannel } from '../../../global/helpers';
import { selectChat, selectChatFullInfo, selectIsCurrentUserPremium } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { formatDateInFuture } from '../../../util/dateFormat';
import { getServerTime } from '../../../util/serverTime';
import { getBoostProgressInfo } from '../../common/helpers/boostInfo';
@ -50,14 +51,16 @@ export type OwnProps = {
type StateProps = {
chat?: ApiChat;
boostedChat?: ApiChat;
chatFullInfo?: ApiChatFullInfo;
prevBoostedChat?: ApiChat;
isCurrentUserPremium?: boolean;
};
const BoostModal = ({
info,
chat,
boostedChat,
chatFullInfo,
prevBoostedChat,
isCurrentUserPremium,
}: OwnProps & StateProps) => {
const {
@ -65,16 +68,25 @@ const BoostModal = ({
closeBoostModal,
requestConfetti,
openPremiumModal,
loadFullChat,
} = getActions();
const [isReplaceModalOpen, openReplaceModal, closeReplaceModal] = useFlag();
const [isWaitDialogOpen, openWaitDialog, closeWaitDialog] = useFlag();
const [isPremiumDialogOpen, openPremiumDialog, closePremiumDialog] = useFlag();
const isChannel = chat && isChatChannel(chat);
const isOpen = Boolean(info);
const lang = useLang();
useEffect(() => {
if (chat && !chatFullInfo) {
loadFullChat({ chatId: chat.id });
}
}, [chat, chatFullInfo]);
const chatTitle = useMemo(() => {
if (!chat) {
return undefined;
@ -84,12 +96,12 @@ const BoostModal = ({
}, [chat, lang]);
const boostedChatTitle = useMemo(() => {
if (!boostedChat) {
if (!prevBoostedChat) {
return undefined;
}
return getChatTitle(lang, boostedChat);
}, [boostedChat, lang]);
return getChatTitle(lang, prevBoostedChat);
}, [prevBoostedChat, lang]);
const {
isStatusLoaded,
@ -111,7 +123,7 @@ const BoostModal = ({
}
const {
level, currentLevelBoosts, hasMyBoost,
hasMyBoost,
} = info.boostStatus;
const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts, chat.id);
@ -123,36 +135,28 @@ const BoostModal = ({
hasNextLevel,
levelProgress,
remainingBoosts,
isMaxLevel,
} = getBoostProgressInfo(info.boostStatus, true);
const hasBoost = hasMyBoost;
const isJustUpgraded = boosts === currentLevelBoosts && hasBoost;
const left = lang('BoostsLevel', currentLevel);
const right = hasNextLevel ? lang('BoostsLevel', currentLevel + 1) : undefined;
const moreBoosts = lang('ChannelBoost.MoreBoosts', remainingBoosts);
const currentStoriesPerDay = lang('ChannelBoost.StoriesPerDay', level);
const nextLevelStoriesPerDay = lang('ChannelBoost.StoriesPerDay', level + 1);
const modalTitle = hasBoost ? lang('YouBoostedChannel2', chatTitle)
: level === 0 ? lang('lng_boost_channel_title_first') : lang('lng_boost_channel_title_more');
const modalTitle = isChannel ? lang('BoostChannel') : lang('BoostGroup');
const boostsLeftToUnrestrict = (chatFullInfo?.boostsToUnrestrict || 0) - (chatFullInfo?.boostsApplied || 0);
let description: string | undefined;
if (level === 0) {
if (!hasBoost) {
description = lang('ChannelBoost.EnableStoriesForChannelText', [chatTitle, moreBoosts]);
} else {
description = lang('ChannelBoost.EnableStoriesMoreRequired', moreBoosts);
}
} else if (isJustUpgraded) {
if (level === 1) {
description = lang('ChannelBoost.EnabledStoriesForChannelText');
} else {
description = lang('ChannelBoost.BoostedChannelReachedLevel', [level, currentStoriesPerDay]);
}
if (isMaxLevel) {
description = lang('BoostsMaxLevelReached');
} else if (boostsLeftToUnrestrict > 0 && !isChatAdmin(chat)) {
const boostTimes = lang('GroupBoost.BoostToUnrestrict.Times', boostsLeftToUnrestrict);
description = lang('GroupBoost.BoostToUnrestrict', [boostTimes, chatTitle]);
} else {
description = lang('ChannelBoost.HelpUpgradeChannelText', [chatTitle, moreBoosts, nextLevelStoriesPerDay]);
description = lang('ChannelBoost.MoreBoostsNeeded.Text', [chatTitle, moreBoosts]);
}
return {
@ -166,9 +170,9 @@ const BoostModal = ({
descriptionText: description,
boost: firstBoost,
isBoosted: hasBoost,
canBoostMore: areBoostsInDifferentChannels,
canBoostMore: areBoostsInDifferentChannels && !isMaxLevel,
};
}, [chat, chatTitle, info, lang]);
}, [chat, chatTitle, info, lang, chatFullInfo, isChannel]);
const isBoostDisabled = !info?.myBoosts?.length && isCurrentUserPremium;
const isReplacingBoost = boost?.chatId && boost.chatId !== info?.chatId;
@ -189,6 +193,7 @@ const BoostModal = ({
if (!boost) {
if (!isCurrentUserPremium) {
openPremiumDialog();
return;
}
closeBoostModal();
@ -231,6 +236,11 @@ const BoostModal = ({
floatingBadgeText={value}
floatingBadgeIcon="boost"
/>
{isBoosted && (
<div className={buildClassName(styles.description, styles.bold)}>
{lang('ChannelBoost.YouBoostedChannelText', chatTitle)}
</div>
)}
<div className={styles.description}>
{renderText(descriptionText, ['simple_markdown', 'emoji'])}
</div>
@ -239,7 +249,7 @@ const BoostModal = ({
{canBoostMore ? (
<>
<Icon name="boost" />
{lang(isBoosted && canBoostMore ? 'BoostingBoostAgain' : 'ChannelBoost.BoostChannel')}
{lang(isChannel ? 'ChannelBoost.BoostChannel' : 'GroupBoost.BoostGroup')}
</>
) : lang('OK')}
</Button>
@ -269,7 +279,7 @@ const BoostModal = ({
>
<div className={styles.avatarContainer}>
<div className={styles.boostedWrapper}>
<Avatar peer={boostedChat} size="large" />
<Avatar peer={prevBoostedChat} size="large" />
<Icon name="boostcircle" className={styles.boostedMark} />
</div>
<Icon name="next" className={styles.arrow} />
@ -334,12 +344,14 @@ function areAllBoostsInChannel(myBoosts: ApiMyBoost[], chatId: string) {
export default memo(withGlobal<OwnProps>(
(global, { info }): StateProps => {
const chat = info && selectChat(global, info?.chatId);
const chatFullInfo = chat && selectChatFullInfo(global, chat.id);
const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts, info.chatId);
const boostedChat = firstBoost?.chatId ? selectChat(global, firstBoost?.chatId) : undefined;
return {
chat,
boostedChat,
chatFullInfo,
prevBoostedChat: boostedChat,
isCurrentUserPremium: selectIsCurrentUserPremium(global),
};
},

View File

@ -2,13 +2,14 @@ import type { FC } from '../../lib/teact/teact';
import React, { memo, useCallback, useRef } from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global';
import type { ApiChat, ApiVideo } from '../../api/types';
import type { ApiChat, ApiChatFullInfo, ApiVideo } from '../../api/types';
import type { MessageList } from '../../global/types';
import { getAllowedAttachmentOptions, getCanPostInChat } from '../../global/helpers';
import {
selectCanScheduleUntilOnline,
selectChat,
selectChatFullInfo,
selectCurrentGifSearch,
selectCurrentMessageList,
selectIsChatWithBot,
@ -37,6 +38,7 @@ type StateProps = {
query?: string;
results?: ApiVideo[];
chat?: ApiChat;
chatFullInfo?: ApiChatFullInfo;
isChatWithBot?: boolean;
canScheduleUntilOnline?: boolean;
isSavedMessages?: boolean;
@ -52,6 +54,7 @@ const GifSearch: FC<OwnProps & StateProps> = ({
query,
results,
chat,
chatFullInfo,
isChatWithBot,
canScheduleUntilOnline,
isSavedMessages,
@ -74,7 +77,7 @@ const GifSearch: FC<OwnProps & StateProps> = ({
observe: observeIntersection,
} = useIntersectionObserver({ rootRef: containerRef, debounceMs: INTERSECTION_DEBOUNCE });
const canSendGifs = canPostInChat && getAllowedAttachmentOptions(chat, isChatWithBot).canSendGifs;
const canSendGifs = canPostInChat && getAllowedAttachmentOptions(chat, chatFullInfo, isChatWithBot).canSendGifs;
const handleGifClick = useCallback((gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => {
if (canSendGifs) {
@ -166,11 +169,13 @@ export default memo(withGlobal(
const { query, results } = currentSearch || {};
const { chatId, threadId } = selectCurrentMessageList(global) || {};
const chat = chatId ? selectChat(global, chatId) : undefined;
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
const isChatWithBot = chat ? selectIsChatWithBot(global, chat) : undefined;
const isSavedMessages = Boolean(chatId) && selectIsChatWithSelf(global, chatId);
const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined;
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
const canPostInChat = Boolean(chat) && Boolean(threadId) && getCanPostInChat(chat, threadId, isMessageThread);
const canPostInChat = Boolean(chat) && Boolean(threadId)
&& getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo);
return {
query,

View File

@ -260,42 +260,36 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
onChange={handlePermissionChange}
/>
</div>
{isChannel && (
<div className="ListItem">
<Checkbox
name="postStories"
checked={Boolean(permissions.postStories)}
label={lang('EditAdminPostStories')}
blocking
disabled={getControlIsDisabled('postStories')}
onChange={handlePermissionChange}
/>
</div>
)}
{isChannel && (
<div className="ListItem">
<Checkbox
name="editStories"
checked={Boolean(permissions.editStories)}
label={lang('EditAdminEditStories')}
blocking
disabled={getControlIsDisabled('editStories')}
onChange={handlePermissionChange}
/>
</div>
)}
{isChannel && (
<div className="ListItem">
<Checkbox
name="deleteStories"
checked={Boolean(permissions.deleteStories)}
label={lang('EditAdminDeleteStories')}
blocking
disabled={getControlIsDisabled('deleteStories')}
onChange={handlePermissionChange}
/>
</div>
)}
<div className="ListItem">
<Checkbox
name="postStories"
checked={Boolean(permissions.postStories)}
label={lang('EditAdminPostStories')}
blocking
disabled={getControlIsDisabled('postStories')}
onChange={handlePermissionChange}
/>
</div>
<div className="ListItem">
<Checkbox
name="editStories"
checked={Boolean(permissions.editStories)}
label={lang('EditAdminEditStories')}
blocking
disabled={getControlIsDisabled('editStories')}
onChange={handlePermissionChange}
/>
</div>
<div className="ListItem">
<Checkbox
name="deleteStories"
checked={Boolean(permissions.deleteStories)}
label={lang('EditAdminDeleteStories')}
blocking
disabled={getControlIsDisabled('deleteStories')}
onChange={handlePermissionChange}
/>
</div>
{!isChannel && (
<div className="ListItem">
<Checkbox

View File

@ -5,6 +5,7 @@ import React, {
import { getActions, withGlobal } from '../../global';
import type {
ApiChat,
ApiMediaAreaChannelPost,
ApiPeer, ApiStealthMode, ApiStory, ApiTypeStory,
} from '../../api/types';
@ -13,7 +14,7 @@ import type { Signal } from '../../util/signals';
import { MAIN_THREAD_ID } from '../../api/types';
import { EDITABLE_STORY_INPUT_CSS_SELECTOR, EDITABLE_STORY_INPUT_ID } from '../../config';
import { getSenderTitle, isUserId } from '../../global/helpers';
import { getSenderTitle, isChatChannel, isUserId } from '../../global/helpers';
import {
selectChat,
selectIsCurrentUserPremium,
@ -86,6 +87,7 @@ interface OwnProps {
interface StateProps {
peer: ApiPeer;
forwardSender?: ApiPeer;
fromPeer?: ApiPeer;
story?: ApiTypeStory;
isMuted: boolean;
orderedIds?: number[];
@ -109,6 +111,7 @@ function Story({
storyId,
peer,
forwardSender,
fromPeer,
isMuted,
isArchivedStories,
isPrivateStories,
@ -124,10 +127,10 @@ function Story({
getIsAnimating,
isCurrentUserPremium,
stealthMode,
withHeaderAnimation,
onDelete,
onClose,
onReport,
withHeaderAnimation,
}: OwnProps & StateProps) {
const {
viewStory,
@ -179,7 +182,9 @@ function Story({
const isLoadedStory = story && 'content' in story;
const isChangelog = peerId === storyChangelogUserId;
const isChannel = !isUserId(peerId);
const isUserStory = isUserId(peerId);
const isChatStory = !isUserStory;
const isChannelStory = isChatStory && isChatChannel(peer as ApiChat);
const isOut = isLoadedStory && story.isOut;
const canPinToProfile = useCurrentOrPrev(
@ -221,7 +226,8 @@ function Story({
? story.content.video.duration
: undefined;
const shouldShowFooter = isLoadedStory && (isOut || isChannel);
const shouldShowComposer = !(isOut && isUserStory) && !isChangelog && !isChannelStory;
const shouldShowFooter = isLoadedStory && !shouldShowComposer && (isOut || isChannelStory);
const headerAnimation = isMobile && withHeaderAnimation ? 'slideFade' : 'none';
const {
@ -239,7 +245,7 @@ function Story({
const {
shouldRender: shouldRenderComposer,
transitionClassNames: composerAppearanceAnimationClassNames,
} = useShowTransition(!isOut && !isChangelog && !isChannel);
} = useShowTransition(shouldShowComposer);
const {
shouldRender: shouldRenderCaptionBackdrop,
@ -340,11 +346,11 @@ function Story({
}, [hasAllData]);
useEffect(() => {
if (!isOut || isDeletedStory || areViewsExpired) return;
if (!isLoadedStory || isDeletedStory || areViewsExpired) return;
// Refresh recent viewers list each time
loadStoryViews({ peerId, storyId, isPreload: true });
}, [isDeletedStory, areViewsExpired, isOut, peerId, storyId]);
// Refresh counters each time
loadStoryViews({ peerId, storyId });
}, [isDeletedStory, areViewsExpired, isLoadedStory, peerId, storyId]);
useEffect(() => {
if (
@ -415,6 +421,11 @@ function Story({
openChat({ id: forwardSender!.id });
});
const handleFromPeerClick = useLastCallback(() => {
onClose();
openChat({ id: fromPeer!.id });
});
const handleOpenPrevStory = useLastCallback(() => {
openPreviousStory();
});
@ -569,7 +580,7 @@ function Story({
}
function renderStoryPrivacyButton() {
if (isChannel) return undefined;
if (!isUserStory) return undefined;
let privacyIcon = 'channel-filled';
const gradient: Record<string, [string, string]> = {
@ -638,11 +649,24 @@ function Story({
onClick={forwardSender ? handleForwardPeerClick : undefined}
>
<Icon name="loop" />
<span className={styles.forwardHeaderText}>
<span className={styles.headerTitle}>
{renderText(forwardSenderTitle)}
</span>
</span>
)}
{fromPeer && (
<span
className={buildClassName(
styles.storyMeta, styles.fromPeer,
)}
onClick={handleFromPeerClick}
>
<Avatar peer={fromPeer} size="micro" />
<span className={styles.headerTitle}>
{renderText(getSenderTitle(lang, fromPeer) || '')}
</span>
</span>
)}
{story && 'date' in story && (
<span className={styles.storyMeta}>{formatRelativeTime(lang, serverTime, story.date)}</span>
)}
@ -693,17 +717,25 @@ function Story({
>
{canCopyLink && <MenuItem icon="copy" onClick={handleCopyStoryLink}>{lang('CopyLink')}</MenuItem>}
{canPinToProfile && (
<MenuItem icon="save-story" onClick={handlePinClick}>{lang('StorySave')}</MenuItem>
<MenuItem icon="save-story" onClick={handlePinClick}>
{lang(isUserStory ? 'StorySave' : 'SaveToPosts')}
</MenuItem>
)}
{canUnpinFromProfile && (
<MenuItem icon="delete" onClick={handleUnpinClick}>{lang('ArchiveStory')}</MenuItem>
<MenuItem icon="delete" onClick={handleUnpinClick}>
{lang(isUserStory ? 'ArchiveStory' : 'RemoveFromPosts')}
</MenuItem>
)}
{canDownload && (
<MenuItem icon="download" disabled={!downloadMediaData} onClick={handleDownload}>
{lang('lng_media_download')}
</MenuItem>
)}
<MenuItem icon="eye-closed-outline" onClick={handleOpenStealthModal}>{lang('StealthMode')}</MenuItem>
{!isOut && isUserStory && (
<MenuItem icon="eye-closed-outline" onClick={handleOpenStealthModal}>
{lang('StealthMode')}
</MenuItem>
)}
{!isOut && <MenuItem icon="flag" onClick={handleReportStoryClick}>{lang('lng_report_story')}</MenuItem>}
{isOut && <MenuItem icon="delete" destructive onClick={handleDeleteStoryClick}>{lang('Delete')}</MenuItem>}
</DropdownMenu>
@ -818,7 +850,7 @@ function Story({
</div>
{shouldShowFooter && (
<StoryFooter story={story} className={appearanceAnimationClassNames} areViewsExpired={areViewsExpired} />
<StoryFooter story={story} className={appearanceAnimationClassNames} />
)}
{shouldRenderCaptionBackdrop && (
<div
@ -853,7 +885,7 @@ function Story({
editableInputId={EDITABLE_STORY_INPUT_ID}
inputId="story-input-text"
className={buildClassName(styles.composer, composerAppearanceAnimationClassNames)}
inputPlaceholder={lang('ReplyPrivately')}
inputPlaceholder={lang(isChatStory ? 'ReplyToGroupStory' : 'ReplyPrivately')}
onForward={canShare ? handleForwardClick : undefined}
onFocus={markComposerHasFocus}
onBlur={unmarkComposerHasFocus}
@ -888,21 +920,25 @@ export default memo(withGlobal<OwnProps>((global, {
} = tabState;
const { isOpen: isPremiumModalOpen } = premiumModal || {};
const story = selectPeerStory(global, peerId, storyId);
const isLoadedStory = story && 'content' in story;
const shouldForcePause = Boolean(
viewModal || forwardedStoryId || tabState.reactionPicker?.storyId || isReportModalOpen || isPrivacyModalOpen
|| isPremiumModalOpen || isDeleteModalOpen || safeLinkModalUrl || isStealthModalOpen || mapModal,
);
const forwardInfo = (story && 'forwardInfo' in story) ? story.forwardInfo : undefined;
const mediaAreas = (story && 'mediaAreas' in story) ? story.mediaAreas : undefined;
const forwardInfo = isLoadedStory ? story.forwardInfo : undefined;
const mediaAreas = isLoadedStory ? story.mediaAreas : undefined;
const forwardSenderId = forwardInfo?.fromPeerId
|| mediaAreas?.find((area): area is ApiMediaAreaChannelPost => area.type === 'channelPost')?.channelId;
const forwardSender = forwardSenderId ? selectPeer(global, forwardSenderId) : undefined;
const withHeaderAnimation = selectPerformanceSettingsValue(global, 'mediaViewerAnimations');
const fromPeer = isLoadedStory && story.fromId ? selectPeer(global, story.fromId) : undefined;
return {
peer: (user || chat)!,
forwardSender,
fromPeer,
story,
orderedIds: storyList?.storyIdsByPeerId[peerId],
isMuted,

View File

@ -19,13 +19,11 @@ import styles from './StoryFooter.module.scss';
type OwnProps = {
story: ApiStory;
areViewsExpired?: boolean;
className?: string;
};
const StoryFooter = ({
story,
areViewsExpired,
className,
}: OwnProps) => {
const { openStoryViewModal, openForwardMenu, sendStoryReaction } = getActions();
@ -94,7 +92,7 @@ const StoryFooter = ({
className={buildClassName(styles.viewInfo, !isChannel && styles.interactive)}
onClick={!isChannel ? handleOpenStoryViewModal : undefined}
>
{!areViewsExpired && Boolean(recentViewers?.length) && (
{Boolean(recentViewers?.length) && (
<AvatarList
size="small"
peers={recentViewers}

View File

@ -58,7 +58,7 @@ function StoryViewModal({
isCurrentUserPremium,
}: StateProps) {
const {
loadStoryViews, closeStoryViewModal, clearStoryViews,
loadStoryViewList, closeStoryViewModal, clearStoryViews,
} = getActions();
const [areJustContacts, markJustContacts, unmarkJustContacts] = useFlag(false);
@ -102,7 +102,7 @@ function StoryViewModal({
const handleLoadMore = useLastCallback(() => {
if (!story?.id || nextOffset === undefined) return;
loadStoryViews({
loadStoryViewList({
peerId: story.peerId,
storyId: story.id,
offset: nextOffset,

View File

@ -737,17 +737,17 @@
&.clickable {
cursor: var(--custom-cursor, pointer);
&:hover {
text-decoration: underline;
}
}
}
.forwardHeaderText {
.headerTitle {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.forwardHeader.clickable:hover & {
text-decoration: underline;
}
}
.forwardInfo {
@ -759,3 +759,14 @@
margin-bottom: 0.5rem;
z-index: 1;
}
.fromPeer {
display: flex;
align-items: center;
gap: 0.25rem;
cursor: var(--custom-cursor, pointer);
&:hover {
text-decoration: underline;
}
}

View File

@ -51,7 +51,7 @@ export const CUSTOM_EMOJI_PREVIEW_CACHE_DISABLED = false;
export const CUSTOM_EMOJI_PREVIEW_CACHE_NAME = 'tt-custom-emoji-preview';
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
export const LANG_CACHE_NAME = 'tt-lang-packs-v31';
export const LANG_CACHE_NAME = 'tt-lang-packs-v32';
export const ASSET_CACHE_NAME = 'tt-assets';
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
export const DATA_BROADCAST_CHANNEL_NAME = 'tt-global';
@ -206,7 +206,6 @@ export const POPULAR_SYMBOL_SET_ID = 'popular';
export const RECENT_SYMBOL_SET_ID = 'recent';
export const FAVORITE_SYMBOL_SET_ID = 'favorite';
export const CHAT_STICKER_SET_ID = 'chatStickers';
export const PREMIUM_STICKER_SET_ID = 'premium';
export const DEFAULT_TOPIC_ICON_STICKER_ID = 'topic-default-icon';
export const DEFAULT_STATUS_ICON_ID = 'status-default-icon';
export const EMOJI_IMG_REGEX = /<img[^>]+alt="([^"]+)"(?![^>]*data-document-id)[^>]*>/gm;

View File

@ -1463,7 +1463,7 @@ addActionHandler('processBoostParameters', async (global, actions, payload): Pro
}
}
if (!isChatChannel(chat)) {
if (!isChatChannel(chat) && !isChatSuperGroup(chat)) {
actions.openChat({ id: chat.id, tabId });
return;
}
@ -2781,13 +2781,12 @@ export async function loadFullChat<T extends GlobalState>(
}
const {
users, userStatusesById, fullInfo, groupCall, membersCount, isForumAsMessages,
chats, users, userStatusesById, fullInfo, groupCall, membersCount, isForumAsMessages,
} = result;
global = getGlobal();
if (users) {
global = addUsers(global, buildCollectionByKey(users, 'id'));
}
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = updateChats(global, buildCollectionByKey(chats, 'id'));
if (userStatusesById) {
global = addUserStatuses(global, userStatusesById);
@ -2825,6 +2824,18 @@ export async function loadFullChat<T extends GlobalState>(
});
}
const emojiSet = fullInfo.emojiSet;
const localEmojiSet = emojiSet && selectStickerSet(global, emojiSet);
if (emojiSet && !localEmojiSet) {
actions.loadStickers({
stickerSetInfo: {
id: emojiSet.id,
accessHash: emojiSet.accessHash,
},
tabId,
});
}
return result;
}

View File

@ -312,7 +312,7 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
const storyReplyInfo = isStoryReply ? {
type: 'story',
userId: storyPeerId!,
peerId: storyPeerId!,
storyId: storyId!,
} satisfies ApiInputStoryReplyInfo : undefined;

View File

@ -9,7 +9,7 @@ import { buildCollectionByKey, unique } from '../../../util/iteratees';
import * as langProvider from '../../../util/langProvider';
import { buildQueryString } from '../../../util/requestQuery';
import { callApi } from '../../../api/gramjs';
import { getStripeError, isChatChannel } from '../../helpers';
import { getStripeError, isChatChannel, isChatSuperGroup } from '../../helpers';
import { addActionHandler, getGlobal, setGlobal } from '../../index';
import {
addChats,
@ -19,12 +19,14 @@ import {
setReceipt,
setRequestInfoId,
setSmartGlocalCardInfo, setStripeCardInfo,
updateChatFullInfo,
updatePayment,
updateShippingOptions,
} from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
import {
selectChat,
selectChatFullInfo,
selectChatMessage,
selectPaymentFormId,
selectPaymentInputInvoice, selectPaymentRequestId,
@ -36,6 +38,8 @@ import {
selectUser,
} from '../../selectors';
const LOCAL_BOOST_COOLDOWN = 86400; // 24 hours
addActionHandler('validateRequestedInfo', (global, actions, payload): ActionReturnType => {
const { requestInfo, saveInfo, tabId = getCurrentTabId() } = payload;
@ -477,7 +481,7 @@ async function validateRequestedInfo<T extends GlobalState>(
addActionHandler('openBoostModal', async (global, actions, payload): Promise<void> => {
const { chatId, tabId = getCurrentTabId() } = payload;
const chat = selectChat(global, chatId);
if (!chat || !isChatChannel(chat)) return;
if (!chat || !(isChatChannel(chat) || isChatSuperGroup(chat))) return;
global = updateTabState(global, {
boostModal: {
@ -614,19 +618,96 @@ addActionHandler('applyBoost', async (global, actions, payload): Promise<void> =
const chat = selectChat(global, chatId);
if (!chat) return;
const oldChatFullInfo = selectChatFullInfo(global, chatId);
const oldBoostsApplied = oldChatFullInfo?.boostsApplied || 0;
const appliedBoostsCount = slots.length;
let tabState = selectTabState(global, tabId);
const oldStatus = tabState.boostModal?.boostStatus;
if (oldStatus) {
const boostsPerLevel = oldStatus.nextLevelBoosts ? oldStatus.nextLevelBoosts - oldStatus.currentLevelBoosts : 1;
const newBoosts = oldStatus.boosts + appliedBoostsCount;
const isLevelUp = oldStatus.nextLevelBoosts && newBoosts >= oldStatus.nextLevelBoosts;
const newCurrentLevelBoosts = isLevelUp ? oldStatus.nextLevelBoosts! : oldStatus.currentLevelBoosts;
const newNextLevelBoosts = isLevelUp ? oldStatus.nextLevelBoosts! + boostsPerLevel : oldStatus.nextLevelBoosts;
global = updateTabState(global, {
boostModal: {
...tabState.boostModal!,
boostStatus: {
...oldStatus,
level: isLevelUp ? oldStatus.level + 1 : oldStatus.level,
currentLevelBoosts: newCurrentLevelBoosts,
nextLevelBoosts: newNextLevelBoosts,
hasMyBoost: true,
boosts: newBoosts,
},
},
}, tabId);
setGlobal(global);
}
global = getGlobal();
tabState = selectTabState(global, tabId);
const oldMyBoosts = tabState.boostModal?.myBoosts;
if (oldMyBoosts) {
const unixNow = Math.floor(Date.now() / 1000);
const newMyBoosts = oldMyBoosts.map((boost) => {
if (slots.includes(boost.slot)) {
return {
...boost,
chatId,
date: unixNow,
cooldownUntil: unixNow + LOCAL_BOOST_COOLDOWN, // Will be refetched below
};
}
return boost;
});
global = updateTabState(global, {
boostModal: {
...tabState.boostModal!,
myBoosts: newMyBoosts,
},
}, tabId);
setGlobal(global);
}
const result = await callApi('applyBoost', {
slots,
chat,
});
global = getGlobal();
if (!result) {
// Rollback local changes
const boostModal = selectTabState(global, tabId).boostModal;
if (boostModal) {
global = updateTabState(global, {
boostModal: {
...boostModal,
boostStatus: oldStatus,
myBoosts: oldMyBoosts,
},
}, tabId);
setGlobal(global);
}
return;
}
global = getGlobal();
let tabState = selectTabState(global, tabId);
tabState = selectTabState(global, tabId);
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
if (oldChatFullInfo) {
global = updateChatFullInfo(global, chatId, {
boostsApplied: oldBoostsApplied + slots.length,
});
}
if (tabState.boostModal) {
global = updateTabState(global, {
boostModal: {
@ -636,25 +717,6 @@ addActionHandler('applyBoost', async (global, actions, payload): Promise<void> =
}, tabId);
}
setGlobal(global);
const newStatusResult = await callApi('fetchBoostsStatus', {
chat,
});
if (!newStatusResult) {
return;
}
global = getGlobal();
tabState = selectTabState(global, tabId);
if (!tabState.boostModal?.boostStatus) return;
global = updateTabState(global, {
boostModal: {
...tabState.boostModal,
boostStatus: newStatusResult,
},
}, tabId);
setGlobal(global);
});
addActionHandler('checkGiftCode', async (global, actions, payload): Promise<void> => {

View File

@ -1,7 +1,6 @@
import type { ApiStoryView } from '../../../api/types';
import type { ActionReturnType } from '../../types';
import { DEBUG, PREVIEW_AVATAR_COUNT } from '../../../config';
import { DEBUG } from '../../../config';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { translate } from '../../../util/langProvider';
@ -312,31 +311,43 @@ addActionHandler('loadPeerStoriesByIds', async (global, actions, payload): Promi
});
addActionHandler('loadStoryViews', async (global, actions, payload): Promise<void> => {
const { peerId, storyId } = payload;
const peer = selectPeer(global, peerId);
if (!peer) {
return;
}
const result = await callApi('fetchStoriesViews', { peer, storyIds: [storyId] });
if (!result) {
return;
}
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = updatePeerStoryViews(global, peerId, storyId, result.views);
setGlobal(global);
});
addActionHandler('loadStoryViewList', async (global, actions, payload): Promise<void> => {
const {
peerId,
storyId,
offset,
areReactionsFirst,
areJustContacts,
query,
limit,
tabId = getCurrentTabId(),
} = payload;
const isPreload = 'isPreload' in payload;
const {
offset, areReactionsFirst, areJustContacts, query, limit,
} = isPreload ? {
offset: undefined,
areReactionsFirst: undefined,
areJustContacts: undefined,
query: undefined,
limit: PREVIEW_AVATAR_COUNT,
} : payload;
const peer = selectPeer(global, peerId);
if (!peer) {
return;
}
if (!isPreload) {
global = updateStoryViewsLoading(global, true, tabId);
setGlobal(global);
}
global = updateStoryViewsLoading(global, true, tabId);
setGlobal(global);
const result = await callApi('fetchStoryViewList', {
peer,
@ -357,18 +368,7 @@ addActionHandler('loadStoryViews', async (global, actions, payload): Promise<voi
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
if (!isPreload) global = updateStoryViews(global, storyId, result.views, result.nextOffset, tabId);
if (isPreload && result.views?.length) {
const recentViewerIds = result.views
.filter((view): view is ApiStoryView => 'date' in view)
.map((view) => view.peerId);
global = updatePeerStoryViews(global, peerId, storyId, {
recentViewerIds,
viewsCount: result.viewsCount,
reactionsCount: result.reactionsCount,
});
}
global = updateStoryViews(global, storyId, result.views, result.nextOffset, tabId);
setGlobal(global);
});

View File

@ -149,29 +149,6 @@ addActionHandler('loadPremiumStickers', async (global): Promise<void> => {
setGlobal(global);
});
addActionHandler('loadPremiumSetStickers', async (global): Promise<void> => {
const { hash } = global.stickers.premium || {};
const result = await callApi('fetchStickersForEmoji', { emoji: '📂⭐️', hash });
if (!result) {
return;
}
global = getGlobal();
global = {
...global,
stickers: {
...global.stickers,
premiumSet: {
hash: result.hash,
stickers: result.stickers,
},
},
};
setGlobal(global);
});
addActionHandler('loadGreetingStickers', async (global): Promise<void> => {
const { hash } = global.stickers.greeting || {};

View File

@ -24,6 +24,7 @@ import { updateTabState } from '../../reducers/tabs';
import {
selectCanAnimateInterface,
selectChat,
selectChatFullInfo,
selectChatMessage,
selectCurrentChat,
selectCurrentMessageList,
@ -319,11 +320,12 @@ addActionHandler('showAllowedMessageTypesNotification', (global, actions, payloa
const chat = selectChat(global, chatId);
if (!chat) return;
const chatFullInfo = selectChatFullInfo(global, chatId);
const {
canSendPlainText, canSendPhotos, canSendVideos, canSendDocuments, canSendAudios,
canSendStickers, canSendRoundVideos, canSendVoices,
} = getAllowedAttachmentOptions(chat);
} = getAllowedAttachmentOptions(chat, chatFullInfo);
const allowedContent = compact([
canSendPlainText ? 'Chat.SendAllowedContentTypeText' : undefined,
canSendPhotos ? 'Chat.SendAllowedContentTypePhoto' : undefined,

View File

@ -3,6 +3,7 @@ import type {
ApiChatAdminRights,
ApiChatBannedRights,
ApiChatFolder,
ApiChatFullInfo,
ApiPeer,
ApiTopic,
ApiUser,
@ -130,14 +131,18 @@ export function getCanManageTopic(chat: ApiChat, topic: ApiTopic) {
return chat.isCreator || getHasAdminRight(chat, 'manageTopics') || topic.isOwner;
}
export function isUserRightBanned(chat: ApiChat, key: keyof ApiChatBannedRights) {
export function isUserRightBanned(chat: ApiChat, key: keyof ApiChatBannedRights, chatFullInfo?: ApiChatFullInfo) {
const unrestrictedByBoosts = chatFullInfo?.boostsToUnrestrict
&& (chatFullInfo.boostsApplied || 0) >= chatFullInfo.boostsToUnrestrict;
return Boolean(
(chat.currentUserBannedRights?.[key])
|| (chat.defaultBannedRights?.[key]),
|| (chat.defaultBannedRights?.[key] && !unrestrictedByBoosts),
);
}
export function getCanPostInChat(chat: ApiChat, threadId: ThreadId, isMessageThread?: boolean) {
export function getCanPostInChat(
chat: ApiChat, threadId: ThreadId, isMessageThread?: boolean, chatFullInfo?: ApiChatFullInfo,
) {
if (threadId !== MAIN_THREAD_ID) {
if (chat.isForum) {
if (chat.isNotJoined) {
@ -168,7 +173,7 @@ export function getCanPostInChat(chat: ApiChat, threadId: ThreadId, isMessageThr
return getHasAdminRight(chat, 'postMessages');
}
return isChatAdmin(chat) || !isUserRightBanned(chat, 'sendMessages');
return isChatAdmin(chat) || !isUserRightBanned(chat, 'sendMessages', chatFullInfo);
}
export interface IAllowedAttachmentOptions {
@ -188,6 +193,7 @@ export interface IAllowedAttachmentOptions {
export function getAllowedAttachmentOptions(
chat?: ApiChat,
chatFullInfo?: ApiChatFullInfo,
isChatWithBot = false,
isStoryReply = false,
): IAllowedAttachmentOptions {
@ -211,20 +217,20 @@ export function getAllowedAttachmentOptions(
const isAdmin = isChatAdmin(chat);
return {
canAttachMedia: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendMedia'),
canAttachMedia: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendMedia', chatFullInfo),
canAttachPolls: !isStoryReply
&& (isAdmin || !isUserRightBanned(chat, 'sendPolls'))
&& (isAdmin || !isUserRightBanned(chat, 'sendPolls', chatFullInfo))
&& (!isUserId(chat.id) || isChatWithBot),
canSendStickers: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendStickers'),
canSendGifs: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendGifs'),
canAttachEmbedLinks: !isStoryReply && (isAdmin || !isUserRightBanned(chat, 'embedLinks')),
canSendPhotos: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendPhotos'),
canSendVideos: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendVideos'),
canSendRoundVideos: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendRoundvideos'),
canSendAudios: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendAudios'),
canSendVoices: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendVoices'),
canSendPlainText: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendPlain'),
canSendDocuments: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendDocs'),
canSendStickers: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendStickers', chatFullInfo),
canSendGifs: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendGifs', chatFullInfo),
canAttachEmbedLinks: !isStoryReply && (isAdmin || !isUserRightBanned(chat, 'embedLinks', chatFullInfo)),
canSendPhotos: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendPhotos', chatFullInfo),
canSendVideos: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendVideos', chatFullInfo),
canSendRoundVideos: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendRoundvideos', chatFullInfo),
canSendAudios: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendAudios', chatFullInfo),
canSendVoices: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendVoices', chatFullInfo),
canSendPlainText: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendPlain', chatFullInfo),
canSendDocuments: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendDocs', chatFullInfo),
};
}

View File

@ -172,9 +172,6 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
premium: {
stickers: [],
},
premiumSet: {
stickers: [],
},
featured: {
setIds: [],
},

View File

@ -591,6 +591,8 @@ export function selectAllowedMessageActions<T extends GlobalState>(global: T, me
const { content } = message;
const messageTopic = selectTopicFromMessage(global, message);
const isDocumentSticker = isMessageDocumentSticker(message);
const chatFullInfo = selectChatFullInfo(global, chat.id);
const isBoostMessage = message.content.action?.type === 'chatBoost';
const canEditMessagesIndefinitely = isChatWithSelf
|| (isSuperGroup && getHasAdminRight(chat, 'pinMessages'))
@ -614,7 +616,7 @@ export function selectAllowedMessageActions<T extends GlobalState>(global: T, me
const threadInfo = selectThreadInfo(global, message.chatId, threadId);
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
const canReply = !isLocal && !isServiceNotification && !chat.isForbidden
&& getCanPostInChat(chat, threadId, isMessageThread)
&& getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo)
&& (!messageTopic || !messageTopic.isClosed || messageTopic.isOwner || getHasAdminRight(chat, 'manageTopics'));
const hasPinPermission = isPrivate || (
@ -633,7 +635,10 @@ export function selectAllowedMessageActions<T extends GlobalState>(global: T, me
canPin = !canUnpin;
}
const canDelete = (!isLocal || isFailed) && !isServiceNotification && (
const canNotDeleteBoostMessage = isBoostMessage && isOwn
&& !chat.isCreator && !getHasAdminRight(chat, 'deleteMessages');
const canDelete = (!isLocal || isFailed) && !isServiceNotification && !canNotDeleteBoostMessage && (
isPrivate
|| isOwn
|| isBasicGroup
@ -1363,11 +1368,12 @@ export function selectForwardsCanBeSentToChat<T extends GlobalState>(
return true;
}
const chatFullInfo = selectChatFullInfo(global, toChatId);
const chatMessages = selectChatMessages(global, fromChatId!);
const {
canSendVoices, canSendRoundVideos, canSendStickers, canSendDocuments, canSendAudios, canSendVideos,
canSendPhotos, canSendGifs, canSendPlainText,
} = getAllowedAttachmentOptions(chat);
} = getAllowedAttachmentOptions(chat, chatFullInfo);
return !messageIds!.some((messageId) => {
const message = chatMessages[messageId];
const isVoice = message.content.voice;

View File

@ -910,10 +910,6 @@ export type GlobalState = {
hash?: string;
stickers: ApiSticker[];
};
premiumSet: {
hash?: string;
stickers: ApiSticker[];
};
featured: {
hash?: string;
setIds?: string[];
@ -2212,11 +2208,11 @@ export interface ActionPayloads {
isMuted: boolean;
} & WithTabId;
closeStoryViewer: WithTabId | undefined;
loadStoryViews: ({
loadStoryViews: {
peerId: string;
storyId: number;
isPreload: true;
} | {
};
loadStoryViewList: ({
peerId: string;
storyId: number;
offset?: string;
@ -2900,9 +2896,6 @@ export interface ActionPayloads {
loadPremiumGifts: undefined;
loadDefaultTopicIcons: undefined;
loadPremiumStickers: undefined;
loadPremiumSetStickers: {
hash?: string;
} | undefined;
openGiftPremiumModal: ({
forUserId?: string;

View File

@ -1,6 +1,6 @@
const api = require('./api');
const LAYER = 173;
const LAYER = 174;
const tlobjects = {};
for (const tl of Object.values(api)) {

View File

@ -70,7 +70,7 @@ namespace Api {
export type TypeChatPhoto = ChatPhotoEmpty | ChatPhoto;
export type TypeMessage = MessageEmpty | Message | MessageService;
export type TypeMessageMedia = MessageMediaEmpty | MessageMediaPhoto | MessageMediaGeo | MessageMediaContact | MessageMediaUnsupported | MessageMediaDocument | MessageMediaWebPage | MessageMediaVenue | MessageMediaGame | MessageMediaInvoice | MessageMediaGeoLive | MessageMediaPoll | MessageMediaDice | MessageMediaStory | MessageMediaGiveaway | MessageMediaGiveawayResults;
export type TypeMessageAction = MessageActionEmpty | MessageActionChatCreate | MessageActionChatEditTitle | MessageActionChatEditPhoto | MessageActionChatDeletePhoto | MessageActionChatAddUser | MessageActionChatDeleteUser | MessageActionChatJoinedByLink | MessageActionChannelCreate | MessageActionChatMigrateTo | MessageActionChannelMigrateFrom | MessageActionPinMessage | MessageActionHistoryClear | MessageActionGameScore | MessageActionPaymentSentMe | MessageActionPaymentSent | MessageActionPhoneCall | MessageActionScreenshotTaken | MessageActionCustomAction | MessageActionBotAllowed | MessageActionSecureValuesSentMe | MessageActionSecureValuesSent | MessageActionContactSignUp | MessageActionGeoProximityReached | MessageActionGroupCall | MessageActionInviteToGroupCall | MessageActionSetMessagesTTL | MessageActionGroupCallScheduled | MessageActionSetChatTheme | MessageActionChatJoinedByRequest | MessageActionWebViewDataSentMe | MessageActionWebViewDataSent | MessageActionGiftPremium | MessageActionTopicCreate | MessageActionTopicEdit | MessageActionSuggestProfilePhoto | MessageActionRequestedPeer | MessageActionSetChatWallPaper | MessageActionGiftCode | MessageActionGiveawayLaunch | MessageActionGiveawayResults;
export type TypeMessageAction = MessageActionEmpty | MessageActionChatCreate | MessageActionChatEditTitle | MessageActionChatEditPhoto | MessageActionChatDeletePhoto | MessageActionChatAddUser | MessageActionChatDeleteUser | MessageActionChatJoinedByLink | MessageActionChannelCreate | MessageActionChatMigrateTo | MessageActionChannelMigrateFrom | MessageActionPinMessage | MessageActionHistoryClear | MessageActionGameScore | MessageActionPaymentSentMe | MessageActionPaymentSent | MessageActionPhoneCall | MessageActionScreenshotTaken | MessageActionCustomAction | MessageActionBotAllowed | MessageActionSecureValuesSentMe | MessageActionSecureValuesSent | MessageActionContactSignUp | MessageActionGeoProximityReached | MessageActionGroupCall | MessageActionInviteToGroupCall | MessageActionSetMessagesTTL | MessageActionGroupCallScheduled | MessageActionSetChatTheme | MessageActionChatJoinedByRequest | MessageActionWebViewDataSentMe | MessageActionWebViewDataSent | MessageActionGiftPremium | MessageActionTopicCreate | MessageActionTopicEdit | MessageActionSuggestProfilePhoto | MessageActionRequestedPeer | MessageActionSetChatWallPaper | MessageActionGiftCode | MessageActionGiveawayLaunch | MessageActionGiveawayResults | MessageActionBoostApply;
export type TypeDialog = Dialog | DialogFolder;
export type TypePhoto = PhotoEmpty | Photo;
export type TypePhotoSize = PhotoSizeEmpty | PhotoSize | PhotoCachedSize | PhotoStrippedSize | PhotoSizeProgressive | PhotoPathSize;
@ -168,7 +168,7 @@ namespace Api {
export type TypeLangPackString = LangPackString | LangPackStringPluralized | LangPackStringDeleted;
export type TypeLangPackDifference = LangPackDifference;
export type TypeLangPackLanguage = LangPackLanguage;
export type TypeChannelAdminLogEventAction = ChannelAdminLogEventActionChangeTitle | ChannelAdminLogEventActionChangeAbout | ChannelAdminLogEventActionChangeUsername | ChannelAdminLogEventActionChangePhoto | ChannelAdminLogEventActionToggleInvites | ChannelAdminLogEventActionToggleSignatures | ChannelAdminLogEventActionUpdatePinned | ChannelAdminLogEventActionEditMessage | ChannelAdminLogEventActionDeleteMessage | ChannelAdminLogEventActionParticipantJoin | ChannelAdminLogEventActionParticipantLeave | ChannelAdminLogEventActionParticipantInvite | ChannelAdminLogEventActionParticipantToggleBan | ChannelAdminLogEventActionParticipantToggleAdmin | ChannelAdminLogEventActionChangeStickerSet | ChannelAdminLogEventActionTogglePreHistoryHidden | ChannelAdminLogEventActionDefaultBannedRights | ChannelAdminLogEventActionStopPoll | ChannelAdminLogEventActionChangeLinkedChat | ChannelAdminLogEventActionChangeLocation | ChannelAdminLogEventActionToggleSlowMode | ChannelAdminLogEventActionStartGroupCall | ChannelAdminLogEventActionDiscardGroupCall | ChannelAdminLogEventActionParticipantMute | ChannelAdminLogEventActionParticipantUnmute | ChannelAdminLogEventActionToggleGroupCallSetting | ChannelAdminLogEventActionParticipantJoinByInvite | ChannelAdminLogEventActionExportedInviteDelete | ChannelAdminLogEventActionExportedInviteRevoke | ChannelAdminLogEventActionExportedInviteEdit | ChannelAdminLogEventActionParticipantVolume | ChannelAdminLogEventActionChangeHistoryTTL | ChannelAdminLogEventActionParticipantJoinByRequest | ChannelAdminLogEventActionToggleNoForwards | ChannelAdminLogEventActionSendMessage | ChannelAdminLogEventActionChangeAvailableReactions | ChannelAdminLogEventActionChangeUsernames | ChannelAdminLogEventActionToggleForum | ChannelAdminLogEventActionCreateTopic | ChannelAdminLogEventActionEditTopic | ChannelAdminLogEventActionDeleteTopic | ChannelAdminLogEventActionPinTopic | ChannelAdminLogEventActionToggleAntiSpam | ChannelAdminLogEventActionChangePeerColor | ChannelAdminLogEventActionChangeProfilePeerColor | ChannelAdminLogEventActionChangeWallpaper | ChannelAdminLogEventActionChangeEmojiStatus;
export type TypeChannelAdminLogEventAction = ChannelAdminLogEventActionChangeTitle | ChannelAdminLogEventActionChangeAbout | ChannelAdminLogEventActionChangeUsername | ChannelAdminLogEventActionChangePhoto | ChannelAdminLogEventActionToggleInvites | ChannelAdminLogEventActionToggleSignatures | ChannelAdminLogEventActionUpdatePinned | ChannelAdminLogEventActionEditMessage | ChannelAdminLogEventActionDeleteMessage | ChannelAdminLogEventActionParticipantJoin | ChannelAdminLogEventActionParticipantLeave | ChannelAdminLogEventActionParticipantInvite | ChannelAdminLogEventActionParticipantToggleBan | ChannelAdminLogEventActionParticipantToggleAdmin | ChannelAdminLogEventActionChangeStickerSet | ChannelAdminLogEventActionTogglePreHistoryHidden | ChannelAdminLogEventActionDefaultBannedRights | ChannelAdminLogEventActionStopPoll | ChannelAdminLogEventActionChangeLinkedChat | ChannelAdminLogEventActionChangeLocation | ChannelAdminLogEventActionToggleSlowMode | ChannelAdminLogEventActionStartGroupCall | ChannelAdminLogEventActionDiscardGroupCall | ChannelAdminLogEventActionParticipantMute | ChannelAdminLogEventActionParticipantUnmute | ChannelAdminLogEventActionToggleGroupCallSetting | ChannelAdminLogEventActionParticipantJoinByInvite | ChannelAdminLogEventActionExportedInviteDelete | ChannelAdminLogEventActionExportedInviteRevoke | ChannelAdminLogEventActionExportedInviteEdit | ChannelAdminLogEventActionParticipantVolume | ChannelAdminLogEventActionChangeHistoryTTL | ChannelAdminLogEventActionParticipantJoinByRequest | ChannelAdminLogEventActionToggleNoForwards | ChannelAdminLogEventActionSendMessage | ChannelAdminLogEventActionChangeAvailableReactions | ChannelAdminLogEventActionChangeUsernames | ChannelAdminLogEventActionToggleForum | ChannelAdminLogEventActionCreateTopic | ChannelAdminLogEventActionEditTopic | ChannelAdminLogEventActionDeleteTopic | ChannelAdminLogEventActionPinTopic | ChannelAdminLogEventActionToggleAntiSpam | ChannelAdminLogEventActionChangePeerColor | ChannelAdminLogEventActionChangeProfilePeerColor | ChannelAdminLogEventActionChangeWallpaper | ChannelAdminLogEventActionChangeEmojiStatus | ChannelAdminLogEventActionChangeEmojiStickerSet;
export type TypeChannelAdminLogEvent = ChannelAdminLogEvent;
export type TypeChannelAdminLogEventsFilter = ChannelAdminLogEventsFilter;
export type TypePopularContact = PopularContact;
@ -1395,6 +1395,9 @@ namespace Api {
availableReactions?: Api.TypeChatReactions;
stories?: Api.TypePeerStories;
wallpaper?: Api.TypeWallPaper;
boostsApplied?: int;
boostsUnrestrict?: int;
emojiset?: Api.TypeStickerSet;
}> {
// flags: undefined;
canViewParticipants?: true;
@ -1449,6 +1452,9 @@ namespace Api {
availableReactions?: Api.TypeChatReactions;
stories?: Api.TypePeerStories;
wallpaper?: Api.TypeWallPaper;
boostsApplied?: int;
boostsUnrestrict?: int;
emojiset?: Api.TypeStickerSet;
};
export class ChatParticipant extends VirtualClass<{
userId: long;
@ -1529,6 +1535,7 @@ namespace Api {
invertMedia?: true;
id: int;
fromId?: Api.TypePeer;
fromBoostsApplied?: int;
peerId: Api.TypePeer;
savedPeerId?: Api.TypePeer;
fwdFrom?: Api.TypeMessageFwdHeader;
@ -1563,6 +1570,7 @@ namespace Api {
invertMedia?: true;
id: int;
fromId?: Api.TypePeer;
fromBoostsApplied?: int;
peerId: Api.TypePeer;
savedPeerId?: Api.TypePeer;
fwdFrom?: Api.TypeMessageFwdHeader;
@ -2107,6 +2115,11 @@ namespace Api {
winnersCount: int;
unclaimedCount: int;
};
export class MessageActionBoostApply extends VirtualClass<{
boosts: int;
}> {
boosts: int;
};
export class Dialog extends VirtualClass<{
// flags: undefined;
pinned?: true;
@ -6600,6 +6613,13 @@ namespace Api {
prevValue: Api.TypeEmojiStatus;
newValue: Api.TypeEmojiStatus;
};
export class ChannelAdminLogEventActionChangeEmojiStickerSet extends VirtualClass<{
prevStickerset: Api.TypeInputStickerSet;
newStickerset: Api.TypeInputStickerSet;
}> {
prevStickerset: Api.TypeInputStickerSet;
newStickerset: Api.TypeInputStickerSet;
};
export class ChannelAdminLogEvent extends VirtualClass<{
id: long;
date: int;
@ -7876,10 +7896,10 @@ namespace Api {
quoteOffset?: int;
};
export class MessageReplyStoryHeader extends VirtualClass<{
userId: long;
peer: Api.TypePeer;
storyId: int;
}> {
userId: long;
peer: Api.TypePeer;
storyId: int;
};
export class MessageReplies extends VirtualClass<{
@ -8842,6 +8862,7 @@ namespace Api {
out?: true;
id: int;
date: int;
fromId?: Api.TypePeer;
fwdFrom?: Api.TypeStoryFwdHeader;
expireDate: int;
caption?: string;
@ -8864,6 +8885,7 @@ namespace Api {
out?: true;
id: int;
date: int;
fromId?: Api.TypePeer;
fwdFrom?: Api.TypeStoryFwdHeader;
expireDate: int;
caption?: string;
@ -8931,10 +8953,10 @@ namespace Api {
quoteOffset?: int;
};
export class InputReplyToStory extends VirtualClass<{
userId: Api.TypeInputUser;
peer: Api.TypeInputPeer;
storyId: int;
}> {
userId: Api.TypeInputUser;
peer: Api.TypeInputPeer;
storyId: int;
};
export class ExportedStoryLink extends VirtualClass<{
@ -10839,6 +10861,7 @@ namespace Api {
colors?: help.TypePeerColorSet;
darkColors?: help.TypePeerColorSet;
channelMinLevel?: int;
groupMinLevel?: int;
}> {
// flags: undefined;
hidden?: true;
@ -10846,6 +10869,7 @@ namespace Api {
colors?: help.TypePeerColorSet;
darkColors?: help.TypePeerColorSet;
channelMinLevel?: int;
groupMinLevel?: int;
};
export class PeerColorsNotModified extends VirtualClass<void> {};
export class PeerColors extends VirtualClass<{
@ -15506,6 +15530,20 @@ namespace Api {
channel: Api.TypeInputChannel;
emojiStatus: Api.TypeEmojiStatus;
};
export class SetBoostsToUnblockRestrictions extends Request<Partial<{
channel: Api.TypeInputChannel;
boosts: int;
}>, Api.TypeUpdates> {
channel: Api.TypeInputChannel;
boosts: int;
};
export class SetEmojiStickers extends Request<Partial<{
channel: Api.TypeInputChannel;
stickerset: Api.TypeInputStickerSet;
}>, Bool> {
channel: Api.TypeInputChannel;
stickerset: Api.TypeInputStickerSet;
};
}
export namespace bots {
@ -16606,7 +16644,7 @@ namespace Api {
| photos.UpdateProfilePhoto | photos.UploadProfilePhoto | photos.DeletePhotos | photos.GetUserPhotos | photos.UploadContactProfilePhoto
| upload.SaveFilePart | upload.GetFile | upload.SaveBigFilePart | upload.GetWebFile | upload.GetCdnFile | upload.ReuploadCdnFile | upload.GetCdnFileHashes | upload.GetFileHashes
| help.GetConfig | help.GetNearestDc | help.GetAppUpdate | help.GetInviteText | help.GetSupport | help.SetBotUpdatesStatus | help.GetCdnConfig | help.GetRecentMeUrls | help.GetTermsOfServiceUpdate | help.AcceptTermsOfService | help.GetDeepLinkInfo | help.GetAppConfig | help.SaveAppLog | help.GetPassportConfig | help.GetSupportName | help.GetUserInfo | help.EditUserInfo | help.GetPromoData | help.HidePromoData | help.DismissSuggestion | help.GetCountriesList | help.GetPremiumPromo | help.GetPeerColors | help.GetPeerProfileColors
| channels.ReadHistory | channels.DeleteMessages | channels.ReportSpam | channels.GetMessages | channels.GetParticipants | channels.GetParticipant | channels.GetChannels | channels.GetFullChannel | channels.CreateChannel | channels.EditAdmin | channels.EditTitle | channels.EditPhoto | channels.CheckUsername | channels.UpdateUsername | channels.JoinChannel | channels.LeaveChannel | channels.InviteToChannel | channels.DeleteChannel | channels.ExportMessageLink | channels.ToggleSignatures | channels.GetAdminedPublicChannels | channels.EditBanned | channels.GetAdminLog | channels.SetStickers | channels.ReadMessageContents | channels.DeleteHistory | channels.TogglePreHistoryHidden | channels.GetLeftChannels | channels.GetGroupsForDiscussion | channels.SetDiscussionGroup | channels.EditCreator | channels.EditLocation | channels.ToggleSlowMode | channels.GetInactiveChannels | channels.ConvertToGigagroup | channels.ViewSponsoredMessage | channels.GetSponsoredMessages | channels.GetSendAs | channels.DeleteParticipantHistory | channels.ToggleJoinToSend | channels.ToggleJoinRequest | channels.ReorderUsernames | channels.ToggleUsername | channels.DeactivateAllUsernames | channels.ToggleForum | channels.CreateForumTopic | channels.GetForumTopics | channels.GetForumTopicsByID | channels.EditForumTopic | channels.UpdatePinnedForumTopic | channels.DeleteTopicHistory | channels.ReorderPinnedForumTopics | channels.ToggleAntiSpam | channels.ReportAntiSpamFalsePositive | channels.ToggleParticipantsHidden | channels.ClickSponsoredMessage | channels.UpdateColor | channels.ToggleViewForumAsMessages | channels.GetChannelRecommendations | channels.UpdateEmojiStatus
| channels.ReadHistory | channels.DeleteMessages | channels.ReportSpam | channels.GetMessages | channels.GetParticipants | channels.GetParticipant | channels.GetChannels | channels.GetFullChannel | channels.CreateChannel | channels.EditAdmin | channels.EditTitle | channels.EditPhoto | channels.CheckUsername | channels.UpdateUsername | channels.JoinChannel | channels.LeaveChannel | channels.InviteToChannel | channels.DeleteChannel | channels.ExportMessageLink | channels.ToggleSignatures | channels.GetAdminedPublicChannels | channels.EditBanned | channels.GetAdminLog | channels.SetStickers | channels.ReadMessageContents | channels.DeleteHistory | channels.TogglePreHistoryHidden | channels.GetLeftChannels | channels.GetGroupsForDiscussion | channels.SetDiscussionGroup | channels.EditCreator | channels.EditLocation | channels.ToggleSlowMode | channels.GetInactiveChannels | channels.ConvertToGigagroup | channels.ViewSponsoredMessage | channels.GetSponsoredMessages | channels.GetSendAs | channels.DeleteParticipantHistory | channels.ToggleJoinToSend | channels.ToggleJoinRequest | channels.ReorderUsernames | channels.ToggleUsername | channels.DeactivateAllUsernames | channels.ToggleForum | channels.CreateForumTopic | channels.GetForumTopics | channels.GetForumTopicsByID | channels.EditForumTopic | channels.UpdatePinnedForumTopic | channels.DeleteTopicHistory | channels.ReorderPinnedForumTopics | channels.ToggleAntiSpam | channels.ReportAntiSpamFalsePositive | channels.ToggleParticipantsHidden | channels.ClickSponsoredMessage | channels.UpdateColor | channels.ToggleViewForumAsMessages | channels.GetChannelRecommendations | channels.UpdateEmojiStatus | channels.SetBoostsToUnblockRestrictions | channels.SetEmojiStickers
| bots.SendCustomRequest | bots.AnswerWebhookJSONQuery | bots.SetBotCommands | bots.ResetBotCommands | bots.GetBotCommands | bots.SetBotMenuButton | bots.GetBotMenuButton | bots.SetBotBroadcastDefaultAdminRights | bots.SetBotGroupDefaultAdminRights | bots.SetBotInfo | bots.GetBotInfo | bots.ReorderUsernames | bots.ToggleUsername | bots.CanSendMessage | bots.AllowSendMessage | bots.InvokeWebViewCustomMethod
| payments.GetPaymentForm | payments.GetPaymentReceipt | payments.ValidateRequestedInfo | payments.SendPaymentForm | payments.GetSavedInfo | payments.ClearSavedInfo | payments.GetBankCardData | payments.ExportInvoice | payments.AssignAppStoreTransaction | payments.AssignPlayMarketTransaction | payments.CanPurchasePremium | payments.GetPremiumGiftCodeOptions | payments.CheckGiftCode | payments.ApplyGiftCode | payments.GetGiveawayInfo | payments.LaunchPrepaidGiveaway
| stickers.CreateStickerSet | stickers.RemoveStickerFromSet | stickers.ChangeStickerPosition | stickers.AddStickerToSet | stickers.SetStickerSetThumb | stickers.CheckShortName | stickers.SuggestShortName | stickers.ChangeSticker | stickers.RenameStickerSet | stickers.DeleteStickerSet

View File

@ -81,7 +81,7 @@ chatForbidden#6592a1a7 id:long title:string = Chat;
channel#aadfc8f 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 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 = 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#c9d31138 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 = ChatFull;
channelFull#f2bcb6f 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 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 stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper = ChatFull;
channelFull#44c054a7 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 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 stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
chatParticipantAdmin#a0933f5b user_id:long inviter_id:long date:int = ChatParticipant;
@ -90,7 +90,7 @@ chatParticipants#3cbc93f8 chat_id:long participants:Vector<ChatParticipant> vers
chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
message#76bec211 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
message#1e4c8a69 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
@ -149,6 +149,7 @@ messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.
messageActionGiftCode#678c2e09 flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long = MessageAction;
messageActionGiveawayLaunch#332ba9ed = MessageAction;
messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction;
messageActionBoostApply#cc02aa6d boosts:int = 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;
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;
photoEmpty#2331b22d id:long = Photo;
@ -797,6 +798,7 @@ channelAdminLogEventActionChangePeerColor#5796e780 prev_value:PeerColor new_valu
channelAdminLogEventActionChangeProfilePeerColor#5e477b25 prev_value:PeerColor new_value:PeerColor = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeWallpaper#31bb5d52 prev_value:WallPaper new_value:WallPaper = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStatus#3ea9feb1 prev_value:EmojiStatus new_value:EmojiStatus = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStickerSet#46d840ab prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter;
@ -982,7 +984,7 @@ messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:fla
messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messageReplyHeader#afbc09db flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true quote:flags.9?true reply_to_msg_id:flags.4?int reply_to_peer_id:flags.0?Peer reply_from:flags.5?MessageFwdHeader reply_media:flags.8?MessageMedia reply_to_top_id:flags.1?int quote_text:flags.6?string quote_entities:flags.7?Vector<MessageEntity> quote_offset:flags.10?int = MessageReplyHeader;
messageReplyStoryHeader#9c98bfc1 user_id:long story_id:int = MessageReplyHeader;
messageReplyStoryHeader#e5af939 peer:Peer story_id:int = MessageReplyHeader;
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#7fe91c14 views_graph:StatsGraph reactions_by_emotion_graph:StatsGraph = stats.MessageStats;
@ -1152,7 +1154,7 @@ sponsoredWebPage#3db8ec63 flags:# url:string site_name:string photo:flags.0?Phot
storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int forwards_count:flags.2?int reactions:flags.3?Vector<ReactionCount> reactions_count:flags.4?int recent_viewers:flags.0?Vector<long> = StoryViews;
storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true id:int date:int expire_date:int = StoryItem;
storyItem#af6365a1 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int fwd_from:flags.17?StoryFwdHeader expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
storyItem#79b26a24 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int from_id:flags.18?Peer fwd_from:flags.17?StoryFwdHeader expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories;
stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string peer_stories:Vector<PeerStories> chats:Vector<Chat> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories;
stories.stories#5dd8c3c8 count:int stories:Vector<StoryItem> chats:Vector<Chat> users:Vector<User> = stories.Stories;
@ -1162,7 +1164,7 @@ storyViewPublicRepost#bd74cf49 flags:# blocked:flags.0?true blocked_my_stories_f
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;
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;
inputReplyToStory#15b0f283 user_id:InputUser story_id:int = InputReplyTo;
inputReplyToStory#5881323a peer:InputPeer story_id:int = InputReplyTo;
exportedStoryLink#3fc9053b link:string = ExportedStoryLink;
storiesStealthMode#712e27fd flags:# active_until_date:flags.0?int cooldown_until_date:flags.1?int = StoriesStealthMode;
mediaAreaCoordinates#3d1ea4e x:double y:double w:double h:double rotation:double = MediaAreaCoordinates;
@ -1195,7 +1197,7 @@ stats.publicForwards#93037e20 flags:# count:int forwards:Vector<PublicForward> n
peerColor#b54b5acf flags:# color:flags.0?int background_emoji_id:flags.1?long = PeerColor;
help.peerColorSet#26219a58 colors:Vector<int> = help.PeerColorSet;
help.peerColorProfileSet#767d61eb palette_colors:Vector<int> bg_colors:Vector<int> story_colors:Vector<int> = help.PeerColorSet;
help.peerColorOption#ef8430ab flags:# hidden:flags.0?true color_id:int colors:flags.1?help.PeerColorSet dark_colors:flags.2?help.PeerColorSet channel_min_level:flags.3?int = help.PeerColorOption;
help.peerColorOption#adec6ebe flags:# hidden:flags.0?true color_id:int colors:flags.1?help.PeerColorSet dark_colors:flags.2?help.PeerColorSet channel_min_level:flags.3?int group_min_level:flags.4?int = help.PeerColorOption;
help.peerColorsNotModified#2ba1f5ce = help.PeerColors;
help.peerColors#f8ed08 hash:int colors:Vector<help.PeerColorOption> = help.PeerColors;
storyReaction#6090d6d5 peer_id:Peer date:int reaction:Reaction = StoryReaction;
@ -1538,6 +1540,7 @@ stories.getStoriesByID#5774ca74 peer:InputPeer id:Vector<int> = stories.Stories;
stories.readStories#a556dac8 peer:InputPeer max_id:int = Vector<int>;
stories.incrementStoryViews#b2028afb peer:InputPeer id:Vector<int> = Bool;
stories.getStoryViewsList#7ed23c57 flags:# just_contacts:flags.0?true reactions_first:flags.2?true forwards_first:flags.3?true peer:InputPeer q:flags.1?string id:int offset:string limit:int = stories.StoryViewsList;
stories.getStoriesViews#28e16cc8 peer:InputPeer id:Vector<int> = stories.StoryViews;
stories.exportStoryLink#7b8def20 peer:InputPeer id:int = ExportedStoryLink;
stories.report#1923fa8c peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
stories.activateStealthMode#57bbd166 flags:# past:flags.0?true future:flags.1?true = Updates;

View File

@ -331,6 +331,7 @@
"stories.getPeerMaxIDs",
"stories.togglePeerStoriesHidden",
"stories.getPeerStories",
"stories.getStoriesViews",
"premium.getBoostsStatus",
"premium.getBoostersList",
"premium.applyBoost",

View File

@ -101,7 +101,7 @@ channel#aadfc8f flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5
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#c9d31138 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 = ChatFull;
channelFull#f2bcb6f 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 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 stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper = ChatFull;
channelFull#44c054a7 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 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 stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -114,7 +114,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
message#76bec211 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
message#1e4c8a69 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
@ -175,6 +175,7 @@ messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.
messageActionGiftCode#678c2e09 flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long = MessageAction;
messageActionGiveawayLaunch#332ba9ed = MessageAction;
messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction;
messageActionBoostApply#cc02aa6d boosts:int = 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;
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;
@ -980,6 +981,7 @@ channelAdminLogEventActionChangePeerColor#5796e780 prev_value:PeerColor new_valu
channelAdminLogEventActionChangeProfilePeerColor#5e477b25 prev_value:PeerColor new_value:PeerColor = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeWallpaper#31bb5d52 prev_value:WallPaper new_value:WallPaper = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStatus#3ea9feb1 prev_value:EmojiStatus new_value:EmojiStatus = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStickerSet#46d840ab prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@ -1275,7 +1277,7 @@ messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> use
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messageReplyHeader#afbc09db flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true quote:flags.9?true reply_to_msg_id:flags.4?int reply_to_peer_id:flags.0?Peer reply_from:flags.5?MessageFwdHeader reply_media:flags.8?MessageMedia reply_to_top_id:flags.1?int quote_text:flags.6?string quote_entities:flags.7?Vector<MessageEntity> quote_offset:flags.10?int = MessageReplyHeader;
messageReplyStoryHeader#9c98bfc1 user_id:long story_id:int = MessageReplyHeader;
messageReplyStoryHeader#e5af939 peer:Peer story_id:int = MessageReplyHeader;
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
@ -1554,7 +1556,7 @@ storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int forwards_co
storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true id:int date:int expire_date:int = StoryItem;
storyItem#af6365a1 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int fwd_from:flags.17?StoryFwdHeader expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
storyItem#79b26a24 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int from_id:flags.18?Peer fwd_from:flags.17?StoryFwdHeader expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories;
stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string peer_stories:Vector<PeerStories> chats:Vector<Chat> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories;
@ -1570,7 +1572,7 @@ stories.storyViewsList#59d78fc5 flags:# count:int views_count:int forwards_count
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;
inputReplyToStory#15b0f283 user_id:InputUser story_id:int = InputReplyTo;
inputReplyToStory#5881323a peer:InputPeer story_id:int = InputReplyTo;
exportedStoryLink#3fc9053b link:string = ExportedStoryLink;
@ -1627,7 +1629,7 @@ peerColor#b54b5acf flags:# color:flags.0?int background_emoji_id:flags.1?long =
help.peerColorSet#26219a58 colors:Vector<int> = help.PeerColorSet;
help.peerColorProfileSet#767d61eb palette_colors:Vector<int> bg_colors:Vector<int> story_colors:Vector<int> = help.PeerColorSet;
help.peerColorOption#ef8430ab flags:# hidden:flags.0?true color_id:int colors:flags.1?help.PeerColorSet dark_colors:flags.2?help.PeerColorSet channel_min_level:flags.3?int = help.PeerColorOption;
help.peerColorOption#adec6ebe flags:# hidden:flags.0?true color_id:int colors:flags.1?help.PeerColorSet dark_colors:flags.2?help.PeerColorSet channel_min_level:flags.3?int group_min_level:flags.4?int = help.PeerColorOption;
help.peerColorsNotModified#2ba1f5ce = help.PeerColors;
help.peerColors#f8ed08 hash:int colors:Vector<help.PeerColorOption> = help.PeerColors;
@ -2111,6 +2113,8 @@ channels.updateColor#d8aa3671 flags:# for_profile:flags.1?true channel:InputChan
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#83b70d97 channel:InputChannel = messages.Chats;
channels.updateEmojiStatus#f0d3e6a8 channel:InputChannel emoji_status:EmojiStatus = Updates;
channels.setBoostsToUnblockRestrictions#ad399cee channel:InputChannel boosts:int = Updates;
channels.setEmojiStickers#3cd930b7 channel:InputChannel stickerset:InputStickerSet = Bool;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;

View File

@ -53,212 +53,214 @@ $icons-map: (
"avatar-deleted-account": "\f116",
"avatar-saved-messages": "\f117",
"bold": "\f118",
"boost": "\f119",
"boostcircle": "\f11a",
"bot-command": "\f11b",
"bot-commands-filled": "\f11c",
"bots": "\f11d",
"bug": "\f11e",
"calendar-filter": "\f11f",
"calendar": "\f120",
"camera-add": "\f121",
"camera": "\f122",
"car": "\f123",
"card": "\f124",
"channel-filled": "\f125",
"channel": "\f126",
"channelviews": "\f127",
"chat-badge": "\f128",
"chats-badge": "\f129",
"check": "\f12a",
"close-circle": "\f12b",
"close-topic": "\f12c",
"close": "\f12d",
"cloud-download": "\f12e",
"collapse": "\f12f",
"colorize": "\f130",
"comments-sticker": "\f131",
"comments": "\f132",
"copy-media": "\f133",
"copy": "\f134",
"darkmode": "\f135",
"data": "\f136",
"delete-filled": "\f137",
"delete-left": "\f138",
"delete-user": "\f139",
"delete": "\f13a",
"document": "\f13b",
"double-badge": "\f13c",
"down": "\f13d",
"download": "\f13e",
"eats": "\f13f",
"edit": "\f140",
"email": "\f141",
"enter": "\f142",
"expand": "\f143",
"eye-closed-outline": "\f144",
"eye-closed": "\f145",
"eye-outline": "\f146",
"eye": "\f147",
"favorite-filled": "\f148",
"favorite": "\f149",
"file-badge": "\f14a",
"flag": "\f14b",
"folder-badge": "\f14c",
"folder": "\f14d",
"fontsize": "\f14e",
"forums": "\f14f",
"forward": "\f150",
"fullscreen": "\f151",
"gifs": "\f152",
"gift": "\f153",
"group-filled": "\f154",
"group": "\f155",
"grouped-disable": "\f156",
"grouped": "\f157",
"hand-stop": "\f158",
"hashtag": "\f159",
"heart-outline": "\f15a",
"heart": "\f15b",
"help": "\f15c",
"info-filled": "\f15d",
"info": "\f15e",
"install": "\f15f",
"italic": "\f160",
"key": "\f161",
"keyboard": "\f162",
"lamp": "\f163",
"language": "\f164",
"large-pause": "\f165",
"large-play": "\f166",
"link-badge": "\f167",
"link-broken": "\f168",
"link": "\f169",
"location": "\f16a",
"lock-badge": "\f16b",
"lock": "\f16c",
"logout": "\f16d",
"loop": "\f16e",
"mention": "\f16f",
"message-failed": "\f170",
"message-pending": "\f171",
"message-read": "\f172",
"message-succeeded": "\f173",
"message": "\f174",
"microphone-alt": "\f175",
"microphone": "\f176",
"monospace": "\f177",
"more-circle": "\f178",
"more": "\f179",
"mute": "\f17a",
"muted": "\f17b",
"my-notes": "\f17c",
"new-chat-filled": "\f17d",
"next": "\f17e",
"noise-suppression": "\f17f",
"non-contacts": "\f180",
"one-filled": "\f181",
"open-in-new-tab": "\f182",
"password-off": "\f183",
"pause": "\f184",
"permissions": "\f185",
"phone-discard-outline": "\f186",
"phone-discard": "\f187",
"phone": "\f188",
"photo": "\f189",
"pin-badge": "\f18a",
"pin-list": "\f18b",
"pin": "\f18c",
"pinned-chat": "\f18d",
"pinned-message": "\f18e",
"pip": "\f18f",
"play-story": "\f190",
"play": "\f191",
"poll": "\f192",
"premium": "\f193",
"previous": "\f194",
"privacy-policy": "\f195",
"quote-text": "\f196",
"quote": "\f197",
"readchats": "\f198",
"recent": "\f199",
"reload": "\f19a",
"remove": "\f19b",
"reopen-topic": "\f19c",
"replace": "\f19d",
"replies": "\f19e",
"reply-filled": "\f19f",
"reply": "\f1a0",
"revote": "\f1a1",
"save-story": "\f1a2",
"saved-messages": "\f1a3",
"schedule": "\f1a4",
"search": "\f1a5",
"select": "\f1a6",
"send-outline": "\f1a7",
"send": "\f1a8",
"settings-filled": "\f1a9",
"settings": "\f1aa",
"share-filled": "\f1ab",
"share-screen-outlined": "\f1ac",
"share-screen-stop": "\f1ad",
"share-screen": "\f1ae",
"sidebar": "\f1af",
"skip-next": "\f1b0",
"skip-previous": "\f1b1",
"smallscreen": "\f1b2",
"smile": "\f1b3",
"sort": "\f1b4",
"speaker-muted-story": "\f1b5",
"speaker-outline": "\f1b6",
"speaker-story": "\f1b7",
"speaker": "\f1b8",
"spoiler-disable": "\f1b9",
"spoiler": "\f1ba",
"sport": "\f1bb",
"stats": "\f1bc",
"stealth-future": "\f1bd",
"stealth-past": "\f1be",
"stickers": "\f1bf",
"stop-raising-hand": "\f1c0",
"stop": "\f1c1",
"story-caption": "\f1c2",
"story-expired": "\f1c3",
"story-priority": "\f1c4",
"story-reply": "\f1c5",
"strikethrough": "\f1c6",
"tag-add": "\f1c7",
"tag-crossed": "\f1c8",
"tag-filter": "\f1c9",
"tag-name": "\f1ca",
"tag": "\f1cb",
"timer": "\f1cc",
"transcribe": "\f1cd",
"truck": "\f1ce",
"unarchive": "\f1cf",
"underlined": "\f1d0",
"unlock-badge": "\f1d1",
"unlock": "\f1d2",
"unmute": "\f1d3",
"unpin": "\f1d4",
"unread": "\f1d5",
"up": "\f1d6",
"user-filled": "\f1d7",
"user-online": "\f1d8",
"user": "\f1d9",
"video-outlined": "\f1da",
"video-stop": "\f1db",
"video": "\f1dc",
"view-once": "\f1dd",
"voice-chat": "\f1de",
"volume-1": "\f1df",
"volume-2": "\f1e0",
"volume-3": "\f1e1",
"web": "\f1e2",
"webapp": "\f1e3",
"word-wrap": "\f1e4",
"zoom-in": "\f1e5",
"zoom-out": "\f1e6",
"boost-outline": "\f119",
"boost": "\f11a",
"boostcircle": "\f11b",
"boosts": "\f11c",
"bot-command": "\f11d",
"bot-commands-filled": "\f11e",
"bots": "\f11f",
"bug": "\f120",
"calendar-filter": "\f121",
"calendar": "\f122",
"camera-add": "\f123",
"camera": "\f124",
"car": "\f125",
"card": "\f126",
"channel-filled": "\f127",
"channel": "\f128",
"channelviews": "\f129",
"chat-badge": "\f12a",
"chats-badge": "\f12b",
"check": "\f12c",
"close-circle": "\f12d",
"close-topic": "\f12e",
"close": "\f12f",
"cloud-download": "\f130",
"collapse": "\f131",
"colorize": "\f132",
"comments-sticker": "\f133",
"comments": "\f134",
"copy-media": "\f135",
"copy": "\f136",
"darkmode": "\f137",
"data": "\f138",
"delete-filled": "\f139",
"delete-left": "\f13a",
"delete-user": "\f13b",
"delete": "\f13c",
"document": "\f13d",
"double-badge": "\f13e",
"down": "\f13f",
"download": "\f140",
"eats": "\f141",
"edit": "\f142",
"email": "\f143",
"enter": "\f144",
"expand": "\f145",
"eye-closed-outline": "\f146",
"eye-closed": "\f147",
"eye-outline": "\f148",
"eye": "\f149",
"favorite-filled": "\f14a",
"favorite": "\f14b",
"file-badge": "\f14c",
"flag": "\f14d",
"folder-badge": "\f14e",
"folder": "\f14f",
"fontsize": "\f150",
"forums": "\f151",
"forward": "\f152",
"fullscreen": "\f153",
"gifs": "\f154",
"gift": "\f155",
"group-filled": "\f156",
"group": "\f157",
"grouped-disable": "\f158",
"grouped": "\f159",
"hand-stop": "\f15a",
"hashtag": "\f15b",
"heart-outline": "\f15c",
"heart": "\f15d",
"help": "\f15e",
"info-filled": "\f15f",
"info": "\f160",
"install": "\f161",
"italic": "\f162",
"key": "\f163",
"keyboard": "\f164",
"lamp": "\f165",
"language": "\f166",
"large-pause": "\f167",
"large-play": "\f168",
"link-badge": "\f169",
"link-broken": "\f16a",
"link": "\f16b",
"location": "\f16c",
"lock-badge": "\f16d",
"lock": "\f16e",
"logout": "\f16f",
"loop": "\f170",
"mention": "\f171",
"message-failed": "\f172",
"message-pending": "\f173",
"message-read": "\f174",
"message-succeeded": "\f175",
"message": "\f176",
"microphone-alt": "\f177",
"microphone": "\f178",
"monospace": "\f179",
"more-circle": "\f17a",
"more": "\f17b",
"mute": "\f17c",
"muted": "\f17d",
"my-notes": "\f17e",
"new-chat-filled": "\f17f",
"next": "\f180",
"noise-suppression": "\f181",
"non-contacts": "\f182",
"one-filled": "\f183",
"open-in-new-tab": "\f184",
"password-off": "\f185",
"pause": "\f186",
"permissions": "\f187",
"phone-discard-outline": "\f188",
"phone-discard": "\f189",
"phone": "\f18a",
"photo": "\f18b",
"pin-badge": "\f18c",
"pin-list": "\f18d",
"pin": "\f18e",
"pinned-chat": "\f18f",
"pinned-message": "\f190",
"pip": "\f191",
"play-story": "\f192",
"play": "\f193",
"poll": "\f194",
"premium": "\f195",
"previous": "\f196",
"privacy-policy": "\f197",
"quote-text": "\f198",
"quote": "\f199",
"readchats": "\f19a",
"recent": "\f19b",
"reload": "\f19c",
"remove": "\f19d",
"reopen-topic": "\f19e",
"replace": "\f19f",
"replies": "\f1a0",
"reply-filled": "\f1a1",
"reply": "\f1a2",
"revote": "\f1a3",
"save-story": "\f1a4",
"saved-messages": "\f1a5",
"schedule": "\f1a6",
"search": "\f1a7",
"select": "\f1a8",
"send-outline": "\f1a9",
"send": "\f1aa",
"settings-filled": "\f1ab",
"settings": "\f1ac",
"share-filled": "\f1ad",
"share-screen-outlined": "\f1ae",
"share-screen-stop": "\f1af",
"share-screen": "\f1b0",
"sidebar": "\f1b1",
"skip-next": "\f1b2",
"skip-previous": "\f1b3",
"smallscreen": "\f1b4",
"smile": "\f1b5",
"sort": "\f1b6",
"speaker-muted-story": "\f1b7",
"speaker-outline": "\f1b8",
"speaker-story": "\f1b9",
"speaker": "\f1ba",
"spoiler-disable": "\f1bb",
"spoiler": "\f1bc",
"sport": "\f1bd",
"stats": "\f1be",
"stealth-future": "\f1bf",
"stealth-past": "\f1c0",
"stickers": "\f1c1",
"stop-raising-hand": "\f1c2",
"stop": "\f1c3",
"story-caption": "\f1c4",
"story-expired": "\f1c5",
"story-priority": "\f1c6",
"story-reply": "\f1c7",
"strikethrough": "\f1c8",
"tag-add": "\f1c9",
"tag-crossed": "\f1ca",
"tag-filter": "\f1cb",
"tag-name": "\f1cc",
"tag": "\f1cd",
"timer": "\f1ce",
"transcribe": "\f1cf",
"truck": "\f1d0",
"unarchive": "\f1d1",
"underlined": "\f1d2",
"unlock-badge": "\f1d3",
"unlock": "\f1d4",
"unmute": "\f1d5",
"unpin": "\f1d6",
"unread": "\f1d7",
"up": "\f1d8",
"user-filled": "\f1d9",
"user-online": "\f1da",
"user": "\f1db",
"video-outlined": "\f1dc",
"video-stop": "\f1dd",
"video": "\f1de",
"view-once": "\f1df",
"voice-chat": "\f1e0",
"volume-1": "\f1e1",
"volume-2": "\f1e2",
"volume-3": "\f1e3",
"web": "\f1e4",
"webapp": "\f1e5",
"word-wrap": "\f1e6",
"zoom-in": "\f1e7",
"zoom-out": "\f1e8",
);
.icon-active-sessions::before {
@ -333,12 +335,18 @@ $icons-map: (
.icon-bold::before {
content: map.get($icons-map, "bold");
}
.icon-boost-outline::before {
content: map.get($icons-map, "boost-outline");
}
.icon-boost::before {
content: map.get($icons-map, "boost");
}
.icon-boostcircle::before {
content: map.get($icons-map, "boostcircle");
}
.icon-boosts::before {
content: map.get($icons-map, "boosts");
}
.icon-bot-command::before {
content: map.get($icons-map, "bot-command");
}

Binary file not shown.

Binary file not shown.

View File

@ -23,8 +23,10 @@ export type FontIconName =
| 'avatar-deleted-account'
| 'avatar-saved-messages'
| 'bold'
| 'boost-outline'
| 'boost'
| 'boostcircle'
| 'boosts'
| 'bot-command'
| 'bot-commands-filled'
| 'bots'