From 35dcbe0aa0f23b2bba01742ead802b50413c3f10 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 6 Apr 2018 22:47:29 +0400 Subject: [PATCH] Save value without closing the passport panel. --- Telegram/Resources/langs/lang.strings | 4 +- Telegram/SourceFiles/messenger.cpp | 44 ++++--- .../passport/passport_form_controller.cpp | 121 ++++++++++++------ .../passport/passport_form_controller.h | 11 +- .../passport/passport_panel_controller.cpp | 85 ++++++++---- .../passport/passport_panel_controller.h | 4 +- .../passport/passport_panel_edit_document.cpp | 11 +- .../passport/passport_panel_edit_document.h | 2 +- .../SourceFiles/window/window_controller.cpp | 12 +- .../SourceFiles/window/window_controller.h | 6 +- 10 files changed, 196 insertions(+), 104 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4c1e4db8c..37c0c79a4 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1520,8 +1520,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_passport_phone_description" = "Enter your phone number"; "lng_passport_email_title" = "Email"; "lng_passport_email_description" = "Enter your email address"; -"lng_passport_accept_allow" = "You accept {policy} and allow their {bot} to send messages to you."; -"lng_passport_allow" = "You allow {bot} to send messages to you."; +"lng_passport_accept_allow" = "You accept {policy} and allow their {bot} to send you messages."; +"lng_passport_allow" = "You allow {bot} to send you messages."; "lng_passport_policy" = "{bot} privacy policy"; "lng_passport_authorize" = "Authorize"; "lng_passport_form_error" = "Could not get authorization form."; diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 4c2e7edcf..441b29314 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -836,6 +836,25 @@ bool Messenger::openLocalUrl(const QString &url) { } auto command = urlTrimmed.midRef(qstr("tg://").size()); + const auto showPassportForm = [](const QMap ¶ms) { + if (const auto botId = params.value("bot_id", QString()).toInt()) { + const auto scope = params.value("scope", QString()); + const auto callback = params.value("callback_url", QString()); + const auto publicKey = params.value("public_key", QString()); + if (const auto window = App::wnd()) { + if (const auto controller = window->controller()) { + controller->showPassportForm(Passport::FormRequest( + botId, + scope, + callback, + publicKey)); + return true; + } + } + } + return false; + }; + using namespace qthelp; auto matchOptions = RegExOption::CaseInsensitive; if (auto joinChatMatch = regex_match(qsl("^join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"), command, matchOptions)) { @@ -871,7 +890,9 @@ bool Messenger::openLocalUrl(const QString &url) { if (auto main = App::main()) { auto params = url_parse_params(usernameMatch->captured(1), UrlParamNameTransform::ToLower); auto domain = params.value(qsl("domain")); - if (regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) { + if (domain == qsl("telegrampassport")) { + return showPassportForm(params); + } else if (regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) { auto start = qsl("start"); auto startToken = params.value(start); if (startToken.isEmpty()) { @@ -909,25 +930,10 @@ bool Messenger::openLocalUrl(const QString &url) { auto params = url_parse_params(proxyMatch->captured(1), UrlParamNameTransform::ToLower); ProxiesBoxController::ShowApplyConfirmation(ProxyData::Type::Mtproto, params); return true; - } else if (auto authMatch = regex_match(qsl("^secureid/?\\?(.+)(#|$)"), command, matchOptions)) { - const auto params = url_parse_params( + } else if (auto authMatch = regex_match(qsl("^passport/?\\?(.+)(#|$)"), command, matchOptions)) { + return showPassportForm(url_parse_params( authMatch->captured(1), - UrlParamNameTransform::ToLower); - if (const auto botId = params.value("bot_id", QString()).toInt()) { - const auto scope = params.value("scope", QString()); - const auto callback = params.value("callback_url", QString()); - const auto publicKey = params.value("public_key", QString()); - if (const auto window = App::wnd()) { - if (const auto controller = window->controller()) { - controller->showAuthForm(Passport::FormRequest( - botId, - scope, - callback, - publicKey)); - return true; - } - } - } + UrlParamNameTransform::ToLower)); } return false; } diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index c76c0410b..5ba68a517 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -200,36 +200,36 @@ void FormController::decryptValues() { Expects(!_secret.empty()); for (auto &[type, value] : _form.values) { - if (value.data.original.isEmpty()) { - continue; - } decryptValue(value); } } void FormController::decryptValue(Value &value) { Expects(!_secret.empty()); - Expects(!value.data.original.isEmpty()); if (!validateValueSecrets(value)) { resetValue(value); return; } - value.data.parsed.fields = DeserializeData(DecryptData( - bytes::make_span(value.data.original), - value.data.hash, - value.data.secret)); + if (!value.data.original.isEmpty()) { + value.data.parsed.fields = DeserializeData(DecryptData( + bytes::make_span(value.data.original), + value.data.hash, + value.data.secret)); + } } bool FormController::validateValueSecrets(Value &value) { - value.data.secret = DecryptValueSecret( - value.data.encryptedSecret, - _secret, - value.data.hash); - if (value.data.secret.empty()) { - LOG(("API Error: Could not decrypt data secret. " - "Forgetting files and data :(")); - return false; + if (!value.data.original.isEmpty()) { + value.data.secret = DecryptValueSecret( + value.data.encryptedSecret, + _secret, + value.data.hash); + if (value.data.secret.empty()) { + LOG(("API Error: Could not decrypt data secret. " + "Forgetting files and data :(")); + return false; + } } for (auto &file : value.files) { file.secret = DecryptValueSecret( @@ -451,6 +451,16 @@ auto FormController::scanUpdated() const return _scanUpdated.events(); } +auto FormController::valueSaved() const +->rpl::producer> { + return _valueSaved.events(); +} + +auto FormController::verificationNeeded() const +->rpl::producer> { + return _verificationNeeded.events(); +} + const Form &FormController::form() const { return _form; } @@ -465,6 +475,9 @@ not_null FormController::findValue(not_null value) { } void FormController::startValueEdit(not_null value) { + if (value->saveRequestId) { + return; + } const auto nonconst = findValue(value); loadFiles(nonconst->files); nonconst->filesInEdit = ranges::view::all( @@ -472,6 +485,7 @@ void FormController::startValueEdit(not_null value) { ) | ranges::view::transform([=](const File &file) { return EditFile(value, file, nullptr); }) | ranges::to_vector; + nonconst->data.parsedInEdit = nonconst->data.parsed; } void FormController::loadFiles(std::vector &files) { @@ -557,8 +571,14 @@ void FormController::fileLoadFail(FileKey key) { } void FormController::cancelValueEdit(not_null value) { + if (value->saveRequestId) { + return; + } const auto nonconst = findValue(value); nonconst->filesInEdit.clear(); + nonconst->data.encryptedSecretInEdit.clear(); + nonconst->data.hashInEdit.clear(); + nonconst->data.parsedInEdit = ValueMap(); } bool FormController::isEncryptedValue(Value::Type type) const { @@ -568,8 +588,11 @@ bool FormController::isEncryptedValue(Value::Type type) const { void FormController::saveValueEdit( not_null value, ValueMap &&data) { + if (value->saveRequestId) { + return; + } const auto nonconst = findValue(value); - nonconst->data.parsed = std::move(data); + nonconst->data.parsedInEdit = std::move(data); if (isEncryptedValue(nonconst->type)) { saveEncryptedValue(nonconst); @@ -616,13 +639,13 @@ void FormController::saveEncryptedValue(not_null value) { value->data.secret = GenerateSecretBytes(); } const auto encryptedData = EncryptData( - SerializeData(value->data.parsed.fields), + SerializeData(value->data.parsedInEdit.fields), value->data.secret); - value->data.hash = encryptedData.hash; - value->data.encryptedSecret = EncryptValueSecret( + value->data.hashInEdit = encryptedData.hash; + value->data.encryptedSecretInEdit = EncryptValueSecret( value->data.secret, _secret, - value->data.hash); + value->data.hashInEdit); const auto selfie = value->selfieInEdit ? inputFile(*value->selfieInEdit) @@ -650,7 +673,7 @@ void FormController::saveEncryptedValue(not_null value) { Unexpected("Value type in saveEncryptedValue()."); }(); const auto flags = ((value->filesInEdit.empty() - && value->data.parsed.fields.empty()) + && value->data.parsedInEdit.fields.empty()) ? MTPDinputSecureValue::Flag(0) : MTPDinputSecureValue::Flag::f_data) | (value->filesInEdit.empty() @@ -659,26 +682,24 @@ void FormController::saveEncryptedValue(not_null value) { | (value->selfieInEdit ? MTPDinputSecureValue::Flag::f_selfie : MTPDinputSecureValue::Flag(0)); - if (!flags) { - request(MTPaccount_DeleteSecureValue(MTP_vector(1, type))).send(); - } else { - sendSaveRequest(value, MTP_inputSecureValue( - MTP_flags(flags), - type, - MTP_secureData( - MTP_bytes(encryptedData.bytes), - MTP_bytes(value->data.hash), - MTP_bytes(value->data.encryptedSecret)), - MTP_vector(inputFiles), - MTPSecurePlainData(), - selfie)); - } + Assert(flags != MTPDinputSecureValue::Flags(0)); + + sendSaveRequest(value, MTP_inputSecureValue( + MTP_flags(flags), + type, + MTP_secureData( + MTP_bytes(encryptedData.bytes), + MTP_bytes(value->data.hashInEdit), + MTP_bytes(value->data.encryptedSecretInEdit)), + MTP_vector(inputFiles), + MTPSecurePlainData(), + selfie)); } void FormController::savePlainTextValue(not_null value) { Expects(!isEncryptedValue(value->type)); - const auto text = value->data.parsed.fields[QString("value")]; + const auto text = value->data.parsedInEdit.fields["value"]; const auto type = [&] { switch (value->type) { case Value::Type::Phone: return MTP_secureValueTypePhone(); @@ -705,7 +726,9 @@ void FormController::savePlainTextValue(not_null value) { void FormController::sendSaveRequest( not_null value, const MTPInputSecureValue &data) { - request(MTPaccount_SaveSecureValue( + Expects(value->saveRequestId == 0); + + value->saveRequestId = request(MTPaccount_SaveSecureValue( data, MTP_long(_secretId) )).done([=](const MTPSecureValue &result) { @@ -715,9 +738,27 @@ void FormController::sendSaveRequest( value->files = parseFiles( data.vfiles.v, base::take(value->filesInEdit)); + value->data.encryptedSecret = std::move( + value->data.encryptedSecretInEdit); + value->data.parsed = std::move(value->data.parsedInEdit); + value->data.hash = std::move(value->data.hashInEdit); - _view->show(Box("Saved")); + value->saveRequestId = 0; + + _valueSaved.fire_copy(value); }).fail([=](const RPCError &error) { + value->saveRequestId = 0; + if (error.type() == qstr("PHONE_VERIFICATION_NEEDED")) { + if (value->type == Value::Type::Phone) { + _verificationNeeded.fire_copy(value); + return; + } + } else if (error.type() == qstr("EMAIL_VERIFICATION_NEEDED")) { + if (value->type == Value::Type::Email) { + _verificationNeeded.fire_copy(value); + return; + } + } _view->show(Box("Error saving value:\n" + error.type())); }).send(); } @@ -1019,7 +1060,7 @@ void FormController::cancel() { if (!_cancelled) { _cancelled = true; crl::on_main(this, [=] { - _controller->clearAuthForm(); + _controller->clearPassportForm(); }); } } diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index 6bfce5115..103e09742 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -102,10 +102,13 @@ struct ValueMap { struct ValueData { QByteArray original; + bytes::vector secret; ValueMap parsed; bytes::vector hash; - bytes::vector secret; bytes::vector encryptedSecret; + ValueMap parsedInEdit; + bytes::vector hashInEdit; + bytes::vector encryptedSecretInEdit; }; struct Value { @@ -132,6 +135,8 @@ struct Value { std::vector filesInEdit; base::optional selfie; base::optional selfieInEdit; + mtpRequestId saveRequestId = 0; + }; struct Form { @@ -200,6 +205,8 @@ public: QString defaultPhoneNumber() const; rpl::producer> scanUpdated() const; + rpl::producer> valueSaved() const; + rpl::producer> verificationNeeded() const; const Form &form() const; void startValueEdit(not_null value); @@ -288,6 +295,8 @@ private: std::map> _fileLoaders; rpl::event_stream> _scanUpdated; + rpl::event_stream> _valueSaved; + rpl::event_stream> _verificationNeeded; bytes::vector _secret; uint64 _secretId = 0; diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index c6ab3a093..e9eded87f 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -423,42 +423,77 @@ void PanelController::editScope(int index) { _panel.get(), this, std::move(GetDocumentScheme(_editScope->type)), - _editScope->fields->data.parsed, - _editScope->files[_editScopeFilesIndex]->data.parsed, + _editScope->fields->data.parsedInEdit, + _editScope->files[_editScopeFilesIndex]->data.parsedInEdit, valueFiles(*_editScope->files[_editScopeFilesIndex])) : object_ptr( _panel.get(), this, std::move(GetDocumentScheme(_editScope->type)), - _editScope->fields->data.parsed); + _editScope->fields->data.parsedInEdit); case Scope::Type::Phone: case Scope::Type::Email: { - const auto &fields = _editScope->fields->data.parsed.fields; - const auto valueIt = fields.find("value"); + const auto &parsed = _editScope->fields->data.parsedInEdit; + const auto valueIt = parsed.fields.find("value"); return object_ptr( _panel.get(), this, std::move(GetContactScheme(_editScope->type)), - (valueIt == end(fields)) ? QString() : valueIt->second, + (valueIt == end(parsed.fields) + ? QString() + : valueIt->second), getDefaultContactValue(_editScope->type)); } break; } Unexpected("Type in PanelController::editScope()."); }(); - if (content) { - _panel->setBackAllowed(true); - _panel->backRequests( - ) | rpl::start_with_next([=] { - cancelValueEdit(index); + + _panel->setBackAllowed(true); + + _panel->backRequests( + ) | rpl::start_with_next([=] { + _panel->showForm(); + }, content->lifetime()); + + content->lifetime().add([=] { + cancelValueEdit(); + }); + + _form->valueSaved( + ) | rpl::start_with_next([=](not_null value) { + processValueSaved(value); + }, content->lifetime()); + + _form->verificationNeeded( + ) | rpl::start_with_next([=](not_null value) { + processVerificationNeeded(value); + }, content->lifetime()); + + _panel->showEditValue(std::move(content)); +} + +void PanelController::processValueSaved(not_null value) { + Expects(_editScope != nullptr); + + const auto value1 = _editScope->fields; + const auto value2 = (_editScopeFilesIndex >= 0) + ? _editScope->files[_editScopeFilesIndex].get() + : nullptr; + if (value == value1 || value == value2) { + if (!value1->saveRequestId && (!value2 || !value2->saveRequestId)) { _panel->showForm(); - }, content->lifetime()); - _panel->showEditValue(std::move(content)); - } else { - cancelValueEdit(index); + show(Box("Saved")); + } } } -std::vector PanelController::valueFiles(const Value &value) const { +void PanelController::processVerificationNeeded( + not_null value) { + show(Box("Verification needed :(")); +} + +std::vector PanelController::valueFiles( + const Value &value) const { auto result = std::vector(); for (const auto &file : value.filesInEdit) { result.push_back(collectScanInfo(file)); @@ -466,12 +501,12 @@ std::vector PanelController::valueFiles(const Value &value) const { return result; } -void PanelController::cancelValueEdit(int index) { +void PanelController::cancelValueEdit() { if (const auto scope = base::take(_editScope)) { - _form->startValueEdit(scope->fields); + _form->cancelValueEdit(scope->fields); const auto index = std::exchange(_editScopeFilesIndex, -1); if (index >= 0) { - _form->startValueEdit(scope->files[index]); + _form->cancelValueEdit(scope->files[index]); } } } @@ -480,16 +515,14 @@ void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) { Expects(_panel != nullptr); Expects(_editScope != nullptr); - const auto scope = base::take(_editScope); - _form->saveValueEdit(scope->fields, std::move(data)); - const auto index = std::exchange(_editScopeFilesIndex, -1); - if (index >= 0) { - _form->saveValueEdit(scope->files[index], std::move(filesData)); + _form->saveValueEdit(_editScope->fields, std::move(data)); + if (_editScopeFilesIndex >= 0) { + _form->saveValueEdit( + _editScope->files[_editScopeFilesIndex], + std::move(filesData)); } else { Assert(filesData.fields.empty()); } - - _panel->showForm(); } void PanelController::cancelAuth() { diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.h b/Telegram/SourceFiles/passport/passport_panel_controller.h index 54c375d1d..fd111feca 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.h +++ b/Telegram/SourceFiles/passport/passport_panel_controller.h @@ -81,8 +81,10 @@ public: private: void ensurePanelCreated(); - void cancelValueEdit(int index); + void cancelValueEdit(); std::vector valueFiles(const Value &value) const; + void processValueSaved(not_null value); + void processVerificationNeeded(not_null value); ScanInfo collectScanInfo(const EditFile &file) const; QString getDefaultContactValue(Scope::Type type) const; diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp index dbe062f09..7111459c6 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp @@ -115,14 +115,15 @@ not_null PanelEditDocument::setupContent( return QString(); }; - for (const auto &row : _scheme.rows) { + for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) { + const auto &row = _scheme.rows[i]; auto fields = (row.type == Scheme::ValueType::Fields) ? &data : scanData; if (!fields) { continue; } - _details.push_back(inner->add(object_ptr( + _details.emplace(i, inner->add(object_ptr( inner, row.label, valueOrEmpty(*fields, row.key)))); @@ -132,7 +133,7 @@ not_null PanelEditDocument::setupContent( } void PanelEditDocument::focusInEvent(QFocusEvent *e) { - for (const auto row : _details) { + for (const auto [index, row] : _details) { if (row->setFocusFast()) { return; } @@ -157,11 +158,9 @@ void PanelEditDocument::updateControlsGeometry() { } void PanelEditDocument::save() { - Expects(_details.size() == _scheme.rows.size()); - auto data = ValueMap(); auto scanData = ValueMap(); - for (auto i = 0, count = int(_details.size()); i != count; ++i) { + for (const auto [i, field] : _details) { const auto &row = _scheme.rows[i]; auto &fields = (row.type == Scheme::ValueType::Fields) ? data diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.h b/Telegram/SourceFiles/passport/passport_panel_edit_document.h index 06db0d2ca..aadeca619 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.h @@ -81,7 +81,7 @@ private: object_ptr _bottomShadow; QPointer _editScans; - std::vector> _details; + std::map> _details; object_ptr _done; diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 9abc21f76..4e9e48752 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -405,13 +405,15 @@ void Controller::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { Ui::show(std::move(box)); } -void Controller::showAuthForm(const Passport::FormRequest &request) { - _authForm = std::make_unique(this, request); - _authForm->show(); +void Controller::showPassportForm(const Passport::FormRequest &request) { + _passportForm = std::make_unique( + this, + request); + _passportForm->show(); } -void Controller::clearAuthForm() { - _authForm = nullptr; +void Controller::clearPassportForm() { + _passportForm = nullptr; } void Controller::updateColumnLayout() { diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index eeae7ae22..e286e33f4 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -205,8 +205,8 @@ public: Dialogs::Key chat, QDate requestedDate); - void showAuthForm(const Passport::FormRequest &request); - void clearAuthForm(); + void showPassportForm(const Passport::FormRequest &request); + void clearPassportForm(); base::Variable &dialogsListFocused() { return _dialogsListFocused; @@ -254,7 +254,7 @@ private: not_null _window; - std::unique_ptr _authForm; + std::unique_ptr _passportForm; GifPauseReasons _gifPauseReasons = 0; base::Observable _gifPauseLevelChanged;