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) {
_adminsRequests.remove(channel);
result.match([&](const MTPDchannels_channelParticipants &data) {
_session->data().processUsers(data.vusers());
applyAdminsList(
channel,
data.vcount().v,
data.vparticipants().v);
Data::ApplyChannelAdmins(channel, data);
}, [&](const MTPDchannels_channelParticipantsNotModified &) {
LOG(("API Error: channels.channelParticipantsNotModified received!"));
});
@ -1671,10 +1667,12 @@ void ApiWrap::applyLastParticipantsList(
auto user = _session->data().user(userId);
if (p.type() == mtpc_channelParticipantCreator) {
const auto &creator = p.c_channelParticipantCreator();
const auto rank = qs(creator.vrank().value_or_empty());
channel->mgInfo->creator = user;
if (!channel->mgInfo->admins.empty()
&& !channel->mgInfo->admins.contains(userId)) {
Data::ChannelAdminChanges(channel).feed(userId, true);
channel->mgInfo->creatorRank = rank;
if (!channel->mgInfo->admins.empty()) {
Data::ChannelAdminChanges(channel).add(userId, rank);
}
}
if (!base::contains(channel->mgInfo->lastParticipants, user)) {
@ -1758,39 +1756,6 @@ void ApiWrap::applyBotsList(
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) {
if (_selfParticipantRequests.contains(channel)) {
return;
@ -3547,9 +3512,13 @@ void ApiWrap::refreshChannelAdmins(
const auto userId = p.match([](const auto &data) {
return data.vuser_id().v;
});
const auto isAdmin = (p.type() == mtpc_channelParticipantAdmin)
|| (p.type() == mtpc_channelParticipantCreator);
changes.feed(userId, isAdmin);
p.match([&](const MTPDchannelParticipantAdmin &data) {
changes.add(userId, qs(data.vrank().value_or_empty()));
}, [&](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,
int availableCount,
const QVector<MTPChannelParticipant> &list);
void applyAdminsList(
not_null<ChannelData*> channel,
int availableCount,
const QVector<MTPChannelParticipant> &list);
void resolveWebPages();
void gotWebPages(
ChannelData *channel,

View file

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

View file

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

View file

@ -62,7 +62,8 @@ public:
using AdminDoneCallback = Fn<void(
not_null<UserData*> user,
const MTPChatAdminRights &adminRights)>;
const MTPChatAdminRights &adminRights,
const QString &rank)>;
using BannedDoneCallback = Fn<void(
not_null<UserData*> user,
const MTPChatBannedRights &bannedRights)>;
@ -89,7 +90,8 @@ private:
void showAdmin(not_null<UserData*> user, bool sure = false);
void editAdminDone(
not_null<UserData*> user,
const MTPChatAdminRights &rights);
const MTPChatAdminRights &rights,
const QString &rank);
void showRestricted(not_null<UserData*> user, bool sure = false);
void editRestrictedDone(
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/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h"
#include "ui/text_options.h"
@ -247,13 +248,15 @@ EditAdminBox::EditAdminBox(
QWidget*,
not_null<PeerData*> peer,
not_null<UserData*> user,
const MTPChatAdminRights &rights)
const MTPChatAdminRights &rights,
const QString &rank)
: EditParticipantBox(
nullptr,
peer,
user,
(rights.c_chatAdminRights().vflags().v != 0))
, _oldRights(rights) {
, _oldRights(rights)
, _oldRank(rank) {
}
MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) {
@ -362,6 +365,16 @@ void EditAdminBox::prepare() {
refreshAboutAddAdminsText(checked);
}, 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()) {
addButton(tr::lng_settings_save(), [=, value = getChecked] {
if (!_saveCallback) {
@ -373,7 +386,8 @@ void EditAdminBox::prepare() {
: channel->adminRights());
_saveCallback(
_oldRights,
MTP_chatAdminRights(MTP_flags(newFlags)));
MTP_chatAdminRights(MTP_flags(newFlags)),
rank->getLastText().trimmed());
});
addButton(tr::lng_cancel(), [this] { closeBox(); });
} else {

View file

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

View file

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

View file

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

View file

@ -701,7 +701,7 @@ for restype in typesList:
prmsInit.append('_' + paramName + '(' + paramName + '_)');
if (paramName in conditions):
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)');
else:
readText += '\t\t&& _' + paramName + '.read(from, end)\n';

View file

@ -194,7 +194,8 @@ MTPChatBannedRights ChannelData::KickedRestrictedRights() {
void ChannelData::applyEditAdmin(
not_null<UserData*> user,
const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights) {
const MTPChatAdminRights &newRights,
const QString &rank) {
if (mgInfo) {
// If rights are empty - still add participant? TODO check
if (!base::contains(mgInfo->lastParticipants, user)) {
@ -226,7 +227,7 @@ void ChannelData::applyEditAdmin(
} else {
it->second = lastAdmin;
}
Data::ChannelAdminChanges(this).feed(userId, true);
Data::ChannelAdminChanges(this).add(userId, rank);
} else {
if (it != mgInfo->lastAdmins.cend()) {
mgInfo->lastAdmins.erase(it);
@ -234,7 +235,7 @@ void ChannelData::applyEditAdmin(
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) {
@ -307,7 +308,7 @@ void ChannelData::applyEditBanned(not_null<UserData*> user, const MTPChatBannedR
owner().removeMegagroupParticipant(this, user);
}
}
Data::ChannelAdminChanges(this).feed(peerToUser(user->id), false);
Data::ChannelAdminChanges(this).remove(peerToUser(user->id));
} else {
if (isKicked) {
if (membersCount() > 1) {
@ -515,7 +516,7 @@ void ChannelData::setAdminRights(const MTPChatAdminRights &rights) {
}
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);
}
@ -535,7 +536,7 @@ void ChannelData::setRestrictions(const MTPChatBannedRights &rights) {
mgInfo->lastRestricted.emplace(self, me);
}
mgInfo->lastAdmins.remove(self);
Data::ChannelAdminChanges(this).feed(session().userId(), false);
Data::ChannelAdminChanges(this).remove(session().userId());
} else {
mgInfo->lastRestricted.remove(self);
}
@ -740,4 +741,54 @@ void ApplyChannelUpdate(
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

View file

@ -62,10 +62,11 @@ public:
base::flat_set<not_null<PeerData*>> markupSenders;
base::flat_set<not_null<UserData*>> bots;
// For admin badges, full admins list.
base::flat_set<UserId> admins;
// For admin badges, full admins list with ranks.
base::flat_map<UserId, QString> admins;
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
bool joinedMessageFound = false;
MTPInputStickerSet stickerSet = MTP_inputStickerSetEmpty();
@ -208,7 +209,8 @@ public:
void applyEditAdmin(
not_null<UserData*> user,
const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights);
const MTPChatAdminRights &newRights,
const QString &rank);
void applyEditBanned(
not_null<UserData*> user,
const MTPChatBannedRights &oldRights,
@ -454,4 +456,8 @@ void ApplyChannelUpdate(
not_null<ChannelData*> channel,
const MTPDchannelFull &update);
void ApplyChannelAdmins(
not_null<ChannelData*> channel,
const MTPDchannels_channelParticipants &data);
} // namespace Data

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -637,30 +637,29 @@ bool HistoryMessage::updateDependencyItem() {
}
void HistoryMessage::updateAdminBadgeState() {
auto hasAdminBadge = [&] {
if (auto channel = history()->peer->asChannel()) {
if (auto user = author()->asUser()) {
return channel->isGroupAdmin(user);
}
_adminBadge = [&] {
const auto channel = history()->peer->asMegagroup();
const auto user = author()->asUser();
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(
const base::flat_map<UserId, bool> &changes) {
auto i = changes.find(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;
}
const base::flat_set<UserId> &changes) {
if (!out() && changes.contains(peerToUser(author()->id))) {
history()->owner().requestItemResize(this);
}
}

View file

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

View file

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