Migrate games to AttachWebView.

This commit is contained in:
John Preston 2024-07-09 15:47:14 +02:00
parent 78093173a9
commit 6effac7915
11 changed files with 223 additions and 223 deletions

View file

@ -127,11 +127,7 @@ void SendBotCallbackData(
UrlClickHandler::Open(link); UrlClickHandler::Open(link);
return; return;
} }
const auto scoreLink = AppendShareGameScoreUrl( BotGameUrlClickHandler(bot, link).onClick({
session,
link,
item->fullId());
BotGameUrlClickHandler(bot, scoreLink).onClick({
Qt::LeftButton, Qt::LeftButton,
QVariant::fromValue(ClickHandlerContext{ QVariant::fromValue(ClickHandlerContext{
.itemId = item->fullId(), .itemId = item->fullId(),

View file

@ -1409,55 +1409,6 @@ std::vector<not_null<Data::Thread*>> ShareBox::Inner::selected() const {
return result; return result;
} }
QString AppendShareGameScoreUrl(
not_null<Main::Session*> session,
const QString &url,
const FullMsgId &fullId) {
auto shareHashData = QByteArray(0x20, Qt::Uninitialized);
auto shareHashDataInts = reinterpret_cast<uint64*>(shareHashData.data());
const auto peer = fullId.peer
? session->data().peerLoaded(fullId.peer)
: static_cast<PeerData*>(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<uint64*>(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( ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs(
const std::vector<not_null<Data::Thread*>> &result, const std::vector<not_null<Data::Thread*>> &result,
const MessageIdsList &msgIds) { const MessageIdsList &msgIds) {
@ -1612,9 +1563,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
} }
void FastShareMessage( void FastShareMessage(
not_null<Window::SessionController*> controller, std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item) { not_null<HistoryItem*> item) {
const auto show = controller->uiShow();
const auto history = item->history(); const auto history = item->history();
const auto owner = &history->owner(); const auto owner = &history->owner();
const auto session = &history->session(); const auto session = &history->session();
@ -1643,7 +1593,7 @@ void FastShareMessage(
} }
if (item->hasDirectLink()) { if (item->hasDirectLink()) {
using namespace HistoryView; using namespace HistoryView;
CopyPostLink(controller, item->fullId(), Context::History); CopyPostLink(show, item->fullId(), Context::History);
} else if (const auto bot = item->getMessageBot()) { } else if (const auto bot = item->getMessageBot()) {
if (const auto media = item->media()) { if (const auto media = item->media()) {
if (const auto game = media->game()) { if (const auto game = media->game()) {
@ -1675,23 +1625,27 @@ void FastShareMessage(
auto copyLinkCallback = canCopyLink auto copyLinkCallback = canCopyLink
? Fn<void()>(std::move(copyCallback)) ? Fn<void()>(std::move(copyCallback))
: Fn<void()>(); : Fn<void()>();
controller->show( show->show(Box<ShareBox>(ShareBox::Descriptor{
Box<ShareBox>(ShareBox::Descriptor{ .session = session,
.session = session, .copyCallback = std::move(copyLinkCallback),
.copyCallback = std::move(copyLinkCallback), .submitCallback = ShareBox::DefaultForwardCallback(
.submitCallback = ShareBox::DefaultForwardCallback( show,
show, history,
history, msgIds),
msgIds), .filterCallback = std::move(filterCallback),
.filterCallback = std::move(filterCallback), .forwardOptions = {
.forwardOptions = { .sendersCount = ItemsForwardSendersCount(items),
.sendersCount = ItemsForwardSendersCount(items), .captionsCount = ItemsForwardCaptionsCount(items),
.captionsCount = ItemsForwardCaptionsCount(items), .show = !hasOnlyForcedForwardedInfo,
.show = !hasOnlyForcedForwardedInfo, },
}, .premiumRequiredError = SharePremiumRequiredError(),
.premiumRequiredError = SharePremiumRequiredError(), }), Ui::LayerOption::CloseOther);
}), }
Ui::LayerOption::CloseOther);
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item) {
FastShareMessage(controller->uiShow(), item);
} }
void FastShareLink( void FastShareLink(
@ -1793,111 +1747,3 @@ auto SharePremiumRequiredError()
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> { -> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> {
return WritePremiumRequiredError; return WritePremiumRequiredError;
} }
void ShareGameScoreByHash(
not_null<Window::SessionController*> 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<uint64*>(hashEncrypted.data()) ^ *reinterpret_cast<uint64*>(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<Ui::InformBox>(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<uint64*>(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<MTPInputChannel>(
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();
}
}
}

View file

@ -59,13 +59,11 @@ class SlideWrap;
class PopupMenu; class PopupMenu;
} // namespace Ui } // namespace Ui
QString AppendShareGameScoreUrl( class ShareBox;
not_null<Main::Session*> session,
const QString &url, void FastShareMessage(
const FullMsgId &fullId); std::shared_ptr<Main::SessionShow> show,
void ShareGameScoreByHash( not_null<HistoryItem*> item);
not_null<Window::SessionController*> controller,
const QString &hash);
void FastShareMessage( void FastShareMessage(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item); not_null<HistoryItem*> item);

View file

@ -20,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/history_item.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_user.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "window/window_controller.h" #include "window/window_controller.h"
@ -171,23 +173,40 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
if (Core::InternalPassportLink(url)) { if (Core::InternalPassportLink(url)) {
return; return;
} }
const auto openLink = [=] {
const auto open = [=] {
UrlClickHandler::Open(url, context.other); UrlClickHandler::Open(url, context.other);
}; };
if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive)) { const auto my = context.other.value<ClickHandlerContext>();
open(); const auto weakController = my.sessionWindow;
} else if (!_bot const auto controller = weakController.get();
|| _bot->isVerified() 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)) { || _bot->session().local().isBotTrustedOpenGame(_bot->id)) {
open(); openGame();
} else { } else {
const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) { if (const auto controller = my.sessionWindow.get()) {
const auto callback = [=, bot = _bot](Fn<void()> close) { const auto callback = [=, bot = _bot](Fn<void()> close) {
close(); close();
bot->session().local().markBotTrustedOpenGame(bot->id); bot->session().local().markBotTrustedOpenGame(bot->id);
open(); openGame();
}; };
controller->show(Ui::MakeConfirmBox({ controller->show(Ui::MakeConfirmBox({
.text = tr::lng_allow_bot_pass( .text = tr::lng_allow_bot_pass(

View file

@ -327,21 +327,6 @@ bool ConfirmPhone(
return true; 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( bool ApplySocksProxy(
Window::SessionController *controller, Window::SessionController *controller,
const Match &match, const Match &match,
@ -1230,10 +1215,6 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
u"^confirmphone/?\\?(.+)(#|$)"_q, u"^confirmphone/?\\?(.+)(#|$)"_q,
ConfirmPhone ConfirmPhone
}, },
{
u"^share_game_score/?\\?(.+)(#|$)"_q,
ShareGameScore
},
{ {
u"^socks/?\\?(.+)(#|$)"_q, u"^socks/?\\?(.+)(#|$)"_q,
ApplySocksProxy ApplySocksProxy

View file

@ -1284,7 +1284,14 @@ void CopyPostLink(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
FullMsgId itemId, FullMsgId itemId,
Context context) { Context context) {
const auto item = controller->session().data().message(itemId); CopyPostLink(controller->uiShow(), itemId, context);
}
void CopyPostLink(
std::shared_ptr<Main::SessionShow> show,
FullMsgId itemId,
Context context) {
const auto item = show->session().data().message(itemId);
if (!item || !item->hasDirectLink()) { if (!item || !item->hasDirectLink()) {
return; return;
} }
@ -1311,7 +1318,7 @@ void CopyPostLink(
return channel->hasUsername(); return channel->hasUsername();
}(); }();
controller->showToast(isPublicLink show->showToast(isPublicLink
? tr::lng_channel_public_link_copied(tr::now) ? tr::lng_channel_public_link_copied(tr::now)
: tr::lng_context_about_private_link(tr::now)); : tr::lng_context_about_private_link(tr::now));
} }

View file

@ -61,6 +61,10 @@ void CopyPostLink(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
FullMsgId itemId, FullMsgId itemId,
Context context); Context context);
void CopyPostLink(
std::shared_ptr<Main::SessionShow> show,
FullMsgId itemId,
Context context);
void CopyStoryLink( void CopyStoryLink(
std::shared_ptr<Main::SessionShow> show, std::shared_ptr<Main::SessionShow> show,
FullStoryId storyId); FullStoryId storyId);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_blocked_peers.h" #include "api/api_blocked_peers.h"
#include "api/api_common.h" #include "api/api_common.h"
#include "base/qthelp_url.h" #include "base/qthelp_url.h"
#include "boxes/share_box.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "data/data_bot_app.h" #include "data/data_bot_app.h"
#include "data/data_changes.h" #include "data/data_changes.h"
@ -802,6 +803,16 @@ void AttachWebView::botInvokeCustomMethod(
}).send(); }).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() { void AttachWebView::botClose() {
crl::on_main(this, [=] { cancel(); }); crl::on_main(this, [=] { cancel(); });
} }
@ -1547,6 +1558,7 @@ void AttachWebView::show(
_lastShownUrl = url; _lastShownUrl = url;
_lastShownQueryId = queryId; _lastShownQueryId = queryId;
_lastShownButtonText = buttonText; _lastShownButtonText = buttonText;
_gameContext = {};
base::take(_panel); base::take(_panel);
_catchingCancelInShowCall = true; _catchingCancelInShowCall = true;
_panel = Ui::BotWebView::Show({ _panel = Ui::BotWebView::Show({
@ -1562,6 +1574,24 @@ void AttachWebView::show(
started(queryId); started(queryId);
} }
void AttachWebView::showGame(ShowGameParams &&params) {
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<Ui::BotWebView::Delegate*>(this),
.menuButtons = Ui::BotWebView::MenuButton::ShareGame,
});
_catchingCancelInShowCall = false;
}
void AttachWebView::started(uint64 queryId) { void AttachWebView::started(uint64 queryId) {
Expects(_bot != nullptr); Expects(_bot != nullptr);
Expects(_context != nullptr); Expects(_context != nullptr);
@ -1601,6 +1631,57 @@ void AttachWebView::started(uint64 queryId) {
}, _panel->lifetime()); }, _panel->lifetime());
} }
std::shared_ptr<Main::SessionShow> AttachWebView::uiShow() {
class Show final : public Main::SessionShow {
public:
explicit Show(not_null<AttachWebView*> that) : _that(that) {
}
void showOrHideBoxOrLayer(
std::variant<
v::null_t,
object_ptr<Ui::BoxContent>,
std::unique_ptr<Ui::LayerWidget>> &&layer,
Ui::LayerOptions options,
anim::type animated) const override {
using UniqueLayer = std::unique_ptr<Ui::LayerWidget>;
using ObjectBox = object_ptr<Ui::BoxContent>;
const auto panel = _that ? _that->_panel.get() : nullptr;
if (auto layerWidget = std::get_if<UniqueLayer>(&layer)) {
Unexpected("Layers in AttachWebView are not implemented.");
} else if (auto box = std::get_if<ObjectBox>(&layer)) {
if (panel) {
panel->showBox(std::move(*box), options, animated);
}
} else if (panel) {
panel->hideLayer(animated);
}
}
[[nodiscard]] not_null<QWidget*> 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<AttachWebView> _that;
};
return std::make_shared<Show>(this);
}
void AttachWebView::showToast( void AttachWebView::showToast(
const QString &text, const QString &text,
Window::SessionController *controller) { Window::SessionController *controller) {

View file

@ -29,6 +29,7 @@ class Panel;
namespace Main { namespace Main {
class Session; class Session;
class SessionShow;
} // namespace Main } // namespace Main
namespace Window { namespace Window {
@ -155,12 +156,21 @@ public:
[[nodiscard]] std::optional<Api::SendAction> lookupLastAction( [[nodiscard]] std::optional<Api::SendAction> lookupLastAction(
const QString &url) const; const QString &url) const;
struct ShowGameParams {
not_null<UserData*> bot;
FullMsgId context;
QString url;
QString title;
};
void showGame(ShowGameParams &&params);
[[nodiscard]] std::shared_ptr<Main::SessionShow> uiShow();
static void ClearAll(); static void ClearAll();
private: private:
struct Context; struct Context;
Webview::ThemeParams botThemeParams() override; Webview::ThemeParams botThemeParams() override;
bool botHandleLocalUri(QString uri, bool keepOpen) override; bool botHandleLocalUri(QString uri, bool keepOpen) override;
void botHandleInvoice(QString slug) override; void botHandleInvoice(QString slug) override;
@ -176,6 +186,7 @@ private:
void botSharePhone(Fn<void(bool shared)> callback) override; void botSharePhone(Fn<void(bool shared)> callback) override;
void botInvokeCustomMethod( void botInvokeCustomMethod(
Ui::BotWebView::CustomMethodRequest request) override; Ui::BotWebView::CustomMethodRequest request) override;
void botShareGameScore() override;
void botClose() override; void botClose() override;
[[nodiscard]] static Context LookupContext( [[nodiscard]] static Context LookupContext(
@ -271,6 +282,8 @@ private:
rpl::event_stream<> _attachBotsUpdates; rpl::event_stream<> _attachBotsUpdates;
base::flat_set<not_null<UserData*>> _disclaimerAccepted; base::flat_set<not_null<UserData*>> _disclaimerAccepted;
FullMsgId _gameContext;
std::unique_ptr<Ui::BotWebView::Panel> _panel; std::unique_ptr<Ui::BotWebView::Panel> _panel;
bool _catchingCancelInShowCall = false; bool _catchingCancelInShowCall = false;

View file

@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "webview/webview_interface.h" #include "webview/webview_interface.h"
#include "base/debug_log.h" #include "base/debug_log.h"
#include "base/invoke_queued.h" #include "base/invoke_queued.h"
#include "base/qt_signal_producer.h"
#include "styles/style_payments.h" #include "styles/style_payments.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_menu_icons.h" #include "styles/style_menu_icons.h"
@ -34,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QJsonArray> #include <QtCore/QJsonArray>
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtGui/QWindow>
namespace Ui::BotWebView { namespace Ui::BotWebView {
namespace { namespace {
@ -373,6 +375,13 @@ Panel::~Panel() {
void Panel::requestActivate() { void Panel::requestActivate() {
_widget->showAndActivate(); _widget->showAndActivate();
if (const auto widget = _webview ? _webview->window.widget() : nullptr) {
InvokeQueued(widget, [=] {
if (widget->isVisible()) {
_webview->window.focus();
}
});
}
} }
void Panel::toggleProgress(bool shown) { void Panel::toggleProgress(bool shown) {
@ -527,9 +536,15 @@ bool Panel::showWebview(
_webview->window.navigate(url); _webview->window.navigate(url);
} }
}, &st::menuIconRestore); }, &st::menuIconRestore);
callback(tr::lng_bot_terms(tr::now), [=] { if (_menuButtons & MenuButton::ShareGame) {
File::OpenUrl(tr::lng_mini_apps_tos_url(tr::now)); callback(tr::lng_iv_share(tr::now), [=] {
}, &st::menuIconGroupLog); _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); const auto main = (_menuButtons & MenuButton::RemoveFromMainMenu);
if (main || (_menuButtons & MenuButton::RemoveFromMenu)) { if (main || (_menuButtons & MenuButton::RemoveFromMenu)) {
const auto handler = [=] { const auto handler = [=] {
@ -691,6 +706,8 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
requestClipboardText(arguments); requestClipboardText(arguments);
} else if (command == "web_app_set_header_color") { } else if (command == "web_app_set_header_color") {
processHeaderColor(arguments); processHeaderColor(arguments);
} else if (command == "share_score") {
_delegate->botShareGameScore();
} }
}); });
@ -722,6 +739,17 @@ postEvent: function(eventType, eventData) {
setupProgressGeometry(); 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; return true;
} }
@ -1207,6 +1235,13 @@ void Panel::updateFooterHeight() {
} }
void Panel::showBox(object_ptr<BoxContent> box) { void Panel::showBox(object_ptr<BoxContent> box) {
showBox(std::move(box), LayerOption::KeepOther, anim::type::normal);
}
void Panel::showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) {
if (const auto widget = _webview ? _webview->window.widget() : nullptr) { if (const auto widget = _webview ? _webview->window.widget() : nullptr) {
const auto hideNow = !widget->isHidden(); const auto hideNow = !widget->isHidden();
if (hideNow || _webview->lastHidingBox) { if (hideNow || _webview->lastHidingBox) {
@ -1220,10 +1255,12 @@ void Panel::showBox(object_ptr<BoxContent> box) {
&& widget->isHidden() && widget->isHidden()
&& _webview->lastHidingBox == raw) { && _webview->lastHidingBox == raw) {
widget->show(); widget->show();
_webviewBottom->show();
} }
}, _webview->lifetime); }, _webview->lifetime);
if (hideNow) { if (hideNow) {
widget->hide(); widget->hide();
_webviewBottom->hide();
} }
} }
} }
@ -1237,6 +1274,14 @@ void Panel::showToast(TextWithEntities &&text) {
_widget->showToast(std::move(text)); _widget->showToast(std::move(text));
} }
not_null<QWidget*> Panel::toastParent() const {
return _widget->uiShow()->toastParent();
}
void Panel::hideLayer(anim::type animated) {
_widget->hideLayer(animated);
}
void Panel::showCriticalError(const TextWithEntities &text) { void Panel::showCriticalError(const TextWithEntities &text) {
_progress = nullptr; _progress = nullptr;
_webviewProgress = false; _webviewProgress = false;

View file

@ -20,6 +20,8 @@ namespace Ui {
class BoxContent; class BoxContent;
class RpWidget; class RpWidget;
class SeparatePanel; class SeparatePanel;
enum class LayerOption;
using LayerOptions = base::flags<LayerOption>;
} // namespace Ui } // namespace Ui
namespace Webview { namespace Webview {
@ -40,6 +42,7 @@ enum class MenuButton {
OpenBot = 0x01, OpenBot = 0x01,
RemoveFromMenu = 0x02, RemoveFromMenu = 0x02,
RemoveFromMainMenu = 0x04, RemoveFromMainMenu = 0x04,
ShareGame = 0x08,
}; };
inline constexpr bool is_flag_type(MenuButton) { return true; } inline constexpr bool is_flag_type(MenuButton) { return true; }
using MenuButtons = base::flags<MenuButton>; using MenuButtons = base::flags<MenuButton>;
@ -67,6 +70,7 @@ public:
virtual void botAllowWriteAccess(Fn<void(bool allowed)> callback) = 0; virtual void botAllowWriteAccess(Fn<void(bool allowed)> callback) = 0;
virtual void botSharePhone(Fn<void(bool shared)> callback) = 0; virtual void botSharePhone(Fn<void(bool shared)> callback) = 0;
virtual void botInvokeCustomMethod(CustomMethodRequest request) = 0; virtual void botInvokeCustomMethod(CustomMethodRequest request) = 0;
virtual void botShareGameScore() = 0;
virtual void botClose() = 0; virtual void botClose() = 0;
}; };
@ -89,7 +93,13 @@ public:
rpl::producer<QString> bottomText); rpl::producer<QString> bottomText);
void showBox(object_ptr<BoxContent> box); void showBox(object_ptr<BoxContent> box);
void showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated);
void hideLayer(anim::type animated);
void showToast(TextWithEntities &&text); void showToast(TextWithEntities &&text);
not_null<QWidget*> toastParent() const;
void showCriticalError(const TextWithEntities &text); void showCriticalError(const TextWithEntities &text);
void showWebviewError( void showWebviewError(
const QString &text, const QString &text,