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 b342279b0..666ecc276 100644 Binary files a/src/styles/icons.woff and b/src/styles/icons.woff differ diff --git a/src/styles/icons.woff2 b/src/styles/icons.woff2 index 871d36114..f7455e16c 100644 Binary files a/src/styles/icons.woff2 and b/src/styles/icons.woff2 differ 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'