diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 45f656430e..0e003b8bfd 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1223,6 +1223,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_open_link" = "Open"; "lng_allow_bot_pass" = "Allow {bot_name} to pass your Telegram name and ID to the web pages you open via this bot?"; "lng_allow_bot" = "Allow"; +"lng_url_auth_open_confirm" = "Do you want to open {link}?"; +"lng_url_auth_login_option" = "Log in to {domain} as {user}"; +"lng_url_auth_allow_messages" = "Allow {bot} to send me messages"; "lng_bot_start" = "Start"; "lng_bot_choose_group" = "Select a Group"; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 753537d5d9..7595d61c33 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -970,3 +970,7 @@ backgroundCheck: ServiceCheck { color: msgServiceFg; duration: 200; } + +urlAuthCheckbox: Checkbox(defaultBoxCheckbox) { + width: 240px; +} diff --git a/Telegram/SourceFiles/boxes/url_auth_box.cpp b/Telegram/SourceFiles/boxes/url_auth_box.cpp new file mode 100644 index 0000000000..bf0af8662b --- /dev/null +++ b/Telegram/SourceFiles/boxes/url_auth_box.cpp @@ -0,0 +1,204 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "boxes/url_auth_box.h" + +#include "history/history.h" +#include "history/history_item.h" +#include "history/history_item_components.h" +#include "data/data_session.h" +#include "data/data_user.h" +#include "core/click_handler_types.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/labels.h" +#include "lang/lang_keys.h" +#include "auth_session.h" +#include "apiwrap.h" +#include "styles/style_boxes.h" + +void UrlAuthBox::Activate( + not_null message, + int row, + int column) { + const auto itemId = message->fullId(); + const auto button = HistoryMessageMarkupButton::Get(itemId, row, column); + if (button->requestId || !IsServerMsgId(itemId.msg)) { + return; + } + const auto session = &message->history()->session(); + const auto inputPeer = message->history()->peer->input; + const auto buttonId = button->buttonId; + const auto url = QString::fromUtf8(button->data); + + button->requestId = session->api().request(MTPmessages_RequestUrlAuth( + inputPeer, + MTP_int(itemId.msg), + MTP_int(buttonId) + )).done([=](const MTPUrlAuthResult &result) { + const auto button = HistoryMessageMarkupButton::Get( + itemId, + row, + column); + if (!button) return; + + button->requestId = 0; + result.match([&](const MTPDurlAuthResultAccepted &data) { + UrlClickHandler::Open(qs(data.vurl)); + }, [&](const MTPDurlAuthResultDefault &data) { + HiddenUrlClickHandler::Open(url); + }, [&](const MTPDurlAuthResultRequest &data) { + Request(data, session->data().message(itemId), row, column); + }); + }).fail([=](const RPCError &error) { + const auto button = HistoryMessageMarkupButton::Get( + itemId, + row, + column); + if (!button) return; + + button->requestId = 0; + HiddenUrlClickHandler::Open(url); + }).send(); +} + +void UrlAuthBox::Request( + const MTPDurlAuthResultRequest &request, + not_null message, + int row, + int column) { + const auto itemId = message->fullId(); + const auto button = HistoryMessageMarkupButton::Get(itemId, row, column); + if (button->requestId || !IsServerMsgId(itemId.msg)) { + return; + } + const auto session = &message->history()->session(); + const auto inputPeer = message->history()->peer->input; + const auto buttonId = button->buttonId; + const auto url = QString::fromUtf8(button->data); + + const auto bot = request.is_request_write_access() + ? session->data().processUser(request.vbot).get() + : nullptr; + const auto box = std::make_shared>(); + const auto finishWithUrl = [=](const QString &url) { + if (*box) { + (*box)->closeBox(); + } + UrlClickHandler::Open(url); + }; + const auto callback = [=](Result result) { + if (result == Result::None) { + finishWithUrl(url); + } else if (const auto msg = session->data().message(itemId)) { + const auto allowWrite = (result == Result::AuthAndAllowWrite); + using Flag = MTPmessages_AcceptUrlAuth::Flag; + session->api().request(MTPmessages_AcceptUrlAuth( + MTP_flags(allowWrite ? Flag::f_write_allowed : Flag(0)), + inputPeer, + MTP_int(itemId.msg), + MTP_int(buttonId) + )).done([=](const MTPUrlAuthResult &result) { + const auto to = result.match( + [&](const MTPDurlAuthResultAccepted &data) { + return qs(data.vurl); + }, [&](const MTPDurlAuthResultDefault &data) { + return url; + }, [&](const MTPDurlAuthResultRequest &data) { + LOG(("API Error: " + "got urlAuthResultRequest after acceptUrlAuth.")); + return url; + }); + finishWithUrl(to); + }).fail([=](const RPCError &error) { + finishWithUrl(url); + }).send(); + } + }; + *box = Ui::show( + Box(url, qs(request.vdomain), bot, callback), + LayerOption::KeepOther); +} + +UrlAuthBox::UrlAuthBox( + QWidget*, + const QString &url, + const QString &domain, + UserData *bot, + Fn callback) +: _content(setupContent(url, domain, bot, std::move(callback))) { +} + +void UrlAuthBox::prepare() { + setDimensionsToContent(st::boxWidth, _content); + addButton(langFactory(lng_open_link), [=] { _callback(); }); + addButton(langFactory(lng_cancel), [=] { closeBox(); }); +} + +not_null UrlAuthBox::setupContent( + const QString &url, + const QString &domain, + UserData *bot, + Fn callback) { + const auto result = Ui::CreateChild(this); + result->add( + object_ptr( + result, + lng_url_auth_open_confirm(lt_link, url), + Ui::FlatLabel::InitType::Simple, + st::boxLabel), + st::boxPadding); + const auto addCheckbox = [&](const QString &text) { + const auto checkbox = result->add( + object_ptr( + result, + QString(), + false, + st::urlAuthCheckbox), + style::margins( + st::boxPadding.left(), + st::boxPadding.bottom(), + st::boxPadding.right(), + st::boxPadding.bottom())); + checkbox->setAllowMultiline(true); + checkbox->setText(text, true); + return checkbox; + }; + const auto auth = addCheckbox( + lng_url_auth_login_option( + lt_domain, + textcmdStartSemibold() + domain + textcmdStopSemibold(), + lt_user, + (textcmdStartSemibold() + + App::peerName(Auth().user()) + + textcmdStopSemibold()))); + const auto allow = bot + ? addCheckbox(lng_url_auth_allow_messages( + lt_bot, + textcmdStartSemibold() + bot->firstName + textcmdStopSemibold())) + : nullptr; + if (allow) { + rpl::single( + auth->checked() + ) | rpl::then( + auth->checkedChanges() + ) | rpl::start_with_next([=](bool checked) { + allow->setDisabled(!checked); + }, auth->lifetime()); + } + _callback = [=, callback = std::move(callback)]() { + const auto authed = auth->checked(); + const auto allowed = (authed && allow && allow->checked()); + const auto onstack = callback; + onstack(allowed + ? Result::AuthAndAllowWrite + : authed + ? Result::Auth + : Result::None); + }; + return result; +} diff --git a/Telegram/SourceFiles/boxes/url_auth_box.h b/Telegram/SourceFiles/boxes/url_auth_box.h new file mode 100644 index 0000000000..c3869ea603 --- /dev/null +++ b/Telegram/SourceFiles/boxes/url_auth_box.h @@ -0,0 +1,56 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "boxes/abstract_box.h" + +class HistoryItem; +struct HistoryMessageMarkupButton; + +class UrlAuthBox : public BoxContent { +public: + static void Activate( + not_null message, + int row, + int column); + +protected: + void prepare() override; + +private: + static void Request( + const MTPDurlAuthResultRequest &request, + not_null message, + int row, + int column); + + enum class Result { + None, + Auth, + AuthAndAllowWrite, + }; + +public: + UrlAuthBox( + QWidget*, + const QString &url, + const QString &domain, + UserData *bot, + Fn callback); + +private: + not_null setupContent( + const QString &url, + const QString &domain, + UserData *bot, + Fn callback); + + Fn _callback; + not_null _content; + +}; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index f9d0b4e1e3..193ae55141 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "auth_session.h" #include "boxes/confirm_box.h" +#include "boxes/url_auth_box.h" #include "window/layer_widget.h" #include "lang/lang_keys.h" #include "base/observer.h" @@ -62,15 +63,7 @@ void activateBotCommand( not_null msg, int row, int column) { - const HistoryMessageMarkupButton *button = nullptr; - if (auto markup = msg->Get()) { - if (row < markup->rows.size()) { - auto &buttonRow = markup->rows[row]; - if (column < buttonRow.size()) { - button = &buttonRow[column]; - } - } - } + const auto button = HistoryMessageMarkupButton::Get(msg->fullId(), row, column); if (!button) return; using ButtonType = HistoryMessageMarkupButton::Type; @@ -147,6 +140,10 @@ void activateBotCommand( } } } break; + + case ButtonType::Auth: + UrlAuthBox::Activate(msg, row, column); + break; } } diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 04f0873794..902a52e7a1 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -365,17 +365,7 @@ QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const { // Note: it is possible that we will point to the different button // than the one was used when constructing the handler, but not a big deal. const HistoryMessageMarkupButton *ReplyMarkupClickHandler::getButton() const { - if (const auto item = Auth().data().message(_itemId)) { - if (const auto markup = item->Get()) { - if (_row < markup->rows.size()) { - const auto &row = markup->rows[_row]; - if (_column < row.size()) { - return &row[_column]; - } - } - } - } - return nullptr; + return HistoryMessageMarkupButton::Get(_itemId, _row, _column); } void ReplyMarkupClickHandler::onClickImpl() const { @@ -730,66 +720,93 @@ void ReplyKeyboard::Style::paintButton( button.text.drawElided(p, tx, rect.y() + _st->textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top); } -void HistoryMessageReplyMarkup::createFromButtonRows(const QVector &v) { - if (v.isEmpty()) { - rows.clear(); +HistoryMessageMarkupButton::HistoryMessageMarkupButton( + Type type, + const QString &text, + const QByteArray &data, + int32 buttonId) +: type(type) +, text(text) +, data(data) +, buttonId(buttonId) { +} + +HistoryMessageMarkupButton *HistoryMessageMarkupButton::Get( + FullMsgId itemId, + int row, + int column) { + if (const auto item = Auth().data().message(itemId)) { + if (const auto markup = item->Get()) { + if (row < markup->rows.size()) { + auto &buttons = markup->rows[row]; + if (column < buttons.size()) { + return &buttons[column]; + } + } + } + } + return nullptr; +} + +void HistoryMessageReplyMarkup::createFromButtonRows( + const QVector &list) { + rows.clear(); + if (list.isEmpty()) { return; } - rows.reserve(v.size()); - for_const (auto &row, v) { - switch (row.type()) { - case mtpc_keyboardButtonRow: { - auto &r = row.c_keyboardButtonRow(); - auto &b = r.vbuttons.v; - if (!b.isEmpty()) { - auto buttonRow = std::vector