From 7ee2e3d8bcf645e4c19a88a72d8a70ab1ccfa0fa Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Oct 2024 09:31:03 +0400 Subject: [PATCH] Support hashtags with mentions. --- .../data/data_message_reaction_id.cpp | 19 +++++++++++++++++++ .../data/data_message_reaction_id.h | 7 +++++++ Telegram/SourceFiles/mainwidget.cpp | 10 ++++++++++ .../SourceFiles/storage/storage_account.cpp | 4 +++- .../window/window_session_controller.cpp | 2 ++ .../window_session_controller_link_info.h | 1 + 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/data/data_message_reaction_id.cpp b/Telegram/SourceFiles/data/data_message_reaction_id.cpp index 1deac2bbb..b4a34a7d3 100644 --- a/Telegram/SourceFiles/data/data_message_reaction_id.cpp +++ b/Telegram/SourceFiles/data/data_message_reaction_id.cpp @@ -40,6 +40,25 @@ std::vector SearchTagsFromQuery( return result; } +HashtagWithUsername HashtagWithUsernameFromQuery(QStringView query) { + const auto match = TextUtilities::RegExpHashtag(true).match(query); + if (match.hasMatch()) { + const auto username = match.capturedView(2).mid(1).toString(); + const auto offset = int(match.capturedLength(1)); + const auto full = int(query.size()); + const auto length = full + - int(username.size()) + - 1 + - offset + - int(match.capturedLength(3)); + if (!username.isEmpty() && length > 0 && offset + length <= full) { + const auto hashtag = query.mid(offset, length).toString(); + return { hashtag, username }; + } + } + 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 c624ef865..5462ba65e 100644 --- a/Telegram/SourceFiles/data/data_message_reaction_id.h +++ b/Telegram/SourceFiles/data/data_message_reaction_id.h @@ -65,6 +65,13 @@ struct MessageReaction { [[nodiscard]] std::vector SearchTagsFromQuery( const QString &query); +struct HashtagWithUsername { + QString hashtag; + QString username; +}; +[[nodiscard]] HashtagWithUsername HashtagWithUsernameFromQuery( + QStringView query); + [[nodiscard]] QString ReactionEntityData(const ReactionId &id); [[nodiscard]] ReactionId ReactionFromMTP(const MTPReaction &reaction); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 909415988..ded80eb6d 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_history_hider.h" #include "window/window_controller.h" #include "window/window_peer_menu.h" +#include "window/window_session_controller_link_info.h" #include "window/themes/window_theme.h" #include "chat_helpers/bot_command.h" #include "chat_helpers/tabbed_selector.h" // TabbedSelector::refreshStickers @@ -744,6 +745,15 @@ void MainWidget::hideSingleUseKeyboard(FullMsgId replyToId) { } void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) { + const auto complex = Data::HashtagWithUsernameFromQuery(query); + if (!complex.username.isEmpty()) { + _controller->showPeerByLink(Window::PeerByLinkInfo{ + .usernameOrId = complex.username, + .text = complex.hashtag, + .resolveType = Window::ResolveType::HashtagSearch, + }); + return; + } auto tags = Data::SearchTagsFromQuery(query); if (_dialogs) { auto state = Dialogs::SearchState{ diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index 5af0677c5..031e61e53 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -2722,7 +2722,9 @@ std::optional Account::saveRecentHashtags( auto found = false; auto m = QRegularExpressionMatch(); auto recent = getPack(); - for (auto i = 0, next = 0; (m = TextUtilities::RegExpHashtag(false).match(text, i)).hasMatch(); i = next) { + for (auto i = 0, next = 0 + ; (m = TextUtilities::RegExpHashtag(false).match(text, i)).hasMatch() + ; i = next) { i = m.capturedStart(); next = m.capturedEnd(); if (m.hasMatch()) { diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 1442bb90f..f46d7a4d1 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -580,6 +580,8 @@ void SessionNavigation::showPeerByLinkResolved( params); } else if (resolveType == ResolveType::Profile) { showPeerInfo(peer, params); + } else if (resolveType == ResolveType::HashtagSearch) { + searchMessages(info.text, peer->owner().history(peer)); } else if (peer->isForum() && resolveType != ResolveType::Boost) { const auto itemId = info.messageId; if (!itemId) { diff --git a/Telegram/SourceFiles/window/window_session_controller_link_info.h b/Telegram/SourceFiles/window/window_session_controller_link_info.h index d64d82c53..329dc4d65 100644 --- a/Telegram/SourceFiles/window/window_session_controller_link_info.h +++ b/Telegram/SourceFiles/window/window_session_controller_link_info.h @@ -19,6 +19,7 @@ enum class ResolveType { BotStart, AddToGroup, AddToChannel, + HashtagSearch, ShareGame, Mention, Boost,