From 033f8d479124b15682e4a22f75d1fde7675fadf5 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 1 Mar 2024 14:02:45 -0500 Subject: [PATCH] Layer 174 (#4277) --- src/api/gramjs/apiBuilders/appConfig.ts | 3 + src/api/gramjs/apiBuilders/chats.ts | 4 +- src/api/gramjs/apiBuilders/messages.ts | 26 +- src/api/gramjs/apiBuilders/stories.ts | 47 +- src/api/gramjs/gramjsBuilders/index.ts | 2 +- src/api/gramjs/methods/chats.ts | 13 +- src/api/gramjs/methods/stories.ts | 28 ++ src/api/types/chats.ts | 5 + src/api/types/messages.ts | 6 +- src/api/types/misc.ts | 1 + src/api/types/stories.ts | 2 + src/assets/font-icons/boost-outline.svg | 1 + src/assets/font-icons/boost.svg | 2 +- src/assets/font-icons/boosts.svg | 1 + src/components/common/Avatar.tsx | 4 +- src/components/common/ChatOrUserPicker.tsx | 21 +- src/components/common/Composer.tsx | 20 +- src/components/common/CustomEmojiPicker.tsx | 23 +- src/components/common/RecipientPicker.tsx | 13 +- src/components/common/StickerButton.scss | 1 + src/components/common/StickerButton.tsx | 4 +- src/components/common/StickerSet.tsx | 15 +- src/components/common/StickerSetModal.tsx | 7 +- src/components/common/helpers/boostInfo.ts | 3 + src/components/main/GameModal.tsx | 5 +- src/components/main/Main.tsx | 2 - src/components/middle/HeaderActions.tsx | 8 +- src/components/middle/HeaderMenuContainer.tsx | 21 +- src/components/middle/MiddleColumn.tsx | 5 +- .../middle/composer/StickerPicker.tsx | 54 +-- .../middle/composer/SymbolMenu.scss | 8 + src/components/middle/message/Message.tsx | 40 +- .../middle/message/_message-content.scss | 15 +- .../middle/message/helpers/webpageType.ts | 1 + .../modals/boost/BoostModal.module.scss | 6 + src/components/modals/boost/BoostModal.tsx | 78 ++-- src/components/right/GifSearch.tsx | 11 +- .../management/ManageGroupAdminRights.tsx | 66 ++- src/components/story/Story.tsx | 72 ++- src/components/story/StoryFooter.tsx | 4 +- src/components/story/StoryViewModal.tsx | 4 +- src/components/story/StoryViewer.module.scss | 21 +- src/config.ts | 3 +- src/global/actions/api/chats.ts | 21 +- src/global/actions/api/messages.ts | 2 +- src/global/actions/api/payments.ts | 108 ++++- src/global/actions/api/stories.ts | 56 +-- src/global/actions/api/symbols.ts | 23 - src/global/actions/ui/misc.ts | 4 +- src/global/helpers/chats.ts | 38 +- src/global/initialState.ts | 3 - src/global/selectors/messages.ts | 12 +- src/global/types.ts | 13 +- src/lib/gramjs/tl/AllTLObjects.js | 2 +- src/lib/gramjs/tl/api.d.ts | 52 ++- src/lib/gramjs/tl/apiTl.js | 15 +- src/lib/gramjs/tl/static/api.json | 1 + src/lib/gramjs/tl/static/api.tl | 16 +- src/styles/icons.scss | 420 +++++++++--------- src/styles/icons.woff | Bin 29268 -> 29464 bytes src/styles/icons.woff2 | Bin 24508 -> 24692 bytes src/types/icons/font.ts | 2 + 62 files changed, 883 insertions(+), 581 deletions(-) create mode 100644 src/assets/font-icons/boost-outline.svg create mode 100644 src/assets/font-icons/boosts.svg diff --git a/src/api/gramjs/apiBuilders/appConfig.ts b/src/api/gramjs/apiBuilders/appConfig.ts index 7e723b251..6ed51aa58 100644 --- a/src/api/gramjs/apiBuilders/appConfig.ts +++ b/src/api/gramjs/apiBuilders/appConfig.ts @@ -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, }; } diff --git a/src/api/gramjs/apiBuilders/chats.ts b/src/api/gramjs/apiBuilders/chats.ts index 2928aad4a..6afcec7fc 100644 --- a/src/api/gramjs/apiBuilders/chats.ts +++ b/src/api/gramjs/apiBuilders/chats.ts @@ -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({ isMin, hasPrivateLink, isSignaturesShown, @@ -93,6 +94,7 @@ function buildApiChatFieldsFromPeerEntity( maxStoryId, hasStories: Boolean(maxStoryId) && !storiesUnavailable, emojiStatus, + boostLevel, }); } diff --git a/src/api/gramjs/apiBuilders/messages.ts b/src/api/gramjs/apiBuilders/messages.ts index 474db0ace..2f3139be8 100644 --- a/src/api/gramjs/apiBuilders/messages.ts +++ b/src/api/gramjs/apiBuilders/messages.ts @@ -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({ 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, }; } diff --git a/src/api/gramjs/apiBuilders/stories.ts b/src/api/gramjs/apiBuilders/stories.ts index 82867cd42..c5b216c97 100644 --- a/src/api/gramjs/apiBuilders/stories.ts +++ b/src/api/gramjs/apiBuilders/stories.ts @@ -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({ 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({ + 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 { diff --git a/src/api/gramjs/gramjsBuilders/index.ts b/src/api/gramjs/gramjsBuilders/index.ts index 9a2450e50..08aff7d95 100644 --- a/src/api/gramjs/gramjsBuilders/index.ts +++ b/src/api/gramjs/gramjsBuilders/index.ts @@ -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, }); } diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index 36cd15572..1bafd820f 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -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; membersCount?: number; @@ -519,6 +520,7 @@ async function getFullChatInfo(chatId: string): Promise buildApiChatFromPreview(chat)).filter(Boolean); return { fullInfo: { @@ -536,6 +538,7 @@ async function getFullChatInfo(chatId: string): Promise 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, diff --git a/src/api/gramjs/methods/stories.ts b/src/api/gramjs/methods/stories.ts index 32ee0886d..b74c75b47 100644 --- a/src/api/gramjs/methods/stories.ts +++ b/src/api/gramjs/methods/stories.ts @@ -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), diff --git a/src/api/types/chats.ts b/src/api/types/chats.ts index 5575da6c5..bc7b4f8fc 100644 --- a/src/api/types/chats.ts +++ b/src/api/types/chats.ts @@ -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 { diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index da523d379..013a6a24b 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -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 { diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index 931f20647..e9fcb78fb 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -197,6 +197,7 @@ export interface ApiAppConfig { storyExpirePeriod: number; storyViewersExpirePeriod: number; storyChangelogUserId: string; + groupTranscribeLevelMin?: number; } export interface ApiConfig { diff --git a/src/api/types/stories.ts b/src/api/types/stories.ts index 760725be1..02a1eea06 100644 --- a/src/api/types/stories.ts +++ b/src/api/types/stories.ts @@ -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; diff --git a/src/assets/font-icons/boost-outline.svg b/src/assets/font-icons/boost-outline.svg new file mode 100644 index 000000000..861f4e1d2 --- /dev/null +++ b/src/assets/font-icons/boost-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/boost.svg b/src/assets/font-icons/boost.svg index 062c8e599..228eb0cca 100644 --- a/src/assets/font-icons/boost.svg +++ b/src/assets/font-icons/boost.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/boosts.svg b/src/assets/font-icons/boosts.svg new file mode 100644 index 000000000..14108b1d1 --- /dev/null +++ b/src/assets/font-icons/boosts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/common/Avatar.tsx b/src/components/common/Avatar.tsx index e70e9af50..e4b4f4b18 100644 --- a/src/components/common/Avatar.tsx +++ b/src/components/common/Avatar.tsx @@ -207,6 +207,8 @@ const Avatar: FC = ({ 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 = ({ 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', diff --git a/src/components/common/ChatOrUserPicker.tsx b/src/components/common/ChatOrUserPicker.tsx index a1d6b04c8..e42c5680d 100644 --- a/src/components/common/ChatOrUserPicker.tsx +++ b/src/components/common/ChatOrUserPicker.tsx @@ -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; isOpen: boolean; searchPlaceholder: string; search: string; @@ -55,7 +54,6 @@ const ChatOrUserPicker: FC = ({ isOpen, currentUserId, chatOrUserIds, - chatsById, search, searchPlaceholder, className, @@ -89,7 +87,11 @@ const ChatOrUserPicker: FC = ({ 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 = ({ 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 = ({ : 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 = ({ 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 = ({ }, '.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 }); diff --git a/src/components/common/Composer.tsx b/src/components/common/Composer.tsx index e7678d724..9d7412a36 100644 --- a/src/components/common/Composer.tsx +++ b/src/components/common/Composer.tsx @@ -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; botCommands?: ApiBotCommand[] | false; botMenuButton?: ApiBotMenuButton; - chatBotCommands?: ApiBotCommand[]; sendAsUser?: ApiUser; sendAsChat?: ApiChat; sendAsId?: string; @@ -293,6 +291,7 @@ const Composer: FC = ({ messageListType, draft, chat, + chatFullInfo, replyToTopic, isForCurrentMessageList, isCurrentUserPremium, @@ -312,7 +311,6 @@ const Composer: FC = ({ withScheduledButton, stickersForEmoji, customEmojiForEmoji, - groupChatMembers, topInlineBotIds, currentUserId, currentUser, @@ -326,7 +324,6 @@ const Composer: FC = ({ inlineBots, isInlineBotLoading, botCommands, - chatBotCommands, sendAsUser, sendAsChat, sendAsId, @@ -405,6 +402,9 @@ const Composer: FC = ({ 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 = ({ 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 = ({ }, [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( 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( 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, diff --git a/src/components/common/CustomEmojiPicker.tsx b/src/components/common/CustomEmojiPicker.tsx index 63e8033e4..5448298ae 100644 --- a/src/components/common/CustomEmojiPicker.tsx +++ b/src/components/common/CustomEmojiPicker.tsx @@ -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; 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 = ({ @@ -112,6 +111,7 @@ const CustomEmojiPicker: FC = ({ selectedReactionIds, recentStatusEmojis, stickerSetsById, + chatEmojiSetId, topReactions, recentReactions, availableReactions, @@ -250,7 +250,12 @@ const CustomEmojiPicker: FC = ({ }); } - 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 = ({ }, [ 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 = ({ onClick={() => selectStickerSet(isRecent ? 0 : index)} > {isRecent ? ( - + ) : ( = ({ {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 ( = ({ 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( } = 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( defaultStatusIconsId: global.defaultStatusIconsId, topReactions: isReactionPicker ? topReactions : undefined, recentReactions: isReactionPicker ? recentReactions : undefined, + chatEmojiSetId: chatFullInfo?.emojiSet?.id, availableReactions: isReactionPicker ? availableReactions : undefined, defaultTagReactions: isReactionPicker ? defaultTags : undefined, }; diff --git a/src/components/common/RecipientPicker.tsx b/src/components/common/RecipientPicker.tsx index 38fd95a97..ea021444b 100644 --- a/src/components/common/RecipientPicker.tsx +++ b/src/components/common/RecipientPicker.tsx @@ -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; activeListIds?: string[]; archivedListIds?: string[]; pinnedIds?: string[]; @@ -45,7 +44,6 @@ type StateProps = { const RecipientPicker: FC = ({ isOpen, currentUserId, - chatsById, activeListIds, archivedListIds, pinnedIds, @@ -71,6 +69,8 @@ const RecipientPicker: FC = ({ // 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 = ({ 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 = ({ ]), 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 = ({ isOpen={isOpen} className={className} chatOrUserIds={renderingIds} - chatsById={chatsById} searchPlaceholder={searchPlaceholder} search={search} onSearchChange={setSearch} @@ -114,7 +113,6 @@ export default memo(withGlobal( (global): StateProps => { const { chats: { - byId: chatsById, listIds, orderedPinnedIds, }, @@ -122,7 +120,6 @@ export default memo(withGlobal( } = global; return { - chatsById, activeListIds: listIds.active, archivedListIds: listIds.archived, pinnedIds: orderedPinnedIds.active, diff --git a/src/components/common/StickerButton.scss b/src/components/common/StickerButton.scss index 846f3991a..4a7894492 100644 --- a/src/components/common/StickerButton.scss +++ b/src/components/common/StickerButton.scss @@ -32,6 +32,7 @@ vertical-align: bottom; align-self: center; justify-self: center; + font-weight: 500; } .sticker-locked { diff --git a/src/components/common/StickerButton.tsx b/src/components/common/StickerButton.tsx index 47dccd20b..bafca49f7 100644 --- a/src/components/common/StickerButton.tsx +++ b/src/components/common/StickerButton.tsx @@ -38,6 +38,7 @@ type OwnProps = { canViewSet?: boolean; isSelected?: boolean; isCurrentUserPremium?: boolean; + shouldIgnorePremium?: boolean; sharedCanvasRef?: React.RefObject; withTranslucentThumb?: boolean; forcePlayback?: boolean; @@ -76,6 +77,7 @@ const StickerButton = = ({ withDefaultTopicIcon, selectedReactionIds, withDefaultStatusIcon, + isChatEmojiSet, + isChatStickerSet, isTranslucent, noContextMenus, forcePlayback, @@ -224,10 +228,11 @@ const StickerSet: FC = ({ } }, [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 = ({ 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 = ({

{isLocked && } {stickerSet.title} + {(isChatEmojiSet || isChatStickerSet) && ( + {lang(isChatEmojiSet ? 'GroupEmoji' : 'GroupStickers')} + )} {withAddSetButton && Boolean(stickerSet.stickers) && ( {lang(isEmoji ? 'EmojiCount' : 'Stickers', stickerSet.stickers.length, 'i')} @@ -360,6 +368,7 @@ const StickerSet: FC = ({ canViewSet noContextMenu={noContextMenus} isCurrentUserPremium={isCurrentUserPremium} + shouldIgnorePremium={isChatEmojiSet} sharedCanvasRef={canvasRef} withTranslucentThumb={isTranslucent} onClick={onStickerSelect} diff --git a/src/components/common/StickerSetModal.tsx b/src/components/common/StickerSetModal.tsx index 82b06f62a..7f6123b91 100644 --- a/src/components/common/StickerSetModal.tsx +++ b/src/components/common/StickerSetModal.tsx @@ -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( 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); diff --git a/src/components/common/helpers/boostInfo.ts b/src/components/common/helpers/boostInfo.ts index 466078074..a0fe6c27b 100644 --- a/src/components/common/helpers/boostInfo.ts +++ b/src/components/common/helpers/boostInfo.ts @@ -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, }; } diff --git a/src/components/main/GameModal.tsx b/src/components/main/GameModal.tsx index 640fd7bb7..b0ea54353 100644 --- a/src/components/main/GameModal.tsx +++ b/src/components/main/GameModal.tsx @@ -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( (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, diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 936622f37..cfc8a4f36 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -268,7 +268,6 @@ const Main: FC = ({ loadDefaultTagReactions, loadFeaturedEmojiStickers, setIsElectronUpdateAvailable, - loadPremiumSetStickers, loadAuthorizations, loadPeerColors, loadSavedReactionTags, @@ -357,7 +356,6 @@ const Main: FC = ({ if (isMasterTab && isCurrentUserPremium) { loadDefaultStatusIcons(); loadRecentEmojiStatuses(); - loadPremiumSetStickers(); } }, [isCurrentUserPremium, isMasterTab]); diff --git a/src/components/middle/HeaderActions.tsx b/src/components/middle/HeaderActions.tsx index af7057877..3efff303a 100644 --- a/src/components/middle/HeaderActions.tsx +++ b/src/components/middle/HeaderActions.tsx @@ -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 = ({ canMute, canViewStatistics, canViewBoosts, + canShowBoostModal, canLeave, canEnterVoiceChat, canCreateVoiceChat, @@ -433,6 +435,7 @@ const HeaderActions: FC = ({ canMute={canMute} canViewStatistics={canViewStatistics} canViewBoosts={canViewBoosts} + canShowBoostModal={canShowBoostModal} canLeave={canLeave} canEnterVoiceChat={canEnterVoiceChat} canCreateVoiceChat={canCreateVoiceChat} @@ -456,6 +459,7 @@ export default memo(withGlobal( }): 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( 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( && (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( canMute, canViewStatistics, canViewBoosts, + canShowBoostModal, canLeave, canEnterVoiceChat, canCreateVoiceChat, diff --git a/src/components/middle/HeaderMenuContainer.tsx b/src/components/middle/HeaderMenuContainer.tsx index 170ff6e92..beda5747a 100644 --- a/src/components/middle/HeaderMenuContainer.tsx +++ b/src/components/middle/HeaderMenuContainer.tsx @@ -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 = ({ isBot, isChatWithSelf, savedDialog, + canShowBoostModal, onJoinRequestsClick, onSubscribeChannel, onSearchClick, @@ -195,6 +197,7 @@ const HeaderMenuContainer: FC = ({ blockUser, unblockUser, setViewForumAsMessages, + openBoostModal, } = getActions(); const { isMobile } = useAppLayout(); @@ -346,8 +349,12 @@ const HeaderMenuContainer: FC = ({ }); const handleBoostClick = useLastCallback(() => { - openBoostStatistics({ chatId }); - setShouldCloseFast(!isRightColumnShown); + if (canViewBoosts) { + openBoostStatistics({ chatId }); + setShouldCloseFast(!isRightColumnShown); + } else { + openBoostModal({ chatId }); + } closeMenu(); }); @@ -522,6 +529,14 @@ const HeaderMenuContainer: FC = ({ {lang(isChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')} )} + {canShowBoostModal && !canViewBoosts && ( + + {lang(isChannel ? 'BoostingBoostChannelMenu' : 'BoostingBoostGroupMenu')} + + )} {canAddContact && ( = ({ )} {canViewBoosts && ( {lang('Boosts')} diff --git a/src/components/middle/MiddleColumn.tsx b/src/components/middle/MiddleColumn.tsx index b7055c1c3..12117b4f4 100644 --- a/src/components/middle/MiddleColumn.tsx +++ b/src/components/middle/MiddleColumn.tsx @@ -763,10 +763,11 @@ export default memo(withGlobal( 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( 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 diff --git a/src/components/middle/composer/StickerPicker.tsx b/src/components/middle/composer/StickerPicker.tsx index a2b27d7d4..e56169879 100644 --- a/src/components/middle/composer/StickerPicker.tsx +++ b/src/components/middle/composer/StickerPicker.tsx @@ -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; chatStickerSetId?: string; addedSetIds?: string[]; @@ -86,7 +83,6 @@ const StickerPicker: FC = ({ canSendStickers, recentStickers, favoriteStickers, - premiumStickers, addedSetIds, stickerSetsById, chatStickerSetId, @@ -140,8 +136,6 @@ const StickerPicker: FC = ({ 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 = ({ }); } - 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 = ({ 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 = ({ // eslint-disable-next-line react/jsx-no-bind onClick={() => selectStickerSet(index)} > - {stickerSet.id === PREMIUM_STICKER_SET_ID ? ( - - ) : stickerSet.id === RECENT_SYMBOL_SET_ID ? ( + {stickerSet.id === RECENT_SYMBOL_SET_ID ? ( ) : stickerSet.id === FAVORITE_SYMBOL_SET_ID ? ( @@ -374,6 +337,7 @@ const StickerPicker: FC = ({ isSavedMessages={isSavedMessages} isCurrentUserPremium={isCurrentUserPremium} isTranslucent={isTranslucent} + isChatStickerSet={stickerSet.id === chatStickerSetId} onStickerSelect={handleStickerSelect} onStickerUnfave={handleStickerUnfave} onStickerFave={handleStickerFave} @@ -393,7 +357,6 @@ export default memo(withGlobal( added, recent, favorite, - premiumSet, } = global.stickers; const isSavedMessages = selectIsChatWithSelf(global, chatId); @@ -404,7 +367,6 @@ export default memo(withGlobal( chat, recentStickers: recent.stickers, favoriteStickers: favorite.stickers, - premiumStickers: premiumSet.stickers, stickerSetsById: setsById, addedSetIds: added.setIds, canAnimate: selectShouldLoopStickers(global), diff --git a/src/components/middle/composer/SymbolMenu.scss b/src/components/middle/composer/SymbolMenu.scss index 215217b82..2ab88d080 100644 --- a/src/components/middle/composer/SymbolMenu.scss +++ b/src/components/middle/composer/SymbolMenu.scss @@ -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; } diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index 718a369b8..fc31a156f 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -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; + canTranscribeVoice?: boolean; }; type MetaPosition = @@ -394,7 +397,9 @@ const Message: FC = ({ isConnected, getIsMessageListReady, shouldWarnAboutSvg, + senderBoosts, tags, + canTranscribeVoice, onPinnedIntersectionChange, }) => { const { @@ -655,7 +660,7 @@ const Message: FC = ({ } = 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 = ({ ); useEnsureStory( - storyReplyUserId || chatId, + storyReplyPeerId || chatId, storyReplyId, replyStory, ); @@ -1132,7 +1137,7 @@ const Message: FC = ({ isTranscriptionError={isTranscriptionError} canDownload={!isProtected} onHideTranscription={setTranscriptionHidden} - canTranscribe={isPremium && !hasTtl} + canTranscribe={canTranscribeVoice && !hasTtl} /> )} {document && ( @@ -1312,6 +1317,7 @@ const Message: FC = ({ )} +

{forwardInfo?.isLinkedChannelPost ? ( {lang('DiscussChannel')} ) : message.forwardInfo?.postAuthorTitle && isGroup && asForwarded ? ( @@ -1325,6 +1331,12 @@ const Message: FC = ({ )} ) : undefined} + {Boolean(senderBoosts) && ( + + 1 ? 'boosts' : 'boost'} /> + {senderBoosts > 1 ? senderBoosts : undefined} + + )}
); } @@ -1499,7 +1511,7 @@ export default memo(withGlobal( 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( 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( 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( 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( isResizingContainer, focusedQuote, }), + senderBoosts, tags: global.savedReactionTags?.byKey, + canTranscribeVoice, }; }, )(Message)); diff --git a/src/components/middle/message/_message-content.scss b/src/components/middle/message/_message-content.scss index d908d2725..128b7311a 100644 --- a/src/components/middle/message/_message-content.scss +++ b/src/components/middle/message/_message-content.scss @@ -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 { diff --git a/src/components/middle/message/helpers/webpageType.ts b/src/components/middle/message/helpers/webpageType.ts index 6cc06711c..fbcb56b33 100644 --- a/src/components/middle/message/helpers/webpageType.ts +++ b/src/components/middle/message/helpers/webpageType.ts @@ -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; diff --git a/src/components/modals/boost/BoostModal.module.scss b/src/components/modals/boost/BoostModal.module.scss index 48fca70a6..8d881d205 100644 --- a/src/components/modals/boost/BoostModal.module.scss +++ b/src/components/modals/boost/BoostModal.module.scss @@ -27,6 +27,12 @@ .description { padding: 0.5rem; line-height: 1.25; + text-align: center; + text-wrap: balance; +} + +.bold { + font-weight: 500; } .chip { diff --git a/src/components/modals/boost/BoostModal.tsx b/src/components/modals/boost/BoostModal.tsx index e7c2cd0a4..3de495118 100644 --- a/src/components/modals/boost/BoostModal.tsx +++ b/src/components/modals/boost/BoostModal.tsx @@ -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 && ( +
+ {lang('ChannelBoost.YouBoostedChannelText', chatTitle)} +
+ )}
{renderText(descriptionText, ['simple_markdown', 'emoji'])}
@@ -239,7 +249,7 @@ const BoostModal = ({ {canBoostMore ? ( <> - {lang(isBoosted && canBoostMore ? 'BoostingBoostAgain' : 'ChannelBoost.BoostChannel')} + {lang(isChannel ? 'ChannelBoost.BoostChannel' : 'GroupBoost.BoostGroup')} ) : lang('OK')} @@ -269,7 +279,7 @@ const BoostModal = ({ >
- +
@@ -334,12 +344,14 @@ function areAllBoostsInChannel(myBoosts: ApiMyBoost[], chatId: string) { export default memo(withGlobal( (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), }; }, diff --git a/src/components/right/GifSearch.tsx b/src/components/right/GifSearch.tsx index cf9de0f90..7900c08b7 100644 --- a/src/components/right/GifSearch.tsx +++ b/src/components/right/GifSearch.tsx @@ -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 = ({ query, results, chat, + chatFullInfo, isChatWithBot, canScheduleUntilOnline, isSavedMessages, @@ -74,7 +77,7 @@ const GifSearch: FC = ({ 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, diff --git a/src/components/right/management/ManageGroupAdminRights.tsx b/src/components/right/management/ManageGroupAdminRights.tsx index c416aad40..e1ddbc92e 100644 --- a/src/components/right/management/ManageGroupAdminRights.tsx +++ b/src/components/right/management/ManageGroupAdminRights.tsx @@ -260,42 +260,36 @@ const ManageGroupAdminRights: FC = ({ onChange={handlePermissionChange} />
- {isChannel && ( -
- -
- )} - {isChannel && ( -
- -
- )} - {isChannel && ( -
- -
- )} +
+ +
+
+ +
+
+ +
{!isChannel && (
{ - 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 = { @@ -638,11 +649,24 @@ function Story({ onClick={forwardSender ? handleForwardPeerClick : undefined} > - + {renderText(forwardSenderTitle)} )} + {fromPeer && ( + + + + {renderText(getSenderTitle(lang, fromPeer) || '')} + + + )} {story && 'date' in story && ( {formatRelativeTime(lang, serverTime, story.date)} )} @@ -693,17 +717,25 @@ function Story({ > {canCopyLink && {lang('CopyLink')}} {canPinToProfile && ( - {lang('StorySave')} + + {lang(isUserStory ? 'StorySave' : 'SaveToPosts')} + )} {canUnpinFromProfile && ( - {lang('ArchiveStory')} + + {lang(isUserStory ? 'ArchiveStory' : 'RemoveFromPosts')} + )} {canDownload && ( {lang('lng_media_download')} )} - {lang('StealthMode')} + {!isOut && isUserStory && ( + + {lang('StealthMode')} + + )} {!isOut && {lang('lng_report_story')}} {isOut && {lang('Delete')}} @@ -818,7 +850,7 @@ function Story({
{shouldShowFooter && ( - + )} {shouldRenderCaptionBackdrop && (
((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, diff --git a/src/components/story/StoryFooter.tsx b/src/components/story/StoryFooter.tsx index 2b9a74715..4a35ccd41 100644 --- a/src/components/story/StoryFooter.tsx +++ b/src/components/story/StoryFooter.tsx @@ -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) && ( { if (!story?.id || nextOffset === undefined) return; - loadStoryViews({ + loadStoryViewList({ peerId: story.peerId, storyId: story.id, offset: nextOffset, diff --git a/src/components/story/StoryViewer.module.scss b/src/components/story/StoryViewer.module.scss index be7a6613d..6ceed13dd 100644 --- a/src/components/story/StoryViewer.module.scss +++ b/src/components/story/StoryViewer.module.scss @@ -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; + } +} diff --git a/src/config.ts b/src/config.ts index 4ec977dc9..52b9213c6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -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 = /]+alt="([^"]+)"(?![^>]*data-document-id)[^>]*>/gm; diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index 9485bee9a..7c1d84948 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -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( } 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( }); } + 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; } diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index 8842f6925..a77438858 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -312,7 +312,7 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType => const storyReplyInfo = isStoryReply ? { type: 'story', - userId: storyPeerId!, + peerId: storyPeerId!, storyId: storyId!, } satisfies ApiInputStoryReplyInfo : undefined; diff --git a/src/global/actions/api/payments.ts b/src/global/actions/api/payments.ts index d316f1fdb..c572d348c 100644 --- a/src/global/actions/api/payments.ts +++ b/src/global/actions/api/payments.ts @@ -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( addActionHandler('openBoostModal', async (global, actions, payload): Promise => { 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 = 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 = }, 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 => { diff --git a/src/global/actions/api/stories.ts b/src/global/actions/api/stories.ts index 92997b0ca..a1971a3fa 100644 --- a/src/global/actions/api/stories.ts +++ b/src/global/actions/api/stories.ts @@ -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 => { + 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 => { 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 '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); }); diff --git a/src/global/actions/api/symbols.ts b/src/global/actions/api/symbols.ts index 94fd44119..d36d91dbb 100644 --- a/src/global/actions/api/symbols.ts +++ b/src/global/actions/api/symbols.ts @@ -149,29 +149,6 @@ addActionHandler('loadPremiumStickers', async (global): Promise => { setGlobal(global); }); -addActionHandler('loadPremiumSetStickers', async (global): Promise => { - 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 => { const { hash } = global.stickers.greeting || {}; diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index c53590139..dfef467f1 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -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, diff --git a/src/global/helpers/chats.ts b/src/global/helpers/chats.ts index eb9418607..40f9cf9d8 100644 --- a/src/global/helpers/chats.ts +++ b/src/global/helpers/chats.ts @@ -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), }; } diff --git a/src/global/initialState.ts b/src/global/initialState.ts index ebc3dd878..41320efc4 100644 --- a/src/global/initialState.ts +++ b/src/global/initialState.ts @@ -172,9 +172,6 @@ export const INITIAL_GLOBAL_STATE: GlobalState = { premium: { stickers: [], }, - premiumSet: { - stickers: [], - }, featured: { setIds: [], }, diff --git a/src/global/selectors/messages.ts b/src/global/selectors/messages.ts index f6199fc8c..31cf9a760 100644 --- a/src/global/selectors/messages.ts +++ b/src/global/selectors/messages.ts @@ -591,6 +591,8 @@ export function selectAllowedMessageActions(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(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(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( 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; diff --git a/src/global/types.ts b/src/global/types.ts index 0e7b38141..be1fbbdc8 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -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; diff --git a/src/lib/gramjs/tl/AllTLObjects.js b/src/lib/gramjs/tl/AllTLObjects.js index fbb01803d..076798811 100644 --- a/src/lib/gramjs/tl/AllTLObjects.js +++ b/src/lib/gramjs/tl/AllTLObjects.js @@ -1,6 +1,6 @@ const api = require('./api'); -const LAYER = 173; +const LAYER = 174; const tlobjects = {}; for (const tl of Object.values(api)) { diff --git a/src/lib/gramjs/tl/api.d.ts b/src/lib/gramjs/tl/api.d.ts index 8ac26e9ad..e1fee0bf5 100644 --- a/src/lib/gramjs/tl/api.d.ts +++ b/src/lib/gramjs/tl/api.d.ts @@ -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 {}; export class PeerColors extends VirtualClass<{ @@ -15506,6 +15530,20 @@ namespace Api { channel: Api.TypeInputChannel; emojiStatus: Api.TypeEmojiStatus; }; + export class SetBoostsToUnblockRestrictions extends Request, Api.TypeUpdates> { + channel: Api.TypeInputChannel; + boosts: int; + }; + export class SetEmojiStickers extends Request, 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 diff --git a/src/lib/gramjs/tl/apiTl.js b/src/lib/gramjs/tl/apiTl.js index b15dd8d3a..4f7822ef8 100644 --- a/src/lib/gramjs/tl/apiTl.js +++ b/src/lib/gramjs/tl/apiTl.js @@ -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 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 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 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 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 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 groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector 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 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 groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector 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 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 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 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 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 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 chats:Vector users:Vector = 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 chats:Vector users:Vector = messages.MessageViews; messages.discussionMessage#a6341782 flags:# messages:Vector max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector users:Vector = 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 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 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 reactions_count:flags.4?int recent_viewers:flags.0?Vector = 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 media:MessageMedia media_areas:flags.14?Vector privacy:flags.2?Vector 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 media:MessageMedia media_areas:flags.14?Vector privacy:flags.2?Vector 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 chats:Vector users:Vector stealth_mode:StoriesStealthMode = stories.AllStories; stories.stories#5dd8c3c8 count:int stories:Vector chats:Vector users:Vector = 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 chats:Vector users:Vector next_offset:flags.0?string = stories.StoryViewsList; stories.storyViews#de9eed1d views:Vector users:Vector = 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 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 n peerColor#b54b5acf flags:# color:flags.0?int background_emoji_id:flags.1?long = PeerColor; help.peerColorSet#26219a58 colors:Vector = help.PeerColorSet; help.peerColorProfileSet#767d61eb palette_colors:Vector bg_colors:Vector story_colors:Vector = 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.PeerColors; storyReaction#6090d6d5 peer_id:Peer date:int reaction:Reaction = StoryReaction; @@ -1538,6 +1540,7 @@ stories.getStoriesByID#5774ca74 peer:InputPeer id:Vector = stories.Stories; stories.readStories#a556dac8 peer:InputPeer max_id:int = Vector; stories.incrementStoryViews#b2028afb peer:InputPeer id:Vector = 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 = stories.StoryViews; stories.exportStoryLink#7b8def20 peer:InputPeer id:int = ExportedStoryLink; stories.report#1923fa8c peer:InputPeer id:Vector reason:ReportReason message:string = Bool; stories.activateStealthMode#57bbd166 flags:# past:flags.0?true future:flags.1?true = Updates; diff --git a/src/lib/gramjs/tl/static/api.json b/src/lib/gramjs/tl/static/api.json index ec368aa9b..dafbc6975 100644 --- a/src/lib/gramjs/tl/static/api.json +++ b/src/lib/gramjs/tl/static/api.json @@ -331,6 +331,7 @@ "stories.getPeerMaxIDs", "stories.togglePeerStoriesHidden", "stories.getPeerStories", + "stories.getStoriesViews", "premium.getBoostsStatus", "premium.getBoostersList", "premium.applyBoost", diff --git a/src/lib/gramjs/tl/static/api.tl b/src/lib/gramjs/tl/static/api.tl index 73bd22a88..dbdfe06f9 100644 --- a/src/lib/gramjs/tl/static/api.tl +++ b/src/lib/gramjs/tl/static/api.tl @@ -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 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 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 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 groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector 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 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 groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector 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 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 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 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 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 chats:Vector use messages.discussionMessage#a6341782 flags:# messages:Vector max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector users:Vector = 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 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 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 media:MessageMedia media_areas:flags.14?Vector privacy:flags.2?Vector 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 media:MessageMedia media_areas:flags.14?Vector privacy:flags.2?Vector 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 chats:Vector users:Vector 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 users:Vector = 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 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 = help.PeerColorSet; help.peerColorProfileSet#767d61eb palette_colors:Vector bg_colors:Vector story_colors:Vector = 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.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; diff --git a/src/styles/icons.scss b/src/styles/icons.scss index 2571103a1..e1e9b47ec 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -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"); } diff --git a/src/styles/icons.woff b/src/styles/icons.woff index b342279b01a4d6cc8eeb6f76702cf9a69b4bbfbf..666ecc276aba6aa2ecc28eaa7db3283801ec29dd 100644 GIT binary patch delta 29015 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 OGo7`!_df#(&&B|WXa`vU delta 28720 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 diff --git a/src/styles/icons.woff2 b/src/styles/icons.woff2 index 871d361145f6972e408f8e0fbe62f1f992f53689..f7455e16c3e5920c245f3be154c9494340dcde90 100644 GIT binary patch literal 24692 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 WYRoD8gUSEBtM0>Pgj#w&@%;}Q9xxC9 literal 24508 zcmbq)LvSt(kZo++wr^}FUu@g9ZQHhO+}O5l+rCNWpPJQMzq4snFS=KK>U6ccyf`xu zFwlR{?*@YXU%TP*pKtwtx&Mv-U*M@g7yxCv!35pn1!WoovxAU(?&-kT^$~WOl1W zJ&D>i9ExJv{8dJ*=AkECBV?h!ks`>+I>cfS>k3zEy8n&0sDuR zi_LT4{MhqQVQ+3TWA;f8D4LJapc1VgCdC1EnbLJBM{_8-^SkT8&Q$l7!E!NEM_gOn z@^*1x_OhUc)poIIDKP|N-i4f~IuwMyC~ZM_KrzfuC^Ra&&~@+^V_ILd;=GJB+@wi7=0SPW3P{;Vq7%4Ohm`UdDZ!#roG0-6o zP{EHsM8+>b@MkK8&JGuyu~82XnGepU-JJ}g4c8lB1d%5@PfpSA`LCZ7z7(Mdp%FMC z=j&_rvAxmr^;g?!tw-970XIt6@Op&e6-F#L#o?qTME7r@p;ych!f0(U$E*ZQUkZ&{ z5|z~RQ|?#xE>Lt)i^Sw}8OLsRnz-g~#QUD#<*&4|rIg$Da}vG?Oghm$lvnhbhCe+b zOBSAN=>>$y5^n>SC1c9|pWpP%4qNiQMsI&VA41WFHj1WsAh|3#`_9kB)%(JhJl)w$ z-MLw8?GEP)@CEY3ybFwa{k~O~_ThOe=!6hW$!WYDdMqs?4awhxKcpqhIi6h zks9USc#Lr`O^#+2LSd*Vc&NB*Ut1!7GyPKpuZ&q0$Ruza>72@#JXu&-Ioj*v6uL=- z$RhBKyBR?&#@`liTGt5u^Z0N`j;|ZL;wT~h%FQKU=J2(!dW_ZzO^7Zun|1esDM8#d zgZS^X$9K%a7$D;@8u&h`@_+gJm}4lZi2!T#0#SiR1q2!l96Grb&ELt1LL@Mg9of}c zuQ)>8ukM0L*l-XjVBxj!hfAR0h!89F3R0nfMlcn|z*e?2GC&n`N;Y>>Rd4?6g{`&d z3dAgR*KGOatbKRyAqF7D6ky3Epv^SkF-9cSRAkv@q}}|@BMec@DbdtRQQi6ftB?P` z9wZm(7$>XjHtPsSE%jDy1!k}O^kC&qI9DKnZn-iV3d#Zmn82w;4WkrNjL^!#$f?CF zCl%71Qp!QlD@9GK6jH5J$|2Y)#cVGX(!G|;f#EGhjkD%c%vj68=@*!5=V@vexNYYt zY!}#X<{54l`0?k7@fZH&&Qs?uaOuvI>n^bE%+v2I@bS%)@-49R%+vNP@c7PC`Yv#M z&oh262oPHV!IucJW`xZv;D`@dg2k37vSy9WYv2HmEJ0*TBv~`d=2f!9mn

N;Fxs zPv>>A08f^{^d({~8ENxMTH;fd;I$>HEm^DcT3UcBOHkVq*_O<U z*Ll57z}E#3eu;=nM%=t2xA@ovSZ;~3OV;eXCO6>h0z|h&+9k7YUbRzv?E+$_MB63% zc3!s=@OA;rR|0s>$eUO85}&&O?S*H`-(L zJk$FE_LdNzNxww}mKdLDzs38O2%jmx#r&59o*BOZzwz=x7}%fKhOeMYFAySsshKFzvv{huRnizpRr!>N9f#gVFBxqp%rYPDCellffs3CU;B zHT*b5wLBsYOPvc7j4xpKJSWxkV$o%3X;nVK_6uc`V4@)t#Idtt$oCw;@1;}WEZM`h z{-G68A!V^T?o4WkF4SZVr>1WHzv02UGG1hjbCs>U zv_s!EkiXPUEvxr#MntSniYqPgDF`kP3TTEDg0r{VidBaFZcmxWIL#&tX?I_9TQ+D$ zXnf6n%z+i2QkST2DUH8{H$xfol@W%hLnppyzY2X_o6KSrIh%#a^kKkK`Xgo(V@KhG z{ptN$xJ=IJ*#S95VCfQ3bUNaHe`j*}9d!B83x$kd>_Ah=Rs_87Vz$)2y0Y2X%((13 z%N}1i*BmYd?ZtJWU)3Bd3$P#haGUJ{8|YXh8e(vr)tED1%9_b4G<_H3b-eg_%8$p= zJAtaUvn7P5=tP8<@~+VlRhlfdD5F2GWXHPoZMMlG8|#Ns=3_CM2@J6tbu(P`rr}67in9XWo2INF9-A0XuHU zNiOUyWjTYGAUVpjvPL!q%av7KU)UAptFJKE9*I}~F&7PFN4W5OA?b@^j1G;%(4q_X zy3&c3b!lNy-vt)5c~q_P@RQ2HAR$NEnSLWeq=1KL5fFukRm7eHXOWk6BJ23{HTgAW z!034S1JogC;5V;-IIg6XpA%T+*XAIFqpLsk9T5elK)m|r1=6u;yjjsZ%h!uz*E=mg zN3}fu%TlwJz~-=1My1gkWsKyd#8U+xUPmTk=&2{UD~=?v<`L1QfJk{$%TcRs(J(ty zJg>Gv6~fb1N8}0{IVLdcHp6Q9QQ*Bi14r`HjL%Gff1`xL7PGcTu}Bpr&bY~r8;Fa6 zGZ5H`k7>x(y5A=Ns=#eQsoVUSOu%}0y$>;L$X|5!Be_2F>=XjeCp8goBF zt&9Vp&|P|dQ%Z)*smh{h9hcH{uc;1F)K8rpMo9wIRk6HACWR~pDh~L2iUC()TMWp3 z{~s|+(ZzX8Xv!dfG}JZT_eCT{FOMq6rPAV;$asZ#p`d`D$)qfSAYaWqcypQ0(tQ;% z`u!#U+c>uO9h9H#Rt`<~j1lw9rpvloZC}C!277`p>ba2)G?o63H*-1@*2(#2^ehex zIS{Q@IB_V=#x~C-#06utNp*7_n*oqH?A?ZUH1|($+lsBg@1J=2U!Y`Z3f0V{-pBQs zJE^WW^J-uT)ayi_w<<8bsNp(PW#Ea0+_;zHVFDF5wOUksK34E;emy>q<33qEW6$4% zDIUt$Vum2Aa{6o&)0-zgNb=m(P`g1duWUGG4Q7$>Rd#|@?J>#dDBVHWo)Einx+ zgpSklR|+0B6Copf#cE=-Qh1~b%3t1wMD|3_4cj_!IkM9j-(We=s=JmAh=?f-oN1$>vH;AGHbn1pQar!v-) zq0|IyQZqxhs-Lc&q(;(=MC1MIC-~DwQqFs5BUbM{(L38F&7quv4kX!u>dv=99d?#O zRtwnLFJsrcYx*_QtF1a=u3d9%zTkJ~%Y@H3w#}Lp$5eR+EtRq=1xADuLWaO6J*ki? zAy=QeXxkFau-S}%gltyr5mkf|bcs2h#O-*4k_gd z$LXG+t!{ohv%EQ-VaApf>Ksd1Ui?)BGx_?ME|$oN1KOsg4Cz-`UsE05iA#frh89jj_O+ z!|`j)8lW6nL0e-|YK2nQ8)C*N;qwCTouP!^ZHKW5#Aub=)}(h6pnX-~-J{jSmyzQD z`+;tx&W(9*Pdv$)*TZI*#E&q9o(=~^^EA_n>E3Br>xIpolV~?Z@kw^4nq~X1(Eav? z_Bm-_ATv~+s-ey!cy%?P9vNmSMMlir6gqgMKB9iQaSa50Fgxt83vsfJtb0^0xu(g> z*T-HbKxn-eqhBja=djI5@Ndv1CMKP~ab(YSba9;euxyGg?W7;EDDn~jF`@V``E-tV z+Sc`kq}%anCA6uB#3EzOI}LLF+}Gu(Lj&WD#LBGdVDLA8aAo2$PdPPPMvmqEY5%;I z>}xdL8YQ8|k-DvaeAybp+lBbezC%Rs`x^q4W5fi5TG4zk|3JEJZc>#gg#&bcaviSM6xV?csRiCLAY^;{~ z9AG1;os6R|o`BL`9LOo1;hm(Afrn)?I(-9|mW6~HC-XfXYYbh9iuO3QBJ9)`30QBy zJfvuyJ_})itm@QR3;{8dUhR}y5Z^GQ?Rsh))GXR{%5RAPr^jMr#a{q-sx0UjkK8!T zjP0)W7my1#7Dg`}cYbpr@Ap|0y79H0_Uz|)cP6mqxD1AR?^A{^3%OGznggO@a;9_r z%8k#e5~JoxZwjouR<7t(+4N+%Pf8V~opLNfQPr%047R=*Qs}D%kG+55ek1P6Wbn`@ zG@j_yV{L9m!l8EuSB-rdP~qc%%r=!#_-D3RnDR6w@Wz(t&Qq~}9*AhOxi72x-ft|x z$qA8>>oB&o$eCPNZ7cBX4Om6e@byFHHPHpY%qkhUF`5Cf&!0;ZlVkH1AvGvqe&8+W z1RK%D&#G?CuKU6%^GlSP<@Jnh&&jg&DX;S;)ZSLP^ro!jx}G@xvkTz04gzN3)w{5` z#|JQ!au|jp+*9V>?tSMNGR{>TCZ+XzP|bl`?Q=GkFb`A&-f;k$P?!*k1JZ z$QCq^`V5LJT96P5d$N1IBv`ucCFIRUbgP*4hOXh`ify9R2JhoYDEt}3Nlf6`Wv1CaY3rq*IPAqBz8acFS4 z&7l>i6R+lKqb}R%VzA8K_(0kTRx~ zO&t)QS+FgQN6(l@jSjN$6Kj-&Xfi9?&C|vUBZt9k*{eH&0Yz+BPki?`9YLf;CDl2J zMtL&3Ee~VUC*s~s!@~j8$U#%ZXd4X3K4}|&ZyKPKi~s(Dnh6wH$dOm7fYp3g5UxlE_qeH6{J8p+!V&b=^v?4 z)xz5T(*Qe@tWQI=ff}1FMpg+q+7FySb|c_S$Kz(vy#$F4&_}kGuB$S;e$y>b9tDf6rJ5{A63c8YHaCMu7B$_62!KDqLVWDbh?V(cqZyJP zAP~O6Y+jRmu0*o-WHamZDoQF~4MqXtETkh2gY~)z{>T`f$yS)b6ga=fY*5T)4nDEt8^$UJOe6lL+rA(pEKc0(r@-fA*@RN)C;Y20{B=ed0 z=!M7_21(E%aX_vhm<4UrLy!iGyqx6xdGSzLVM9D`y$kzd^u=>+9-q>rddE-Cox}%K z2&Ez^jzccOD7*XhIvQOCs(ccNNK-{NZoWyAKe0zkE1CRz_U8&f7RxH~uQPKjIBzfC3M%sa(dU6O+6wXvcurz!##Bje79c8j;jHRc?Hmj9iTI<4oI zmbiI!;yJAvt9}7BiszZOl1c1pvSX8G3Ul4kVw771?C+|m>?eqz7P|HEI=CMEaf)NJ zXN#6@0(vW*b&<-<+Fv9wK4f}yFr$dtjzB7AV8JG4_VKXF%rsI`E%ItEyUi1LS~-+R z&!Fr3Z^3wyvx1S@Y+-16>9jS%(ljXv|Lw%BX;FyfyZo#)sn=ZhZf2&?l9$uIi9^gj zb;q*s(EM?~z6jW(ygic_o`|=+RA`!MyvlqPm=iRDb1NN#EksF5{1KcGQ77Vl1GRgl zu||4#Zm*BN-B0?WenC0pIwWc~5odmH12h=r^lhq^-U_1|D=L`Y*f<*<1BohsaAB3X zCF#mu6-2q{E0SU+_{i?aJbpV89%f4E=$IwG&Yg&c$c#en2FQJcPad0p2|a%V8w4Q$ z*Hc&F&4|iy+|#(U|HfbvEGvQ9F*CVHDh%OBP}S2nqCatqJH?osA#`roHuhQ=Q;a)U zd%y1eHdG_K^I3PJdcQs!)XdU!3M}i#M#%UBF|!ce48GgMopR=R~@&0T4v$>pS@uNvHkSCaPSPcZ}95wItI-XEGQb0E_q zYn)G^$I14TYdof4Gyj z15*s`{RM|RS}bERU5SJHeMEv(EF1ZPqAODSp#xG7Bf(6&LDY-nbfMnydM0t7Boj(> zP|F8ZaTT8k@dZ=6jojlXepY_{F&4ZW5Omq@zkKF%~3YTest zz0nw{&z3tbKibnfoYB6Ue}4%KRFBsj64c#rXLdIzUlX(m3$jL!{oWq-P-5v#_*a_| z^OTvBuGW6n9zC?Ln*`*KAyi^JNSHzq{64l|c4y!6oHm|jEr^Wjlvwpwy-dN2;EN@O z=Up;*y{T&eQw=$33u0%J*#Y*Ecp*wmyT#DPmz_U~3R`Pv?s8M3Utz`3rVVw`qSQwG zRM0AD=@CH-(j{0~Jd6bC7@liBq2ke`pk>Xrm4Cp)A(q)tWc}yxV|Cd{ep>UgO#9Qd z9L>&xR`4e(sb;=(%S8u~6(h5>NUM#3Y8lvuhUZxUWya$m5pqL9UV9ALX}0%8tH1`g z${f5rG+@V;fuu}?gkwtxbx-ATkL?IlpX!7;7dmDwUmD;my^RVG-BPAobm|+r9V75aI$Eb>_tY4#QA<&Kdte00#2(r6^Kt+a4T%n(2ayOJ5Da#Q?>pI zmz-NOh^bL1x$+QFA(#=a_l=R3`$F1AMko~HyuyP4xu*u@JXO6vjY`X#c#WqIj%I>v-~qU`Y8D$#2Av?p7i9f-a{O$p<=0m zyJ|OOawj0cBnsORas4fdGG!S>m;l2nrRk(>v#qEpylwMn)$kMcS0VVq=Nk9=j4?hT zuu^4r_0$Mj)Ic2q>;iNFADlHeh!f)=WDjk+%{#hz-U9031Kzzys*doZ;`8u>(82ot zK_{K^20s!;d;x=tt(IP_UGa$po^yxVz~RMoUi>LBUgJugi0N1|*&r!`vF0am?dgfT zt0CF!cP(mM-;5!3{+mo9Jg#(~BQ^YL6qLt_BbW5f-qY2*B|H+6 zvF$4L&*S6qb|wsni}aUW9L8p&Hep+0`dX8}K<^1~@I_d}rp{6kf%#o#&oW{seV1vx)1`S& z*PgL-YZkd~@7J@H_TTjVc|R-zQ!8$-ITvD%Bt@%#dW!juFYR$-?gkKG%yQ_-pe5T^tD;JKO7Sl7Y>MMhaA8{iF9< zd>{?F)BKzkB#qQUHFb|SqQ^sj@6uQNWY7#7CgxF6tHAAsbr_Z?p_+tSl8#r|3brLl zm|%cn-bbtK;#G3Fq5MZUSq{mkY!xyv_|WOZ*-@|Ap6u5d0`_&5#|$j6YUw9PfCbT% z_$|tjaV$1HrSNUDUncdinPiDeqWDX)oPoB0{c`tG=$&L0`U&{H+EPyC38jMDi#OT? zA*`2+WIFP;DmO5ML(7!kY#B5b6;~T;!?uXyj@{d{6a>W+BS@s>CElTnLdjs}m*3~W zSiI``q}?4p1;HHrWz=ksH*CI5mtPhekZT_gm(mNT;+jQLDHP8LxubH z8PaK303c2Zxxq8SJ?H2^I4O;AExhlK^(ik_;w@iEwV-d-km=h4a!Tnj7I@4tQ)`x_ z_3eYoW-Hp2O z<%#zrI2sN2A11DPv8J60bk=}AK&T=FLyJ^&P`&lBm?%z2TrixV1~IO7%FCS7`I^^o zddQ_E5<{}#^kU+!SKz8I#yFJ{wo))IasMs3Ha}kVeJQ8-F&aU-^iZeCB73D&RFgVl z@>)pMK+vC~*{cvWCiLx9lI)Flhil~wD>7mArDnzr4nKMl`S+g1Q`!gD`Kd23e)@_q zuffX}0);V9X7s2>1KCX_{@D{;&W)R*1!}|AgI%AgJ%XRl}Zkdo6Wv3$x1SbX9GJG``^i5KRbll5uJ^w3BcH`iTyK*9r zLsfYbrNYn_;R+Ivl23evUA15;yOrrnTnD;dM!Y1}D33Xv#E*z3z6;jqm#Rf_!7Y=E zEBO*PE-g$LZF0brD(`)w$os(v zs4Wgnq%W$IZjDbv9a+P!)&0jFpZ8lPjT2Y&##)GjPSu`eKmw|YpE>kB`-rEKwx{(i zN$XrnoXW!F`jjrv92T#&X}y86snJ5k@`#;CTFF$~AYE@$Yp|7wCj>0*7u`$T`5ick z3Q)lzRfteaJC(Qr)45U$05;_FzFU~FX}vDoF?t$UQiCZh#d$wExjAZH0mY9o=%2~& z?WVn0ooBK2Ls)PZvw^ozDxRhiX9FUM3{X*+;w8@=@lEV1?vz1Gw@yW|P`BKVW7%{- zoK)CuNqzqrgZMNwZN(M-Gk61l`1pCMOYMwCQ!9^7HYx>|jC2P;!NB&Z;TofU-cW|< zx7aU`RKQsH2nPwX_c_F;l1R?LD6pATC_A@_;3%d=f8$d6p6tLQYCF}lLm`vbrt_Hgc<2= z`_CC{ya>piKOtk|AGV(Q0@hRG9&-M|-1(M)P=YXm8leVjhi`dZzZSKIJx`w8=@hHR^N(CHc^jf)3Qg<> zAKX%Huk&_#S2I2!k+d)L`Os1~{j`02y@WKJrLXglT`>CbphZayf$TRq` zv9xb-04yA}eVvO(i*hI-aGgs@m3UlfAb{kSJVmPT^`b)UwE?0XGmh;%HmP{}1;V3i zU2uO+*iF3_zVn3F&Hf|!2{5IlWcqeOz}f|VO)Un@A3Sb}(pn6o8~u;w3rfAXq}PX( z628$q2k+Bhq;njyyoTL3@F07k;TFO!Q;B|EB#Kckur6nd_ z#Y3ft$Rt6a$`QS_qtrW&vZl!un{&0&xeJXif0*aiotfDs7?HM=)aj`Fwcc8ua80hg z`tw{2w$|*9P!roC^l(N(psAy7xn!X2#cw*S^+PW9y9Qj?k!yERBR`L{-6H=G6H8U}KqmA-s<3A}PmR&g?b(f*6Tjt-BjN{i5Y8Wr0W;$OHf&j-f@Vdq z^}d~ZvoDEq!|+09viq$hVRTCeT*=bh{X*|0Qq?|&M%pV6;#!<4N%>$QnvincZW9IR zu|H5o1EmO%o~Xz=M{*G=q_!7^w6dr4 z4=SWFf?#1f2cl3#9%KigpvvK7I^+0!t!_d<**~(j@8VxG5K^Y>SuDQP21!bc9`D4} z9&`5c8tY3bgcO~{Tz!`FoxjZ!>P=(uo-rd`Q3hx@$~;w4Xk|E&RH)=Jm?%RsOfU#P zVD|FRpujKwkOr8xk9Yi!oh8a0u4Kj8ou7R#zq)*?V+q{tQj|S@ChgB^)0jiOx0j#X zPv!SZ;R2sUG+DOh(%?##_xZ61tT+n+U;9GtKy2&?qH~|{Qc6^&HyaC8jQrd+8iM?* zHFkb6CU!6!`YO5F>7BPZQXJfNXf-jg7|wAUNkcqNc)b4cOj%EEKCEvyf_N0z)C)yh z%fw4;B8$@MP2%xV0tc!aUJnQ*_rKCIHS)|e&J0tSPf`Xc^D5StKJ&{rGDtO#=(f49 z=;YF5{p}Dh*IhKHa?U62xKrDPDt|(QkSUHAOFO4aAC9P10~9+U7L`-|)MJT3zL!qZ z1egGks*}4`%^$~zF!c6D_`4B0T(BOmzAEnbe7A>nos4giz1QiNf3{K{!0T{~!8R)= zG+A%5wDZsW{iJQ%_Eco99=qUA>J}fAZ8VO0Z74NbgbrSbM9BrG84yCDDPStU{6%nZ z6ZdY*3)WqUCp)I0GAL8Jo+gU3jLXj#JXQXZw(^4oyjuTMKI)Uw`R*D(9DFQv8Ikgjtb^sTcxplfS%fG#ZIz8%f!YaLd-) zsc`K3Yn*un$d{Eyghskasn1Htq%hOr5e?nd5?JBaVONq4B-Q*iaZNvUmV=_ps&;U(aWg$3wpPTP#h`C1Z|G~dD^Srlr*R&>8rS@ z#0g8WdISZMK>EoN2gik<&CNNOe6Gs;#k!jv$b+% zwnn9Gwy;|t(bbrp4KsXeVWn~{&6HLUni#~q)I!3a;!}UySiKy(3>W-!2k*UhT)&aM z2M@PBLa;Ilo>?!O!G6$cDHW``;(_E25T%iblw5#ZP$RM9g(6^p3N@ivru5|Fc}Rc_ zME*N<IjrtfR9p@%}O^n<;F@(a%APP<{=-MW6Zo# zK(K|;lh4+!{bYjKV3t-^^4F%j2|2Buc5{lD?T$`==+Un1%Zu0h8s#Dx==Rs!A=lFe zY2jak#l)YdM%w#9a|{Zj79HpZVowRZ77r%pd#U%wf0OM;^n7R!uk?NbvwNieeq*_w zPH+K`>Q=7j&CW$oCOjI0&l6o&6jnRDLC;!ux!I9Mq>K%i0j>|yFLY(}Vlvk}6vEo7 zB`aDOBz&79E#Qm(W&b3NBOINZ=-~lwz&f3>h`zh7*Bg5jziw%|XXHNIN*HHg=mR4~ zaBBX{VjIimq3F|8eDMUPWY?m(Sy;dM+{wpBl^R`9V z&1gRNVH_(3=SxQNqIg;)LJr#X2}b;N_%L1(gSWtRie{K6X3*M%wHPGO6`Rf+ef9J^ z)ut8D;c*;IA>4lJueKZ$7yBzt)%}2cU50xa$4CIzI{NP>;u|1H_ptaqHZZg&`+`@CSLG(>S z*(R$E6(^-Wq6H(gNi#!!boRm*(30YL_jf&Mq)SWx$|XA`Q%~hCSeDHvPM{~Hl;&p#b{NJ&vRyW8AS3yInB)m9F%9=g^gg_rE=4-%@+umOUOd zp(Bs04chD}V}#ME4H)R?>s~9J4MQRYQ6LOG${Z;C3-8%4DGb2Oj{cN^a>xdTNuA(x z?v>Xp#!Yyyb7DKNsB>d7r(4#03-{>0W0nOfcB6|su=!Kum)e>d$s|d7==TuSA%oK{ z4$Pb_=66wVw4Kxg{_Wl5O%9Ickc=%`PBOv$A|JeQRbI;!$z><%`$2VNQziscq zHfv-hiLR{8V7J_F54X|~Vl}M!TQx9k`lTy6-G@w3P-V*}gnI1XTe45Cw#1boo72Ni6W^Dk)9}93eDMSsl6R;l|+J6hPh3*v~f<~2O8H}YF z#xj-L;U^{7@f(x{hzco}0yDT79Tv+uUf3nuku6p-O5DzzSO9xKDuZg@gz+qc8++L{ zIFs-cp{0_Z7Pv;iXH_*<$qL=$l}A{C%O>2I24|ttu{L+JSH17J)(xO4{>2xML`7OJ zD2Q;{oNO9DWRIOIG2uGbHH7ZcL+xI#(5%R-b6)eOxpIDfEPvsC(|NHKh;+94PlwJY z-PdRXMwv^dZK@u}JP*5`h{^qtaSZC1SkFAOSl69RaTjs&4gCZH!Q2Wr-SoPhTH97e zjCxPHaK)xya@>YPkaO*OCFj|d4ri7cxY-I<7dp}$b&46c6z^?q*QxB zojj2Xli|t`W}ZEfD(a&te&yj0_dP*e%CG+yB1qr*gXe9@mnv-YRQ}N(SpQ7ZdHgjb`8G_ z^4T}ffo}f8CC7C;H+@(J?Nvl^a@W0-&OsaS^9?M^^NeP!{h{`)ph3769)7u}Wob;+ z11C8+s$_*u4=?$Vp<`O)g9kmZ4TJV^g@P4z*=W%b3Gcx%(*%HK!5VDgUV{(oz3!) zeev6k>9%I6J2B-t)5@SCjVv;8z#z!J`ZDO02W$JO(WyT=9Jbbc(4#4v@RJ$E@zss@ zI!O0kAi@}QA*1~JERZS5-1OyPazF|4yeAh)p$t^|OJMXF%|I)|M4T=bS@KhGKLjQB zaH-zfxlD9Agud~;Mb~yGN~i@)+930!IM295NW@UcA4wmoSRi)? zxn>0{o(aV;lrV)I*i^3)S@M2D(TMX`tjP%qRu{XaLE}n;j^fY|E7WbAUZ4M6ttZ9f z(uK9JrmQd!)2Y=lwfK?hJfCcLPX6rf?Ir()c_}Mh(n3};Mub;nXh&7`cabE&&r;{* zFP*{|v5v>zpDR@{n4m2ovJ9$5k?P~9%Nf$`I7Sg?g;Ld*YR(x_yL3%9JJdrY)J*;h@_J|$&+Yf`rs);q?#hXBZVv1n|p@!(b8i4+p=EK zmfS!-Slbl0(zGV}b3_-bDHM(u#(ReC=G=l7ko_zq1U$Sb+cBiwz+-FSkBDFZ(~K6} zi6C;g1#(KS@EWL=Of*S)y6}J{P^p~*q|xX!6hQgrtR7Ye9vKaQoDs6?6JtBnk(ZAH z1!WDtx9zb${_S$Hb^hD%?!wc4kjLkGI|*xBBM_P?ud zB}~AX)BQN=QywRZOVZbnD!-R-tKPThUHvcQzhCvEi?P_rCDK}MA0{5^qsqMgTIzjI ztIl>M-0J5tLse&{hPYs-_aW6ww!s)`Y|izYqW+Q* zMI6EDrl~7d*Vmd(S)9IWu<`UmXnxL^LOh^dOjlFUP2+$cKYUGNjFL=EDJP$dWX1Cx zGakmxyCXGmnTR7mtQGyDFlN4bu{nXkdwtX@n|h2RQ#X1sasar$XayhjAKH!y!wP73Mk)ec~i~uPiG>lsOla5rb9vK;xpRwGmZ(I+z;AG6{~ z>Qo-gDEI6N>W%EUX$iK?pp# zdGX_SjHwlUEqLPbRX(;^Z(HphZ{(BktklxKOn#=Ry~rK=JC{*O6RdZ9#n0u&mH!Z9 z?@fF;U6!^C6VE*~1E7kn3kyXyZ1gXK6b$yGOi=3aItKWim7Amj}q|?%}XLXZ|#u z(7>;){P6T92<2nFzZVXSL$%sf?nml~TsddBJ*q6YtX=+`Yx_3)X&0uLwEp*!cc^|v zQB!%-++WW0p_^CFKyP)^R+2{%>U7yFn1AXWat!@eGP+jWdhP!{l+cDq^a5hSTy)Ey zZZwi?eF5|R7rlu8+P9&cQ1|HrvCJtG@~B{){E)j!o+!yW&B|FHWXc-b>F#DZZ$MFh zh?iI}Dj9D)J_)}c{1?8P@p_lp*fQVF3jL1OJ)Zb6zFwUhj;6|&qSECVdKL;LxBmO; z5Mu-c&6a<`&Z=$wyW!G_&;M#;AlwA)ZJhZi+w}PANPS2f;~a{wm+Ad^ug-Xsco*9` z9PwFvIL(eD)3=Eu0R7fSH!>gFnV`E$1zvaXeEm|rnk~)KrsPRFQ#8a~pSy)@8`Bx_ zVXFfLuLn1iy?^!8vDkqbSMQ4ALn8MY5*p;yzi$ZSur2!#0`Wr5huNm|0(0(1xg}RP zm+ZpSQ4H_o^#<3Y1>*Lpr)fof7?h8@o%tMP<)FHx3q2?hQhqeG-omxPUin*tHVB1? z10Dro2P`NmUuov5($e7l+qaFYhw_Nu_9=kYyp z>4Gj^!KpZn=HMB1sVPIeCaHp>f33fS7=Q67yhvv|j0)TPM;&cg!3aE{n&U1#Gn?q8 zJYXjxl2cU+AGa&t+xu-wIR%1iy^%RE}W=P5XZjYnEt$k01tw^ z3$L@SxeLC{3o_F}P=SMZnOKmkY{v(xkbln(q4h!aa6ve?TYW)oQpfU@0qZNjw1zz| zTU4YHrZssgi8?Ws2%DxoQ zmbkHkzj(Vq({Zi##N-e|y@*EsP>0-HvNIVph?4q7lL7A|-+CD|B5jjvC>mtBT1 zwh+aoI1G}iVBZ=<4|3KFGnt;Zh#>Fni9BB1jFwDo8T?@F-Uc^LM&G&JKAf>r;A*ke8Bucu2Jz@6Y{w}I zC!>I1-7auNqT2HV%nfU`kjwLJ-lvYxX!zNl3up(m45M|r&u-2}oK{WK%>c`)nWbv_ zEVlXlBfTSJHAXB!RNMeA_B?9^L_MqMe}V$6%qy|FyZA1Zbi(ZLV}!vd3TDN|8&trP zWk8rG!PQz_10Nk%YF2L7L7}uBfUXV~GO4rkjXYn6au<5ce1EDrKfwS_@&)hJ6v&V- zB%LU=>;ev}$%y5EXrpHC1%TFb`2Zg(rkM?7`AbGkB(iMCObh%Kosi(xud<(Uf;n9~ z#`$W_yZ-8LoHd*Q3J489nHq1IpAC57U*hfcLgi`i(G_hMhdsbZ=m_CWGB#Ey?rEJ& z=u(UB9;ro87QDX}lGpQ=`Gp~f8;1YMQkALeZS4>G*&%-pniy&f^<;_p7WE_Fs-AXz zE2IzI1o+n}=Y`)?u9A@vT+2-htfbi6r7O!SYto|=&W#vL3;8$PZFk^Ize% zRJzg$cJg@34w)R3u(~bha7U!2x}Vy7n&Un^B|Uq~Sg`CvYoEeKrN0^Jlap1Cnc5cg zRNMVx+0ExPKK!b(Ms0?OBq)8mnYuAcdwQX3HMS0}2P0tuNtFuG>l#_osM-e5X2F_J zNE9Ba1^qm9^j_QO6ZxX5zRn1rAhG6KSO2Y>LgXB&L1 zAL4|7)`3dV|4CLXDY=miJ{@$1cc?hNSeaJMRo+-CIqrYDwq@Cx1YbkTI%w8wIrE-? z#SDmJg;GYG6OyWFELES|R7m(%apqS;qq1^UMEp@)(o-+8=B2jGsFPrFMoOkJYqG4T zU0@^Fc5>J?)|@`eY~3(lKBmcx8%l1jI~l-bf{6{O3_~P#oJv+>PoR+$W3S0Ei6B?L z6nJ(CwMZNfX|5G5fX;IEG`MgI_`V4qmsf`ik;tv3p^>37XY^wph_&Z^)+DqqCeoWM zwtqlQ#6XTr3@T1z5bB3oQ-P^JeCoPaia3B@S60qHtaiXlgZa?aQ0*qdZ21lNxF2&M zWS^~OMaltA+v)Xh40cpuQR3hQ?;2>l6Tca|E-uDwV)W>`&3AZgT+%Fm>+^LsxI51( z%T~{sT%j?)6gg-W+Rqrie~itc?6MnQ{H7mQ$6(FfUHXojEAnuq?`{DJMNfk+Wy`sjIcx@B5mnSkj-6XXR%29sE8Xd? zv{a{p(Pus$5ehH*1?+dm4+b#J4yhe;4L|xR_B-?J((sEC@Xq9gcV^%H!r$`W52*)Q ze~hoP7VrP`oY`*VZK%Oao#P3!L4%ZFO68)3?!I5-IW3_12@lC`99oZTQZAw-1SSrY ztNp`RGWRv{KXwo#swnPcPOsU_!7qsx;aK%@2e-RaVfgK$%;%cBN9^;M9QhxFkY|Q2 zt3&YE!ODN#|15O2d1~1+?U<`^HDt)P5 zr}w|7=R3zjdud=}W8-F7VeHul(!trGVX8QvGl8;F8e2Q}S@HJGc(om^1$YEtf*kcgcT)Z0OHgB5cG{h;)yuMR-f+c$T}1e!mYT!Ik6 zCe=-4lv%sWvl#ADU0!vw*5MdbuiYqw0qm0GG5O!g?^GS;| z`P>TCisuQ%P2CLzcqDt-FLQrcHrIbYH%6!xD=7k#u`1%+IkHM^R)I0YfbN1YAY9Nb zryM)S+)%97d3<8lD{e5W%WyC0%y(|n5?Y`QYkOtPhas33E61D|F#QU~#zZ>CNaoe* zYIT$@3Gd5PGVb)FBTQnt_eXNf)!WdM=H3gd5$TYk=fNE?0$buR z35|t8{9O$UK-_`AAXFJ6EC~uCn!(HaRYzUR^xi(#QPr{0!M?qf4R?`gWo4<aH# zLd}2LMM}J)jf;K>4jRO;`AYC|+rQ(X1o$nNgP_r$P)uk@S9H`>gW%;5AIV#w<29ET z;Xk)${F9-IQ@hh0SqF*&dXMWg;4kv36gGORZAo$Qk|K5K*skL)S&7~haTvi8cbcAlH;mFz*G$g(>fZ^?0^W8{fYf0nre|-G zXd_BIwcAriSaF-9y`xR9i>0;spFe2kYSdwOcim*Zq+Le1j}y9z=%WE9jthUx&gf(U zk47tWiH|u%>L}qS*LAzYlMm(#P|G9oCa*S>gOF2jj4JUpf)1onLP{tUP9c$;kk-3N zMtCjs0`ThBtoCNo*1vxCF_9KeuNEmnrgQsF2R7b&$aJa98p`DA&3HzXdu2XnH(z75 zH-L7EIwm>_?%C#3`UXekVVRW`@@IoSl{eI#UsZVihc8O545ovz+cqt%(Y6IFLs)-b zWR1Y|`gn4XbkBM4qy^6DVIx)KIJ{LFsx?Y4ylEn-RRvA6tebY{2#eP`l`x4bm3XYL zBaK7|5|4b3RdE$f1&c41+_OX|c9;(0($*vHoXNYTkN#oK&aQrl{GQ6AZg%>I;%gU& zFAfPocd4(>yFI-QS^{&Zp=ODZ zzLR^1kBL2zIf7RG{zIDqEp zw#5jBrIbb-i!E-(UAwa_#OFY*Fk`zLJoU=$9Jh%uxpQv6^3<)$-iemgk0I>;7pL^C zp2`AbnSH4h00aT-)YW|{i~kQ!bIc9&-9CMRO~~E7$ztm_pv?D*namaLfX8;{=3Tt) z(dzHp$Q3KdN^{}!f5R2=np{6B)I=j|`E=N7!c0Y5O zp37Y5o4bsx6o3gsIhNMas0pFCm^EH?#-L0~;1nm_k~+^4v-Q;0jisVb#JJhQ1f^Sf z+?)!@`6Rmy9NC2U&FmjeE+w1)wmaX1Ztp_NkSB?a!miDe`8|_{jZBxfI&et`c7Bf+*2l%V1+qe}TPVis3-R0vGO+JX5ce`Pfu!iRQE5NdBNg>_phD^$OVV=srBGlsDkk&E_I*o$V9%B!7ap#X_&)@U zwDF=^p-_vwjl}ssc&RFkyNb5#DZsgcdI!vk{{49TMohTbU04UFmjh5i*1fT>s&4QT z+`O4gWu`Z9sm@+kweO93q9VP5s6XD_`-$_q43+v#@Azfv$O=^t7xrie2P7c$JW+pc z`-*94SbtZ{+giljY(BnZ`@%Se5LzdJdH+b-ZQM++2_GAyE&ZhZ;GzU4SA7#DYCiKAYqLEYEGqDd(Fxf|E(aQ%z;Qw>u@86mDhKy7b7^oqGcEDH9uHr_BGhXFBbWHdF9wANQv5EFYH5 z*6{r$oy&ZI%B7?+^f%iroe<-4e5&9$xLF*i9^Dy@vgfhxB1? z375$&@q~B6=D_N}7wBIr%UAwoM(J!m*L`4Y^4I}S&g5N4t721!Y%ChwPY{1eRK)Hv zaCfYFlY8KYe7fB&o;Gtko~fT0^D#NFXXg){TF@iObfEfJp^aw|$eQkFg|5xH0&6L> zB?2EFX{oW5@-w=8->gD0^dF>|M?(KT3*ETDpv5C7Rp zHBn8LNH9hx=qM8{I7ZaaAw`r zy{ftC6#c;cG!Zr>;SJ(1X43qN*nlfP zikl2;_8Ky;QP~f86-T^xb*4QS@GpsH{ii9{8M1jF z$Rr3%#17J5+YpQ-prx20lFFpYX*Q7-Je{INOd@0~QvhX#D+V<m*?p9X}?IuCwQ~)>r!q!l?H`@`B&l6?4VEgo!40Y zm_me@EK*ri#mnYp|wbJ!_m-axS|pNV+Ja)t525L5loFuo3DbbL+ONZsWGn< z6jTx}pQ7=$qkv$+0t?ka?Wlh|u{mCC-La64i_aU2 zA|>(MK;EBLQ=z`WB;=2j*}NA^sn6%ZzJ}I%0QH~^b-22*h8-8oWHlv@ibLv`O=s0b zpM1~E|C9u)&;IndRH7*X&BZ0l~Y^s9DX#E%R3te-|4 zm2eEvXmlKAs>_cbF4zC^{yPyIg}NzBrr>U9l#q9{-u`+ASfb0UCbRA6{y)njKSm@= zSvFdLEa96$1!a03@Y(I|Dp`f_Y91NK0K9us`X&}hQ4skEr7)sMU*HOo6A{#2&LMwT z7EBh{pOOFiwt(_7bgTK)|K45x0cigct0GgE3Hnie*Y{s541X>a$oJOF4j&xq`^Ba* zq`9iD8R@vIl!~~*TP>Z^rw^U~A1__v+80DaaVg#oSr%{X4B;6Wc!*gbQI>~3l{#w- z;tOlVuH`FLV!_w~kiq5u6REQpP^;gZ=f!Yup4o3j!Hiz{PRzQ4r8FjxQ9^DszsCr3 zr7w-X&QMB3wG@PVql`q4y`-uxhv);`=v-Rb> zM#haBx$EVv&+vJZExW@0!&|&gSCgecKYqNvz|!P$dbb?*hdFvBb49o@vtL7Mqs$?tO_zbvl&K zczNq#!JUF+zk(RzW3^mEO&W3xB%}9|DRu)pbwLazkva~wuVhLFv&0Q>2o5e2DV!V_ z-dO#0U)}i1z+s(N?{+-8P~i3Ip{DC81r%;u_eiI1F`(Qhw|ae*P1Zskm@on83N1|m zpLgp?H^tL;D{knn=nTU@R@U81{cou$C;}vpy05{o+42rki~uocTKXwnVVI5bte8|5 zqgpnXMe~$N5nv!nqPV_uq}#BoIGxlN>dExtUB!9ydsQA9kbU)=oiIUHXl<(Z$peQf z$$YQTr#rcIWaF!qwvKE)sq^Vws{RzN&q=}5|Es&|@M>;+qcC~%ZoxDwYnF9g$z)+d zBU)`KG0rrC@B9Ef<&-+Q9AwBCAF=neDoJCGkyKiNUeZY17$O~A9g_1(fnYQW1;^_x zTEq1o;WS+TphyR4aq#RLJd}U%l)ft`HAH{^z6ZpBe@SsX2=l6@Ox>&8J9SD`9t?u_ z#vG$&oOPcScJD40_S%K+c6)GQ!wjtz*U8p~u4e);0uemwsyZXR)d#oF*`yPS&*<%` zni5lqW{zJas4>obc|&nlPt#Q*%zSR-wCOY*ABdOi9Ho2T>dc>*Q(!IX2{&>Y|%+cO?1-z4{1#_fq>RgR#{vW-$L;^gRfqqwR8c!VkkVrSa zW0$|HHx|bO?NZ&YK&n%hO3J{Z&$SJCD~H0;ku|LAJ}NW4yahDsazw?1mIE;Pr$ zU!BD1T<%Njixec6#gWu4Nr5^xW}v$8@UoiPaatu>RPPw(S~wY+>x)n&m%;6x%f~^# zXl@<$^FH1`+?oLpbBY_iMhjL_9~!nUNWI}>yY^x_5R{i5yQCCA`bG9@BiH=W;^R%G z{pPZFzDzV93C9o8xPtfq@f{*AVTyxuTn+?!yEuXCwE|0tH+H^|L|D0oQ?gJ0U^C1+tDzQdWvBpzLpKBAg%-=%EL2UB|s^<>y# z(>LsUX1XSe06&r$cLS$;I@w^>Oa%#7?>+1nXj8SGyvuBU_5gX0=N|Hag?)nFN}pzH zreOvyHq8!WU30I=)WB}12qkmtxlNw}Iw4&5T;MZaFIfmGV= zf1_cTN^vXdfOc+yk}_ncP>O<8m+FV(DWANIE;5Bw*w=qtHB1@oDb#So8*fgN2~$^Q zzb`WAXU(0Ihw3ylUH+|1t^c6XrD!{Il?VN`%vOKb5$n_P#P>^<-YPlqhn(t}=h_Mj z3&}5EB1I7{r=p%T+3Hq*sj2?sgnV%07MN5dA*tP?3s$ujsAV;HwHqdg$q!`s+8%9U zTuE4Ay!NpG#0h-Uq_{wn6b?C`W~fhk5<5xtkYS#7sj519#{ZiqvZ z;9moBw(sw}Tk^O=^38#P8v>gs7Af(dH-B{BR^Enf zz3-z+GJW6Z|Gs(cB6sn%!w+}Yk4tS*&A4^~U+jV))e^C%rWOWU;`6iqDsq@v^KAsw zcV()Jv#5Dpe6a@9nJl6fm}VCFHJxm{+S%9VTy;@1qS4IIs5b4_vH!%)I`UW1OcT(i zBYb^}MpK+stI2F>-N>z|Ew&WGnB6dt`AA~F5dd-~d*re=$UO7vO!M+%$kcB5ns zZLx5JmO-$NFz#RE`24Tk7?+n^E`R5kj<}d%)V{laUg{2_e3#rwld4v?X=>HP7x;ry z<1g|>C7%2@jdw?7{*YE}^zj3Ye&e(i(=#U<(uk=?(*O}fF`PM6L3CGfJ9`Y@kr8sv zIE({;(dT^Vm*`1DCsoo3$9;;?7ilNzQ_n>=jb~%(n_NKhkLS$-V;%=*DO* z*IZ_>Ckclh5*7vqUqX+=n5R>d;A$!9Sz{=>lu*+??tEhfQxZeSh{N(uQ4*pTI9GZ1 zNY+vXUXB|56EK&JRv6RFv>KapQ{uHT2h7w)=F#FU;o9|( z;F{5hQl)?IdrEcm|3$ip#U4#c0fanxd7^ZrVjc((a^nRbE%1UH8R1C2L{0bo2=MuT zV6~=zBV@JT$YZ|;<-O%fBvL1m;BSf+jYYE3n3rW0hTLcnEU=xLb=nhkKUFy008K?bJ=y=^oDda^Q5O8Akwvwsuz6CglL7l4}*jncJd0gc3*Dv(dm>{uR223 zcw;pE9A%cLZKcK~fWx0^j_9U9-ngq=*PicpP+A`efCOM;m@JZ8eO5_^{=` z9=J#s39$L{;_s2|Q*+>A*@R1`$=m|jjtahE>%6&Rh7VQhzYH&Rv`Zf|`;YZ+`Tw$` z)qjnCKia48c$Tq$981uo@6{*()g6XFd0F#_g0D~fNaQ$lJUVcuA~?-CGeCw zwLYQ>dO#``GlP2?#(Q%1Cf+x9ASD_g7>&w3D7Cxp!7$c&@XoFGKze`gAut}hhY|9x zv4TBBQ&REow2TT<(4p3EGG~=(zuD=ZjyEJ^2+GrZJ^5)lM3kwRw&}G>P^~(3({oC( zStI(>?Y5BqTsKME8B3IK-;u>ofhl>B{u_BP0l{mZ>}~9_Ja4FE-yZ_W`s_Mzx|790 zob+PybB$^yud5{kscKqHI=R+4l7x-5g3&$w#raC5DBaY|+Lr$G@IMmni1CTQB*jDX z@-Zwnho1}Jr_=5xlyM=IHr54U6enqx7iCrZrtSJ+oaSZS_T#+n=ly;F{N>#z69mO@ zf~07M<#<7qWJT3w9A67R^%XVDP55g!;(kw5^s&3k@AI523)@?t|>wezvhhPN7aDt?0 zMk1BT6-t#_qt)pRMw8iMwb>m`m)qlIeg3$;isbpp;ABgom)(#FvKVI2&KvfG!&#wl zDNKV!zJ(i&$Y-uIYLORsXMFUKe4^O|MQ4>-Vd<=SIE4uw9 zBr_FueL7N-t9nk=VYk%C40pCLYoR!+n9l*`3|xPO4oS}wEB#WKH5!y@;P!1E`<76+ z5_ad9vkv&OQ^LuLfJ|MS{Fb1>S7_yNv{o)`SdX#=nx2_oHti%IF>#J1Gq|_{r^k}2 z7z3a9hb^?_6jibY+C{xuy$I5fv=nT{VV@BG&)-|p&DB#LIXwjCZ+ukI_J&53F~sW) z8TvSdIAl29x|0`~%@`Uoeg`L#)PdBHHC+^^ELpF6nSNprNFMmA9|&%Na`s3KbBQh} zU5l<7R{BZT42}5}5a{rdD^IuL9P{@)-wxdho}yVWdB8oB@ZjZ2#eFGSh}DxiL>weP zv0edqQm@nc%cfqaG?h_`C1tK*;ZHkH^<^=++9PO0=oYNR1ep!*h5N%rk1HyHhqQi$ z9gh2LB~<5{#@z~GrP5}WPdSKT=hCy7V<{a6=A6D=(0lMVdKXj*6=xeN>VDzQbA>V$ zIxgc$JFMb+s$3t_2fG%N2iKq`hN@{blPTnLh#@w2Rd>1E0OG=t8_iPJg)#>Ua$(xg zIh`uyx-Z5-Bcvbp3O-U|CZ^u|9j%R%N;{-7p!%e}m$7LTYnXU$s&iTldtZIoaE=XV zXxAX>Y@5()l?y#pL+GFX5Y5ILg{0eFuSK*%vN$HUsYaYPd(+MpSb#?3z*>5-Jtyu_K?h$P$yyejX)y?gAA9kbL~rUh?14fzv*N?u(~<1)#W<{q^WHh zEGu}3zC&CmbSCp&jqcC?yiK}0} zvgdY>WJ!(Fg%$Wuc)IU{n7AR6RIwzAB30jkhUb&=$(o0sH0!x2GfAv zBB8&=W^cHDYesLmaBx-s^>Q;c@sMWv9Sx0()vgyvZ)_O0luWo24tcgv#L-?M=R z3gp_9<_G(sa_vVS*a4Yln3YV-n5HZLf`V>RaPqgXI|eVUQy5lDn(rK$a;3*OmkR+W*yjy}I-U{_O_3=kZ$o|st1SDFs6=`_aT_ynv9 tDv76)=ER`S9Zp)VlVKlr9ZbjI)4lT(*xI;`YhN0+{zl~lmI0=8_y=?@+9m)1 diff --git a/src/types/icons/font.ts b/src/types/icons/font.ts index 8d91a7400..6792d6e59 100644 --- a/src/types/icons/font.ts +++ b/src/types/icons/font.ts @@ -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'