diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index b1ea783c3b..55f6326e04 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -114,6 +114,8 @@ PRIVATE api/api_cloud_password.cpp api/api_cloud_password.h api/api_common.h + api/api_confirm_phone.cpp + api/api_confirm_phone.h api/api_editing.cpp api/api_editing.h api/api_global_privacy.cpp diff --git a/Telegram/SourceFiles/api/api_confirm_phone.cpp b/Telegram/SourceFiles/api/api_confirm_phone.cpp new file mode 100644 index 0000000000..a8960ef085 --- /dev/null +++ b/Telegram/SourceFiles/api/api_confirm_phone.cpp @@ -0,0 +1,125 @@ +/* +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 "api/api_confirm_phone.h" + +#include "apiwrap.h" +#include "boxes/confirm_box.h" +#include "boxes/confirm_phone_box.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "ui/text/format_values.h" // Ui::FormatPhone +#include "window/window_session_controller.h" + +namespace Api { + +ConfirmPhone::ConfirmPhone(not_null api) +: _api(&api->instance()) { +} + +void ConfirmPhone::resolve( + not_null controller, + const QString &phone, + const QString &hash) { + if (_sendRequestId) { + return; + } + _sendRequestId = _api.request(MTPaccount_SendConfirmPhoneCode( + MTP_string(hash), + MTP_codeSettings(MTP_flags(0)) + )).done([=](const MTPauth_SentCode &result) { + _sendRequestId = 0; + + result.match([&](const MTPDauth_sentCode &data) { + const auto sentCodeLength = data.vtype().match([&]( + const MTPDauth_sentCodeTypeApp &data) { + LOG(("Error: should not be in-app code!")); + return 0; + }, [&](const MTPDauth_sentCodeTypeSms &data) { + return data.vlength().v; + }, [&](const MTPDauth_sentCodeTypeCall &data) { + return data.vlength().v; + }, [&](const MTPDauth_sentCodeTypeFlashCall &data) { + LOG(("Error: should not be flashcall!")); + return 0; + }); + const auto phoneHash = qs(data.vphone_code_hash()); + const auto timeout = [&]() -> std::optional { + if (const auto nextType = data.vnext_type()) { + if (nextType->type() == mtpc_auth_codeTypeCall) { + return data.vtimeout().value_or(60); + } + } + return std::nullopt; + }(); + auto box = Box( + phone, + sentCodeLength, + timeout); + const auto boxWeak = Ui::MakeWeak(box.data()); + box->resendRequests( + ) | rpl::start_with_next([=] { + _api.request(MTPauth_ResendCode( + MTP_string(phone), + MTP_string(phoneHash) + )).done([=](const MTPauth_SentCode &result) { + if (boxWeak) { + boxWeak->callDone(); + } + }).send(); + }, box->lifetime()); + box->checkRequests( + ) | rpl::start_with_next([=](const QString &code) { + if (_checkRequestId) { + return; + } + _checkRequestId = _api.request(MTPaccount_ConfirmPhone( + MTP_string(phoneHash), + MTP_string(code) + )).done([=](const MTPBool &result) { + _checkRequestId = 0; + controller->show( + Box( + tr::lng_confirm_phone_success( + tr::now, + lt_phone, + Ui::FormatPhone(phone))), + Ui::LayerOption::CloseOther); + }).fail([=](const MTP::Error &error) { + _checkRequestId = 0; + if (!boxWeak) { + return; + } + + const auto errorText = MTP::IsFloodError(error) + ? tr::lng_flood_error(tr::now) + : (error.type() == (u"PHONE_CODE_EMPTY"_q) + || error.type() == (u"PHONE_CODE_INVALID"_q)) + ? tr::lng_bad_code(tr::now) + : Lang::Hard::ServerError(); + boxWeak->showServerError(errorText); + }).handleFloodErrors().send(); + }, box->lifetime()); + + controller->show(std::move(box), Ui::LayerOption::CloseOther); + }); + }).fail([=](const MTP::Error &error) { + _sendRequestId = 0; + _checkRequestId = 0; + + const auto errorText = MTP::IsFloodError(error) + ? tr::lng_flood_error(tr::now) + : (error.code() == 400) + ? tr::lng_confirm_phone_link_invalid(tr::now) + : Lang::Hard::ServerError(); + controller->show( + Box(errorText), + Ui::LayerOption::CloseOther); + }).handleFloodErrors().send(); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_confirm_phone.h b/Telegram/SourceFiles/api/api_confirm_phone.h new file mode 100644 index 0000000000..24108b17ae --- /dev/null +++ b/Telegram/SourceFiles/api/api_confirm_phone.h @@ -0,0 +1,36 @@ +/* +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 "mtproto/sender.h" + +class ApiWrap; + +namespace Window { +class SessionController; +} // namespace Window + +namespace Api { + +class ConfirmPhone final { +public: + explicit ConfirmPhone(not_null api); + + void resolve( + not_null controller, + const QString &phone, + const QString &hash); + +private: + MTP::Sender _api; + mtpRequestId _sendRequestId = 0; + mtpRequestId _checkRequestId = 0; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 90ad696d53..32c21ce3d3 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_updates.h" #include "api/api_user_privacy.h" #include "api/api_views.h" +#include "api/api_confirm_phone.h" #include "data/stickers/data_stickers.h" #include "data/data_drafts.h" #include "data/data_changes.h" @@ -143,7 +144,8 @@ ApiWrap::ApiWrap(not_null session) , _globalPrivacy(std::make_unique(this)) , _userPrivacy(std::make_unique(this)) , _inviteLinks(std::make_unique(this)) -, _views(std::make_unique(this)) { +, _views(std::make_unique(this)) +, _confirmPhone(std::make_unique(this)) { crl::on_main(session, [=] { // You can't use _session->lifetime() in the constructor, // only queued, because it is not constructed yet. @@ -4703,6 +4705,10 @@ Api::ViewsManager &ApiWrap::views() { return *_views; } +Api::ConfirmPhone &ApiWrap::confirmPhone() { + return *_confirmPhone; +} + void ApiWrap::createPoll( const PollData &data, const SendAction &action, diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index ead199a49d..c555c4e284 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -63,6 +63,7 @@ class GlobalPrivacy; class UserPrivacy; class InviteLinks; class ViewsManager; +class ConfirmPhone; namespace details { @@ -402,6 +403,7 @@ public: [[nodiscard]] Api::UserPrivacy &userPrivacy(); [[nodiscard]] Api::InviteLinks &inviteLinks(); [[nodiscard]] Api::ViewsManager &views(); + [[nodiscard]] Api::ConfirmPhone &confirmPhone(); void createPoll( const PollData &data, @@ -723,6 +725,7 @@ private: const std::unique_ptr _userPrivacy; const std::unique_ptr _inviteLinks; const std::unique_ptr _views; + const std::unique_ptr _confirmPhone; base::flat_map _pollVotesRequestIds; base::flat_map _pollCloseRequestIds; diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp index 7aa5453a08..32bb76ffb8 100644 --- a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp @@ -9,119 +9,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "ui/widgets/buttons.h" -#include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/text/format_values.h" // Ui::FormatPhone #include "ui/text/text_utilities.h" -#include "core/click_handler_types.h" // UrlClickHandler -#include "base/qthelp_url.h" // qthelp::url_encode -#include "base/platform/base_platform_info.h" -#include "main/main_session.h" -#include "mainwidget.h" #include "lang/lang_keys.h" -#include "mtproto/facade.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" -namespace { - -object_ptr CurrentConfirmPhoneBox = { nullptr }; - -} // namespace - -void ConfirmPhoneBox::Start( - not_null session, - const QString &phone, - const QString &hash) { - if (CurrentConfirmPhoneBox - && (CurrentConfirmPhoneBox->getPhone() != phone - || &CurrentConfirmPhoneBox->session() != session)) { - CurrentConfirmPhoneBox.destroyDelayed(); - } - if (!CurrentConfirmPhoneBox) { - CurrentConfirmPhoneBox = Box(session, phone, hash); - } - CurrentConfirmPhoneBox->checkPhoneAndHash(); -} - ConfirmPhoneBox::ConfirmPhoneBox( QWidget*, - not_null session, const QString &phone, - const QString &hash) -: _session(session) -, _api(&session->mtp()) -, _phone(phone) -, _hash(hash) + int codeLength, + std::optional timeout) +: _phone(phone) +, _sentCodeLength(codeLength) , _call([this] { sendCall(); }, [this] { update(); }) { + if (timeout) { + _call.setStatus({ Ui::SentCodeCall::State::Waiting, *timeout }); + } } void ConfirmPhoneBox::sendCall() { - _api.request(MTPauth_ResendCode( - MTP_string(_phone), - MTP_string(_phoneHash) - )).done([=](const MTPauth_SentCode &result) { - callDone(result); - }).send(); -} - -void ConfirmPhoneBox::checkPhoneAndHash() { - if (_sendCodeRequestId) { - return; - } - _sendCodeRequestId = _api.request(MTPaccount_SendConfirmPhoneCode( - MTP_string(_hash), - MTP_codeSettings(MTP_flags(0)) - )).done([=](const MTPauth_SentCode &result) { - sendCodeDone(result); - }).fail([=](const MTP::Error &error) { - sendCodeFail(error); - }).handleFloodErrors().send(); -} - -void ConfirmPhoneBox::sendCodeDone(const MTPauth_SentCode &result) { - result.match([&](const MTPDauth_sentCode &data) { - _sendCodeRequestId = 0; - _sentCodeLength = data.vtype().match([&](const MTPDauth_sentCodeTypeApp &data) { - LOG(("Error: should not be in-app code!")); - return 0; - }, [&](const MTPDauth_sentCodeTypeSms &data) { - return data.vlength().v; - }, [&](const MTPDauth_sentCodeTypeCall &data) { - return data.vlength().v; - }, [&](const MTPDauth_sentCodeTypeFlashCall &data) { - LOG(("Error: should not be flashcall!")); - return 0; - }); - _phoneHash = qs(data.vphone_code_hash()); - if (const auto nextType = data.vnext_type()) { - if (nextType->type() == mtpc_auth_codeTypeCall) { - _call.setStatus({ Ui::SentCodeCall::State::Waiting, data.vtimeout().value_or(60) }); - } - } - launch(); - }); -} - -void ConfirmPhoneBox::sendCodeFail(const MTP::Error &error) { - auto errorText = Lang::Hard::ServerError(); - if (MTP::IsFloodError(error)) { - errorText = tr::lng_flood_error(tr::now); - } else if (error.code() == 400) { - errorText = tr::lng_confirm_phone_link_invalid(tr::now); - } - _sendCodeRequestId = 0; - Ui::show(Box(errorText)); - if (this == CurrentConfirmPhoneBox) { - CurrentConfirmPhoneBox.destroyDelayed(); - } else { - deleteLater(); - } -} - -void ConfirmPhoneBox::launch() { - if (!CurrentConfirmPhoneBox) return; - Ui::show(std::move(CurrentConfirmPhoneBox)); + _resendRequests.fire({}); } void ConfirmPhoneBox::prepare() { @@ -142,19 +51,21 @@ void ConfirmPhoneBox::prepare() { addButton(tr::lng_confirm_phone_send(), [=] { sendCode(); }); addButton(tr::lng_cancel(), [=] { closeBox(); }); - setDimensions(st::boxWidth, st::usernamePadding.top() + _code->height() + st::usernameSkip + _about->height() + st::usernameSkip); + setDimensions( + st::boxWidth, + st::usernamePadding.top() + + _code->height() + + st::usernameSkip + + _about->height() + + st::usernameSkip); connect(_code, &Ui::InputField::submitted, [=] { sendCode(); }); showChildren(); } -void ConfirmPhoneBox::callDone(const MTPauth_SentCode &result) { - _call.callDone(); -} - void ConfirmPhoneBox::sendCode() { - if (_sendCodeRequestId) { + if (_isWaitingCheck) { return; } const auto code = _code->getDigitsOnly(); @@ -168,35 +79,8 @@ void ConfirmPhoneBox::sendCode() { showError(QString()); - _sendCodeRequestId = _api.request(MTPaccount_ConfirmPhone( - MTP_string(_phoneHash), - MTP_string(code) - )).done([=](const MTPBool &result) { - confirmDone(result); - }).fail([=](const MTP::Error &error) { - confirmFail(error); - }).handleFloodErrors().send(); -} - -void ConfirmPhoneBox::confirmDone(const MTPBool &result) { - _sendCodeRequestId = 0; - Ui::show(Box(tr::lng_confirm_phone_success(tr::now, lt_phone, Ui::FormatPhone(_phone)))); -} - -void ConfirmPhoneBox::confirmFail(const MTP::Error &error) { - auto errorText = Lang::Hard::ServerError(); - if (MTP::IsFloodError(error)) { - errorText = tr::lng_flood_error(tr::now); - } else { - auto &errorType = error.type(); - if (errorType == qstr("PHONE_CODE_EMPTY") || errorType == qstr("PHONE_CODE_INVALID")) { - errorText = tr::lng_bad_code(tr::now); - } - } - _sendCodeRequestId = 0; - _code->setDisabled(false); - _code->setFocus(); - showError(errorText); + _checkRequests.fire_copy(code); + _isWaitingCheck = true; } void ConfirmPhoneBox::showError(const QString &error) { @@ -213,13 +97,14 @@ void ConfirmPhoneBox::paintEvent(QPaintEvent *e) { Painter p(this); p.setFont(st::boxTextFont); - auto callText = _call.getText(); + const auto callText = _call.getText(); if (!callText.isEmpty()) { p.setPen(st::usernameDefaultFg); - auto callTextRectLeft = st::usernamePadding.left(); - auto callTextRectTop = _about->y() + _about->height(); - auto callTextRectWidth = width() - 2 * st::usernamePadding.left(); - auto callTextRect = QRect(callTextRectLeft, callTextRectTop, callTextRectWidth, st::usernameSkip); + const auto callTextRect = QRect( + st::usernamePadding.left(), + _about->y() + _about->height(), + width() - 2 * st::usernamePadding.left(), + st::usernameSkip); p.drawText(callTextRect, callText, style::al_left); } auto errorText = _error; @@ -229,22 +114,50 @@ void ConfirmPhoneBox::paintEvent(QPaintEvent *e) { } else { p.setPen(st::boxTextFgError); } - auto errorTextRectLeft = st::usernamePadding.left(); - auto errorTextRectTop = _code->y() + _code->height(); - auto errorTextRectWidth = width() - 2 * st::usernamePadding.left(); - auto errorTextRect = QRect(errorTextRectLeft, errorTextRectTop, errorTextRectWidth, st::usernameSkip); + const auto errorTextRect = QRect( + st::usernamePadding.left(), + _code->y() + _code->height(), + width() - 2 * st::usernamePadding.left(), + st::usernameSkip); p.drawText(errorTextRect, errorText, style::al_left); } void ConfirmPhoneBox::resizeEvent(QResizeEvent *e) { BoxContent::resizeEvent(e); - _code->resize(width() - st::usernamePadding.left() - st::usernamePadding.right(), _code->height()); + _code->resize( + width() - st::usernamePadding.left() - st::usernamePadding.right(), + _code->height()); _code->moveToLeft(st::usernamePadding.left(), st::usernamePadding.top()); - _about->moveToLeft(st::usernamePadding.left(), _code->y() + _code->height() + st::usernameSkip); + _about->moveToLeft( + st::usernamePadding.left(), + _code->y() + _code->height() + st::usernameSkip); } void ConfirmPhoneBox::setInnerFocus() { _code->setFocusFast(); } + +rpl::producer ConfirmPhoneBox::checkRequests() const { + return _checkRequests.events(); +} + +rpl::producer<> ConfirmPhoneBox::resendRequests() const { + return _resendRequests.events(); +} + +void ConfirmPhoneBox::callDone() { + _call.callDone(); +} + +void ConfirmPhoneBox::showServerError(const QString &text) { + _isWaitingCheck = false; + _code->setDisabled(false); + _code->setFocus(); + showError(text); +} + +QString ConfirmPhoneBox::getPhone() const { + return _phone; +} diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.h b/Telegram/SourceFiles/boxes/confirm_phone_box.h index fc21671a91..67ff3b73ac 100644 --- a/Telegram/SourceFiles/boxes/confirm_phone_box.h +++ b/Telegram/SourceFiles/boxes/confirm_phone_box.h @@ -8,30 +8,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "boxes/abstract_box.h" -#include "base/timer.h" -#include "ui/widgets/input_fields.h" #include "ui/widgets/sent_code_field.h" -#include "mtproto/sender.h" namespace Ui { -class InputField; class FlatLabel; } // namespace Ui -namespace Main { -class Session; -} // namespace Main - class ConfirmPhoneBox final : public Ui::BoxContent { public: - static void Start( - not_null session, + ConfirmPhoneBox( + QWidget*, const QString &phone, - const QString &hash); + int codeLength, + std::optional timeout); - [[nodiscard]] Main::Session &session() const { - return *_session; - } + [[nodiscard]] rpl::producer checkRequests() const; + [[nodiscard]] rpl::producer<> resendRequests() const; + + void callDone(); + void showServerError(const QString &text); protected: void prepare() override; @@ -41,45 +36,21 @@ protected: void resizeEvent(QResizeEvent *e) override; private: - ConfirmPhoneBox( - QWidget*, - not_null session, - const QString &phone, - const QString &hash); - friend class object_ptr; - void sendCode(); void sendCall(); void checkPhoneAndHash(); - void sendCodeDone(const MTPauth_SentCode &result); - void sendCodeFail(const MTP::Error &error); - - void callDone(const MTPauth_SentCode &result); - - void confirmDone(const MTPBool &result); - void confirmFail(const MTP::Error &error); - - QString getPhone() const { - return _phone; - } - void launch(); - + QString getPhone() const; void showError(const QString &error); - const not_null _session; - MTP::Sender _api; - mtpRequestId _sendCodeRequestId = 0; - // _hash from the link for account.sendConfirmPhoneCode call. // _phoneHash from auth.sentCode for account.confirmPhone call. - QString _phone, _hash; - QString _phoneHash; + const QString _phone; // If we receive the code length, we autosubmit _code field when enough symbols is typed. - int _sentCodeLength = 0; + const int _sentCodeLength = 0; - mtpRequestId _checkCodeRequestId = 0; + bool _isWaitingCheck = false; object_ptr _about = { nullptr }; object_ptr _code = { nullptr }; @@ -87,4 +58,7 @@ private: QString _error; Ui::SentCodeCall _call; + rpl::event_stream _checkRequests; + rpl::event_stream<> _resendRequests; + }; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index b186e8ac0e..35d556f7a7 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/local_url_handlers.h" #include "api/api_authorizations.h" +#include "api/api_confirm_phone.h" #include "api/api_text_entities.h" #include "api/api_chat_invite.h" #include "base/qthelp_regex.h" @@ -136,15 +137,18 @@ bool ConfirmPhone( if (!controller) { return false; } - auto params = url_parse_params( + const auto params = url_parse_params( match->captured(1), qthelp::UrlParamNameTransform::ToLower); - auto phone = params.value(qsl("phone")); - auto hash = params.value(qsl("hash")); + const auto phone = params.value(qsl("phone")); + const auto hash = params.value(qsl("hash")); if (phone.isEmpty() || hash.isEmpty()) { return false; } - ConfirmPhoneBox::Start(&controller->session(), phone, hash); + controller->session().api().confirmPhone().resolve( + controller, + phone, + hash); return true; }