diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 08819cd5b7..e41e0b12eb 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -5981,6 +5981,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_rights_edit_admin_header" = "What can this admin do?"; "lng_rights_edit_admin_rank_name" = "Custom title"; "lng_rights_edit_admin_rank_about" = "A title that members will see instead of '{title}'."; +"lng_manage_peer_custom_ranks_title" = "Custom Member Titles"; +"lng_manage_peer_custom_ranks" = "Allow Custom Titles"; +"lng_manage_peer_custom_ranks_about" = "Allow members to set custom titles that will be shown next to their names in messages."; "lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with equal or fewer rights."; "lng_rights_about_add_admins_no" = "This admin will not be able to add new admins."; "lng_rights_about_by" = "This admin promoted by {user} on {date}."; diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index 495a4cbfe2..a1d0a8cb94 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -112,6 +112,7 @@ void ApplyLastList( channel->mgInfo->lastAdmins.clear(); channel->mgInfo->lastRestricted.clear(); channel->mgInfo->lastParticipants.clear(); + channel->mgInfo->memberRanks.clear(); channel->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate | MegagroupInfo::LastParticipantsOnceReceived; @@ -148,6 +149,9 @@ void ApplyLastList( channel->mgInfo->botStatus = 2; } } + if (!p.rank().isEmpty() && !p.isCreatorOrAdmin()) { + channel->mgInfo->memberRanks[p.userId()] = p.rank(); + } } } // @@ -287,12 +291,14 @@ ChatParticipant::ChatParticipant( _type = Type::Member; _date = data.vdate().v; _by = peerToUser(peerFromUser(data.vinviter_id())); + _rank = qs(data.vrank().value_or_empty()); if (data.vsubscription_until_date()) { _subscriptionDate = data.vsubscription_until_date()->v; } }, [&](const MTPDchannelParticipant &data) { _type = Type::Member; _date = data.vdate().v; + _rank = qs(data.vrank().value_or_empty()); if (data.vsubscription_until_date()) { _subscriptionDate = data.vsubscription_until_date()->v; } diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 0f7db43b9b..1d41f6033c 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -1894,6 +1894,10 @@ void Updates::feedUpdate(const MTPUpdate &update) { session().data().applyUpdate(update.c_updateChatParticipantAdmin()); } break; + case mtpc_updateChatParticipantRank: { + session().data().applyUpdate(update.c_updateChatParticipantRank()); + } break; + case mtpc_updateChatDefaultBannedRights: { session().data().applyUpdate(update.c_updateChatDefaultBannedRights()); } break; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 66de189874..94f6952493 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -389,6 +389,12 @@ QString ParticipantsAdditionalData::adminRank( return (i != end(_adminRanks)) ? i->second : QString(); } +QString ParticipantsAdditionalData::memberRank( + not_null user) const { + const auto i = _memberRanks.find(user); + return (i != end(_memberRanks)) ? i->second : QString(); +} + TimeId ParticipantsAdditionalData::adminPromotedSince( not_null user) const { const auto i = _adminPromotedSince.find(user); @@ -626,43 +632,53 @@ PeerData *ParticipantsAdditionalData::applyParticipant( return nullptr; }; - switch (data.type()) { - case Api::ChatParticipant::Type::Creator: { - if (overrideRole != Role::Profile - && overrideRole != Role::Members - && overrideRole != Role::Admins) { - return logBad(); + const auto result = [&]() -> PeerData* { + switch (data.type()) { + case Api::ChatParticipant::Type::Creator: { + if (overrideRole != Role::Profile + && overrideRole != Role::Members + && overrideRole != Role::Admins) { + return logBad(); + } + return applyCreator(data); + } + case Api::ChatParticipant::Type::Admin: { + if (overrideRole != Role::Profile + && overrideRole != Role::Members + && overrideRole != Role::Admins) { + return logBad(); + } + return applyAdmin(data); + } + case Api::ChatParticipant::Type::Member: { + if (overrideRole != Role::Profile + && overrideRole != Role::Members) { + return logBad(); + } + return applyRegular(data.userId()); + } + case Api::ChatParticipant::Type::Restricted: + case Api::ChatParticipant::Type::Banned: + if (overrideRole != Role::Profile + && overrideRole != Role::Members + && overrideRole != Role::Restricted + && overrideRole != Role::Kicked) { + return logBad(); + } + return applyBanned(data); + case Api::ChatParticipant::Type::Left: + return logBad(); + }; + Unexpected("Api::ChatParticipant::type in applyParticipant."); + }(); + if (const auto user = result ? result->asUser() : nullptr) { + if (!data.rank().isEmpty() && !data.isCreatorOrAdmin()) { + _memberRanks[user] = data.rank(); + } else { + _memberRanks.remove(user); } - return applyCreator(data); } - case Api::ChatParticipant::Type::Admin: { - if (overrideRole != Role::Profile - && overrideRole != Role::Members - && overrideRole != Role::Admins) { - return logBad(); - } - return applyAdmin(data); - } - case Api::ChatParticipant::Type::Member: { - if (overrideRole != Role::Profile - && overrideRole != Role::Members) { - return logBad(); - } - return applyRegular(data.userId()); - } - case Api::ChatParticipant::Type::Restricted: - case Api::ChatParticipant::Type::Banned: - if (overrideRole != Role::Profile - && overrideRole != Role::Members - && overrideRole != Role::Restricted - && overrideRole != Role::Kicked) { - return logBad(); - } - return applyBanned(data); - case Api::ChatParticipant::Type::Left: - return logBad(); - }; - Unexpected("Api::ChatParticipant::type in applyParticipant."); + return result; } UserData *ParticipantsAdditionalData::applyCreator( @@ -2135,7 +2151,10 @@ auto ParticipantsBoxController::computeType( && user && user->isInaccessible(); if (!result.canRemove) { - result.adminRank = user ? _additional.adminRank(user) : QString(); + const auto aRank = user ? _additional.adminRank(user) : QString(); + result.adminRank = !aRank.isEmpty() + ? aRank + : (user ? _additional.memberRank(user) : QString()); } return result; } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index 6e14120302..91cc64375d 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -107,6 +107,7 @@ public: [[nodiscard]] std::optional adminRights( not_null user) const; [[nodiscard]] QString adminRank(not_null user) const; + [[nodiscard]] QString memberRank(not_null user) const; [[nodiscard]] std::optional restrictedRights( not_null participant) const; [[nodiscard]] bool isCreator(not_null user) const; @@ -149,6 +150,7 @@ private: // Data for channels. base::flat_map, ChatAdminRightsInfo> _adminRights; base::flat_map, QString> _adminRanks; + base::flat_map, QString> _memberRanks; base::flat_map, TimeId> _adminPromotedSince; base::flat_map, TimeId> _restrictedSince; base::flat_map, TimeId> _memberSince; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index f3c2c6fc43..f5613e4c1d 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -405,6 +405,7 @@ private: std::optional signatures; std::optional signatureProfiles; std::optional noForwards; + std::optional customRanks; std::optional joinToWrite; std::optional requestToJoin; std::optional discussionLink; @@ -465,6 +466,7 @@ private: [[nodiscard]] bool validateAutotranslate(Saving &to) const; [[nodiscard]] bool validateSignatures(Saving &to) const; [[nodiscard]] bool validateForwards(Saving &to) const; + [[nodiscard]] bool validateCustomRanks(Saving &to) const; [[nodiscard]] bool validateJoinToWrite(Saving &to) const; [[nodiscard]] bool validateRequestToJoin(Saving &to) const; @@ -480,6 +482,7 @@ private: void saveAutotranslate(); void saveSignatures(); void saveForwards(); + void saveCustomRanks(); void saveJoinToWrite(); void saveRequestToJoin(); void savePhoto(); @@ -999,6 +1002,11 @@ void Controller::fillPrivacyTypeButton() { ? _peer->asChannel()->usernames() : std::vector()), .noForwards = !_peer->allowsForwarding(), + .customRanks = (_peer->isChat() + ? _peer->asChat()->customRanksEnabled() + : _peer->isChannel() + ? _peer->asChannel()->customRanksEnabled() + : false), .joinToWrite = (_peer->isMegagroup() && _peer->asChannel()->joinToWrite()), .requestToJoin = (_peer->isMegagroup() @@ -2096,6 +2104,7 @@ std::optional Controller::validate() const { && validateAutotranslate(result) && validateSignatures(result) && validateForwards(result) + && validateCustomRanks(result) && validateJoinToWrite(result) && validateRequestToJoin(result)) { return result; @@ -2218,6 +2227,14 @@ bool Controller::validateForwards(Saving &to) const { return true; } +bool Controller::validateCustomRanks(Saving &to) const { + if (!_typeDataSavedValue) { + return true; + } + to.customRanks = _typeDataSavedValue->customRanks; + return true; +} + bool Controller::validateJoinToWrite(Saving &to) const { if (!_typeDataSavedValue) { return true; @@ -2253,6 +2270,7 @@ void Controller::save() { pushSaveStage([=] { saveAutotranslate(); }); pushSaveStage([=] { saveSignatures(); }); pushSaveStage([=] { saveForwards(); }); + pushSaveStage([=] { saveCustomRanks(); }); pushSaveStage([=] { saveJoinToWrite(); }); pushSaveStage([=] { saveRequestToJoin(); }); pushSaveStage([=] { savePhoto(); }); @@ -2761,6 +2779,32 @@ void Controller::saveForwards() { }).send(); } +void Controller::saveCustomRanks() { + const auto isEnabled = _peer->isChat() + ? _peer->asChat()->customRanksEnabled() + : _peer->isChannel() + ? _peer->asChannel()->customRanksEnabled() + : false; + if (!_savingData.customRanks + || *_savingData.customRanks == isEnabled) { + return continueSave(); + } + _api.request(MTPmessages_ToggleChatCustomRanks( + _peer->input(), + MTP_bool(*_savingData.customRanks) + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + continueSave(); + }).fail([=](const MTP::Error &error) { + if (error.type() == u"CHAT_NOT_MODIFIED"_q) { + continueSave(); + } else { + _navigation->showToast(error.type()); + cancelSave(); + } + }).send(); +} + void Controller::saveJoinToWrite() { const auto joinToWrite = _peer->isMegagroup() && _peer->asChannel()->joinToWrite(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 4c2a53fc11..2218ee64ac 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -83,6 +83,10 @@ public: [[nodiscard]] bool noForwards() const { return _controls.noForwards->toggled(); } + [[nodiscard]] bool customRanks() const { + return _controls.customRanks + && _controls.customRanks->toggled(); + } [[nodiscard]] bool joinToWrite() const { return _controls.joinToWrite && _controls.joinToWrite->toggled(); } @@ -112,6 +116,7 @@ private: Ui::SlideWrap *whoSendWrap = nullptr; Ui::SettingsButton *noForwards = nullptr; + Ui::SettingsButton *customRanks = nullptr; Ui::SettingsButton *joinToWrite = nullptr; Ui::SettingsButton *requestToJoin = nullptr; }; @@ -296,6 +301,35 @@ void Controller::createContent() { (_isGroup ? tr::lng_manage_peer_no_forwards_about : tr::lng_manage_peer_no_forwards_about_channel)()); + + const auto amCreator = (_peer->isChat() + && _peer->asChat()->amCreator()) + || (_peer->isChannel() + && _peer->asChannel()->amCreator()); + if (amCreator) { + Ui::AddSkip(_wrap.get()); + Ui::AddSubsectionTitle( + _wrap.get(), + tr::lng_manage_peer_custom_ranks_title()); + _controls.customRanks = _wrap->add( + EditPeerInfoBox::CreateButton( + _wrap.get(), + tr::lng_manage_peer_custom_ranks(), + rpl::single(QString()), + [] {}, + st::peerPermissionsButton, + {})); + _controls.customRanks->toggleOn( + rpl::single(_dataSavedValue->customRanks) + )->toggledValue( + ) | rpl::on_next([=](bool toggled) { + _dataSavedValue->customRanks = toggled; + }, _wrap->lifetime()); + Ui::AddSkip(_wrap.get()); + Ui::AddDividerText( + _wrap.get(), + tr::lng_manage_peer_custom_ranks_about()); + } } if (_linkOnly) { _controls.inviteLinkWrap->show(anim::type::instant); @@ -777,6 +811,7 @@ void EditPeerTypeBox::prepare() { ? controller->usernamesOrder() : std::vector()), .noForwards = controller->noForwards(), + .customRanks = controller->customRanks(), .joinToWrite = controller->joinToWrite(), .requestToJoin = controller->requestToJoin(), }); // We don't need username with private type. diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 7b9aba6f9a..3238894481 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -39,6 +39,7 @@ struct EditPeerTypeData { std::vector usernamesOrder; bool hasDiscussionLink = false; bool noForwards = false; + bool customRanks = false; bool joinToWrite = false; bool requestToJoin = false; }; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 108a9eddc7..94049d9270 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -701,6 +701,10 @@ bool ChannelData::allowsForwarding() const { return !(flags() & Flag::NoForwards); } +bool ChannelData::customRanksEnabled() const { + return flags() & Flag::CustomRanksEnabled; +} + bool ChannelData::canViewMembers() const { return (flags() & Flag::CanViewParticipants) && (!(flags() & Flag::ParticipantsHidden) diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index ef5eaeea84..c805f29fa2 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -86,6 +86,7 @@ enum class ChannelDataFlag : uint64 { HasStarsPerMessage = (1ULL << 43), StarsPerMessageKnown = (1ULL << 44), HasActiveVideoStream = (1ULL << 45), + CustomRanksEnabled = (1ULL << 46), }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; using ChannelDataFlags = base::flags; @@ -143,6 +144,7 @@ public: // For admin badges, full admins list with ranks. base::flat_map admins; + base::flat_map memberRanks; UserData *creator = nullptr; // nullptr means unknown QString creatorRank; @@ -355,6 +357,7 @@ public: [[nodiscard]] bool autoTranslation() const { return flags() & Flag::AutoTranslation; } + [[nodiscard]] bool customRanksEnabled() const; [[nodiscard]] auto adminRights() const { return _adminRights.current(); diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 4eea779332..669eac3d10 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -67,6 +67,10 @@ bool ChatData::allowsForwarding() const { return !(flags() & Flag::NoForwards); } +bool ChatData::customRanksEnabled() const { + return flags() & Flag::CustomRanksEnabled; +} + bool ChatData::canEditInformation() const { return amIn() && !amRestricted(ChatRestriction::ChangeInfo); } @@ -437,6 +441,25 @@ void ApplyChatUpdate( session->changes().peerUpdated(chat, UpdateFlag::Admins); } +void ApplyChatUpdate( + not_null chat, + const MTPDupdateChatParticipantRank &update) { + if (chat->applyUpdateVersion(update.vversion().v) + != ChatData::UpdateStatus::Good) { + return; + } + const auto rank = qs(update.vrank().v); + const auto userId = UserId(update.vuser_id().v); + if (rank.isEmpty()) { + chat->memberRanks.remove(userId); + } else { + chat->memberRanks[userId] = rank; + } + chat->session().changes().peerUpdated( + chat, + Data::PeerUpdate::Flag::Members); +} + void ApplyChatUpdate( not_null chat, const MTPDupdateChatDefaultBannedRights &update) { @@ -532,6 +555,7 @@ void ApplyChatUpdate( chat->participants.clear(); chat->invitedByMe.clear(); chat->admins.clear(); + chat->memberRanks.clear(); chat->setAdminRights(ChatAdminRights()); const auto selfUserId = session->userId(); for (const auto &participant : list) { @@ -558,13 +582,25 @@ void ApplyChatUpdate( participant.match([&](const MTPDchatParticipantCreator &data) { chat->creator = userId; + const auto rank = qs(data.vrank().value_or_empty()); + if (!rank.isEmpty()) { + chat->memberRanks[userId] = rank; + } }, [&](const MTPDchatParticipantAdmin &data) { chat->admins.emplace(user); if (user->isSelf()) { chat->setAdminRights( chat->defaultAdminRights(user).flags); } - }, [](const MTPDchatParticipant &) { + const auto rank = qs(data.vrank().value_or_empty()); + if (!rank.isEmpty()) { + chat->memberRanks[userId] = rank; + } + }, [&](const MTPDchatParticipant &data) { + const auto rank = qs(data.vrank().value_or_empty()); + if (!rank.isEmpty()) { + chat->memberRanks[userId] = rank; + } }); } if (chat->participants.empty()) { diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index 24b719c619..d4d5f5f15c 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -23,6 +23,7 @@ enum class ChatDataFlag { CallNotEmpty = (1 << 6), CanSetUsername = (1 << 7), NoForwards = (1 << 8), + CustomRanksEnabled = (1 << 9), }; inline constexpr bool is_flag_type(ChatDataFlag) { return true; }; using ChatDataFlags = base::flags; @@ -99,6 +100,7 @@ public: // Like in ChannelData. [[nodiscard]] bool allowsForwarding() const; + [[nodiscard]] bool customRanksEnabled() const; [[nodiscard]] bool canEditInformation() const; [[nodiscard]] bool canEditPermissions() const; [[nodiscard]] bool canEditUsername() const; @@ -178,6 +180,7 @@ public: base::flat_set> admins; std::deque> lastAuthors; base::flat_set> markupSenders; + base::flat_map memberRanks; int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other private: @@ -215,6 +218,9 @@ void ApplyChatUpdate( void ApplyChatUpdate( not_null chat, const MTPDupdateChatParticipantAdmin &update); +void ApplyChatUpdate( + not_null chat, + const MTPDupdateChatParticipantRank &update); void ApplyChatUpdate( not_null chat, const MTPDupdateChatDefaultBannedRights &update); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 755d14d1d4..55622c912a 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -866,7 +866,8 @@ not_null Session::processChat(const MTPChat &data) { | Flag::Forbidden | Flag::CallActive | Flag::CallNotEmpty - | Flag::NoForwards; + | Flag::NoForwards + | Flag::CustomRanksEnabled; const auto flagsSet = (data.is_left() ? Flag::Left : Flag()) | (data.is_creator() ? Flag::Creator : Flag()) | (data.is_deactivated() ? Flag::Deactivated : Flag()) @@ -876,7 +877,8 @@ not_null Session::processChat(const MTPChat &data) { && chat->groupCall()->fullCount() > 0)) ? Flag::CallNotEmpty : Flag()) - | (data.is_noforwards() ? Flag::NoForwards : Flag()); + | (data.is_noforwards() ? Flag::NoForwards : Flag()) + | (data.is_custom_ranks_enabled() ? Flag::CustomRanksEnabled : Flag()); chat->setFlags((chat->flags() & ~flagsMask) | flagsSet); chat->count = data.vparticipants_count().v; @@ -997,7 +999,8 @@ not_null Session::processChat(const MTPChat &data) { | Flag::AutoTranslation | Flag::Monoforum | Flag::HasStarsPerMessage - | Flag::StarsPerMessageKnown; + | Flag::StarsPerMessageKnown + | Flag::CustomRanksEnabled; const auto hasStarsPerMessage = data.vsend_paid_messages_stars().has_value(); if (!hasStarsPerMessage) { @@ -1055,7 +1058,8 @@ not_null Session::processChat(const MTPChat &data) { | (channel->starsPerMessageKnown() ? Flag::StarsPerMessageKnown : Flag())) - : Flag::StarsPerMessageKnown); + : Flag::StarsPerMessageKnown) + | (data.is_custom_ranks_enabled() ? Flag::CustomRanksEnabled : Flag()); channel->setFlags((channel->flags() & ~flagsMask) | flagsSet); channel->setBotVerifyDetailsIcon( data.vbot_verification_icon().value_or_empty()); @@ -4407,6 +4411,12 @@ void Session::applyUpdate(const MTPDupdateChatParticipantAdmin &update) { } } +void Session::applyUpdate(const MTPDupdateChatParticipantRank &update) { + if (const auto chat = chatLoaded(update.vchat_id().v)) { + ApplyChatUpdate(chat, update); + } +} + void Session::applyUpdate(const MTPDupdateChatDefaultBannedRights &update) { if (const auto peer = peerLoaded(peerFromMTP(update.vpeer()))) { if (const auto chat = peer->asChat()) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index d4096e7845..b64fefd748 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -469,6 +469,7 @@ public: void applyUpdate(const MTPDupdateChatParticipantAdd &update); void applyUpdate(const MTPDupdateChatParticipantDelete &update); void applyUpdate(const MTPDupdateChatParticipantAdmin &update); + void applyUpdate(const MTPDupdateChatParticipantRank &update); void applyUpdate(const MTPDupdateChatDefaultBannedRights &update); void applyDialogs( diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 00b1a22a5b..0629a64e36 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -478,6 +478,13 @@ HistoryItem::HistoryItem( } } + if (const auto rank = data.vfrom_rank()) { + if (!rank->v.isEmpty()) { + AddComponents(HistoryMessageFromRank::Bit()); + Get()->rank = qs(*rank); + } + } + if (const auto until = data.vreport_delivery_until_date()) { if (base::unixtime::now() < TimeId(until->v)) { history->owner().histories().reportDelivery(this); @@ -3276,6 +3283,13 @@ QString HistoryItem::originalPostAuthor() const { return QString(); } +QString HistoryItem::fromRank() const { + if (const auto component = Get()) { + return component->rank; + } + return QString(); +} + MsgId HistoryItem::originalId() const { if (const auto forwarded = Get()) { return forwarded->originalId; diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index b483ac7a43..4e21760ed3 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -604,6 +604,8 @@ public: return _boostsApplied; } + [[nodiscard]] QString fromRank() const; + MsgId id; private: diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 4267e928fb..50dc9b204e 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -103,6 +103,11 @@ struct HistoryMessageSigned bool isAnonymousRank = false; }; +struct HistoryMessageFromRank +: RuntimeComponent { + QString rank; +}; + struct HistoryMessageEdited : RuntimeComponent { TimeId date = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 314766a84e..2d7e3ec04b 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/components/sponsored_messages.h" #include "data/data_session.h" #include "data/data_user.h" +#include "data/data_chat.h" #include "data/data_channel.h" #include "data/data_forum_topic.h" #include "data/data_message_reactions.h" @@ -272,23 +273,47 @@ void Message::refreshRightBadge() { } const auto channel = item->history()->peer->asMegagroup(); const auto user = item->author()->asUser(); - if (!channel || !user) { + if (!channel) { + if (const auto chat = item->history()->peer->asChat()) { + if (user) { + const auto j = chat->memberRanks.find( + peerToUser(user->id)); + if (j != chat->memberRanks.end()) { + return j->second; + } + } + } + return QString(); + } + if (!user) { return QString(); } const auto info = channel->mgInfo.get(); - const auto i = info->admins.find(peerToUser(user->id)); + const auto userId = peerToUser(user->id); + const auto i = info->admins.find(userId); const auto custom = (i != info->admins.end()) ? i->second : (info->creator == user) ? info->creatorRank : QString(); - return !custom.isEmpty() - ? custom - : (info->creator == user) - ? tr::lng_owner_badge(tr::now) - : (i != info->admins.end()) - ? tr::lng_admin_badge(tr::now) - : QString(); + if (!custom.isEmpty()) { + return custom; + } + if (info->creator == user) { + return tr::lng_owner_badge(tr::now); + } + if (i != info->admins.end()) { + return tr::lng_admin_badge(tr::now); + } + const auto fromRank = item->fromRank(); + if (!fromRank.isEmpty()) { + return fromRank; + } + const auto j = info->memberRanks.find(userId); + if (j != info->memberRanks.end()) { + return j->second; + } + return QString(); }(); auto badge = TextWithEntities{ (text.isEmpty()