From 51808da2be6576fa8f7a1894f5a579f18e655fff Mon Sep 17 00:00:00 2001 From: RadRussianRus Date: Sun, 11 Sep 2022 04:13:57 +0300 Subject: [PATCH] [Improvement] Admin ranks --- Telegram/SourceFiles/boxes/peer_list_box.cpp | 112 ++++++++++++++++-- Telegram/SourceFiles/boxes/peer_list_box.h | 12 ++ .../boxes/peer_list_controllers.cpp | 57 ++++++++- .../SourceFiles/boxes/peer_list_controllers.h | 17 +++ .../boxes/peers/edit_participant_box.cpp | 18 +++ .../boxes/peers/edit_participant_box.h | 2 + .../boxes/peers/edit_participants_box.cpp | 20 +++- Telegram/SourceFiles/data/data_channel.cpp | 21 ++++ Telegram/SourceFiles/data/data_channel.h | 1 + .../info_profile_members_controllers.cpp | 29 ++--- .../info_profile_members_controllers.h | 5 +- .../profile/profile_block_group_members.cpp | 6 + .../profile/profile_block_peer_list.cpp | 17 +-- .../profile/profile_block_peer_list.h | 2 + 14 files changed, 276 insertions(+), 43 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index fe1cd918e..e61a8eb0c 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "styles/style_dialogs.h" #include "styles/style_widgets.h" +#include "styles/style_info.h" #include @@ -633,9 +634,37 @@ void PeerListRow::invalidatePixmapsCache() { } int PeerListRow::nameIconWidth() const { - return (special() || !_peer->isVerified()) + if (special()) { + return 0; + } + auto hasCreatorRights = false; + auto hasAdminRights = false; + if (const auto chat = _peer->asChat()) { + if (chat->amCreator()) { + hasCreatorRights = true; + hasAdminRights = true; + } else if (chat->hasAdminRights()) { + hasAdminRights = true; + } + } else if (const auto channel = _peer->asChannel()) { + if (channel->amCreator()) { + hasCreatorRights = true; + hasAdminRights = true; + } else if (channel->hasAdminRights()) { + hasAdminRights = true; + } + } + + return special() ? 0 - : st::dialogsVerifiedIcon.width(); + : (_peer->isVerified() + ? st::dialogsVerifiedIcon.width() + : 0) + + (hasCreatorRights + ? st::infoMembersCreatorIcon.width() + : hasAdminRights + ? st::infoMembersAdminIcon.width() + : 0); } void PeerListRow::paintNameIcon( @@ -644,7 +673,43 @@ void PeerListRow::paintNameIcon( int y, int outerWidth, bool selected) { - st::dialogsVerifiedIcon.paint(p, x, y, outerWidth); + if (special()) { + return; + } + + auto hasCreatorRights = false; + auto hasAdminRights = false; + if (const auto chat = _peer->asChat()) { + if (chat->amCreator()) { + hasCreatorRights = true; + hasAdminRights = true; + } else if (chat->hasAdminRights()) { + hasAdminRights = true; + } + } else if (const auto channel = _peer->asChannel()) { + if (channel->amCreator()) { + hasCreatorRights = true; + hasAdminRights = true; + } else if (channel->hasAdminRights()) { + hasAdminRights = true; + } + } + + auto icon = [&] { + return hasCreatorRights + ? (selected + ? &st::infoMembersCreatorIconOver + : &st::infoMembersCreatorIcon) + : (selected + ? &st::infoMembersAdminIconOver + : &st::infoMembersAdminIcon); + }(); + if (_peer->isVerified()) { + st::dialogsVerifiedIcon.paint(p, x, y, outerWidth); + } + if (hasAdminRights) { + icon->paint(p, x + (_peer->isVerified() ? st::dialogsVerifiedIcon.width() : 0 ), y, outerWidth); + } } void PeerListRow::paintStatusText( @@ -662,6 +727,10 @@ void PeerListRow::paintStatusText( _status.drawLeftElided(p, x, y, availableWidth, outerWidth); } +bool PeerListRow::hasAction() { + return true; +} + template void PeerListRow::addRipple(const style::PeerListItem &st, MaskGenerator &&maskGenerator, QPoint point, UpdateCallback &&updateCallback) { if (!_ripple) { @@ -674,6 +743,18 @@ void PeerListRow::addRipple(const style::PeerListItem &st, MaskGenerator &&maskG _ripple->add(point); } +int PeerListRow::adminRankWidth() const { + return 0; +} + +void PeerListRow::paintAdminRank( + Painter &p, + int x, + int y, + int outerWidth, + bool selected) { +} + void PeerListRow::stopLastRipple() { if (_ripple) { _ripple->lastStop(); @@ -1538,20 +1619,22 @@ crl::time PeerListContent::paintRow( p.setPen(st::contactsNameFg); auto skipRight = _st.item.photoPosition.x(); - auto rightActionSize = row->rightActionSize(); - auto rightActionMargins = rightActionSize.isEmpty() - ? QMargins() - : row->rightActionMargins(); + auto rightActionSize = !row->rightActionSize().isEmpty() + && (row->placeholderSize().isEmpty() + || selected) + ? row->rightActionSize() + : row->placeholderSize(); + auto rightActionMargins = rightActionSize.isEmpty() ? QMargins() : row->rightActionMargins(); auto &name = row->name(); auto namex = _st.item.namePosition.x(); auto namew = outerWidth - namex - skipRight; + auto statusw = namew; if (!rightActionSize.isEmpty()) { - namew -= rightActionMargins.left() + statusw -= rightActionMargins.left() + rightActionSize.width() + rightActionMargins.right() - skipRight; } - auto statusw = namew; if (auto iconWidth = row->nameIconWidth()) { namew -= iconWidth; row->paintNameIcon( @@ -1561,6 +1644,17 @@ crl::time PeerListContent::paintRow( width(), selected); } + if (auto adminRankWidth = row->adminRankWidth()) { + namew -= adminRankWidth + skipRight; + auto rankx = width() - adminRankWidth - skipRight; + p.setFont(st::normalFont); + row->paintAdminRank( + p, + rankx, + _st.item.namePosition.y(), + width(), + selected); + } auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio(); p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameCheckedRatio)); name.drawLeftElided(p, namex, _st.item.namePosition.y(), namew, width()); diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 39260c4e3..4fe28ff05 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -101,10 +101,20 @@ public: int y, int outerWidth, bool selected); + virtual int adminRankWidth() const; + virtual void paintAdminRank( + Painter &p, + int x, + int y, + int outerWidth, + bool selected); virtual QSize rightActionSize() const { return QSize(); } + virtual QSize placeholderSize() const { + return QSize(); + } virtual QMargins rightActionMargins() const { return QMargins(); } @@ -231,6 +241,8 @@ public: int outerWidth, bool selected); + virtual bool hasAction(); + protected: bool isInitialized() const { return _initialized; diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 6dbc9c95e..74c260b9c 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -146,9 +146,16 @@ void PeerListRowWithLink::setActionLink(const QString &action) { refreshActionLink(); } +void PeerListRowWithLink::setActionPlaceholder(const QString &placeholder, bool active) { + _actionPlaceholder = placeholder; + _actionPlaceholderActive = active; + refreshActionLink(); +} + void PeerListRowWithLink::refreshActionLink() { if (!isInitialized()) return; _actionWidth = _action.isEmpty() ? 0 : st::normalFont->width(_action); + _actionPlaceholderWidth = _actionPlaceholder.isEmpty() ? 0 : st::normalFont->width(_actionPlaceholder); } void PeerListRowWithLink::lazyInitialize(const style::PeerListItem &st) { @@ -156,10 +163,18 @@ void PeerListRowWithLink::lazyInitialize(const style::PeerListItem &st) { refreshActionLink(); } +bool PeerListRowWithLink::hasAction() { + return !_action.isEmpty(); +} + QSize PeerListRowWithLink::rightActionSize() const { return QSize(_actionWidth, st::normalFont->height); } +QSize PeerListRowWithLink::placeholderSize() const { + return QSize(_actionPlaceholderWidth, st::normalFont->height); +} + QMargins PeerListRowWithLink::rightActionMargins() const { return QMargins( st::contactsCheckPosition.x(), @@ -175,9 +190,45 @@ void PeerListRowWithLink::rightActionPaint( int outerWidth, bool selected, bool actionSelected) { - p.setFont(actionSelected ? st::linkOverFont : st::linkFont); - p.setPen(actionSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color); - p.drawTextLeft(x, y, outerWidth, _action, _actionWidth); + if (!_action.isEmpty() && ((_actionPlaceholder.isEmpty() && _adminRank.isEmpty()) || selected)) { + p.setFont(actionSelected ? st::linkOverFont : st::linkFont); + p.setPen(actionSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color); + p.drawTextLeft(x, y, outerWidth, _action, _actionWidth); + } else { + p.setFont(st::linkFont); + p.setPen(_actionPlaceholderActive + ? st::defaultPeerListItem.statusFgActive + : selected + ? st::defaultPeerListItem.statusFgOver + : st::defaultPeerListItem.statusFg); + p.drawTextLeft(x, y, outerWidth, _actionPlaceholder, _actionPlaceholderWidth); + } +} + +void PeerListRowWithLink::setAdminRank(const QString &rank, bool isCreator) { + _adminRank = rank; + _isCreator = isCreator; +} + +int PeerListRowWithLink::adminRankWidth() const { + return st::normalFont->width(_adminRank); +} + +void PeerListRowWithLink::paintAdminRank( + Painter &p, + int x, + int y, + int outerWidth, + bool selected) { + if (hasAction() && selected) { + return; + } + p.setPen(_isCreator + ? st::defaultPeerListItem.statusFgActive + : selected + ? st::defaultPeerListItem.statusFgOver + : st::defaultPeerListItem.statusFg); + p.drawTextLeft(x, y, outerWidth, _adminRank, adminRankWidth()); } PeerListGlobalSearchController::PeerListGlobalSearchController( diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index a9599d8f9..c3ddc5054 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -43,12 +43,24 @@ public: using PeerListRow::PeerListRow; void setActionLink(const QString &action); + void setActionPlaceholder(const QString &placeholder, bool active = false); void lazyInitialize(const style::PeerListItem &st) override; + bool hasAction() override; + + void setAdminRank(const QString &rank, bool isCreator = false); + int adminRankWidth() const override; + void paintAdminRank( + Painter &p, + int x, + int y, + int outerWidth, + bool selected) override; private: void refreshActionLink(); QSize rightActionSize() const override; + QSize placeholderSize() const override; QMargins rightActionMargins() const override; void rightActionPaint( Painter &p, @@ -59,8 +71,13 @@ private: bool actionSelected) override; QString _action; + QString _actionPlaceholder; + bool _actionPlaceholderActive = false; int _actionWidth = 0; + int _actionPlaceholderWidth = 0; + QString _adminRank; + bool _isCreator = false; }; class PeerListGlobalSearchController : public PeerListSearchController { diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index c59f8ed59..3bd4ea453 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -60,6 +60,7 @@ public: template Widget *addControl(object_ptr widget, QMargins margin); + void setCustomStatus(const QString &status); protected: int resizeGetHeight(int newWidth) override; @@ -72,6 +73,7 @@ private: Ui::Text::String _userName; bool _hasAdminRights = false; object_ptr _rows; + QString _customStatus; }; @@ -109,6 +111,10 @@ Widget *EditParticipantBox::Inner::addControl( return _rows->add(std::move(widget), margin); } +void EditParticipantBox::Inner::setCustomStatus(const QString &status) { + _customStatus = status; +} + int EditParticipantBox::Inner::resizeGetHeight(int newWidth) { _userPhoto->moveToLeft( st::rightsPhotoMargin.left(), @@ -138,6 +144,10 @@ void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) { namew, width()); const auto statusText = [&] { + if (!_customStatus.isEmpty()) { + return _customStatus; + } + if (_user->isBot()) { const auto seesAllMessages = _user->botInfo->readsAllHistory || _hasAdminRights; @@ -166,6 +176,13 @@ EditParticipantBox::EditParticipantBox( , _hasAdminRights(hasAdminRights) { } +void EditParticipantBox::setCustomStatus(const QString &status) { + _customStatus = status; + if (_inner) { + _inner->setCustomStatus(status); + } +} + void EditParticipantBox::prepare() { _inner = setInnerWidget(object_ptr( this, @@ -173,6 +190,7 @@ void EditParticipantBox::prepare() { _user, hasAdminRights())); setDimensionsToContent(st::boxWideWidth, _inner); + _inner->setCustomStatus(_customStatus); } template diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index 54f83210f..75c88365f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -36,6 +36,7 @@ public: not_null user, bool hasAdminRights); + void setCustomStatus(const QString &status); protected: void prepare() override; @@ -61,6 +62,7 @@ private: class Inner; QPointer _inner; + QString _customStatus; }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 4146a5823..4946938c4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -1912,10 +1912,17 @@ std::unique_ptr ParticipantsBoxController::createRow( refreshCustomStatus(row.get()); if (_role == Role::Admins && user - && !_additional.isCreator(user) - && _additional.adminRights(user).has_value() - && _additional.canEditAdmin(user)) { - row->setActionLink(tr::lng_profile_kick(tr::now)); + && (_additional.adminRights(user).has_value() + || _additional.isCreator(user))) { + if (_additional.canEditAdmin(user) && !_additional.isCreator(user)) { + row->setActionLink(tr::lng_profile_kick(tr::now)); + } + row->setAdminRank(channel + ? channel->adminRank(user) + : (chat && _additional.isCreator(user)) + ? tr::lng_owner_badge(tr::now) + : QString(), + _additional.isCreator(user)); } else if (_role == Role::Kicked || _role == Role::Restricted) { if (_additional.canRestrictParticipant(participant)) { row->setActionLink(tr::lng_profile_delete_removed(tr::now)); @@ -1948,6 +1955,11 @@ auto ParticipantsBoxController::computeType( ? Rights::Admin : Rights::Normal; result.canRemove = _additional.canRemoveParticipant(participant); + if (user) { + if (const auto channel = _peer->asChannel()) { + result.adminRank = channel->adminRank(user); + } + } return result; } diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index a35c1e879..13dfc26e7 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_histories.h" #include "data/data_group_call.h" #include "data/data_message_reactions.h" +#include "lang/lang_keys.h" #include "main/main_session.h" #include "main/session/send_as_peers.h" #include "base/unixtime.h" @@ -403,6 +404,26 @@ bool ChannelData::lastParticipantsRequestNeeded() const { & MegagroupInfo::LastParticipantsCountOutdated); } +QString ChannelData::adminRank(not_null user) const { + if (!isGroupAdmin(user)) { + return QString(); + } + const auto info = mgInfo.get(); + const auto i = mgInfo->admins.find(peerToUser(user->id)); + const auto custom = (i != mgInfo->admins.end()) + ? i->second + : (info->creator == user) + ? info->creatorRank + : QString(); + return !custom.isEmpty() + ? custom + : (info->creator == user) + ? tr::lng_owner_badge(tr::now) + : (i != mgInfo->admins.end()) + ? tr::lng_admin_badge(tr::now) + : QString(); +} + auto ChannelData::unavailableReasons() const -> const std::vector & { return _unavailableReasons; diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 12d7a1389..76d3b2498 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -233,6 +233,7 @@ public: void markForbidden(); [[nodiscard]] bool isGroupAdmin(not_null user) const; + [[nodiscard]] QString adminRank(not_null user) const; [[nodiscard]] bool lastParticipantsRequestNeeded() const; [[nodiscard]] bool isMegagroup() const { return flags() & Flag::Megagroup; diff --git a/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp b/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp index 435236ed0..6eb37fa12 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp @@ -62,34 +62,29 @@ void MemberListRow::rightActionPaint( } } -int MemberListRow::nameIconWidth() const { - return (_type.rights == Rights::Admin) - ? st::infoMembersAdminIcon.width() - : (_type.rights == Rights::Creator) - ? st::infoMembersCreatorIcon.width() - : 0; +int MemberListRow::adminRankWidth() const { + return st::normalFont->width(_type.adminRank); } not_null MemberListRow::user() const { return peer()->asUser(); } -void MemberListRow::paintNameIcon( +void MemberListRow::paintAdminRank( Painter &p, int x, int y, int outerWidth, bool selected) { - auto icon = [&] { - return (_type.rights == Rights::Admin) - ? (selected - ? &st::infoMembersAdminIconOver - : &st::infoMembersAdminIcon) - : (selected - ? &st::infoMembersCreatorIconOver - : &st::infoMembersCreatorIcon); - }(); - icon->paint(p, x, y, outerWidth); + if (_type.canRemove && selected) { + return; + } + p.setPen(_type.rights == Rights::Creator + ? st::defaultPeerListItem.statusFgActive + : selected + ? st::defaultPeerListItem.statusFgOver + : st::defaultPeerListItem.statusFg); + p.drawTextLeft(x, y, outerWidth, _type.adminRank, adminRankWidth()); } void MemberListRow::refreshStatus() { diff --git a/Telegram/SourceFiles/info/profile/info_profile_members_controllers.h b/Telegram/SourceFiles/info/profile/info_profile_members_controllers.h index d081e559a..6cdd09b49 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members_controllers.h +++ b/Telegram/SourceFiles/info/profile/info_profile_members_controllers.h @@ -26,6 +26,7 @@ public: struct Type { Rights rights; bool canRemove = false; + QString adminRank; }; MemberListRow(not_null user, Type type); @@ -39,8 +40,8 @@ public: int outerWidth, bool selected, bool actionSelected) override; - int nameIconWidth() const override; - void paintNameIcon( + int adminRankWidth() const override; + void paintAdminRank( Painter &p, int x, int y, diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp index e23f6c8b9..1322edabe 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp @@ -307,6 +307,10 @@ void GroupMembersWidget::setItemFlags( ? AdminState::Admin : AdminState::None; item->adminState = adminState; + if (isCreator) { + item->adminRank = tr::lng_owner_badge(tr::now); + item->adminRankWidth = st::normalFont->width(item->adminRank); + } if (item->peer->id == chat->session().userPeerId()) { item->hasRemoveLink = false; } else if (chat->amCreator() @@ -410,6 +414,8 @@ void GroupMembersWidget::setItemFlags( updateItemStatusText(item); } } + item->adminRank = megagroup->adminRank(item->peer->asUser()); + item->adminRankWidth = st::normalFont->width(item->adminRank); if (item->peer->isSelf()) { item->hasRemoveLink = false; } else if (megagroup->amCreator() || (megagroup->canBanMembers() && ((adminState == AdminState::None) || adminCanEdit))) { diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp index 329d15ab3..0d5b5619f 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp @@ -110,14 +110,15 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select p.setPen(st::windowActiveTextFg); p.drawTextLeft(nameLeft + nameWidth - _removeWidth, nameTop, width(), _removeText, _removeWidth); nameWidth -= _removeWidth + skip; - } - if (item->adminState != Item::AdminState::None) { - nameWidth -= st::profileMemberAdminIcon.width(); - auto iconLeft = nameLeft + qMin(nameWidth, item->name.maxWidth()); - auto &icon = (item->adminState == Item::AdminState::Creator) - ? (selected ? st::profileMemberCreatorIconOver : st::profileMemberCreatorIcon) - : (selected ? st::profileMemberAdminIconOver : st::profileMemberAdminIcon); - icon.paint(p, QPoint(iconLeft, nameTop), width()); + } else if (item->adminState != Item::AdminState::None) { + p.setFont(st::normalFont); + p.setPen(item->adminState == Item::AdminState::Creator + ? _st.statusFgActive + : selected + ? _st.statusFgOver + : _st.statusFg); + p.drawTextLeft(nameLeft + nameWidth - item->adminRankWidth, nameTop, width(), item->adminRank, item->adminRankWidth); + nameWidth -= item->adminRankWidth + skip; } p.setPen(st::profileMemberNameFg); item->name.drawLeftElided(p, nameLeft, nameTop, nameWidth, width()); diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.h b/Telegram/SourceFiles/profile/profile_block_peer_list.h index 02a208ef8..2d69d37a0 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.h +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.h @@ -43,6 +43,8 @@ public: Creator, }; AdminState adminState = AdminState::None; + QString adminRank; + int adminRankWidth = 0; bool hasRemoveLink = false; std::unique_ptr ripple; };