diff --git a/Telegram/SourceFiles/api/api_bot.cpp b/Telegram/SourceFiles/api/api_bot.cpp index cf4611563..929051261 100644 --- a/Telegram/SourceFiles/api/api_bot.cpp +++ b/Telegram/SourceFiles/api/api_bot.cpp @@ -127,11 +127,7 @@ void SendBotCallbackData( UrlClickHandler::Open(link); return; } - const auto scoreLink = AppendShareGameScoreUrl( - session, - link, - item->fullId()); - BotGameUrlClickHandler(bot, scoreLink).onClick({ + BotGameUrlClickHandler(bot, link).onClick({ Qt::LeftButton, QVariant::fromValue(ClickHandlerContext{ .itemId = item->fullId(), diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 043a8ad18..e7e3054aa 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -1409,55 +1409,6 @@ std::vector> ShareBox::Inner::selected() const { return result; } -QString AppendShareGameScoreUrl( - not_null session, - const QString &url, - const FullMsgId &fullId) { - auto shareHashData = QByteArray(0x20, Qt::Uninitialized); - auto shareHashDataInts = reinterpret_cast(shareHashData.data()); - const auto peer = fullId.peer - ? session->data().peerLoaded(fullId.peer) - : static_cast(nullptr); - const auto channelAccessHash = uint64((peer && peer->isChannel()) - ? peer->asChannel()->access - : 0); - shareHashDataInts[0] = session->userId().bare; - shareHashDataInts[1] = fullId.peer.value; - shareHashDataInts[2] = uint64(fullId.msg.bare); - shareHashDataInts[3] = channelAccessHash; - - // Count SHA1() of data. - auto key128Size = 0x10; - auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized); - hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data()); - - //// Mix in channel access hash to the first 64 bits of SHA1 of data. - //*reinterpret_cast(shareHashEncrypted.data()) ^= channelAccessHash; - - // Encrypt data. - if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) { - return url; - } - - auto shareHash = shareHashEncrypted.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - auto shareUrl = u"tg://share_game_score?hash="_q + QString::fromLatin1(shareHash); - - auto shareComponent = u"tgShareScoreUrl="_q + qthelp::url_encode(shareUrl); - - auto hashPosition = url.indexOf('#'); - if (hashPosition < 0) { - return url + '#' + shareComponent; - } - auto hash = url.mid(hashPosition + 1); - if (hash.indexOf('=') >= 0 || hash.indexOf('?') >= 0) { - return url + '&' + shareComponent; - } - if (!hash.isEmpty()) { - return url + '?' + shareComponent; - } - return url + shareComponent; -} - ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs( const std::vector> &result, const MessageIdsList &msgIds) { @@ -1612,9 +1563,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback( } void FastShareMessage( - not_null controller, + std::shared_ptr show, not_null item) { - const auto show = controller->uiShow(); const auto history = item->history(); const auto owner = &history->owner(); const auto session = &history->session(); @@ -1643,7 +1593,7 @@ void FastShareMessage( } if (item->hasDirectLink()) { using namespace HistoryView; - CopyPostLink(controller, item->fullId(), Context::History); + CopyPostLink(show, item->fullId(), Context::History); } else if (const auto bot = item->getMessageBot()) { if (const auto media = item->media()) { if (const auto game = media->game()) { @@ -1675,23 +1625,27 @@ void FastShareMessage( auto copyLinkCallback = canCopyLink ? Fn(std::move(copyCallback)) : Fn(); - controller->show( - Box(ShareBox::Descriptor{ - .session = session, - .copyCallback = std::move(copyLinkCallback), - .submitCallback = ShareBox::DefaultForwardCallback( - show, - history, - msgIds), - .filterCallback = std::move(filterCallback), - .forwardOptions = { - .sendersCount = ItemsForwardSendersCount(items), - .captionsCount = ItemsForwardCaptionsCount(items), - .show = !hasOnlyForcedForwardedInfo, - }, - .premiumRequiredError = SharePremiumRequiredError(), - }), - Ui::LayerOption::CloseOther); + show->show(Box(ShareBox::Descriptor{ + .session = session, + .copyCallback = std::move(copyLinkCallback), + .submitCallback = ShareBox::DefaultForwardCallback( + show, + history, + msgIds), + .filterCallback = std::move(filterCallback), + .forwardOptions = { + .sendersCount = ItemsForwardSendersCount(items), + .captionsCount = ItemsForwardCaptionsCount(items), + .show = !hasOnlyForcedForwardedInfo, + }, + .premiumRequiredError = SharePremiumRequiredError(), + }), Ui::LayerOption::CloseOther); +} + +void FastShareMessage( + not_null controller, + not_null item) { + FastShareMessage(controller->uiShow(), item); } void FastShareLink( @@ -1793,111 +1747,3 @@ auto SharePremiumRequiredError() -> Fn)> { return WritePremiumRequiredError; } - -void ShareGameScoreByHash( - not_null controller, - const QString &hash) { - auto &session = controller->session(); - auto key128Size = 0x10; - - auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() != key128Size + 0x20)) { - controller->show( - Ui::MakeInformBox(tr::lng_confirm_phone_link_invalid()), - Ui::LayerOption::CloseOther); - return; - } - - // Decrypt data. - auto hashData = QByteArray(hashEncrypted.size() - key128Size, Qt::Uninitialized); - if (!session.local().decrypt(hashEncrypted.constData() + key128Size, hashData.data(), hashEncrypted.size() - key128Size, hashEncrypted.constData())) { - return; - } - - // Count SHA1() of data. - char dataSha1[20] = { 0 }; - hashSha1(hashData.constData(), hashData.size(), dataSha1); - - //// Mix out channel access hash from the first 64 bits of SHA1 of data. - //auto channelAccessHash = *reinterpret_cast(hashEncrypted.data()) ^ *reinterpret_cast(dataSha1); - - //// Check next 64 bits of SHA1() of data. - //auto skipSha1Part = sizeof(channelAccessHash); - //if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) { - // Ui::show(Box(tr::lng_share_wrong_user(tr::now))); - // return; - //} - - // Check 128 bits of SHA1() of data. - if (memcmp(dataSha1, hashEncrypted.constData(), key128Size) != 0) { - controller->show( - Ui::MakeInformBox(tr::lng_share_wrong_user()), - Ui::LayerOption::CloseOther); - return; - } - - auto hashDataInts = reinterpret_cast(hashData.data()); - if (hashDataInts[0] != session.userId().bare) { - controller->show( - Ui::MakeInformBox(tr::lng_share_wrong_user()), - Ui::LayerOption::CloseOther); - return; - } - - const auto peerId = PeerId(hashDataInts[1]); - const auto channelAccessHash = hashDataInts[3]; - if (!peerIsChannel(peerId) && channelAccessHash) { - // If there is no channel id, there should be no channel access_hash. - controller->show( - Ui::MakeInformBox(tr::lng_share_wrong_user()), - Ui::LayerOption::CloseOther); - return; - } - - const auto msgId = MsgId(int64(hashDataInts[2])); - if (const auto item = session.data().message(peerId, msgId)) { - FastShareMessage(controller, item); - } else { - const auto weak = base::make_weak(controller); - const auto resolveMessageAndShareScore = crl::guard(weak, [=]( - PeerData *peer) { - auto done = crl::guard(weak, [=] { - const auto item = weak->session().data().message( - peerId, - msgId); - if (item) { - FastShareMessage(weak.get(), item); - } else { - weak->show( - Ui::MakeInformBox(tr::lng_edit_deleted()), - Ui::LayerOption::CloseOther); - } - }); - auto &api = weak->session().api(); - api.requestMessageData(peer, msgId, std::move(done)); - }); - - const auto peer = peerIsChannel(peerId) - ? controller->session().data().peerLoaded(peerId) - : nullptr; - if (peer || !peerIsChannel(peerId)) { - resolveMessageAndShareScore(peer); - } else { - const auto owner = &controller->session().data(); - controller->session().api().request(MTPchannels_GetChannels( - MTP_vector( - 1, - MTP_inputChannel( - MTP_long(peerToChannel(peerId).bare), - MTP_long(channelAccessHash))) - )).done([=](const MTPmessages_Chats &result) { - result.match([&](const auto &data) { - owner->processChats(data.vchats()); - }); - if (const auto peer = owner->peerLoaded(peerId)) { - resolveMessageAndShareScore(peer); - } - }).send(); - } - } -} diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index 32e824b15..d0bf28ce9 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -59,13 +59,11 @@ class SlideWrap; class PopupMenu; } // namespace Ui -QString AppendShareGameScoreUrl( - not_null session, - const QString &url, - const FullMsgId &fullId); -void ShareGameScoreByHash( - not_null controller, - const QString &hash); +class ShareBox; + +void FastShareMessage( + std::shared_ptr show, + not_null item); void FastShareMessage( not_null controller, not_null item); diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 9b03e0b74..eadb16181 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -20,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/view/history_view_element.h" #include "history/history_item.h" +#include "inline_bots/bot_attach_web_view.h" +#include "data/data_game.h" #include "data/data_user.h" #include "data/data_session.h" #include "window/window_controller.h" @@ -171,23 +173,40 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const { if (Core::InternalPassportLink(url)) { return; } - - const auto open = [=] { + const auto openLink = [=] { UrlClickHandler::Open(url, context.other); }; - if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive)) { - open(); - } else if (!_bot - || _bot->isVerified() + const auto my = context.other.value(); + const auto weakController = my.sessionWindow; + const auto controller = weakController.get(); + const auto item = controller + ? controller->session().data().message(my.itemId) + : nullptr; + const auto media = item ? item->media() : nullptr; + const auto game = media ? media->game() : nullptr; + if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive) || !_bot || !game) { + openLink(); + } + const auto bot = _bot; + const auto title = game->title; + const auto itemId = my.itemId; + const auto openGame = [=] { + bot->session().attachWebView().showGame({ + .bot = bot, + .context = itemId, + .url = url, + .title = title, + }); + }; + if (_bot->isVerified() || _bot->session().local().isBotTrustedOpenGame(_bot->id)) { - open(); + openGame(); } else { - const auto my = context.other.value(); if (const auto controller = my.sessionWindow.get()) { const auto callback = [=, bot = _bot](Fn close) { close(); bot->session().local().markBotTrustedOpenGame(bot->id); - open(); + openGame(); }; controller->show(Ui::MakeConfirmBox({ .text = tr::lng_allow_bot_pass( diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 03c3254ab..9f4860da3 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -327,21 +327,6 @@ bool ConfirmPhone( return true; } -bool ShareGameScore( - Window::SessionController *controller, - const Match &match, - const QVariant &context) { - if (!controller) { - return false; - } - const auto params = url_parse_params( - match->captured(1), - qthelp::UrlParamNameTransform::ToLower); - ShareGameScoreByHash(controller, params.value(u"hash"_q)); - controller->window().activate(); - return true; -} - bool ApplySocksProxy( Window::SessionController *controller, const Match &match, @@ -1230,10 +1215,6 @@ const std::vector &LocalUrlHandlers() { u"^confirmphone/?\\?(.+)(#|$)"_q, ConfirmPhone }, - { - u"^share_game_score/?\\?(.+)(#|$)"_q, - ShareGameScore - }, { u"^socks/?\\?(.+)(#|$)"_q, ApplySocksProxy diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index a53ab2028..49013f1c8 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -1284,7 +1284,14 @@ void CopyPostLink( not_null controller, FullMsgId itemId, Context context) { - const auto item = controller->session().data().message(itemId); + CopyPostLink(controller->uiShow(), itemId, context); +} + +void CopyPostLink( + std::shared_ptr show, + FullMsgId itemId, + Context context) { + const auto item = show->session().data().message(itemId); if (!item || !item->hasDirectLink()) { return; } @@ -1311,7 +1318,7 @@ void CopyPostLink( return channel->hasUsername(); }(); - controller->showToast(isPublicLink + show->showToast(isPublicLink ? tr::lng_channel_public_link_copied(tr::now) : tr::lng_context_about_private_link(tr::now)); } diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.h b/Telegram/SourceFiles/history/view/history_view_context_menu.h index 8f00f4da8..efb2a5fdd 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.h +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.h @@ -61,6 +61,10 @@ void CopyPostLink( not_null controller, FullMsgId itemId, Context context); +void CopyPostLink( + std::shared_ptr show, + FullMsgId itemId, + Context context); void CopyStoryLink( std::shared_ptr show, FullStoryId storyId); diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index eb938ed26..ff2637011 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_blocked_peers.h" #include "api/api_common.h" #include "base/qthelp_url.h" +#include "boxes/share_box.h" #include "core/click_handler_types.h" #include "data/data_bot_app.h" #include "data/data_changes.h" @@ -802,6 +803,16 @@ void AttachWebView::botInvokeCustomMethod( }).send(); } +void AttachWebView::botShareGameScore() { + if (!_panel || !_gameContext) { + return; + } else if (const auto item = _session->data().message(_gameContext)) { + FastShareMessage(uiShow(), item); + } else { + _panel->showToast({ tr::lng_message_not_found(tr::now) }); + } +} + void AttachWebView::botClose() { crl::on_main(this, [=] { cancel(); }); } @@ -1547,6 +1558,7 @@ void AttachWebView::show( _lastShownUrl = url; _lastShownQueryId = queryId; _lastShownButtonText = buttonText; + _gameContext = {}; base::take(_panel); _catchingCancelInShowCall = true; _panel = Ui::BotWebView::Show({ @@ -1562,6 +1574,24 @@ void AttachWebView::show( started(queryId); } +void AttachWebView::showGame(ShowGameParams &¶ms) { + ActiveWebViews().emplace(this); + + base::take(_panel); + _gameContext = params.context; + + _catchingCancelInShowCall = true; + _panel = Ui::BotWebView::Show({ + .url = params.url, + .storageId = _session->local().resolveStorageIdBots(), + .title = rpl::single(params.title), + .bottom = rpl::single('@' + params.bot->username()), + .delegate = static_cast(this), + .menuButtons = Ui::BotWebView::MenuButton::ShareGame, + }); + _catchingCancelInShowCall = false; +} + void AttachWebView::started(uint64 queryId) { Expects(_bot != nullptr); Expects(_context != nullptr); @@ -1601,6 +1631,57 @@ void AttachWebView::started(uint64 queryId) { }, _panel->lifetime()); } +std::shared_ptr AttachWebView::uiShow() { + class Show final : public Main::SessionShow { + public: + explicit Show(not_null that) : _that(that) { + } + + void showOrHideBoxOrLayer( + std::variant< + v::null_t, + object_ptr, + std::unique_ptr> &&layer, + Ui::LayerOptions options, + anim::type animated) const override { + using UniqueLayer = std::unique_ptr; + using ObjectBox = object_ptr; + const auto panel = _that ? _that->_panel.get() : nullptr; + if (auto layerWidget = std::get_if(&layer)) { + Unexpected("Layers in AttachWebView are not implemented."); + } else if (auto box = std::get_if(&layer)) { + if (panel) { + panel->showBox(std::move(*box), options, animated); + } + } else if (panel) { + panel->hideLayer(animated); + } + } + [[nodiscard]] not_null toastParent() const override { + const auto panel = _that ? _that->_panel.get() : nullptr; + + Ensures(panel != nullptr); + return panel->toastParent(); + } + [[nodiscard]] bool valid() const override { + return _that && (_that->_panel != nullptr); + } + operator bool() const override { + return valid(); + } + + [[nodiscard]] Main::Session &session() const override { + Expects(_that.get() != nullptr); + return *_that->_session; + } + + private: + const base::weak_ptr _that; + + }; + return std::make_shared(this); +} + void AttachWebView::showToast( const QString &text, Window::SessionController *controller) { diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index 0f8cb6e13..87648b07e 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -29,6 +29,7 @@ class Panel; namespace Main { class Session; +class SessionShow; } // namespace Main namespace Window { @@ -155,12 +156,21 @@ public: [[nodiscard]] std::optional lookupLastAction( const QString &url) const; + struct ShowGameParams { + not_null bot; + FullMsgId context; + QString url; + QString title; + }; + void showGame(ShowGameParams &¶ms); + + [[nodiscard]] std::shared_ptr uiShow(); + static void ClearAll(); private: struct Context; - Webview::ThemeParams botThemeParams() override; bool botHandleLocalUri(QString uri, bool keepOpen) override; void botHandleInvoice(QString slug) override; @@ -176,6 +186,7 @@ private: void botSharePhone(Fn callback) override; void botInvokeCustomMethod( Ui::BotWebView::CustomMethodRequest request) override; + void botShareGameScore() override; void botClose() override; [[nodiscard]] static Context LookupContext( @@ -271,6 +282,8 @@ private: rpl::event_stream<> _attachBotsUpdates; base::flat_set> _disclaimerAccepted; + FullMsgId _gameContext; + std::unique_ptr _panel; bool _catchingCancelInShowCall = false; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index 569c61bb9..d367e3382 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "webview/webview_interface.h" #include "base/debug_log.h" #include "base/invoke_queued.h" +#include "base/qt_signal_producer.h" #include "styles/style_payments.h" #include "styles/style_layers.h" #include "styles/style_menu_icons.h" @@ -34,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include namespace Ui::BotWebView { namespace { @@ -373,6 +375,13 @@ Panel::~Panel() { void Panel::requestActivate() { _widget->showAndActivate(); + if (const auto widget = _webview ? _webview->window.widget() : nullptr) { + InvokeQueued(widget, [=] { + if (widget->isVisible()) { + _webview->window.focus(); + } + }); + } } void Panel::toggleProgress(bool shown) { @@ -527,9 +536,15 @@ bool Panel::showWebview( _webview->window.navigate(url); } }, &st::menuIconRestore); - callback(tr::lng_bot_terms(tr::now), [=] { - File::OpenUrl(tr::lng_mini_apps_tos_url(tr::now)); - }, &st::menuIconGroupLog); + if (_menuButtons & MenuButton::ShareGame) { + callback(tr::lng_iv_share(tr::now), [=] { + _delegate->botShareGameScore(); + }, &st::menuIconShare); + } else { + callback(tr::lng_bot_terms(tr::now), [=] { + File::OpenUrl(tr::lng_mini_apps_tos_url(tr::now)); + }, &st::menuIconGroupLog); + } const auto main = (_menuButtons & MenuButton::RemoveFromMainMenu); if (main || (_menuButtons & MenuButton::RemoveFromMenu)) { const auto handler = [=] { @@ -691,6 +706,8 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) { requestClipboardText(arguments); } else if (command == "web_app_set_header_color") { processHeaderColor(arguments); + } else if (command == "share_score") { + _delegate->botShareGameScore(); } }); @@ -722,6 +739,17 @@ postEvent: function(eventType, eventData) { setupProgressGeometry(); + base::qt_signal_producer( + _widget->window()->windowHandle(), + &QWindow::activeChanged + ) | rpl::filter([=] { + return _webview && _widget->window()->windowHandle()->isActive(); + }) | rpl::start_with_next([=] { + if (_webview && !_webview->window.widget()->isHidden()) { + _webview->window.focus(); + } + }, _webview->lifetime); + return true; } @@ -1207,6 +1235,13 @@ void Panel::updateFooterHeight() { } void Panel::showBox(object_ptr box) { + showBox(std::move(box), LayerOption::KeepOther, anim::type::normal); +} + +void Panel::showBox( + object_ptr box, + LayerOptions options, + anim::type animated) { if (const auto widget = _webview ? _webview->window.widget() : nullptr) { const auto hideNow = !widget->isHidden(); if (hideNow || _webview->lastHidingBox) { @@ -1220,10 +1255,12 @@ void Panel::showBox(object_ptr box) { && widget->isHidden() && _webview->lastHidingBox == raw) { widget->show(); + _webviewBottom->show(); } }, _webview->lifetime); if (hideNow) { widget->hide(); + _webviewBottom->hide(); } } } @@ -1237,6 +1274,14 @@ void Panel::showToast(TextWithEntities &&text) { _widget->showToast(std::move(text)); } +not_null Panel::toastParent() const { + return _widget->uiShow()->toastParent(); +} + +void Panel::hideLayer(anim::type animated) { + _widget->hideLayer(animated); +} + void Panel::showCriticalError(const TextWithEntities &text) { _progress = nullptr; _webviewProgress = false; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h index 91e423279..d9071c846 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h @@ -20,6 +20,8 @@ namespace Ui { class BoxContent; class RpWidget; class SeparatePanel; +enum class LayerOption; +using LayerOptions = base::flags; } // namespace Ui namespace Webview { @@ -40,6 +42,7 @@ enum class MenuButton { OpenBot = 0x01, RemoveFromMenu = 0x02, RemoveFromMainMenu = 0x04, + ShareGame = 0x08, }; inline constexpr bool is_flag_type(MenuButton) { return true; } using MenuButtons = base::flags; @@ -67,6 +70,7 @@ public: virtual void botAllowWriteAccess(Fn callback) = 0; virtual void botSharePhone(Fn callback) = 0; virtual void botInvokeCustomMethod(CustomMethodRequest request) = 0; + virtual void botShareGameScore() = 0; virtual void botClose() = 0; }; @@ -89,7 +93,13 @@ public: rpl::producer bottomText); void showBox(object_ptr box); + void showBox( + object_ptr box, + LayerOptions options, + anim::type animated); + void hideLayer(anim::type animated); void showToast(TextWithEntities &&text); + not_null toastParent() const; void showCriticalError(const TextWithEntities &text); void showWebviewError( const QString &text,