Search by tag on click in Saved Messages.

This commit is contained in:
John Preston 2024-01-09 12:18:55 +04:00
parent 94a542a1d1
commit d1a0dfbb97
10 changed files with 102 additions and 20 deletions

View file

@ -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 {};

View file

@ -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);

View file

@ -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<Data::ReactionId> 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<Data::ReactionId> &&list) {

View file

@ -139,7 +139,10 @@ public:
}
[[nodiscard]] bool hasFilteredResults() const;
void searchInChat(Key key, PeerData *from);
void searchInChat(
Key key,
PeerData *from,
std::vector<Data::ReactionId> tags);
[[nodiscard]] auto searchTagsValue() const
-> rpl::producer<std::vector<Data::ReactionId>>;

View file

@ -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<Data::Session*> owner,
rpl::producer<std::vector<Data::Reaction>> tags)
: _owner(owner) {
rpl::producer<std::vector<Data::Reaction>> tags,
std::vector<Data::ReactionId> selected)
: _owner(owner)
, _added(selected) {
std::move(
tags
) | rpl::start_with_next([=](const std::vector<Data::Reaction> &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<Data::Reaction> &list) {
return std::make_shared<LambdaClickHandler>(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<Data::Reaction> &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();

View file

@ -25,7 +25,8 @@ class SearchTags final : public base::has_weak_ptr {
public:
SearchTags(
not_null<Data::Session*> owner,
rpl::producer<std::vector<Data::Reaction>> tags);
rpl::producer<std::vector<Data::Reaction>> tags,
std::vector<Data::ReactionId> selected);
~SearchTags();
void resizeToWidth(int width);
@ -61,6 +62,7 @@ private:
[[nodiscard]] const QImage &validateBg(bool selected) const;
const not_null<Data::Session*> _owner;
std::vector<Data::ReactionId> _added;
std::vector<Tag> _tags;
rpl::event_stream<> _selectedChanges;
rpl::event_stream<> _repaintRequests;

View file

@ -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<Data::ReactionId>();
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<Data::ReactionId> 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<Data::ReactionId> &&list) {
if (_searchTags != list) {

View file

@ -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<Ui::RpWidget*> 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<Data::ReactionId> tags = {});
void showCalendar();
void showSearchFrom();
void showMainMenu();

View file

@ -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<LambdaClickHandler>([=] {
return std::make_shared<LambdaClickHandler>([=](
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);

@ -1 +1 @@
Subproject commit 0d111bd4633324533195aa0a840730b4bf6ba75b
Subproject commit bc78a03b12b40ee6264ad2235465197b89588288