[Improvement] Admin ranks

This commit is contained in:
Eric Kotato 2022-09-11 04:13:57 +03:00
parent dec9de947c
commit 51808da2be
14 changed files with 276 additions and 43 deletions

View file

@ -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 <rpl/range.h>
@ -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 <typename MaskGenerator, typename UpdateCallback>
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());

View file

@ -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;

View file

@ -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(

View file

@ -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 {

View file

@ -60,6 +60,7 @@ public:
template <typename Widget>
Widget *addControl(object_ptr<Widget> 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<Ui::VerticalLayout> _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<Inner>(
this,
@ -173,6 +190,7 @@ void EditParticipantBox::prepare() {
_user,
hasAdminRights()));
setDimensionsToContent(st::boxWideWidth, _inner);
_inner->setCustomStatus(_customStatus);
}
template <typename Widget>

View file

@ -36,6 +36,7 @@ public:
not_null<UserData*> user,
bool hasAdminRights);
void setCustomStatus(const QString &status);
protected:
void prepare() override;
@ -61,6 +62,7 @@ private:
class Inner;
QPointer<Inner> _inner;
QString _customStatus;
};

View file

@ -1912,10 +1912,17 @@ std::unique_ptr<PeerListRow> 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;
}

View file

@ -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<UserData*> 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<Data::UnavailableReason> & {
return _unavailableReasons;

View file

@ -233,6 +233,7 @@ public:
void markForbidden();
[[nodiscard]] bool isGroupAdmin(not_null<UserData*> user) const;
[[nodiscard]] QString adminRank(not_null<UserData*> user) const;
[[nodiscard]] bool lastParticipantsRequestNeeded() const;
[[nodiscard]] bool isMegagroup() const {
return flags() & Flag::Megagroup;

View file

@ -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<UserData*> 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() {

View file

@ -26,6 +26,7 @@ public:
struct Type {
Rights rights;
bool canRemove = false;
QString adminRank;
};
MemberListRow(not_null<UserData*> 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,

View file

@ -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))) {

View file

@ -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());

View file

@ -43,6 +43,8 @@ public:
Creator,
};
AdminState adminState = AdminState::None;
QString adminRank;
int adminRankWidth = 0;
bool hasRemoveLink = false;
std::unique_ptr<Ui::RippleAnimation> ripple;
};