From 75fe14ad28d1829375869bb353b59619b16ab89e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 31 Dec 2025 08:24:48 +0300 Subject: [PATCH] Added initial ability to kick participants from common groups. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/boxes.style | 17 ++ .../boxes/moderate_messages_box.cpp | 198 +++++++++++++++++- Telegram/lib_ui | 2 +- 4 files changed, 216 insertions(+), 2 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 420702ec65..752c41c9dd 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4346,6 +4346,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_restrict_users_part_single_header" = "What can this user do?"; "lng_restrict_users_part_header#one" = "What can {count} selected user do?"; "lng_restrict_users_part_header#other" = "What can {count} selected users do?"; +"lng_restrict_users_kick_from_common_group" = "Also ban from:"; "lng_report_spam" = "Report Spam"; "lng_report_spam_and_leave" = "Report spam and leave"; "lng_report_spam_done" = "Thank you for your report"; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index a0f708341b..e4739225c1 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -1165,3 +1165,20 @@ fakeUserpicButton: UserpicButton(defaultUserpicButton) { changeIcon: icon {{ "settings/photo", transparent }}; uploadBg: transparent; } + +moderateCommonGroupsCheckbox: RoundImageCheckbox(defaultPeerListCheckbox) { + imageRadius: 12px; + imageSmallRadius: 11px; + selectWidth: 2px; + check: RoundCheckbox(defaultPeerListCheck) { + size: 16px; + sizeSmall: 0.3; + bgInactive: overviewCheckBg; + bgActive: overviewCheckBgActive; + check: icon {{ + "default_checkbox_check", + overviewCheckFgActive, + point(1px, 4px) + }}; + } +} diff --git a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp index b07cba4528..2053d54e29 100644 --- a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp @@ -50,6 +50,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_layers.h" #include "styles/style_window.h" +#include "boxes/peer_list_box.h" +#include "main/main_session_settings.h" +#include "ui/painter.h" +#include "ui/rect.h" +#include "ui/widgets/menu/menu_multiline_action.h" +#include "ui/widgets/popup_menu.h" +#include "ui/widgets/menu/menu_action.h" +#include "ui/effects/round_checkbox.h" +#include "styles/style_chat.h" +#include "styles/style_info.h" + namespace { struct ModerateOptions final { @@ -133,6 +144,155 @@ ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) { }; } +using CommonGroups = std::vector>; +using CollectCommon = std::shared_ptr>; + +void FillMenuModerateCommonGroups( + not_null menu, + CommonGroups common, + CollectCommon collectCommon) { + const auto resultList + = menu->lifetime().make_state>(); + const auto rememberCheckbox = Ui::CreateChild( + menu, + QString()); + auto multiline = base::make_unique_q( + menu->menu(), + menu->st().menu, + st::historyHasCustomEmoji, + st::historyHasCustomEmojiPosition, + tr::lng_restrict_users_kick_from_common_group(tr::now, tr::rich)); + multiline->setDisabled(true); + multiline->setAttribute(Qt::WA_TransparentForMouseEvents); + menu->addAction(std::move(multiline)); + const auto session = &common.front()->session(); + const auto save = [=] { + auto result = std::vector( + resultList->begin(), + resultList->end()); + if (rememberCheckbox->checked()) { + session->settings().setModerateCommonGroups(result); + session->saveSettingsDelayed(); + } + *collectCommon = std::move(result); + }; + for (const auto &group : common) { + struct State { + std::optional checkbox; + Ui::RpWidget *checkboxWidget = nullptr; + }; + auto item = base::make_unique_q( + menu->menu(), + menu->st().menu, + Ui::Menu::CreateAction( + menu->menu(), + group->name(), + [] {}), + nullptr, + nullptr); + const auto state = item->lifetime().make_state(); + item->AbstractButton::setDisabled(true); + item->Ui::Menu::ItemBase::setClickedCallback([=, peerId = group->id] { + state->checkbox->setChecked(!state->checkbox->checked()); + if (state->checkbox->checked()) { + resultList->insert(peerId); + } else { + resultList->erase(peerId); + } + save(); + }); + const auto raw = item.get(); + state->checkboxWidget = Ui::CreateChild(raw); + state->checkboxWidget->setAttribute(Qt::WA_TransparentForMouseEvents); + state->checkboxWidget->resize(item->width() * 2, item->height()); + state->checkboxWidget->show(); + state->checkbox.emplace( + st::moderateCommonGroupsCheckbox, + [=] { state->checkboxWidget->update(); }, + PaintUserpicCallback(group, true), + [=](int size) { return (group->isForum() || group->isMonoforum()) + ? int(size * Ui::ForumUserpicRadiusMultiplier()) + : std::optional(); }); + state->checkbox->setChecked( + ranges::contains( + session->settings().moderateCommonGroups(), + group->id) + || (collectCommon + && ranges::contains(*collectCommon, group->id)), + anim::type::instant); + state->checkboxWidget->paintOn([=](QPainter &p) { + auto pp = Painter(state->checkboxWidget); + state->checkbox->paint( + pp, + st::menuWithIcons.itemIconPosition.x(), + st::menuWithIcons.itemIconPosition.y(), + raw->width()); + }); + menu->addAction(std::move(item)); + } + menu->addSeparator(); + { + auto item = base::make_unique_q( + menu->menu(), + menu->st().menu, + Ui::Menu::CreateAction( + menu->menu(), + tr::lng_remember(tr::now), + [] {}), + nullptr, + nullptr); + item->AbstractButton::setDisabled(true); + item->Ui::Menu::ItemBase::setClickedCallback([=] { + rememberCheckbox->setChecked(!rememberCheckbox->checked()); + }); + rememberCheckbox->setParent(item.get()); + rememberCheckbox->setAttribute(Qt::WA_TransparentForMouseEvents); + rememberCheckbox->move(st::lineWidth * 8, -st::lineWidth * 2); + rememberCheckbox->show(); + menu->addAction(std::move(item)); + } +} + +void ProccessCommonGroups( + const HistoryItemsList &items, + Fn processHas) { + const auto moderateOptions = CalculateModerateOptions(items); + if (moderateOptions.participants.size() != 1 + || !moderateOptions.allCanBan) { + return; + } + const auto participant = moderateOptions.participants.front(); + const auto user = participant->asUser(); + if (!user) { + return; + } + const auto currentGroupId = items.front()->history()->peer->id; + user->session().api().requestBotCommonGroups(user, [=] { + const auto commonGroups = user->session().api().botCommonGroups(user); + if (!commonGroups || commonGroups->empty()) { + return; + } + + auto filtered = CommonGroups(); + for (const auto &group : *commonGroups) { + if (group->id == currentGroupId) { + continue; + } + const auto channel = group->asChannel(); + if (channel && channel->canRestrictParticipant(user)) { + if (channel->isGroupAdmin(user) && !channel->amCreator()) { + continue; + } + filtered.push_back(group); + } + } + + if (!filtered.empty()) { + processHas(filtered); + } + }); +} + } // namespace void CreateModerateMessagesBox( @@ -150,6 +310,7 @@ void CreateModerateMessagesBox( Assert(!participants.empty()); const auto confirms = inner->lifetime().make_state>(); + const auto collectCommon = std::make_shared>(); const auto isSingle = participants.size() == 1; const auto buttonPadding = isSingle @@ -168,6 +329,32 @@ void CreateModerateMessagesBox( const auto historyPeerId = history->peer->id; const auto ids = session->data().itemsToIds(items); + ProccessCommonGroups( + items, + [=](CommonGroups groups) { + using namespace Ui; + const auto top = box->addTopButton(st::infoTopBarMenu); + auto &lifetime = top->lifetime(); + const auto menu + = lifetime.make_state>(); + + top->setClickedCallback([=] { + top->setForceRippled(true); + *menu = base::make_unique_q( + top, + st::popupMenuExpandedSeparator); + (*menu)->setDestroyedCallback([=, weak = top] { + if (const auto strong = weak.data()) { + strong->setForceRippled(false); + } + }); + FillMenuModerateCommonGroups(*menu, groups, collectCommon); + (*menu)->setForcedOrigin(PanelAnimation::Origin::TopRight); + const auto point = QPoint(top->width(), top->height()); + (*menu)->popup(top->mapToGlobal(point)); + }); + }); + using Request = Fn, not_null)>; const auto sequentiallyRequest = [=]( Request request, @@ -548,7 +735,16 @@ void CreateModerateMessagesBox( } } }; - sequentiallyRequest(request, controller->collectRequests()); + if (collectCommon && !collectCommon->empty()) { + sequentiallyRequest( + request, + controller->collectRequests(), + *collectCommon); + } else { + sequentiallyRequest( + request, + controller->collectRequests()); + } } }, ban->lifetime()); } diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 22fe65cc24..2d3c80e463 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 22fe65cc24f5a8bc92641f0850059c0ac6e4aec2 +Subproject commit 2d3c80e463ad0c939742e488fe82fb19738123ba