Display custom admin ranks in messages.

This commit is contained in:
John Preston 2019-07-19 15:34:09 +02:00
parent 22f210ea8e
commit f36e2981ca
20 changed files with 252 additions and 140 deletions

View file

@ -1623,11 +1623,7 @@ void ApiWrap::requestAdmins(not_null<ChannelData*> channel) {
)).done([this, channel](const MTPchannels_ChannelParticipants &result) { )).done([this, channel](const MTPchannels_ChannelParticipants &result) {
_adminsRequests.remove(channel); _adminsRequests.remove(channel);
result.match([&](const MTPDchannels_channelParticipants &data) { result.match([&](const MTPDchannels_channelParticipants &data) {
_session->data().processUsers(data.vusers()); Data::ApplyChannelAdmins(channel, data);
applyAdminsList(
channel,
data.vcount().v,
data.vparticipants().v);
}, [&](const MTPDchannels_channelParticipantsNotModified &) { }, [&](const MTPDchannels_channelParticipantsNotModified &) {
LOG(("API Error: channels.channelParticipantsNotModified received!")); LOG(("API Error: channels.channelParticipantsNotModified received!"));
}); });
@ -1671,10 +1667,12 @@ void ApiWrap::applyLastParticipantsList(
auto user = _session->data().user(userId); auto user = _session->data().user(userId);
if (p.type() == mtpc_channelParticipantCreator) { if (p.type() == mtpc_channelParticipantCreator) {
const auto &creator = p.c_channelParticipantCreator();
const auto rank = qs(creator.vrank().value_or_empty());
channel->mgInfo->creator = user; channel->mgInfo->creator = user;
if (!channel->mgInfo->admins.empty() channel->mgInfo->creatorRank = rank;
&& !channel->mgInfo->admins.contains(userId)) { if (!channel->mgInfo->admins.empty()) {
Data::ChannelAdminChanges(channel).feed(userId, true); Data::ChannelAdminChanges(channel).add(userId, rank);
} }
} }
if (!base::contains(channel->mgInfo->lastParticipants, user)) { if (!base::contains(channel->mgInfo->lastParticipants, user)) {
@ -1758,39 +1756,6 @@ void ApiWrap::applyBotsList(
fullPeerUpdated().notify(channel); fullPeerUpdated().notify(channel);
} }
void ApiWrap::applyAdminsList(
not_null<ChannelData*> channel,
int availableCount,
const QVector<MTPChannelParticipant> &list) {
auto admins = ranges::make_iterator_range(
list.begin(), list.end()
) | ranges::view::transform([](const MTPChannelParticipant &p) {
return p.match([](const auto &data) { return data.vuser_id().v; });
});
auto adding = base::flat_set<UserId>{ admins.begin(), admins.end() };
if (channel->mgInfo->creator) {
adding.insert(peerToUser(channel->mgInfo->creator->id));
}
auto removing = channel->mgInfo->admins;
if (removing.empty() && adding.empty()) {
// Add some admin-placeholder so we don't DDOS
// server with admins list requests.
LOG(("API Error: Got empty admins list from server."));
adding.insert(0);
}
Data::ChannelAdminChanges changes(channel);
for (const auto addingId : adding) {
if (!removing.remove(addingId)) {
changes.feed(addingId, true);
}
}
for (const auto removingId : removing) {
changes.feed(removingId, false);
}
}
void ApiWrap::requestSelfParticipant(not_null<ChannelData*> channel) { void ApiWrap::requestSelfParticipant(not_null<ChannelData*> channel) {
if (_selfParticipantRequests.contains(channel)) { if (_selfParticipantRequests.contains(channel)) {
return; return;
@ -3547,9 +3512,13 @@ void ApiWrap::refreshChannelAdmins(
const auto userId = p.match([](const auto &data) { const auto userId = p.match([](const auto &data) {
return data.vuser_id().v; return data.vuser_id().v;
}); });
const auto isAdmin = (p.type() == mtpc_channelParticipantAdmin) p.match([&](const MTPDchannelParticipantAdmin &data) {
|| (p.type() == mtpc_channelParticipantCreator); changes.add(userId, qs(data.vrank().value_or_empty()));
changes.feed(userId, isAdmin); }, [&](const MTPDchannelParticipantCreator &data) {
changes.add(userId, qs(data.vrank().value_or_empty()));
}, [&](const auto &data) {
changes.remove(userId);
});
} }
} }

View file

@ -570,10 +570,6 @@ private:
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
int availableCount, int availableCount,
const QVector<MTPChannelParticipant> &list); const QVector<MTPChannelParticipant> &list);
void applyAdminsList(
not_null<ChannelData*> channel,
int availableCount,
const QVector<MTPChannelParticipant> &list);
void resolveWebPages(); void resolveWebPages();
void gotWebPages( void gotWebPages(
ChannelData *channel, ChannelData *channel,

View file

@ -103,7 +103,8 @@ void ShowAddParticipantsError(
auto box = Box<EditAdminBox>( auto box = Box<EditAdminBox>(
channel, channel,
user, user,
MTP_chatAdminRights(MTP_flags(0))); MTP_chatAdminRights(MTP_flags(0)),
QString());
box->setSaveCallback(saveCallback); box->setSaveCallback(saveCallback);
*weak = Ui::show(std::move(box)); *weak = Ui::show(std::move(box));
}; };

View file

@ -553,11 +553,16 @@ void AddSpecialBoxController::showAdmin(
: adminRights : adminRights
? *adminRights ? *adminRights
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0))); : MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
auto box = Box<EditAdminBox>(_peer, user, currentRights); auto box = Box<EditAdminBox>(
_peer,
user,
currentRights,
_additional.adminRank(user));
if (_additional.canAddOrEditAdmin(user)) { if (_additional.canAddOrEditAdmin(user)) {
const auto done = crl::guard(this, [=]( const auto done = crl::guard(this, [=](
const MTPChatAdminRights &newRights) { const MTPChatAdminRights &newRights,
editAdminDone(user, newRights); const QString &rank) {
editAdminDone(user, newRights, rank);
}); });
const auto fail = crl::guard(this, [=] { const auto fail = crl::guard(this, [=] {
if (_editParticipantBox) { if (_editParticipantBox) {
@ -571,7 +576,8 @@ void AddSpecialBoxController::showAdmin(
void AddSpecialBoxController::editAdminDone( void AddSpecialBoxController::editAdminDone(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &rights) { const MTPChatAdminRights &rights,
const QString &rank) {
if (_editParticipantBox) { if (_editParticipantBox) {
_editParticipantBox->closeBox(); _editParticipantBox->closeBox();
} }
@ -582,9 +588,11 @@ void AddSpecialBoxController::editAdminDone(
MTP_int(user->bareId()), MTP_int(user->bareId()),
MTP_int(date))); MTP_int(date)));
} else { } else {
using Flag = MTPDchannelParticipantAdmin::Flag;
const auto alreadyPromotedBy = _additional.adminPromotedBy(user); const auto alreadyPromotedBy = _additional.adminPromotedBy(user);
_additional.applyParticipant(MTP_channelParticipantAdmin( _additional.applyParticipant(MTP_channelParticipantAdmin(
MTP_flags(MTPDchannelParticipantAdmin::Flag::f_can_edit), MTP_flags(Flag::f_can_edit
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
MTP_int(user->bareId()), MTP_int(user->bareId()),
MTPint(), // inviter_id MTPint(), // inviter_id
MTP_int(alreadyPromotedBy MTP_int(alreadyPromotedBy
@ -592,10 +600,10 @@ void AddSpecialBoxController::editAdminDone(
: user->session().userId()), : user->session().userId()),
MTP_int(date), MTP_int(date),
rights, rights,
MTPstring())); // #TODO ranks MTP_string(rank)));
} }
if (const auto callback = _adminDoneCallback) { if (const auto callback = _adminDoneCallback) {
callback(user, rights); callback(user, rights, rank);
} }
} }

View file

@ -62,7 +62,8 @@ public:
using AdminDoneCallback = Fn<void( using AdminDoneCallback = Fn<void(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &adminRights)>; const MTPChatAdminRights &adminRights,
const QString &rank)>;
using BannedDoneCallback = Fn<void( using BannedDoneCallback = Fn<void(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatBannedRights &bannedRights)>; const MTPChatBannedRights &bannedRights)>;
@ -89,7 +90,8 @@ private:
void showAdmin(not_null<UserData*> user, bool sure = false); void showAdmin(not_null<UserData*> user, bool sure = false);
void editAdminDone( void editAdminDone(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &rights); const MTPChatAdminRights &rights,
const QString &rank);
void showRestricted(not_null<UserData*> user, bool sure = false); void showRestricted(not_null<UserData*> user, bool sure = false);
void editRestrictedDone( void editRestrictedDone(
not_null<UserData*> user, not_null<UserData*> user,

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/text_options.h" #include "ui/text_options.h"
@ -247,13 +248,15 @@ EditAdminBox::EditAdminBox(
QWidget*, QWidget*,
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &rights) const MTPChatAdminRights &rights,
const QString &rank)
: EditParticipantBox( : EditParticipantBox(
nullptr, nullptr,
peer, peer,
user, user,
(rights.c_chatAdminRights().vflags().v != 0)) (rights.c_chatAdminRights().vflags().v != 0))
, _oldRights(rights) { , _oldRights(rights)
, _oldRank(rank) {
} }
MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) { MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) {
@ -362,6 +365,16 @@ void EditAdminBox::prepare() {
refreshAboutAddAdminsText(checked); refreshAboutAddAdminsText(checked);
}, lifetime()); }, lifetime());
const auto rank = canSave()
? addControl(
object_ptr<Ui::InputField>(
this,
st::defaultInputField,
tr::lng_rights_edit_admin_header(),
_oldRank),
st::rightsAboutMargin)
: nullptr;
if (canSave()) { if (canSave()) {
addButton(tr::lng_settings_save(), [=, value = getChecked] { addButton(tr::lng_settings_save(), [=, value = getChecked] {
if (!_saveCallback) { if (!_saveCallback) {
@ -373,7 +386,8 @@ void EditAdminBox::prepare() {
: channel->adminRights()); : channel->adminRights());
_saveCallback( _saveCallback(
_oldRights, _oldRights,
MTP_chatAdminRights(MTP_flags(newFlags))); MTP_chatAdminRights(MTP_flags(newFlags)),
rank->getLastText().trimmed());
}); });
addButton(tr::lng_cancel(), [this] { closeBox(); }); addButton(tr::lng_cancel(), [this] { closeBox(); });
} else { } else {

View file

@ -68,10 +68,14 @@ public:
QWidget*, QWidget*,
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &rights); const MTPChatAdminRights &rights,
const QString &rank);
void setSaveCallback( void setSaveCallback(
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> callback) { Fn<void(
MTPChatAdminRights,
MTPChatAdminRights,
const QString &rank)> callback) {
_saveCallback = std::move(callback); _saveCallback = std::move(callback);
} }
@ -93,14 +97,18 @@ private:
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
const Core::CloudPasswordResult &result); const Core::CloudPasswordResult &result);
bool canSave() const { bool canSave() const {
return !!_saveCallback; return _saveCallback != nullptr;
} }
void refreshAboutAddAdminsText(bool canAddAdmins); void refreshAboutAddAdminsText(bool canAddAdmins);
bool canTransferOwnership() const; bool canTransferOwnership() const;
not_null<Ui::SlideWrap<Ui::RpWidget>*> setupTransferButton(bool isGroup); not_null<Ui::SlideWrap<Ui::RpWidget>*> setupTransferButton(bool isGroup);
const MTPChatAdminRights _oldRights; const MTPChatAdminRights _oldRights;
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> _saveCallback; const QString _oldRank;
Fn<void(
MTPChatAdminRights,
MTPChatAdminRights,
const QString &rank)> _saveCallback;
QPointer<Ui::FlatLabel> _aboutAddAdmins; QPointer<Ui::FlatLabel> _aboutAddAdmins;
mtpRequestId _checkTransferRequestId = 0; mtpRequestId _checkTransferRequestId = 0;

View file

@ -49,10 +49,10 @@ void RemoveAdmin(
channel->inputChannel, channel->inputChannel,
user->inputUser, user->inputUser,
newRights, newRights,
MTP_string(QString()) // #TODO ranks MTP_string(QString())
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result); channel->session().api().applyUpdates(result);
channel->applyEditAdmin(user, oldRights, newRights); channel->applyEditAdmin(user, oldRights, newRights, QString());
if (onDone) { if (onDone) {
onDone(); onDone();
} }
@ -120,16 +120,17 @@ void SaveChannelAdmin(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &oldRights, const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights, const MTPChatAdminRights &newRights,
const QString &rank,
Fn<void()> onDone, Fn<void()> onDone,
Fn<void()> onFail) { Fn<void()> onFail) {
channel->session().api().request(MTPchannels_EditAdmin( channel->session().api().request(MTPchannels_EditAdmin(
channel->inputChannel, channel->inputChannel,
user->inputUser, user->inputUser,
newRights, newRights,
MTP_string(QString()) // #TODO ranks MTP_string(rank)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result); channel->session().api().applyUpdates(result);
channel->applyEditAdmin(user, oldRights, newRights); channel->applyEditAdmin(user, oldRights, newRights, rank);
if (onDone) { if (onDone) {
onDone(); onDone();
} }
@ -189,21 +190,26 @@ void SaveChatParticipantKick(
Fn<void( Fn<void(
const MTPChatAdminRights &oldRights, const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights)> SaveAdminCallback( const MTPChatAdminRights &newRights,
const QString &rank)> SaveAdminCallback(
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> user, not_null<UserData*> user,
Fn<void(const MTPChatAdminRights &newRights)> onDone, Fn<void(
const MTPChatAdminRights &newRights,
const QString &rank)> onDone,
Fn<void()> onFail) { Fn<void()> onFail) {
return [=]( return [=](
const MTPChatAdminRights &oldRights, const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights) { const MTPChatAdminRights &newRights,
const auto done = [=] { if (onDone) onDone(newRights); }; const QString &rank) {
const auto done = [=] { if (onDone) onDone(newRights, rank); };
const auto saveForChannel = [=](not_null<ChannelData*> channel) { const auto saveForChannel = [=](not_null<ChannelData*> channel) {
SaveChannelAdmin( SaveChannelAdmin(
channel, channel,
user, user,
oldRights, oldRights,
newRights, newRights,
rank,
done, done,
onFail); onFail);
}; };
@ -361,6 +367,12 @@ auto ParticipantsAdditionalData::adminRights(
: std::nullopt; : std::nullopt;
} }
QString ParticipantsAdditionalData::adminRank(
not_null<UserData*> user) const {
const auto i = _adminRanks.find(user);
return (i != end(_adminRanks)) ? i->second : QString();
}
auto ParticipantsAdditionalData::restrictedRights( auto ParticipantsAdditionalData::restrictedRights(
not_null<UserData*> user) const not_null<UserData*> user) const
-> std::optional<MTPChatBannedRights> { -> std::optional<MTPChatBannedRights> {
@ -455,9 +467,11 @@ void ParticipantsAdditionalData::fillFromChannel(
} }
if (information->creator) { if (information->creator) {
_creator = information->creator; _creator = information->creator;
_adminRanks[information->creator] = information->creatorRank;
} }
for (const auto user : information->lastParticipants) { for (const auto user : information->lastParticipants) {
const auto admin = information->lastAdmins.find(user); const auto admin = information->lastAdmins.find(user);
const auto rank = information->admins.find(peerToUser(user->id));
const auto restricted = information->lastRestricted.find(user); const auto restricted = information->lastRestricted.find(user);
if (admin != information->lastAdmins.cend()) { if (admin != information->lastAdmins.cend()) {
_restrictedRights.erase(user); _restrictedRights.erase(user);
@ -469,6 +483,10 @@ void ParticipantsAdditionalData::fillFromChannel(
_adminCanEdit.erase(user); _adminCanEdit.erase(user);
} }
_adminRights.emplace(user, admin->second.rights); _adminRights.emplace(user, admin->second.rights);
if (rank != end(information->admins)
&& !rank->second.isEmpty()) {
_adminRanks[user] = rank->second;
}
} else if (restricted != information->lastRestricted.cend()) { } else if (restricted != information->lastRestricted.cend()) {
_adminRights.erase(user); _adminRights.erase(user);
_adminCanEdit.erase(user); _adminCanEdit.erase(user);
@ -535,6 +553,11 @@ UserData *ParticipantsAdditionalData::applyCreator(
const MTPDchannelParticipantCreator &data) { const MTPDchannelParticipantCreator &data) {
if (const auto user = applyRegular(data.vuser_id())) { if (const auto user = applyRegular(data.vuser_id())) {
_creator = user; _creator = user;
if (const auto rank = data.vrank()) {
_adminRanks[user] = qs(*rank);
} else {
_adminRanks.remove(user);
}
return user; return user;
} }
return nullptr; return nullptr;
@ -561,6 +584,11 @@ UserData *ParticipantsAdditionalData::applyAdmin(
} else { } else {
_adminCanEdit.erase(user); _adminCanEdit.erase(user);
} }
if (const auto rank = data.vrank()) {
_adminRanks[user] = qs(*rank);
} else {
_adminRanks.remove(user);
}
if (const auto by = _peer->owner().userLoaded(data.vpromoted_by().v)) { if (const auto by = _peer->owner().userLoaded(data.vpromoted_by().v)) {
const auto i = _adminPromotedBy.find(user); const auto i = _adminPromotedBy.find(user);
if (i == _adminPromotedBy.end()) { if (i == _adminPromotedBy.end()) {
@ -852,8 +880,9 @@ void ParticipantsBoxController::addNewItem() {
} }
const auto adminDone = crl::guard(this, [=]( const auto adminDone = crl::guard(this, [=](
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &rights) { const MTPChatAdminRights &rights,
editAdminDone(user, rights); const QString &rank) {
editAdminDone(user, rights, rank);
}); });
const auto restrictedDone = crl::guard(this, [=]( const auto restrictedDone = crl::guard(this, [=](
not_null<UserData*> user, not_null<UserData*> user,
@ -1418,13 +1447,18 @@ void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
: adminRights : adminRights
? *adminRights ? *adminRights
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0))); : MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
auto box = Box<EditAdminBox>(_peer, user, currentRights); auto box = Box<EditAdminBox>(
_peer,
user,
currentRights,
_additional.adminRank(user));
const auto chat = _peer->asChat(); const auto chat = _peer->asChat();
const auto channel = _peer->asChannel(); const auto channel = _peer->asChannel();
if (_additional.canAddOrEditAdmin(user)) { if (_additional.canAddOrEditAdmin(user)) {
const auto done = crl::guard(this, [=]( const auto done = crl::guard(this, [=](
const MTPChatAdminRights &newRights) { const MTPChatAdminRights &newRights,
editAdminDone(user, newRights); const QString &rank) {
editAdminDone(user, newRights, rank);
}); });
const auto fail = crl::guard(this, [=] { const auto fail = crl::guard(this, [=] {
if (_editParticipantBox) { if (_editParticipantBox) {
@ -1438,7 +1472,8 @@ void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
void ParticipantsBoxController::editAdminDone( void ParticipantsBoxController::editAdminDone(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &rights) { const MTPChatAdminRights &rights,
const QString &rank) {
_addBox = nullptr; _addBox = nullptr;
if (_editParticipantBox) { if (_editParticipantBox) {
_editParticipantBox->closeBox(); _editParticipantBox->closeBox();
@ -1453,9 +1488,11 @@ void ParticipantsBoxController::editAdminDone(
removeRow(user); removeRow(user);
} }
} else { } else {
using Flag = MTPDchannelParticipantAdmin::Flag;
const auto alreadyPromotedBy = _additional.adminPromotedBy(user); const auto alreadyPromotedBy = _additional.adminPromotedBy(user);
_additional.applyParticipant(MTP_channelParticipantAdmin( _additional.applyParticipant(MTP_channelParticipantAdmin(
MTP_flags(MTPDchannelParticipantAdmin::Flag::f_can_edit), MTP_flags(Flag::f_can_edit
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
MTP_int(user->bareId()), MTP_int(user->bareId()),
MTPint(), // inviter_id MTPint(), // inviter_id
MTP_int(alreadyPromotedBy MTP_int(alreadyPromotedBy
@ -1463,7 +1500,7 @@ void ParticipantsBoxController::editAdminDone(
: user->session().userId()), : user->session().userId()),
MTP_int(date), MTP_int(date),
rights, rights,
MTPstring())); // #TODO ranks MTP_string(rank)));
if (_role == Role::Admins) { if (_role == Role::Admins) {
prependRow(user); prependRow(user);
} else if (_role == Role::Kicked || _role == Role::Restricted) { } else if (_role == Role::Kicked || _role == Role::Restricted) {
@ -1619,7 +1656,10 @@ void ParticipantsBoxController::removeAdminSure(not_null<UserData*> user) {
if (const auto chat = _peer->asChat()) { if (const auto chat = _peer->asChat()) {
SaveChatAdmin(chat, user, false, crl::guard(this, [=] { SaveChatAdmin(chat, user, false, crl::guard(this, [=] {
editAdminDone(user, MTP_chatAdminRights(MTP_flags(0))); editAdminDone(
user,
MTP_chatAdminRights(MTP_flags(0)),
QString());
}), nullptr); }), nullptr);
} else if (const auto channel = _peer->asChannel()) { } else if (const auto channel = _peer->asChannel()) {
const auto adminRights = _additional.adminRights(user); const auto adminRights = _additional.adminRights(user);
@ -1627,7 +1667,10 @@ void ParticipantsBoxController::removeAdminSure(not_null<UserData*> user) {
return; return;
} }
RemoveAdmin(channel, user, *adminRights, crl::guard(this, [=] { RemoveAdmin(channel, user, *adminRights, crl::guard(this, [=] {
editAdminDone(user, MTP_chatAdminRights(MTP_flags(0))); editAdminDone(
user,
MTP_chatAdminRights(MTP_flags(0)),
QString());
}), nullptr); }), nullptr);
} }
} }
@ -1819,9 +1862,9 @@ void ParticipantsBoxController::subscribeToCreatorChange(
MTP_int(Global::ChatSizeMax()), MTP_int(Global::ChatSizeMax()),
MTP_int(0) MTP_int(0)
)).done([=](const MTPchannels_ChannelParticipants &result) { )).done([=](const MTPchannels_ChannelParticipants &result) {
channel->mgInfo->creator = channel->amCreator() if (channel->amCreator()) {
? channel->session().user().get() channel->mgInfo->creator = channel->session().user().get();
: nullptr; }
channel->mgInfo->lastAdmins.clear(); channel->mgInfo->lastAdmins.clear();
channel->mgInfo->lastRestricted.clear(); channel->mgInfo->lastRestricted.clear();
channel->mgInfo->lastParticipants.clear(); channel->mgInfo->lastParticipants.clear();

View file

@ -20,10 +20,13 @@ class SessionNavigation;
Fn<void( Fn<void(
const MTPChatAdminRights &oldRights, const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights)> SaveAdminCallback( const MTPChatAdminRights &newRights,
const QString &rank)> SaveAdminCallback(
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> user, not_null<UserData*> user,
Fn<void(const MTPChatAdminRights &newRights)> onDone, Fn<void(
const MTPChatAdminRights &newRights,
const QString &rank)> onDone,
Fn<void()> onFail); Fn<void()> onFail);
Fn<void( Fn<void(
@ -87,6 +90,7 @@ public:
[[nodiscard]] bool canRestrictUser(not_null<UserData*> user) const; [[nodiscard]] bool canRestrictUser(not_null<UserData*> user) const;
[[nodiscard]] std::optional<MTPChatAdminRights> adminRights( [[nodiscard]] std::optional<MTPChatAdminRights> adminRights(
not_null<UserData*> user) const; not_null<UserData*> user) const;
QString adminRank(not_null<UserData*> user) const;
[[nodiscard]] std::optional<MTPChatBannedRights> restrictedRights( [[nodiscard]] std::optional<MTPChatBannedRights> restrictedRights(
not_null<UserData*> user) const; not_null<UserData*> user) const;
[[nodiscard]] bool isCreator(not_null<UserData*> user) const; [[nodiscard]] bool isCreator(not_null<UserData*> user) const;
@ -104,7 +108,6 @@ private:
UserData *applyBanned(const MTPDchannelParticipantBanned &data); UserData *applyBanned(const MTPDchannelParticipantBanned &data);
void fillFromChat(not_null<ChatData*> chat); void fillFromChat(not_null<ChatData*> chat);
void fillFromChannel(not_null<ChannelData*> channel); void fillFromChannel(not_null<ChannelData*> channel);
void subscribeToCreatorChange(not_null<ChannelData*> channel);
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
Role _role = Role::Members; Role _role = Role::Members;
@ -116,6 +119,7 @@ private:
// Data for channels. // Data for channels.
base::flat_map<not_null<UserData*>, MTPChatAdminRights> _adminRights; base::flat_map<not_null<UserData*>, MTPChatAdminRights> _adminRights;
base::flat_map<not_null<UserData*>, QString> _adminRanks;
base::flat_set<not_null<UserData*>> _adminCanEdit; base::flat_set<not_null<UserData*>> _adminCanEdit;
base::flat_map<not_null<UserData*>, not_null<UserData*>> _adminPromotedBy; base::flat_map<not_null<UserData*>, not_null<UserData*>> _adminPromotedBy;
std::map<not_null<UserData*>, MTPChatBannedRights> _restrictedRights; std::map<not_null<UserData*>, MTPChatBannedRights> _restrictedRights;
@ -204,7 +208,8 @@ private:
void showAdmin(not_null<UserData*> user); void showAdmin(not_null<UserData*> user);
void editAdminDone( void editAdminDone(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &rights); const MTPChatAdminRights &rights,
const QString &rank);
void showRestricted(not_null<UserData*> user); void showRestricted(not_null<UserData*> user);
void editRestrictedDone( void editRestrictedDone(
not_null<UserData*> user, not_null<UserData*> user,

View file

@ -701,7 +701,7 @@ for restype in typesList:
prmsInit.append('_' + paramName + '(' + paramName + '_)'); prmsInit.append('_' + paramName + '(' + paramName + '_)');
if (paramName in conditions): if (paramName in conditions):
readText += '\t\t&& (v' + paramName + '() ? _' + paramName + '.read(from, end) : ((_' + paramName + ' = MTP' + paramType + '()), true))\n'; readText += '\t\t&& (v' + paramName + '() ? _' + paramName + '.read(from, end) : ((_' + paramName + ' = MTP' + paramType + '()), true))\n';
writeText += '\tif (const auto v' + paramName + ' = v.v' + paramName + '()) v' + paramName + '->write(to);\n'; writeText += '\t\tif (const auto v' + paramName + ' = v.v' + paramName + '()) v' + paramName + '->write(to);\n';
sizeList.append('(v.v' + paramName + '() ? v.v' + paramName + '()->innerLength() : 0)'); sizeList.append('(v.v' + paramName + '() ? v.v' + paramName + '()->innerLength() : 0)');
else: else:
readText += '\t\t&& _' + paramName + '.read(from, end)\n'; readText += '\t\t&& _' + paramName + '.read(from, end)\n';

View file

@ -194,7 +194,8 @@ MTPChatBannedRights ChannelData::KickedRestrictedRights() {
void ChannelData::applyEditAdmin( void ChannelData::applyEditAdmin(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &oldRights, const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights) { const MTPChatAdminRights &newRights,
const QString &rank) {
if (mgInfo) { if (mgInfo) {
// If rights are empty - still add participant? TODO check // If rights are empty - still add participant? TODO check
if (!base::contains(mgInfo->lastParticipants, user)) { if (!base::contains(mgInfo->lastParticipants, user)) {
@ -226,7 +227,7 @@ void ChannelData::applyEditAdmin(
} else { } else {
it->second = lastAdmin; it->second = lastAdmin;
} }
Data::ChannelAdminChanges(this).feed(userId, true); Data::ChannelAdminChanges(this).add(userId, rank);
} else { } else {
if (it != mgInfo->lastAdmins.cend()) { if (it != mgInfo->lastAdmins.cend()) {
mgInfo->lastAdmins.erase(it); mgInfo->lastAdmins.erase(it);
@ -234,7 +235,7 @@ void ChannelData::applyEditAdmin(
setAdminsCount(adminsCount() - 1); setAdminsCount(adminsCount() - 1);
} }
} }
Data::ChannelAdminChanges(this).feed(userId, false); Data::ChannelAdminChanges(this).remove(userId);
} }
} }
if (oldRights.c_chatAdminRights().vflags().v && !newRights.c_chatAdminRights().vflags().v) { if (oldRights.c_chatAdminRights().vflags().v && !newRights.c_chatAdminRights().vflags().v) {
@ -307,7 +308,7 @@ void ChannelData::applyEditBanned(not_null<UserData*> user, const MTPChatBannedR
owner().removeMegagroupParticipant(this, user); owner().removeMegagroupParticipant(this, user);
} }
} }
Data::ChannelAdminChanges(this).feed(peerToUser(user->id), false); Data::ChannelAdminChanges(this).remove(peerToUser(user->id));
} else { } else {
if (isKicked) { if (isKicked) {
if (membersCount() > 1) { if (membersCount() > 1) {
@ -515,7 +516,7 @@ void ChannelData::setAdminRights(const MTPChatAdminRights &rights) {
} }
auto amAdmin = hasAdminRights() || amCreator(); auto amAdmin = hasAdminRights() || amCreator();
Data::ChannelAdminChanges(this).feed(session().userId(), amAdmin); Data::ChannelAdminChanges(this).add(session().userId(), QString());
} }
Notify::peerUpdatedDelayed(this, UpdateFlag::RightsChanged | UpdateFlag::AdminsChanged | UpdateFlag::BannedUsersChanged); Notify::peerUpdatedDelayed(this, UpdateFlag::RightsChanged | UpdateFlag::AdminsChanged | UpdateFlag::BannedUsersChanged);
} }
@ -535,7 +536,7 @@ void ChannelData::setRestrictions(const MTPChatBannedRights &rights) {
mgInfo->lastRestricted.emplace(self, me); mgInfo->lastRestricted.emplace(self, me);
} }
mgInfo->lastAdmins.remove(self); mgInfo->lastAdmins.remove(self);
Data::ChannelAdminChanges(this).feed(session().userId(), false); Data::ChannelAdminChanges(this).remove(session().userId());
} else { } else {
mgInfo->lastRestricted.remove(self); mgInfo->lastRestricted.remove(self);
} }
@ -740,4 +741,54 @@ void ApplyChannelUpdate(
update.vnotify_settings()); update.vnotify_settings());
} }
void ApplyChannelAdmins(
not_null<ChannelData*> channel,
const MTPDchannels_channelParticipants &data) {
channel->owner().processUsers(data.vusers());
const auto &list = data.vparticipants().v;
auto adding = base::flat_map<UserId, QString>();
auto admins = ranges::make_iterator_range(
list.begin(), list.end()
) | ranges::view::transform([](const MTPChannelParticipant &p) {
const auto userId = p.match([](const auto &data) {
return data.vuser_id().v;
});
const auto rank = p.match([](const MTPDchannelParticipantAdmin &data) {
return qs(data.vrank().value_or_empty());
}, [](const MTPDchannelParticipantCreator &data) {
return qs(data.vrank().value_or_empty());
}, [](const auto &data) {
return QString();
});
return std::make_pair(userId, rank);
});
for (const auto &[userId, rank] : admins) {
adding.emplace(userId, rank);
}
if (channel->mgInfo->creator) {
adding.emplace(
peerToUser(channel->mgInfo->creator->id),
channel->mgInfo->creatorRank);
}
auto removing = channel->mgInfo->admins;
if (removing.empty() && adding.empty()) {
// Add some admin-placeholder so we don't DDOS
// server with admins list requests.
LOG(("API Error: Got empty admins list from server."));
adding.emplace(0, QString());
}
Data::ChannelAdminChanges changes(channel);
for (const auto &[addingId, rank] : adding) {
if (!removing.remove(addingId)) {
changes.add(addingId, rank);
}
}
for (const auto &[removingId, rank] : removing) {
changes.remove(removingId);
}
}
} // namespace Data } // namespace Data

View file

@ -62,10 +62,11 @@ public:
base::flat_set<not_null<PeerData*>> markupSenders; base::flat_set<not_null<PeerData*>> markupSenders;
base::flat_set<not_null<UserData*>> bots; base::flat_set<not_null<UserData*>> bots;
// For admin badges, full admins list. // For admin badges, full admins list with ranks.
base::flat_set<UserId> admins; base::flat_map<UserId, QString> admins;
UserData *creator = nullptr; // nullptr means unknown UserData *creator = nullptr; // nullptr means unknown
QString creatorRank;
int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other
bool joinedMessageFound = false; bool joinedMessageFound = false;
MTPInputStickerSet stickerSet = MTP_inputStickerSetEmpty(); MTPInputStickerSet stickerSet = MTP_inputStickerSetEmpty();
@ -208,7 +209,8 @@ public:
void applyEditAdmin( void applyEditAdmin(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatAdminRights &oldRights, const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights); const MTPChatAdminRights &newRights,
const QString &rank);
void applyEditBanned( void applyEditBanned(
not_null<UserData*> user, not_null<UserData*> user,
const MTPChatBannedRights &oldRights, const MTPChatBannedRights &oldRights,
@ -454,4 +456,8 @@ void ApplyChannelUpdate(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
const MTPDchannelFull &update); const MTPDchannelFull &update);
void ApplyChannelAdmins(
not_null<ChannelData*> channel,
const MTPDchannels_channelParticipants &data);
} // namespace Data } // namespace Data

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "auth_session.h"
namespace Data { namespace Data {
@ -18,18 +19,25 @@ ChannelAdminChanges::ChannelAdminChanges(not_null<ChannelData*> channel)
, _admins(_channel->mgInfo->admins) { , _admins(_channel->mgInfo->admins) {
} }
void ChannelAdminChanges::feed(UserId userId, bool isAdmin) { void ChannelAdminChanges::add(UserId userId, const QString &rank) {
if (isAdmin && !_admins.contains(userId)) { const auto i = _admins.find(userId);
_admins.insert(userId); if (i == end(_admins) || i->second != rank) {
_changes.emplace(userId, true); _admins[userId] = rank;
} else if (!isAdmin && _admins.contains(userId)) { _changes.emplace(userId);
}
}
void ChannelAdminChanges::remove(UserId userId) {
if (_admins.contains(userId)) {
_admins.remove(userId); _admins.remove(userId);
_changes.emplace(userId, false); _changes.emplace(userId);
} }
} }
ChannelAdminChanges::~ChannelAdminChanges() { ChannelAdminChanges::~ChannelAdminChanges() {
if (!_changes.empty()) { if (_changes.size() > 1
|| (!_changes.empty()
&& _changes.front() != _channel->session().userId())) {
if (const auto history = _channel->owner().historyLoaded(_channel)) { if (const auto history = _channel->owner().historyLoaded(_channel)) {
history->applyGroupAdminChanges(_changes); history->applyGroupAdminChanges(_changes);
} }

View file

@ -13,14 +13,15 @@ class ChannelAdminChanges {
public: public:
ChannelAdminChanges(not_null<ChannelData*> channel); ChannelAdminChanges(not_null<ChannelData*> channel);
void feed(UserId userId, bool isAdmin); void add(UserId userId, const QString &rank);
void remove(UserId userId);
~ChannelAdminChanges(); ~ChannelAdminChanges();
private: private:
not_null<ChannelData*> _channel; not_null<ChannelData*> _channel;
base::flat_set<UserId> &_admins; base::flat_map<UserId, QString> &_admins;
base::flat_map<UserId, bool> _changes; base::flat_set<UserId> _changes;
}; };

View file

@ -1100,7 +1100,7 @@ void History::applyServiceChanges(
mgInfo->botStatus = -1; mgInfo->botStatus = -1;
} }
} }
Data::ChannelAdminChanges(megagroup).feed(uid, false); Data::ChannelAdminChanges(megagroup).remove(uid);
} }
} break; } break;
@ -3093,8 +3093,7 @@ void History::clearUpTill(MsgId availableMinId) {
owner().sendHistoryChangeNotifications(); owner().sendHistoryChangeNotifications();
} }
void History::applyGroupAdminChanges( void History::applyGroupAdminChanges(const base::flat_set<UserId> &changes) {
const base::flat_map<UserId, bool> &changes) {
for (const auto &block : blocks) { for (const auto &block : blocks) {
for (const auto &message : block->messages) { for (const auto &message : block->messages) {
message->data()->applyGroupAdminChanges(changes); message->data()->applyGroupAdminChanges(changes);

View file

@ -90,8 +90,7 @@ public:
void clear(ClearType type); void clear(ClearType type);
void clearUpTill(MsgId availableMinId); void clearUpTill(MsgId availableMinId);
void applyGroupAdminChanges( void applyGroupAdminChanges(const base::flat_set<UserId> &changes);
const base::flat_map<UserId, bool> &changes);
HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type); HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
HistoryItem *addToHistory(const MTPMessage &msg); HistoryItem *addToHistory(const MTPMessage &msg);

View file

@ -80,7 +80,7 @@ public:
return true; return true;
} }
virtual void applyGroupAdminChanges( virtual void applyGroupAdminChanges(
const base::flat_map<UserId, bool> &changes) { const base::flat_set<UserId> &changes) {
} }
UserData *viaBot() const; UserData *viaBot() const;

View file

@ -637,30 +637,29 @@ bool HistoryMessage::updateDependencyItem() {
} }
void HistoryMessage::updateAdminBadgeState() { void HistoryMessage::updateAdminBadgeState() {
auto hasAdminBadge = [&] { _adminBadge = [&] {
if (auto channel = history()->peer->asChannel()) { const auto channel = history()->peer->asMegagroup();
if (auto user = author()->asUser()) { const auto user = author()->asUser();
return channel->isGroupAdmin(user); if (!channel || !user) {
} return QString();
} }
return false; const auto info = channel->mgInfo.get();
const auto i = channel->mgInfo->admins.find(peerToUser(user->id));
return (i == channel->mgInfo->admins.end())
? (info->creator != user
? QString()
: info->creatorRank.isEmpty()
? tr::lng_admin_badge(tr::now)
: info->creatorRank)
: i->second.isEmpty()
? tr::lng_admin_badge(tr::now)
: i->second;
}(); }();
if (hasAdminBadge) {
_flags |= MTPDmessage_ClientFlag::f_has_admin_badge;
} else {
_flags &= ~MTPDmessage_ClientFlag::f_has_admin_badge;
}
} }
void HistoryMessage::applyGroupAdminChanges( void HistoryMessage::applyGroupAdminChanges(
const base::flat_map<UserId, bool> &changes) { const base::flat_set<UserId> &changes) {
auto i = changes.find(peerToUser(author()->id)); if (!out() && changes.contains(peerToUser(author()->id))) {
if (i != changes.end()) {
if (i->second) {
_flags |= MTPDmessage_ClientFlag::f_has_admin_badge;
} else {
_flags &= ~MTPDmessage_ClientFlag::f_has_admin_badge;
}
history()->owner().requestItemResize(this); history()->owner().requestItemResize(this);
} }
} }

View file

@ -104,13 +104,16 @@ public:
[[nodiscard]] bool allowsEdit(TimeId now) const override; [[nodiscard]] bool allowsEdit(TimeId now) const override;
[[nodiscard]] bool uploading() const; [[nodiscard]] bool uploading() const;
[[nodiscard]] QString adminBadge() const {
return _adminBadge;
}
[[nodiscard]] bool hasAdminBadge() const { [[nodiscard]] bool hasAdminBadge() const {
return _flags & MTPDmessage_ClientFlag::f_has_admin_badge; return !_adminBadge.isEmpty();
} }
[[nodiscard]] bool hasMessageBadge() const; [[nodiscard]] bool hasMessageBadge() const;
void applyGroupAdminChanges( void applyGroupAdminChanges(
const base::flat_map<UserId, bool> &changes) override; const base::flat_set<UserId> &changes) override;
void setViewsCount(int32 count) override; void setViewsCount(int32 count) override;
void setRealId(MsgId newId) override; void setRealId(MsgId newId) override;
@ -179,6 +182,7 @@ private:
void updateAdminBadgeState(); void updateAdminBadgeState();
QString _adminBadge;
QString _timeText; QString _timeText;
int _timeWidth = 0; int _timeWidth = 0;

View file

@ -124,9 +124,8 @@ int KeyboardStyle::minButtonWidth(
} }
QString MessageBadgeText(not_null<const HistoryMessage*> message) { QString MessageBadgeText(not_null<const HistoryMessage*> message) {
return message->hasAdminBadge() const auto result = message->adminBadge();
? tr::lng_admin_badge(tr::now) return result.isEmpty() ? tr::lng_channel_badge(tr::now) : result;
: tr::lng_channel_badge(tr::now);
} }
QString FastReplyText() { QString FastReplyText() {