diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ff2b34db83..4eb1d8230f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1048,6 +1048,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_channels_too_much_public_revoke_confirm_group" = "Are you sure you want to revoke the link {link}?\n\nThe group «{group}» will become private."; "lng_channels_too_much_public_revoke_confirm_channel" = "Are you sure you want to revoke the link {link}?\n\nThe channel «{group}» will become private."; "lng_channels_too_much_public_revoke" = "Revoke"; +"lng_channels_too_much_public_other" = "Sorry, the target user has too many public groups or channels already. Please ask them to make one of their existing groups or channels private first."; "lng_group_invite_bad_link" = "This invite link is broken or has expired."; "lng_group_invite_join" = "Join"; @@ -1661,7 +1662,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_rights_transfer_set_password" = "Set password"; "lng_rights_transfer_about" = "This will transfer the full **owner rights** for {group} to {user}."; "lng_rights_transfer_sure" = "Change owner"; -"lng_rights_transfer_password" = "Please enter your password to complete the transfer."; +"lng_rights_transfer_password_title" = "Two-step verification"; +"lng_rights_transfer_password_description" = "Please enter your password to complete the transfer."; "lng_rights_transfer_done_group" = "{user} is now the owner of the group."; "lng_rights_transfer_done_channel" = "{user} is now the owner of the channel."; diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 1d5c619ded..18bee19e4d 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -88,12 +88,12 @@ bool PasscodeBox::currentlyHave() const { } bool PasscodeBox::onlyCheckCurrent() const { - return _turningOff || _cloudFields.customCheckedCallback; + return _turningOff || _cloudFields.customCheckCallback; } void PasscodeBox::prepare() { addButton([=] { - return _cloudFields.button.value_or(lang(_turningOff + return _cloudFields.customSubmitButton.value_or(lang(_turningOff ? lng_passcode_remove_button : lng_settings_save)); }, [=] { save(); }); @@ -101,7 +101,7 @@ void PasscodeBox::prepare() { _about.setText( st::passcodeTextStyle, - _cloudFields.description.value_or(lang(_cloudPwd + _cloudFields.customDescription.value_or(lang(_cloudPwd ? lng_cloud_password_about : lng_passcode_about))); _aboutHeight = _about.countHeight(st::boxWidth - st::boxPadding.left() * 1.5); @@ -109,7 +109,7 @@ void PasscodeBox::prepare() { if (onlyCheck) { _oldPasscode->show(); setTitle([=] { - return _cloudFields.title.value_or(lang(_cloudPwd + return _cloudFields.customTitle.value_or(lang(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove)); }); @@ -286,7 +286,7 @@ void PasscodeBox::setPasswordFail(const RPCError &error) { closeReplacedBy(); _setRequest = 0; - const auto err = error.type(); + const auto &err = error.type(); if (err == qstr("PASSWORD_HASH_INVALID") || err == qstr("SRP_PASSWORD_CHANGED")) { if (_oldPasscode->isHidden()) { @@ -295,7 +295,7 @@ void PasscodeBox::setPasswordFail(const RPCError &error) { } else { badOldPasscode(); } - } else if (error.type() == qstr("SRP_ID_INVALID")) { + } else if (err == qstr("SRP_ID_INVALID")) { handleSrpIdInvalid(); //} else if (err == qstr("NEW_PASSWORD_BAD")) { //} else if (err == qstr("NEW_SALT_INVALID")) { @@ -504,7 +504,7 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) { sendOnlyCheckCloudPassword(oldPassword); }; if (_cloudFields.turningOff && _cloudFields.notEmptyPassport) { - Assert(!_cloudFields.customCheckedCallback); + Assert(!_cloudFields.customCheckCallback); const auto box = std::make_shared>(); const auto confirmed = [=] { @@ -524,8 +524,8 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) { void PasscodeBox::sendOnlyCheckCloudPassword(const QString &oldPassword) { checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) { - if (const auto onstack = _cloudFields.customCheckedCallback) { - onstack(check.result); + if (const auto onstack = _cloudFields.customCheckCallback) { + onstack(check); } else { Assert(_cloudFields.turningOff); sendClearCloudPassword(check); @@ -588,6 +588,18 @@ void PasscodeBox::serverError() { closeBox(); } +bool PasscodeBox::handleCustomCheckError(const RPCError &error) { + const auto &type = error.type(); + if (MTP::isFloodError(error) + || type == qstr("PASSWORD_HASH_INVALID") + || type == qstr("SRP_PASSWORD_CHANGED") + || type == qstr("SRP_ID_INVALID")) { + setPasswordFail(error); + return true; + } + return false; +} + void PasscodeBox::sendClearCloudPassword( const Core::CloudPasswordResult &check) { const auto newPasswordData = QByteArray(); diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h index 19c1f4a0bf..9414069d44 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.h +++ b/Telegram/SourceFiles/boxes/passcode_box.h @@ -37,10 +37,10 @@ public: bool turningOff = false; // Check cloud password for some action. - Fn customCheckedCallback; - std::optional title; - std::optional description; - std::optional button; + Fn customCheckCallback; + std::optional customTitle; + std::optional customDescription; + std::optional customSubmitButton; }; PasscodeBox(QWidget*, const CloudFields &fields); @@ -48,6 +48,8 @@ public: rpl::producer<> passwordReloadNeeded() const; rpl::producer<> clearUnconfirmedPassword() const; + bool handleCustomCheckError(const RPCError &error); + protected: void prepare() override; void setInnerFocus() override; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index 441a4af2d2..95bb5de676 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_privacy_security.h" #include "boxes/calendar_box.h" #include "boxes/generic_box.h" +#include "boxes/confirm_box.h" +#include "boxes/passcode_box.h" #include "boxes/peers/edit_peer_permissions_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "data/data_peer_values.h" @@ -46,7 +48,6 @@ enum class PasswordErrorType { }; void SetCloudPassword(not_null box, not_null user) { - user->session().api().reloadPasswordState(); user->session().api().passwordState( ) | rpl::start_with_next([=] { using namespace Settings; @@ -414,10 +415,12 @@ void EditAdminBox::transferOwnership() { if (_checkTransferRequestId) { return; } + const auto channel = peer()->isChannel() ? peer()->asChannel()->inputChannel : MTP_inputChannelEmpty(); const auto api = &peer()->session().api(); + api->reloadPasswordState(); _checkTransferRequestId = api->request(MTPchannels_EditCreator( channel, MTP_inputUserEmpty(), @@ -425,7 +428,7 @@ void EditAdminBox::transferOwnership() { )).fail([=](const RPCError &error) { _checkTransferRequestId = 0; if (!handleTransferPasswordError(error)) { - requestTransferPassword(); + transferOwnershipChecked(); } }).send(); } @@ -449,7 +452,90 @@ bool EditAdminBox::handleTransferPasswordError(const RPCError &error) { return true; } -void EditAdminBox::requestTransferPassword() { +void EditAdminBox::transferOwnershipChecked() { + if (const auto chat = peer()->asChatNotMigrated()) { + peer()->session().api().migrateChat(chat, crl::guard(this, [=]( + not_null channel) { + requestTransferPassword(channel); + })); + } else if (const auto channel = peer()->asChannelOrMigrated()) { + requestTransferPassword(channel); + } else { + Unexpected("Peer in SaveAdminCallback."); + } + +} + +void EditAdminBox::requestTransferPassword(not_null channel) { + peer()->session().api().passwordState( + ) | rpl::take( + 1 + ) | rpl::start_with_next([=](const Core::CloudPasswordState &state) { + const auto box = std::make_shared>(); + auto fields = PasscodeBox::CloudFields::From(state); + fields.customTitle = lang(lng_rights_transfer_password_title); + fields.customDescription + = lang(lng_rights_transfer_password_description); + fields.customSubmitButton = lang(lng_passcode_submit); + fields.customCheckCallback = crl::guard(this, [=]( + const Core::CloudPasswordResult &result) { + sendTransferRequestFrom(*box, channel, result); + }); + *box = getDelegate()->show(Box(fields)); + }, lifetime()); +} + +void EditAdminBox::sendTransferRequestFrom( + QPointer box, + not_null channel, + const Core::CloudPasswordResult &result) { + const auto weak = make_weak(this); + channel->session().api().request(MTPchannels_EditCreator( + channel->inputChannel, + user()->inputUser, + result.result + )).done([=](const MTPUpdates &result) { + channel->session().api().applyUpdates(result); + if (weak) { + closeBox(); + } + if (box) { + box->closeBox(); + } + }).fail(crl::guard(this, [=](const RPCError &error) { + if (box && box->handleCustomCheckError(error)) { + return; + } + + const auto &type = error.type(); + const auto problem = [&] { + if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) { + return lang(lng_channels_too_much_public_other); + } else if (type == qstr("ADMINS_TOO_MUCH")) { + return lang(channel->isBroadcast() + ? lng_error_admin_limit_channel + : lng_error_admin_limit); + } else if (type == qstr("CHANNEL_INVALID")) { + return lang(channel->isBroadcast() + ? lng_channel_not_accessible + : lng_group_not_accessible); + } + return Lang::Hard::ServerError(); + }(); + const auto recoverable = [&] { + return (type == qstr("PASSWORD_MISSING")) + || (type == qstr("PASSWORD_TOO_FRESH_XXX")) + || (type == qstr("SESSION_TOO_FRESH_XXX")); + }(); + const auto weak = make_weak(this); + getDelegate()->show(Box(problem)); + if (box) { + box->closeBox(); + } + if (weak && !recoverable) { + closeBox(); + } + })).send(); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index 987608414a..b473510efb 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -20,7 +20,12 @@ template class SlideWrap; } // namespace Ui +namespace Core { +struct CloudPasswordResult; +} // namespace Core + class CalendarBox; +class PasscodeBox; class EditParticipantBox : public BoxContent { public: @@ -70,6 +75,9 @@ public: _saveCallback = std::move(callback); } + ~EditAdminBox() { + } + protected: void prepare() override; @@ -80,8 +88,13 @@ private: static MTPChatAdminRights Defaults(not_null peer); void transferOwnership(); + void transferOwnershipChecked(); bool handleTransferPasswordError(const RPCError &error); - void requestTransferPassword(); + void requestTransferPassword(not_null channel); + void sendTransferRequestFrom( + QPointer box, + not_null channel, + const Core::CloudPasswordResult &result); bool canSave() const { return !!_saveCallback; }