/* 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/peers/edit_peer_info_box.h" #include "apiwrap.h" #include "auth_session.h" #include "boxes/add_contact_box.h" #include "boxes/confirm_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_group_type_box.h" #include "boxes/peers/edit_peer_history_visibility_box.h" #include "boxes/peers/edit_peer_permissions_box.h" #include "boxes/stickers_box.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_peer.h" #include "history/admin_log/history_admin_log_section.h" #include "info/profile/info_profile_button.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" #include "mainwidget.h" #include "mainwindow.h" #include "mtproto/sender.h" #include "observer_peer.h" #include "styles/style_boxes.h" #include "styles/style_info.h" #include "ui/rp_widget.h" #include "ui/special_buttons.h" #include "ui/toast/toast.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "window/window_controller.h" #include #include namespace { Fn ManagePeerTitle(not_null peer) { return langFactory((peer->isChat() || peer->isMegagroup()) ? lng_manage_group_title : lng_manage_channel_title); } auto ToPositiveNumberString() { return rpl::map([](int count) { return count ? QString::number(count) : QString(); }); } auto ToPositiveNumberStringRestrictions() { return rpl::map([](int count) { return QString::number(count) + QString("/") + QString::number(int(Data::ListOfRestrictions().size())); }); } void AddSkip(not_null container) { container->add(object_ptr( container, st::editPeerSkip)); container->add(object_ptr(container)); } Info::Profile::Button *AddButton( not_null parent, rpl::producer &&text, Fn callback, const style::icon &icon) { return ManagePeerBox::CreateButton( parent, std::move(text), rpl::single(QString()), std::move(callback), st::manageGroupButton, &icon); } void AddButtonWithCount( not_null parent, rpl::producer &&text, rpl::producer &&count, Fn callback, const style::icon &icon) { ManagePeerBox::CreateButton( parent, std::move(text), std::move(count), std::move(callback), st::manageGroupButton, &icon); } Info::Profile::Button *AddButtonWithText( not_null parent, rpl::producer &&text, rpl::producer &&label, Fn callback) { return ManagePeerBox::CreateButton( parent, std::move(text), std::move(label), std::move(callback), st::manageGroupTopButtonWithText, nullptr); } bool HasRecentActions(not_null channel) { return channel->hasAdminRights() || channel->amCreator(); } void ShowRecentActions( not_null navigation, not_null channel) { navigation->showSection(AdminLog::SectionMemento(channel)); } bool HasEditInfoBox(not_null peer) { if (const auto chat = peer->asChat()) { if (chat->canEditInformation()) { return true; } } else if (const auto channel = peer->asChannel()) { if (channel->canEditInformation()) { return true; } else if (!channel->isPublic() && channel->canAddMembers()) { // Edit invite link. return true; } } return false; } void ShowEditPermissions(not_null peer) { const auto box = Ui::show( Box(peer), LayerOption::KeepOther); box->saveEvents( ) | rpl::start_with_next([=](MTPDchatBannedRights::Flags restrictions) { const auto callback = crl::guard(box, [=](bool success) { if (success) { box->closeBox(); } }); peer->session().api().saveDefaultRestrictions( peer->migrateToOrMe(), MTP_chatBannedRights(MTP_flags(restrictions), MTP_int(0)), callback); }, box->lifetime()); } void FillManageChatBox( not_null navigation, not_null chat, not_null content) { if (chat->canEditPermissions()) { AddButtonWithCount( content, Lang::Viewer(lng_manage_peer_permissions), Info::Profile::RestrictionsCountValue(chat) | ToPositiveNumberStringRestrictions(), [=] { ShowEditPermissions(chat); }, st::infoIconPermissions); } if (chat->amIn()) { AddButtonWithCount( content, Lang::Viewer(lng_manage_peer_administrators), Info::Profile::AdminsCountValue(chat) | ToPositiveNumberString(), [=] { ParticipantsBoxController::Start( navigation, chat, ParticipantsBoxController::Role::Admins); }, st::infoIconAdministrators); AddButtonWithCount( content, Lang::Viewer(lng_manage_peer_members), Info::Profile::MembersCountValue(chat) | ToPositiveNumberString(), [=] { ParticipantsBoxController::Start( navigation, chat, ParticipantsBoxController::Role::Members); }, st::infoIconMembers); } } void FillManageChannelBox( not_null navigation, not_null channel, not_null content) { auto isGroup = channel->isMegagroup(); if (channel->canEditPermissions()) { AddButtonWithCount( content, Lang::Viewer(lng_manage_peer_permissions), Info::Profile::RestrictionsCountValue(channel) | ToPositiveNumberStringRestrictions(), [=] { ShowEditPermissions(channel); }, st::infoIconPermissions); } if (channel->canViewAdmins()) { AddButtonWithCount( content, Lang::Viewer(lng_manage_peer_administrators), Info::Profile::AdminsCountValue(channel) | ToPositiveNumberString(), [=] { ParticipantsBoxController::Start( navigation, channel, ParticipantsBoxController::Role::Admins); }, st::infoIconAdministrators); } if (channel->canViewMembers()) { AddButtonWithCount( content, Lang::Viewer(lng_manage_peer_members), Info::Profile::MembersCountValue(channel) | ToPositiveNumberString(), [=] { ParticipantsBoxController::Start( navigation, channel, ParticipantsBoxController::Role::Members); }, st::infoIconMembers); } if (!channel->isMegagroup()) { AddButtonWithCount( content, Lang::Viewer(lng_manage_peer_removed_users), Info::Profile::KickedCountValue(channel) | ToPositiveNumberString(), [=] { ParticipantsBoxController::Start( navigation, channel, ParticipantsBoxController::Role::Kicked); }, st::infoIconBlacklist); } if (HasRecentActions(channel)) { AddButton( content, Lang::Viewer(lng_manage_peer_recent_actions), [=] { ShowRecentActions(navigation, channel); }, st::infoIconRecentActions); } } } // namespace namespace { constexpr auto kUsernameCheckTimeout = crl::time(200); constexpr auto kMinUsernameLength = 5; constexpr auto kMaxGroupChannelTitle = 255; // See also add_contact_box. constexpr auto kMaxChannelDescription = 255; // See also add_contact_box. class Controller : public base::has_weak_ptr , private MTP::Sender { public: Controller( not_null box, not_null peer); object_ptr createContent(); void setFocus(); private: struct Controls { Ui::InputField *title = nullptr; Ui::InputField *description = nullptr; Ui::UserpicButton *photo = nullptr; rpl::lifetime initialPhotoImageWaiting; std::shared_ptr> privacy; Ui::SlideWrap *usernameWrap = nullptr; Ui::UsernameInput *username = nullptr; base::unique_qptr usernameResult; const style::FlatLabel *usernameResultStyle = nullptr; Ui::SlideWrap *createInviteLinkWrap = nullptr; Ui::SlideWrap *editInviteLinkWrap = nullptr; Ui::FlatLabel *inviteLink = nullptr; Ui::SlideWrap *historyVisibilityWrap = nullptr; std::optional historyVisibilitySavedValue = std::nullopt; std::optional privacySavedValue = std::nullopt; std::optional usernameSavedValue = std::nullopt; std::optional signaturesSavedValue = std::nullopt; }; struct Saving { std::optional username; std::optional title; std::optional description; std::optional hiddenPreHistory; std::optional signatures; }; Fn computeTitle() const; object_ptr createPhotoAndTitleEdit(); object_ptr createTitleEdit(); object_ptr createPhotoEdit(); object_ptr createDescriptionEdit(); object_ptr createUsernameEdit(); object_ptr createInviteLinkCreate(); object_ptr createInviteLinkEdit(); object_ptr createStickersEdit(); object_ptr createDeleteButton(); object_ptr createPrivaciesButtons(); object_ptr createManageGroupButtons(); QString inviteLinkText() const; void observeInviteLink(); void submitTitle(); void submitDescription(); void deleteWithConfirmation(); void deleteChannel(); void refreshHistoryVisibility(bool instant = false); std::optional validate() const; bool validateUsername(Saving &to) const; bool validateTitle(Saving &to) const; bool validateDescription(Saving &to) const; bool validateHistoryVisibility(Saving &to) const; bool validateSignatures(Saving &to) const; void save(); void saveUsername(); void saveTitle(); void saveDescription(); void saveHistoryVisibility(); void saveSignatures(); void savePhoto(); void pushSaveStage(FnMut &&lambda); void continueSave(); void cancelSave(); void subscribeToMigration(); void migrate(not_null channel); not_null _box; not_null _peer; bool _isGroup = false; base::unique_qptr _wrap; Controls _controls; mtpRequestId _checkUsernameRequestId = 0; UsernameState _usernameState = UsernameState::Normal; rpl::event_stream> _usernameResultTexts; std::deque> _saveStagesQueue; Saving _savingData; rpl::lifetime _lifetime; }; Controller::Controller( not_null box, not_null peer) : _box(box) , _peer(peer) , _isGroup(_peer->isChat() || _peer->isMegagroup()) { _box->setTitle(computeTitle()); _box->addButton(langFactory(lng_settings_save), [this] { save(); }); _box->addButton(langFactory(lng_cancel), [this] { _box->closeBox(); }); subscribeToMigration(); _peer->updateFull(); } void Controller::subscribeToMigration() { SubscribeToMigration( _peer, _lifetime, [=](not_null channel) { migrate(channel); }); } void Controller::migrate(not_null channel) { _peer = channel; // observeInviteLink(); _peer->updateFull(); } Fn Controller::computeTitle() const { return langFactory(_isGroup ? lng_edit_group : lng_edit_channel_title); } object_ptr Controller::createContent() { auto result = object_ptr(_box); _wrap.reset(result.data()); _controls = Controls(); _wrap->add(createPhotoAndTitleEdit()); _wrap->add(createDescriptionEdit()); AddSkip(_wrap); // Divider. _wrap->add(createPrivaciesButtons()); AddSkip(_wrap); // Divider. _wrap->add(createManageGroupButtons()); AddSkip(_wrap); // Divider. _wrap->add(createStickersEdit()); _wrap->add(createDeleteButton()); return result; } void Controller::setFocus() { if (_controls.title) { _controls.title->setFocusFast(); } } object_ptr Controller::createPhotoAndTitleEdit() { Expects(_wrap != nullptr); const auto canEdit = [&] { if (const auto channel = _peer->asChannel()) { return channel->canEditInformation(); } else if (const auto chat = _peer->asChat()) { return chat->canEditInformation(); } return false; }(); if (!canEdit) { return nullptr; } auto result = object_ptr(_wrap); auto container = result.data(); auto photoWrap = Ui::AttachParentChild( container, createPhotoEdit()); auto titleEdit = Ui::AttachParentChild( container, createTitleEdit()); photoWrap->heightValue( ) | rpl::start_with_next([container](int height) { container->resize(container->width(), height); }, photoWrap->lifetime()); container->widthValue( ) | rpl::start_with_next([titleEdit](int width) { auto left = st::editPeerPhotoMargins.left() + st::defaultUserpicButton.size.width(); titleEdit->resizeToWidth(width - left); titleEdit->moveToLeft(left, 0, width); }, titleEdit->lifetime()); return result; } object_ptr Controller::createPhotoEdit() { Expects(_wrap != nullptr); using PhotoWrap = Ui::PaddingWrap; auto photoWrap = object_ptr( _wrap, object_ptr( _wrap, _peer, Ui::UserpicButton::Role::ChangePhoto, st::defaultUserpicButton), st::editPeerPhotoMargins); _controls.photo = photoWrap->entity(); return photoWrap; } object_ptr Controller::createTitleEdit() { Expects(_wrap != nullptr); auto result = object_ptr>( _wrap, object_ptr( _wrap, st::defaultInputField, langFactory(_isGroup ? lng_dlg_new_group_name : lng_dlg_new_channel_name), _peer->name), st::editPeerTitleMargins); result->entity()->setMaxLength(kMaxGroupChannelTitle); result->entity()->setInstantReplaces(Ui::InstantReplaces::Default()); result->entity()->setInstantReplacesEnabled( Global::ReplaceEmojiValue()); Ui::Emoji::SuggestionsController::Init( _wrap->window(), result->entity()); QObject::connect( result->entity(), &Ui::InputField::submitted, [=] { submitTitle(); }); _controls.title = result->entity(); return std::move(result); } object_ptr Controller::createDescriptionEdit() { Expects(_wrap != nullptr); auto result = object_ptr>( _wrap, object_ptr( _wrap, st::editPeerDescription, Ui::InputField::Mode::MultiLine, langFactory(lng_create_group_description), _peer->about()), st::editPeerDescriptionMargins); result->entity()->setMaxLength(kMaxChannelDescription); result->entity()->setInstantReplaces(Ui::InstantReplaces::Default()); result->entity()->setInstantReplacesEnabled( Global::ReplaceEmojiValue()); Ui::Emoji::SuggestionsController::Init( _wrap->window(), result->entity()); QObject::connect( result->entity(), &Ui::InputField::submitted, [=] { submitDescription(); }); _controls.description = result->entity(); return std::move(result); } object_ptr Controller::createPrivaciesButtons() { Expects(_wrap != nullptr); const auto canEditUsername = [&] { if (const auto chat = _peer->asChat()) { return chat->canEditUsername(); } else if (const auto channel = _peer->asChannel()) { return channel->canEditUsername(); } Unexpected("Peer type in Controller::createPrivaciesEdit."); }(); if (!canEditUsername) { return nullptr; } const auto channel = _peer->asChannel(); auto isRealChannel = !(!channel || !channel->canEditSignatures() || channel->isMegagroup()); // Create Privacy Button. _controls.privacySavedValue = (_peer->isChannel() && _peer->asChannel()->isPublic()) ? Privacy::Public : Privacy::Private; const auto updateType = std::make_shared>(); auto result = object_ptr>( _wrap, object_ptr(_wrap), st::editPeerTopButtonsLayoutMargins); auto resultContainer = result->entity(); const auto boxCallback = [=](Privacy checked, QString publicLink) { updateType->fire(std::move(checked)); _controls.privacySavedValue = checked; _controls.usernameSavedValue = publicLink; refreshHistoryVisibility(); }; const auto buttonCallback = [=]{ Ui::show(Box( _peer, boxCallback, _controls.privacySavedValue, _controls.usernameSavedValue ), LayerOption::KeepOther); }; AddButtonWithText( resultContainer, std::move(Lang::Viewer((_peer->isChat() || _peer->isMegagroup()) ? lng_manage_peer_group_type : lng_manage_peer_channel_type)), updateType->events( ) | rpl::map([](Privacy flag) { return lang(Privacy::Public == flag ? lng_manage_public_peer_title : lng_manage_private_peer_title); }), buttonCallback); updateType->fire(std::move(_controls.privacySavedValue.value())); // Create Signatures Toggle Button. if (isRealChannel) { AddButtonWithText( resultContainer, std::move(Lang::Viewer(lng_edit_sign_messages)), rpl::single(QString()), [=] {} )->toggleOn(rpl::single(channel->addsSignature()) )->toggledValue( ) | rpl::start_with_next([=](bool toggled) { _controls.signaturesSavedValue = toggled; }, resultContainer->lifetime()); return std::move(result); } // Create History Visibility Button. const auto addHistoryVisibilityButton = [=](LangKey privacyTextKey, Ui::VerticalLayout* container) { // Bug with defaultValue here. _controls.historyVisibilitySavedValue = (!channel || channel->hiddenPreHistory()) ? HistoryVisibility::Hidden : HistoryVisibility::Visible; const auto updateHistoryVisibility = std::make_shared>(); const auto boxCallback = [=](HistoryVisibility checked) { updateHistoryVisibility->fire(std::move(checked)); _controls.historyVisibilitySavedValue = checked; }; const auto buttonCallback = [=]{ Ui::show(Box( _peer, boxCallback, _controls.historyVisibilitySavedValue ), LayerOption::KeepOther); }; AddButtonWithText( container, std::move(Lang::Viewer(privacyTextKey)), updateHistoryVisibility->events( ) | rpl::map([](HistoryVisibility flag) { return lang(HistoryVisibility::Visible == flag ? lng_manage_history_visibility_shown : lng_manage_history_visibility_hidden); }), buttonCallback); updateHistoryVisibility->fire( std::move(_controls.historyVisibilitySavedValue.value()) ); }; auto wrapLayout = resultContainer->add(object_ptr>( resultContainer, object_ptr(resultContainer), st::boxOptionListPadding)); // Empty margins. _controls.historyVisibilityWrap = wrapLayout; addHistoryVisibilityButton(lng_manage_history_visibility_title, wrapLayout->entity()); //While appearing box we should use instant animation. refreshHistoryVisibility(true); return std::move(result); } object_ptr Controller::createManageGroupButtons() { Expects(_wrap != nullptr); auto result = object_ptr>( _wrap, object_ptr(_wrap), st::editPeerBottomButtonsLayoutMargins); auto container = result->entity(); if (const auto chat = _peer->asChat()) { FillManageChatBox(App::wnd()->controller(), chat, container); } else if (const auto channel = _peer->asChannel()) { FillManageChannelBox(App::wnd()->controller(), channel, container); } // setDimensionsToContent(st::boxWidth, content); return std::move(result); } void Controller::refreshHistoryVisibility(bool instant) { if (!_controls.historyVisibilityWrap) { return; } _controls.historyVisibilityWrap->toggle( _controls.privacySavedValue == Privacy::Private, instant ? anim::type::instant : anim::type::normal); } object_ptr Controller::createStickersEdit() { Expects(_wrap != nullptr); auto channel = _peer->asChannel(); if (!channel || !channel->canEditStickers()) { return nullptr; } auto result = object_ptr>( _wrap, object_ptr(_wrap), st::editPeerInviteLinkMargins); auto container = result->entity(); container->add(object_ptr( container, Lang::Viewer(lng_group_stickers), st::editPeerSectionLabel)); container->add(object_ptr( container, st::editPeerInviteLinkSkip)); container->add(object_ptr( container, Lang::Viewer(lng_group_stickers_description), st::editPeerPrivacyLabel)); container->add(object_ptr( container, st::editPeerInviteLinkSkip)); container->add(object_ptr( _wrap, lang(lng_group_stickers_add), st::editPeerInviteLinkButton) )->addClickHandler([=] { Ui::show(Box(channel), LayerOption::KeepOther); }); return std::move(result); } object_ptr Controller::createDeleteButton() { Expects(_wrap != nullptr); auto channel = _peer->asChannel(); if (!channel || !channel->canDelete()) { return nullptr; } auto text = lang(_isGroup ? lng_profile_delete_group : lng_profile_delete_channel); auto result = object_ptr>( _wrap, object_ptr( _wrap, text, st::editPeerDeleteButton), st::editPeerDeleteButtonMargins); result->entity()->addClickHandler([this] { deleteWithConfirmation(); }); return std::move(result); } void Controller::submitTitle() { Expects(_controls.title != nullptr); if (_controls.title->getLastText().isEmpty()) { _controls.title->showError(); _box->scrollToWidget(_controls.title); } else if (_controls.description) { _controls.description->setFocus(); _box->scrollToWidget(_controls.description); } } void Controller::submitDescription() { Expects(_controls.title != nullptr); Expects(_controls.description != nullptr); if (_controls.title->getLastText().isEmpty()) { _controls.title->showError(); _box->scrollToWidget(_controls.title); } else { save(); } } std::optional Controller::validate() const { auto result = Saving(); if (validateUsername(result) && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) && validateSignatures(result)) { return result; } return {}; } bool Controller::validateUsername(Saving &to) const { if (_controls.privacySavedValue != Privacy::Public) { to.username = QString(); return true; } auto username = _controls.usernameSavedValue.value_or( _peer->isChannel() ? _peer->asChannel()->username : QString() ); if (username.isEmpty()) { return false; } to.username = username; return true; } bool Controller::validateTitle(Saving &to) const { if (!_controls.title) { return true; } auto title = _controls.title->getLastText().trimmed(); if (title.isEmpty()) { _controls.title->showError(); _box->scrollToWidget(_controls.title); return false; } to.title = title; return true; } bool Controller::validateDescription(Saving &to) const { if (!_controls.description) { return true; } to.description = _controls.description->getLastText().trimmed(); return true; } bool Controller::validateHistoryVisibility(Saving &to) const { if (!_controls.historyVisibilityWrap) return true; if (!_controls.historyVisibilityWrap->toggled() || (_controls.privacySavedValue == Privacy::Public)) { return true; } to.hiddenPreHistory = (_controls.historyVisibilitySavedValue == HistoryVisibility::Hidden); return true; } bool Controller::validateSignatures(Saving &to) const { if (!_controls.signaturesSavedValue.has_value()) { return true; } to.signatures = _controls.signaturesSavedValue; return true; } void Controller::save() { Expects(_wrap != nullptr); if (!_saveStagesQueue.empty()) { return; } if (auto saving = validate()) { _savingData = *saving; pushSaveStage([this] { saveUsername(); }); pushSaveStage([this] { saveTitle(); }); pushSaveStage([this] { saveDescription(); }); pushSaveStage([this] { saveHistoryVisibility(); }); pushSaveStage([this] { saveSignatures(); }); pushSaveStage([this] { savePhoto(); }); continueSave(); } } void Controller::pushSaveStage(FnMut &&lambda) { _saveStagesQueue.push_back(std::move(lambda)); } void Controller::continueSave() { if (!_saveStagesQueue.empty()) { auto next = std::move(_saveStagesQueue.front()); _saveStagesQueue.pop_front(); next(); } } void Controller::cancelSave() { _saveStagesQueue.clear(); } void Controller::saveUsername() { const auto channel = _peer->asChannel(); const auto username = (channel ? channel->username : QString()); if (!_savingData.username || *_savingData.username == username) { return continueSave(); } else if (!channel) { const auto saveForChannel = [=](not_null channel) { if (_peer->asChannel() == channel) { saveUsername(); } else { cancelSave(); } }; _peer->session().api().migrateChat( _peer->asChat(), crl::guard(this, saveForChannel)); return; } request(MTPchannels_UpdateUsername( channel->inputChannel, MTP_string(*_savingData.username) )).done([=](const MTPBool &result) { channel->setName( TextUtilities::SingleLine(channel->name), *_savingData.username); continueSave(); }).fail([=](const RPCError &error) { const auto &type = error.type(); if (type == qstr("USERNAME_NOT_MODIFIED")) { channel->setName( TextUtilities::SingleLine(channel->name), TextUtilities::SingleLine(*_savingData.username)); continueSave(); return; } const auto errorKey = [&] { if (type == qstr("USERNAME_INVALID")) { return lng_create_channel_link_invalid; } else if (type == qstr("USERNAME_OCCUPIED") || type == qstr("USERNAMES_UNAVAILABLE")) { return lng_create_channel_link_invalid; } return lng_create_channel_link_invalid; }(); _controls.username->showError(); _box->scrollToWidget(_controls.username); // showUsernameError(Lang::Viewer(errorKey)); cancelSave(); }).send(); } void Controller::saveTitle() { if (!_savingData.title || *_savingData.title == _peer->name) { return continueSave(); } const auto onDone = [=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); continueSave(); }; const auto onFail = [=](const RPCError &error) { const auto &type = error.type(); if (type == qstr("CHAT_NOT_MODIFIED") || type == qstr("CHAT_TITLE_NOT_MODIFIED")) { if (const auto channel = _peer->asChannel()) { channel->setName(*_savingData.title, channel->username); } else if (const auto chat = _peer->asChat()) { chat->setName(*_savingData.title); } continueSave(); return; } _controls.title->showError(); if (type == qstr("NO_CHAT_TITLE")) { _box->scrollToWidget(_controls.title); } cancelSave(); }; if (const auto channel = _peer->asChannel()) { request(MTPchannels_EditTitle( channel->inputChannel, MTP_string(*_savingData.title) )).done(std::move(onDone) ).fail(std::move(onFail) ).send(); } else if (const auto chat = _peer->asChat()) { request(MTPmessages_EditChatTitle( chat->inputChat, MTP_string(*_savingData.title) )).done(std::move(onDone) ).fail(std::move(onFail) ).send(); } else { continueSave(); } } void Controller::saveDescription() { const auto channel = _peer->asChannel(); if (!_savingData.description || *_savingData.description == _peer->about()) { return continueSave(); } const auto successCallback = [=] { _peer->setAbout(*_savingData.description); continueSave(); }; request(MTPmessages_EditChatAbout( _peer->input, MTP_string(*_savingData.description) )).done([=](const MTPBool &result) { successCallback(); }).fail([=](const RPCError &error) { const auto &type = error.type(); if (type == qstr("CHAT_ABOUT_NOT_MODIFIED")) { successCallback(); return; } _controls.description->showError(); cancelSave(); }).send(); } void Controller::saveHistoryVisibility() { const auto channel = _peer->asChannel(); const auto hidden = channel ? channel->hiddenPreHistory() : true; if (!_savingData.hiddenPreHistory || *_savingData.hiddenPreHistory == hidden) { return continueSave(); } else if (!channel) { const auto saveForChannel = [=](not_null channel) { if (_peer->asChannel() == channel) { saveHistoryVisibility(); } else { cancelSave(); } }; _peer->session().api().migrateChat( _peer->asChat(), crl::guard(this, saveForChannel)); return; } request(MTPchannels_TogglePreHistoryHidden( channel->inputChannel, MTP_bool(*_savingData.hiddenPreHistory) )).done([=](const MTPUpdates &result) { // Update in the result doesn't contain the // channelFull:flags field which holds this value. // So after saving we need to update it manually. channel->updateFullForced(); channel->session().api().applyUpdates(result); continueSave(); }).fail([=](const RPCError &error) { if (error.type() == qstr("CHAT_NOT_MODIFIED")) { continueSave(); } else { cancelSave(); } }).send(); } void Controller::saveSignatures() { const auto channel = _peer->asChannel(); if (!_savingData.signatures || !channel || *_savingData.signatures == channel->addsSignature()) { return continueSave(); } request(MTPchannels_ToggleSignatures( channel->inputChannel, MTP_bool(*_savingData.signatures) )).done([=](const MTPUpdates &result) { channel->session().api().applyUpdates(result); continueSave(); }).fail([=](const RPCError &error) { if (error.type() == qstr("CHAT_NOT_MODIFIED")) { continueSave(); } else { cancelSave(); } }).send(); } void Controller::savePhoto() { auto image = _controls.photo ? _controls.photo->takeResultImage() : QImage(); if (!image.isNull()) { _peer->session().api().uploadPeerPhoto(_peer, std::move(image)); } _box->closeBox(); } void Controller::deleteWithConfirmation() { const auto channel = _peer->asChannel(); Assert(channel != nullptr); const auto text = lang(_isGroup ? lng_sure_delete_group : lng_sure_delete_channel); const auto deleteCallback = crl::guard(this, [=] { deleteChannel(); }); Ui::show( Box( text, lang(lng_box_delete), st::attentionBoxButton, deleteCallback), LayerOption::KeepOther); } void Controller::deleteChannel() { const auto channel = _peer->asChannel(); Assert(channel != nullptr); const auto chat = channel->migrateFrom(); Ui::hideLayer(); Ui::showChatsList(); if (chat) { App::main()->deleteAndExit(chat); } MTP::send( MTPchannels_DeleteChannel(channel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::deleteChannelFailed)); } } // namespace EditPeerInfoBox::EditPeerInfoBox( QWidget*, not_null peer) : _peer(peer->migrateToOrMe()) { } void EditPeerInfoBox::prepare() { auto controller = Ui::CreateChild(this, this, _peer); _focusRequests.events( ) | rpl::start_with_next( [=] { controller->setFocus(); }, lifetime()); auto content = controller->createContent(); setDimensionsToContent(st::boxWideWidth, content); setInnerWidget(object_ptr( this, std::move(content))); }