From 8dbc9a590eb6e81302187aaa321543d748908340 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Thu, 28 Dec 2023 14:38:19 +0100 Subject: [PATCH] Layer 169 (#4131) --- src/api/gramjs/apiBuilders/chats.ts | 25 +- src/api/gramjs/apiBuilders/messageContent.ts | 38 ++- src/api/gramjs/apiBuilders/payments.ts | 2 +- src/api/gramjs/apiBuilders/peers.ts | 16 +- src/api/gramjs/apiBuilders/stories.ts | 77 ++++- src/api/gramjs/apiBuilders/users.ts | 17 +- src/api/gramjs/methods/statistics.ts | 34 +- src/api/gramjs/methods/stories.ts | 5 +- src/api/gramjs/methods/symbols.ts | 4 +- src/api/gramjs/updater.ts | 5 +- src/api/types/chats.ts | 5 +- src/api/types/messages.ts | 17 + src/api/types/statistics.ts | 3 +- src/api/types/stories.ts | 37 ++- src/assets/tgs/general/PartyPopper.tgs | Bin 0 -> 14103 bytes src/components/common/FullNameTitle.tsx | 7 +- src/components/common/GroupChatInfo.tsx | 15 +- src/components/common/PickerSelectedItem.tsx | 4 + src/components/common/PrivateChatInfo.tsx | 3 +- .../common/helpers/animatedAssets.ts | 2 + src/components/main/Main.tsx | 3 +- src/components/middle/ActionMessage.tsx | 2 +- src/components/middle/MiddleHeader.tsx | 1 + .../middle/message/Giveaway.module.scss | 40 ++- src/components/middle/message/Giveaway.tsx | 255 ++++++++++----- src/components/middle/message/Message.tsx | 3 +- .../message/helpers/buildContentClassName.ts | 5 +- .../modals/giftcode/GiftCodeModal.tsx | 34 +- src/components/story/Story.tsx | 7 +- src/components/story/StoryView.tsx | 117 +++++-- .../story/StoryViewModal.module.scss | 5 + src/components/story/StoryViewModal.tsx | 50 +-- .../story/mediaArea/MediaAreaOverlay.tsx | 23 +- src/global/actions/api/payments.ts | 3 +- src/global/actions/api/statistics.ts | 16 +- src/global/actions/api/stories.ts | 10 +- src/global/actions/ui/stories.ts | 25 +- src/global/helpers/messageSummary.ts | 5 + src/global/helpers/messages.ts | 4 +- src/global/reducers/stories.ts | 14 +- src/global/selectors/messages.ts | 2 +- src/global/types.ts | 12 +- src/lib/gramjs/tl/AllTLObjects.js | 2 +- src/lib/gramjs/tl/api.d.ts | 299 +++++++++++++++--- src/lib/gramjs/tl/apiTl.js | 46 ++- src/lib/gramjs/tl/static/api.tl | 57 ++-- 46 files changed, 1014 insertions(+), 342 deletions(-) create mode 100644 src/assets/tgs/general/PartyPopper.tgs diff --git a/src/api/gramjs/apiBuilders/chats.ts b/src/api/gramjs/apiBuilders/chats.ts index a779921f4..27f7dcaa4 100644 --- a/src/api/gramjs/apiBuilders/chats.ts +++ b/src/api/gramjs/apiBuilders/chats.ts @@ -19,13 +19,17 @@ import type { ApiTopic, } from '../../types'; -import { pick, pickTruthy } from '../../../util/iteratees'; +import { omitUndefined, pick, pickTruthy } from '../../../util/iteratees'; import { getServerTime, getServerTimeOffset } from '../../../util/serverTime'; import { buildApiUsernames } from './common'; import { omitVirtualClassFields } from './helpers'; import { + buildApiEmojiStatus, buildApiPeerColor, - buildApiPeerId, getApiChatIdFromMtpPeer, isPeerChat, isPeerUser, + buildApiPeerId, + getApiChatIdFromMtpPeer, + isPeerChat, + isPeerUser, } from './peers'; import { buildApiReaction } from './reactions'; @@ -40,10 +44,10 @@ function buildApiChatFieldsFromPeerEntity( isSupport = false, ): PeerEntityApiChatFields { const isMin = Boolean('min' in peerEntity && peerEntity.min); - const accessHash = ('accessHash' in peerEntity) && String(peerEntity.accessHash); + const accessHash = ('accessHash' in peerEntity) ? String(peerEntity.accessHash) : undefined; const hasVideoAvatar = 'photo' in peerEntity && peerEntity.photo && 'hasVideo' in peerEntity.photo && peerEntity.photo.hasVideo; - const avatarHash = ('photo' in peerEntity) && peerEntity.photo && buildAvatarHash(peerEntity.photo); + const avatarHash = ('photo' in peerEntity) && peerEntity.photo ? buildAvatarHash(peerEntity.photo) : undefined; const isSignaturesShown = Boolean('signatures' in peerEntity && peerEntity.signatures); const hasPrivateLink = Boolean('hasLink' in peerEntity && peerEntity.hasLink); const isScam = Boolean('scam' in peerEntity && peerEntity.scam); @@ -56,15 +60,17 @@ function buildApiChatFieldsFromPeerEntity( const maxStoryId = 'storiesMaxId' in peerEntity ? peerEntity.storiesMaxId : undefined; const storiesUnavailable = Boolean('storiesUnavailable' in peerEntity && peerEntity.storiesUnavailable); const color = ('color' in peerEntity && peerEntity.color) ? buildApiPeerColor(peerEntity.color) : undefined; + const emojiStatus = ('emojiStatus' in peerEntity && peerEntity.emojiStatus) + ? buildApiEmojiStatus(peerEntity.emojiStatus) : undefined; - return { + return omitUndefined({ isMin, hasPrivateLink, isSignaturesShown, usernames, - ...(accessHash && { accessHash }), + accessHash, hasVideoAvatar, - ...(avatarHash && { avatarHash }), + avatarHash, ...('verified' in peerEntity && { isVerified: peerEntity.verified }), ...('callActive' in peerEntity && { isCallActive: peerEntity.callActive }), ...('callNotEmpty' in peerEntity && { isCallNotEmpty: peerEntity.callNotEmpty }), @@ -73,7 +79,7 @@ function buildApiChatFieldsFromPeerEntity( membersCount: peerEntity.participantsCount, }), ...('noforwards' in peerEntity && { isProtected: Boolean(peerEntity.noforwards) }), - ...(isSupport && { isSupport: true }), + isSupport: isSupport || undefined, ...buildApiChatPermissions(peerEntity), ...('creator' in peerEntity && { isCreator: peerEntity.creator }), ...buildApiChatRestrictions(peerEntity), @@ -86,7 +92,8 @@ function buildApiChatFieldsFromPeerEntity( areStoriesHidden, maxStoryId, hasStories: Boolean(maxStoryId) && !storiesUnavailable, - }; + emojiStatus, + }); } export function buildApiChatFromDialog( diff --git a/src/api/gramjs/apiBuilders/messageContent.ts b/src/api/gramjs/apiBuilders/messageContent.ts index 2e43ca914..1aead322e 100644 --- a/src/api/gramjs/apiBuilders/messageContent.ts +++ b/src/api/gramjs/apiBuilders/messageContent.ts @@ -7,6 +7,7 @@ import type { ApiFormattedText, ApiGame, ApiGiveaway, + ApiGiveawayResults, ApiInvoice, ApiLocation, ApiMessageExtendedMediaPreview, @@ -122,6 +123,9 @@ export function buildMessageMediaContent(media: GramJs.TypeMessageMedia): MediaC const giveaway = buildGiweawayFromMedia(media); if (giveaway) return { giveaway }; + const giveawayResults = buildGiweawayResultsFromMedia(media); + if (giveawayResults) return { giveawayResults }; + return undefined; } @@ -480,7 +484,7 @@ function buildGiweawayFromMedia(media: GramJs.TypeMessageMedia): ApiGiveaway | u function buildGiveaway(media: GramJs.MessageMediaGiveaway): ApiGiveaway | undefined { const { - channels, months, quantity, untilDate, countriesIso2, onlyNewSubscribers, + channels, months, quantity, untilDate, countriesIso2, onlyNewSubscribers, prizeDescription, } = media; const channelIds = channels.map((channel) => buildApiPeerId(channel, 'channel')); @@ -492,6 +496,38 @@ function buildGiveaway(media: GramJs.MessageMediaGiveaway): ApiGiveaway | undefi untilDate, countries: countriesIso2, isOnlyForNewSubscribers: onlyNewSubscribers, + prizeDescription, + }; +} + +function buildGiweawayResultsFromMedia(media: GramJs.TypeMessageMedia): ApiGiveawayResults | undefined { + if (!(media instanceof GramJs.MessageMediaGiveawayResults)) { + return undefined; + } + + return buildGiveawayResults(media); +} + +function buildGiveawayResults(media: GramJs.MessageMediaGiveawayResults): ApiGiveawayResults | undefined { + const { + months, untilDate, onlyNewSubscribers, launchMsgId, unclaimedCount, winners, winnersCount, + additionalPeersCount, prizeDescription, refunded, channelId, + } = media; + + const winnerIds = winners.map((winner) => buildApiPeerId(winner, 'user')); + + return { + months, + untilDate, + isOnlyForNewSubscribers: onlyNewSubscribers, + launchMessageId: launchMsgId, + channelId: buildApiPeerId(channelId, 'channel'), + unclaimedCount, + additionalPeersCount, + isRefunded: refunded, + prizeDescription, + winnerIds, + winnersCount, }; } diff --git a/src/api/gramjs/apiBuilders/payments.ts b/src/api/gramjs/apiBuilders/payments.ts index 7fa293b74..f279a3975 100644 --- a/src/api/gramjs/apiBuilders/payments.ts +++ b/src/api/gramjs/apiBuilders/payments.ts @@ -248,7 +248,7 @@ export function buildApiGiveawayInfo(info: GramJs.payments.TypeGiveawayInfo): Ap type: 'active', startDate, isParticipating: participating, - adminDisallowedChatId: adminDisallowedChatId?.toString(), + adminDisallowedChatId: adminDisallowedChatId && buildApiPeerId(adminDisallowedChatId, 'channel'), disallowedCountry, joinedTooEarlyDate, isPreparingResults: preparingResults, diff --git a/src/api/gramjs/apiBuilders/peers.ts b/src/api/gramjs/apiBuilders/peers.ts index 0d1b1d1ec..13e18a8a9 100644 --- a/src/api/gramjs/apiBuilders/peers.ts +++ b/src/api/gramjs/apiBuilders/peers.ts @@ -1,7 +1,7 @@ import type BigInt from 'big-integer'; +import { Api as GramJs } from '../../../lib/gramjs'; -import type { Api as GramJs } from '../../../lib/gramjs'; -import type { ApiPeerColor } from '../../types'; +import type { ApiEmojiStatus, ApiPeerColor } from '../../types'; export function isPeerUser(peer: GramJs.TypePeer | GramJs.TypeInputPeer): peer is GramJs.PeerUser { return peer.hasOwnProperty('userId'); @@ -44,3 +44,15 @@ export function buildApiPeerColor(peerColor: GramJs.TypePeerColor): ApiPeerColor backgroundEmojiId: backgroundEmojiId?.toString(), }; } + +export function buildApiEmojiStatus(mtpEmojiStatus: GramJs.TypeEmojiStatus): ApiEmojiStatus | undefined { + if (mtpEmojiStatus instanceof GramJs.EmojiStatus) { + return { documentId: mtpEmojiStatus.documentId.toString() }; + } + + if (mtpEmojiStatus instanceof GramJs.EmojiStatusUntil) { + return { documentId: mtpEmojiStatus.documentId.toString(), until: mtpEmojiStatus.until }; + } + + return undefined; +} diff --git a/src/api/gramjs/apiBuilders/stories.ts b/src/api/gramjs/apiBuilders/stories.ts index 03e61a29f..82867cd42 100644 --- a/src/api/gramjs/apiBuilders/stories.ts +++ b/src/api/gramjs/apiBuilders/stories.ts @@ -5,14 +5,17 @@ import type { ApiMediaAreaCoordinates, ApiStealthMode, ApiStoryForwardInfo, - ApiStoryView, ApiStoryViews, + ApiStoryView, + ApiStoryViews, ApiTypeStory, + ApiTypeStoryView, MediaContent, } from '../../types'; -import { buildCollectionByCallback } from '../../../util/iteratees'; +import { buildCollectionByCallback, omitUndefined } from '../../../util/iteratees'; import { buildPrivacyRules } from './common'; import { buildGeoPoint, buildMessageMediaContent, buildMessageTextContent } from './messageContent'; +import { buildApiMessage } from './messages'; import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers'; import { buildApiReaction, buildReactionCount } from './reactions'; @@ -88,17 +91,53 @@ function buildApiStoryViews(views: GramJs.TypeStoryViews): ApiStoryViews | undef }; } -export function buildApiStoryView(view: GramJs.TypeStoryView): ApiStoryView { +export function buildApiStoryView(view: GramJs.TypeStoryView): ApiTypeStoryView | undefined { const { - userId, date, reaction, blockedMyStoriesFrom, blocked, + blockedMyStoriesFrom, blocked, } = view; - return { - userId: userId.toString(), - date, - ...(reaction && { reaction: buildApiReaction(reaction) }), - areStoriesBlocked: blocked || blockedMyStoriesFrom, - isUserBlocked: blocked, - }; + + if (view instanceof GramJs.StoryView) { + return omitUndefined({ + type: 'user', + peerId: buildApiPeerId(view.userId, 'user'), + date: view.date, + reaction: view.reaction && buildApiReaction(view.reaction), + areStoriesBlocked: blocked || blockedMyStoriesFrom, + isUserBlocked: blocked, + }); + } + + if (view instanceof GramJs.StoryViewPublicForward) { + const message = buildApiMessage(view.message); + if (!message) return undefined; + return { + type: 'forward', + peerId: message.chatId, + messageId: message.id, + message, + date: message.date, + areStoriesBlocked: blocked || blockedMyStoriesFrom, + isUserBlocked: blocked, + }; + } + + if (view instanceof GramJs.StoryViewPublicRepost) { + const peerId = getApiChatIdFromMtpPeer(view.peerId); + const story = buildApiStory(peerId, view.story); + if (!('content' in story)) return undefined; + + return { + type: 'repost', + peerId, + storyId: view.story.id, + date: story.date, + story, + areStoriesBlocked: blocked || blockedMyStoriesFrom, + isUserBlocked: blocked, + }; + } + + return undefined; } export function buildApiStealthMode(stealthMode: GramJs.TypeStoriesStealthMode): ApiStealthMode { @@ -167,6 +206,17 @@ export function buildApiMediaArea(area: GramJs.TypeMediaArea): ApiMediaArea | un }; } + if (area instanceof GramJs.MediaAreaChannelPost) { + const { coordinates, channelId, msgId } = area; + + return { + type: 'channelPost', + coordinates: buildApiMediaAreaCoordinates(coordinates), + channelId: buildApiPeerId(channelId, 'channel'), + messageId: msgId, + }; + } + return undefined; } @@ -177,11 +227,14 @@ export function buildApiPeerStories(peerStories: GramJs.PeerStories) { } export function buildApiStoryForwardInfo(forwardHeader: GramJs.TypeStoryFwdHeader): ApiStoryForwardInfo { - const { from, fromName, storyId } = forwardHeader; + const { + from, fromName, storyId, modified, + } = forwardHeader; return { storyId, fromPeerId: from && getApiChatIdFromMtpPeer(from), fromName, + isModified: modified, }; } diff --git a/src/api/gramjs/apiBuilders/users.ts b/src/api/gramjs/apiBuilders/users.ts index 8cd0980aa..db74a705e 100644 --- a/src/api/gramjs/apiBuilders/users.ts +++ b/src/api/gramjs/apiBuilders/users.ts @@ -1,7 +1,6 @@ import { Api as GramJs } from '../../../lib/gramjs'; import type { - ApiEmojiStatus, ApiPremiumGiftOption, ApiUser, ApiUserFullInfo, @@ -11,7 +10,7 @@ import type { import { buildApiBotInfo } from './bots'; import { buildApiPhoto, buildApiUsernames } from './common'; -import { buildApiPeerColor, buildApiPeerId } from './peers'; +import { buildApiEmojiStatus, buildApiPeerColor, buildApiPeerId } from './peers'; export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUserFullInfo { const { @@ -57,7 +56,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined { : undefined; const userType = buildApiUserType(mtpUser); const usernames = buildApiUsernames(mtpUser); - const emojiStatus = mtpUser.emojiStatus ? buildApiUserEmojiStatus(mtpUser.emojiStatus) : undefined; + const emojiStatus = mtpUser.emojiStatus ? buildApiEmojiStatus(mtpUser.emojiStatus) : undefined; return { id: buildApiPeerId(id, 'user'), @@ -116,18 +115,6 @@ export function buildApiUserStatus(mtpStatus?: GramJs.TypeUserStatus): ApiUserSt } } -export function buildApiUserEmojiStatus(mtpEmojiStatus: GramJs.TypeEmojiStatus): ApiEmojiStatus | undefined { - if (mtpEmojiStatus instanceof GramJs.EmojiStatus) { - return { documentId: mtpEmojiStatus.documentId.toString() }; - } - - if (mtpEmojiStatus instanceof GramJs.EmojiStatusUntil) { - return { documentId: mtpEmojiStatus.documentId.toString(), until: mtpEmojiStatus.until }; - } - - return undefined; -} - export function buildApiUsersAndStatuses(mtpUsers: GramJs.TypeUser[]) { const userStatusesById: Record = {}; const usersById: Record = {}; diff --git a/src/api/gramjs/methods/statistics.ts b/src/api/gramjs/methods/statistics.ts index c367659da..31384c6b7 100644 --- a/src/api/gramjs/methods/statistics.ts +++ b/src/api/gramjs/methods/statistics.ts @@ -87,22 +87,23 @@ export async function fetchMessagePublicForwards({ chat, messageId, dcId, - offsetRate = 0, + offset, }: { chat: ApiChat; messageId: number; dcId?: number; - offsetRate?: number; + offset?: string; }): Promise<{ forwards?: ApiMessagePublicForward[]; count?: number; - nextRate?: number; + nextOffset?: string; + chats: ApiChat[]; + users: ApiUser[]; } | undefined> { const result = await invokeRequest(new GramJs.stats.GetMessagePublicForwards({ channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel, msgId: messageId, - offsetPeer: new GramJs.InputPeerEmpty(), - offsetRate, + offset, limit: STATISTICS_PUBLIC_FORWARDS_LIMIT, }), { dcId, @@ -112,16 +113,15 @@ export async function fetchMessagePublicForwards({ return undefined; } - if ('chats' in result) { - addEntitiesToLocalDb(result.chats); - } + addEntitiesToLocalDb(result.chats); + addEntitiesToLocalDb(result.users); return { forwards: buildMessagePublicForwards(result), - ...('nextRate' in result ? { - count: result.count, - nextRate: result.nextRate, - } : undefined), + count: result.count, + nextOffset: result.nextOffset, + chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), + users: result.users.map(buildApiUser).filter(Boolean), }; } @@ -177,23 +177,23 @@ export async function fetchStoryPublicForwards({ chat, storyId, dcId, - offsetId = '0', + offset, }: { chat: ApiChat; storyId: number; dcId?: number; - offsetId?: string; + offset?: string; }): Promise<{ publicForwards: (ApiMessagePublicForward | ApiStoryPublicForward)[] | undefined; users: ApiUser[]; chats: ApiChat[]; count?: number; - nextOffsetId?: string; + nextOffset?: string; } | undefined> { const result = await invokeRequest(new GramJs.stats.GetStoryPublicForwards({ peer: buildInputPeer(chat.id, chat.accessHash), id: storyId, - offset: offsetId, + offset, limit: STATISTICS_PUBLIC_FORWARDS_LIMIT, }), { dcId, @@ -211,6 +211,6 @@ export async function fetchStoryPublicForwards({ users: result.users.map(buildApiUser).filter(Boolean), chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), count: result.count, - nextOffsetId: result.nextOffset, + nextOffset: result.nextOffset, }; } diff --git a/src/api/gramjs/methods/stories.ts b/src/api/gramjs/methods/stories.ts index 89f7e6023..36ad42abd 100644 --- a/src/api/gramjs/methods/stories.ts +++ b/src/api/gramjs/methods/stories.ts @@ -285,11 +285,14 @@ export async function fetchStoryViewList({ } addEntitiesToLocalDb(result.users); + addEntitiesToLocalDb(result.chats); const users = result.users.map(buildApiUser).filter(Boolean); - const views = result.views.map(buildApiStoryView); + const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); + const views = result.views.map(buildApiStoryView).filter(Boolean); return { users, + chats, views, nextOffset: result.nextOffset, reactionsCount: result.reactionsCount, diff --git a/src/api/gramjs/methods/symbols.ts b/src/api/gramjs/methods/symbols.ts index b050c4ffb..31192d6af 100644 --- a/src/api/gramjs/methods/symbols.ts +++ b/src/api/gramjs/methods/symbols.ts @@ -7,10 +7,10 @@ import type { import { DEFAULT_GIF_SEARCH_BOT_USERNAME, RECENT_STATUS_LIMIT, RECENT_STICKERS_LIMIT } from '../../../config'; import { buildVideoFromDocument } from '../apiBuilders/messageContent'; +import { buildApiEmojiStatus } from '../apiBuilders/peers'; import { buildStickerSet, buildStickerSetCovered, processStickerPackResult, processStickerResult, } from '../apiBuilders/symbols'; -import { buildApiUserEmojiStatus } from '../apiBuilders/users'; import { buildInputDocument, buildInputStickerSet, buildInputStickerSetShortName } from '../gramjsBuilders'; import localDb from '../localDb'; import { invokeRequest } from './client'; @@ -445,7 +445,7 @@ export async function fetchRecentEmojiStatuses(hash = '0') { const documentIds = result.statuses .slice(0, RECENT_STATUS_LIMIT) - .map(buildApiUserEmojiStatus) + .map(buildApiEmojiStatus) .filter(Boolean) .map(({ documentId }) => documentId); const emojiStatuses = await fetchCustomEmoji({ documentId: documentIds }); diff --git a/src/api/gramjs/updater.ts b/src/api/gramjs/updater.ts index 0d530849c..f558c6d86 100644 --- a/src/api/gramjs/updater.ts +++ b/src/api/gramjs/updater.ts @@ -46,7 +46,7 @@ import { buildApiNotifyExceptionTopic, buildPrivacyKey, } from './apiBuilders/misc'; -import { buildApiPeerId, getApiChatIdFromMtpPeer } from './apiBuilders/peers'; +import { buildApiEmojiStatus, buildApiPeerId, getApiChatIdFromMtpPeer } from './apiBuilders/peers'; import { buildApiReaction, buildMessageReactions, @@ -55,7 +55,6 @@ import { buildApiStealthMode, buildApiStory } from './apiBuilders/stories'; import { buildApiEmojiInteraction, buildStickerSet } from './apiBuilders/symbols'; import { buildApiUser, - buildApiUserEmojiStatus, buildApiUserStatus, } from './apiBuilders/users'; import { @@ -795,7 +794,7 @@ export function updater(update: Update) { id: buildApiPeerId(update.userId, 'user'), }); } else if (update instanceof GramJs.UpdateUserEmojiStatus) { - const emojiStatus = buildApiUserEmojiStatus(update.emojiStatus); + const emojiStatus = buildApiEmojiStatus(update.emojiStatus); onUpdate({ '@type': 'updateUserEmojiStatus', userId: buildApiPeerId(update.userId, 'user'), diff --git a/src/api/types/chats.ts b/src/api/types/chats.ts index 60d8d85a4..4dddfc705 100644 --- a/src/api/types/chats.ts +++ b/src/api/types/chats.ts @@ -3,7 +3,9 @@ import type { ApiChatReactions, ApiMessage, ApiPhoto, ApiStickerSet, } from './messages'; import type { ApiChatInviteImporter } from './misc'; -import type { ApiFakeType, ApiUser, ApiUsername } from './users'; +import type { + ApiEmojiStatus, ApiFakeType, ApiUser, ApiUsername, +} from './users'; type ApiChatType = ( 'chatTypePrivate' | 'chatTypeSecret' | @@ -43,6 +45,7 @@ export interface ApiChat { isProtected?: boolean; fakeType?: ApiFakeType; color?: ApiPeerColor; + emojiStatus?: ApiEmojiStatus; isForum?: boolean; isForumAsMessages?: true; topics?: Record; diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index 9bf4b5684..4f7b87636 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -260,6 +260,21 @@ export type ApiGiveaway = { isOnlyForNewSubscribers?: true; countries?: string[]; channelIds: string[]; + prizeDescription?: string; +}; + +export type ApiGiveawayResults = { + months: number; + untilDate: number; + isRefunded?: true; + isOnlyForNewSubscribers?: true; + channelId: string; + prizeDescription?: string; + winnersCount?: number; + winnerIds: string[]; + additionalPeersCount?: number; + launchMessageId: number; + unclaimedCount: number; }; export type ApiNewPoll = { @@ -370,6 +385,7 @@ export interface ApiStoryForwardInfo { fromPeerId?: string; fromName?: string; storyId?: number; + isModified?: boolean; } export type ApiMessageEntityDefault = { @@ -458,6 +474,7 @@ export type MediaContent = { game?: ApiGame; storyData?: ApiMessageStoryData; giveaway?: ApiGiveaway; + giveawayResults?: ApiGiveawayResults; }; export interface ApiMessage { diff --git a/src/api/types/statistics.ts b/src/api/types/statistics.ts index b0c9cefd1..459e4da90 100644 --- a/src/api/types/statistics.ts +++ b/src/api/types/statistics.ts @@ -46,8 +46,7 @@ export interface ApiPostStatistics { publicForwards?: number; publicForwardsData?: (ApiMessagePublicForward | ApiStoryPublicForward)[]; - nextRate?: number; - nextOffsetId?: string; + nextOffset?: string; } export interface ApiBoostStatistics { diff --git a/src/api/types/stories.ts b/src/api/types/stories.ts index 13fd8fe92..760725be1 100644 --- a/src/api/types/stories.ts +++ b/src/api/types/stories.ts @@ -1,6 +1,6 @@ import type { ApiPrivacySettings } from '../../types'; import type { - ApiGeoPoint, ApiReaction, ApiReactionCount, ApiStoryForwardInfo, MediaContent, + ApiGeoPoint, ApiMessage, ApiReaction, ApiReactionCount, ApiStoryForwardInfo, MediaContent, } from './messages'; export interface ApiStory { @@ -71,14 +71,37 @@ export type ApiWebPageStoryData = { peerId: string; }; +export type ApiStoryViewPublicForward = { + type: 'forward'; + peerId: string; + messageId: number; + message: ApiMessage; + date: number; + isUserBlocked?: true; + areStoriesBlocked?: true; +}; + +export type ApiStoryViewPublicRepost = { + type: 'repost'; + isUserBlocked?: true; + areStoriesBlocked?: true; + date: number; + peerId: string; + storyId: number; + story: ApiStory; +}; + export type ApiStoryView = { - userId: string; + type: 'user'; + peerId: string; date: number; reaction?: ApiReaction; isUserBlocked?: true; areStoriesBlocked?: true; }; +export type ApiTypeStoryView = ApiStoryView | ApiStoryViewPublicForward | ApiStoryViewPublicRepost; + export type ApiStealthMode = { activeUntil?: number; cooldownUntil?: number; @@ -113,4 +136,12 @@ export type ApiMediaAreaSuggestedReaction = { isFlipped?: boolean; }; -export type ApiMediaArea = ApiMediaAreaVenue | ApiMediaAreaGeoPoint | ApiMediaAreaSuggestedReaction; +export type ApiMediaAreaChannelPost = { + type: 'channelPost'; + coordinates: ApiMediaAreaCoordinates; + channelId: string; + messageId: number; +}; + +export type ApiMediaArea = ApiMediaAreaVenue | ApiMediaAreaGeoPoint | ApiMediaAreaSuggestedReaction +| ApiMediaAreaChannelPost; diff --git a/src/assets/tgs/general/PartyPopper.tgs b/src/assets/tgs/general/PartyPopper.tgs new file mode 100644 index 0000000000000000000000000000000000000000..d1112ecbe732edca653edd932309de28c6393ea6 GIT binary patch literal 14103 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 VK3R8fCVlhA{|7}PTEIU30RV~bMGOD{ literal 0 HcmV?d00001 diff --git a/src/components/common/FullNameTitle.tsx b/src/components/common/FullNameTitle.tsx index 8bd5a53ab..d5d5a3106 100644 --- a/src/components/common/FullNameTitle.tsx +++ b/src/components/common/FullNameTitle.tsx @@ -53,7 +53,6 @@ const FullNameTitle: FC = ({ const { showNotification } = getActions(); const isUser = isUserId(peer.id); const title = isUser ? getUserFullName(peer as ApiUser) : getChatTitle(lang, peer as ApiChat); - const emojiStatus = isUser && (peer as ApiUser).emojiStatus; const isPremium = isUser && (peer as ApiUser).isPremium; const handleTitleClick = useLastCallback((e) => { @@ -86,16 +85,16 @@ const FullNameTitle: FC = ({ {!noVerified && peer.isVerified && } {!noFake && peer.fakeType && } - {withEmojiStatus && emojiStatus && ( + {withEmojiStatus && peer.emojiStatus && ( )} - {withEmojiStatus && !emojiStatus && isPremium && } + {withEmojiStatus && !peer.emojiStatus && isPremium && } ); }; diff --git a/src/components/common/GroupChatInfo.tsx b/src/components/common/GroupChatInfo.tsx index 32bcf0f70..5e135596d 100644 --- a/src/components/common/GroupChatInfo.tsx +++ b/src/components/common/GroupChatInfo.tsx @@ -6,6 +6,7 @@ import type { ApiChat, ApiThreadInfo, ApiTopic, ApiTypingStatus, } from '../../api/types'; import type { LangFn } from '../../hooks/useLang'; +import type { IconName } from '../../types/icons'; import { MediaViewerOrigin, type StoryViewerOrigin } from '../../types'; import { @@ -30,6 +31,7 @@ import useLastCallback from '../../hooks/useLastCallback'; import Avatar from './Avatar'; import DotAnimation from './DotAnimation'; import FullNameTitle from './FullNameTitle'; +import Icon from './Icon'; import TopicIcon from './TopicIcon'; import TypingStatus from './TypingStatus'; @@ -39,6 +41,7 @@ type OwnProps = { chatId: string; threadId?: number; className?: string; + statusIcon?: IconName; typingStatus?: ApiTypingStatus; avatarSize?: 'tiny' | 'small' | 'medium' | 'large' | 'jumbo'; status?: string; @@ -48,6 +51,8 @@ type OwnProps = { withFullInfo?: boolean; withUpdatingStatus?: boolean; withChatType?: boolean; + noEmojiStatus?: boolean; + emojiStatusSize?: number; noRtl?: boolean; noAvatar?: boolean; noStatusOrTyping?: boolean; @@ -69,6 +74,7 @@ type StateProps = const GroupChatInfo: FC = ({ typingStatus, className, + statusIcon, avatarSize = 'medium', noAvatar, status, @@ -88,6 +94,8 @@ const GroupChatInfo: FC = ({ noStatusOrTyping, withStory, storyViewerOrigin, + noEmojiStatus, + emojiStatusSize, onClick, }) => { const { @@ -133,7 +141,10 @@ const GroupChatInfo: FC = ({ return withDots ? ( ) : ( - {status} + + {statusIcon && } + {renderText(status)} + ); } @@ -206,7 +217,7 @@ const GroupChatInfo: FC = ({
{topic ?

{renderText(topic.title)}

- : } + : } {!noStatusOrTyping && renderStatusOrTyping()}
diff --git a/src/components/common/PickerSelectedItem.tsx b/src/components/common/PickerSelectedItem.tsx index c0343b1aa..1048d352c 100644 --- a/src/components/common/PickerSelectedItem.tsx +++ b/src/components/common/PickerSelectedItem.tsx @@ -8,6 +8,7 @@ import type { IconName } from '../../types/icons'; import { getChatTitle, getUserFirstOrLastName } from '../../global/helpers'; import { selectChat, selectUser } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; +import { getPeerColorClass } from './helpers/peerColor'; import renderText from './helpers/renderText'; import useLang from '../../hooks/useLang'; @@ -26,6 +27,7 @@ type OwnProps = { clickArg?: any; className?: string; fluid?: boolean; + withPeerColors?: boolean; onClick: (arg: any) => void; }; @@ -46,6 +48,7 @@ const PickerSelectedItem: FC = ({ className, fluid, isSavedMessages, + withPeerColors, onClick, }) => { const lang = useLang(); @@ -84,6 +87,7 @@ const PickerSelectedItem: FC = ({ isMinimized && 'minimized', canClose && 'closeable', fluid && 'fluid', + withPeerColors && getPeerColorClass(chat || user), ); return ( diff --git a/src/components/common/PrivateChatInfo.tsx b/src/components/common/PrivateChatInfo.tsx index 7cf0ff191..bb219275f 100644 --- a/src/components/common/PrivateChatInfo.tsx +++ b/src/components/common/PrivateChatInfo.tsx @@ -21,6 +21,7 @@ import RippleEffect from '../ui/RippleEffect'; import Avatar from './Avatar'; import DotAnimation from './DotAnimation'; import FullNameTitle from './FullNameTitle'; +import Icon from './Icon'; import TypingStatus from './TypingStatus'; type OwnProps = { @@ -122,7 +123,7 @@ const PrivateChatInfo: FC = ({ ) : ( - {statusIcon && } + {statusIcon && } {renderText(status)} ); diff --git a/src/components/common/helpers/animatedAssets.ts b/src/components/common/helpers/animatedAssets.ts index 5f98c1116..a1fac8420 100644 --- a/src/components/common/helpers/animatedAssets.ts +++ b/src/components/common/helpers/animatedAssets.ts @@ -7,6 +7,7 @@ import VoiceAllowTalk from '../../../assets/tgs/calls/VoiceAllowTalk.tgs'; import VoiceMini from '../../../assets/tgs/calls/VoiceMini.tgs'; import VoiceMuted from '../../../assets/tgs/calls/VoiceMuted.tgs'; import VoiceOutlined from '../../../assets/tgs/calls/VoiceOutlined.tgs'; +import PartyPopper from '../../../assets/tgs/general/PartyPopper.tgs'; import Invite from '../../../assets/tgs/invites/Invite.tgs'; import JoinRequest from '../../../assets/tgs/invites/Requests.tgs'; import MonkeyClose from '../../../assets/tgs/monkeys/TwoFactorSetupMonkeyClose.tgs'; @@ -44,4 +45,5 @@ export const LOCAL_TGS_URLS = { QrPlane, Congratulations, Experimental, + PartyPopper, }; diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index b94de2222..07747e51e 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -179,7 +179,6 @@ const Main: FC = ({ isMediaViewerOpen, isStoryViewerOpen, isForwardModalOpen, - currentUserId, hasNotifications, hasDialogs, audioMessage, @@ -568,7 +567,7 @@ const Main: FC = ({ isByPhoneNumber={newContactByPhoneNumber} /> - + diff --git a/src/components/middle/ActionMessage.tsx b/src/components/middle/ActionMessage.tsx index c07e90fee..045e50524 100644 --- a/src/components/middle/ActionMessage.tsx +++ b/src/components/middle/ActionMessage.tsx @@ -203,7 +203,7 @@ const ActionMessage: FC = ({ const handleGiftCodeClick = () => { const slug = message.content.action?.slug; if (!slug) return; - checkGiftCode({ slug }); + checkGiftCode({ slug, message: { chatId: message.chatId, messageId: message.id } }); }; // TODO Refactoring for action rendering diff --git a/src/components/middle/MiddleHeader.tsx b/src/components/middle/MiddleHeader.tsx index 931fce55d..07ec90a98 100644 --- a/src/components/middle/MiddleHeader.tsx +++ b/src/components/middle/MiddleHeader.tsx @@ -405,6 +405,7 @@ const MiddleHeader: FC = ({ withUpdatingStatus withStory storyViewerOrigin={StoryViewerOrigin.MiddleHeaderAvatar} + emojiStatusSize={EMOJI_STATUS_SIZE} noRtl /> )} diff --git a/src/components/middle/message/Giveaway.module.scss b/src/components/middle/message/Giveaway.module.scss index 3e0059b71..086792937 100644 --- a/src/components/middle/message/Giveaway.module.scss +++ b/src/components/middle/message/Giveaway.module.scss @@ -7,14 +7,19 @@ .title { display: block; + margin-top: 0.5rem; } -.gift { +.sticker { position: relative; margin-top: -2.5rem; margin-bottom: 1rem; } +.resultSticker { + margin-top: 0; +} + .count { position: absolute; bottom: 0; @@ -43,15 +48,16 @@ margin-bottom: 0; } -.channels { +.peers { display: flex; - flex-direction: column; + flex-wrap: wrap; + justify-content: center; align-items: center; gap: 0.5rem; margin-block: 0.25rem; } -.channel { +.peer { background-color: var(--accent-background-color); color: var(--accent-color); margin: unset; @@ -64,3 +70,29 @@ .button { margin-bottom: 1rem; } + +.result { + font-weight: 500; +} + +.separator { + display: flex; + align-items: center; + text-align: center; + color: var(--color-text-secondary); +} + +.separator::before, +.separator::after { + content: ''; + flex: 1; + border-bottom: 1px solid var(--color-dividers); +} + +.separator:not(:empty)::before { + margin-right: 0.25em; +} + +.separator:not(:empty)::after { + margin-left: 0.25em; +} diff --git a/src/components/middle/message/Giveaway.tsx b/src/components/middle/message/Giveaway.tsx index 1e70c2ccc..17e560c7d 100644 --- a/src/components/middle/message/Giveaway.tsx +++ b/src/components/middle/message/Giveaway.tsx @@ -4,25 +4,30 @@ import React, { import { getActions, getGlobal, withGlobal } from '../../../global'; import type { - ApiChat, ApiGiveawayInfo, ApiMessage, ApiPeer, ApiSticker, + ApiChat, ApiGiveaway, ApiGiveawayInfo, ApiGiveawayResults, ApiMessage, ApiPeer, ApiSticker, } from '../../../api/types'; -import { getChatTitle, getUserFullName, isApiPeerChat } from '../../../global/helpers'; +import { + getChatTitle, getUserFullName, isApiPeerChat, isOwnMessage, +} from '../../../global/helpers'; import { selectCanPlayAnimatedEmojis, selectChat, selectForwardedSender, selectGiftStickerForDuration, } from '../../../global/selectors'; +import buildClassName from '../../../util/buildClassName'; import { formatDateAtTime, formatDateTimeToString } from '../../../util/dateFormat'; import { isoToEmoji } from '../../../util/emoji'; import { getServerTime } from '../../../util/serverTime'; import { callApi } from '../../../api/gramjs'; +import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets'; import renderText from '../../common/helpers/renderText'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; +import AnimatedIcon from '../../common/AnimatedIcon'; import AnimatedIconFromSticker from '../../common/AnimatedIconFromSticker'; import PickerSelectedItem from '../../common/PickerSelectedItem'; import Button from '../../ui/Button'; @@ -43,6 +48,7 @@ type StateProps = { const NBSP = '\u00A0'; const GIFT_STICKER_SIZE = 175; +const RESULT_STICKER_SIZE = 150; const Giveaway = ({ chat, @@ -57,20 +63,27 @@ const Giveaway = ({ const [giveawayInfo, setGiveawayInfo] = useState(); const lang = useLang(); + const { giveaway, giveawayResults } = message.content; + const isResults = Boolean(giveawayResults); const { - months, quantity, channelIds, untilDate, countries, - } = message.content.giveaway!; + months, untilDate, prizeDescription, + } = (giveaway || giveawayResults)!; + + const isOwn = isOwnMessage(message); + + const quantity = isResults ? giveawayResults.winnersCount : giveaway!.quantity; const hasEnded = getServerTime() > untilDate; const countryList = useMemo(() => { + if (isResults) return undefined; const translatedNames = new Intl.DisplayNames([lang.code!, 'en'].filter(Boolean), { type: 'region' }); - return countries?.map((countryCode) => ( + return giveaway?.countries?.map((countryCode) => ( `${isoToEmoji(countryCode)}${NBSP}${translatedNames.of(countryCode)}` )).join(', '); - }, [countries, lang.code]); + }, [giveaway, isResults, lang.code]); - const handleChannelClick = useLastCallback((channelId: string) => { + const handlePeerClick = useLastCallback((channelId: string) => { openChat({ id: channelId }); }); @@ -95,36 +108,149 @@ const Giveaway = ({ return lang(giveawayInfo.type === 'results' ? 'BoostingGiveawayEnd' : 'BoostingGiveAwayAbout'); }, [giveawayInfo, lang]); + function renderGiveawayDescription(media: ApiGiveaway) { + const channelIds = media.channelIds; + return ( + <> +
+ + {renderText(lang('BoostingGiveawayPrizes'), ['simple_markdown'])} + + {prizeDescription && ( + <> +

+ {renderText( + lang('BoostingGiveawayMsgPrizes', [quantity, prizeDescription], undefined, quantity), + ['simple_markdown'], + )} +

+
{lang('BoostingGiveawayMsgWithDivider')}
+ + )} +

+ {renderText(lang('Chat.Giveaway.Info.Subscriptions', quantity), ['simple_markdown'])} +
+ {renderText(lang( + 'ActionGiftPremiumSubtitle', + lang('Chat.Giveaway.Info.Months', months), + ), ['simple_markdown'])} +

+
+
+ + {renderText(lang('BoostingGiveawayMsgParticipants'), ['simple_markdown'])} + +

+ {renderText(lang('BoostingGiveawayMsgAllSubsPlural', channelIds.length), ['simple_markdown'])} +

+
+ {channelIds.map((peerId) => ( + + ))} +
+ {countryList && ( + {renderText(lang('Chat.Giveaway.Message.CountriesFrom', countryList))} + )} +
+
+ + {renderText(lang('BoostingWinnersDate'), ['simple_markdown'])} + +

+ {formatDateTimeToString(untilDate * 1000, lang.code, true)} +

+
+ + ); + } + + function renderGiveawayResultsDescription(media: ApiGiveawayResults) { + const winnerIds = media.winnerIds; + return ( + <> +
+ + {renderText(lang('BoostingGiveawayResultsMsgWinnersSelected'), ['simple_markdown'])} + +

+ {renderText(lang('BoostingGiveawayResultsMsgWinnersTitle', winnerIds.length), ['simple_markdown'])} +

+ + {lang('lng_prizes_results_winners')} + +
+ {winnerIds.map((peerId) => ( + + ))} +
+
+
+

+ {lang('BoostingGiveawayResultsMsgAllWinnersReceivedLinks')} +

+
+ + ); + } + function renderGiveawayInfo() { if (!sender || !giveawayInfo) return undefined; - const isResults = giveawayInfo.type === 'results'; + const isResultsInfo = giveawayInfo.type === 'results'; const chatTitle = isApiPeerChat(sender) ? getChatTitle(lang, sender) : getUserFullName(sender); const duration = lang('Chat.Giveaway.Info.Months', months); const endDate = formatDateAtTime(lang, untilDate * 1000); - const otherChannelsCount = channelIds.length ? channelIds.length - 1 : 0; + const otherChannelsCount = giveaway?.channelIds ? giveaway.channelIds.length - 1 : 0; const otherChannelsString = lang('Chat.Giveaway.Info.OtherChannels', otherChannelsCount); const isSeveral = otherChannelsCount > 0; - const firstKey = isResults ? 'BoostingGiveawayHowItWorksTextEnd' : 'BoostingGiveawayHowItWorksText'; + const firstKey = isResultsInfo ? 'BoostingGiveawayHowItWorksTextEnd' : 'BoostingGiveawayHowItWorksText'; const firstParagraph = lang(firstKey, [chatTitle, quantity, duration], undefined, quantity); + const additionalPrizes = prizeDescription + ? lang('BoostingGiveawayHowItWorksIncludeText', [chatTitle, quantity, prizeDescription], undefined, quantity) + : undefined; + let secondKey = ''; - if (isResults) { + if (isResultsInfo) { secondKey = isSeveral ? 'BoostingGiveawayHowItWorksSubTextSeveralEnd' : 'BoostingGiveawayHowItWorksSubTextEnd'; } else { secondKey = isSeveral ? 'BoostingGiveawayHowItWorksSubTextSeveral' : 'BoostingGiveawayHowItWorksSubText'; } let secondParagraph = lang(secondKey, [endDate, quantity, chatTitle, otherChannelsCount], undefined, quantity); - if (isResults && giveawayInfo.activatedCount) { + if (isResultsInfo && giveawayInfo.activatedCount) { secondParagraph += ` ${lang('BoostingGiveawayUsedLinksPlural', giveawayInfo.activatedCount)}`; } + let result = ''; + + if (isResultsInfo) { + if (giveawayInfo.isRefunded) { + result = lang('BoostingGiveawayCanceledByPayment'); + } else { + result = lang(giveawayInfo.isWinner ? 'BoostingGiveawayYouWon' : 'BoostingGiveawayYouNotWon'); + } + } + let lastParagraph = ''; - if (isResults && giveawayInfo.isRefunded) { - lastParagraph = lang('BoostingGiveawayCanceledByPayment'); - } else if (isResults) { - lastParagraph = lang(giveawayInfo.isWinner ? 'BoostingGiveawayYouWon' : 'BoostingGiveawayYouNotWon'); + if (isResultsInfo) { + // Nothing } else if (giveawayInfo.disallowedCountry) { lastParagraph = lang('BoostingGiveawayNotEligibleCountry'); } else if (giveawayInfo.adminDisallowedChatId) { @@ -148,78 +274,55 @@ const Giveaway = ({ return ( <> + {result && ( +

+ {renderText(result, ['simple_markdown'])} +

+ )}

{renderText(firstParagraph, ['simple_markdown'])}

+ {additionalPrizes && ( +

+ {renderText(additionalPrizes, ['simple_markdown'])} +

+ )}

{renderText(secondParagraph, ['simple_markdown'])}

-

- {renderText(lastParagraph, ['simple_markdown'])} -

+ {lastParagraph && ( +

+ {renderText(lastParagraph, ['simple_markdown'])} +

+ )} ); } return (
-
- +
+ {isResults ? ( + + ) : ( + + )} {`x${quantity}`}
-
- - {renderText(lang('BoostingGiveawayPrizes'), ['simple_markdown'])} - -

- {renderText(lang('Chat.Giveaway.Info.Subscriptions', quantity), ['simple_markdown'])} -
- {renderText(lang( - 'ActionGiftPremiumSubtitle', - lang('Chat.Giveaway.Info.Months', months), - ), ['simple_markdown'])} -

-
-
- - {renderText(lang('BoostingGiveawayMsgParticipants'), ['simple_markdown'])} - -

- {renderText(lang('BoostingGiveawayMsgAllSubsPlural', channelIds.length), ['simple_markdown'])} -

-
- {channelIds.map((channelId) => ( - - ))} -
- {Boolean(countries?.length) && ( - {renderText(lang('Chat.Giveaway.Message.CountriesFrom', countryList))} - )} -
-
- - {renderText(lang('BoostingWinnersDate'), ['simple_markdown'])} - -

- {formatDateTimeToString(untilDate * 1000, lang.code, true)} -

-
+ {isResults ? renderGiveawayResultsDescription(giveawayResults) : renderGiveawayDescription(giveaway!)}