Mini Apps: Implement Share Support (#5576)
This commit is contained in:
parent
c8f82b1f91
commit
87fc3a832f
@ -13,32 +13,280 @@ import type {
|
|||||||
ApiBotInlineSwitchPm,
|
ApiBotInlineSwitchPm,
|
||||||
ApiBotInlineSwitchWebview,
|
ApiBotInlineSwitchWebview,
|
||||||
ApiBotMenuButton,
|
ApiBotMenuButton,
|
||||||
|
ApiInlineQueryPeerType,
|
||||||
ApiInlineResultType,
|
ApiInlineResultType,
|
||||||
|
ApiKeyboardButton,
|
||||||
ApiMessagesBotApp,
|
ApiMessagesBotApp,
|
||||||
|
ApiReplyKeyboard,
|
||||||
|
MediaContainer,
|
||||||
|
MediaContent,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { numberToHexColor } from '../../../util/colors';
|
import { numberToHexColor } from '../../../util/colors';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
|
import { generateRandomInt } from '../gramjsBuilders';
|
||||||
import { addDocumentToLocalDb } from '../helpers/localDb';
|
import { addDocumentToLocalDb } from '../helpers/localDb';
|
||||||
import { buildApiPhoto, buildApiThumbnailFromStripped } from './common';
|
import { serializeBytes } from '../helpers/misc';
|
||||||
|
import { buildApiMessageEntity, buildApiPhoto } from './common';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import { buildApiDocument, buildApiWebDocument, buildVideoFromDocument } from './messageContent';
|
import {
|
||||||
|
buildApiDocument,
|
||||||
|
buildApiWebDocument,
|
||||||
|
buildAudioFromDocument,
|
||||||
|
buildGeoPoint,
|
||||||
|
buildVideoFromDocument,
|
||||||
|
} from './messageContent';
|
||||||
import { buildSvgPath } from './pathBytesToSvg';
|
import { buildSvgPath } from './pathBytesToSvg';
|
||||||
import { buildApiPeerId } from './peers';
|
import { buildApiPeerId } from './peers';
|
||||||
import { buildStickerFromDocument } from './symbols';
|
import { buildStickerFromDocument } from './symbols';
|
||||||
|
|
||||||
|
export function buildReplyButtons(
|
||||||
|
replyMarkup: GramJs.TypeReplyMarkup | undefined,
|
||||||
|
receiptMessageId?: number,
|
||||||
|
): ApiReplyKeyboard | undefined {
|
||||||
|
if (!(replyMarkup instanceof GramJs.ReplyKeyboardMarkup || replyMarkup instanceof GramJs.ReplyInlineMarkup)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const markup = replyMarkup.rows.map(({ buttons }) => {
|
||||||
|
return buttons.map((button): ApiKeyboardButton | undefined => {
|
||||||
|
const { text } = button;
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButton) {
|
||||||
|
return {
|
||||||
|
type: 'command',
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonUrl) {
|
||||||
|
if (button.url.includes('?startgroup=')) {
|
||||||
|
return {
|
||||||
|
type: 'unsupported',
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'url',
|
||||||
|
text,
|
||||||
|
url: button.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonCallback) {
|
||||||
|
if (button.requiresPassword) {
|
||||||
|
return {
|
||||||
|
type: 'unsupported',
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'callback',
|
||||||
|
text,
|
||||||
|
data: serializeBytes(button.data),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonRequestPoll) {
|
||||||
|
return {
|
||||||
|
type: 'requestPoll',
|
||||||
|
text,
|
||||||
|
isQuiz: button.quiz,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonRequestPhone) {
|
||||||
|
return {
|
||||||
|
type: 'requestPhone',
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonBuy) {
|
||||||
|
if (receiptMessageId) {
|
||||||
|
return {
|
||||||
|
type: 'receipt',
|
||||||
|
receiptMessageId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'buy',
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonGame) {
|
||||||
|
return {
|
||||||
|
type: 'game',
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonSwitchInline) {
|
||||||
|
return {
|
||||||
|
type: 'switchBotInline',
|
||||||
|
text,
|
||||||
|
query: button.query,
|
||||||
|
isSamePeer: button.samePeer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonUserProfile) {
|
||||||
|
return {
|
||||||
|
type: 'userProfile',
|
||||||
|
text,
|
||||||
|
userId: button.userId.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonSimpleWebView) {
|
||||||
|
return {
|
||||||
|
type: 'simpleWebView',
|
||||||
|
text,
|
||||||
|
url: button.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonWebView) {
|
||||||
|
return {
|
||||||
|
type: 'webView',
|
||||||
|
text,
|
||||||
|
url: button.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonUrlAuth) {
|
||||||
|
return {
|
||||||
|
type: 'urlAuth',
|
||||||
|
text,
|
||||||
|
url: button.url,
|
||||||
|
buttonId: button.buttonId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button instanceof GramJs.KeyboardButtonCopy) {
|
||||||
|
return {
|
||||||
|
type: 'copy',
|
||||||
|
text,
|
||||||
|
copyText: button.copyText,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'unsupported',
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}).filter(Boolean);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (markup.every((row) => !row.length)) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
[replyMarkup instanceof GramJs.ReplyKeyboardMarkup ? 'keyboardButtons' : 'inlineButtons']: markup,
|
||||||
|
...(replyMarkup instanceof GramJs.ReplyKeyboardMarkup && {
|
||||||
|
keyboardPlaceholder: replyMarkup.placeholder,
|
||||||
|
isKeyboardSingleUse: replyMarkup.singleUse,
|
||||||
|
isKeyboardSelective: replyMarkup.selective,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildBotInlineMessage(
|
||||||
|
sendMessage: GramJs.TypeBotInlineMessage, type: string, document?: GramJs.TypeDocument, photo?: GramJs.TypePhoto,
|
||||||
|
): MediaContainer & { replyMarkup?: ApiReplyKeyboard } {
|
||||||
|
const content: MediaContent = {};
|
||||||
|
|
||||||
|
if (sendMessage instanceof GramJs.BotInlineMessageText) {
|
||||||
|
content.text = {
|
||||||
|
text: sendMessage.message,
|
||||||
|
entities: sendMessage.entities?.map(buildApiMessageEntity),
|
||||||
|
};
|
||||||
|
} else if (sendMessage instanceof GramJs.BotInlineMessageMediaAuto) {
|
||||||
|
if (type === 'photo' && photo instanceof GramJs.Photo) {
|
||||||
|
content.photo = buildApiPhoto(photo);
|
||||||
|
} else if (type === 'audio' && document instanceof GramJs.Document) {
|
||||||
|
content.audio = buildAudioFromDocument(document);
|
||||||
|
} else if (type === 'video' && document instanceof GramJs.Document) {
|
||||||
|
content.video = buildVideoFromDocument(document);
|
||||||
|
} else if (type === 'sticker' && document instanceof GramJs.Document) {
|
||||||
|
content.sticker = buildStickerFromDocument(document);
|
||||||
|
} else if (type === 'file' && document instanceof GramJs.Document) {
|
||||||
|
content.document = buildApiDocument(document);
|
||||||
|
} else if (type === 'gif' && document instanceof GramJs.Document) {
|
||||||
|
content.video = buildVideoFromDocument(document);
|
||||||
|
} else {
|
||||||
|
content.text = {
|
||||||
|
text: sendMessage.message,
|
||||||
|
entities: sendMessage.entities?.map(buildApiMessageEntity),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (sendMessage instanceof GramJs.BotInlineMessageMediaGeo) {
|
||||||
|
content.location = {
|
||||||
|
mediaType: 'geo',
|
||||||
|
geo: buildGeoPoint(sendMessage.geo)!,
|
||||||
|
};
|
||||||
|
} else if (sendMessage instanceof GramJs.BotInlineMessageMediaVenue) {
|
||||||
|
content.location = {
|
||||||
|
mediaType: 'venue',
|
||||||
|
geo: buildGeoPoint(sendMessage.geo)!,
|
||||||
|
title: sendMessage.title,
|
||||||
|
address: sendMessage.address,
|
||||||
|
provider: sendMessage.provider,
|
||||||
|
venueId: sendMessage.venueId,
|
||||||
|
venueType: sendMessage.venueType,
|
||||||
|
};
|
||||||
|
} else if (sendMessage instanceof GramJs.BotInlineMessageMediaContact) {
|
||||||
|
content.contact = {
|
||||||
|
mediaType: 'contact',
|
||||||
|
phoneNumber: sendMessage.phoneNumber,
|
||||||
|
firstName: sendMessage.firstName,
|
||||||
|
lastName: sendMessage.lastName,
|
||||||
|
userId: '0',
|
||||||
|
};
|
||||||
|
} else if (sendMessage instanceof GramJs.BotInlineMessageMediaInvoice) {
|
||||||
|
content.invoice = {
|
||||||
|
mediaType: 'invoice',
|
||||||
|
isTest: sendMessage.test,
|
||||||
|
title: sendMessage.title,
|
||||||
|
description: sendMessage.description,
|
||||||
|
photo: buildApiWebDocument(sendMessage.photo),
|
||||||
|
currency: sendMessage.currency,
|
||||||
|
amount: sendMessage.totalAmount.toJSNumber(),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const mediaSize = sendMessage.forceSmallMedia ? 'small' : sendMessage.forceLargeMedia ? 'large' : undefined;
|
||||||
|
|
||||||
|
content.webPage = {
|
||||||
|
mediaType: 'webpage',
|
||||||
|
id: generateRandomInt(),
|
||||||
|
mediaSize,
|
||||||
|
url: sendMessage.url,
|
||||||
|
displayUrl: sendMessage.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
replyMarkup: buildReplyButtons(sendMessage.replyMarkup) || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function buildApiBotInlineResult(result: GramJs.BotInlineResult, queryId: string): ApiBotInlineResult {
|
export function buildApiBotInlineResult(result: GramJs.BotInlineResult, queryId: string): ApiBotInlineResult {
|
||||||
const {
|
const {
|
||||||
id, type, title, description, url, thumb,
|
id, type, title, description, url, thumb, content, sendMessage,
|
||||||
} = result;
|
} = result;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
queryId,
|
queryId,
|
||||||
type: type as ApiInlineResultType,
|
type: type as ApiInlineResultType,
|
||||||
|
sendMessage: buildBotInlineMessage(sendMessage, type),
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
url,
|
url,
|
||||||
|
content: buildApiWebDocument(content),
|
||||||
webThumbnail: buildApiWebDocument(thumb),
|
webThumbnail: buildApiWebDocument(thumb),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -47,7 +295,7 @@ export function buildApiBotInlineMediaResult(
|
|||||||
result: GramJs.BotInlineMediaResult, queryId: string,
|
result: GramJs.BotInlineMediaResult, queryId: string,
|
||||||
): ApiBotInlineMediaResult {
|
): ApiBotInlineMediaResult {
|
||||||
const {
|
const {
|
||||||
id, type, title, description, photo, document,
|
id, type, title, description, sendMessage, photo, document,
|
||||||
} = result;
|
} = result;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -59,9 +307,10 @@ export function buildApiBotInlineMediaResult(
|
|||||||
...(type === 'sticker' && document instanceof GramJs.Document && { sticker: buildStickerFromDocument(document) }),
|
...(type === 'sticker' && document instanceof GramJs.Document && { sticker: buildStickerFromDocument(document) }),
|
||||||
...(photo instanceof GramJs.Photo && { photo: buildApiPhoto(photo) }),
|
...(photo instanceof GramJs.Photo && { photo: buildApiPhoto(photo) }),
|
||||||
...(type === 'gif' && document instanceof GramJs.Document && { gif: buildVideoFromDocument(document) }),
|
...(type === 'gif' && document instanceof GramJs.Document && { gif: buildVideoFromDocument(document) }),
|
||||||
...(type === 'video' && document instanceof GramJs.Document && {
|
...(type === 'file' && document instanceof GramJs.Document && { document: buildApiDocument(document) }),
|
||||||
thumbnail: buildApiThumbnailFromStripped(document.thumbs),
|
...(type === 'audio' && document instanceof GramJs.Document && { audio: buildAudioFromDocument(document) }),
|
||||||
}),
|
...(type === 'video' && document instanceof GramJs.Document && { video: buildVideoFromDocument(document) }),
|
||||||
|
sendMessage: buildBotInlineMessage(sendMessage, type, document, photo),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,20 +329,22 @@ export function buildApiAttachBot(bot: GramJs.AttachMenuBot): ApiAttachBot {
|
|||||||
shortName: bot.shortName,
|
shortName: bot.shortName,
|
||||||
isForAttachMenu: bot.showInAttachMenu!,
|
isForAttachMenu: bot.showInAttachMenu!,
|
||||||
isForSideMenu: bot.showInSideMenu,
|
isForSideMenu: bot.showInSideMenu,
|
||||||
attachMenuPeerTypes: bot.peerTypes?.map(buildApiAttachMenuPeerType)!,
|
attachMenuPeerTypes: bot.peerTypes && buildApiAttachMenuPeerType(bot.peerTypes),
|
||||||
icons: bot.icons.map(buildApiAttachMenuIcon).filter(Boolean),
|
icons: bot.icons.map(buildApiAttachMenuIcon).filter(Boolean),
|
||||||
isInactive: bot.inactive,
|
isInactive: bot.inactive,
|
||||||
isDisclaimerNeeded: bot.sideMenuDisclaimerNeeded,
|
isDisclaimerNeeded: bot.sideMenuDisclaimerNeeded,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildApiAttachMenuPeerType(peerType: GramJs.TypeAttachMenuPeerType): ApiAttachMenuPeerType {
|
function buildApiAttachMenuPeerType(peerTypes: GramJs.TypeAttachMenuPeerType[]): ApiAttachMenuPeerType[] {
|
||||||
if (peerType instanceof GramJs.AttachMenuPeerTypeBotPM) return 'bots';
|
return peerTypes.flatMap((peerType) => {
|
||||||
if (peerType instanceof GramJs.AttachMenuPeerTypePM) return 'users';
|
if (peerType instanceof GramJs.AttachMenuPeerTypeBotPM) return ['bots'];
|
||||||
if (peerType instanceof GramJs.AttachMenuPeerTypeChat) return 'chats';
|
if (peerType instanceof GramJs.AttachMenuPeerTypePM) return ['users'];
|
||||||
if (peerType instanceof GramJs.AttachMenuPeerTypeBroadcast) return 'channels';
|
if (peerType instanceof GramJs.AttachMenuPeerTypeChat) return ['chats', 'groups'];
|
||||||
if (peerType instanceof GramJs.AttachMenuPeerTypeSameBotPM) return 'self';
|
if (peerType instanceof GramJs.AttachMenuPeerTypeBroadcast) return ['channels'];
|
||||||
return undefined!; // Never reached
|
if (peerType instanceof GramJs.AttachMenuPeerTypeSameBotPM) return ['self'];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildApiAttachMenuIcon(icon: GramJs.AttachMenuBotIcon): ApiAttachBotIcon | undefined {
|
function buildApiAttachMenuIcon(icon: GramJs.AttachMenuBotIcon): ApiAttachBotIcon | undefined {
|
||||||
@ -200,3 +451,13 @@ export function buildApiMessagesBotApp(botApp: GramJs.messages.BotApp): ApiMessa
|
|||||||
shouldRequestWriteAccess: requestWriteAccess,
|
shouldRequestWriteAccess: requestWriteAccess,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildApiInlineQueryPeerType(peerType: GramJs.TypeInlineQueryPeerType): ApiInlineQueryPeerType {
|
||||||
|
if (peerType instanceof GramJs.InlineQueryPeerTypeBotPM) return 'bots';
|
||||||
|
if (peerType instanceof GramJs.InlineQueryPeerTypePM) return 'users';
|
||||||
|
if (peerType instanceof GramJs.InlineQueryPeerTypeChat) return 'chats';
|
||||||
|
if (peerType instanceof GramJs.InlineQueryPeerTypeMegagroup) return 'supergroups';
|
||||||
|
if (peerType instanceof GramJs.InlineQueryPeerTypeBroadcast) return 'channels';
|
||||||
|
if (peerType instanceof GramJs.InlineQueryPeerTypeSameBotPM) return 'self';
|
||||||
|
return undefined!; // Never reached
|
||||||
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import type {
|
|||||||
ApiFactCheck,
|
ApiFactCheck,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
ApiKeyboardButton,
|
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiMessageForwardInfo,
|
ApiMessageForwardInfo,
|
||||||
@ -17,9 +16,9 @@ import type {
|
|||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
ApiPoll,
|
ApiPoll,
|
||||||
|
ApiPreparedInlineMessage,
|
||||||
ApiQuickReply,
|
ApiQuickReply,
|
||||||
ApiReplyInfo,
|
ApiReplyInfo,
|
||||||
ApiReplyKeyboard,
|
|
||||||
ApiSponsoredMessage,
|
ApiSponsoredMessage,
|
||||||
ApiSticker,
|
ApiSticker,
|
||||||
ApiStory,
|
ApiStory,
|
||||||
@ -28,9 +27,7 @@ import type {
|
|||||||
ApiVideo,
|
ApiVideo,
|
||||||
MediaContent,
|
MediaContent,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import {
|
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../types';
|
||||||
ApiMessageEntityTypes, MAIN_THREAD_ID,
|
|
||||||
} from '../../types';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DELETED_COMMENTS_CHANNEL_ID,
|
DELETED_COMMENTS_CHANNEL_ID,
|
||||||
@ -47,10 +44,18 @@ import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
|||||||
import { interpolateArray } from '../../../util/waveform';
|
import { interpolateArray } from '../../../util/waveform';
|
||||||
import { buildPeer } from '../gramjsBuilders';
|
import { buildPeer } from '../gramjsBuilders';
|
||||||
import {
|
import {
|
||||||
|
addDocumentToLocalDb,
|
||||||
addPhotoToLocalDb,
|
addPhotoToLocalDb,
|
||||||
|
addWebDocumentToLocalDb,
|
||||||
type MediaRepairContext,
|
type MediaRepairContext,
|
||||||
} from '../helpers/localDb';
|
} from '../helpers/localDb';
|
||||||
import { resolveMessageApiChatId, serializeBytes } from '../helpers/misc';
|
import { resolveMessageApiChatId, serializeBytes } from '../helpers/misc';
|
||||||
|
import {
|
||||||
|
buildApiBotInlineMediaResult,
|
||||||
|
buildApiBotInlineResult,
|
||||||
|
buildApiInlineQueryPeerType,
|
||||||
|
buildReplyButtons,
|
||||||
|
} from './bots';
|
||||||
import {
|
import {
|
||||||
buildApiFormattedText,
|
buildApiFormattedText,
|
||||||
buildApiPhoto,
|
buildApiPhoto,
|
||||||
@ -193,7 +198,7 @@ export function buildApiMessageWithChatId(
|
|||||||
const isEdited = Boolean(mtpMessage.editDate) && !mtpMessage.editHide;
|
const isEdited = Boolean(mtpMessage.editDate) && !mtpMessage.editHide;
|
||||||
const {
|
const {
|
||||||
inlineButtons, keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse, isKeyboardSelective,
|
inlineButtons, keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse, isKeyboardSelective,
|
||||||
} = buildReplyButtons(mtpMessage, isInvoiceMedia) || {};
|
} = buildReplyButtons(mtpMessage.replyMarkup, mtpMessage.id) || {};
|
||||||
const { mediaUnread: isMediaUnread, postAuthor } = mtpMessage;
|
const { 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);
|
||||||
@ -357,159 +362,6 @@ export function buildApiFactCheck(factCheck: GramJs.FactCheck): ApiFactCheck {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildReplyButtons(message: UniversalMessage, shouldSkipBuyButton?: boolean): ApiReplyKeyboard | undefined {
|
|
||||||
const { replyMarkup, media } = message;
|
|
||||||
|
|
||||||
if (!(replyMarkup instanceof GramJs.ReplyKeyboardMarkup || replyMarkup instanceof GramJs.ReplyInlineMarkup)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const markup = replyMarkup.rows.map(({ buttons }) => {
|
|
||||||
return buttons.map((button): ApiKeyboardButton | undefined => {
|
|
||||||
const { text } = button;
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButton) {
|
|
||||||
return {
|
|
||||||
type: 'command',
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonUrl) {
|
|
||||||
if (button.url.includes('?startgroup=')) {
|
|
||||||
return {
|
|
||||||
type: 'unsupported',
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'url',
|
|
||||||
text,
|
|
||||||
url: button.url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonCallback) {
|
|
||||||
if (button.requiresPassword) {
|
|
||||||
return {
|
|
||||||
type: 'unsupported',
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'callback',
|
|
||||||
text,
|
|
||||||
data: serializeBytes(button.data),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonRequestPoll) {
|
|
||||||
return {
|
|
||||||
type: 'requestPoll',
|
|
||||||
text,
|
|
||||||
isQuiz: button.quiz,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonRequestPhone) {
|
|
||||||
return {
|
|
||||||
type: 'requestPhone',
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonBuy) {
|
|
||||||
if (media instanceof GramJs.MessageMediaInvoice && media.receiptMsgId) {
|
|
||||||
return {
|
|
||||||
type: 'receipt',
|
|
||||||
receiptMessageId: media.receiptMsgId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (shouldSkipBuyButton) return undefined;
|
|
||||||
return {
|
|
||||||
type: 'buy',
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonGame) {
|
|
||||||
return {
|
|
||||||
type: 'game',
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonSwitchInline) {
|
|
||||||
return {
|
|
||||||
type: 'switchBotInline',
|
|
||||||
text,
|
|
||||||
query: button.query,
|
|
||||||
isSamePeer: button.samePeer,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonUserProfile) {
|
|
||||||
return {
|
|
||||||
type: 'userProfile',
|
|
||||||
text,
|
|
||||||
userId: button.userId.toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonSimpleWebView) {
|
|
||||||
return {
|
|
||||||
type: 'simpleWebView',
|
|
||||||
text,
|
|
||||||
url: button.url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonWebView) {
|
|
||||||
return {
|
|
||||||
type: 'webView',
|
|
||||||
text,
|
|
||||||
url: button.url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonUrlAuth) {
|
|
||||||
return {
|
|
||||||
type: 'urlAuth',
|
|
||||||
text,
|
|
||||||
url: button.url,
|
|
||||||
buttonId: button.buttonId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button instanceof GramJs.KeyboardButtonCopy) {
|
|
||||||
return {
|
|
||||||
type: 'copy',
|
|
||||||
text,
|
|
||||||
copyText: button.copyText,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'unsupported',
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}).filter(Boolean);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (markup.every((row) => !row.length)) return undefined;
|
|
||||||
|
|
||||||
return {
|
|
||||||
[replyMarkup instanceof GramJs.ReplyKeyboardMarkup ? 'keyboardButtons' : 'inlineButtons']: markup,
|
|
||||||
...(replyMarkup instanceof GramJs.ReplyKeyboardMarkup && {
|
|
||||||
keyboardPlaceholder: replyMarkup.placeholder,
|
|
||||||
isKeyboardSingleUse: replyMarkup.singleUse,
|
|
||||||
isKeyboardSelective: replyMarkup.selective,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildNewPoll(poll: ApiNewPoll, localId: number): ApiPoll {
|
function buildNewPoll(poll: ApiNewPoll, localId: number): ApiPoll {
|
||||||
return {
|
return {
|
||||||
mediaType: 'poll',
|
mediaType: 'poll',
|
||||||
@ -879,3 +731,36 @@ export function buildApiReportResult(
|
|||||||
options,
|
options,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processInlineBotResult(queryId: string, result: GramJs.TypeBotInlineResult) {
|
||||||
|
if (result instanceof GramJs.BotInlineMediaResult) {
|
||||||
|
if (result.document instanceof GramJs.Document) {
|
||||||
|
addDocumentToLocalDb(result.document);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.photo instanceof GramJs.Photo) {
|
||||||
|
addPhotoToLocalDb(result.photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildApiBotInlineMediaResult(result, queryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.thumb) {
|
||||||
|
addWebDocumentToLocalDb(result.thumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildApiBotInlineResult(result, queryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildPreparedInlineMessage(
|
||||||
|
result: GramJs.messages.TypePreparedInlineMessage,
|
||||||
|
): ApiPreparedInlineMessage {
|
||||||
|
const queryId = result.queryId.toString();
|
||||||
|
|
||||||
|
return {
|
||||||
|
queryId,
|
||||||
|
result: processInlineBotResult(queryId, result.result),
|
||||||
|
peerTypes: result.peerTypes?.map(buildApiInlineQueryPeerType),
|
||||||
|
cacheTime: result.cacheTime,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -24,11 +24,15 @@ import type {
|
|||||||
ApiSticker,
|
ApiSticker,
|
||||||
ApiStory,
|
ApiStory,
|
||||||
ApiStorySkipped,
|
ApiStorySkipped,
|
||||||
|
ApiUser,
|
||||||
ApiUserStatus,
|
ApiUserStatus,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
MediaContent,
|
MediaContent,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import { MAIN_THREAD_ID, MESSAGE_DELETED } from '../../types';
|
import {
|
||||||
|
MAIN_THREAD_ID,
|
||||||
|
MESSAGE_DELETED,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
API_GENERAL_ID_LIMIT,
|
API_GENERAL_ID_LIMIT,
|
||||||
@ -65,6 +69,7 @@ import {
|
|||||||
buildApiThreadInfo,
|
buildApiThreadInfo,
|
||||||
buildLocalForwardedMessage,
|
buildLocalForwardedMessage,
|
||||||
buildLocalMessage,
|
buildLocalMessage,
|
||||||
|
buildPreparedInlineMessage,
|
||||||
buildUploadingMedia,
|
buildUploadingMedia,
|
||||||
} from '../apiBuilders/messages';
|
} from '../apiBuilders/messages';
|
||||||
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||||
@ -2178,3 +2183,18 @@ export async function exportMessageLink({
|
|||||||
|
|
||||||
return result?.link;
|
return result?.link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchPreparedInlineMessage({
|
||||||
|
bot, id,
|
||||||
|
}: {
|
||||||
|
bot: ApiUser;
|
||||||
|
id: string;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.messages.GetPreparedInlineMessage({
|
||||||
|
bot: buildInputEntity(bot.id, bot.accessHash) as GramJs.InputUser,
|
||||||
|
id,
|
||||||
|
}));
|
||||||
|
if (!result) return undefined;
|
||||||
|
|
||||||
|
return buildPreparedInlineMessage(result);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import type {
|
import type {
|
||||||
ApiDimensions,
|
ApiDimensions, ApiDocument,
|
||||||
ApiPhoto, ApiSticker, ApiThumbnail, ApiVideo, MediaContainer,
|
ApiPhoto, ApiReplyKeyboard,
|
||||||
|
ApiSticker, ApiThumbnail,
|
||||||
|
ApiVideo, MediaContainer,
|
||||||
|
MediaContent,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
|
|
||||||
export type ApiInlineResultType = (
|
export type ApiInlineResultType = (
|
||||||
'article' | 'audio' | 'contact' | 'document' | 'game' | 'gif' | 'location' | 'mpeg4_gif' |
|
'article' | 'audio' | 'contact' | 'document' | 'game' | 'gif' | 'location' | 'mpeg4_gif' |
|
||||||
'photo' | 'sticker' | 'venue' | 'video' | 'voice' | 'file'
|
'photo' | 'sticker' | 'venue' | 'video' | 'voice' | 'file' | 'geo'
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface ApiWebDocument {
|
export interface ApiWebDocument {
|
||||||
@ -17,6 +20,11 @@ export interface ApiWebDocument {
|
|||||||
dimensions?: ApiDimensions;
|
dimensions?: ApiDimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ApiBotInlineMessage = {
|
||||||
|
content: MediaContent;
|
||||||
|
replyMarkup?: ApiReplyKeyboard;
|
||||||
|
};
|
||||||
|
|
||||||
export interface ApiBotInlineResult {
|
export interface ApiBotInlineResult {
|
||||||
id: string;
|
id: string;
|
||||||
queryId: string;
|
queryId: string;
|
||||||
@ -24,7 +32,9 @@ export interface ApiBotInlineResult {
|
|||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
|
content?: ApiWebDocument;
|
||||||
webThumbnail?: ApiWebDocument;
|
webThumbnail?: ApiWebDocument;
|
||||||
|
sendMessage: ApiBotInlineMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiBotInlineMediaResult {
|
export interface ApiBotInlineMediaResult {
|
||||||
@ -34,9 +44,11 @@ export interface ApiBotInlineMediaResult {
|
|||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
sticker?: ApiSticker;
|
sticker?: ApiSticker;
|
||||||
|
document?: ApiDocument;
|
||||||
photo?: ApiPhoto;
|
photo?: ApiPhoto;
|
||||||
gif?: ApiVideo;
|
gif?: ApiVideo;
|
||||||
thumbnail?: ApiThumbnail;
|
thumbnail?: ApiThumbnail;
|
||||||
|
sendMessage: ApiBotInlineMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiBotInlineSwitchPm {
|
export interface ApiBotInlineSwitchPm {
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import type { ThreadId, WebPageMediaSize } from '../../types';
|
import type { ThreadId, WebPageMediaSize } from '../../types';
|
||||||
import type { ApiWebDocument } from './bots';
|
import type {
|
||||||
|
ApiBotInlineMediaResult,
|
||||||
|
ApiBotInlineResult,
|
||||||
|
ApiWebDocument,
|
||||||
|
} from './bots';
|
||||||
import type { ApiPeerColor } from './chats';
|
import type { ApiPeerColor } from './chats';
|
||||||
import type { ApiMessageAction } from './messageActions';
|
import type { ApiMessageAction } from './messageActions';
|
||||||
import type {
|
import type {
|
||||||
@ -9,6 +13,7 @@ import type {
|
|||||||
import type {
|
import type {
|
||||||
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
||||||
} from './stories';
|
} from './stories';
|
||||||
|
import type { ApiInlineQueryPeerType } from './users';
|
||||||
|
|
||||||
export interface ApiDimensions {
|
export interface ApiDimensions {
|
||||||
width: number;
|
width: number;
|
||||||
@ -255,12 +260,12 @@ export interface ApiGeoPoint {
|
|||||||
accuracyRadius?: number;
|
accuracyRadius?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiGeo {
|
export interface ApiGeo {
|
||||||
mediaType: 'geo';
|
mediaType: 'geo';
|
||||||
geo: ApiGeoPoint;
|
geo: ApiGeoPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiVenue {
|
export interface ApiVenue {
|
||||||
mediaType: 'venue';
|
mediaType: 'venue';
|
||||||
geo: ApiGeoPoint;
|
geo: ApiGeoPoint;
|
||||||
title: string;
|
title: string;
|
||||||
@ -270,11 +275,11 @@ interface ApiVenue {
|
|||||||
venueType: string;
|
venueType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiGeoLive {
|
export interface ApiGeoLive {
|
||||||
mediaType: 'geoLive';
|
mediaType: 'geoLive';
|
||||||
geo: ApiGeoPoint;
|
geo: ApiGeoPoint;
|
||||||
heading?: number;
|
heading?: number;
|
||||||
period: number;
|
period?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiLocation = ApiGeo | ApiVenue | ApiGeoLive;
|
export type ApiLocation = ApiGeo | ApiVenue | ApiGeoLive;
|
||||||
@ -915,6 +920,13 @@ export type ApiSponsoredMessageReportResult = {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiPreparedInlineMessage = {
|
||||||
|
queryId: string;
|
||||||
|
result: ApiBotInlineResult | ApiBotInlineMediaResult;
|
||||||
|
peerTypes: ApiInlineQueryPeerType[];
|
||||||
|
cacheTime: number;
|
||||||
|
};
|
||||||
|
|
||||||
export const MAIN_THREAD_ID = -1;
|
export const MAIN_THREAD_ID = -1;
|
||||||
|
|
||||||
// `Symbol` can not be transferred from worker
|
// `Symbol` can not be transferred from worker
|
||||||
|
|||||||
@ -103,9 +103,11 @@ export interface ApiUsername {
|
|||||||
export type ApiChatType = typeof API_CHAT_TYPES[number];
|
export type ApiChatType = typeof API_CHAT_TYPES[number];
|
||||||
export type ApiAttachMenuPeerType = 'self' | ApiChatType;
|
export type ApiAttachMenuPeerType = 'self' | ApiChatType;
|
||||||
|
|
||||||
|
export type ApiInlineQueryPeerType = 'self' | 'supergroups' | ApiChatType;
|
||||||
|
|
||||||
type ApiAttachBotForMenu = {
|
type ApiAttachBotForMenu = {
|
||||||
isForAttachMenu: true;
|
isForAttachMenu: true;
|
||||||
attachMenuPeerTypes: ApiAttachMenuPeerType[];
|
attachMenuPeerTypes?: ApiAttachMenuPeerType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type ApiAttachBotBase = {
|
type ApiAttachBotBase = {
|
||||||
|
|||||||
@ -154,6 +154,8 @@
|
|||||||
"Code" = "Code";
|
"Code" = "Code";
|
||||||
"Open" = "Open";
|
"Open" = "Open";
|
||||||
"LoginHeaderPassword" = "Enter Password";
|
"LoginHeaderPassword" = "Enter Password";
|
||||||
|
"BotShareMessageShare" = "Share with...";
|
||||||
|
"BotShareMessage" = "Share Message";
|
||||||
"LoginEnterPasswordDescription" = "You have Two-Step Verification enabled, so your account is protected with an additional password.";
|
"LoginEnterPasswordDescription" = "You have Two-Step Verification enabled, so your account is protected with an additional password.";
|
||||||
"StartText" = "Please confirm your country code\nand enter your phone number.";
|
"StartText" = "Please confirm your country code\nand enter your phone number.";
|
||||||
"LoginPhonePlaceholder" = "Your phone number";
|
"LoginPhonePlaceholder" = "Your phone number";
|
||||||
@ -538,8 +540,10 @@
|
|||||||
"EditAdminTransferSetPassword" = "Set Password";
|
"EditAdminTransferSetPassword" = "Set Password";
|
||||||
"WebAppAddToAttachmentText" = "{bot} requests your permission to be added as an option to your attachment menu, so you can access it from any chat.";
|
"WebAppAddToAttachmentText" = "{bot} requests your permission to be added as an option to your attachment menu, so you can access it from any chat.";
|
||||||
"BotOpenPageTitle" = "Open page";
|
"BotOpenPageTitle" = "Open page";
|
||||||
|
"WebAppShareMessageInfo" = "{user} mini app suggests you to send this message to a chat you select.";
|
||||||
"BotPermissionGameAlert" = "Allow {bot} to pass your Telegram name and id (not your phone number) to pages you open with this bot?";
|
"BotPermissionGameAlert" = "Allow {bot} to pass your Telegram name and id (not your phone number) to pages you open with this bot?";
|
||||||
"BotOpenPageMessage" = "**{bot}** would like to open its web app to proceed.\n\nIt will be able to access your **IP address** and basic device info.";
|
"BotOpenPageMessage" = "**{bot}** would like to open its web app to proceed.\n\nIt will be able to access your **IP address** and basic device info.";
|
||||||
|
"BotSharedToOne" = "Sent message to {peer}";
|
||||||
"FilterDeleteAlert" = "Are you sure you want to remove this folder? Your chats will not be deleted.";
|
"FilterDeleteAlert" = "Are you sure you want to remove this folder? Your chats will not be deleted.";
|
||||||
"RequestToJoinChannelSentDescription" = "You will be added to the channel once its admins approve your request.";
|
"RequestToJoinChannelSentDescription" = "You will be added to the channel once its admins approve your request.";
|
||||||
"RequestToJoinGroupSentDescription" = "You will be added to the group once an admin approves your request.";
|
"RequestToJoinGroupSentDescription" = "You will be added to the group once an admin approves your request.";
|
||||||
|
|||||||
@ -33,6 +33,9 @@ export { default as EmojiStatusAccessModal } from '../components/modals/emojiSta
|
|||||||
export { default as LocationAccessModal } from '../components/modals/locationAccess/LocationAccessModal';
|
export { default as LocationAccessModal } from '../components/modals/locationAccess/LocationAccessModal';
|
||||||
export { default as ReportAdModal } from '../components/modals/reportAd/ReportAdModal';
|
export { default as ReportAdModal } from '../components/modals/reportAd/ReportAdModal';
|
||||||
export { default as ReportModal } from '../components/modals/reportModal/ReportModal';
|
export { default as ReportModal } from '../components/modals/reportModal/ReportModal';
|
||||||
|
export { default as PreparedMessageModal } from '../components/modals/preparedMessage/PreparedMessageModal';
|
||||||
|
export { default as SharePreparedMessageModal }
|
||||||
|
from '../components/modals/sharePreparedMessage/SharePreparedMessageModal';
|
||||||
export { default as CalendarModal } from '../components/common/CalendarModal';
|
export { default as CalendarModal } from '../components/common/CalendarModal';
|
||||||
export { default as DeleteMessageModal } from '../components/common/DeleteMessageModal';
|
export { default as DeleteMessageModal } from '../components/common/DeleteMessageModal';
|
||||||
export { default as PinMessageModal } from '../components/common/PinMessageModal';
|
export { default as PinMessageModal } from '../components/common/PinMessageModal';
|
||||||
|
|||||||
@ -1125,10 +1125,11 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
const { id, queryId, isSilent } = args;
|
const { id, queryId, isSilent } = args;
|
||||||
sendInlineBotResult({
|
sendInlineBotResult({
|
||||||
id,
|
id,
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
queryId,
|
queryId,
|
||||||
scheduledAt,
|
scheduledAt,
|
||||||
isSilent,
|
isSilent,
|
||||||
messageList,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1272,8 +1273,9 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
sendInlineBotResult({
|
sendInlineBotResult({
|
||||||
id: inlineResult.id,
|
id: inlineResult.id,
|
||||||
queryId: inlineResult.queryId,
|
queryId: inlineResult.queryId,
|
||||||
|
threadId,
|
||||||
|
chatId,
|
||||||
isSilent,
|
isSilent,
|
||||||
messageList: currentMessageList!,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,7 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const chatFullInfo = selectChatFullInfo(global, id);
|
const chatFullInfo = selectChatFullInfo(global, id);
|
||||||
|
|
||||||
return chat && chatFullInfo && getCanPostInChat(chat, undefined, undefined, chatFullInfo);
|
return chat && (!chatFullInfo || getCanPostInChat(chat, undefined, undefined, chatFullInfo));
|
||||||
});
|
});
|
||||||
|
|
||||||
const sorted = sortChatIds(
|
const sorted = sortChatIds(
|
||||||
|
|||||||
@ -154,7 +154,8 @@ const AttachMenu: FC<OwnProps> = ({
|
|||||||
return attachBots
|
return attachBots
|
||||||
? Object.values(attachBots).filter((bot) => {
|
? Object.values(attachBots).filter((bot) => {
|
||||||
if (!peerType || !bot.isForAttachMenu) return false;
|
if (!peerType || !bot.isForAttachMenu) return false;
|
||||||
if (peerType === 'bots' && bot.id === chatId && bot.attachMenuPeerTypes.includes('self')) {
|
if (peerType === 'bots' && bot.id === chatId
|
||||||
|
&& bot.attachMenuPeerTypes && bot.attachMenuPeerTypes.includes('self')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return bot.attachMenuPeerTypes!.includes(peerType);
|
return bot.attachMenuPeerTypes!.includes(peerType);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
import React, { memo } from '../../../../lib/teact/teact';
|
import React, { memo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiBotInlineResult } from '../../../../api/types';
|
import type { ApiBotInlineMediaResult, ApiBotInlineResult } from '../../../../api/types';
|
||||||
|
|
||||||
import useLastCallback from '../../../../hooks/useLastCallback';
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
|
||||||
@ -9,15 +9,18 @@ import BaseResult from './BaseResult';
|
|||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
inlineResult: ApiBotInlineResult;
|
inlineResult: ApiBotInlineResult | ApiBotInlineMediaResult;
|
||||||
onClick: (result: ApiBotInlineResult) => void;
|
onClick: (result: ApiBotInlineResult | ApiBotInlineMediaResult) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArticleResult: FC<OwnProps> = ({ focus, inlineResult, onClick }) => {
|
const ArticleResult: FC<OwnProps> = ({ focus, inlineResult, onClick }) => {
|
||||||
const {
|
const {
|
||||||
title, url, description, webThumbnail,
|
title, description,
|
||||||
} = inlineResult;
|
} = inlineResult;
|
||||||
|
|
||||||
|
const url = 'url' in inlineResult ? inlineResult.url : undefined;
|
||||||
|
const webThumbnail = 'webThumbnail' in inlineResult ? inlineResult.webThumbnail : undefined;
|
||||||
|
|
||||||
const handleClick = useLastCallback(() => {
|
const handleClick = useLastCallback(() => {
|
||||||
onClick(inlineResult);
|
onClick(inlineResult);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
import React, { memo } from '../../../../lib/teact/teact';
|
import React, { memo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiBotInlineMediaResult, ApiBotInlineResult, ApiVideo } from '../../../../api/types';
|
import type { ApiBotInlineMediaResult, ApiVideo } from '../../../../api/types';
|
||||||
import type { ObserveFn } from '../../../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../../../hooks/useIntersectionObserver';
|
||||||
|
|
||||||
import useLastCallback from '../../../../hooks/useLastCallback';
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
@ -13,7 +13,7 @@ type OwnProps = {
|
|||||||
isSavedMessages?: boolean;
|
isSavedMessages?: boolean;
|
||||||
canSendGifs?: boolean;
|
canSendGifs?: boolean;
|
||||||
observeIntersection: ObserveFn;
|
observeIntersection: ObserveFn;
|
||||||
onClick: (result: ApiBotInlineResult, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
onClick: (result: ApiBotInlineMediaResult, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GifResult: FC<OwnProps> = ({
|
const GifResult: FC<OwnProps> = ({
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export type OwnProps = {
|
|||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
isForGallery?: boolean;
|
isForGallery?: boolean;
|
||||||
inlineResult: ApiBotInlineMediaResult | ApiBotInlineResult;
|
inlineResult: ApiBotInlineMediaResult | ApiBotInlineResult;
|
||||||
onClick: (result: ApiBotInlineResult) => void;
|
onClick: (result: ApiBotInlineMediaResult | ApiBotInlineResult) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MediaResult: FC<OwnProps> = ({
|
const MediaResult: FC<OwnProps> = ({
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
import React, { memo } from '../../../../lib/teact/teact';
|
import React, { memo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiBotInlineMediaResult, ApiBotInlineResult } from '../../../../api/types';
|
import type { ApiBotInlineMediaResult } from '../../../../api/types';
|
||||||
import type { ObserveFn } from '../../../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../../../hooks/useIntersectionObserver';
|
||||||
|
|
||||||
import { STICKER_SIZE_INLINE_BOT_RESULT } from '../../../../config';
|
import { STICKER_SIZE_INLINE_BOT_RESULT } from '../../../../config';
|
||||||
@ -12,7 +12,7 @@ type OwnProps = {
|
|||||||
inlineResult: ApiBotInlineMediaResult;
|
inlineResult: ApiBotInlineMediaResult;
|
||||||
isSavedMessages?: boolean;
|
isSavedMessages?: boolean;
|
||||||
observeIntersection: ObserveFn;
|
observeIntersection: ObserveFn;
|
||||||
onClick: (result: ApiBotInlineResult, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
onClick: (result: ApiBotInlineMediaResult, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||||
isCurrentUserPremium?: boolean;
|
isCurrentUserPremium?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -201,9 +201,9 @@ type MessagePositionProperties = {
|
|||||||
type OwnProps =
|
type OwnProps =
|
||||||
{
|
{
|
||||||
message: ApiMessage;
|
message: ApiMessage;
|
||||||
observeIntersectionForBottom: ObserveFn;
|
observeIntersectionForBottom?: ObserveFn;
|
||||||
observeIntersectionForLoading: ObserveFn;
|
observeIntersectionForLoading?: ObserveFn;
|
||||||
observeIntersectionForPlaying: ObserveFn;
|
observeIntersectionForPlaying?: ObserveFn;
|
||||||
album?: IAlbum;
|
album?: IAlbum;
|
||||||
noAvatars?: boolean;
|
noAvatars?: boolean;
|
||||||
withAvatar?: boolean;
|
withAvatar?: boolean;
|
||||||
@ -214,9 +214,9 @@ type OwnProps =
|
|||||||
noReplies: boolean;
|
noReplies: boolean;
|
||||||
appearanceOrder: number;
|
appearanceOrder: number;
|
||||||
isJustAdded: boolean;
|
isJustAdded: boolean;
|
||||||
memoFirstUnreadIdRef: { current: number | undefined };
|
memoFirstUnreadIdRef?: { current: number | undefined };
|
||||||
getIsMessageListReady: Signal<boolean>;
|
getIsMessageListReady?: Signal<boolean>;
|
||||||
onIntersectPinnedMessage: OnIntersectPinnedMessage;
|
onIntersectPinnedMessage?: OnIntersectPinnedMessage;
|
||||||
}
|
}
|
||||||
& MessagePositionProperties;
|
& MessagePositionProperties;
|
||||||
|
|
||||||
@ -494,7 +494,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
useUnmountCleanup(() => {
|
useUnmountCleanup(() => {
|
||||||
if (message.isPinned) {
|
if (message.isPinned) {
|
||||||
const id = album ? album.mainMessage.id : messageId;
|
const id = album ? album.mainMessage.id : messageId;
|
||||||
onIntersectPinnedMessage({ viewportPinnedIdsToRemove: [id] });
|
onIntersectPinnedMessage?.({ viewportPinnedIdsToRemove: [id] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -729,7 +729,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ((sticker?.hasEffect || effect) && ((
|
if ((sticker?.hasEffect || effect) && ((
|
||||||
memoFirstUnreadIdRef.current && messageId >= memoFirstUnreadIdRef.current
|
memoFirstUnreadIdRef?.current && messageId >= memoFirstUnreadIdRef.current
|
||||||
) || isLocal)) {
|
) || isLocal)) {
|
||||||
requestEffect();
|
requestEffect();
|
||||||
}
|
}
|
||||||
@ -1105,7 +1105,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{sticker && (
|
{sticker && observeIntersectionForLoading && observeIntersectionForPlaying && (
|
||||||
<Sticker
|
<Sticker
|
||||||
message={message}
|
message={message}
|
||||||
observeIntersection={observeIntersectionForLoading}
|
observeIntersection={observeIntersectionForLoading}
|
||||||
@ -1383,7 +1383,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
function renderInvertibleMediaContent(hasCustomAppendix: boolean) {
|
function renderInvertibleMediaContent(hasCustomAppendix: boolean) {
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
{isAlbum && (
|
{isAlbum && observeIntersectionForLoading && (
|
||||||
<Album
|
<Album
|
||||||
album={album!}
|
album={album!}
|
||||||
albumLayout={albumLayout!}
|
albumLayout={albumLayout!}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export default function useOuterHandlers(
|
|||||||
isContextMenuShown: boolean,
|
isContextMenuShown: boolean,
|
||||||
quickReactionRef: RefObject<HTMLDivElement>,
|
quickReactionRef: RefObject<HTMLDivElement>,
|
||||||
shouldHandleMouseLeave: boolean,
|
shouldHandleMouseLeave: boolean,
|
||||||
getIsMessageListReady: Signal<boolean>,
|
getIsMessageListReady?: Signal<boolean>,
|
||||||
) {
|
) {
|
||||||
const { updateDraftReplyInfo, sendDefaultReaction } = getActions();
|
const { updateDraftReplyInfo, sendDefaultReaction } = getActions();
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ export default function useOuterHandlers(
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!IS_TOUCH_ENV || isInSelectMode || !canReply || isContextMenuShown || !getIsMessageListReady()) {
|
if (!IS_TOUCH_ENV || isInSelectMode || !canReply || isContextMenuShown || !getIsMessageListReady?.()) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,8 +28,10 @@ import LocationAccessModal from './locationAccess/LocationAccessModal.async';
|
|||||||
import MapModal from './map/MapModal.async';
|
import MapModal from './map/MapModal.async';
|
||||||
import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
|
import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
|
||||||
import PaidReactionModal from './paidReaction/PaidReactionModal.async';
|
import PaidReactionModal from './paidReaction/PaidReactionModal.async';
|
||||||
|
import PreparedMessageModal from './preparedMessage/PreparedMessageModal.async';
|
||||||
import ReportAdModal from './reportAd/ReportAdModal.async';
|
import ReportAdModal from './reportAd/ReportAdModal.async';
|
||||||
import ReportModal from './reportModal/ReportModal.async';
|
import ReportModal from './reportModal/ReportModal.async';
|
||||||
|
import SharePreparedMessageModal from './sharePreparedMessage/SharePreparedMessageModal.async';
|
||||||
import StarsGiftModal from './stars/gift/StarsGiftModal.async';
|
import StarsGiftModal from './stars/gift/StarsGiftModal.async';
|
||||||
import StarsBalanceModal from './stars/StarsBalanceModal.async';
|
import StarsBalanceModal from './stars/StarsBalanceModal.async';
|
||||||
import StarsPaymentModal from './stars/StarsPaymentModal.async';
|
import StarsPaymentModal from './stars/StarsPaymentModal.async';
|
||||||
@ -72,6 +74,8 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'giftUpgradeModal' |
|
'giftUpgradeModal' |
|
||||||
'monetizationVerificationModal' |
|
'monetizationVerificationModal' |
|
||||||
'giftWithdrawModal' |
|
'giftWithdrawModal' |
|
||||||
|
'preparedMessageModal' |
|
||||||
|
'sharePreparedMessageModal' |
|
||||||
'giftStatusInfoModal' |
|
'giftStatusInfoModal' |
|
||||||
'giftTransferModal'
|
'giftTransferModal'
|
||||||
>;
|
>;
|
||||||
@ -120,6 +124,8 @@ const MODALS: ModalRegistry = {
|
|||||||
monetizationVerificationModal: VerificationMonetizationModal,
|
monetizationVerificationModal: VerificationMonetizationModal,
|
||||||
giftWithdrawModal: GiftWithdrawModal,
|
giftWithdrawModal: GiftWithdrawModal,
|
||||||
giftStatusInfoModal: GiftStatusInfoModal,
|
giftStatusInfoModal: GiftStatusInfoModal,
|
||||||
|
preparedMessageModal: PreparedMessageModal,
|
||||||
|
sharePreparedMessageModal: SharePreparedMessageModal,
|
||||||
giftTransferModal: GiftTransferModal,
|
giftTransferModal: GiftTransferModal,
|
||||||
};
|
};
|
||||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
import React from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './PreparedMessageModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const PreparedMessageModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const PreparedMessageModal = useModuleLoader(Bundles.Extra, 'PreparedMessageModal', !modal);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return PreparedMessageModal ? <PreparedMessageModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PreparedMessageModalAsync;
|
||||||
@ -0,0 +1,185 @@
|
|||||||
|
@use "../../../styles/mixins";
|
||||||
|
|
||||||
|
.root {
|
||||||
|
:global(.modal-dialog) {
|
||||||
|
max-width: 26rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalTitle {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
border-bottom-left-radius: var(--border-radius-modal);
|
||||||
|
border-bottom-right-radius: var(--border-radius-modal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-bottom: 0.0625rem solid var(--color-borders);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionMessageView {
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
min-height: 22.5rem;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
margin: 0.75rem;
|
||||||
|
width: calc(100% - 1.5rem);
|
||||||
|
|
||||||
|
border-radius: var(--border-radius-default);
|
||||||
|
|
||||||
|
background-color: var(--theme-background-color);
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
|
||||||
|
:global(.Message) {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.message-content) {
|
||||||
|
max-width: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-light) & {
|
||||||
|
background-image: url('../../../assets/chat-bg-br.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-image: url('../../../assets/chat-bg-pattern-light.png');
|
||||||
|
background-position: top right;
|
||||||
|
background-size: 510px auto;
|
||||||
|
background-repeat: repeat;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
|
||||||
|
:global(html.theme-dark) & {
|
||||||
|
background-image: url('../../../assets/chat-bg-pattern-dark.png');
|
||||||
|
mix-blend-mode: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
bottom: auto;
|
||||||
|
height: calc(var(--vh, 1vh) * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: var(--theme-background-color);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-light) &:not(.customBgImage)::before {
|
||||||
|
background-image: url('../../../assets/chat-bg-br.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.customBgImage).customBgColor::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.customBgImage::before {
|
||||||
|
background-image: var(--custom-background) !important;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(body:not(.no-page-transitions)) &.withTransition {
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
|
||||||
|
&.customBgImage::before {
|
||||||
|
transition: background-image var(--layer-transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.customBgImage.blurred::before {
|
||||||
|
filter: blur(12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1276px) {
|
||||||
|
:global(body:not(.no-page-transitions)) &:not(.customBgImage)::before {
|
||||||
|
overflow: hidden;
|
||||||
|
transform: scale(1);
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-light body:not(.no-page-transitions)) &:not(.customBgImage).withRightColumn::before {
|
||||||
|
@media screen and (min-width: 1276px) {
|
||||||
|
transform: scaleX(0.73) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1921px) {
|
||||||
|
transform: scaleX(0.8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 2600px) {
|
||||||
|
transform: scaleX(0.95) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line @stylistic/max-line-length */
|
||||||
|
:global(html.theme-light body:not(.no-page-transitions)) &:not(.customBgImage).withRightColumn.withTransition::before {
|
||||||
|
transition: transform var(--layer-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.customBgImage):not(.customBgColor)::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-image: url('../../../assets/chat-bg-pattern-light.png');
|
||||||
|
background-position: top right;
|
||||||
|
background-size: 510px auto;
|
||||||
|
background-repeat: repeat;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
|
||||||
|
:global(html.theme-dark) & {
|
||||||
|
background-image: url('../../../assets/chat-bg-pattern-dark.png');
|
||||||
|
mix-blend-mode: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
198
src/components/modals/preparedMessage/PreparedMessageModal.tsx
Normal file
198
src/components/modals/preparedMessage/PreparedMessageModal.tsx
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
import React, {
|
||||||
|
type FC,
|
||||||
|
memo, useMemo, useRef,
|
||||||
|
} from '../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiUser } from '../../../api/types';
|
||||||
|
import type { TabState } from '../../../global/types';
|
||||||
|
import type { ThemeKey } from '../../../types';
|
||||||
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
|
|
||||||
|
import { getMockPreparedMessageFromResult, getUserFullName } from '../../../global/helpers';
|
||||||
|
import { selectTheme, selectUser } from '../../../global/selectors';
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import buildStyle from '../../../util/buildStyle';
|
||||||
|
|
||||||
|
import useCustomBackground from '../../../hooks/useCustomBackground';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import Message from '../../middle/message/Message';
|
||||||
|
import Button from '../../ui/Button';
|
||||||
|
import Modal from '../../ui/Modal';
|
||||||
|
|
||||||
|
import styles from './PreparedMessageModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['preparedMessageModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
theme: ThemeKey;
|
||||||
|
isBackgroundBlurred?: boolean;
|
||||||
|
patternColor?: string;
|
||||||
|
customBackground?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
bot?: ApiUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PreparedMessageModal: FC<OwnProps & StateProps> = ({
|
||||||
|
modal,
|
||||||
|
theme,
|
||||||
|
isBackgroundBlurred,
|
||||||
|
patternColor,
|
||||||
|
customBackground,
|
||||||
|
backgroundColor,
|
||||||
|
bot,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
closePreparedInlineMessageModal, sendWebAppEvent, openSharePreparedMessageModal,
|
||||||
|
} = getActions();
|
||||||
|
const lang = useLang();
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
|
||||||
|
const { webAppKey, message, botId } = modal || {};
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const customBackgroundValue = useCustomBackground(theme, customBackground);
|
||||||
|
|
||||||
|
const handleOpenClick = useLastCallback(() => {
|
||||||
|
if (webAppKey && botId && message) {
|
||||||
|
openSharePreparedMessageModal({
|
||||||
|
webAppKey,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
closePreparedInlineMessageModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCloseClick = useLastCallback(() => {
|
||||||
|
closePreparedInlineMessageModal();
|
||||||
|
if (webAppKey) {
|
||||||
|
sendWebAppEvent({
|
||||||
|
webAppKey,
|
||||||
|
event: {
|
||||||
|
eventType: 'prepared_message_failed',
|
||||||
|
eventData: { error: 'USER_DECLINED' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const header = useMemo(() => {
|
||||||
|
if (!modal) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.header}>
|
||||||
|
<Button
|
||||||
|
round
|
||||||
|
color="translucent"
|
||||||
|
size="smaller"
|
||||||
|
ariaLabel={lang('Close')}
|
||||||
|
onClick={handleCloseClick}
|
||||||
|
>
|
||||||
|
<Icon name="close" />
|
||||||
|
</Button>
|
||||||
|
<h3 className={buildClassName('modal-title', styles.modalTitle)}>
|
||||||
|
{lang('BotShareMessage')}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}, [lang, modal]);
|
||||||
|
|
||||||
|
const localMessage = useMemo(() => {
|
||||||
|
if (!botId || !message || !webAppKey) return undefined;
|
||||||
|
return getMockPreparedMessageFromResult(botId, message);
|
||||||
|
}, [botId, message, webAppKey]);
|
||||||
|
|
||||||
|
const bgClassName = buildClassName(
|
||||||
|
styles.background,
|
||||||
|
styles.withTransition,
|
||||||
|
customBackground && styles.customBgImage,
|
||||||
|
backgroundColor && styles.customBgColor,
|
||||||
|
customBackground && isBackgroundBlurred && styles.blurred,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
dialogRef={containerRef}
|
||||||
|
isOpen={isOpen}
|
||||||
|
header={header}
|
||||||
|
onClose={handleCloseClick}
|
||||||
|
className={styles.root}
|
||||||
|
contentClassName={styles.content}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.actionMessageView, 'MessageList')}
|
||||||
|
// @ts-ignore -- FIXME: Find a way to disable interactions but keep a11y
|
||||||
|
inert
|
||||||
|
style={buildStyle(
|
||||||
|
`--pattern-color: ${patternColor}`,
|
||||||
|
backgroundColor && `--theme-background-color: ${backgroundColor}`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={bgClassName}
|
||||||
|
style={customBackgroundValue ? `--custom-background: ${customBackgroundValue}` : undefined}
|
||||||
|
/>
|
||||||
|
{localMessage && (
|
||||||
|
<Message
|
||||||
|
key={botId}
|
||||||
|
message={localMessage}
|
||||||
|
threadId={MAIN_THREAD_ID}
|
||||||
|
messageListType="thread"
|
||||||
|
noComments
|
||||||
|
noReplies
|
||||||
|
appearanceOrder={0}
|
||||||
|
isJustAdded={false}
|
||||||
|
isFirstInGroup
|
||||||
|
isLastInGroup
|
||||||
|
isLastInList={false}
|
||||||
|
isFirstInDocumentGroup={false}
|
||||||
|
isLastInDocumentGroup={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<p className={styles.info}>
|
||||||
|
{lang('WebAppShareMessageInfo', { user: getUserFullName(bot) })}
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
size="smaller"
|
||||||
|
onClick={handleOpenClick}
|
||||||
|
>
|
||||||
|
{lang('BotShareMessageShare')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { modal }) => {
|
||||||
|
const theme = selectTheme(global);
|
||||||
|
const {
|
||||||
|
isBlurred: isBackgroundBlurred,
|
||||||
|
patternColor,
|
||||||
|
background: customBackground,
|
||||||
|
backgroundColor,
|
||||||
|
} = global.settings.themes[theme] || {};
|
||||||
|
const bot = modal ? selectUser(global, modal?.botId) : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
theme,
|
||||||
|
isBackgroundBlurred,
|
||||||
|
patternColor,
|
||||||
|
customBackground,
|
||||||
|
backgroundColor,
|
||||||
|
bot,
|
||||||
|
currentUserId: global.currentUserId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(PreparedMessageModal));
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
import React from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './SharePreparedMessageModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const SharePreparedMessageModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const SharePreparedMessageModal = useModuleLoader(Bundles.Extra, 'SharePreparedMessageModal', !modal);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return SharePreparedMessageModal ? <SharePreparedMessageModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SharePreparedMessageModalAsync;
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
import React, {
|
||||||
|
type FC,
|
||||||
|
memo, useEffect,
|
||||||
|
} from '../../../lib/teact/teact';
|
||||||
|
import { getActions, getGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { TabState } from '../../../global/types';
|
||||||
|
import type { ThreadId } from '../../../types';
|
||||||
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
|
|
||||||
|
import { getPeerTitle } from '../../../global/helpers';
|
||||||
|
import { selectPeer } from '../../../global/selectors';
|
||||||
|
|
||||||
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import RecipientPicker from '../../common/RecipientPicker';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['sharePreparedMessageModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const SharePreparedMessageModal: FC<OwnProps> = ({
|
||||||
|
modal,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
closeSharePreparedMessageModal,
|
||||||
|
sendInlineBotResult,
|
||||||
|
sendWebAppEvent,
|
||||||
|
showNotification,
|
||||||
|
} = getActions();
|
||||||
|
const lang = useOldLang();
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
|
||||||
|
const [isShown, markIsShown, unmarkIsShown] = useFlag();
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
markIsShown();
|
||||||
|
}
|
||||||
|
}, [isOpen, markIsShown]);
|
||||||
|
|
||||||
|
const { message, filter, webAppKey } = modal || {};
|
||||||
|
|
||||||
|
const handleClose = useLastCallback(() => {
|
||||||
|
closeSharePreparedMessageModal();
|
||||||
|
if (webAppKey) {
|
||||||
|
sendWebAppEvent({
|
||||||
|
webAppKey,
|
||||||
|
event: {
|
||||||
|
eventType: 'prepared_message_failed',
|
||||||
|
eventData: { error: 'USER_DECLINED' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSelectRecipient = useLastCallback((id: string, threadId?: ThreadId) => {
|
||||||
|
if (message && webAppKey) {
|
||||||
|
const global = getGlobal();
|
||||||
|
const peer = selectPeer(global, id);
|
||||||
|
sendInlineBotResult({
|
||||||
|
chatId: id,
|
||||||
|
threadId: threadId || MAIN_THREAD_ID,
|
||||||
|
id: message.result.id,
|
||||||
|
queryId: message.result.queryId,
|
||||||
|
});
|
||||||
|
sendWebAppEvent({
|
||||||
|
webAppKey,
|
||||||
|
event: {
|
||||||
|
eventType: 'prepared_message_sent',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
showNotification({
|
||||||
|
message: lang('BotSharedToOne', getPeerTitle(lang, peer!)),
|
||||||
|
});
|
||||||
|
closeSharePreparedMessageModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isOpen && !isShown) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecipientPicker
|
||||||
|
isOpen={isOpen}
|
||||||
|
searchPlaceholder={lang('Search')}
|
||||||
|
filter={filter}
|
||||||
|
onSelectRecipient={handleSelectRecipient}
|
||||||
|
onClose={handleClose}
|
||||||
|
onCloseAnimationEnd={unmarkIsShown}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(SharePreparedMessageModal);
|
||||||
@ -148,6 +148,7 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
openLocationAccessModal,
|
openLocationAccessModal,
|
||||||
changeWebAppModalState,
|
changeWebAppModalState,
|
||||||
closeWebAppModal,
|
closeWebAppModal,
|
||||||
|
openPreparedInlineMessageModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const [mainButton, setMainButton] = useState<WebAppButton | undefined>();
|
const [mainButton, setMainButton] = useState<WebAppButton | undefined>();
|
||||||
const [secondaryButton, setSecondaryButton] = useState<WebAppButton | undefined>();
|
const [secondaryButton, setSecondaryButton] = useState<WebAppButton | undefined>();
|
||||||
@ -698,6 +699,12 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
handleCheckDownloadFile(eventData.url, eventData.file_name);
|
handleCheckDownloadFile(eventData.url, eventData.file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventType === 'web_app_send_prepared_message') {
|
||||||
|
if (!bot || !webAppKey) return;
|
||||||
|
const { id } = eventData;
|
||||||
|
openPreparedInlineMessageModal({ botId: bot.id, messageId: id, webAppKey });
|
||||||
|
}
|
||||||
|
|
||||||
if (eventType === 'web_app_request_emoji_status_access') {
|
if (eventType === 'web_app_request_emoji_status_access') {
|
||||||
if (!bot) return;
|
if (!bot) return;
|
||||||
openEmojiStatusAccessModal({ bot, webAppKey });
|
openEmojiStatusAccessModal({ bot, webAppKey });
|
||||||
|
|||||||
@ -312,7 +312,7 @@ export const LANG_PACK = 'weba';
|
|||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
export const COUNTRIES_WITH_12H_TIME_FORMAT = new Set(['AU', 'BD', 'CA', 'CO', 'EG', 'HN', 'IE', 'IN', 'JO', 'MX', 'MY', 'NI', 'NZ', 'PH', 'PK', 'SA', 'SV', 'US']);
|
export const COUNTRIES_WITH_12H_TIME_FORMAT = new Set(['AU', 'BD', 'CA', 'CO', 'EG', 'HN', 'IE', 'IN', 'JO', 'MX', 'MY', 'NI', 'NZ', 'PH', 'PK', 'SA', 'SV', 'US']);
|
||||||
|
|
||||||
export const API_CHAT_TYPES = ['bots', 'channels', 'chats', 'users'] as const;
|
export const API_CHAT_TYPES = ['bots', 'channels', 'chats', 'users', 'groups'] as const;
|
||||||
|
|
||||||
export const HEART_REACTION: ApiReactionEmoji = {
|
export const HEART_REACTION: ApiReactionEmoji = {
|
||||||
type: 'emoji',
|
type: 'emoji',
|
||||||
|
|||||||
@ -381,14 +381,13 @@ addActionHandler('switchBotInline', (global, actions, payload): ActionReturnType
|
|||||||
|
|
||||||
addActionHandler('sendInlineBotResult', (global, actions, payload): ActionReturnType => {
|
addActionHandler('sendInlineBotResult', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
id, queryId, isSilent, scheduledAt, messageList,
|
id, queryId, isSilent, scheduledAt, threadId, chatId,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { chatId, threadId } = messageList;
|
|
||||||
const chat = selectChat(global, chatId)!;
|
const chat = selectChat(global, chatId)!;
|
||||||
const draftReplyInfo = selectDraft(global, chatId, threadId)?.replyInfo;
|
const draftReplyInfo = selectDraft(global, chatId, threadId)?.replyInfo;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
ApiAttachment,
|
ApiAttachment,
|
||||||
ApiChat,
|
ApiChat,
|
||||||
|
ApiChatType,
|
||||||
ApiDraft,
|
ApiDraft,
|
||||||
ApiError,
|
ApiError,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
@ -2342,6 +2343,59 @@ addActionHandler('reportMessageDelivery', (global, actions, payload): ActionRetu
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('openPreparedInlineMessageModal', async (global, actions, payload): Promise<void> => {
|
||||||
|
const {
|
||||||
|
botId, messageId, webAppKey, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const bot = selectUser(global, botId);
|
||||||
|
if (!bot) return;
|
||||||
|
|
||||||
|
const result = await callApi('fetchPreparedInlineMessage', {
|
||||||
|
bot,
|
||||||
|
id: messageId,
|
||||||
|
});
|
||||||
|
if (!result) {
|
||||||
|
actions.sendWebAppEvent({
|
||||||
|
webAppKey,
|
||||||
|
event: {
|
||||||
|
eventType: 'prepared_message_failed',
|
||||||
|
eventData: { error: 'MESSAGE_EXPIRED' },
|
||||||
|
},
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = updateTabState(global, {
|
||||||
|
preparedMessageModal: {
|
||||||
|
message: result,
|
||||||
|
webAppKey,
|
||||||
|
botId,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('openSharePreparedMessageModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
webAppKey, message, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const supportedFilters = message.peerTypes?.filter((type): type is ApiChatType => type !== 'self');
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = updateTabState(global, {
|
||||||
|
sharePreparedMessageModal: {
|
||||||
|
webAppKey,
|
||||||
|
filter: supportedFilters,
|
||||||
|
message,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
function countSortedIds(ids: number[], from: number, to: number) {
|
function countSortedIds(ids: number[], from: number, to: number) {
|
||||||
// If ids are outside viewport, we cannot get correct count
|
// If ids are outside viewport, we cannot get correct count
|
||||||
if (ids.length === 0 || from < ids[0] || to > ids[ids.length - 1]) return undefined;
|
if (ids.length === 0 || from < ids[0] || to > ids[ids.length - 1]) return undefined;
|
||||||
|
|||||||
@ -1077,3 +1077,19 @@ addActionHandler('closeAboutAdsModal', (global, actions, payload): ActionReturnT
|
|||||||
aboutAdsModal: undefined,
|
aboutAdsModal: undefined,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('closePreparedInlineMessageModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
preparedMessageModal: undefined,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('closeSharePreparedMessageModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
sharePreparedMessageModal: undefined,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|||||||
@ -5,7 +5,9 @@ import type {
|
|||||||
ApiChatFolder,
|
ApiChatFolder,
|
||||||
ApiChatFullInfo,
|
ApiChatFullInfo,
|
||||||
ApiChatInviteInfo,
|
ApiChatInviteInfo,
|
||||||
|
ApiMessage,
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
|
ApiPreparedInlineMessage,
|
||||||
ApiTopic,
|
ApiTopic,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
@ -22,6 +24,7 @@ import {
|
|||||||
VERIFICATION_CODES_USER_ID,
|
VERIFICATION_CODES_USER_ID,
|
||||||
} from '../../config';
|
} from '../../config';
|
||||||
import { formatDateToString, formatTime } from '../../util/dates/dateFormat';
|
import { formatDateToString, formatTime } from '../../util/dates/dateFormat';
|
||||||
|
import { getServerTime } from '../../util/serverTime';
|
||||||
import { getGlobal } from '..';
|
import { getGlobal } from '..';
|
||||||
import { isSystemBot } from './bots';
|
import { isSystemBot } from './bots';
|
||||||
import { getMainUsername, getUserFirstOrLastName } from './users';
|
import { getMainUsername, getUserFirstOrLastName } from './users';
|
||||||
@ -474,3 +477,19 @@ export function getCustomPeerFromInvite(invite: ApiChatInviteInfo): CustomPeer {
|
|||||||
fakeType: isFake ? 'fake' : isScam ? 'scam' : undefined,
|
fakeType: isFake ? 'fake' : isScam ? 'scam' : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMockPreparedMessageFromResult(botId: string, preparedMessage: ApiPreparedInlineMessage) {
|
||||||
|
const { result } = preparedMessage;
|
||||||
|
|
||||||
|
const inlineButtons = result?.sendMessage?.replyMarkup?.inlineButtons;
|
||||||
|
|
||||||
|
return {
|
||||||
|
chatId: botId,
|
||||||
|
content: result.sendMessage.content,
|
||||||
|
date: getServerTime(),
|
||||||
|
id: 0,
|
||||||
|
isOutgoing: true,
|
||||||
|
viaBotId: botId,
|
||||||
|
inlineButtons,
|
||||||
|
} satisfies ApiMessage;
|
||||||
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import type {
|
|||||||
ApiPaymentStatus,
|
ApiPaymentStatus,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
ApiPremiumSection,
|
ApiPremiumSection,
|
||||||
|
ApiPreparedInlineMessage,
|
||||||
ApiPrivacyKey,
|
ApiPrivacyKey,
|
||||||
ApiPrivacySettings,
|
ApiPrivacySettings,
|
||||||
ApiReaction,
|
ApiReaction,
|
||||||
@ -528,6 +529,17 @@ export interface ActionPayloads {
|
|||||||
chatId: string;
|
chatId: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeAboutAdsModal: WithTabId | undefined;
|
closeAboutAdsModal: WithTabId | undefined;
|
||||||
|
openPreparedInlineMessageModal: {
|
||||||
|
botId: string;
|
||||||
|
messageId: string;
|
||||||
|
webAppKey: string;
|
||||||
|
} & WithTabId;
|
||||||
|
closePreparedInlineMessageModal: WithTabId | undefined;
|
||||||
|
openSharePreparedMessageModal: {
|
||||||
|
webAppKey: string;
|
||||||
|
message: ApiPreparedInlineMessage;
|
||||||
|
} & WithTabId;
|
||||||
|
closeSharePreparedMessageModal: WithTabId | undefined;
|
||||||
openPreviousReportAdModal: WithTabId | undefined;
|
openPreviousReportAdModal: WithTabId | undefined;
|
||||||
openPreviousReportModal: WithTabId | undefined;
|
openPreviousReportModal: WithTabId | undefined;
|
||||||
closeReportAdModal: WithTabId | undefined;
|
closeReportAdModal: WithTabId | undefined;
|
||||||
@ -1872,7 +1884,8 @@ export interface ActionPayloads {
|
|||||||
sendInlineBotResult: {
|
sendInlineBotResult: {
|
||||||
id: string;
|
id: string;
|
||||||
queryId: string;
|
queryId: string;
|
||||||
messageList: MessageList;
|
chatId: string;
|
||||||
|
threadId: ThreadId;
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
scheduledAt?: number;
|
scheduledAt?: number;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import type {
|
|||||||
ApiPremiumGiftCodeOption,
|
ApiPremiumGiftCodeOption,
|
||||||
ApiPremiumPromo,
|
ApiPremiumPromo,
|
||||||
ApiPremiumSection,
|
ApiPremiumSection,
|
||||||
|
ApiPreparedInlineMessage,
|
||||||
ApiReactionWithPaid,
|
ApiReactionWithPaid,
|
||||||
ApiReceiptRegular,
|
ApiReceiptRegular,
|
||||||
ApiSavedGifts,
|
ApiSavedGifts,
|
||||||
@ -499,6 +500,18 @@ export type TabState = {
|
|||||||
isQuiz?: boolean;
|
isQuiz?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
preparedMessageModal?: {
|
||||||
|
message: ApiPreparedInlineMessage;
|
||||||
|
webAppKey: string;
|
||||||
|
botId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
sharePreparedMessageModal?: {
|
||||||
|
webAppKey: string;
|
||||||
|
message: ApiPreparedInlineMessage;
|
||||||
|
filter: ApiChatType[];
|
||||||
|
};
|
||||||
|
|
||||||
webApps: {
|
webApps: {
|
||||||
activeWebAppKey?: string;
|
activeWebAppKey?: string;
|
||||||
openedOrderedKeys: string[];
|
openedOrderedKeys: string[];
|
||||||
|
|||||||
@ -1624,6 +1624,7 @@ messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool;
|
|||||||
messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool;
|
messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool;
|
||||||
messages.reportSponsoredMessage#1af3dbb8 peer:InputPeer random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
|
messages.reportSponsoredMessage#1af3dbb8 peer:InputPeer random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
|
||||||
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
|
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
|
||||||
|
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
|
||||||
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
||||||
|
|||||||
@ -219,6 +219,7 @@
|
|||||||
"messages.reportSponsoredMessage",
|
"messages.reportSponsoredMessage",
|
||||||
"messages.getSponsoredMessages",
|
"messages.getSponsoredMessages",
|
||||||
"messages.reportMessagesDelivery",
|
"messages.reportMessagesDelivery",
|
||||||
|
"messages.getPreparedInlineMessage",
|
||||||
"updates.getState",
|
"updates.getState",
|
||||||
"updates.getDifference",
|
"updates.getDifference",
|
||||||
"updates.getChannelDifference",
|
"updates.getChannelDifference",
|
||||||
|
|||||||
8
src/types/language.d.ts
vendored
8
src/types/language.d.ts
vendored
@ -483,6 +483,8 @@ export interface LangPair {
|
|||||||
'SetAdditionalPasswordInfo': undefined;
|
'SetAdditionalPasswordInfo': undefined;
|
||||||
'EditAdminTransferSetPassword': undefined;
|
'EditAdminTransferSetPassword': undefined;
|
||||||
'BotOpenPageTitle': undefined;
|
'BotOpenPageTitle': undefined;
|
||||||
|
'BotShareMessageShare': undefined;
|
||||||
|
'BotShareMessage': undefined;
|
||||||
'FilterDeleteAlert': undefined;
|
'FilterDeleteAlert': undefined;
|
||||||
'RequestToJoinChannelSentDescription': undefined;
|
'RequestToJoinChannelSentDescription': undefined;
|
||||||
'RequestToJoinGroupSentDescription': undefined;
|
'RequestToJoinGroupSentDescription': undefined;
|
||||||
@ -1547,6 +1549,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
|||||||
'ConversationOpenBotLinkAllowMessages': {
|
'ConversationOpenBotLinkAllowMessages': {
|
||||||
'bot': V;
|
'bot': V;
|
||||||
};
|
};
|
||||||
|
'WebAppShareMessageInfo': {
|
||||||
|
'user': V;
|
||||||
|
};
|
||||||
'BlockUserTitle': {
|
'BlockUserTitle': {
|
||||||
'user': V;
|
'user': V;
|
||||||
};
|
};
|
||||||
@ -2216,6 +2221,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
|||||||
'UniqueStatusWearTitle': {
|
'UniqueStatusWearTitle': {
|
||||||
'gift': V;
|
'gift': V;
|
||||||
};
|
};
|
||||||
|
'BotSharedToOne': {
|
||||||
|
'peer': V;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LangPairPlural {
|
export interface LangPairPlural {
|
||||||
|
|||||||
@ -137,6 +137,9 @@ export type WebAppInboundEvent =
|
|||||||
url: string;
|
url: string;
|
||||||
file_name: string;
|
file_name: string;
|
||||||
}> |
|
}> |
|
||||||
|
WebAppEvent<'web_app_send_prepared_message', {
|
||||||
|
id: string;
|
||||||
|
}> |
|
||||||
WebAppEvent<'web_app_request_viewport' | 'web_app_request_theme' | 'web_app_ready' | 'web_app_expand'
|
WebAppEvent<'web_app_request_viewport' | 'web_app_request_theme' | 'web_app_ready' | 'web_app_expand'
|
||||||
| 'web_app_request_phone' | 'web_app_close' | 'web_app_close_scan_qr_popup'
|
| 'web_app_request_phone' | 'web_app_close' | 'web_app_close_scan_qr_popup'
|
||||||
| 'web_app_request_write_access' | 'iframe_will_reload'
|
| 'web_app_request_write_access' | 'iframe_will_reload'
|
||||||
@ -259,6 +262,10 @@ export type WebAppOutboundEvent =
|
|||||||
WebAppEvent<'file_download_requested', {
|
WebAppEvent<'file_download_requested', {
|
||||||
status: 'cancelled' | 'downloading';
|
status: 'cancelled' | 'downloading';
|
||||||
}> |
|
}> |
|
||||||
|
WebAppEvent<'prepared_message_failed', {
|
||||||
|
error: 'UNSUPPORTED' | 'MESSAGE_EXPIRED' | 'MESSAGE_SEND_FAILED'
|
||||||
|
| 'USER_DECLINED' | 'UNKNOWN_ERROR';
|
||||||
|
}> |
|
||||||
WebAppEvent<'main_button_pressed' |
|
WebAppEvent<'main_button_pressed' |
|
||||||
'secondary_button_pressed' | 'back_button_pressed' | 'settings_button_pressed' | 'scan_qr_popup_closed'
|
'secondary_button_pressed' | 'back_button_pressed' | 'settings_button_pressed' | 'scan_qr_popup_closed'
|
||||||
| 'reload_iframe' | 'emoji_status_set', null>;
|
| 'reload_iframe' | 'prepared_message_sent' | 'emoji_status_set', null>;
|
||||||
|
|||||||
@ -231,6 +231,11 @@ export function formatShareText(url?: string, text?: string, title?: string): Ap
|
|||||||
|
|
||||||
function parseChooseParameter(choose?: string) {
|
function parseChooseParameter(choose?: string) {
|
||||||
if (!choose) return undefined;
|
if (!choose) return undefined;
|
||||||
const types = choose.toLowerCase().split(' ');
|
const types = choose.toLowerCase().split(' ').flatMap((type) => {
|
||||||
|
if (type === 'groups') {
|
||||||
|
return ['chats', 'groups'];
|
||||||
|
}
|
||||||
|
return [type];
|
||||||
|
});
|
||||||
return types.filter((type): type is ApiChatType => API_CHAT_TYPES.includes(type as ApiChatType));
|
return types.filter((type): type is ApiChatType => API_CHAT_TYPES.includes(type as ApiChatType));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user