From 9fa11844f241f2a8bcc23f52e97d0246b9f2bcef Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 17 Jul 2026 17:34:48 +0300 Subject: [PATCH] Added members grouping by role to participants controller. --- .../boxes/peers/edit_participants_box.cpp | 152 ++++++++++++++---- .../boxes/peers/edit_participants_box.h | 11 ++ 2 files changed, 134 insertions(+), 29 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 5534c68532..68a4cc50e8 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -1034,33 +1034,37 @@ void ParticipantsOnlineSorter::sort() { _onlineCount = 0; return; } - const auto now = base::unixtime::now(); - _delegate->peerListSortRows([&]( - const PeerListRow &a, - const PeerListRow &b) { - return Data::SortByOnlineValue(a.peer()->asUser(), now) > - Data::SortByOnlineValue(b.peer()->asUser(), now); - }); + if (_sortingEnabled) { + const auto now = base::unixtime::now(); + _delegate->peerListSortRows([&]( + const PeerListRow &a, + const PeerListRow &b) { + return Data::SortByOnlineValue(a.peer()->asUser(), now) > + Data::SortByOnlineValue(b.peer()->asUser(), now); + }); + } refreshOnlineCount(); } +void ParticipantsOnlineSorter::setSortingEnabled(bool enabled) { + _sortingEnabled = enabled; +} + rpl::producer ParticipantsOnlineSorter::onlineCountValue() const { return _onlineCount.value(); } void ParticipantsOnlineSorter::refreshOnlineCount() { const auto now = base::unixtime::now(); - auto left = 0, right = _delegate->peerListFullRowsCount(); - while (right > left) { - const auto middle = (left + right) / 2; - const auto row = _delegate->peerListRowAt(middle); - if (Data::OnlineTextActive(row->peer()->asUser(), now)) { - left = middle + 1; - } else { - right = middle; + auto count = 0; + const auto rows = _delegate->peerListFullRowsCount(); + for (auto i = 0; i != rows; ++i) { + const auto user = _delegate->peerListRowAt(i)->peer()->asUser(); + if (user && Data::OnlineTextActive(user, now)) { + ++count; } } - _onlineCount = left; + _onlineCount = count; } ParticipantsBoxController::SavedState::SavedState( @@ -1112,6 +1116,20 @@ void ParticipantsBoxController::setupListChangeViewers() { channel->owner().megagroupParticipantAdded( channel ) | rpl::on_next([=](not_null user) { + if (_groupByRole.current()) { + if (!delegate()->peerListFindRow(user->id.value)) { + if (auto row = createRow(user)) { + const auto raw = row.get(); + delegate()->peerListPrependRow(std::move(row)); + if (_stories) { + _stories->process(raw); + } + refreshRows(); + resort(); + } + } + return; + } if (delegate()->peerListFullRowsCount() > 0) { if (delegate()->peerListRowAt(0)->peer() == user) { return; @@ -1128,9 +1146,7 @@ void ParticipantsBoxController::setupListChangeViewers() { _stories->process(raw); } refreshRows(); - if (_onlineSorter) { - _onlineSorter->sort(); - } + resort(); } }, lifetime()); @@ -1401,9 +1417,7 @@ void ParticipantsBoxController::restoreState( refreshRows(); } } - if (_onlineSorter) { - _onlineSorter->sort(); - } + resort(); } } @@ -1501,6 +1515,9 @@ void ParticipantsBoxController::prepare() { } } recomputeTypeFor(user); + if (_groupByRole.current()) { + resort(); + } refreshRows(); }, lifetime()); @@ -1618,7 +1635,7 @@ void ParticipantsBoxController::rebuildChatParticipants( } } } - _onlineSorter->sort(); + resort(); refreshRows(); chatListReady(); @@ -1777,9 +1794,7 @@ void ParticipantsBoxController::loadMoreRows() { || (firstLoad && delegate()->peerListFullRowsCount() > 0)) { refreshDescription(); } - if (_onlineSorter) { - _onlineSorter->sort(); - } + resort(); refreshRows(); }).fail([this] { _loadRequestId = 0; @@ -1839,9 +1854,7 @@ bool ParticipantsBoxController::feedMegagroupLastParticipants() { // //++_offset; } - if (_onlineSorter) { - _onlineSorter->sort(); - } + resort(); return added; } @@ -2644,6 +2657,87 @@ void ParticipantsBoxController::refreshRows() { delegate()->peerListRefreshRows(); } +int ParticipantsBoxController::memberRoleTier( + not_null peer) const { + const auto user = peer->asUser(); + if (user && _additional.isCreator(user)) { + return 0; + } else if (user && user->isBot()) { + return 2; + } else if (user && _additional.adminRights(user).has_value()) { + return 1; + } + return 3; +} + +void ParticipantsBoxController::sortByRoleAndName() { + delegate()->peerListSortRows([&]( + const PeerListRow &a, + const PeerListRow &b) { + const auto tierA = memberRoleTier(a.peer()); + const auto tierB = memberRoleTier(b.peer()); + return (tierA != tierB) + ? (tierA < tierB) + : (a.peer()->name().compare( + b.peer()->name(), + Qt::CaseInsensitive) < 0); + }); +} + +void ParticipantsBoxController::applyRoleSectionHeaders() { + const auto count = delegate()->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + const auto row = delegate()->peerListRowAt(i); + row->setSection([&] { + switch (memberRoleTier(row->peer())) { + case 0: return tr::lng_channel_admin_status_creator(tr::now); + case 1: return tr::lng_channel_admins(tr::now); + case 2: return tr::lng_filters_type_bots(tr::now); + } + return tr::lng_profile_participants_section(tr::now); + }()); + } +} + +void ParticipantsBoxController::resort() { + if (_groupByRole.current()) { + if (_onlineSorter) { + _onlineSorter->setSortingEnabled(false); + _onlineSorter->sort(); + } + sortByRoleAndName(); + applyRoleSectionHeaders(); + delegate()->peerListSetShowSectionHeaders(true); + delegate()->peerListRefreshRows(); + } else { + delegate()->peerListSetShowSectionHeaders(false); + if (_onlineSorter) { + _onlineSorter->setSortingEnabled(true); + _onlineSorter->sort(); + } + } +} + +void ParticipantsBoxController::setGroupByRole(bool grouped) { + if (_groupByRole.current() == grouped) { + return; + } + _groupByRole = grouped; + resort(); +} + +rpl::producer ParticipantsBoxController::groupByRoleValue() const { + return _groupByRole.value(); +} + +auto ParticipantsBoxController::groupByRoleAvailableValue() const +-> rpl::producer { + if (_peer->isMegagroup()) { + return Info::Profile::CanViewParticipantsValue(_peer->asMegagroup()); + } + return rpl::single(true); +} + ParticipantsBoxSearchController::ParticipantsBoxSearchController( not_null channel, Role role, diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index 6d40bec4c2..43b2786535 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -87,6 +87,7 @@ public: not_null delegate); void sort(); + void setSortingEnabled(bool enabled); rpl::producer onlineCountValue() const; private: @@ -97,6 +98,7 @@ private: const not_null _delegate; base::Timer _sortByOnlineTimer; rpl::variable _onlineCount = 0; + bool _sortingEnabled = true; rpl::lifetime _lifetime; }; @@ -228,6 +230,10 @@ public: [[nodiscard]] rpl::producer onlineCountValue() const; [[nodiscard]] rpl::producer fullCountValue() const; + void setGroupByRole(bool grouped); + [[nodiscard]] rpl::producer groupByRoleValue() const; + [[nodiscard]] rpl::producer groupByRoleAvailableValue() const; + void setStoriesShown(bool shown); protected: @@ -314,6 +320,10 @@ private: void subscribeToCreatorChange(not_null channel); void fullListRefresh(); void refreshRows(); + void resort(); + void sortByRoleAndName(); + void applyRoleSectionHeaders(); + [[nodiscard]] int memberRoleTier(not_null peer) const; // It may be nullptr in subclasses of this controller. Window::SessionNavigation *_navigation = nullptr; @@ -326,6 +336,7 @@ private: bool _allLoaded = false; ParticipantsAdditionalData _additional; std::unique_ptr _onlineSorter; + rpl::variable _groupByRole = false; rpl::variable _onlineCountValue; rpl::variable _fullCountValue; Ui::BoxPointer _editBox;