Support Bot Apps (#3041)
This commit is contained in:
parent
ac28fe0162
commit
920b046d2e
@ -3,11 +3,13 @@ import type {
|
||||
ApiAttachBot,
|
||||
ApiAttachBotIcon,
|
||||
ApiAttachMenuPeerType,
|
||||
ApiBotApp,
|
||||
ApiBotCommand,
|
||||
ApiBotInfo,
|
||||
ApiBotInlineMediaResult,
|
||||
ApiBotInlineResult,
|
||||
ApiBotInlineSwitchPm,
|
||||
ApiBotInlineSwitchWebview,
|
||||
ApiBotMenuButton,
|
||||
ApiInlineResultType,
|
||||
} from '../../types';
|
||||
@ -62,6 +64,10 @@ export function buildBotSwitchPm(switchPm?: GramJs.InlineBotSwitchPM) {
|
||||
return switchPm ? pick(switchPm, ['text', 'startParam']) as ApiBotInlineSwitchPm : undefined;
|
||||
}
|
||||
|
||||
export function buildBotSwitchWebview(switchWebview?: GramJs.InlineBotWebView) {
|
||||
return switchWebview ? pick(switchWebview, ['text', 'url']) as ApiBotInlineSwitchWebview : undefined;
|
||||
}
|
||||
|
||||
export function buildApiAttachBot(bot: GramJs.AttachMenuBot): ApiAttachBot {
|
||||
return {
|
||||
id: bot.botId.toString(),
|
||||
@ -138,3 +144,26 @@ export function buildApiBotMenuButton(menuButton?: GramJs.TypeBotMenuButton): Ap
|
||||
type: 'commands',
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiBotApp(botApp: GramJs.messages.BotApp): ApiBotApp | undefined {
|
||||
const { app, inactive, requestWriteAccess } = botApp;
|
||||
if (app instanceof GramJs.BotAppNotModified) return undefined;
|
||||
const {
|
||||
id, accessHash, title, description, shortName, photo, document,
|
||||
} = app;
|
||||
|
||||
const apiPhoto = photo instanceof GramJs.Photo ? buildApiPhoto(photo) : undefined;
|
||||
const apiDocument = document instanceof GramJs.Document ? buildApiDocument(document) : undefined;
|
||||
|
||||
return {
|
||||
id: id.toString(),
|
||||
accessHash: accessHash.toString(),
|
||||
title,
|
||||
description,
|
||||
shortName,
|
||||
photo: apiPhoto,
|
||||
document: apiDocument,
|
||||
isInactive: inactive,
|
||||
shouldRequestWriteAccess: requestWriteAccess,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1145,6 +1145,15 @@ function buildReplyButtons(message: UniversalMessage, shouldSkipBuyButton?: bool
|
||||
}]],
|
||||
};
|
||||
}
|
||||
if (media.webpage.type === 'telegram_botapp') {
|
||||
return {
|
||||
inlineButtons: [[{
|
||||
type: 'url',
|
||||
text: 'Open App',
|
||||
url: media.webpage.url,
|
||||
}]],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@ -23,6 +23,7 @@ import type {
|
||||
ApiChatReactions,
|
||||
ApiReaction,
|
||||
ApiFormattedText,
|
||||
ApiBotApp,
|
||||
} from '../../types';
|
||||
import {
|
||||
ApiMessageEntityTypes,
|
||||
@ -606,3 +607,10 @@ export function buildInputTextWithEntities(formatted: ApiFormattedText) {
|
||||
entities: formatted.entities?.map(buildMtpMessageEntity) || [],
|
||||
});
|
||||
}
|
||||
|
||||
export function buildInputBotApp(app: ApiBotApp) {
|
||||
return new GramJs.InputBotAppID({
|
||||
id: BigInt(app.id),
|
||||
accessHash: BigInt(app.accessHash),
|
||||
});
|
||||
}
|
||||
|
||||
@ -2,15 +2,24 @@ import BigInt from 'big-integer';
|
||||
import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import type {
|
||||
ApiBotApp,
|
||||
ApiChat, ApiThemeParameters, ApiUser, OnApiUpdate,
|
||||
} from '../../types';
|
||||
|
||||
import localDb from '../localDb';
|
||||
import { WEB_APP_PLATFORM } from '../../../config';
|
||||
import { invokeRequest } from './client';
|
||||
import { buildInputPeer, buildInputThemeParams, generateRandomBigInt } from '../gramjsBuilders';
|
||||
import {
|
||||
buildInputBotApp, buildInputEntity, buildInputPeer, buildInputThemeParams, generateRandomBigInt,
|
||||
} from '../gramjsBuilders';
|
||||
import { buildApiUser } from '../apiBuilders/users';
|
||||
import {
|
||||
buildApiAttachBot, buildApiBotInlineMediaResult, buildApiBotInlineResult, buildBotSwitchPm,
|
||||
buildApiAttachBot,
|
||||
buildApiBotApp,
|
||||
buildApiBotInlineMediaResult,
|
||||
buildApiBotInlineResult,
|
||||
buildBotSwitchPm,
|
||||
buildBotSwitchWebview,
|
||||
} from '../apiBuilders/bots';
|
||||
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||
import { addEntitiesWithPhotosToLocalDb, addUserToLocalDb, deserializeBytes } from '../helpers';
|
||||
@ -102,6 +111,7 @@ export async function fetchInlineBotResults({
|
||||
help: bot.botPlaceholder,
|
||||
nextOffset: getInlineBotResultsNextOffset(bot.usernames![0].username, result.nextOffset),
|
||||
switchPm: buildBotSwitchPm(result.switchPm),
|
||||
switchWebview: buildBotSwitchWebview(result.switchWebview),
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
results: processInlineBotResult(String(result.queryId), result.results),
|
||||
cacheTime: result.cacheTime,
|
||||
@ -184,7 +194,7 @@ export async function requestWebView({
|
||||
startParam,
|
||||
themeParams: theme ? buildInputThemeParams(theme) : undefined,
|
||||
fromBotMenu: isFromBotMenu || undefined,
|
||||
platform: 'weba',
|
||||
platform: WEB_APP_PLATFORM,
|
||||
...(threadId && { topMsgId: threadId }),
|
||||
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||
}));
|
||||
@ -210,7 +220,53 @@ export async function requestSimpleWebView({
|
||||
url,
|
||||
bot: buildInputPeer(bot.id, bot.accessHash),
|
||||
themeParams: theme ? buildInputThemeParams(theme) : undefined,
|
||||
platform: 'weba',
|
||||
platform: WEB_APP_PLATFORM,
|
||||
}));
|
||||
|
||||
return result?.url;
|
||||
}
|
||||
|
||||
export async function fetchBotApp({
|
||||
bot,
|
||||
appName,
|
||||
}: {
|
||||
bot: ApiUser;
|
||||
appName: string;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.GetBotApp({
|
||||
app: new GramJs.InputBotAppShortName({
|
||||
botId: buildInputEntity(bot.id, bot.accessHash) as GramJs.InputUser,
|
||||
shortName: appName,
|
||||
}),
|
||||
}));
|
||||
|
||||
if (!result || result instanceof GramJs.BotAppNotModified) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return buildApiBotApp(result);
|
||||
}
|
||||
|
||||
export async function requestAppWebView({
|
||||
peer,
|
||||
app,
|
||||
startParam,
|
||||
theme,
|
||||
isWriteAllowed,
|
||||
}: {
|
||||
peer: ApiChat | ApiUser;
|
||||
app: ApiBotApp;
|
||||
startParam?: string;
|
||||
theme?: ApiThemeParameters;
|
||||
isWriteAllowed?: boolean;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.RequestAppWebView({
|
||||
peer: buildInputPeer(peer.id, peer.accessHash),
|
||||
app: buildInputBotApp(app),
|
||||
startParam,
|
||||
themeParams: theme ? buildInputThemeParams(theme) : undefined,
|
||||
platform: WEB_APP_PLATFORM,
|
||||
writeAllowed: isWriteAllowed || undefined,
|
||||
}));
|
||||
|
||||
return result?.url;
|
||||
|
||||
@ -71,8 +71,8 @@ export {
|
||||
|
||||
export {
|
||||
answerCallbackButton, fetchTopInlineBots, fetchInlineBot, fetchInlineBotResults, sendInlineBotResult, startBot,
|
||||
requestWebView, requestSimpleWebView, sendWebViewData, prolongWebView, loadAttachBots, toggleAttachBot,
|
||||
requestBotUrlAuth, requestLinkUrlAuth, acceptBotUrlAuth, acceptLinkUrlAuth, loadAttachBot,
|
||||
requestWebView, requestSimpleWebView, sendWebViewData, prolongWebView, loadAttachBots, toggleAttachBot, fetchBotApp,
|
||||
requestBotUrlAuth, requestLinkUrlAuth, acceptBotUrlAuth, acceptLinkUrlAuth, loadAttachBot, requestAppWebView,
|
||||
} from './bots';
|
||||
|
||||
export {
|
||||
|
||||
@ -43,6 +43,11 @@ export interface ApiBotInlineSwitchPm {
|
||||
startParam: string;
|
||||
}
|
||||
|
||||
export interface ApiBotInlineSwitchWebview {
|
||||
text: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface ApiBotCommand {
|
||||
botId: string;
|
||||
command: string;
|
||||
|
||||
@ -637,6 +637,18 @@ export type ApiThemeParameters = {
|
||||
secondary_bg_color: string;
|
||||
};
|
||||
|
||||
export type ApiBotApp = {
|
||||
id: string;
|
||||
accessHash: string;
|
||||
title: string;
|
||||
shortName: string;
|
||||
description: string;
|
||||
photo?: ApiPhoto;
|
||||
document?: ApiDocument;
|
||||
isInactive?: boolean;
|
||||
shouldRequestWriteAccess?: boolean;
|
||||
};
|
||||
|
||||
export const MAIN_THREAD_ID = -1;
|
||||
|
||||
// `Symbol` can not be transferred from worker
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { memo, useCallback } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useMemo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type { ApiUser } from '../../api/types';
|
||||
|
||||
import { getUserFullName } from '../../global/helpers';
|
||||
@ -10,15 +12,20 @@ import renderText from '../common/helpers/renderText';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
|
||||
import Checkbox from '../ui/Checkbox';
|
||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||
|
||||
export type OwnProps = {
|
||||
bot?: ApiUser;
|
||||
type?: 'game' | 'webApp';
|
||||
type?: 'game' | 'webApp' | 'botApp';
|
||||
shouldRequestWriteAccess?: boolean;
|
||||
};
|
||||
|
||||
const BotTrustModal: FC<OwnProps> = ({ bot, type }) => {
|
||||
const BotTrustModal: FC<OwnProps> = ({ bot, type, shouldRequestWriteAccess }) => {
|
||||
const { cancelBotTrustRequest, markBotTrusted } = getActions();
|
||||
|
||||
const [isWriteAllowed, setIsWriteAllowed] = useState(shouldRequestWriteAccess || false);
|
||||
|
||||
const lang = useLang();
|
||||
// Keep props a little bit longer, to show correct text on closing animation
|
||||
const previousBot = usePrevious(bot, false);
|
||||
@ -27,21 +34,46 @@ const BotTrustModal: FC<OwnProps> = ({ bot, type }) => {
|
||||
const currentType = type || previousType;
|
||||
|
||||
const handleBotTrustAccept = useCallback(() => {
|
||||
markBotTrusted({ botId: bot!.id });
|
||||
}, [markBotTrusted, bot]);
|
||||
markBotTrusted({ botId: bot!.id, isWriteAllowed });
|
||||
}, [markBotTrusted, isWriteAllowed, bot]);
|
||||
|
||||
const handleBotTrustDecline = useCallback(() => {
|
||||
cancelBotTrustRequest();
|
||||
}, []);
|
||||
|
||||
const title = currentType === 'game' ? lang('AppName') : lang('BotOpenPageTitle');
|
||||
const text = currentType === 'game' ? lang('BotPermissionGameAlert', getUserFullName(currentBot))
|
||||
: lang('BotOpenPageMessage', getUserFullName(currentBot));
|
||||
const text = useMemo(() => {
|
||||
switch (currentType) {
|
||||
case 'game':
|
||||
return lang('BotPermissionGameAlert', getUserFullName(currentBot));
|
||||
case 'webApp':
|
||||
return lang('BotOpenPageMessage', getUserFullName(currentBot));
|
||||
case 'botApp':
|
||||
default:
|
||||
return lang('BotWebViewStartPermission');
|
||||
}
|
||||
}, [currentBot, currentType, lang]);
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
isOpen={Boolean(bot)}
|
||||
onClose={cancelBotTrustRequest}
|
||||
confirmHandler={handleBotTrustAccept}
|
||||
onClose={handleBotTrustDecline}
|
||||
title={title}
|
||||
textParts={renderText(text, ['br', 'simple_markdown', 'emoji'])}
|
||||
/>
|
||||
confirmHandler={handleBotTrustAccept}
|
||||
>
|
||||
{text}
|
||||
{shouldRequestWriteAccess && (
|
||||
<Checkbox
|
||||
className="dialog-checkbox"
|
||||
checked={isWriteAllowed}
|
||||
label={renderText(
|
||||
lang('WebApp.AddToAttachmentAllowMessages', currentBot?.firstName),
|
||||
['simple_markdown'],
|
||||
)}
|
||||
onCheck={setIsWriteAllowed}
|
||||
/>
|
||||
)}
|
||||
</ConfirmDialog>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -54,6 +54,7 @@ const DraftRecipientPicker: FC<OwnProps> = ({
|
||||
<RecipientPicker
|
||||
isOpen={isOpen}
|
||||
searchPlaceholder={lang('ForwardTo')}
|
||||
filter={requestedDraft?.filter}
|
||||
onSelectRecipient={handleSelectRecipient}
|
||||
onClose={handleClose}
|
||||
onCloseAnimationEnd={unmarkIsShown}
|
||||
|
||||
@ -511,7 +511,11 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
<PhoneCall isActive={isPhoneCallActive} />
|
||||
<UnreadCount isForAppBadge />
|
||||
<RatePhoneCallModal isOpen={isRatePhoneCallModalOpen} />
|
||||
<BotTrustModal bot={botTrustRequestBot} type={botTrustRequest?.type} />
|
||||
<BotTrustModal
|
||||
bot={botTrustRequestBot}
|
||||
type={botTrustRequest?.type}
|
||||
shouldRequestWriteAccess={botTrustRequest?.shouldRequestWriteAccess}
|
||||
/>
|
||||
<AttachBotInstallModal bot={attachBotToInstall} />
|
||||
<AttachBotRecipientPicker requestedAttachBotInChat={requestedAttachBotInChat} />
|
||||
<MessageListHistoryHandler />
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { extractCurrentThemeParams, validateHexColor } from '../../util/themeStyle';
|
||||
import { convertToApiChatType } from '../../global/helpers';
|
||||
|
||||
import useInterval from '../../hooks/useInterval';
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -94,19 +95,26 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
openInvoice,
|
||||
setWebAppPaymentSlug,
|
||||
showNotification,
|
||||
switchBotInline,
|
||||
} = getActions();
|
||||
const [mainButton, setMainButton] = useState<WebAppButton | undefined>();
|
||||
const [isBackButtonVisible, setIsBackButtonVisible] = useState(false);
|
||||
const [backgroundColor, setBackgroundColor] = useState(extractCurrentThemeParams().bg_color);
|
||||
const [headerColor, setHeaderColor] = useState(extractCurrentThemeParams().bg_color);
|
||||
const [backgroundColor, setBackgroundColor] = useState<string>();
|
||||
const [headerColor, setHeaderColor] = useState<string>();
|
||||
const [confirmClose, setConfirmClose] = useState(false);
|
||||
const [isCloseModalOpen, openCloseModal, closeCloseModal] = useFlag(false);
|
||||
const [isCloseModalOpen, openCloseModal, closeModal] = useFlag(false);
|
||||
const [isLoaded, markLoaded, markUnloaded] = useFlag(false);
|
||||
const [popupParams, setPopupParams] = useState<PopupOptions | undefined>();
|
||||
const { isMobile } = useAppLayout();
|
||||
const prevPopupParams = usePrevious(popupParams);
|
||||
const renderingPopupParams = popupParams || prevPopupParams;
|
||||
|
||||
useEffect(() => {
|
||||
const themeParams = extractCurrentThemeParams();
|
||||
setBackgroundColor(themeParams.bg_color);
|
||||
setHeaderColor(themeParams.bg_color);
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const frameRef = useRef<HTMLIFrameElement>(null);
|
||||
|
||||
@ -115,7 +123,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
url, buttonText, queryId, replyToMessageId, threadId,
|
||||
} = webApp || {};
|
||||
const isOpen = Boolean(url);
|
||||
const isSimple = !queryId;
|
||||
const isSimple = Boolean(buttonText);
|
||||
|
||||
const handleEvent = useCallback((event: WebAppInboundEvent) => {
|
||||
const { eventType, eventData } = event;
|
||||
@ -198,6 +206,20 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
message: 'Scan QR code is not supported in this client yet',
|
||||
});
|
||||
}
|
||||
|
||||
if (eventType === 'web_app_switch_inline_query') {
|
||||
const filter = eventData.chat_types?.map(convertToApiChatType).filter(Boolean);
|
||||
const isSamePeer = !filter?.length;
|
||||
|
||||
switchBotInline({
|
||||
botId: bot!.id,
|
||||
query: eventData.query,
|
||||
filter,
|
||||
isSamePeer,
|
||||
});
|
||||
|
||||
closeWebApp();
|
||||
}
|
||||
}, [
|
||||
bot, buttonText, closeWebApp, openInvoice, openTelegramLink, sendWebViewData, setWebAppPaymentSlug,
|
||||
isPaymentModalOpen, showNotification,
|
||||
@ -314,11 +336,11 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setConfirmClose(false);
|
||||
closeCloseModal();
|
||||
closeModal();
|
||||
setPopupParams(undefined);
|
||||
markUnloaded();
|
||||
}
|
||||
}, [closeCloseModal, isOpen, markUnloaded]);
|
||||
}, [closeModal, isOpen, markUnloaded]);
|
||||
|
||||
const MoreMenuButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
|
||||
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
||||
@ -460,7 +482,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
{confirmClose && (
|
||||
<ConfirmDialog
|
||||
isOpen={isCloseModalOpen}
|
||||
onClose={closeCloseModal}
|
||||
onClose={closeModal}
|
||||
title={lang('lng_bot_close_warning_title')}
|
||||
text={lang('lng_bot_close_warning')}
|
||||
confirmHandler={closeWebApp}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useRef } from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../lib/teact/teactn';
|
||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||
import useWindowSize from '../../../hooks/useWindowSize';
|
||||
|
||||
@ -83,6 +84,12 @@ export type WebAppInboundEvent = {
|
||||
eventData: {
|
||||
req_id: string;
|
||||
};
|
||||
} | {
|
||||
eventType: 'web_app_switch_inline_query';
|
||||
eventData: {
|
||||
query: string;
|
||||
chat_types: ('users' | 'bots' | 'groups' | 'channels')[];
|
||||
};
|
||||
} | {
|
||||
eventType: 'web_app_request_viewport' | 'web_app_request_theme' | 'web_app_ready' | 'web_app_expand'
|
||||
| 'web_app_request_phone' | 'web_app_close' | 'iframe_ready' | 'web_app_close_scan_qr_popup';
|
||||
@ -171,6 +178,10 @@ const useWebAppFrame = (
|
||||
onEvent: (event: WebAppInboundEvent) => void,
|
||||
onLoad?: () => void,
|
||||
) => {
|
||||
const {
|
||||
showNotification,
|
||||
} = getActions();
|
||||
|
||||
const ignoreEventsRef = useRef<boolean>(false);
|
||||
const lastFrameSizeRef = useRef<{ width: number; height: number; isResizing?: boolean }>();
|
||||
const windowSize = useWindowSize();
|
||||
@ -266,23 +277,24 @@ const useWebAppFrame = (
|
||||
}
|
||||
|
||||
if (data.eventType === 'web_app_read_text_from_clipboard') {
|
||||
const { req_id: requestId } = data.eventData;
|
||||
// eslint-disable-next-line no-null/no-null -- Required by spec
|
||||
window.navigator.clipboard.readText().catch(() => null).then((text) => {
|
||||
sendEvent({
|
||||
eventType: 'clipboard_text_received',
|
||||
eventData: {
|
||||
req_id: requestId,
|
||||
data: text,
|
||||
},
|
||||
});
|
||||
sendEvent({
|
||||
eventType: 'clipboard_text_received',
|
||||
eventData: {
|
||||
req_id: data.eventData.req_id,
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
data: null,
|
||||
},
|
||||
});
|
||||
|
||||
showNotification({
|
||||
message: 'Clipboard access is not supported in this client yet',
|
||||
});
|
||||
}
|
||||
onEvent(data);
|
||||
} catch (err) {
|
||||
// Ignore other messages
|
||||
}
|
||||
}, [isSimpleView, onEvent, sendCustomStyle, sendEvent, sendTheme, sendViewport, onLoad, windowSize.isResizing]);
|
||||
}, [isSimpleView, sendEvent, onEvent, sendCustomStyle, sendTheme, sendViewport, onLoad, windowSize.isResizing]);
|
||||
|
||||
useEffect(() => {
|
||||
const { width, height, isResizing } = windowSize;
|
||||
|
||||
@ -496,6 +496,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
botId: inlineBotId,
|
||||
isGallery: isInlineBotTooltipGallery,
|
||||
switchPm: inlineBotSwitchPm,
|
||||
switchWebview: inlineBotSwitchWebview,
|
||||
results: inlineBotResults,
|
||||
closeTooltip: closeInlineBotTooltip,
|
||||
help: inlineBotHelp,
|
||||
@ -1322,6 +1323,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
isGallery={isInlineBotTooltipGallery}
|
||||
inlineBotResults={inlineBotResults}
|
||||
switchPm={inlineBotSwitchPm}
|
||||
switchWebview={inlineBotSwitchWebview}
|
||||
loadMore={loadMoreForInlineBot}
|
||||
isSavedMessages={isChatWithSelf}
|
||||
canSendGifs={canSendGifs}
|
||||
|
||||
@ -1,17 +1,21 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../global';
|
||||
|
||||
import type { ApiBotInlineMediaResult, ApiBotInlineResult, ApiBotInlineSwitchPm } from '../../../api/types';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type {
|
||||
ApiBotInlineMediaResult, ApiBotInlineResult, ApiBotInlineSwitchPm, ApiBotInlineSwitchWebview,
|
||||
} from '../../../api/types';
|
||||
import { LoadMoreDirection } from '../../../types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
import usePrevious from '../../../hooks/usePrevious';
|
||||
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||
@ -35,6 +39,7 @@ export type OwnProps = {
|
||||
isGallery?: boolean;
|
||||
inlineBotResults?: (ApiBotInlineResult | ApiBotInlineMediaResult)[];
|
||||
switchPm?: ApiBotInlineSwitchPm;
|
||||
switchWebview?: ApiBotInlineSwitchWebview;
|
||||
isSavedMessages?: boolean;
|
||||
canSendGifs?: boolean;
|
||||
onSelectResult: (
|
||||
@ -51,6 +56,7 @@ const InlineBotTooltip: FC<OwnProps> = ({
|
||||
isGallery,
|
||||
inlineBotResults,
|
||||
switchPm,
|
||||
switchWebview,
|
||||
isSavedMessages,
|
||||
canSendGifs,
|
||||
loadMore,
|
||||
@ -61,6 +67,7 @@ const InlineBotTooltip: FC<OwnProps> = ({
|
||||
const {
|
||||
openChat,
|
||||
startBot,
|
||||
requestSimpleWebView,
|
||||
} = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -99,6 +106,17 @@ const InlineBotTooltip: FC<OwnProps> = ({
|
||||
startBot({ botId: botId!, param: switchPm!.startParam });
|
||||
}, [botId, openChat, startBot, switchPm]);
|
||||
|
||||
const handleOpenWebview = useCallback(() => {
|
||||
const theme = extractCurrentThemeParams();
|
||||
|
||||
requestSimpleWebView({
|
||||
botId: botId!,
|
||||
url: switchWebview!.url,
|
||||
buttonText: switchWebview!.text,
|
||||
theme,
|
||||
});
|
||||
}, [botId, switchWebview]);
|
||||
|
||||
const prevInlineBotResults = usePrevious(
|
||||
inlineBotResults?.length
|
||||
? inlineBotResults
|
||||
@ -126,6 +144,14 @@ const InlineBotTooltip: FC<OwnProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
function renderSwitchWebview() {
|
||||
return (
|
||||
<ListItem ripple className="switch-pm scroll-item" onClick={handleOpenWebview}>
|
||||
<span className="title">{switchWebview!.text}</span>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
function renderContent() {
|
||||
return renderedInlineBotResults!.map((inlineBotResult, index) => {
|
||||
switch (inlineBotResult.type) {
|
||||
@ -203,6 +229,7 @@ const InlineBotTooltip: FC<OwnProps> = ({
|
||||
sensitiveArea={160}
|
||||
>
|
||||
{switchPm && renderSwitchPm()}
|
||||
{switchWebview && renderSwitchWebview()}
|
||||
{Boolean(renderedInlineBotResults?.length) && renderContent()}
|
||||
</InfiniteScroll>
|
||||
);
|
||||
|
||||
@ -60,6 +60,7 @@ export default function useInlineBotTooltip(
|
||||
const {
|
||||
id: botId,
|
||||
switchPm,
|
||||
switchWebview,
|
||||
offset,
|
||||
results,
|
||||
isGallery,
|
||||
@ -87,6 +88,7 @@ export default function useInlineBotTooltip(
|
||||
botId,
|
||||
isGallery,
|
||||
switchPm,
|
||||
switchWebview,
|
||||
results,
|
||||
closeTooltip: markManuallyClosed,
|
||||
help: canShowHelp && help ? `@${username} ${help}` : undefined,
|
||||
|
||||
@ -45,7 +45,7 @@ export const CUSTOM_EMOJI_PREVIEW_CACHE_DISABLED = false;
|
||||
export const CUSTOM_EMOJI_PREVIEW_CACHE_NAME = 'tt-custom-emoji-preview';
|
||||
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
|
||||
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
|
||||
export const LANG_CACHE_NAME = 'tt-lang-packs-v17';
|
||||
export const LANG_CACHE_NAME = 'tt-lang-packs-v18';
|
||||
export const ASSET_CACHE_NAME = 'tt-assets';
|
||||
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
|
||||
export const DATA_BROADCAST_CHANNEL_NAME = 'tt-global';
|
||||
@ -233,7 +233,7 @@ export const SUPPORTED_TRANSLATION_LANGUAGES = [
|
||||
// Official
|
||||
'en', 'ar', 'be', 'ca', 'zh', 'nl', 'fr', 'de', 'id',
|
||||
'it', 'ja', 'ko', 'pl', 'pt', 'ru', 'es', 'uk',
|
||||
// Unnofficial
|
||||
// Unofficial
|
||||
'af', 'sq', 'am', 'hy', 'az', 'eu', 'bn', 'bs', 'bg',
|
||||
'ceb', 'zh-CN', 'zh-TW', 'co', 'hr', 'cs', 'da', 'eo',
|
||||
'et', 'fi', 'fy', 'gl', 'ka', 'el', 'gu', 'ht', 'ha',
|
||||
@ -257,6 +257,7 @@ export const TME_LINK_PREFIX = 'https://t.me/';
|
||||
export const USERNAME_PURCHASE_ERROR = 'USERNAME_PURCHASE_AVAILABLE';
|
||||
export const PURCHASE_USERNAME = 'auction';
|
||||
export const TME_WEB_DOMAINS = new Set(['t.me', 'web.t.me', 'a.t.me', 'k.t.me', 'z.t.me']);
|
||||
export const WEB_APP_PLATFORM = 'weba';
|
||||
|
||||
// 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']);
|
||||
|
||||
@ -24,6 +24,7 @@ import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||
import PopupManager from '../../../util/PopupManager';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
import { translate } from '../../../util/langProvider';
|
||||
|
||||
const GAMEE_URL = 'https://prizes.gamee.com/';
|
||||
const TOP_PEERS_REQUEST_COOLDOWN = 60; // 1 min
|
||||
@ -290,18 +291,29 @@ addActionHandler('queryInlineBot', async (global, actions, payload): Promise<voi
|
||||
|
||||
addActionHandler('switchBotInline', (global, actions, payload): ActionReturnType => {
|
||||
const {
|
||||
query, isSamePeer, messageId, tabId = getCurrentTabId(),
|
||||
query, isSamePeer, messageId, filter, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
let {
|
||||
botId,
|
||||
} = payload;
|
||||
const chat = selectCurrentChat(global, tabId);
|
||||
if (!chat) {
|
||||
return undefined;
|
||||
}
|
||||
const message = selectChatMessage(global, chat.id, messageId);
|
||||
if (!message) {
|
||||
|
||||
if (!botId && messageId) {
|
||||
const message = selectChatMessage(global, chat.id, messageId);
|
||||
if (!message) {
|
||||
return undefined;
|
||||
}
|
||||
botId = message.viaBotId || message.senderId;
|
||||
}
|
||||
|
||||
if (!botId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const botSender = selectUser(global, message.viaBotId || message.senderId!);
|
||||
const botSender = selectUser(global, botId);
|
||||
if (!botSender) {
|
||||
return undefined;
|
||||
}
|
||||
@ -309,6 +321,7 @@ addActionHandler('switchBotInline', (global, actions, payload): ActionReturnType
|
||||
actions.openChatWithDraft({
|
||||
text: `@${botSender.usernames![0].username} ${query}`,
|
||||
chatId: isSamePeer ? chat.id : undefined,
|
||||
filter,
|
||||
tabId,
|
||||
});
|
||||
return undefined;
|
||||
@ -513,6 +526,65 @@ addActionHandler('requestWebView', async (global, actions, payload): Promise<voi
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('requestAppWebView', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
botId, appName, startApp, theme, isWriteAllowed,
|
||||
tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
const bot = selectUser(global, botId);
|
||||
if (!bot) return;
|
||||
|
||||
const botApp = await callApi('fetchBotApp', {
|
||||
bot,
|
||||
appName,
|
||||
});
|
||||
global = getGlobal();
|
||||
|
||||
if (!botApp) {
|
||||
actions.showNotification({ message: translate('lng_username_app_not_found'), tabId });
|
||||
return;
|
||||
}
|
||||
|
||||
if (botApp.isInactive && !selectIsTrustedBot(global, botId)) {
|
||||
global = updateTabState(global, {
|
||||
botTrustRequest: {
|
||||
botId,
|
||||
shouldRequestWriteAccess: botApp.shouldRequestWriteAccess,
|
||||
type: 'botApp',
|
||||
onConfirm: {
|
||||
action: 'requestAppWebView',
|
||||
payload,
|
||||
},
|
||||
},
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
return;
|
||||
}
|
||||
|
||||
const peer = selectCurrentChat(global, tabId);
|
||||
|
||||
const url = await callApi('requestAppWebView', {
|
||||
peer: peer || bot,
|
||||
app: botApp,
|
||||
startParam: startApp,
|
||||
isWriteAllowed,
|
||||
theme,
|
||||
});
|
||||
global = getGlobal();
|
||||
|
||||
if (!url) return;
|
||||
|
||||
global = updateTabState(global, {
|
||||
webApp: {
|
||||
url,
|
||||
botId,
|
||||
buttonText: '',
|
||||
},
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('prolongWebView', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
botId, peerId, isSilent, replyToMessageId, queryId, threadId,
|
||||
@ -582,7 +654,7 @@ addActionHandler('cancelBotTrustRequest', (global, actions, payload): ActionRetu
|
||||
});
|
||||
|
||||
addActionHandler('markBotTrusted', (global, actions, payload): ActionReturnType => {
|
||||
const { botId, tabId = getCurrentTabId() } = payload;
|
||||
const { botId, isWriteAllowed, tabId = getCurrentTabId() } = payload;
|
||||
const { trustedBotIds } = global;
|
||||
|
||||
const newTrustedBotIds = new Set(trustedBotIds);
|
||||
@ -597,7 +669,10 @@ addActionHandler('markBotTrusted', (global, actions, payload): ActionReturnType
|
||||
if (tabState.botTrustRequest?.onConfirm) {
|
||||
const { action, payload: callbackPayload } = tabState.botTrustRequest.onConfirm;
|
||||
// @ts-ignore
|
||||
actions[action](callbackPayload);
|
||||
actions[action]({
|
||||
...(callbackPayload as {}),
|
||||
isWriteAllowed,
|
||||
});
|
||||
}
|
||||
|
||||
global = updateTabState(global, {
|
||||
@ -921,6 +996,7 @@ async function searchInlineBot<T extends GlobalState>(global: T, {
|
||||
cacheTime: Date.now() + result.cacheTime * 1000,
|
||||
...(newResults.length && { isGallery: result.isGallery }),
|
||||
...(result.switchPm && { switchPm: result.switchPm }),
|
||||
...(result.switchWebview && { switchWebview: result.switchWebview }),
|
||||
canLoadMore: result.results.length > 0 && Boolean(result.nextOffset),
|
||||
results: newInlineBotData.offset === '' || newInlineBotData.offset === result.nextOffset
|
||||
? result.results
|
||||
|
||||
@ -77,6 +77,7 @@ import * as langProvider from '../../../util/langProvider';
|
||||
import { selectCurrentLimit } from '../../selectors/limits';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||
|
||||
const TOP_CHAT_MESSAGES_PRELOAD_INTERVAL = 100;
|
||||
const INFINITE_LOOP_MARKER = 100;
|
||||
@ -1005,6 +1006,8 @@ addActionHandler('openTelegramLink', (global, actions, payload): ActionReturnTyp
|
||||
startParam: params.start,
|
||||
startAttach,
|
||||
attach: params.attach,
|
||||
startApp: params.startapp,
|
||||
originalParts: [part1, part2, part3],
|
||||
tabId,
|
||||
});
|
||||
}
|
||||
@ -1022,11 +1025,13 @@ addActionHandler('acceptInviteConfirmation', async (global, actions, payload): P
|
||||
|
||||
addActionHandler('openChatByUsername', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
username, messageId, commentId, startParam, startAttach, attach, threadId,
|
||||
username, messageId, commentId, startParam, startAttach, attach, threadId, originalParts, startApp,
|
||||
tabId = getCurrentTabId(),
|
||||
} = payload!;
|
||||
|
||||
const chat = selectCurrentChat(global, tabId);
|
||||
const webAppName = originalParts?.[1];
|
||||
const isWebApp = webAppName && !Number(webAppName);
|
||||
|
||||
if (!commentId) {
|
||||
if (!startAttach && messageId && !startParam && chat?.usernames?.some((c) => c.username === username)) {
|
||||
@ -1035,13 +1040,15 @@ addActionHandler('openChatByUsername', async (global, actions, payload): Promise
|
||||
});
|
||||
return;
|
||||
}
|
||||
await openChatByUsername(global, actions, username, threadId, messageId, startParam, startAttach, attach, tabId);
|
||||
return;
|
||||
if (!isWebApp) {
|
||||
await openChatByUsername(global, actions, username, threadId, messageId, startParam, startAttach, attach, tabId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const { chatId, type } = selectCurrentMessageList(global, tabId) || {};
|
||||
const usernameChat = selectChatByUsername(global, username);
|
||||
if (chatId && messageId && usernameChat && type === 'thread') {
|
||||
if (chatId && commentId && messageId && usernameChat && type === 'thread') {
|
||||
const threadInfo = selectThreadInfo(global, chatId, messageId);
|
||||
|
||||
if (threadInfo && threadInfo.chatId === chatId) {
|
||||
@ -1055,9 +1062,7 @@ addActionHandler('openChatByUsername', async (global, actions, payload): Promise
|
||||
}
|
||||
}
|
||||
|
||||
if (!messageId) return;
|
||||
|
||||
actions.openChat({ id: TMP_CHAT_ID, tabId });
|
||||
if (!isWebApp) actions.openChat({ id: TMP_CHAT_ID, tabId });
|
||||
|
||||
const chatByUsername = await fetchChatByUsername(global, username);
|
||||
|
||||
@ -1065,6 +1070,21 @@ addActionHandler('openChatByUsername', async (global, actions, payload): Promise
|
||||
|
||||
global = getGlobal();
|
||||
|
||||
if (isWebApp && chatByUsername) {
|
||||
const theme = extractCurrentThemeParams();
|
||||
|
||||
actions.requestAppWebView({
|
||||
appName: webAppName,
|
||||
botId: chatByUsername.id,
|
||||
tabId,
|
||||
startApp,
|
||||
theme,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!messageId) return;
|
||||
|
||||
const threadInfo = selectThreadInfo(global, chatByUsername.id, messageId);
|
||||
let discussionChatId: string | undefined;
|
||||
|
||||
|
||||
@ -102,7 +102,7 @@ addActionHandler('openChatWithInfo', (global, actions, payload): ActionReturnTyp
|
||||
|
||||
addActionHandler('openChatWithDraft', (global, actions, payload): ActionReturnType => {
|
||||
const {
|
||||
chatId, text, threadId, files, tabId = getCurrentTabId(),
|
||||
chatId, text, threadId, files, filter, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
if (chatId) {
|
||||
@ -114,6 +114,7 @@ addActionHandler('openChatWithDraft', (global, actions, payload): ActionReturnTy
|
||||
chatId,
|
||||
text,
|
||||
files,
|
||||
filter,
|
||||
},
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import type { ApiPhoto } from '../../api/types';
|
||||
import type { ApiChatType, ApiPhoto } from '../../api/types';
|
||||
|
||||
export function getBotCoverMediaHash(photo: ApiPhoto) {
|
||||
return `photo${photo.id}?size=x`;
|
||||
}
|
||||
|
||||
export function convertToApiChatType(type: string): ApiChatType | undefined {
|
||||
if (type === 'channels') return 'channels';
|
||||
if (type === 'chats' || type === 'groups') return 'chats';
|
||||
if (type === 'users') return 'users';
|
||||
if (type === 'bots') return 'bots';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -452,6 +452,7 @@ export type TabState = {
|
||||
chatId?: string;
|
||||
text: string;
|
||||
files?: File[];
|
||||
filter?: ApiChatType[];
|
||||
};
|
||||
|
||||
pollModal: {
|
||||
@ -471,7 +472,8 @@ export type TabState = {
|
||||
|
||||
botTrustRequest?: {
|
||||
botId: string;
|
||||
type: 'game' | 'webApp';
|
||||
type: 'game' | 'webApp' | 'botApp';
|
||||
shouldRequestWriteAccess?: boolean;
|
||||
onConfirm?: CallbackAction;
|
||||
};
|
||||
requestedAttachBotInstall?: {
|
||||
@ -1260,6 +1262,8 @@ export interface ActionPayloads {
|
||||
startParam?: string;
|
||||
startAttach?: string | boolean;
|
||||
attach?: string;
|
||||
startApp?: string;
|
||||
originalParts?: string[];
|
||||
} & WithTabId;
|
||||
requestThreadInfoUpdate: {
|
||||
chatId: string;
|
||||
@ -1644,6 +1648,7 @@ export interface ActionPayloads {
|
||||
threadId?: number;
|
||||
text: string;
|
||||
files?: File[];
|
||||
filter?: ApiChatType[];
|
||||
} & WithTabId;
|
||||
resetOpenChatWithDraft: WithTabId | undefined;
|
||||
toggleJoinToSend: {
|
||||
@ -2019,9 +2024,11 @@ export interface ActionPayloads {
|
||||
} & WithTabId;
|
||||
|
||||
switchBotInline: {
|
||||
messageId: number;
|
||||
messageId?: number;
|
||||
botId?: string;
|
||||
query: string;
|
||||
isSamePeer?: boolean;
|
||||
filter?: ApiChatType[];
|
||||
} & WithTabId;
|
||||
|
||||
openGame: {
|
||||
@ -2055,6 +2062,13 @@ export interface ActionPayloads {
|
||||
buttonText: string;
|
||||
theme?: ApiThemeParameters;
|
||||
} & WithTabId;
|
||||
requestAppWebView: {
|
||||
botId: string;
|
||||
appName: string;
|
||||
theme?: ApiThemeParameters;
|
||||
startApp?: string;
|
||||
isWriteAllowed?: boolean;
|
||||
} & WithTabId;
|
||||
setWebAppPaymentSlug: {
|
||||
slug?: string;
|
||||
} & WithTabId;
|
||||
@ -2062,6 +2076,7 @@ export interface ActionPayloads {
|
||||
cancelBotTrustRequest: WithTabId | undefined;
|
||||
markBotTrusted: {
|
||||
botId: string;
|
||||
isWriteAllowed?: boolean;
|
||||
} & WithTabId;
|
||||
|
||||
cancelAttachBotInstall: WithTabId | undefined;
|
||||
|
||||
@ -1304,6 +1304,8 @@ messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions;
|
||||
messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions;
|
||||
messages.clearRecentReactions#9dfeefb4 = Bool;
|
||||
messages.getExtendedMedia#84f80814 peer:InputPeer id:Vector<int> = Updates;
|
||||
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
|
||||
messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult;
|
||||
updates.getState#edd4882a = updates.State;
|
||||
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;
|
||||
|
||||
@ -163,6 +163,8 @@
|
||||
"messages.toggleNoForwards",
|
||||
"messages.saveDefaultSendAs",
|
||||
"messages.getExtendedMedia",
|
||||
"messages.getBotApp",
|
||||
"messages.requestAppWebView",
|
||||
"updates.getState",
|
||||
"updates.getDifference",
|
||||
"updates.getChannelDifference",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { TeactNode } from '../lib/teact/teact';
|
||||
import type {
|
||||
ApiBotInlineMediaResult, ApiBotInlineResult, ApiBotInlineSwitchPm,
|
||||
ApiBotInlineSwitchWebview,
|
||||
ApiChatInviteImporter,
|
||||
ApiExportedInvite,
|
||||
ApiLanguage, ApiMessage, ApiReaction, ApiStickerSet,
|
||||
@ -396,5 +397,6 @@ export type InlineBotSettings = {
|
||||
results?: (ApiBotInlineResult | ApiBotInlineMediaResult)[];
|
||||
isGallery?: boolean;
|
||||
switchPm?: ApiBotInlineSwitchPm;
|
||||
switchWebview?: ApiBotInlineSwitchWebview;
|
||||
cacheTime: number;
|
||||
};
|
||||
|
||||
@ -36,6 +36,7 @@ export const processDeepLink = (url: string) => {
|
||||
case 'resolve': {
|
||||
const {
|
||||
domain, phone, post, comment, voicechat, livestream, start, startattach, attach, thread, topic,
|
||||
appname, startapp,
|
||||
} = params;
|
||||
|
||||
const startAttach = params.hasOwnProperty('startattach') && !startattach ? true : startattach;
|
||||
@ -43,7 +44,13 @@ export const processDeepLink = (url: string) => {
|
||||
const threadId = Number(thread) || Number(topic) || undefined;
|
||||
|
||||
if (domain !== 'telegrampassport') {
|
||||
if (startAttach && choose) {
|
||||
if (appname) {
|
||||
openChatByUsername({
|
||||
username: domain,
|
||||
startApp: startapp,
|
||||
originalParts: [domain, appname],
|
||||
});
|
||||
} else if (startAttach && choose) {
|
||||
processAttachBotParameters({
|
||||
username: domain,
|
||||
filter: choose,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user