From 85b3d3f64d5cc7b82a00ce402445c2568fd67eb3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 1 Dec 2017 22:38:44 +0400 Subject: [PATCH] Display admin badges in supergroups. Also prefer std containers to Qt and OrderedSet in data_peer. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/apiwrap.cpp | 40 ++++++- Telegram/SourceFiles/apiwrap.h | 4 + Telegram/SourceFiles/app.cpp | 35 +++--- Telegram/SourceFiles/base/flat_map.h | 105 +++++++----------- .../boxes/peer_list_controllers.cpp | 20 ++-- .../chat_helpers/field_autocomplete.cpp | 24 ++-- Telegram/SourceFiles/data/data_peer.cpp | 39 ++++--- Telegram/SourceFiles/data/data_peer.h | 38 +++---- .../dialogs_search_from_controllers.cpp | 5 +- Telegram/SourceFiles/history/history.cpp | 54 ++++++--- Telegram/SourceFiles/history/history.h | 2 + .../history/history_admin_log_inner.cpp | 2 +- Telegram/SourceFiles/history/history_item.cpp | 4 +- Telegram/SourceFiles/history/history_item.h | 6 +- .../SourceFiles/history/history_message.cpp | 86 +++++++++++++- .../SourceFiles/history/history_message.h | 7 ++ .../history/history_top_bar_widget.cpp | 16 +-- .../info/profile/info_profile_cover.cpp | 4 +- .../info_profile_members_controllers.cpp | 6 +- .../info/profile/info_profile_values.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 2 +- Telegram/SourceFiles/mtproto/type_utils.h | 5 +- .../profile/profile_block_group_members.cpp | 37 +++--- .../profile/profile_channel_controllers.cpp | 19 ++-- .../window/notifications_manager_default.h | 2 +- .../SourceFiles/window/window_peer_menu.cpp | 2 +- 27 files changed, 347 insertions(+), 220 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 46e0cab71..501172e79 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -859,6 +859,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_in_reply_to" = "In reply to"; "lng_edited" = "edited"; "lng_edited_date" = "Edited: {date}"; +"lng_admin_badge" = "admin"; "lng_cancel_edit_post_sure" = "Cancel editing?"; "lng_cancel_edit_post_yes" = "Yes"; "lng_cancel_edit_post_no" = "No"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 88dbc7332..f39ea7a36 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -639,7 +639,7 @@ void ApiWrap::lastParticipantsDone( if (!peer->mgInfo) return; - parseChannelParticipants(result, [&]( + parseChannelParticipants(peer, result, [&]( int availableCount, const QVector &list) { applyLastParticipantsList( @@ -713,12 +713,12 @@ void ApiWrap::applyLastParticipantsList( keyboardBotFound = true; } } else { - if (peer->mgInfo->lastParticipants.indexOf(u) < 0) { + if (!base::contains(peer->mgInfo->lastParticipants, u)) { peer->mgInfo->lastParticipants.push_back(u); if (adminRights.c_channelAdminRights().vflags.v) { - peer->mgInfo->lastAdmins.insert(u, MegagroupInfo::Admin { adminRights, adminCanEdit }); + peer->mgInfo->lastAdmins.emplace(u, MegagroupInfo::Admin { adminRights, adminCanEdit }); } else if (restrictedRights.c_channelBannedRights().vflags.v != 0) { - peer->mgInfo->lastRestricted.insert(u, MegagroupInfo::Restricted { restrictedRights }); + peer->mgInfo->lastRestricted.emplace(u, MegagroupInfo::Restricted { restrictedRights }); } if (u->botInfo) { peer->mgInfo->bots.insert(u); @@ -1765,6 +1765,7 @@ void ApiWrap::readFeaturedSets() { } void ApiWrap::parseChannelParticipants( + not_null channel, const MTPchannels_ChannelParticipants &result, base::lambdamgInfo) { + refreshChannelAdmins(channel, data.vparticipants.v); + } if (callbackList) { callbackList(data.vcount.v, data.vparticipants.v); } @@ -1783,7 +1787,31 @@ void ApiWrap::parseChannelParticipants( LOG(("API Error: channels.channelParticipantsNotModified received!")); } })); -}; +} + +void ApiWrap::refreshChannelAdmins( + not_null channel, + const QVector &participants) { + auto changes = base::flat_map(); + auto &admins = channel->mgInfo->admins; + for (auto &participant : participants) { + auto userId = TLHelp::ReadChannelParticipantUserId(participant); + auto admin = (participant.type() == mtpc_channelParticipantAdmin) + || (participant.type() == mtpc_channelParticipantCreator); + if (admin && !admins.contains(userId)) { + admins.insert(userId); + changes.emplace(userId, true); + } else if (!admin && admins.contains(userId)) { + admins.remove(userId); + changes.emplace(userId, false); + } + } + if (!changes.empty()) { + if (auto history = App::historyLoaded(channel)) { + history->applyGroupAdminChanges(changes); + } + } +} void ApiWrap::parseRecentChannelParticipants( not_null channel, @@ -1792,7 +1820,7 @@ void ApiWrap::parseRecentChannelParticipants( int availableCount, const QVector &list)> callbackList, base::lambda callbackNotModified) { - parseChannelParticipants(result, [&]( + parseChannelParticipants(channel, result, [&]( int availableCount, const QVector &list) { auto applyLast = channel->isMegagroup() diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index efca429c3..f359bd385 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -144,6 +144,7 @@ public: void readFeaturedSetDelayed(uint64 setId); void parseChannelParticipants( + not_null channel, const MTPchannels_ChannelParticipants &result, base::lambda chat); void saveChatAdmins(not_null chat); void sendSaveChatAdminsRequests(not_null chat); + void refreshChannelAdmins( + not_null channel, + const QVector &participants); template void requestMessageAfterDate( diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 86b4495b1..94f59b4cc 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -798,7 +798,9 @@ namespace { chat->version = d.vversion.v; auto &v = d.vparticipants.v; chat->count = v.size(); - int32 pversion = chat->participants.isEmpty() ? 1 : (chat->participants.begin().value() + 1); + int32 pversion = chat->participants.empty() + ? 1 + : (chat->participants.begin()->second + 1); chat->invitedByMe.clear(); chat->admins.clear(); chat->removeFlags(MTPDchat::Flag::f_admin); @@ -840,21 +842,22 @@ namespace { break; } } - if (!chat->participants.isEmpty()) { - History *h = App::historyLoaded(chat->id); + if (!chat->participants.empty()) { + auto h = App::historyLoaded(chat->id); bool found = !h || !h->lastKeyboardFrom; - int32 botStatus = -1; + auto botStatus = -1; for (auto i = chat->participants.begin(), e = chat->participants.end(); i != e;) { - if (i.value() < pversion) { + auto [user, version] = *i; + if (version < pversion) { i = chat->participants.erase(i); } else { - if (i.key()->botInfo) { - botStatus = 2;// (botStatus > 0/* || !i.key()->botInfo->readsAllHistory*/) ? 2 : 1; - if (requestBotInfos && !i.key()->botInfo->inited) { - Auth().api().requestFullPeer(i.key()); + if (user->botInfo) { + botStatus = 2;// (botStatus > 0/* || !user->botInfo->readsAllHistory*/) ? 2 : 1; + if (requestBotInfos && !user->botInfo->inited) { + Auth().api().requestFullPeer(user); } } - if (!found && i.key()->id == h->lastKeyboardFrom) { + if (!found && user->id == h->lastKeyboardFrom) { found = true; } ++i; @@ -884,11 +887,11 @@ namespace { chat->version = d.vversion.v; UserData *user = App::userLoaded(d.vuser_id.v); if (user) { - if (chat->participants.isEmpty() && chat->count) { + if (chat->participants.empty() && chat->count) { chat->count++; chat->botStatus = 0; } else if (chat->participants.find(user) == chat->participants.end()) { - chat->participants[user] = (chat->participants.isEmpty() ? 1 : chat->participants.begin().value()); + chat->participants[user] = (chat->participants.empty() ? 1 : chat->participants.begin()->second); if (d.vinviter_id.v == Auth().userId()) { chat->invitedByMe.insert(user); } else { @@ -921,7 +924,7 @@ namespace { auto canEdit = chat->canEdit(); UserData *user = App::userLoaded(d.vuser_id.v); if (user) { - if (chat->participants.isEmpty()) { + if (chat->participants.empty()) { if (chat->count > 0) { chat->count--; } @@ -943,9 +946,9 @@ namespace { } if (chat->botStatus > 0 && user->botInfo) { int32 botStatus = -1; - for (auto j = chat->participants.cbegin(), e = chat->participants.cend(); j != e; ++j) { - if (j.key()->botInfo) { - if (true || botStatus > 0/* || !j.key()->botInfo->readsAllHistory*/) { + for (auto [participant, v] : chat->participants) { + if (participant->botInfo) { + if (true || botStatus > 0/* || !participant->botInfo->readsAllHistory*/) { botStatus = 2; break; } diff --git a/Telegram/SourceFiles/base/flat_map.h b/Telegram/SourceFiles/base/flat_map.h index adc3e2482..bde096b92 100644 --- a/Telegram/SourceFiles/base/flat_map.h +++ b/Telegram/SourceFiles/base/flat_map.h @@ -47,29 +47,47 @@ template < typename reference_impl> class flat_multi_map_iterator_base_impl; -template -class flat_multi_map_key_const_wrap { -public: - constexpr flat_multi_map_key_const_wrap(const Key &value) - : _value(value) { - } - constexpr flat_multi_map_key_const_wrap(Key &&value) - : _value(std::move(value)) { - } - inline constexpr operator const Key&() const { - return _value; +template +struct flat_multi_map_pair_type { + using first_type = const Key; + using second_type = Value; + + constexpr flat_multi_map_pair_type() + : first() + , second() { } -private: - Key _value; + template + constexpr flat_multi_map_pair_type(OtherKey &&key, OtherValue &&value) + : first(std::forward(key)) + , second(std::forward(value)) { + } + flat_multi_map_pair_type(const flat_multi_map_pair_type&) = default; + flat_multi_map_pair_type(flat_multi_map_pair_type&&) = default; + + flat_multi_map_pair_type &operator=(const flat_multi_map_pair_type&) = delete; + flat_multi_map_pair_type &operator=(flat_multi_map_pair_type &&other) { + const_cast(first) = other.first; + second = std::move(other.second); + return *this; + } + + void swap(flat_multi_map_pair_type &other) { + using std::swap; + + if (this != &other) { + std::swap( + const_cast(first), + const_cast(other.first)); + std::swap(second, other.second); + } + } + + const Key first; + Value second; }; -template -using flat_multi_map_pair_type = std::pair< - flat_multi_map_key_const_wrap, - Type>; - template < typename Me, typename Key, @@ -230,7 +248,6 @@ public: class const_reverse_iterator; private: - using key_const_wrap = flat_multi_map_key_const_wrap; using pair_type = flat_multi_map_pair_type; using impl_t = std::deque; @@ -477,9 +494,7 @@ private: typename OtherType1, typename OtherType2, typename = std::enable_if_t< - !std::is_same_v, key_const_wrap> && !std::is_same_v, pair_type> && - !std::is_same_v, key_const_wrap> && !std::is_same_v, pair_type>>> inline constexpr auto operator()( OtherType1 &&a, @@ -488,42 +503,6 @@ private: std::forward(a), std::forward(b)); } - template < - typename OtherType1, - typename OtherType2> - inline constexpr auto operator()( - OtherType1 &&a, - OtherType2 &&b) const -> std::enable_if_t< - std::is_same_v, key_const_wrap> && - std::is_same_v, key_const_wrap>, bool> { - return initial()( - static_cast(a), - static_cast(b)); - } - template < - typename OtherType, - typename = std::enable_if_t< - !std::is_same_v, key_const_wrap> && - !std::is_same_v, pair_type>>> - inline constexpr auto operator()( - const key_const_wrap &a, - OtherType &&b) const { - return initial()( - static_cast(a), - std::forward(b)); - } - template < - typename OtherType, - typename = std::enable_if_t< - !std::is_same_v, key_const_wrap> && - !std::is_same_v, pair_type>>> - inline constexpr auto operator()( - OtherType &&a, - const key_const_wrap &b) const { - return initial()( - std::forward(a), - static_cast(b)); - } template < typename OtherType1, typename OtherType2> @@ -532,9 +511,7 @@ private: OtherType2 &&b) const -> std::enable_if_t< std::is_same_v, pair_type> && std::is_same_v, pair_type>, bool> { - return initial()( - static_cast(a.first), - static_cast(b.first)); + return initial()(a.first, b.first); } template < typename OtherType, @@ -543,9 +520,7 @@ private: inline constexpr auto operator()( const pair_type &a, OtherType &&b) const { - return operator()( - static_cast(a.first), - std::forward(b)); + return operator()(a.first, std::forward(b)); } template < typename OtherType, @@ -554,9 +529,7 @@ private: inline constexpr auto operator()( OtherType &&a, const pair_type &b) const { - return operator()( - std::forward(a), - static_cast(b.first)); + return operator()(std::forward(a), b.first); } }; diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index adff1a767..aae0e4a04 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -38,8 +38,12 @@ base::flat_set> GetAlreadyInFromPeer(PeerData *peer) { return {}; } if (auto chat = peer->asChat()) { - auto participants = chat->participants.keys(); - return { participants.cbegin(), participants.cend() }; + auto participants = ( + chat->participants + ) | ranges::view::transform([](auto &&pair) -> not_null { + return pair.first; + }); + return { participants.begin(), participants.end() }; } else if (auto channel = peer->asChannel()) { if (channel->isMegagroup()) { auto &participants = channel->mgInfo->lastParticipants; @@ -427,7 +431,7 @@ bool AddParticipantsBoxController::isAlreadyIn(not_null user) const { return chat->participants.contains(user); } else if (auto channel = _peer->asChannel()) { return _alreadyIn.contains(user) - || (channel->isMegagroup() && channel->mgInfo->lastParticipants.contains(user)); + || (channel->isMegagroup() && base::contains(channel->mgInfo->lastParticipants, user)); } Unexpected("User in AddParticipantsBoxController::isAlreadyIn"); } @@ -632,12 +636,12 @@ void EditChatAdminsBoxController::rebuildRows() { admins.reserve(allAdmins ? _chat->participants.size() : _chat->admins.size()); others.reserve(_chat->participants.size()); - for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { - if (i.key()->id == peerFromUser(_chat->creator)) continue; - if (_chat->admins.contains(i.key())) { - admins.push_back(i.key()); + for (auto [user, version] : _chat->participants) { + if (user->id == peerFromUser(_chat->creator)) continue; + if (_chat->admins.contains(user)) { + admins.push_back(user); } else { - others.push_back(i.key()); + others.push_back(user); } } if (!admins.empty()) { diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 975e1e139..e13b70f1e 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -153,9 +153,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { } else if (_type == Type::Mentions) { int maxListSize = _addInlineBots ? cRecentInlineBots().size() : 0; if (_chat) { - maxListSize += (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()); + maxListSize += (_chat->participants.empty() ? _chat->lastAuthors.size() : _chat->participants.size()); } else if (_channel && _channel->isMegagroup()) { - if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) { + if (_channel->mgInfo->lastParticipants.empty() || _channel->lastParticipantsCountOutdated()) { } else { maxListSize += _channel->mgInfo->lastParticipants.size(); } @@ -192,19 +192,18 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { } if (_chat) { QMultiMap ordered; - mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size())); + mrows.reserve(mrows.size() + (_chat->participants.empty() ? _chat->lastAuthors.size() : _chat->participants.size())); if (_chat->noParticipantInfo()) { Auth().api().requestFullPeer(_chat); - } else if (!_chat->participants.isEmpty()) { - for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { - auto user = i.key(); + } else if (!_chat->participants.empty()) { + for (const auto [user, v] : _chat->participants) { if (user->isInaccessible()) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; ordered.insertMulti(App::onlineForSort(user, now), user); } } - for_const (auto user, _chat->lastAuthors) { + for (const auto user : _chat->lastAuthors) { if (user->isInaccessible()) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; @@ -221,11 +220,11 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { } } else if (_channel && _channel->isMegagroup()) { QMultiMap ordered; - if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) { + if (_channel->mgInfo->lastParticipants.empty() || _channel->lastParticipantsCountOutdated()) { Auth().api().requestLastParticipants(_channel); } else { mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size()); - for_const (auto user, _channel->mgInfo->lastParticipants) { + for (const auto user : _channel->mgInfo->lastParticipants) { if (user->isInaccessible()) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; @@ -251,9 +250,8 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { if (_chat) { if (_chat->noParticipantInfo()) { Auth().api().requestFullPeer(_chat); - } else if (!_chat->participants.isEmpty()) { - for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { - auto user = i.key(); + } else if (!_chat->participants.empty()) { + for (const auto [user, version] : _chat->participants) { if (!user->botInfo) continue; if (!user->botInfo->inited) { Auth().api().requestFullPeer(user); @@ -270,7 +268,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { cnt = _user->botInfo->commands.size(); bots.insert(_user, true); } else if (_channel && _channel->isMegagroup()) { - if (_channel->mgInfo->bots.isEmpty()) { + if (_channel->mgInfo->bots.empty()) { if (!_channel->mgInfo->botStatus) { Auth().api().requestBots(_channel); } diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 885d2d730..edbaa948b 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -806,7 +806,7 @@ void ChannelData::setInviteLink(const QString &newInviteLink) { void ChannelData::setMembersCount(int newMembersCount) { if (_membersCount != newMembersCount) { - if (isMegagroup() && !mgInfo->lastParticipants.isEmpty()) { + if (isMegagroup() && !mgInfo->lastParticipants.empty()) { mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated; mgInfo->lastParticipantsCount = membersCount(); } @@ -845,7 +845,8 @@ MTPChannelBannedRights ChannelData::KickedRestrictedRights() { void ChannelData::applyEditAdmin(not_null user, const MTPChannelAdminRights &oldRights, const MTPChannelAdminRights &newRights) { auto flags = Notify::PeerUpdate::Flag::AdminsChanged | Notify::PeerUpdate::Flag::None; if (mgInfo) { - if (!mgInfo->lastParticipants.contains(user)) { // If rights are empty - still add participant? TODO check + // If rights are empty - still add participant? TODO check + if (!base::contains(mgInfo->lastParticipants, user)) { mgInfo->lastParticipants.push_front(user); setMembersCount(membersCount() + 1); if (user->botInfo && !mgInfo->bots.contains(user)) { @@ -855,7 +856,8 @@ void ChannelData::applyEditAdmin(not_null user, const MTPChannelAdmin } } } - if (mgInfo->lastRestricted.contains(user)) { // If rights are empty - still remove restrictions? TODO check + // If rights are empty - still remove restrictions? TODO check + if (mgInfo->lastRestricted.contains(user)) { mgInfo->lastRestricted.remove(user); if (restrictedCount() > 0) { setRestrictedCount(restrictedCount() - 1); @@ -866,10 +868,10 @@ void ChannelData::applyEditAdmin(not_null user, const MTPChannelAdmin auto lastAdmin = MegagroupInfo::Admin { newRights }; lastAdmin.canEdit = true; if (it == mgInfo->lastAdmins.cend()) { - mgInfo->lastAdmins.insert(user, lastAdmin); + mgInfo->lastAdmins.emplace(user, lastAdmin); setAdminsCount(adminsCount() + 1); } else { - it.value() = lastAdmin; + it->second = lastAdmin; } } else { if (it != mgInfo->lastAdmins.cend()) { @@ -913,10 +915,10 @@ void ChannelData::applyEditBanned(not_null user, const MTPChannelBann auto it = mgInfo->lastRestricted.find(user); if (isRestricted) { if (it == mgInfo->lastRestricted.cend()) { - mgInfo->lastRestricted.insert(user, MegagroupInfo::Restricted { newRights }); + mgInfo->lastRestricted.emplace(user, MegagroupInfo::Restricted { newRights }); setRestrictedCount(restrictedCount() + 1); } else { - it->rights = newRights; + it->second.rights = newRights; } } else { if (it != mgInfo->lastRestricted.cend()) { @@ -926,9 +928,9 @@ void ChannelData::applyEditBanned(not_null user, const MTPChannelBann } } if (isKicked) { - auto i = mgInfo->lastParticipants.indexOf(user); - if (i >= 0) { - mgInfo->lastParticipants.removeAt(i); + auto i = ranges::find(mgInfo->lastParticipants, user); + if (i != mgInfo->lastParticipants.end()) { + mgInfo->lastParticipants.erase(i); } if (membersCount() > 1) { setMembersCount(membersCount() - 1); @@ -939,7 +941,7 @@ void ChannelData::applyEditBanned(not_null user, const MTPChannelBann setKickedCount(kickedCount() + 1); if (mgInfo->bots.contains(user)) { mgInfo->bots.remove(user); - if (mgInfo->bots.isEmpty() && mgInfo->botStatus > 0) { + if (mgInfo->bots.empty() && mgInfo->botStatus > 0) { mgInfo->botStatus = -1; } } @@ -956,6 +958,13 @@ void ChannelData::applyEditBanned(not_null user, const MTPChannelBann Notify::peerUpdatedDelayed(this, flags); } +bool ChannelData::isGroupAdmin(not_null user) const { + if (auto info = mgInfo.get()) { + return info->admins.contains(peerToUser(user->id)); + } + return false; +} + void ChannelData::setRestrictionReason(const QString &text) { if (_restrictionReason != text) { _restrictionReason = text; @@ -1087,9 +1096,9 @@ bool ChannelData::canDelete() const { bool ChannelData::canEditLastAdmin(not_null user) const { // Duplicated in ParticipantsBoxController::canEditAdmin :( if (mgInfo) { - auto i = mgInfo->lastAdmins.constFind(user); + auto i = mgInfo->lastAdmins.find(user); if (i != mgInfo->lastAdmins.cend()) { - return i->canEdit; + return i->second.canEdit; } return (user != mgInfo->creator); } @@ -1130,7 +1139,7 @@ void ChannelData::setAdminRights(const MTPChannelAdminRights &rights) { if (!amCreator()) { auto me = MegagroupInfo::Admin { rights }; me.canEdit = false; - mgInfo->lastAdmins.insert(App::self(), me); + mgInfo->lastAdmins.emplace(App::self(), me); } mgInfo->lastRestricted.remove(App::self()); } else { @@ -1151,7 +1160,7 @@ void ChannelData::setRestrictedRights(const MTPChannelBannedRights &rights) { if (hasRestrictions()) { if (!amCreator()) { auto me = MegagroupInfo::Restricted { rights }; - mgInfo->lastRestricted.insert(App::self(), me); + mgInfo->lastRestricted.emplace(App::self(), me); } mgInfo->lastAdmins.remove(App::self()); } else { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index cf931fc97..60ed0e64a 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -115,11 +115,6 @@ private: static const PhotoId UnknownPeerPhotoId = 0xFFFFFFFFFFFFFFFFULL; -inline const QString &emptyUsername() { - static QString empty; - return empty; -} - class PeerData; class PeerClickHandler : public ClickHandler { @@ -195,7 +190,7 @@ public: const Text &dialogName() const; const QString &shortName() const; - const QString &userName() const; + QString userName() const; const PeerId id; int32 bareId() const { @@ -559,7 +554,7 @@ public: void invalidateParticipants(); bool noParticipantInfo() const { - return (count > 0 || amIn()) && participants.isEmpty(); + return (count > 0 || amIn()) && participants.empty(); } MTPint inputChat; @@ -623,11 +618,11 @@ public: bool isMigrated() const { return flags() & MTPDchat::Flag::f_migrated_to; } - QMap, int> participants; - OrderedSet> invitedByMe; - OrderedSet> admins; - QList> lastAuthors; - OrderedSet> markupSenders; + base::flat_map, int> participants; + base::flat_set> invitedByMe; + base::flat_set> admins; + std::deque> lastAuthors; + base::flat_set> markupSenders; int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other // ImagePtr photoFull; @@ -744,11 +739,14 @@ struct MegagroupInfo { } MTPChannelBannedRights rights; }; - QList> lastParticipants; - QMap, Admin> lastAdmins; - QMap, Restricted> lastRestricted; - OrderedSet> markupSenders; - OrderedSet> bots; + std::deque> lastParticipants; + base::flat_map, Admin> lastAdmins; + base::flat_map, Restricted> lastRestricted; + base::flat_set> markupSenders; + base::flat_set> bots; + + // For admin badges, full admins list. + base::flat_set admins; UserData *creator = nullptr; // nullptr means unknown int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other @@ -898,6 +896,8 @@ public: const MTPChannelBannedRights &oldRights, const MTPChannelBannedRights &newRights); + bool isGroupAdmin(not_null user) const; + int32 date = 0; int version = 0; std::unique_ptr mgInfo; @@ -1176,12 +1176,12 @@ inline const Text &PeerData::dialogName() const { inline const QString &PeerData::shortName() const { return isUser() ? asUser()->firstName : name; } -inline const QString &PeerData::userName() const { +inline QString PeerData::userName() const { return isUser() ? asUser()->username : isChannel() ? asChannel()->username - : emptyUsername(); + : QString(); } inline bool PeerData::isVerified() const { return isUser() diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp b/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp index cb8391a9a..364066711 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp @@ -87,9 +87,8 @@ void ChatSearchFromController::rebuildRows() { QMultiMap ordered; if (_chat->noParticipantInfo()) { Auth().api().requestFullPeer(_chat); - } else if (!_chat->participants.isEmpty()) { - for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { - auto user = i.key(); + } else if (!_chat->participants.empty()) { + for (const auto [user, version] : _chat->participants) { ordered.insertMulti(App::onlineForSort(user, now), user); } } diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index c9462f5de..8279a4c52 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -945,7 +945,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, auto &v = d.vusers.v; for (auto i = 0, l = v.size(); i != l; ++i) { if (auto user = App::userLoaded(peerFromUser(v[i]))) { - if (mgInfo->lastParticipants.indexOf(user) < 0) { + if (!base::contains(mgInfo->lastParticipants, user)) { mgInfo->lastParticipants.push_front(user); mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated; Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); @@ -968,7 +968,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, auto mgInfo = megagroup->mgInfo.get(); Assert(mgInfo != nullptr); if (auto user = result->from()->asUser()) { - if (mgInfo->lastParticipants.indexOf(user) < 0) { + if (!base::contains(mgInfo->lastParticipants, user)) { mgInfo->lastParticipants.push_front(user); Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); Auth().data().addNewMegagroupParticipant(megagroup, user); @@ -998,9 +998,12 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, if (auto user = App::userLoaded(uid)) { auto mgInfo = megagroup->mgInfo.get(); Assert(mgInfo != nullptr); - auto index = mgInfo->lastParticipants.indexOf(user); - if (index >= 0) { - mgInfo->lastParticipants.removeAt(index); + auto i = ranges::find( + mgInfo->lastParticipants, + user, + [](not_null user) { return user.get(); }); + if (i != mgInfo->lastParticipants.end()) { + mgInfo->lastParticipants.erase(i); Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); } Auth().data().removeMegagroupParticipant(megagroup, user); @@ -1018,7 +1021,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::AdminsChanged); } mgInfo->bots.remove(user); - if (mgInfo->bots.isEmpty() && mgInfo->botStatus > 0) { + if (mgInfo->bots.empty() && mgInfo->botStatus > 0) { mgInfo->botStatus = -1; } } @@ -1305,7 +1308,7 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { } if (adding->from()->id) { if (auto user = adding->from()->asUser()) { - auto getLastAuthors = [this]() -> QList>* { + auto getLastAuthors = [this]() -> std::deque>* { if (auto chat = peer->asChat()) { return &chat->lastAuthors; } else if (auto channel = peer->asMegagroup()) { @@ -1324,13 +1327,19 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { } } if (auto lastAuthors = getLastAuthors()) { - int prev = lastAuthors->indexOf(user); - if (prev > 0) { - lastAuthors->removeAt(prev); - } else if (prev < 0 && peer->isMegagroup()) { // nothing is outdated if just reordering + auto prev = ranges::find( + *lastAuthors, + user, + [](not_null user) { return user.get(); }); + auto index = (prev != lastAuthors->end()) + ? (lastAuthors->end() - prev) + : -1; + if (index > 0) { + lastAuthors->erase(prev); + } else if (index < 0 && peer->isMegagroup()) { // nothing is outdated if just reordering peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated; } - if (prev) { + if (index) { lastAuthors->push_front(user); } if (auto megagroup = peer->asMegagroup()) { @@ -1342,7 +1351,7 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { if (adding->definesReplyKeyboard()) { auto markupFlags = adding->replyKeyboardFlags(); if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective) || adding->mentionsMe()) { - auto getMarkupSenders = [this]() -> OrderedSet>* { + auto getMarkupSenders = [this]() -> base::flat_set>* { if (auto chat = peer->asChat()) { return &chat->markupSenders; } else if (auto channel = peer->asMegagroup()) { @@ -1360,7 +1369,7 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { } else { bool botNotInChat = false; if (peer->isChat()) { - botNotInChat = adding->from()->isUser() && (!peer->canWrite() || !peer->asChat()->participants.isEmpty()) && !peer->asChat()->participants.contains(adding->from()->asUser()); + botNotInChat = adding->from()->isUser() && (!peer->canWrite() || !peer->asChat()->participants.empty()) && !peer->asChat()->participants.contains(adding->from()->asUser()); } else if (peer->isMegagroup()) { botNotInChat = adding->from()->isUser() && (!peer->canWrite() || peer->asChannel()->mgInfo->botStatus != 0) && !peer->asChannel()->mgInfo->bots.contains(adding->from()->asUser()); } @@ -1577,8 +1586,8 @@ void History::addOlderSlice(const QVector &slice) { } else if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors bool channel = isChannel(); int32 mask = 0; - QList> *lastAuthors = nullptr; - OrderedSet> *markupSenders = nullptr; + std::deque> *lastAuthors = nullptr; + base::flat_set> *markupSenders = nullptr; if (peer->isChat()) { lastAuthors = &peer->asChat()->lastAuthors; markupSenders = &peer->asChat()->markupSenders; @@ -1597,7 +1606,7 @@ void History::addOlderSlice(const QVector &slice) { if (item->from()->id) { if (lastAuthors) { // chats if (auto user = item->from()->asUser()) { - if (!lastAuthors->contains(user)) { + if (!base::contains(*lastAuthors, user)) { lastAuthors->push_back(user); if (peer->isMegagroup()) { peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated; @@ -1620,7 +1629,7 @@ void History::addOlderSlice(const QVector &slice) { if (!lastKeyboardInited) { bool botNotInChat = false; if (peer->isChat()) { - botNotInChat = (!peer->canWrite() || !peer->asChat()->participants.isEmpty()) && item->author()->isUser() && !peer->asChat()->participants.contains(item->author()->asUser()); + botNotInChat = (!peer->canWrite() || !peer->asChat()->participants.empty()) && item->author()->isUser() && !peer->asChat()->participants.contains(item->author()->asUser()); } else if (peer->isMegagroup()) { botNotInChat = (!peer->canWrite() || peer->asChannel()->mgInfo->botStatus != 0) && item->author()->isUser() && !peer->asChannel()->mgInfo->bots.contains(item->author()->asUser()); } @@ -2408,6 +2417,15 @@ void History::clearUpTill(MsgId availableMinId) { } } +void History::applyGroupAdminChanges( + const base::flat_map &changes) { + for (auto block : blocks) { + for (auto item : block->items) { + item->applyGroupAdminChanges(changes); + } + } +} + void History::clearBlocks(bool leaveItems) { Blocks lst; std::swap(lst, blocks); diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 9fd8ea805..a31f80804 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -223,6 +223,8 @@ public: void clear(bool leaveItems = false); void clearUpTill(MsgId availableMinId); + void applyGroupAdminChanges(const base::flat_map &changes); + virtual ~History(); HistoryItem *addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags = 0, bool newMsg = true); diff --git a/Telegram/SourceFiles/history/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/history_admin_log_inner.cpp index 21c771e19..a17428507 100644 --- a/Telegram/SourceFiles/history/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_inner.cpp @@ -357,7 +357,7 @@ void InnerWidget::requestAdmins() { }, [](auto &&) { return false; }); - Auth().api().parseChannelParticipants(result, [&]( + Auth().api().parseChannelParticipants(_channel, result, [&]( int availableCount, const QVector &list) { auto filtered = ( diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 98f67329d..0f34379e9 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -1176,7 +1176,7 @@ void HistoryItem::audioTrackUpdated() { void HistoryItem::recountDisplayDate() { Expects(!isLogEntry()); - setDisplayDate(([this]() { + setDisplayDate([&] { if (isEmpty()) { return false; } @@ -1185,7 +1185,7 @@ void HistoryItem::recountDisplayDate() { return previous->isEmpty() || (previous->date.date() != date.date()); } return true; - })()); + }()); } void HistoryItem::setDisplayDate(bool displayDate) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 28e0d6677..5398313eb 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -520,6 +520,9 @@ public: virtual bool notificationReady() const { return true; } + virtual void applyGroupAdminChanges( + const base::flat_map &changes) { + } UserData *viaBot() const { if (auto via = Get()) { @@ -822,9 +825,6 @@ public: return 0; } - bool hasFromName() const { - return (!out() || isPost()) && !history()->peer->isUser(); - } PeerData *author() const { return isPost() ? history()->peer : from(); } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index abd3ca227..39d60facb 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -50,6 +50,10 @@ inline void initTextOptions() { _textDlgOptions.maxw = st::columnMaximalWidthLeft * 2; } +QString AdminBadgeText() { + return lang(lng_admin_badge); +} + style::color FromNameFg(not_null peer, bool selected) { if (selected) { const style::color colors[] = { @@ -805,6 +809,35 @@ void HistoryMessage::updateMediaInBubbleState() { _media->setInBubbleState(computeState()); } +void HistoryMessage::updateAdminBadgeState() { + auto hasAdminBadge = [&] { + if (auto channel = history()->peer->asChannel()) { + if (auto user = author()->asUser()) { + return channel->isGroupAdmin(user); + } + } + return false; + }(); + if (hasAdminBadge) { + _flags |= MTPDmessage_ClientFlag::f_has_admin_badge; + } else { + _flags &= ~MTPDmessage_ClientFlag::f_has_admin_badge; + } +} + +void HistoryMessage::applyGroupAdminChanges( + const base::flat_map &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; + } + setPendingInitDimensions(); + } +} + bool HistoryMessage::displayEditedBadge(bool hasViaBotOrInlineMarkup) const { if (hasViaBotOrInlineMarkup) { return false; @@ -1062,6 +1095,9 @@ void HistoryMessage::initDimensions() { if (reply) { reply->updateName(); } + if (displayFromName()) { + updateAdminBadgeState(); + } auto mediaDisplayed = false; if (_media) { @@ -1111,6 +1147,11 @@ void HistoryMessage::initDimensions() { if (via && !forwarded) { namew += st::msgServiceFont->spacew + via->_maxWidth; } + if (_flags & MTPDmessage_ClientFlag::f_has_admin_badge) { + auto badgeWidth = st::msgServiceFont->width( + AdminBadgeText()); + namew += st::msgPadding.right() + badgeWidth; + } accumulate_max(_maxw, namew); } else if (via && !forwarded) { accumulate_max(_maxw, st::msgPadding.left() + via->_maxWidth + st::msgPadding.right()); @@ -1188,10 +1229,19 @@ QRect HistoryMessage::countGeometry() const { } void HistoryMessage::fromNameUpdated(int32 width) const { + if (_flags & MTPDmessage_ClientFlag::f_has_admin_badge) { + auto badgeWidth = st::msgServiceFont->width( + AdminBadgeText()); + width -= st::msgPadding.right() + badgeWidth; + } _authorNameVersion = author()->nameVersion; if (!Has()) { if (auto via = Get()) { - via->resize(width - st::msgPadding.left() - st::msgPadding.right() - author()->nameText.maxWidth() - st::msgServiceFont->spacew); + via->resize(width + - st::msgPadding.left() + - st::msgPadding.right() + - author()->nameText.maxWidth() + - st::msgServiceFont->spacew); } } } @@ -1774,20 +1824,46 @@ void HistoryMessage::drawFastShare(Painter &p, int left, int top, int outerWidth void HistoryMessage::paintFromName(Painter &p, QRect &trect, bool selected) const { if (displayFromName()) { + auto badgeWidth = [&] { + if (_flags & MTPDmessage_ClientFlag::f_has_admin_badge) { + return st::msgServiceFont->width(AdminBadgeText()); + } + return 0; + }(); + auto availableLeft = trect.left(); + auto availableWidth = trect.width(); + if (badgeWidth) { + availableWidth -= st::msgPadding.right() + badgeWidth; + } + p.setFont(st::msgNameFont); if (isPost()) { p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg); } else { p.setPen(FromNameFg(author(), selected)); } - author()->nameText.drawElided(p, trect.left(), trect.top(), trect.width()); + author()->nameText.drawElided(p, availableLeft, trect.top(), availableWidth); + auto skipWidth = author()->nameText.maxWidth() + st::msgServiceFont->spacew; + availableLeft += skipWidth; + availableWidth -= skipWidth; auto forwarded = Get(); auto via = Get(); - if (via && !forwarded && trect.width() > author()->nameText.maxWidth() + st::msgServiceFont->spacew) { - bool outbg = out() && !isPost(); + if (via && !forwarded && availableWidth > 0) { + auto outbg = out() && !isPost(); p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); - p.drawText(trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew, trect.top() + st::msgServiceFont->ascent, via->_text); + p.drawText(availableLeft, trect.top() + st::msgServiceFont->ascent, via->_text); + auto skipWidth = via->_width + st::msgServiceFont->spacew; + availableLeft += skipWidth; + availableWidth -= skipWidth; + } + if (badgeWidth) { + p.setPen(selected ? st::msgInDateFgSelected : st::msgInDateFg); + p.setFont(st::msgFont); + p.drawText( + trect.left() + trect.width() - badgeWidth, + trect.top() + st::msgFont->ascent, + AdminBadgeText()); } trect.setY(trect.y() + st::msgNameFont->height); } diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 85dbb2503..56befe8c7 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -62,6 +62,9 @@ public: bool hasBubble() const override { return drawBubble(); } + bool hasFromName() const { + return (!out() || isPost()) && !history()->peer->isUser(); + } bool displayFromName() const { if (!hasFromName()) return false; if (isAttachedToPrevious()) return false; @@ -71,6 +74,9 @@ public: bool uploading() const; bool displayFastShare() const override; + void applyGroupAdminChanges( + const base::flat_map &changes) override; + void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const override; void drawFastShare(Painter &p, int left, int top, int outerWidth) const override; void setViewsCount(int32 count) override; @@ -229,5 +235,6 @@ private: }; void updateMediaInBubbleState(); + void updateAdminBadgeState(); }; diff --git a/Telegram/SourceFiles/history/history_top_bar_widget.cpp b/Telegram/SourceFiles/history/history_top_bar_widget.cpp index d9bd7dcf1..6046103aa 100644 --- a/Telegram/SourceFiles/history/history_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/history_top_bar_widget.cpp @@ -639,7 +639,7 @@ void HistoryTopBarWidget::updateOnlineDisplay() { } else if (auto chat = _historyPeer->asChat()) { if (!chat->amIn()) { text = lang(lng_chat_status_unaccessible); - } else if (chat->participants.isEmpty()) { + } else if (chat->participants.empty()) { if (!_titlePeerText.isEmpty()) { text = _titlePeerText; } else if (chat->count <= 0) { @@ -650,10 +650,10 @@ void HistoryTopBarWidget::updateOnlineDisplay() { } else { auto online = 0; auto onlyMe = true; - for (auto i = chat->participants.cbegin(), e = chat->participants.cend(); i != e; ++i) { - if (i.key()->onlineTill > t) { + for (auto [user, v] : chat->participants) { + if (user->onlineTill > t) { ++online; - if (onlyMe && i.key() != App::self()) onlyMe = false; + if (onlyMe && user != App::self()) onlyMe = false; } } if (online > 0 && !onlyMe) { @@ -668,7 +668,7 @@ void HistoryTopBarWidget::updateOnlineDisplay() { } } else if (auto channel = _historyPeer->asChannel()) { if (channel->isMegagroup() && channel->membersCount() > 0 && channel->membersCount() <= Global::ChatSizeMax()) { - if (channel->mgInfo->lastParticipants.isEmpty() || channel->lastParticipantsCountOutdated()) { + if (channel->mgInfo->lastParticipants.empty() || channel->lastParticipantsCountOutdated()) { Auth().api().requestLastParticipants(channel); } auto online = 0; @@ -713,10 +713,10 @@ void HistoryTopBarWidget::updateOnlineDisplayTimer() { if (auto user = _historyPeer->asUser()) { minIn = App::onlineWillChangeIn(user, t); } else if (auto chat = _historyPeer->asChat()) { - if (chat->participants.isEmpty()) return; + if (chat->participants.empty()) return; - for (auto i = chat->participants.cbegin(), e = chat->participants.cend(); i != e; ++i) { - int32 onlineWillChangeIn = App::onlineWillChangeIn(i.key(), t); + for (auto [user, v] : chat->participants) { + auto onlineWillChangeIn = App::onlineWillChangeIn(user, t); if (onlineWillChangeIn < minIn) { minIn = onlineWillChangeIn; } diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index 608f16cf5..8d7234b24 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -365,9 +365,9 @@ void Cover::refreshStatusText() { if (!chat->amIn()) { return lang(lng_chat_status_unaccessible); } - auto fullCount = qMax( + auto fullCount = std::max( chat->count, - chat->participants.size()); + int(chat->participants.size())); return ChatStatusText(fullCount, _onlineCount, true); } else if (auto channel = _peer->asChannel()) { auto fullCount = qMax(channel->membersCount(), 1); diff --git a/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp b/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp index 31218eccb..0a8f76140 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_members_controllers.cpp @@ -188,10 +188,8 @@ void ChatMembersController::rebuildRows() { --count; } } - for (auto i = participants.cbegin(), e = participants.cend(); - i != e; - ++i) { - if (auto row = createRow(i.key())) { + for (const auto [user, v] : participants) { + if (auto row = createRow(user)) { delegate()->peerListAppendRow(std::move(row)); } } diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 4fe41fe86..cbe4daa23 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -186,7 +186,7 @@ rpl::producer MembersCountValue( Notify::PeerUpdate::Flag::MembersChanged) | rpl::map([chat] { return chat->amIn() - ? qMax(chat->count, chat->participants.size()) + ? std::max(chat->count, int(chat->participants.size())) : 0; }); } else if (auto channel = peer->asChannel()) { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 1b33bef28..d540ae1ca 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -5045,7 +5045,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { // Request last active supergroup participants if the 'from' user was not loaded yet. // This will optimize similar getDifference() calls for almost all next messages. if (isDataLoaded == DataIsLoadedResult::FromNotLoaded && channel && channel->isMegagroup()) { - if (channel->mgInfo->lastParticipants.size() < Global::ChatSizeMax() && (channel->mgInfo->lastParticipants.isEmpty() || channel->mgInfo->lastParticipants.size() < channel->membersCount())) { + if (channel->mgInfo->lastParticipants.size() < Global::ChatSizeMax() && (channel->mgInfo->lastParticipants.empty() || channel->mgInfo->lastParticipants.size() < channel->membersCount())) { Auth().api().requestLastParticipants(channel); } } diff --git a/Telegram/SourceFiles/mtproto/type_utils.h b/Telegram/SourceFiles/mtproto/type_utils.h index 9a3605226..6e704bd52 100644 --- a/Telegram/SourceFiles/mtproto/type_utils.h +++ b/Telegram/SourceFiles/mtproto/type_utils.h @@ -78,8 +78,11 @@ enum class MTPDmessage_ClientFlag : uint32 { // message is generated on the client side and should be unread f_clientside_unread = (1U << 21), + // message has an admin badge in supergroup + f_has_admin_badge = (1U << 20), + // update this when adding new client side flags - MIN_FIELD = (1U << 21), + MIN_FIELD = (1U << 20), }; DEFINE_MTP_CLIENT_FLAGS(MTPDmessage) diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp index 45c743bb8..985b4f857 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp @@ -78,7 +78,7 @@ void GroupMembersWidget::editAdmin(not_null user) { } auto currentRightsIt = megagroup->mgInfo->lastAdmins.find(user); auto hasAdminRights = (currentRightsIt != megagroup->mgInfo->lastAdmins.cend()); - auto currentRights = hasAdminRights ? currentRightsIt->rights : MTP_channelAdminRights(MTP_flags(0)); + auto currentRights = hasAdminRights ? currentRightsIt->second.rights : MTP_channelAdminRights(MTP_flags(0)); auto weak = QPointer(this); auto box = Box(megagroup, user, currentRights); box->setSaveCallback([weak, megagroup, user](const MTPChannelAdminRights &oldRights, const MTPChannelAdminRights &newRights) { @@ -96,8 +96,10 @@ void GroupMembersWidget::restrictUser(not_null user) { if (!megagroup) { return; // not supported } - auto defaultRestricted = MegagroupInfo::Restricted { MTP_channelBannedRights(MTP_flags(0), MTP_int(0)) }; - auto currentRights = megagroup->mgInfo->lastRestricted.value(user, defaultRestricted).rights; + auto currentRightsIt = megagroup->mgInfo->lastRestricted.find(user); + auto currentRights = (currentRightsIt != megagroup->mgInfo->lastRestricted.end()) + ? currentRightsIt->second.rights + : MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); auto hasAdminRights = megagroup->mgInfo->lastAdmins.find(user) != megagroup->mgInfo->lastAdmins.cend(); auto box = Box(megagroup, user, hasAdminRights, currentRights); box->setSaveCallback([megagroup, user](const MTPChannelBannedRights &oldRights, const MTPChannelBannedRights &newRights) { @@ -114,13 +116,15 @@ void GroupMembersWidget::removePeer(PeerData *selectedPeer) { auto user = selectedPeer->asUser(); Assert(user != nullptr); auto text = lng_profile_sure_kick(lt_user, user->firstName); - auto currentRestrictedRights = MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); - if (auto channel = peer()->asMegagroup()) { - auto it = channel->mgInfo->lastRestricted.find(user); - if (it != channel->mgInfo->lastRestricted.cend()) { - currentRestrictedRights = it->rights; + auto currentRestrictedRights = [&]() -> MTPChannelBannedRights { + if (auto channel = peer()->asMegagroup()) { + auto it = channel->mgInfo->lastRestricted.find(user); + if (it != channel->mgInfo->lastRestricted.cend()) { + return it->second.rights; + } } - } + return MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); + }(); Ui::show(Box(text, lang(lng_box_remove), [user, currentRestrictedRights, peer = peer()] { Ui::hideLayer(); if (auto chat = peer->asChat()) { @@ -303,7 +307,7 @@ void GroupMembersWidget::refreshMembers() { refreshLimitReached(); } else if (auto megagroup = peer()->asMegagroup()) { auto &megagroupInfo = megagroup->mgInfo; - if (megagroupInfo->lastParticipants.isEmpty() || megagroup->lastParticipantsCountOutdated()) { + if (megagroupInfo->lastParticipants.empty() || megagroup->lastParticipantsCountOutdated()) { Auth().api().requestLastParticipants(megagroup); } fillMegagroupMembers(megagroup); @@ -335,7 +339,7 @@ void GroupMembersWidget::refreshLimitReached() { } void GroupMembersWidget::checkSelfAdmin(ChatData *chat) { - if (chat->participants.isEmpty()) return; + if (chat->participants.empty()) return; auto self = App::self(); if (chat->amAdmin() && !chat->admins.contains(self)) { @@ -390,7 +394,7 @@ GroupMembersWidget::Member *GroupMembersWidget::addUser(ChatData *chat, UserData } void GroupMembersWidget::fillChatMembers(ChatData *chat) { - if (chat->participants.isEmpty()) return; + if (chat->participants.empty()) return; clearItems(); if (!chat->amIn()) return; @@ -399,8 +403,7 @@ void GroupMembersWidget::fillChatMembers(ChatData *chat) { reserveItemsForSize(chat->participants.size()); addUser(chat, App::self())->onlineForSort = INT_MAX; // Put me on the first place. - for (auto i = chat->participants.cbegin(), e = chat->participants.cend(); i != e; ++i) { - auto user = i.key(); + for (auto [user, v] : chat->participants) { if (!user->isSelf()) { addUser(chat, user); } @@ -434,7 +437,7 @@ GroupMembersWidget::Member *GroupMembersWidget::addUser(ChannelData *megagroup, void GroupMembersWidget::fillMegagroupMembers(ChannelData *megagroup) { Assert(megagroup->mgInfo != nullptr); - if (megagroup->mgInfo->lastParticipants.isEmpty()) return; + if (megagroup->mgInfo->lastParticipants.empty()) return; if (!megagroup->canViewMembers()) { clearItems(); @@ -485,10 +488,10 @@ void GroupMembersWidget::setItemFlags(Item *item, ChannelData *megagroup) { using AdminState = Item::AdminState; auto amCreator = item->peer->isSelf() && megagroup->amCreator(); auto amAdmin = item->peer->isSelf() && megagroup->hasAdminRights(); - auto adminIt = megagroup->mgInfo->lastAdmins.constFind(getMember(item)->user()); + auto adminIt = megagroup->mgInfo->lastAdmins.find(getMember(item)->user()); auto isAdmin = (adminIt != megagroup->mgInfo->lastAdmins.cend()); auto isCreator = megagroup->mgInfo->creator == item->peer; - auto adminCanEdit = isAdmin && adminIt->canEdit; + auto adminCanEdit = isAdmin && adminIt->second.canEdit; auto adminState = (amCreator || isCreator) ? AdminState::Creator : (amAdmin || isAdmin) ? AdminState::Admin : AdminState::None; if (item->adminState != adminState) { item->adminState = adminState; diff --git a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp index 9ae316c47..c87964d8b 100644 --- a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp +++ b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp @@ -475,6 +475,7 @@ void ParticipantsBoxController::loadMoreRows() { callback); } else { Auth().api().parseChannelParticipants( + _channel, result, callback); } @@ -538,7 +539,7 @@ bool ParticipantsBoxController::feedMegagroupLastParticipants() { // _channel->updateFull(); // return false; //} - if (info->lastParticipants.isEmpty()) { + if (info->lastParticipants.empty()) { return false; } @@ -546,21 +547,21 @@ bool ParticipantsBoxController::feedMegagroupLastParticipants() { _additional.creator = info->creator; } for_const (auto user, info->lastParticipants) { - auto admin = info->lastAdmins.constFind(user); + auto admin = info->lastAdmins.find(user); if (admin != info->lastAdmins.cend()) { _additional.restrictedRights.erase(user); - if (admin->canEdit) { + if (admin->second.canEdit) { _additional.adminCanEdit.emplace(user); } else { _additional.adminCanEdit.erase(user); } - _additional.adminRights.emplace(user, admin->rights); + _additional.adminRights.emplace(user, admin->second.rights); } else { _additional.adminCanEdit.erase(user); _additional.adminRights.erase(user); - auto restricted = info->lastRestricted.constFind(user); + auto restricted = info->lastRestricted.find(user); if (restricted != info->lastRestricted.cend()) { - _additional.restrictedRights.emplace(user, restricted->rights); + _additional.restrictedRights.emplace(user, restricted->second.rights); } else { _additional.restrictedRights.erase(user); } @@ -1083,7 +1084,7 @@ void ParticipantsBoxSearchController::searchDone( int requestedCount) { auto query = _query; if (requestId) { - Auth().api().parseChannelParticipants(result, [&](auto&&...) { + Auth().api().parseChannelParticipants(_channel, result, [&](auto&&...) { auto it = _queries.find(requestId); if (it != _queries.cend()) { query = it->second.text; @@ -1184,7 +1185,7 @@ void AddParticipantBoxController::loadMoreRows() { )).done([this](const MTPchannels_ChannelParticipants &result) { _loadRequestId = 0; - Auth().api().parseChannelParticipants(result, [&]( + Auth().api().parseChannelParticipants(_channel, result, [&]( int availableCount, const QVector &list) { for (auto &participant : list) { @@ -1720,7 +1721,7 @@ void AddParticipantBoxSearchController::requestParticipants() { void AddParticipantBoxSearchController::searchParticipantsDone(mtpRequestId requestId, const MTPchannels_ChannelParticipants &result, int requestedCount) { auto query = _query; if (requestId) { - Auth().api().parseChannelParticipants(result, [&](auto&&...) { + Auth().api().parseChannelParticipants(_channel, result, [&](auto&&...) { auto it = _participantsQueries.find(requestId); if (it != _participantsQueries.cend()) { query = it->second.text; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index 34bdc4e49..bc8b4bc8d 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -98,7 +98,7 @@ private: QueuedNotification(HistoryItem *item, int forwardedCount) : history(item->history()) , peer(history->peer) - , author((item->hasFromName() && !item->isPost()) ? item->author() : nullptr) + , author((!peer->isUser() && !item->isPost()) ? item->author() : nullptr) , item((forwardedCount > 1) ? nullptr : item) , forwardedCount(forwardedCount) { } diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 8e84f2fb3..0c7f8bbf8 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -510,7 +510,7 @@ void PeerMenuAddChannelMembers(not_null channel) { return; } auto callback = [channel](const MTPchannels_ChannelParticipants &result) { - Auth().api().parseChannelParticipants(result, [&]( + Auth().api().parseChannelParticipants(channel, result, [&]( int availableCount, const QVector &list) { auto already = (