/* 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/manage_community_box.h" #include "api/api_peer_photo.h" #include "apiwrap.h" #include "boxes/peers/community_box.h" #include "boxes/peers/community_pending_requests_box.h" #include "boxes/peers/edit_participants_box.h" #include "data/data_changes.h" #include "data/data_channel.h" #include "data/data_session.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "main/session/session_show.h" #include "settings/settings_common.h" #include "ui/boxes/confirm_box.h" #include "ui/controls/userpic_button.h" #include "ui/layers/generic_box.h" #include "ui/painter.h" #include "ui/rp_widget.h" #include "ui/ui_utility.h" #include "ui/userpic_view.h" #include "ui/vertical_list.h" #include "ui/widgets/buttons.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/fields/input_field.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_session_controller.h" #include "styles/style_boxes.h" #include "styles/style_info.h" #include "styles/style_layers.h" #include "styles/style_menu_icons.h" #include "styles/style_settings.h" namespace { [[nodiscard]] rpl::producer PositiveNumberString( rpl::producer value) { return std::move(value) | rpl::map([](int count) { return count ? QString::number(count) : QString(); }); } void SaveCommunityTitle( not_null community, const QString &title) { const auto api = &community->session().api(); api->request(MTPchannels_EditTitle( community->inputChannel(), MTP_string(title) )).done([=](const MTPUpdates &result) { api->applyUpdates(result); }).fail([=](const MTP::Error &error) { if (error.type() == u"CHAT_NOT_MODIFIED"_q) { community->setName(title, QString()); } }).send(); } void SaveCommunityWhoCanAddChats( not_null community, bool onlyAdmins) { const auto rights = onlyAdmins ? ChatRestrictions(ChatRestriction::ManageLinkedPeers) : ChatRestrictions(); const auto api = &community->session().api(); api->request(MTPmessages_EditChatDefaultBannedRights( community->input(), RestrictionsToMTP({ rights, 0 }) )).done([=](const MTPUpdates &result) { api->applyUpdates(result); }).fail([=](const MTP::Error &error) { if (error.type() == u"CHAT_NOT_MODIFIED"_q) { community->setDefaultRestrictions(rights); } }).send(); } void DeleteCommunityWithConfirmation( not_null navigation, not_null community) { const auto show = navigation->uiShow(); const auto sure = [=](Fn &&close) { close(); const auto session = &community->session(); session->api().request(MTPchannels_DeleteChannel( community->inputChannel() )).done([=](const MTPUpdates &result) { session->api().applyUpdates(result); }).fail([=](const MTP::Error &error) { if (error.type() != u"CHANNEL_NOT_MODIFIED"_q) { show->showToast(error.type()); } }).send(); show->hideLayer(); }; show->showBox(Ui::MakeConfirmBox({ .text = tr::lng_community_delete_sure(), .confirmed = sure, .confirmText = tr::lng_box_delete(), .confirmStyle = &st::attentionBoxButton, .title = tr::lng_community_delete(), })); } void ManageCommunityBox( not_null box, not_null navigation, not_null community) { box->setTitle(tr::lng_community_title()); const auto canEdit = community->canEditInformation(); const auto container = box->verticalLayout(); auto photo = (Ui::UserpicButton*)nullptr; auto field = (Ui::InputField*)nullptr; if (canEdit) { const auto row = container->add( object_ptr(container)); const auto photoWrap = Ui::AttachParentChild( row, object_ptr>( row, object_ptr( row, navigation->parentController(), community, Ui::UserpicButton::Role::ChangePhoto, Ui::UserpicButton::Source::PeerPhoto, st::defaultUserpicButton, Ui::PeerUserpicShape::Forum), st::editPeerPhotoMargins)); photo = photoWrap->entity(); photo->showCustomOnChosen(); const auto cache = row->lifetime().make_state< Ui::CommunityUserpicEffect>(); row->paintRequest( ) | rpl::on_next([=] { const auto size = photo->width(); const auto origin = photo->mapTo(row, QPoint()); auto p = QPainter(row); Ui::PaintCommunityUserpicEffect( p, *cache, origin.x(), origin.y(), size, st::windowSubTextFg->c); }, row->lifetime()); const auto titleWrap = Ui::AttachParentChild( row, object_ptr>( row, object_ptr( row, st::editPeerTitleField, tr::lng_community_create_name(), community->name()), st::editPeerTitleMargins)); field = titleWrap->entity(); photoWrap->heightValue( ) | rpl::on_next([=](int height) { row->resize(row->width(), height); }, photoWrap->lifetime()); row->widthValue( ) | rpl::on_next([=](int width) { const auto left = st::editPeerPhotoMargins.left() + st::defaultUserpicButton.size.width(); titleWrap->resizeToWidth(width - left); titleWrap->moveToLeft(left, 0, width); }, titleWrap->lifetime()); } else { field = container->add( object_ptr( container, st::defaultInputField, tr::lng_community_create_name(), community->name()), st::boxRowPadding); field->setEnabled(false); } Ui::AddSkip(container, st::defaultVerticalListSkip * 2); Ui::AddDivider(container); Ui::AddSkip(container); Ui::AddSubsectionTitle(container, tr::lng_community_manage_who_add()); const auto whoCanAdd = std::make_shared( community->communityAnyoneCanAddPeers() ? 0 : 1); const auto addOption = [&]( int value, const QString &label, rpl::producer about) { const auto wrap = container->add( object_ptr(container), st::boxRowPadding); const auto radio = wrap->add(object_ptr( wrap, whoCanAdd, value, label, st::defaultBoxCheckbox)); wrap->add( object_ptr( wrap, std::move(about), st::editPeerPrivacyLabel), st::editPeerPreHistoryLabelMargins); if (!canEdit) { radio->setDisabled(true); } }; Ui::AddSkip(container, st::defaultVerticalListSkip * 2); addOption( 0, tr::lng_community_manage_all_members(tr::now), tr::lng_community_manage_all_members_about()); Ui::AddSkip(container, st::defaultVerticalListSkip * 2); addOption( 1, tr::lng_community_manage_only_admins(tr::now), tr::lng_community_manage_only_admins_about()); Ui::AddSkip(container, st::defaultVerticalListSkip * 2); Ui::AddDivider(container); Ui::AddSkip(container); Settings::AddButtonWithLabel( container, tr::lng_manage_peer_administrators(), PositiveNumberString(Info::Profile::AdminsCountValue(community)), st::settingsButton, { &st::menuIconAdmin } )->addClickHandler([=] { ParticipantsBoxController::Start( navigation, community, ParticipantsBoxController::Role::Admins); }); if (community->canManageLinkedPeers()) { const auto wrap = container->add( object_ptr>( container, object_ptr(container))); auto count = Info::Profile::PendingRequestsCountValue( community ) | rpl::start_spawning(wrap->lifetime()); Settings::AddButtonWithLabel( wrap->entity(), tr::lng_community_requests_title(), PositiveNumberString(rpl::duplicate(count)), st::settingsButton, { &st::menuIconPendingRequests } )->addClickHandler([=] { ShowCommunityPendingRequestsBox(navigation, community); }); wrap->toggleOn( std::move(count) | rpl::map(rpl::mappers::_1 > 0), anim::type::instant); wrap->finishAnimating(); } Settings::AddButtonWithLabel( container, tr::lng_manage_peer_removed_users(), PositiveNumberString(Info::Profile::KickedCountValue(community)), st::settingsButton, { &st::menuIconRemovedUsers } )->addClickHandler([=] { ParticipantsBoxController::Start( navigation, community, ParticipantsBoxController::Role::Kicked); }); if (community->communityInfo() && community->canManageLinkedPeers()) { Ui::AddSkip(container); Ui::AddDivider(container); Ui::AddSkip(container); SetupCommunityEditChatsList( container, Main::MakeSessionShow(box->uiShow(), &community->session()), navigation, community); } if (community->canDelete()) { Ui::AddSkip(container); Ui::AddDivider(container); Ui::AddSkip(container); Settings::AddButtonWithIcon( container, tr::lng_community_delete(), st::settingsAttentionButtonWithIcon, { &st::menuIconDeleteAttention } )->addClickHandler([=] { DeleteCommunityWithConfirmation(navigation, community); }); } if (canEdit) { box->addButton(tr::lng_settings_save(), [=] { const auto title = field->getLastText().trimmed(); if (title.isEmpty()) { field->showError(); return; } if (title != community->name()) { SaveCommunityTitle(community, title); } const auto onlyAdmins = (whoCanAdd->current() == 1); const auto wasOnlyAdmins = !community->communityAnyoneCanAddPeers(); if (onlyAdmins != wasOnlyAdmins) { SaveCommunityWhoCanAddChats(community, onlyAdmins); } if (auto image = photo->takeResultImage(); !image.isNull()) { community->session().api().peerPhoto().upload( community, { std::move(image) }); } box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } else { box->addButton(tr::lng_close(), [=] { box->closeBox(); }); } if (!community->wasFullUpdated()) { community->session().api().requestFullPeer(community); } } } // namespace void ShowManageCommunityBox( not_null navigation, not_null community) { navigation->uiShow()->showBox( Box(ManageCommunityBox, navigation, community)); }