Message: Introduce Invoice Media (#2093)

This commit is contained in:
Alexander Zinchuk 2022-11-07 23:00:55 +04:00
parent 58413c6c61
commit 6e8b920525
29 changed files with 528 additions and 73 deletions

View File

@ -1,7 +1,4 @@
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import {
ApiMessageEntityTypes,
} from '../../types';
import type { import type {
ApiMessage, ApiMessage,
ApiMessageForwardInfo, ApiMessageForwardInfo,
@ -36,6 +33,10 @@ import type {
PhoneCallAction, PhoneCallAction,
ApiWebDocument, ApiWebDocument,
ApiMessageEntityDefault, ApiMessageEntityDefault,
ApiMessageExtendedMediaPreview,
} from '../../types';
import {
ApiMessageEntityTypes,
} from '../../types'; } from '../../types';
import { import {
@ -161,16 +162,21 @@ export function buildApiMessageWithChatId(chatId: string, mtpMessage: UniversalM
content.action = action; content.action = action;
} }
const isInvoiceMedia = mtpMessage.media instanceof GramJs.MessageMediaInvoice
&& Boolean(mtpMessage.media.extendedMedia);
const { replyToMsgId, replyToTopId, replyToPeerId } = mtpMessage.replyTo || {}; const { replyToMsgId, replyToTopId, replyToPeerId } = mtpMessage.replyTo || {};
const isEdited = mtpMessage.editDate && !mtpMessage.editHide; const isEdited = mtpMessage.editDate && !mtpMessage.editHide;
const { const {
inlineButtons, keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse, inlineButtons, keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse,
} = buildReplyButtons(mtpMessage) || {}; } = buildReplyButtons(mtpMessage, isInvoiceMedia) || {};
const forwardInfo = mtpMessage.fwdFrom && buildApiMessageForwardInfo(mtpMessage.fwdFrom, isChatWithSelf); const forwardInfo = mtpMessage.fwdFrom && buildApiMessageForwardInfo(mtpMessage.fwdFrom, isChatWithSelf);
const { replies, mediaUnread: isMediaUnread, postAuthor } = mtpMessage; const { replies, mediaUnread: isMediaUnread, postAuthor } = mtpMessage;
const groupedId = mtpMessage.groupedId && String(mtpMessage.groupedId); const groupedId = mtpMessage.groupedId && String(mtpMessage.groupedId);
const isInAlbum = Boolean(groupedId) && !(content.document || content.audio || content.sticker); const isInAlbum = Boolean(groupedId) && !(content.document || content.audio || content.sticker);
const shouldHideKeyboardButtons = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide; const shouldHideKeyboardButtons = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide;
const isProtected = mtpMessage.noforwards || isInvoiceMedia;
const isForwardingAllowed = !mtpMessage.noforwards;
const emojiOnlyCount = content.text && parseEmojiOnlyString(content.text.text); const emojiOnlyCount = content.text && parseEmojiOnlyString(content.text.text);
return { return {
@ -205,7 +211,8 @@ export function buildApiMessageWithChatId(chatId: string, mtpMessage: UniversalM
...(mtpMessage.viaBotId && { viaBotId: buildApiPeerId(mtpMessage.viaBotId, 'user') }), ...(mtpMessage.viaBotId && { viaBotId: buildApiPeerId(mtpMessage.viaBotId, 'user') }),
...(replies?.comments && { threadInfo: buildThreadInfo(replies, mtpMessage.id, chatId) }), ...(replies?.comments && { threadInfo: buildThreadInfo(replies, mtpMessage.id, chatId) }),
...(postAuthor && { adminTitle: postAuthor }), ...(postAuthor && { adminTitle: postAuthor }),
...(mtpMessage.noforwards && { isProtected: true }), isProtected,
isForwardingAllowed,
}; };
} }
@ -336,6 +343,10 @@ export function buildMessageMediaContent(media: GramJs.TypeMessageMedia): ApiMes
return undefined; return undefined;
} }
if ('extendedMedia' in media && media.extendedMedia instanceof GramJs.MessageExtendedMedia) {
return buildMessageMediaContent(media.extendedMedia.media);
}
const sticker = buildSticker(media); const sticker = buildSticker(media);
if (sticker) return { sticker }; if (sticker) return { sticker };
@ -748,9 +759,12 @@ export function buildPoll(poll: GramJs.Poll, pollResults: GramJs.PollResults): A
export function buildInvoice(media: GramJs.MessageMediaInvoice): ApiInvoice { export function buildInvoice(media: GramJs.MessageMediaInvoice): ApiInvoice {
const { const {
description: text, title, photo, test, totalAmount, currency, receiptMsgId, description: text, title, photo, test, totalAmount, currency, receiptMsgId, extendedMedia,
} = media; } = media;
const preview = extendedMedia instanceof GramJs.MessageExtendedMediaPreview
? buildApiMessageExtendedMediaPreview(extendedMedia) : undefined;
return { return {
title, title,
text, text,
@ -759,6 +773,7 @@ export function buildInvoice(media: GramJs.MessageMediaInvoice): ApiInvoice {
amount: Number(totalAmount), amount: Number(totalAmount),
currency, currency,
isTest: test, isTest: test,
extendedMedia: preview,
}; };
} }
@ -1007,7 +1022,7 @@ function buildAction(
}; };
} }
function buildReplyButtons(message: UniversalMessage): ApiReplyKeyboard | undefined { function buildReplyButtons(message: UniversalMessage, shouldSkipBuyButton?: boolean): ApiReplyKeyboard | undefined {
const { replyMarkup, media } = message; const { replyMarkup, media } = message;
// TODO Move to the proper button inside preview // TODO Move to the proper button inside preview
@ -1033,7 +1048,7 @@ function buildReplyButtons(message: UniversalMessage): ApiReplyKeyboard | undefi
} }
const markup = replyMarkup.rows.map(({ buttons }) => { const markup = replyMarkup.rows.map(({ buttons }) => {
return buttons.map((button): ApiKeyboardButton => { return buttons.map((button): ApiKeyboardButton | undefined => {
const { text } = button; const { text } = button;
if (button instanceof GramJs.KeyboardButton) { if (button instanceof GramJs.KeyboardButton) {
@ -1096,6 +1111,7 @@ function buildReplyButtons(message: UniversalMessage): ApiReplyKeyboard | undefi
receiptMessageId: media.receiptMsgId, receiptMessageId: media.receiptMsgId,
}; };
} }
if (shouldSkipBuyButton) return undefined;
return { return {
type: 'buy', type: 'buy',
text, text,
@ -1155,9 +1171,11 @@ function buildReplyButtons(message: UniversalMessage): ApiReplyKeyboard | undefi
type: 'unsupported', type: 'unsupported',
text, text,
}; };
}); }).filter(Boolean);
}); });
if (markup.every((row) => !row.length)) return undefined;
return { return {
[replyMarkup instanceof GramJs.ReplyKeyboardMarkup ? 'keyboardButtons' : 'inlineButtons']: markup, [replyMarkup instanceof GramJs.ReplyKeyboardMarkup ? 'keyboardButtons' : 'inlineButtons']: markup,
...(replyMarkup instanceof GramJs.ReplyKeyboardMarkup && { ...(replyMarkup instanceof GramJs.ReplyKeyboardMarkup && {
@ -1368,6 +1386,21 @@ function buildUploadingMedia(
}; };
} }
export function buildApiMessageExtendedMediaPreview(
preview: GramJs.MessageExtendedMediaPreview,
): ApiMessageExtendedMediaPreview {
const {
w, h, thumb, videoDuration,
} = preview;
return {
width: w,
height: h,
duration: videoDuration,
thumbnail: thumb ? buildApiThumbnailFromStripped([thumb]) : undefined,
};
}
export function buildApiWebDocument(document?: GramJs.TypeWebDocument): ApiWebDocument | undefined { export function buildApiWebDocument(document?: GramJs.TypeWebDocument): ApiWebDocument | undefined {
if (!document) return undefined; if (!document) return undefined;

View File

@ -353,7 +353,7 @@ export function isMessageWithMedia(message: GramJs.Message | GramJs.UpdateServic
media instanceof GramJs.MessageMediaGame media instanceof GramJs.MessageMediaGame
&& (media.game.document instanceof GramJs.Document || media.game.photo instanceof GramJs.Photo) && (media.game.document instanceof GramJs.Document || media.game.photo instanceof GramJs.Photo)
) || ( ) || (
media instanceof GramJs.MessageMediaInvoice && media.photo media instanceof GramJs.MessageMediaInvoice && (media.photo || media.extendedMedia)
) )
); );
} }

View File

@ -26,37 +26,48 @@ export function resolveMessageApiChatId(mtpMessage: GramJs.TypeMessage) {
export function addMessageToLocalDb(message: GramJs.Message | GramJs.MessageService) { export function addMessageToLocalDb(message: GramJs.Message | GramJs.MessageService) {
const messageFullId = `${resolveMessageApiChatId(message)}-${message.id}`; const messageFullId = `${resolveMessageApiChatId(message)}-${message.id}`;
localDb.messages[messageFullId] = message;
if (message instanceof GramJs.Message) { let mockMessage = message;
if (message.media instanceof GramJs.MessageMediaDocument if (message instanceof GramJs.Message
&& message.media.document instanceof GramJs.Document && message.media instanceof GramJs.MessageMediaInvoice
&& message.media.extendedMedia instanceof GramJs.MessageExtendedMedia) {
mockMessage = new GramJs.Message({
...message,
media: message.media.extendedMedia.media,
});
}
localDb.messages[messageFullId] = mockMessage;
if (mockMessage instanceof GramJs.Message) {
if (mockMessage.media instanceof GramJs.MessageMediaDocument
&& mockMessage.media.document instanceof GramJs.Document
) { ) {
localDb.documents[String(message.media.document.id)] = message.media.document; localDb.documents[String(mockMessage.media.document.id)] = mockMessage.media.document;
} }
if (message.media instanceof GramJs.MessageMediaWebPage if (mockMessage.media instanceof GramJs.MessageMediaWebPage
&& message.media.webpage instanceof GramJs.WebPage && mockMessage.media.webpage instanceof GramJs.WebPage
&& message.media.webpage.document instanceof GramJs.Document && mockMessage.media.webpage.document instanceof GramJs.Document
) { ) {
localDb.documents[String(message.media.webpage.document.id)] = message.media.webpage.document; localDb.documents[String(mockMessage.media.webpage.document.id)] = mockMessage.media.webpage.document;
} }
if (message.media instanceof GramJs.MessageMediaGame) { if (mockMessage.media instanceof GramJs.MessageMediaGame) {
if (message.media.game.document instanceof GramJs.Document) { if (mockMessage.media.game.document instanceof GramJs.Document) {
localDb.documents[String(message.media.game.document.id)] = message.media.game.document; localDb.documents[String(mockMessage.media.game.document.id)] = mockMessage.media.game.document;
} }
addPhotoToLocalDb(message.media.game.photo); addPhotoToLocalDb(mockMessage.media.game.photo);
} }
if (message.media instanceof GramJs.MessageMediaInvoice if (mockMessage.media instanceof GramJs.MessageMediaInvoice
&& message.media.photo) { && mockMessage.media.photo) {
localDb.webDocuments[String(message.media.photo.url)] = message.media.photo; localDb.webDocuments[String(mockMessage.media.photo.url)] = mockMessage.media.photo;
} }
} }
if (message instanceof GramJs.MessageService && 'photo' in message.action) { if (mockMessage instanceof GramJs.MessageService && 'photo' in mockMessage.action) {
addPhotoToLocalDb(message.action.photo); addPhotoToLocalDb(mockMessage.action.photo);
} }
} }
@ -90,6 +101,24 @@ export function addEntitiesWithPhotosToLocalDb(entities: (GramJs.TypeUser | Gram
}); });
} }
export function swapLocalInvoiceMedia(
chatId: string, messageId: number, extendedMedia: GramJs.TypeMessageExtendedMedia,
) {
const localMessage = localDb.messages[`${chatId}-${messageId}`];
if (!(localMessage instanceof GramJs.Message) || !localMessage.media) return;
if (extendedMedia instanceof GramJs.MessageExtendedMediaPreview) {
if (!(localMessage.media instanceof GramJs.MessageMediaInvoice)) {
return;
}
localMessage.media.extendedMedia = extendedMedia;
}
if (extendedMedia instanceof GramJs.MessageExtendedMedia) {
localMessage.media = extendedMedia.media;
}
}
export function serializeBytes(value: Buffer) { export function serializeBytes(value: Buffer) {
return String.fromCharCode(...value); return String.fromCharCode(...value);
} }

View File

@ -29,7 +29,7 @@ export {
fetchPinnedMessages, fetchScheduledHistory, sendScheduledMessages, rescheduleMessage, deleteScheduledMessages, fetchPinnedMessages, fetchScheduledHistory, sendScheduledMessages, rescheduleMessage, deleteScheduledMessages,
reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs, reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs,
saveDefaultSendAs, fetchUnreadReactions, readAllReactions, fetchUnreadMentions, readAllMentions, transcribeAudio, saveDefaultSendAs, fetchUnreadReactions, readAllReactions, fetchUnreadMentions, readAllMentions, transcribeAudio,
closePoll, closePoll, fetchExtendedMedia,
} from './messages'; } from './messages';
export { export {

View File

@ -1122,6 +1122,18 @@ export async function loadPollOptionResults({
}; };
} }
export async function fetchExtendedMedia({
chat, ids,
} : {
chat: ApiChat;
ids: number[];
}) {
await invokeRequest(new GramJs.messages.GetExtendedMedia({
peer: buildInputPeer(chat.id, chat.accessHash),
id: ids,
}));
}
export async function forwardMessages({ export async function forwardMessages({
fromChat, fromChat,
toChat, toChat,

View File

@ -1,6 +1,8 @@
import type { GroupCallConnectionData } from '../../lib/secret-sauce'; import type { GroupCallConnectionData } from '../../lib/secret-sauce';
import { Api as GramJs, connection } from '../../lib/gramjs'; import { Api as GramJs, connection } from '../../lib/gramjs';
import type { ApiMessage, ApiUpdateConnectionStateType, OnApiUpdate } from '../types'; import type {
ApiMessage, ApiMessageExtendedMediaPreview, ApiUpdateConnectionStateType, OnApiUpdate,
} from '../types';
import { pick } from '../../util/iteratees'; import { pick } from '../../util/iteratees';
import { import {
@ -14,6 +16,7 @@ import {
buildApiMessageFromNotification, buildApiMessageFromNotification,
buildMessageDraft, buildMessageDraft,
buildMessageReactions, buildMessageReactions,
buildApiMessageExtendedMediaPreview,
} from './apiBuilders/messages'; } from './apiBuilders/messages';
import { import {
buildChatMember, buildChatMember,
@ -40,6 +43,7 @@ import {
resolveMessageApiChatId, resolveMessageApiChatId,
serializeBytes, serializeBytes,
log, log,
swapLocalInvoiceMedia,
} from './helpers'; } from './helpers';
import { buildApiNotifyException, buildPrivacyKey, buildPrivacyRules } from './apiBuilders/misc'; import { buildApiNotifyException, buildPrivacyKey, buildPrivacyRules } from './apiBuilders/misc';
import { buildApiPhoto } from './apiBuilders/common'; import { buildApiPhoto } from './apiBuilders/common';
@ -309,6 +313,30 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
chatId: getApiChatIdFromMtpPeer(update.peer), chatId: getApiChatIdFromMtpPeer(update.peer),
reactions: buildMessageReactions(update.reactions), reactions: buildMessageReactions(update.reactions),
}); });
} else if (update instanceof GramJs.UpdateMessageExtendedMedia) {
let media: ApiMessage['content'] | undefined;
if (update.extendedMedia instanceof GramJs.MessageExtendedMedia) {
media = buildMessageMediaContent(update.extendedMedia.media);
}
let preview: ApiMessageExtendedMediaPreview | undefined;
if (update.extendedMedia instanceof GramJs.MessageExtendedMediaPreview) {
preview = buildApiMessageExtendedMediaPreview(update.extendedMedia);
}
if (!media && !preview) return;
const chatId = getApiChatIdFromMtpPeer(update.peer);
swapLocalInvoiceMedia(chatId, update.msgId, update.extendedMedia);
onUpdate({
'@type': 'updateMessageExtendedMedia',
id: update.msgId,
chatId,
media,
preview,
});
} else if (update instanceof GramJs.UpdateDeleteMessages) { } else if (update instanceof GramJs.UpdateDeleteMessages) {
onUpdate({ onUpdate({
'@type': 'deleteMessages', '@type': 'deleteMessages',

View File

@ -187,10 +187,18 @@ export interface ApiInvoice {
isTest?: boolean; isTest?: boolean;
isRecurring?: boolean; isRecurring?: boolean;
recurringTermsUrl?: string; recurringTermsUrl?: string;
extendedMedia?: ApiMessageExtendedMediaPreview;
maxTipAmount?: number; maxTipAmount?: number;
suggestedTipAmounts?: number[]; suggestedTipAmounts?: number[];
} }
export interface ApiMessageExtendedMediaPreview {
width?: number;
height?: number;
thumbnail?: ApiThumbnail;
duration?: number;
}
export interface ApiPaymentCredentials { export interface ApiPaymentCredentials {
id: string; id: string;
title: string; title: string;
@ -406,6 +414,7 @@ export interface ApiMessage {
isSilent?: boolean; isSilent?: boolean;
seenByUserIds?: string[]; seenByUserIds?: string[];
isProtected?: boolean; isProtected?: boolean;
isForwardingAllowed?: boolean;
transcriptionId?: string; transcriptionId?: string;
isTranscriptionError?: boolean; isTranscriptionError?: boolean;
emojiOnlyCount?: number; emojiOnlyCount?: number;

View File

@ -13,7 +13,14 @@ import type {
ApiChatFolder, ApiChatFolder,
} from './chats'; } from './chats';
import type { import type {
ApiFormattedText, ApiMessage, ApiPhoto, ApiPoll, ApiReactions, ApiStickerSet, ApiThreadInfo, ApiFormattedText,
ApiMessage,
ApiMessageExtendedMediaPreview,
ApiPhoto,
ApiPoll,
ApiReactions,
ApiStickerSet,
ApiThreadInfo,
} from './messages'; } from './messages';
import type { import type {
ApiEmojiStatus, ApiUser, ApiUserFullInfo, ApiUserStatus, ApiEmojiStatus, ApiUser, ApiUserFullInfo, ApiUserStatus,
@ -309,6 +316,14 @@ export type ApiUpdateMessageReactions = {
reactions: ApiReactions; reactions: ApiReactions;
}; };
export type ApiUpdateMessageExtendedMedia = {
'@type': 'updateMessageExtendedMedia';
id: number;
chatId: string;
media?: ApiMessage['content'];
preview?: ApiMessageExtendedMediaPreview;
};
export type ApiDeleteContact = { export type ApiDeleteContact = {
'@type': 'deleteContact'; '@type': 'deleteContact';
id: string; id: string;
@ -556,7 +571,8 @@ export type ApiUpdate = (
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId | ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted | ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted |
ApiUpdatePhoneCall | ApiUpdatePhoneCallSignalingData | ApiUpdatePhoneCallMediaState | ApiUpdatePhoneCall | ApiUpdatePhoneCallSignalingData | ApiUpdatePhoneCallMediaState |
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus |
ApiUpdateMessageExtendedMedia
); );
export type OnApiUpdate = (update: ApiUpdate) => void; export type OnApiUpdate = (update: ApiUpdate) => void;

BIN
src/assets/turbulence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -81,7 +81,7 @@ function getAvailableHeight(isGif?: boolean, aspectRatio?: number) {
return 27 * REM; return 27 * REM;
} }
function calculateDimensionsForMessageMedia({ export function calculateDimensionsForMessageMedia({
width, width,
height, height,
fromOwnMessage, fromOwnMessage,

View File

@ -320,7 +320,6 @@ const MediaViewer: FC<StateProps> = ({
onForward={handleForward} onForward={handleForward}
zoomLevelChange={zoomLevelChange} zoomLevelChange={zoomLevelChange}
setZoomLevelChange={setZoomLevelChange} setZoomLevelChange={setZoomLevelChange}
isAvatar={Boolean(avatarOwner)}
/> />
<ReportModal <ReportModal
isOpen={isReportModalOpen} isOpen={isReportModalOpen}

View File

@ -1,4 +1,3 @@
import type { FC } from '../../lib/teact/teact';
import React, { import React, {
memo, memo,
useCallback, useCallback,
@ -6,21 +5,27 @@ import React, {
} from '../../lib/teact/teact'; } from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global'; import { getActions, withGlobal } from '../../global';
import type { FC } from '../../lib/teact/teact';
import type { ApiMessage } from '../../api/types'; import type { ApiMessage } from '../../api/types';
import type { MessageListType } from '../../global/types'; import type { MessageListType } from '../../global/types';
import type { MenuItemProps } from '../ui/MenuItem';
import {
selectIsDownloading,
selectIsMessageProtected,
selectAllowedMessageActions,
selectCurrentMessageList,
selectIsChatProtected,
} from '../../global/selectors';
import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment'; import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
import { getMessageMediaFormat, getMessageMediaHash } from '../../global/helpers'; import { getMessageMediaFormat, getMessageMediaHash } from '../../global/helpers';
import useLang from '../../hooks/useLang'; import useLang from '../../hooks/useLang';
import useMediaWithLoadProgress from '../../hooks/useMediaWithLoadProgress'; import useMediaWithLoadProgress from '../../hooks/useMediaWithLoadProgress';
import useFlag from '../../hooks/useFlag'; import useFlag from '../../hooks/useFlag';
import {
selectIsDownloading, selectIsMessageProtected, selectAllowedMessageActions, selectCurrentMessageList,
} from '../../global/selectors';
import Button from '../ui/Button'; import Button from '../ui/Button';
import DropdownMenu from '../ui/DropdownMenu'; import DropdownMenu from '../ui/DropdownMenu';
import type { MenuItemProps } from '../ui/MenuItem';
import MenuItem from '../ui/MenuItem'; import MenuItem from '../ui/MenuItem';
import ProgressSpinner from '../ui/ProgressSpinner'; import ProgressSpinner from '../ui/ProgressSpinner';
import DeleteMessageModal from '../common/DeleteMessageModal'; import DeleteMessageModal from '../common/DeleteMessageModal';
@ -30,6 +35,7 @@ import './MediaViewerActions.scss';
type StateProps = { type StateProps = {
isDownloading: boolean; isDownloading: boolean;
isProtected?: boolean; isProtected?: boolean;
isChatProtected?: boolean;
canDelete?: boolean; canDelete?: boolean;
messageListType?: MessageListType; messageListType?: MessageListType;
}; };
@ -40,7 +46,6 @@ type OwnProps = {
zoomLevelChange: number; zoomLevelChange: number;
message?: ApiMessage; message?: ApiMessage;
fileName?: string; fileName?: string;
isAvatar?: boolean;
canReport?: boolean; canReport?: boolean;
onReport: NoneToVoidFunction; onReport: NoneToVoidFunction;
onCloseMediaViewer: NoneToVoidFunction; onCloseMediaViewer: NoneToVoidFunction;
@ -53,17 +58,17 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
isVideo, isVideo,
message, message,
fileName, fileName,
isAvatar, isChatProtected,
isDownloading, isDownloading,
isProtected, isProtected,
canReport, canReport,
zoomLevelChange,
canDelete,
messageListType,
onReport, onReport,
onCloseMediaViewer, onCloseMediaViewer,
zoomLevelChange,
setZoomLevelChange,
canDelete,
onForward, onForward,
messageListType, setZoomLevelChange,
}) => { }) => {
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag(false); const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag(false);
@ -148,7 +153,7 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
if (IS_SINGLE_COLUMN_LAYOUT) { if (IS_SINGLE_COLUMN_LAYOUT) {
const menuItems: MenuItemProps[] = []; const menuItems: MenuItemProps[] = [];
if (!isAvatar && !isProtected) { if (!message?.isForwardingAllowed && !isChatProtected) {
menuItems.push({ menuItems.push({
icon: 'forward', icon: 'forward',
onClick: onForward, onClick: onForward,
@ -227,7 +232,7 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
return ( return (
<div className="MediaViewerActions"> <div className="MediaViewerActions">
{!isAvatar && !isProtected && ( {message?.isForwardingAllowed && !isChatProtected && (
<Button <Button
round round
size="smaller" size="smaller"
@ -306,12 +311,14 @@ export default memo(withGlobal<OwnProps>(
const { threadId } = selectCurrentMessageList(global) || {}; const { threadId } = selectCurrentMessageList(global) || {};
const isDownloading = message ? selectIsDownloading(global, message) : false; const isDownloading = message ? selectIsDownloading(global, message) : false;
const isProtected = selectIsMessageProtected(global, message); const isProtected = selectIsMessageProtected(global, message);
const isChatProtected = message && selectIsChatProtected(global, message?.chatId);
const { canDelete } = (threadId && message && selectAllowedMessageActions(global, message, threadId)) || {}; const { canDelete } = (threadId && message && selectAllowedMessageActions(global, message, threadId)) || {};
const messageListType = currentMessageList?.type; const messageListType = currentMessageList?.type;
return { return {
isDownloading, isDownloading,
isProtected, isProtected,
isChatProtected,
canDelete, canDelete,
messageListType, messageListType,
}; };

View File

@ -14,7 +14,7 @@ import {
selectActiveDownloadIds, selectActiveDownloadIds,
selectAllowedMessageActions, selectAllowedMessageActions,
selectChat, selectChat,
selectCurrentMessageList, selectIsCurrentUserPremium, selectCurrentMessageList, selectIsChatProtected, selectIsCurrentUserPremium,
selectIsMessageProtected, selectIsMessageProtected,
selectIsPremiumPurchaseBlocked, selectIsPremiumPurchaseBlocked,
selectMessageCustomEmojiSets, selectMessageCustomEmojiSets,
@ -535,6 +535,7 @@ export default memo(withGlobal<OwnProps>(
&& !areReactionsEmpty(message.reactions) && message.reactions.canSeeList; && !areReactionsEmpty(message.reactions) && message.reactions.canSeeList;
const canRemoveReaction = isPrivate && message.reactions?.results?.some((l) => l.isChosen); const canRemoveReaction = isPrivate && message.reactions?.results?.some((l) => l.isChosen);
const isProtected = selectIsMessageProtected(global, message); const isProtected = selectIsMessageProtected(global, message);
const isChatProtected = selectIsChatProtected(global, message.chatId);
const canCopyNumber = Boolean(message.content.contact); const canCopyNumber = Boolean(message.content.contact);
const isCurrentUserPremium = selectIsCurrentUserPremium(global); const isCurrentUserPremium = selectIsCurrentUserPremium(global);
@ -554,11 +555,11 @@ export default memo(withGlobal<OwnProps>(
canDelete, canDelete,
canReport, canReport,
canEdit: !isPinned && canEdit, canEdit: !isPinned && canEdit,
canForward: !isProtected && !isScheduled && canForward, canForward: message.isForwardingAllowed && !isChatProtected && !isScheduled && canForward,
canFaveSticker: !isScheduled && canFaveSticker, canFaveSticker: !isScheduled && canFaveSticker,
canUnfaveSticker: !isScheduled && canUnfaveSticker, canUnfaveSticker: !isScheduled && canUnfaveSticker,
canCopy: canCopyNumber || (!isProtected && canCopy), canCopy: canCopyNumber || (!isProtected && canCopy),
canCopyLink: !isProtected && !isScheduled && canCopyLink, canCopyLink: !isScheduled && canCopyLink,
canSelect, canSelect,
canDownload: !isProtected && canDownload, canDownload: !isProtected && canDownload,
canSaveGif: !isProtected && canSaveGif, canSaveGif: !isProtected && canSaveGif,

View File

@ -0,0 +1,117 @@
.root {
position: relative;
z-index: 0;
overflow: hidden;
cursor: pointer;
}
.dots {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
--background-url: url('../../../assets/turbulence.png');
--background-size: 256px;
background: rgba(0, 0, 0, 0.3) var(--background-url);
background-size: var(--background-size) var(--background-size);
z-index: 1;
--x-direction: var(--background-size);
--y-direction: 0;
animation: 10s linear infinite dots;
&.highres {
--background-url: url('../../../assets/turbulence_2x.png');
--background-size: 128px;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: var(--background-url);
background-size: var(--background-size) var(--background-size);
--x-direction: 0;
--y-direction: var(--background-size);
animation: 10s linear -4s infinite dots;
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: var(--background-url);
background-size: var(--background-size) var(--background-size);
--x-direction: calc(-1 * var(--background-size));
--y-direction: calc(-1 * var(--background-size));
animation: 10s linear -8s infinite dots;
}
}
.duration {
z-index: 2;
position: absolute;
top: 0.25rem;
left: 0.25rem;
white-space: nowrap;
font-size: 0.75rem;
padding: 0 0.375rem;
background-color: rgba(0, 0, 0, 0.3);
color: #FFFFFF;
border-radius: 0.5rem;
}
.buy {
display: flex;
align-items: center;
gap: 0.375rem;
z-index: 2;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 0.375rem 0.625rem;
background-color: rgba(0, 0, 0, 0.5);
color: #FFFFFF;
border-radius: 1rem;
white-space: nowrap;
}
.lock {
margin-bottom: 1px;
}
.canvas {
display: block;
max-width: 100%;
}
@keyframes dots {
0% {
background-position: 0 0;
opacity: 1;
}
50% {
opacity: 0.75;
}
100% {
background-position: var(--x-direction) var(--y-direction);
opacity: 1;
}
}

View File

@ -0,0 +1,78 @@
import React, { memo, useCallback } from '../../../lib/teact/teact';
import { getActions } from '../../../global';
import type { FC } from '../../../lib/teact/teact';
import type { ApiMessage } from '../../../api/types';
import { getMessageInvoice } from '../../../global/helpers';
import { formatCurrency } from '../../../util/formatCurrency';
import { formatMediaDuration } from '../../../util/dateFormat';
import buildClassName from '../../../util/buildClassName';
import { DPR } from '../../../util/environment';
import useLang from '../../../hooks/useLang';
import useCanvasBlur from '../../../hooks/useCanvasBlur';
import useInterval from '../../../hooks/useInterval';
import styles from './InvoiceMediaPreview.module.scss';
type OwnProps = {
message: ApiMessage;
lastSyncTime?: number;
};
const POLLING_INTERVAL = 30000;
const BLUR_RADIUS = 25;
const InvoiceMediaPreview: FC<OwnProps> = ({
message,
lastSyncTime,
}) => {
const { openInvoice, loadExtendedMedia } = getActions();
const lang = useLang();
const invoice = getMessageInvoice(message);
const { chatId, id } = message;
const refreshExtendedMedia = useCallback(() => {
loadExtendedMedia({ chatId, ids: [id] });
}, [chatId, id, loadExtendedMedia]);
useInterval(refreshExtendedMedia, lastSyncTime ? POLLING_INTERVAL : undefined);
const {
amount,
currency,
extendedMedia,
} = invoice!;
const {
width, height, thumbnail, duration,
} = extendedMedia!;
const canvasRef = useCanvasBlur(thumbnail?.dataUri, false, undefined, BLUR_RADIUS, width, height);
const handleClick = useCallback(() => {
openInvoice({
chatId,
messageId: id,
});
}, [chatId, id, openInvoice]);
return (
<div
className={buildClassName(styles.root, 'media-inner')}
onClick={handleClick}
>
<canvas ref={canvasRef} className={styles.canvas} width={width} height={height} />
<div className={buildClassName(styles.dots, DPR > 1 && styles.highres)} />
{Boolean(duration) && <div className={styles.duration}>{formatMediaDuration(duration)}</div>}
<div className={styles.buy}>
<i className={buildClassName('icon-lock', styles.lock)} />
{lang('Checkout.PayPrice', formatCurrency(amount, currency))}
</div>
</div>
);
};
export default memo(InvoiceMediaPreview);

View File

@ -56,6 +56,7 @@ import {
selectAnimatedEmoji, selectAnimatedEmoji,
selectLocalAnimatedEmoji, selectLocalAnimatedEmoji,
selectIsCurrentUserPremium, selectIsCurrentUserPremium,
selectIsChatProtected,
} from '../../../global/selectors'; } from '../../../global/selectors';
import { import {
getMessageContent, getMessageContent,
@ -79,7 +80,7 @@ import buildClassName from '../../../util/buildClassName';
import useEnsureMessage from '../../../hooks/useEnsureMessage'; import useEnsureMessage from '../../../hooks/useEnsureMessage';
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers'; import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
import { renderMessageText } from '../../common/helpers/renderMessageText'; import { renderMessageText } from '../../common/helpers/renderMessageText';
import { ROUND_VIDEO_DIMENSIONS_PX } from '../../common/helpers/mediaDimensions'; import { calculateDimensionsForMessageMedia, ROUND_VIDEO_DIMENSIONS_PX } from '../../common/helpers/mediaDimensions';
import { buildContentClassName } from './helpers/buildContentClassName'; import { buildContentClassName } from './helpers/buildContentClassName';
import { getMinMediaWidth, calculateMediaDimensions } from './helpers/mediaDimensions'; import { getMinMediaWidth, calculateMediaDimensions } from './helpers/mediaDimensions';
import { calculateAlbumLayout } from './helpers/calculateAlbumLayout'; import { calculateAlbumLayout } from './helpers/calculateAlbumLayout';
@ -113,6 +114,7 @@ import Contact from './Contact';
import Poll from './Poll'; import Poll from './Poll';
import WebPage from './WebPage'; import WebPage from './WebPage';
import Invoice from './Invoice'; import Invoice from './Invoice';
import InvoiceMediaPreview from './InvoiceMediaPreview';
import Location from './Location'; import Location from './Location';
import Game from './Game'; import Game from './Game';
import Album from './Album'; import Album from './Album';
@ -172,6 +174,7 @@ type StateProps = {
uploadProgress?: number; uploadProgress?: number;
isInDocumentGroup: boolean; isInDocumentGroup: boolean;
isProtected?: boolean; isProtected?: boolean;
isChatProtected?: boolean;
isFocused?: boolean; isFocused?: boolean;
focusDirection?: FocusDirection; focusDirection?: FocusDirection;
noFocusHighlight?: boolean; noFocusHighlight?: boolean;
@ -263,6 +266,7 @@ const Message: FC<OwnProps & StateProps> = ({
uploadProgress, uploadProgress,
isInDocumentGroup, isInDocumentGroup,
isProtected, isProtected,
isChatProtected,
isFocused, isFocused,
focusDirection, focusDirection,
noFocusHighlight, noFocusHighlight,
@ -371,7 +375,7 @@ const Message: FC<OwnProps & StateProps> = ({
!(isContextMenuShown || isInSelectMode || isForwarding) !(isContextMenuShown || isInSelectMode || isForwarding)
&& !isInDocumentGroupNotLast && !isInDocumentGroupNotLast
); );
const canForward = isChannel && !isScheduled && !isProtected; const canForward = isChannel && !isScheduled && message.isForwardingAllowed && !isChatProtected;
const canFocus = Boolean(isPinnedList const canFocus = Boolean(isPinnedList
|| (forwardInfo || (forwardInfo
&& (forwardInfo.isChannelPost || (isChatWithSelf && !isOwn) || isRepliesChat) && (forwardInfo.isChannelPost || (isChatWithSelf && !isOwn) || isRepliesChat)
@ -579,7 +583,7 @@ const Message: FC<OwnProps & StateProps> = ({
}, [isAlbum, isOwn, asForwarded, noAvatars, album]); }, [isAlbum, isOwn, asForwarded, noAvatars, album]);
const extraPadding = asForwarded ? 28 : 0; const extraPadding = asForwarded ? 28 : 0;
if (!isAlbum && (photo || video)) { if (!isAlbum && (photo || video || invoice?.extendedMedia)) {
let width: number | undefined; let width: number | undefined;
if (photo) { if (photo) {
width = calculateMediaDimensions(message, noAvatars).width; width = calculateMediaDimensions(message, noAvatars).width;
@ -589,6 +593,17 @@ const Message: FC<OwnProps & StateProps> = ({
} else { } else {
width = calculateMediaDimensions(message, noAvatars).width; width = calculateMediaDimensions(message, noAvatars).width;
} }
} else if (invoice?.extendedMedia && (
invoice.extendedMedia.width && invoice.extendedMedia.height
)) {
const { width: previewWidth, height: previewHeight } = invoice.extendedMedia;
width = calculateDimensionsForMessageMedia({
width: previewWidth,
height: previewHeight,
fromOwnMessage: isOwn,
isForwarded: isForwarding,
noAvatars,
}).width;
} }
if (width) { if (width) {
@ -840,6 +855,12 @@ const Message: FC<OwnProps & StateProps> = ({
lastSyncTime={lastSyncTime} lastSyncTime={lastSyncTime}
/> />
)} )}
{invoice?.extendedMedia && (
<InvoiceMediaPreview
message={message}
lastSyncTime={lastSyncTime}
/>
)}
{withVoiceTranscription && ( {withVoiceTranscription && (
<p <p
@ -877,7 +898,7 @@ const Message: FC<OwnProps & StateProps> = ({
theme={theme} theme={theme}
/> />
)} )}
{invoice && ( {invoice && !invoice.extendedMedia && (
<Invoice <Invoice
message={message} message={message}
shouldAffectAppendix={hasCustomAppendix} shouldAffectAppendix={hasCustomAppendix}
@ -1176,6 +1197,7 @@ export default memo(withGlobal<OwnProps>(
replyMessageSender, replyMessageSender,
isInDocumentGroup, isInDocumentGroup,
isProtected: selectIsMessageProtected(global, message), isProtected: selectIsMessageProtected(global, message),
isChatProtected: selectIsChatProtected(global, chatId),
isFocused, isFocused,
isForwarding, isForwarding,
reactionMessage, reactionMessage,

View File

@ -36,7 +36,7 @@ export function buildContentClassName(
} = getMessageContent(message); } = getMessageContent(message);
const classNames = ['message-content']; const classNames = ['message-content'];
const isMedia = photo || video || location; const isMedia = photo || video || location || invoice?.extendedMedia;
const hasText = text || location?.type === 'venue' || isGeoLiveActive; const hasText = text || location?.type === 'venue' || isGeoLiveActive;
const isMediaWithNoText = isMedia && !hasText; const isMediaWithNoText = isMedia && !hasText;
const isViaBot = Boolean(message.viaBotId); const isViaBot = Boolean(message.viaBotId);
@ -87,7 +87,7 @@ export function buildContentClassName(
} }
} }
if (invoice) { if (invoice && !invoice.extendedMedia) {
classNames.push('invoice'); classNames.push('invoice');
} }

View File

@ -608,6 +608,14 @@ addActionHandler('loadPollOptionResults', (global, actions, payload) => {
void loadPollOptionResults(chat, messageId, option, offset, limit, shouldResetVoters); void loadPollOptionResults(chat, messageId, option, offset, limit, shouldResetVoters);
}); });
addActionHandler('loadExtendedMedia', (global, actions, payload) => {
const { chatId, ids } = payload;
const chat = selectChat(global, chatId);
if (chat) {
void callApi('fetchExtendedMedia', { chat, ids });
}
});
addActionHandler('forwardMessages', (global, action, payload) => { addActionHandler('forwardMessages', (global, action, payload) => {
const { const {
fromChatId, messageIds, toChatId, withMyScore, noAuthors, noCaptions, fromChatId, messageIds, toChatId, withMyScore, noAuthors, noCaptions,

View File

@ -511,6 +511,37 @@ addActionHandler('apiUpdate', (global, actions, update) => {
break; break;
} }
case 'updateMessageExtendedMedia': {
const {
chatId, id, media, preview,
} = update;
const message = selectChatMessage(global, chatId, id);
const chat = selectChat(global, update.chatId);
if (!chat || !message) return;
if (preview) {
if (!message.content.invoice) return;
setGlobal(updateChatMessage(global, chatId, id, {
content: {
...message.content,
invoice: {
...message.content.invoice,
extendedMedia: preview,
},
},
}));
} else if (media) {
setGlobal(updateChatMessage(global, chatId, id, {
content: {
...media,
},
}));
}
break;
}
case 'updateTranscribedAudio': { case 'updateTranscribedAudio': {
const { transcriptionId, text, isPending } = update; const { transcriptionId, text, isPending } = update;

View File

@ -166,7 +166,7 @@ export function getMessageSummaryDescription(
} }
if (invoice) { if (invoice) {
summary = `${lang('PaymentInvoice')}: ${invoice.text}`; summary = invoice.extendedMedia ? invoice.title : `${lang('PaymentInvoice')}: ${invoice.text}`;
} }
if (text) { if (text) {

View File

@ -901,7 +901,11 @@ export function selectLastServiceNotification(global: GlobalState) {
} }
export function selectIsMessageProtected(global: GlobalState, message?: ApiMessage) { export function selectIsMessageProtected(global: GlobalState, message?: ApiMessage) {
return message ? message.isProtected || selectChat(global, message.chatId)?.isProtected : false; return Boolean(message && (message.isProtected || selectIsChatProtected(global, message.chatId)));
}
export function selectIsChatProtected(global: GlobalState, chatId: string) {
return selectChat(global, chatId)?.isProtected || false;
} }
export function selectHasProtectedMessage(global: GlobalState, chatId: string, messageIds?: number[]) { export function selectHasProtectedMessage(global: GlobalState, chatId: string, messageIds?: number[]) {

View File

@ -790,6 +790,11 @@ export interface ActionPayloads {
messageId: number; messageId: number;
}; };
loadExtendedMedia: {
chatId: string;
ids: number[];
};
// Media Viewer & Audio Player // Media Viewer & Audio Player
openMediaViewer: { openMediaViewer: {
chatId?: string; chatId?: string;

View File

@ -7,7 +7,14 @@ import { IS_CANVAS_FILTER_SUPPORTED } from '../util/environment';
const RADIUS = 2; const RADIUS = 2;
const ITERATIONS = 2; const ITERATIONS = 2;
export default function useCanvasBlur(dataUri?: string, isDisabled = false, withRaf?: boolean) { export default function useCanvasBlur(
dataUri?: string,
isDisabled = false,
withRaf?: boolean,
radius = RADIUS,
preferredWidth?: number,
preferredHeight?: number,
) {
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
@ -22,19 +29,19 @@ export default function useCanvasBlur(dataUri?: string, isDisabled = false, with
const img = new Image(); const img = new Image();
const processBlur = () => { const processBlur = () => {
canvas.width = img.width; canvas.width = preferredWidth || img.width;
canvas.height = img.height; canvas.height = preferredHeight || img.height;
const ctx = canvas.getContext('2d', { alpha: false })!; const ctx = canvas.getContext('2d', { alpha: false })!;
if (IS_CANVAS_FILTER_SUPPORTED) { if (IS_CANVAS_FILTER_SUPPORTED) {
ctx.filter = `blur(${RADIUS}px)`; ctx.filter = `blur(${radius}px)`;
} }
ctx.drawImage(img, -RADIUS * 2, -RADIUS * 2, canvas.width + RADIUS * 4, canvas.height + RADIUS * 4); ctx.drawImage(img, -radius * 2, -radius * 2, canvas.width + radius * 4, canvas.height + radius * 4);
if (!IS_CANVAS_FILTER_SUPPORTED) { if (!IS_CANVAS_FILTER_SUPPORTED) {
fastBlur(ctx, 0, 0, canvas.width, canvas.height, RADIUS, ITERATIONS); fastBlur(ctx, 0, 0, canvas.width, canvas.height, radius, ITERATIONS);
} }
}; };
@ -47,7 +54,7 @@ export default function useCanvasBlur(dataUri?: string, isDisabled = false, with
}; };
img.src = dataUri; img.src = dataUri;
}, [canvasRef, dataUri, forceUpdate, isDisabled, withRaf]); }, [canvasRef, dataUri, forceUpdate, isDisabled, preferredHeight, preferredWidth, withRaf, radius]);
return canvasRef; return canvasRef;
} }

View File

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

File diff suppressed because one or more lines are too long

View File

@ -29,7 +29,7 @@ inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string pro
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#d9799874 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string = InputMedia; inputMediaInvoice#8eb5a6d5 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia;
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia; inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia; inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia; inputMediaDice#e66fbf7b emoticon:string = InputMedia;
@ -99,7 +99,7 @@ messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true document:flags.0?Do
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia; messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia; messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia; messageMediaGame#fdb19008 game:Game = MessageMedia;
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia; messageMediaInvoice#f6a548d3 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string extended_media:flags.4?MessageExtendedMedia = MessageMedia;
messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia; messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia; messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
@ -314,6 +314,7 @@ updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update; updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update; updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update; updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference; updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference; updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
@ -1042,6 +1043,8 @@ account.emailVerified#2b96cd1b email:string = account.EmailVerified;
account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = account.EmailVerified; account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = account.EmailVerified;
premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upgrade:flags.2?true months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption; premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upgrade:flags.2?true months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption;
sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer; sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer;
messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia;
messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia;
---functions--- ---functions---
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X; initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
@ -1223,6 +1226,7 @@ messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.Transcrib
messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector<long> = Vector<Document>; messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector<long> = Vector<Document>;
messages.getEmojiStickers#fbfca18f hash:long = messages.AllStickers; messages.getEmojiStickers#fbfca18f hash:long = messages.AllStickers;
messages.getFeaturedEmojiStickers#ecf6736 hash:long = messages.FeaturedStickers; messages.getFeaturedEmojiStickers#ecf6736 hash:long = messages.FeaturedStickers;
messages.getExtendedMedia#84f80814 peer:InputPeer id:Vector<int> = Updates;
updates.getState#edd4882a = updates.State; updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference; updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;

View File

@ -157,6 +157,7 @@
"messages.hideAllChatJoinRequests", "messages.hideAllChatJoinRequests",
"messages.toggleNoForwards", "messages.toggleNoForwards",
"messages.saveDefaultSendAs", "messages.saveDefaultSendAs",
"messages.getExtendedMedia",
"updates.getState", "updates.getState",
"updates.getDifference", "updates.getDifference",
"updates.getChannelDifference", "updates.getChannelDifference",

View File

@ -38,7 +38,7 @@ inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string pro
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#d9799874 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string = InputMedia; inputMediaInvoice#8eb5a6d5 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia;
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia; inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia; inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia; inputMediaDice#e66fbf7b emoticon:string = InputMedia;
@ -124,7 +124,7 @@ messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true document:flags.0?Do
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia; messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia; messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia; messageMediaGame#fdb19008 game:Game = MessageMedia;
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia; messageMediaInvoice#f6a548d3 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string extended_media:flags.4?MessageExtendedMedia = MessageMedia;
messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia; messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia; messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
@ -367,6 +367,7 @@ updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update; updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update; updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update; updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1414,6 +1415,9 @@ premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upg
sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer; sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer;
messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia;
messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1727,6 +1731,7 @@ messages.reportReaction#3f64c076 peer:InputPeer id:int reaction_peer:InputPeer =
messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions; messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions;
messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions; messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions;
messages.clearRecentReactions#9dfeefb4 = Bool; messages.clearRecentReactions#9dfeefb4 = Bool;
messages.getExtendedMedia#84f80814 peer:InputPeer id:Vector<int> = Updates;
updates.getState#edd4882a = updates.State; updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1889,4 +1894,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 145 // LAYER 146