From 6f57302562dcb64044d90e24f9ee71ee161960e5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Jan 2024 16:03:20 +0400 Subject: [PATCH] Track tag counts in all sublists. --- .../data/data_message_reactions.cpp | 215 ++++++++++++------ .../SourceFiles/data/data_message_reactions.h | 54 +++-- .../dialogs/dialogs_inner_widget.cpp | 9 +- 3 files changed, 179 insertions(+), 99 deletions(-) diff --git a/Telegram/SourceFiles/data/data_message_reactions.cpp b/Telegram/SourceFiles/data/data_message_reactions.cpp index d2107335c..e5a521780 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.cpp +++ b/Telegram/SourceFiles/data/data_message_reactions.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_peer_values.h" +#include "data/data_saved_sublist.h" #include "data/stickers/data_custom_emoji.h" #include "storage/localimageloader.h" #include "ui/image/image_location_factory.h" @@ -258,6 +259,8 @@ Reactions::Reactions(not_null owner) , _repaintTimer([=] { repaintCollected(); }) { refreshDefault(); + _myTags.emplace(nullptr); + base::timer_each( kRefreshFullListEach ) | rpl::start_with_next([=] { @@ -320,17 +323,18 @@ void Reactions::refreshDefault() { requestDefault(); } -void Reactions::refreshMyTags() { - requestMyTags(); +void Reactions::refreshMyTags(SavedSublist *sublist) { + requestMyTags(sublist); } void Reactions::refreshMyTagsDelayed() { - if (_myTagsRequestId || _myTagsRequestScheduled) { + auto &my = _myTags[nullptr]; + if (my.requestId || my.requestScheduled) { return; } - _myTagsRequestScheduled = true; + my.requestScheduled = true; base::call_delayed(kMyTagsRequestTimeout, &_owner->session(), [=] { - if (_myTagsRequestScheduled) { + if (_myTags[nullptr].requestScheduled) { requestMyTags(); } }); @@ -346,20 +350,24 @@ const std::vector &Reactions::list(Type type) const { case Type::Recent: return _recent; case Type::Top: return _top; case Type::All: return _available; - case Type::MyTags: return _myTags; + case Type::MyTags: + return _myTags.find((SavedSublist*)nullptr)->second.tags; case Type::Tags: return _tags; } Unexpected("Type in Reactions::list."); } const std::vector &Reactions::myTagsInfo() const { - return _myTagsInfo; + return _myTags.find((SavedSublist*)nullptr)->second.info; } const QString &Reactions::myTagTitle(const ReactionId &id) const { - const auto i = ranges::find(_myTagsInfo, id, &::Data::MyTagInfo::id); - if (i != end(_myTagsInfo)) { - return i->title; + const auto i = _myTags.find((SavedSublist*)nullptr); + if (i != end(_myTags)) { + const auto j = ranges::find(i->second.info, id, &MyTagInfo::id); + if (j != end(i->second.info)) { + return j->title; + } } static const auto kEmpty = QString(); return kEmpty; @@ -389,14 +397,18 @@ void Reactions::setFavorite(const ReactionId &id) { applyFavorite(id); } -void Reactions::incrementMyTag(const ReactionId &id) { - auto i = ranges::find(_myTagsInfo, id, &MyTagInfo::id); - if (i == end(_myTagsInfo)) { - _myTagsInfo.push_back({ .id = id, .count = 0 }); - i = end(_myTagsInfo) - 1; +void Reactions::incrementMyTag(const ReactionId &id, SavedSublist *sublist) { + if (sublist) { + incrementMyTag(id, nullptr); + } + auto &my = _myTags[sublist]; + auto i = ranges::find(my.info, id, &MyTagInfo::id); + if (i == end(my.info)) { + my.info.push_back({ .id = id, .count = 0 }); + i = end(my.info) - 1; } ++i->count; - while (i != begin(_myTagsInfo)) { + while (i != begin(my.info)) { auto j = i - 1; if (j->count >= i->count) { break; @@ -404,33 +416,43 @@ void Reactions::incrementMyTag(const ReactionId &id) { std::swap(*i, *j); i = j; } - scheduleMyTagsUpdate(); + scheduleMyTagsUpdate(sublist); } -void Reactions::decrementMyTag(const ReactionId &id) { - auto i = ranges::find(_myTagsInfo, id, &MyTagInfo::id); - if (i->count <= 0) { - return; +void Reactions::decrementMyTag(const ReactionId &id, SavedSublist *sublist) { + if (sublist) { + decrementMyTag(id, nullptr); } - --i->count; - while (i + 1 != end(_myTagsInfo)) { - auto j = i + 1; - if (j->count <= i->count) { - break; + auto &my = _myTags[sublist]; + auto i = ranges::find(my.info, id, &MyTagInfo::id); + if (i->count > 0) { + --i->count; + while (i + 1 != end(my.info)) { + auto j = i + 1; + if (j->count <= i->count) { + break; + } + std::swap(*i, *j); + i = j; } - std::swap(*i, *j); - i = j; } - scheduleMyTagsUpdate(); + scheduleMyTagsUpdate(sublist); } void Reactions::renameTag(const ReactionId &id, const QString &name) { - auto i = ranges::find(_myTagsInfo, id, &MyTagInfo::id); - if (i == end(_myTagsInfo) || i->title == name) { + auto changed = false; + for (auto &[sublist, my] : _myTags) { + auto i = ranges::find(my.info, id, &MyTagInfo::id); + if (i == end(my.info) || i->title == name) { + continue; + } + i->title = name; + changed = true; + scheduleMyTagsUpdate(sublist); + } + if (!changed) { return; } - i->title = name; - scheduleMyTagsUpdate(); _myTagRenamed.fire_copy(id); using Flag = MTPmessages_UpdateSavedReactionTag::Flag; @@ -441,15 +463,17 @@ void Reactions::renameTag(const ReactionId &id, const QString &name) { )).send(); } -void Reactions::scheduleMyTagsUpdate() { - _myTagsUpdateScheduled = true; +void Reactions::scheduleMyTagsUpdate(SavedSublist *sublist) { + auto &my = _myTags[sublist]; + my.updateScheduled = true; crl::on_main(&session(), [=] { - if (!_myTagsUpdateScheduled) { + auto &my = _myTags[sublist]; + if (!my.updateScheduled) { return; } - _myTagsUpdateScheduled = false; - _myTags = resolveByInfos(_myTagsInfo, _unresolvedMyTags); - _myTagsUpdated.fire({}); + my.updateScheduled = false; + my.tags = resolveByInfos(my.info, _unresolvedMyTags, sublist); + _myTagsUpdated.fire_copy(sublist); }); } @@ -515,7 +539,10 @@ rpl::producer<> Reactions::favoriteUpdates() const { } rpl::producer<> Reactions::myTagsUpdates() const { - return _myTagsUpdated.events(); + return _myTagsUpdated.events( + ) | rpl::filter( + !rpl::mappers::_1 + ) | rpl::to_empty; } rpl::producer<> Reactions::tagsUpdates() const { @@ -768,25 +795,29 @@ void Reactions::requestGeneric() { }).send(); } -void Reactions::requestMyTags() { - if (_myTagsRequestId) { +void Reactions::requestMyTags(SavedSublist *sublist) { + auto &my = _myTags[sublist]; + if (my.requestId) { return; } auto &api = _owner->session().api(); - _myTagsRequestScheduled = false; - _myTagsRequestId = api.request(MTPmessages_GetSavedReactionTags( - MTP_flags(0), - MTP_inputPeerEmpty(), - MTP_long(_myTagsHash) + my.requestScheduled = false; + using Flag = MTPmessages_GetSavedReactionTags::Flag; + my.requestId = api.request(MTPmessages_GetSavedReactionTags( + MTP_flags(sublist ? Flag::f_peer : Flag()), + (sublist ? sublist->peer()->input : MTP_inputPeerEmpty()), + MTP_long(my.hash) )).done([=](const MTPmessages_SavedReactionTags &result) { - _myTagsRequestId = 0; + auto &my = _myTags[sublist]; + my.requestId = 0; result.match([&](const MTPDmessages_savedReactionTags &data) { - updateMyTags(data); + updateMyTags(sublist, data); }, [](const MTPDmessages_savedReactionTagsNotModified&) { }); }).fail([=] { - _myTagsRequestId = 0; - _myTagsHash = 0; + auto &my = _myTags[sublist]; + my.requestId = 0; + my.hash = 0; }).send(); } @@ -878,20 +909,25 @@ void Reactions::updateGeneric(const MTPDmessages_stickerSet &data) { } } -void Reactions::updateMyTags(const MTPDmessages_savedReactionTags &data) { - _myTagsHash = data.vhash().v; +void Reactions::updateMyTags( + SavedSublist *sublist, + const MTPDmessages_savedReactionTags &data) { + auto &my = _myTags[sublist]; + my.hash = data.vhash().v; auto list = ListFromMTP(data); auto renamed = base::flat_set(); - for (const auto &info : list) { - const auto j = ranges::find(_myTagsInfo, info.id, &MyTagInfo::id); - const auto was = (j != end(_myTagsInfo)) ? j->title : QString(); - if (info.title != was) { - renamed.emplace(info.id); + if (!sublist) { + for (const auto &info : list) { + const auto j = ranges::find(my.info, info.id, &MyTagInfo::id); + const auto was = (j != end(my.info)) ? j->title : QString(); + if (info.title != was) { + renamed.emplace(info.id); + } } } - _myTagsInfo = std::move(list); - _myTags = resolveByInfos(_myTagsInfo, _unresolvedMyTags); - _myTagsUpdated.fire({}); + my.info = std::move(list); + my.tags = resolveByInfos(my.info, _unresolvedMyTags, sublist); + _myTagsUpdated.fire_copy(sublist); for (const auto &id : renamed) { _myTagRenamed.fire_copy(id); } @@ -946,7 +982,9 @@ void Reactions::customEmojiResolveDone(not_null document) { const auto j = _unresolvedRecent.find(id); const auto recent = (j != end(_unresolvedRecent)); const auto k = _unresolvedMyTags.find(id); - const auto myTag = (k != end(_unresolvedMyTags)); + const auto myTagSublists = (k != end(_unresolvedMyTags)) + ? base::take(k->second) + : base::flat_set(); const auto l = _unresolvedTags.find(id); const auto tag = (l != end(_unresolvedTags)); if (favorite) { @@ -961,9 +999,12 @@ void Reactions::customEmojiResolveDone(not_null document) { _unresolvedRecent.erase(j); _recent = resolveByIds(_recentIds, _unresolvedRecent); } - if (myTag) { + if (!myTagSublists.empty()) { _unresolvedMyTags.erase(k); - _myTags = resolveByInfos(_myTagsInfo, _unresolvedMyTags); + for (const auto &sublist : myTagSublists) { + auto &my = _myTags[sublist]; + my.tags = resolveByInfos(my.info, _unresolvedMyTags, sublist); + } } if (tag) { _unresolvedTags.erase(l); @@ -978,8 +1019,8 @@ void Reactions::customEmojiResolveDone(not_null document) { if (recent) { _recentUpdated.fire({}); } - if (myTag) { - _myTagsUpdated.fire({}); + for (const auto &sublist : myTagSublists) { + _myTagsUpdated.fire_copy(sublist); } if (tag) { _tagsUpdated.fire({}); @@ -1016,10 +1057,12 @@ std::vector Reactions::resolveByIds( return result; } -std::optional Reactions::resolveByInfo(const MyTagInfo &info) { +std::optional Reactions::resolveByInfo( + const MyTagInfo &info, + SavedSublist *sublist) { const auto withInfo = [&](Reaction reaction) { - reaction.title = info.title; reaction.count = info.count; + reaction.title = sublist ? myTagTitle(reaction.id) : info.title; return reaction; }; if (const auto emoji = info.id.emoji(); !emoji.isEmpty()) { @@ -1038,13 +1081,20 @@ std::optional Reactions::resolveByInfo(const MyTagInfo &info) { std::vector Reactions::resolveByInfos( const std::vector &infos, - base::flat_set &unresolved) { + base::flat_map< + ReactionId, + base::flat_set> &unresolved, + SavedSublist *sublist) { auto result = std::vector(); result.reserve(infos.size()); for (const auto &tag : infos) { - if (const auto resolved = resolveByInfo(tag)) { + if (auto resolved = resolveByInfo(tag, sublist)) { result.push_back(*resolved); - } else if (unresolved.emplace(tag.id).second) { + } else if (const auto i = unresolved.find(tag.id) + ; i != end(unresolved)) { + i->second.emplace(sublist); + } else { + unresolved[tag.id].emplace(sublist); resolve(tag.id); } } @@ -1180,6 +1230,20 @@ Reaction *Reactions::lookupTemporary(const ReactionId &id) { return nullptr; } +rpl::producer> Reactions::myTagsValue( + SavedSublist *sublist) { + refreshMyTags(sublist); + const auto list = [=] { + return _myTags[sublist].tags; + }; + return rpl::single( + list() + ) | rpl::then(_myTagsUpdated.events( + ) | rpl::filter( + rpl::mappers::_1 == sublist + ) | rpl::map(list)); +} + void Reactions::repaintCollected() { const auto now = crl::now(); auto closest = crl::time(); @@ -1282,7 +1346,8 @@ void MessageReactions::add(const ReactionId &id, bool addToRecent) { auto my = 0; const auto tags = _item->reactionsAreTags(); if (tags) { - history->owner().reactions().incrementMyTag(id); + const auto sublist = _item->savedSublist(); + history->owner().reactions().incrementMyTag(id, sublist); } _list.erase(ranges::remove_if(_list, [&](MessageReaction &one) { const auto removing = one.my && (my == myLimit || ++my == myLimit); @@ -1306,7 +1371,8 @@ void MessageReactions::add(const ReactionId &id, bool addToRecent) { } } if (tags) { - history->owner().reactions().decrementMyTag(one.id); + const auto sublist = _item->savedSublist(); + history->owner().reactions().decrementMyTag(one.id, sublist); } return removed; }), end(_list)); @@ -1365,7 +1431,8 @@ void MessageReactions::remove(const ReactionId &id) { } } if (tags) { - history->owner().reactions().decrementMyTag(id); + const auto sublist = _item->savedSublist(); + history->owner().reactions().decrementMyTag(id, sublist); } auto &owner = history->owner(); owner.reactions().send(_item, false); diff --git a/Telegram/SourceFiles/data/data_message_reactions.h b/Telegram/SourceFiles/data/data_message_reactions.h index db423bc3e..5e56e1c05 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.h +++ b/Telegram/SourceFiles/data/data_message_reactions.h @@ -21,6 +21,7 @@ class CustomEmoji; namespace Data { +class SavedSublist; class DocumentMedia; class Session; @@ -79,7 +80,7 @@ public: void refreshRecent(); void refreshRecentDelayed(); void refreshDefault(); - void refreshMyTags(); + void refreshMyTags(SavedSublist *sublist = nullptr); void refreshMyTagsDelayed(); void refreshTags(); @@ -97,8 +98,8 @@ public: [[nodiscard]] ReactionId favoriteId() const; [[nodiscard]] const Reaction *favorite() const; void setFavorite(const ReactionId &id); - void incrementMyTag(const ReactionId &id); - void decrementMyTag(const ReactionId &id); + void incrementMyTag(const ReactionId &id, SavedSublist *sublist); + void decrementMyTag(const ReactionId &id, SavedSublist *sublist); void renameTag(const ReactionId &id, const QString &name); [[nodiscard]] DocumentData *chooseGenericAnimation( not_null custom) const; @@ -131,6 +132,9 @@ public: void clearTemporary(); [[nodiscard]] Reaction *lookupTemporary(const ReactionId &id); + [[nodiscard]] rpl::producer> myTagsValue( + SavedSublist *sublist = nullptr); + [[nodiscard]] static bool HasUnread(const MTPMessageReactions &data); static void CheckUnknownForUnread( not_null owner, @@ -144,6 +148,20 @@ private: std::unique_ptr icon; bool fromSelectAnimation = false; }; + struct TagsBySublist { + TagsBySublist() = default; + TagsBySublist(TagsBySublist&&) = default; + TagsBySublist(const TagsBySublist&) = delete; + TagsBySublist &operator=(TagsBySublist&&) = default; + TagsBySublist &operator=(const TagsBySublist&) = delete; + + std::vector tags; + std::vector info; + uint64 hash = 0; + mtpRequestId requestId = 0; + bool requestScheduled = false; + bool updateScheduled = false; + }; [[nodiscard]] not_null resolveListener(); void customEmojiResolveDone(not_null document) override; @@ -152,14 +170,16 @@ private: void requestRecent(); void requestDefault(); void requestGeneric(); - void requestMyTags(); + void requestMyTags(SavedSublist *sublist = nullptr); void requestTags(); void updateTop(const MTPDmessages_reactions &data); void updateRecent(const MTPDmessages_reactions &data); void updateDefault(const MTPDmessages_availableReactions &data); void updateGeneric(const MTPDmessages_stickerSet &data); - void updateMyTags(const MTPDmessages_savedReactionTags &data); + void updateMyTags( + SavedSublist *sublist, + const MTPDmessages_savedReactionTags &data); void updateTags(const MTPDmessages_reactions &data); void recentUpdated(); @@ -172,13 +192,17 @@ private: const std::vector &ids, base::flat_set &unresolved); [[nodiscard]] std::optional resolveByInfo( - const MyTagInfo &info); + const MyTagInfo &info, + SavedSublist *sublist); [[nodiscard]] std::vector resolveByInfos( const std::vector &infos, - base::flat_set &unresolved); + base::flat_map< + ReactionId, + base::flat_set> &unresolved, + SavedSublist *sublist); void resolve(const ReactionId &id); void applyFavorite(const ReactionId &id); - void scheduleMyTagsUpdate(); + void scheduleMyTagsUpdate(SavedSublist *sublist); [[nodiscard]] std::optional parse( const MTPAvailableReaction &entry); @@ -201,9 +225,10 @@ private: std::vector _recent; std::vector _recentIds; base::flat_set _unresolvedRecent; - std::vector _myTags; - std::vector _myTagsInfo; - base::flat_set _unresolvedMyTags; + base::flat_map _myTags; + base::flat_map< + ReactionId, + base::flat_set> _unresolvedMyTags; std::vector _tags; std::vector _tagsIds; base::flat_set _unresolvedTags; @@ -224,7 +249,7 @@ private: rpl::event_stream<> _recentUpdated; rpl::event_stream<> _defaultUpdated; rpl::event_stream<> _favoriteUpdated; - rpl::event_stream<> _myTagsUpdated; + rpl::event_stream _myTagsUpdated; rpl::event_stream<> _tagsUpdated; rpl::event_stream _myTagRenamed; @@ -246,11 +271,6 @@ private: mtpRequestId _genericRequestId = 0; - mtpRequestId _myTagsRequestId = 0; - bool _myTagsRequestScheduled = false; - bool _myTagsUpdateScheduled = false; - uint64 _myTagsHash = 0; - mtpRequestId _tagsRequestId = 0; uint64 _tagsHash = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 484569224..46bf60b9d 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -2987,16 +2987,9 @@ void InnerWidget::searchInChat( if (peer->isSelf()) { const auto reactions = &peer->owner().reactions(); - const auto list = [=] { - return reactions->list(Data::Reactions::Type::MyTags); - }; _searchTags = std::make_unique( &peer->owner(), - rpl::single( - list() - ) | rpl::then( - reactions->myTagsUpdates() | rpl::map(list) - ), + reactions->myTagsValue(sublist), tags); _searchTags->selectedValue(