Start chats search rewrite.

This commit is contained in:
John Preston 2024-05-17 14:56:56 +04:00
parent 787cf7853e
commit dd5643ac67
9 changed files with 474 additions and 491 deletions

View file

@ -157,13 +157,11 @@ InnerWidget::InnerWidget(
, _narrowWidth(st::defaultDialogRow.padding.left()
+ st::defaultDialogRow.photoSize
+ st::defaultDialogRow.padding.left())
, _cancelSearchInChat(this, st::dialogsCancelSearchInPeer)
, _cancelSearchFromUser(this, st::dialogsCancelSearchInPeer)
, _chatPreviewTimer([=] { showChatPreview(true); })
, _childListShown(std::move(childListShown)) {
setAttribute(Qt::WA_OpaquePaintEvent, true);
_cancelSearchInChat->hide();
_cancelSearchFromUser->hide();
style::PaletteChanged(
@ -509,13 +507,7 @@ int InnerWidget::searchInChatSkip() const {
if (_searchTags) {
result += _searchTags->height();
}
if (_searchInChat) {
result += st::searchedBarHeight + st::dialogsSearchInHeight;
}
if (_searchFromShown) {
if (_searchInChat) {
result += st::lineWidth;
}
result += st::dialogsSearchInHeight;
}
return result;
@ -856,7 +848,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
}
}
if (_searchInChat || _searchFromPeer) {
if (_searchFromShown) {
paintSearchInChat(p, {
.st = &st::forumTopicRow,
.currentBg = currentBg(),
@ -1141,37 +1133,8 @@ void InnerWidget::paintSearchInChat(
top += height;
}
p.setFont(st::searchedBarFont);
if (_searchInChat) {
const auto bar = st::searchedBarHeight;
p.fillRect(0, top, width(), top + bar, st::searchedBarBg);
p.setPen(st::searchedBarFg);
p.drawTextLeft(st::searchedBarPosition.x(), top + st::searchedBarPosition.y(), width(), tr::lng_dlg_search_in(tr::now));
top += bar;
}
auto fullRect = QRect(0, top, width(), height - top);
p.fillRect(fullRect, currentBg());
if (_searchInChat) {
if (_searchFromShown) {
p.fillRect(QRect(0, top + st::dialogsSearchInHeight, width(), st::lineWidth), st::shadowFg);
}
p.setPen(st::dialogsNameFg);
if (const auto topic = _searchInChat.topic()) {
paintSearchInTopic(p, context, topic, _searchInChatUserpic, top, _searchInChatText);
} else if (const auto peer = _searchInChat.peer()) {
if (peer->isSelf()) {
paintSearchInSaved(p, top, _searchInChatText);
} else if (peer->isRepliesChat()) {
paintSearchInReplies(p, top, _searchInChatText);
} else {
paintSearchInPeer(p, peer, _searchInChatUserpic, top, _searchInChatText);
}
} else if (const auto sublist = _searchInChat.sublist()) {
paintSearchInSaved(p, top, _searchInChatText);
} else {
Unexpected("Empty Key in paintSearchInChat.");
}
top += st::dialogsSearchInHeight + st::lineWidth;
}
if (_searchFromShown) {
p.setPen(st::dialogsTextFg);
p.setTextPalette(st::dialogsSearchFromPalette);
@ -1930,12 +1893,10 @@ void InnerWidget::moveCancelSearchButtons() {
const auto widthForCancelButton = qMax(
width(),
st::columnMinimalWidthLeft - _narrowWidth);
const auto left = widthForCancelButton - st::dialogsSearchInSkip - _cancelSearchInChat->width();
const auto left = widthForCancelButton - st::dialogsSearchInSkip - _cancelSearchFromUser->width();
const auto top = (st::dialogsSearchInHeight - st::dialogsCancelSearchInPeer.height) / 2;
const auto skip = st::searchedBarHeight + (_searchTags ? _searchTags->height() : 0);
_cancelSearchInChat->moveToLeft(left, skip + top);
const auto next = _searchInChat ? (skip + st::dialogsSearchInHeight + st::lineWidth) : 0;
_cancelSearchFromUser->moveToLeft(left, next + top);
_cancelSearchFromUser->moveToLeft(left, skip + top);
}
void InnerWidget::dialogRowReplaced(
@ -2334,7 +2295,7 @@ void InnerWidget::fillArchiveSearchMenu(not_null<Ui::PopupMenu*> menu) {
const auto folder = session().data().folderLoaded(Data::Folder::kId);
if (!folder
|| !folder->chatsList()->fullSize().current()
|| _searchInChat) {
|| _searchState.inChat) {
return;
}
const auto skip = session().settings().skipArchiveInSearch();
@ -2491,17 +2452,26 @@ void InnerWidget::parentGeometryChanged() {
}
}
void InnerWidget::applyFilterUpdate(QString newFilter, bool force) {
void InnerWidget::applySearchState(SearchState state) {
if (_searchState == state) {
return;
}
auto withSameQuery = state;
withSameQuery.query = _searchState.query;
const auto otherChanged = (_searchState != withSameQuery);
_searchState = std::move(state);
auto newFilter = _searchState.query;
const auto mentionsSearch = (newFilter == u"@"_q);
const auto words = mentionsSearch
? QStringList(newFilter)
: TextUtilities::PrepareSearchWords(newFilter);
newFilter = words.isEmpty() ? QString() : words.join(' ');
if (newFilter != _filter || force) {
if (newFilter != _filter || otherChanged) {
_filter = newFilter;
if (_filter.isEmpty()
&& !_searchFromPeer
&& _searchTagsSelected.empty()) {
&& !_searchState.fromPeer
&& _searchState.tags.empty()) {
clearFilter();
} else {
setState(WidgetState::Filtered);
@ -2521,9 +2491,7 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) {
top += i->row->height();
}
};
if (!_searchInChat
&& !_searchFromPeer
&& !words.isEmpty()) {
if (_searchState.filterChatsList() && !words.isEmpty()) {
if (_savedSublists) {
const auto owner = &session().data();
append(owner->savedMessages().chatsList()->indexed());
@ -2549,7 +2517,9 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) {
}
void InnerWidget::onHashtagFilterUpdate(QStringView newFilter) {
if (newFilter.isEmpty() || newFilter.at(0) != '#' || _searchInChat) {
if (newFilter.isEmpty()
|| newFilter.at(0) != '#'
|| _searchState.inChat) {
_hashtagFilter = QString();
if (!_hashtagResults.empty()) {
_hashtagResults.clear();
@ -2751,10 +2721,6 @@ rpl::producer<> InnerWidget::searchMessages() const {
return _searchMessages.events();
}
rpl::producer<> InnerWidget::cancelSearchInChatRequests() const {
return _cancelSearchInChat->clicks() | rpl::to_empty;
}
rpl::producer<QString> InnerWidget::completeHashtagRequests() const {
return _completeHashtagRequests.events();
}
@ -2847,12 +2813,12 @@ void InnerWidget::searchReceived(
const auto isMigratedSearch = (type == SearchRequestType::MigratedFromStart)
|| (type == SearchRequestType::MigratedFromOffset);
const auto key = (!_openedForum || _searchInChat.topic())
? _searchInChat
const auto key = (!_openedForum || _searchState.inChat.topic())
? _searchState.inChat
: Key(_openedForum->history());
if (inject
&& (!_searchInChat
|| inject->history() == _searchInChat.history())) {
&& (!_searchState.inChat
|| inject->history() == _searchState.inChat.history())) {
Assert(_searchResults.empty());
const auto index = int(_searchResults.size());
_searchResults.push_back(
@ -2984,7 +2950,7 @@ void InnerWidget::refresh(bool toTop) {
}
} else if (_state == WidgetState::Filtered) {
if (_waitingForSearch) {
h = searchedOffset() + (_searchResults.size() * _st->height) + ((_searchResults.empty() && !_searchInChat) ? -st::searchedBarHeight : 0);
h = searchedOffset() + (_searchResults.size() * _st->height) + ((_searchResults.empty() && !_searchState.inChat) ? -st::searchedBarHeight : 0);
} else {
h = searchedOffset() + (_searchResults.size() * _st->height);
}
@ -3124,7 +3090,7 @@ void InnerWidget::searchInChat(
_searchTags->selectedChanges(
) | rpl::start_with_next([=](std::vector<Data::ReactionId> &&list) {
_searchTagsSelected = std::move(list);
_searchState.tags = std::move(list);
}, _searchTags->lifetime());
_searchTags->repaintRequests() | rpl::start_with_next([=] {
@ -3150,20 +3116,17 @@ void InnerWidget::searchInChat(
}, _searchTags->lifetime());
} else {
_searchTags = nullptr;
_searchTagsSelected.clear();
_searchState.tags.clear();
}
} else {
_searchTags = nullptr;
_searchTagsSelected.clear();
_searchState.tags.clear();
}
_searchInChat = key;
_searchFromPeer = from;
_searchState.inChat = key;
_searchState.fromPeer = from;
_searchFromShown = key.sublist() ? key.sublist()->peer().get() : from;
if (_searchInChat) {
if (_searchState.inChat) {
onHashtagFilterUpdate(QStringView());
_cancelSearchInChat->show();
} else {
_cancelSearchInChat->hide();
}
if (_searchFromShown) {
_cancelSearchFromUser->show();
@ -3172,15 +3135,9 @@ void InnerWidget::searchInChat(
_cancelSearchFromUser->hide();
_searchFromUserUserpic = {};
}
if (_searchInChat || _searchFromPeer) {
if (_searchState.inChat || _searchState.fromPeer) {
refreshSearchInChatLabel();
}
if (peer) {
_searchInChatUserpic = peer->createUserpicView();
} else {
_searchInChatUserpic = {};
}
moveCancelSearchButtons();
}
@ -3192,27 +3149,6 @@ auto InnerWidget::searchTagsChanges() const
}
void InnerWidget::refreshSearchInChatLabel() {
const auto dialog = [&] {
if (const auto topic = _searchInChat.topic()) {
return topic->title();
} else if (const auto peer = _searchInChat.peer()) {
if (peer->isSelf()) {
return tr::lng_saved_messages(tr::now);
} else if (peer->isRepliesChat()) {
return tr::lng_replies_messages(tr::now);
}
return peer->name();
} else if (_searchInChat.sublist()) {
return tr::lng_saved_messages(tr::now);
}
return QString();
}();
if (!dialog.isEmpty()) {
_searchInChatText.setText(
st::semiboldTextStyle,
dialog,
Ui::DialogTextOptions());
}
const auto from = _searchFromShown ? _searchFromShown->name() : u""_q;
if (!from.isEmpty()) {
const auto fromUserText = tr::lng_dlg_search_from(
@ -3236,8 +3172,8 @@ void InnerWidget::repaintSearchResult(int index) {
}
void InnerWidget::clearFilter() {
if (_state == WidgetState::Filtered || _searchInChat) {
if (_searchInChat) {
if (_state == WidgetState::Filtered || _searchState.inChat) {
if (_searchState.inChat) {
setState(WidgetState::Filtered);
_waitingForSearch = true;
} else {

View file

@ -145,6 +145,7 @@ public:
}
[[nodiscard]] bool hasFilteredResults() const;
void applySearchState(SearchState state);
void searchInChat(
Key key,
PeerData *from,
@ -152,7 +153,6 @@ public:
[[nodiscard]] auto searchTagsChanges() const
-> rpl::producer<std::vector<Data::ReactionId>>;
void applyFilterUpdate(QString newFilter, bool force = false);
void onHashtagFilterUpdate(QStringView newFilter);
void appendToFiltered(Key key);
@ -169,7 +169,6 @@ public:
[[nodiscard]] rpl::producer<Ui::ScrollToRequest> mustScrollTo() const;
[[nodiscard]] rpl::producer<Ui::ScrollToRequest> dialogMoved() const;
[[nodiscard]] rpl::producer<> searchMessages() const;
[[nodiscard]] rpl::producer<> cancelSearchInChatRequests() const;
[[nodiscard]] rpl::producer<QString> completeHashtagRequests() const;
[[nodiscard]] rpl::producer<> refreshHashtagsRequests() const;
@ -486,21 +485,16 @@ private:
WidgetState _state = WidgetState::Default;
object_ptr<Ui::FlatLabel> _empty = { nullptr };
object_ptr<Ui::IconButton> _cancelSearchInChat;
object_ptr<Ui::IconButton> _cancelSearchFromUser;
Ui::DraggingScrollManager _draggingScroll;
Key _searchInChat;
SearchState _searchState;
History *_searchInMigrated = nullptr;
PeerData *_searchFromPeer = nullptr;
PeerData *_searchFromShown = nullptr;
mutable Ui::PeerUserpicView _searchInChatUserpic;
mutable Ui::PeerUserpicView _searchFromUserUserpic;
Ui::Text::String _searchInChatText;
Ui::Text::String _searchFromUserText;
std::unique_ptr<SearchTags> _searchTags;
std::vector<Data::ReactionId> _searchTagsSelected;
int _searchTagsLeft = 0;
RowDescriptor _menuRow;

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_folder.h"
#include "data/data_forum_topic.h"
#include "data/data_saved_sublist.h"
#include "dialogs/ui/chat_search_tabs.h"
#include "history/history.h"
namespace Dialogs {
@ -84,4 +85,21 @@ PeerData *Key::peer() const {
return nullptr;
}
[[nodiscard]] bool SearchState::empty() const {
return !inChat
&& QStringView(query).trimmed().isEmpty();
}
ChatSearchTab SearchState::defaultTabForMe() const {
return inChat.topic()
? ChatSearchTab::ThisTopic
: inChat.history()
? ChatSearchTab::ThisPeer
: ChatSearchTab::MyMessages;
}
bool SearchState::filterChatsList() const {
return !inChat && (tab == ChatSearchTab::MyMessages);
}
} // namespace Dialogs

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/qt/qt_compare.h"
class History;
class PeerData;
@ -15,11 +17,13 @@ class Thread;
class Folder;
class ForumTopic;
class SavedSublist;
struct ReactionId;
} // namespace Data
namespace Dialogs {
class Entry;
enum class ChatSearchTab : uchar;
class Key {
public:
@ -120,4 +124,29 @@ struct EntryState {
= default;
};
struct SearchState {
Key inChat;
PeerData *fromPeer = nullptr;
std::vector<Data::ReactionId> tags;
ChatSearchTab tab = {};
QString query;
[[nodiscard]] bool empty() const;
[[nodiscard]] ChatSearchTab defaultTabForMe() const;
[[nodiscard]] bool filterChatsList() const;
explicit operator bool() const {
return !empty();
}
friend inline auto operator<=>(
const SearchState&,
const SearchState&) noexcept = default;
friend inline bool operator==(
const SearchState&,
const SearchState&) = default;
};
;
} // namespace Dialogs

File diff suppressed because it is too large Load diff

View file

@ -100,7 +100,6 @@ public:
void showForum(
not_null<Data::Forum*> forum,
const Window::SectionShow &params);
void searchInChat(Key chat);
void setInnerFocus(bool unfocusSearch = false);
[[nodiscard]] bool searchHasFocus() const;
@ -122,9 +121,7 @@ public:
void scrollToEntry(const RowDescriptor &entry);
void searchMessages(QString query, Key inChat = {});
void searchTopics();
void searchMore();
void searchMessages(SearchState state);
[[nodiscard]] RowDescriptor resolveChatNext(RowDescriptor from = {}) const;
[[nodiscard]] RowDescriptor resolveChatPrevious(RowDescriptor from = {}) const;
@ -154,14 +151,16 @@ protected:
private:
void chosenRow(const ChosenRow &row);
void listScrollUpdated();
void cancelSearchInChat();
void searchCursorMoved();
void completeHashtag(QString tag);
[[nodiscard]] QString currentSearchQuery() const;
[[nodiscard]] int currentSearchQueryCursorPosition() const;
void clearSearchField();
bool searchMessages(bool searchCache = false);
void needSearchMessages();
void searchRequested();
bool search(bool inCache = false);
void searchTopics();
void searchMore();
void slideFinished();
void searchReceived(
@ -176,6 +175,8 @@ private:
void cancelSearchRequest();
[[nodiscard]] PeerData *searchInPeer() const;
[[nodiscard]] Data::ForumTopic *searchInTopic() const;
[[nodiscard]] PeerData *searchFromPeer() const;
[[nodiscard]] const std::vector<Data::ReactionId> &searchInTags() const;
void setupSupportMode();
void setupConnectingWidget();
@ -190,18 +191,15 @@ 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,
std::vector<Data::ReactionId> tags);
bool setSearchInChat(
Key chat,
PeerData *from = nullptr);
// Child list may be unable to set specific search state.
bool applySearchState(SearchState state);
void showCalendar();
void showSearchFrom();
void showMainMenu();
void clearSearchCache();
void setSearchQuery(const QString &query);
void setSearchQuery(const QString &query, int cursorPosition = -1);
void updateControlsVisibility(bool fast = false);
void updateLockUnlockVisibility(
anim::type animated = anim::type::instant);
@ -227,7 +225,6 @@ private:
QPixmap newContentCache,
Window::SlideDirection direction);
void applySearchTab();
void openChildList(
not_null<Data::Forum*> forum,
const Window::SectionShow &params);
@ -236,7 +233,7 @@ private:
void fullSearchRefreshOn(rpl::producer<> events);
void updateCancelSearch();
bool fixSearchQuery();
void applySearchUpdate(bool force = false);
void applySearchUpdate();
void refreshLoadMoreButton(bool mayBlock, bool isBlocked);
void loadMoreBlockedByDate();
@ -271,7 +268,7 @@ private:
Layout _layout = Layout::Main;
int _narrowWidth = 0;
object_ptr<Ui::RpWidget> _searchControls;
object_ptr<HistoryView::TopBarWidget> _subsectionTopBar = { nullptr } ;
object_ptr<HistoryView::TopBarWidget> _subsectionTopBar = { nullptr };
struct {
object_ptr<Ui::IconButton> toggle;
object_ptr<Ui::AbstractButton> under;
@ -312,23 +309,16 @@ private:
bool _forumSearchRequested = false;
bool _fixingSearchQuery = false;
bool _searchingHashtag = false;
ChatSearchTab _searchTab = ChatSearchTab();
Data::Folder *_openedFolder = nullptr;
Data::Forum *_openedForum = nullptr;
Key _searchInChat;
SearchState _searchState;
History *_searchInMigrated = nullptr;
PeerData *_searchFromAuthor = nullptr;
std::vector<Data::ReactionId> _searchTags;
rpl::lifetime _searchTagsLifetime;
QString _lastSearchText;
bool _searchSuggestionsLocked = false;
bool _searchHasFocus = false;
Key _hiddenSearchInChat;
PeerData *_hiddenSearchFromAuthor = nullptr;
std::vector<Data::ReactionId> _hiddenSearchTags;
rpl::event_stream<rpl::producer<Stories::Content>> _storiesContents;
base::flat_map<PeerId, Ui::PeerUserpicView> _storiesUserpicsViewsHidden;
base::flat_map<PeerId, Ui::PeerUserpicView> _storiesUserpicsViewsShown;
@ -357,6 +347,7 @@ private:
QString _searchQuery;
PeerData *_searchQueryFrom = nullptr;
std::vector<Data::ReactionId> _searchQueryTags;
ChatSearchTab _searchQueryTab = {};
int32 _searchNextRate = 0;
bool _searchFull = false;
bool _searchFullMigrated = false;

View file

@ -1412,15 +1412,25 @@ QString TopBarWidget::searchQueryCurrent() const {
return _searchQuery.current();
}
int TopBarWidget::searchQueryCursorPosition() const {
return _searchMode
? _searchField->textCursor().position()
: _searchQuery.current().size();
}
void TopBarWidget::searchClear() {
if (_searchMode) {
_searchField->clear();
}
}
void TopBarWidget::searchSetText(const QString &query) {
void TopBarWidget::searchSetText(const QString &query, int cursorPosition) {
if (_searchMode) {
if (cursorPosition < 0) {
cursorPosition = query.size();
}
_searchField->setText(query);
_searchField->setCursorPosition(cursorPosition);
}
}

View file

@ -88,8 +88,9 @@ public:
[[nodiscard]] rpl::producer<> searchSubmitted() const;
[[nodiscard]] rpl::producer<QString> searchQuery() const;
[[nodiscard]] QString searchQueryCurrent() const;
[[nodiscard]] int searchQueryCursorPosition() const;
void searchClear();
void searchSetText(const QString &query);
void searchSetText(const QString &query, int cursorPosition = -1);
[[nodiscard]] rpl::producer<> forwardSelectionRequest() const {
return _forwardSelection.events();

View file

@ -731,8 +731,18 @@ void MainWidget::hideSingleUseKeyboard(FullMsgId replyToId) {
}
void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) {
auto tags = Data::SearchTagsFromQuery(query);
if (controller()->isPrimary()) {
_dialogs->searchMessages(query, inChat);
using Tab = Dialogs::ChatSearchTab;
auto state = Dialogs::SearchState{
.inChat = ((tags.empty() || inChat.sublist())
? inChat
: session().data().history(session().user())),
.tags = tags,
.query = tags.empty() ? query : QString(),
};
state.tab = state.defaultTabForMe();
_dialogs->searchMessages(std::move(state));
if (isOneColumn()) {
_controller->clearSectionStack();
} else {
@ -742,7 +752,7 @@ void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) {
if (const auto sublist = inChat.sublist()) {
controller()->showSection(
std::make_shared<HistoryView::SublistMemento>(sublist));
} else if (!Data::SearchTagsFromQuery(query).empty()) {
} else if (!tags.empty()) {
inChat = controller()->session().data().history(
controller()->session().user());
}