From d1a0dfbb9790d86750f9d59294177ef86aae0ed1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Jan 2024 12:18:55 +0400 Subject: [PATCH] Search by tag on click in Saved Messages. --- .../data/data_message_reaction_id.cpp | 20 ++++++++++++ .../data/data_message_reaction_id.h | 7 +++++ .../dialogs/dialogs_inner_widget.cpp | 11 +++++-- .../dialogs/dialogs_inner_widget.h | 5 ++- .../dialogs/dialogs_search_tags.cpp | 31 ++++++++++++++++--- .../SourceFiles/dialogs/dialogs_search_tags.h | 4 ++- .../SourceFiles/dialogs/dialogs_widget.cpp | 27 +++++++++++----- Telegram/SourceFiles/dialogs/dialogs_widget.h | 7 +++-- .../history/view/history_view_message.cpp | 8 ++++- Telegram/lib_base | 2 +- 10 files changed, 102 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/data/data_message_reaction_id.cpp b/Telegram/SourceFiles/data/data_message_reaction_id.cpp index 1103d2e15..2c1a9e503 100644 --- a/Telegram/SourceFiles/data/data_message_reaction_id.cpp +++ b/Telegram/SourceFiles/data/data_message_reaction_id.cpp @@ -11,6 +11,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { +QString SearchTagToQuery(const ReactionId &tagId) { + if (const auto customId = tagId.custom()) { + return u"#tag-custom:%1"_q.arg(customId); + } else if (!tagId) { + return QString(); + } + return u"#tag-emoji:"_q + tagId.emoji(); +} + +ReactionId SearchTagFromQuery(const QString &query) { + const auto list = query.split(QChar(' ')); + const auto tag = list.isEmpty() ? QString() : list[0]; + if (tag.startsWith(u"#tag-custom:"_q)) { + return ReactionId{ DocumentId(tag.mid(12).toULongLong()) }; + } else if (tag.startsWith(u"#tag-emoji:"_q)) { + return ReactionId{ tag.mid(11) }; + } + return {}; +} + QString ReactionEntityData(const ReactionId &id) { if (id.empty()) { return {}; diff --git a/Telegram/SourceFiles/data/data_message_reaction_id.h b/Telegram/SourceFiles/data/data_message_reaction_id.h index 8c50fd9de..53d4a2df8 100644 --- a/Telegram/SourceFiles/data/data_message_reaction_id.h +++ b/Telegram/SourceFiles/data/data_message_reaction_id.h @@ -27,6 +27,10 @@ struct ReactionId { return custom ? *custom : DocumentId(); } + explicit operator bool() const { + return !empty(); + } + friend inline auto operator<=>( const ReactionId &, const ReactionId &) = default; @@ -41,6 +45,9 @@ struct MessageReaction { bool my = false; }; +[[nodiscard]] QString SearchTagToQuery(const ReactionId &tagId); +[[nodiscard]] ReactionId SearchTagFromQuery(const QString &query); + [[nodiscard]] QString ReactionEntityData(const ReactionId &id); [[nodiscard]] ReactionId ReactionFromMTP(const MTPReaction &reaction); diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index d809c893e..d7d17a37c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -2968,13 +2968,17 @@ bool InnerWidget::hasFilteredResults() const { return !_filterResults.empty() && _hashtagResults.empty(); } -void InnerWidget::searchInChat(Key key, PeerData *from) { +void InnerWidget::searchInChat( + Key key, + PeerData *from, + std::vector tags) { _searchInMigrated = nullptr; const auto sublist = key.sublist(); const auto peer = sublist ? session().user().get() : key.peer(); if (peer) { if (const auto migrateTo = peer->migrateTo()) { - return searchInChat(peer->owner().history(migrateTo), from); + const auto to = peer->owner().history(migrateTo); + return searchInChat(to, from, tags); } else if (const auto migrateFrom = peer->migrateFrom()) { _searchInMigrated = peer->owner().history(migrateFrom); } @@ -2990,7 +2994,8 @@ void InnerWidget::searchInChat(Key key, PeerData *from) { list() ) | rpl::then( reactions->myTagsUpdates() | rpl::map(list) - )); + ), + tags); _searchTags->selectedValue( ) | rpl::start_with_next([=](std::vector &&list) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index f252b7add..0f12e7211 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -139,7 +139,10 @@ public: } [[nodiscard]] bool hasFilteredResults() const; - void searchInChat(Key key, PeerData *from); + void searchInChat( + Key key, + PeerData *from, + std::vector tags); [[nodiscard]] auto searchTagsValue() const -> rpl::producer>; diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp b/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp index 22a15ec55..7f0aa16eb 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "dialogs/dialogs_search_tags.h" +#include "base/qt/qt_key_modifiers.h" #include "data/stickers/data_custom_emoji.h" #include "data/data_document.h" #include "data/data_message_reactions.h" @@ -30,14 +31,24 @@ struct SearchTags::Tag { SearchTags::SearchTags( not_null owner, - rpl::producer> tags) -: _owner(owner) { + rpl::producer> tags, + std::vector selected) +: _owner(owner) +, _added(selected) { std::move( tags ) | rpl::start_with_next([=](const std::vector &list) { fill(list); }, _lifetime); + // Mark the `selected` reactions as selected in `_tags`. + for (const auto &id : selected) { + const auto i = ranges::find(_tags, id, &Tag::id); + if (i != end(_tags)) { + i->selected = true; + } + } + style::PaletteChanged( ) | rpl::start_with_next([=] { _normalBg = _selectedBg = QImage(); @@ -54,13 +65,17 @@ void SearchTags::fill(const std::vector &list) { return std::make_shared(crl::guard(this, [=] { const auto i = ranges::find(_tags, id, &Tag::id); if (i != end(_tags)) { + if (!i->selected && !base::IsShiftPressed()) { + for (auto &tag : _tags) { + tag.selected = false; + } + } i->selected = !i->selected; _selectedChanges.fire({}); } })); }; - for (const auto &reaction : list) { - const auto id = reaction.id; + const auto push = [&](Data::ReactionId id) { const auto customId = id.custom(); _tags.push_back({ .id = id, @@ -75,6 +90,14 @@ void SearchTags::fill(const std::vector &list) { if (!customId) { _owner->reactions().preloadImageFor(id); } + }; + for (const auto &reaction : list) { + push(reaction.id); + } + for (const auto &reaction : _added) { + if (!ranges::contains(_tags, reaction, &Tag::id)) { + push(reaction); + } } if (_width > 0) { layout(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_tags.h b/Telegram/SourceFiles/dialogs/dialogs_search_tags.h index b1c193878..4c52162eb 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_search_tags.h +++ b/Telegram/SourceFiles/dialogs/dialogs_search_tags.h @@ -25,7 +25,8 @@ class SearchTags final : public base::has_weak_ptr { public: SearchTags( not_null owner, - rpl::producer> tags); + rpl::producer> tags, + std::vector selected); ~SearchTags(); void resizeToWidth(int width); @@ -61,6 +62,7 @@ private: [[nodiscard]] const QImage &validateBg(bool selected) const; const not_null _owner; + std::vector _added; std::vector _tags; rpl::event_stream<> _selectedChanges; rpl::event_stream<> _repaintRequests; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index eeeb09c9f..b41257487 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -1911,7 +1911,7 @@ void Widget::showMainMenu() { controller()->widget()->showMainMenu(); } -void Widget::searchMessages(const QString &query, Key inChat) { +void Widget::searchMessages(QString query, Key inChat) { if (_childList) { const auto forum = controller()->shownForum().current(); const auto topic = inChat.topic(); @@ -1926,6 +1926,12 @@ void Widget::searchMessages(const QString &query, Key inChat) { controller()->closeFolder(); } + auto tags = std::vector(); + if (const auto tagId = Data::SearchTagFromQuery(query)) { + inChat = session().data().history(session().user()); + query = QString(); + tags.push_back(tagId); + } const auto inChatChanged = [&] { const auto inPeer = inChat.peer(); const auto inTopic = inChat.topic(); @@ -1948,10 +1954,12 @@ void Widget::searchMessages(const QString &query, Key inChat) { } return true; }(); - if ((currentSearchQuery() != query) || inChatChanged) { + if ((currentSearchQuery() != query) + || inChatChanged + || _searchTags != tags) { if (inChat) { cancelSearch(); - setSearchInChat(inChat); + setSearchInChat(inChat, nullptr, tags); } setSearchQuery(query); applyFilterUpdate(true); @@ -2595,9 +2603,12 @@ void Widget::searchInChat(Key chat) { searchMessages(QString(), chat); } -bool Widget::setSearchInChat(Key chat, PeerData *from) { +bool Widget::setSearchInChat( + Key chat, + PeerData *from, + std::vector tags) { if (_childList) { - if (_childList->setSearchInChat(chat, from)) { + if (_childList->setSearchInChat(chat, from, tags)) { return true; } hideChildList(); @@ -2634,7 +2645,8 @@ bool Widget::setSearchInChat(Key chat, PeerData *from) { if (_layout != Layout::Main) { return false; } else if (const auto migrateTo = peer->migrateTo()) { - return setSearchInChat(peer->owner().history(migrateTo), from); + const auto to = peer->owner().history(migrateTo); + return setSearchInChat(to, from, tags); } else if (const auto migrateFrom = peer->migrateFrom()) { _searchInMigrated = peer->owner().history(migrateFrom); } @@ -2653,7 +2665,8 @@ bool Widget::setSearchInChat(Key chat, PeerData *from) { if (_searchInChat && _layout == Layout::Main) { controller()->closeFolder(); } - _inner->searchInChat(_searchInChat, _searchFromAuthor); + _searchTags = std::move(tags); + _inner->searchInChat(_searchInChat, _searchFromAuthor, _searchTags); _searchTagsLifetime = _inner->searchTagsValue( ) | rpl::start_with_next([=](std::vector &&list) { if (_searchTags != list) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index 5ab53e2f9..3c8373cb1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -117,7 +117,7 @@ public: void scrollToEntry(const RowDescriptor &entry); - void searchMessages(const QString &query, Key inChat = {}); + void searchMessages(QString query, Key inChat = {}); void searchTopics(); void searchMore(); @@ -180,7 +180,10 @@ private: void trackScroll(not_null widget); [[nodiscard]] bool searchForPeersRequired(const QString &query) const; [[nodiscard]] bool searchForTopicsRequired(const QString &query) const; - bool setSearchInChat(Key chat, PeerData *from = nullptr); + bool setSearchInChat( + Key chat, + PeerData *from = nullptr, + std::vector tags = {}); void showCalendar(); void showSearchFrom(); void showMainMenu(); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index f319dfc9a..b53fb1454 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2965,8 +2965,14 @@ void Message::refreshReactions() { if (!_reactions) { const auto handlerFactory = [=](ReactionId id) { const auto weak = base::make_weak(this); - return std::make_shared([=] { + return std::make_shared([=]( + ClickContext context) { if (const auto strong = weak.get()) { + if (strong->data()->reactionsAreTags()) { + const auto tag = Data::SearchTagToQuery(id); + HashtagClickHandler(tag).onClick(context); + return; + } strong->data()->toggleReaction( id, HistoryItem::ReactionSource::Existing); diff --git a/Telegram/lib_base b/Telegram/lib_base index 0d111bd46..bc78a03b1 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 0d111bd4633324533195aa0a840730b4bf6ba75b +Subproject commit bc78a03b12b40ee6264ad2235465197b89588288