From 3f0d687656517fbc4660f9e2ae9f05779307e8fa Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 17 Dec 2024 21:09:46 +0400 Subject: [PATCH] Add cache for global media search requests. --- .../SourceFiles/dialogs/dialogs_widget.cpp | 20 +++++- .../dialogs/ui/dialogs_suggestions.cpp | 44 ++++++++++++- .../dialogs/ui/dialogs_suggestions.h | 8 +++ .../info_global_media_inner_widget.cpp | 65 +------------------ .../info_global_media_inner_widget.h | 14 ++-- .../info_global_media_provider.cpp | 57 ++++++++-------- .../global_media/info_global_media_provider.h | 15 +++-- .../ui/search_field_controller.cpp | 4 ++ .../SourceFiles/ui/search_field_controller.h | 2 + 9 files changed, 119 insertions(+), 110 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index a7206c647..5329c0405 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -1427,6 +1427,9 @@ void Widget::updateSuggestions(anim::type animated) { controller(), TopPeersContent(&session()), RecentPeersContent(&session())); + _suggestions->clearSearchQueryRequests() | rpl::start_with_next([=] { + setSearchQuery(QString()); + }, _suggestions->lifetime()); _searchSuggestionsLocked = false; rpl::merge( @@ -2934,7 +2937,11 @@ void Widget::updateCancelSearch() { QString Widget::validateSearchQuery() { const auto query = currentSearchQuery(); - if (_searchState.tab == ChatSearchTab::PublicPosts) { + if (!_subsectionTopBar + && _suggestions + && _suggestions->consumeSearchQuery(query)) { + return QString(); + } else if (_searchState.tab == ChatSearchTab::PublicPosts) { if (_searchHashOrCashtag == HashOrCashtag::None) { _searchHashOrCashtag = HashOrCashtag::Hashtag; } @@ -3932,9 +3939,18 @@ void Widget::setSearchQuery(const QString &query, int cursorPosition) { } bool Widget::cancelSearch(CancelSearchOptions options) { + const auto clearingSuggestionsQuery = _suggestions + && _suggestions->consumeSearchQuery(QString()); + if (clearingSuggestionsQuery) { + setSearchQuery(QString()); + if (!options.forceFullCancel) { + return true; + } + } cancelSearchRequest(); auto updatedState = _searchState; - const auto clearingQuery = !updatedState.query.isEmpty(); + const auto clearingQuery = clearingSuggestionsQuery + || !updatedState.query.isEmpty(); const auto forceFullCancel = options.forceFullCancel; auto clearingInChat = (forceFullCancel || !clearingQuery) && (updatedState.inChat diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index 387daa83c..dc61f35cb 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/delayed_activation.h" #include "ui/dynamic_thumbnails.h" #include "ui/painter.h" +#include "ui/search_field_controller.h" #include "ui/unread_badge_paint.h" #include "ui/ui_utility.h" #include "window/window_separate_id.h" @@ -66,6 +67,7 @@ constexpr auto kCollapsedChannelsCount = 5; constexpr auto kProbablyMaxChannels = 1000; constexpr auto kCollapsedAppsCount = 5; constexpr auto kProbablyMaxApps = 100; +constexpr auto kSearchQueryDelay = crl::time(900); class RecentRow final : public PeerListRow { public: @@ -1335,7 +1337,8 @@ Suggestions::Suggestions( , _appsContent( _appsScroll->setOwnedWidget(object_ptr(this))) , _recentApps(setupRecentApps()) -, _popularApps(setupPopularApps()) { +, _popularApps(setupPopularApps()) +, _searchQueryTimer([=] { applySearchQuery(); }) { setupTabs(); setupChats(); setupChannels(); @@ -1754,6 +1757,43 @@ void Suggestions::chooseRow() { } } +bool Suggestions::consumeSearchQuery(const QString &query) { + using Type = MediaType; + const auto key = _key.current(); + const auto tab = key.tab; + const auto type = (key.tab == Tab::Media) ? key.mediaType : Type::kCount; + if (tab != Tab::Downloads + && type != Type::File + && type != Type::Link + && type != Type::MusicFile) { + return false; + } else if (_searchQuery == query) { + return false; + } + _searchQuery = query; + _persist = !_searchQuery.isEmpty(); + if (query.isEmpty() || tab == Tab::Downloads) { + _searchQueryTimer.cancel(); + applySearchQuery(); + } else { + _searchQueryTimer.callOnce(kSearchQueryDelay); + } + return true; +} + +void Suggestions::applySearchQuery() { + const auto key = _key.current(); + const auto controller = _mediaLists[key].wrap->controller(); + const auto search = controller->searchFieldController(); + if (search->query() != _searchQuery) { + search->setQuery(_searchQuery); + } +} + +rpl::producer<> Suggestions::clearSearchQueryRequests() const { + return _clearSearchQueryRequests.events(); +} + Data::Thread *Suggestions::updateFromParentDrag(QPoint globalPosition) { switch (_key.current().tab) { case Tab::Chats: return updateFromChatsDrag(globalPosition); @@ -1825,8 +1865,10 @@ void Suggestions::switchTab(Key key) { if (was == key) { return; } + consumeSearchQuery(QString()); _key = key; _persist = false; + _clearSearchQueryRequests.fire({}); if (_tabs->isHidden()) { return; } diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h index efb42b2c6..25815d221 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/object_ptr.h" +#include "base/timer.h" #include "dialogs/ui/top_peers_strip.h" #include "ui/effects/animations.h" #include "ui/rp_widget.h" @@ -64,6 +65,9 @@ public: void selectJump(Qt::Key direction, int pageSize = 0); void chooseRow(); + bool consumeSearchQuery(const QString &query); + [[nodiscard]] rpl::producer<> clearSearchQueryRequests() const; + [[nodiscard]] Data::Thread *updateFromParentDrag(QPoint globalPosition); void dragLeft(); @@ -193,6 +197,7 @@ private: void handlePressForChatPreview(PeerId id, Fn callback); void updateControlsGeometry(); + void applySearchQuery(); const not_null _controller; @@ -231,6 +236,9 @@ private: const std::unique_ptr _popularApps; base::flat_map _mediaLists; + rpl::event_stream<> _clearSearchQueryRequests; + QString _searchQuery; + base::Timer _searchQueryTimer; Ui::Animations::Simple _shownAnimation; Fn _showFinished; diff --git a/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.cpp b/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.cpp index 0e18c0d5a..f3e0cad5d 100644 --- a/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/global_media/info_global_media_provider.h" #include "info/global_media/info_global_media_widget.h" +#include "info/media/info_media_empty_widget.h" #include "info/media/info_media_list_widget.h" #include "info/info_controller.h" #include "ui/widgets/labels.h" @@ -18,75 +19,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Info::GlobalMedia { -class EmptyWidget : public Ui::RpWidget { -public: - EmptyWidget(QWidget *parent); - - void setFullHeight(rpl::producer fullHeightValue); - void setSearchQuery(const QString &query); - -protected: - int resizeGetHeight(int newWidth) override; - - void paintEvent(QPaintEvent *e) override; - -private: - object_ptr _text; - int _height = 0; - -}; - -EmptyWidget::EmptyWidget(QWidget *parent) -: RpWidget(parent) -, _text(this, st::infoEmptyLabel) { -} - -void EmptyWidget::setFullHeight(rpl::producer fullHeightValue) { - std::move( - fullHeightValue - ) | rpl::start_with_next([this](int fullHeight) { - // Make icon center be on 1/3 height. - auto iconCenter = fullHeight / 3; - auto iconHeight = st::infoEmptyFile.height(); - auto iconTop = iconCenter - iconHeight / 2; - _height = iconTop + st::infoEmptyIconTop; - resizeToWidth(width()); - }, lifetime()); -} - -void EmptyWidget::setSearchQuery(const QString &query) { - _text->setText(query.isEmpty() - ? tr::lng_media_file_empty(tr::now) - : tr::lng_media_file_empty_search(tr::now)); - resizeToWidth(width()); -} - -int EmptyWidget::resizeGetHeight(int newWidth) { - auto labelTop = _height - st::infoEmptyLabelTop; - auto labelWidth = newWidth - 2 * st::infoEmptyLabelSkip; - _text->resizeToNaturalWidth(labelWidth); - - auto labelLeft = (newWidth - _text->width()) / 2; - _text->moveToLeft(labelLeft, labelTop, newWidth); - - update(); - return _height; -} - -void EmptyWidget::paintEvent(QPaintEvent *e) { - auto p = QPainter(this); - - const auto iconLeft = (width() - st::infoEmptyFile.width()) / 2; - const auto iconTop = height() - st::infoEmptyIconTop; - st::infoEmptyFile.paint(p, iconLeft, iconTop, width()); -} - InnerWidget::InnerWidget( QWidget *parent, not_null controller) : RpWidget(parent) , _controller(controller) , _empty(this) { + _empty->setType(type()); _empty->heightValue( ) | rpl::start_with_next( [this] { refreshHeight(); }, diff --git a/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.h b/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.h index a827b1493..004647cb1 100644 --- a/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.h +++ b/Telegram/SourceFiles/info/global_media/info_global_media_inner_widget.h @@ -21,16 +21,17 @@ enum class SharedMediaType : signed char; } // namespace Storage namespace Info { - class Controller; struct SelectedItems; enum class SelectionAction; +} // namespace Info -namespace Media { +namespace Info::Media { class ListWidget; -} // namespace Media +class EmptyWidget; +} // namespace Info::Media -namespace GlobalMedia { +namespace Info::GlobalMedia { class Memento; class EmptyWidget; @@ -71,7 +72,7 @@ private: const not_null _controller; object_ptr _list = { nullptr }; - object_ptr _empty; + object_ptr _empty; bool _inResize = false; @@ -81,5 +82,4 @@ private: }; -} // namespace GlobalMedia -} // namespace Info \ No newline at end of file +} // namespace Info::GlobalMedia diff --git a/Telegram/SourceFiles/info/global_media/info_global_media_provider.cpp b/Telegram/SourceFiles/info/global_media/info_global_media_provider.cpp index 3fe7b37ed..3353208c2 100644 --- a/Telegram/SourceFiles/info/global_media/info_global_media_provider.cpp +++ b/Telegram/SourceFiles/info/global_media/info_global_media_provider.cpp @@ -227,18 +227,6 @@ void Provider::checkPreload( } } -void Provider::applyListQuery(const QString &query) { - if (_totalListQuery == query) { - return; - } - _totalListQuery = query; - _totalList.clear(); - _totalOffsetPosition = Data::MessagePosition(); - _totalOffsetRate = 0; - _totalFullCount = 0; - _totalLoaded = false; -} - rpl::producer Provider::source( Type type, Data::MessagePosition aroundId, @@ -247,7 +235,7 @@ rpl::producer Provider::source( int limitAfter) { Expects(_type == type); - applyListQuery(query); + _totalListQuery = query; return [=](auto consumer) { auto lifetime = rpl::lifetime(); const auto session = &_controller->session(); @@ -268,7 +256,7 @@ rpl::producer Provider::source( state->pushAndLoadMore = [=] { auto result = fillRequest(aroundId, limitBefore, limitAfter); consumer.put_next(std::move(result.slice)); - if (!_totalLoaded && result.notEnough) { + if (!currentList()->loaded && result.notEnough) { state->requestId = requestMore(state->pushAndLoadMore); } }; @@ -280,30 +268,32 @@ rpl::producer Provider::source( mtpRequestId Provider::requestMore(Fn loaded) { const auto done = [=](const Api::GlobalMediaResult &result) { + const auto list = currentList(); if (result.messageIds.empty()) { - _totalLoaded = true; - _totalFullCount = _totalList.size(); + list->loaded = true; + list->fullCount = list->list.size(); } else { - _totalList.reserve(_totalList.size() + result.messageIds.size()); - _totalFullCount = result.fullCount; + list->list.reserve(list->list.size() + result.messageIds.size()); + list->fullCount = result.fullCount; for (const auto &position : result.messageIds) { _seenIds.emplace(position.fullId); - _totalOffsetPosition = position; - _totalList.push_back(position); + list->offsetPosition = position; + list->list.push_back(position); } } if (!result.offsetRate) { - _totalLoaded = true; + list->loaded = true; } else { - _totalOffsetRate = result.offsetRate; + list->offsetRate = result.offsetRate; } loaded(); }; + const auto list = currentList(); return _controller->session().api().requestGlobalMedia( _type, _totalListQuery, - _totalOffsetRate, - _totalOffsetPosition, + list->offsetRate, + list->offsetPosition, done); } @@ -311,24 +301,25 @@ Provider::FillResult Provider::fillRequest( Data::MessagePosition aroundId, int limitBefore, int limitAfter) { + const auto list = currentList(); const auto i = ranges::lower_bound( - _totalList, + list->list, aroundId, std::greater<>()); - const auto hasAfter = int(i - begin(_totalList)); - const auto hasBefore = int(end(_totalList) - i); + const auto hasAfter = int(i - begin(list->list)); + const auto hasBefore = int(end(list->list) - i); const auto takeAfter = std::min(limitAfter, hasAfter); const auto takeBefore = std::min(limitBefore, hasBefore); - auto list = std::vector{ + auto messages = std::vector{ i - takeAfter, i + takeBefore, }; return FillResult{ .slice = GlobalMediaSlice( GlobalMediaKey{ aroundId }, - std::move(list), - ((!_totalList.empty() || _totalLoaded) - ? _totalFullCount + std::move(messages), + ((!list->list.empty() || list->loaded) + ? list->fullCount : std::optional()), hasAfter - takeAfter), .notEnough = (takeBefore < limitBefore), @@ -400,6 +391,10 @@ void Provider::clearStaleLayouts() { } } +Provider::List *Provider::currentList() { + return &_totalLists[_totalListQuery]; +} + rpl::producer> Provider::layoutRemoved() { return _layoutRemoved.events(); } diff --git a/Telegram/SourceFiles/info/global_media/info_global_media_provider.h b/Telegram/SourceFiles/info/global_media/info_global_media_provider.h index 34bf7f51e..bf856970b 100644 --- a/Telegram/SourceFiles/info/global_media/info_global_media_provider.h +++ b/Telegram/SourceFiles/info/global_media/info_global_media_provider.h @@ -126,6 +126,13 @@ private: GlobalMediaSlice slice; bool notEnough = false; }; + struct List { + std::vector list; + Data::MessagePosition offsetPosition; + int32 offsetRate = 0; + int fullCount = 0; + bool loaded = false; + }; bool sectionHasFloatingHeader() override; QString sectionTitle(not_null item) override; @@ -154,7 +161,7 @@ private: void itemRemoved(not_null item); void markLayoutsStale(); void clearStaleLayouts(); - void applyListQuery(const QString &query); + [[nodiscard]] List *currentList(); [[nodiscard]] FillResult fillRequest( Data::MessagePosition aroundId, int limitBefore, @@ -174,11 +181,7 @@ private: rpl::event_stream<> _refreshed; QString _totalListQuery; - std::vector _totalList; - Data::MessagePosition _totalOffsetPosition; - int32 _totalOffsetRate = 0; - int _totalFullCount = 0; - bool _totalLoaded = false; + base::flat_map _totalLists; rpl::lifetime _lifetime; rpl::lifetime _viewerLifetime; diff --git a/Telegram/SourceFiles/ui/search_field_controller.cpp b/Telegram/SourceFiles/ui/search_field_controller.cpp index 9c94190c0..b1d3ae5fa 100644 --- a/Telegram/SourceFiles/ui/search_field_controller.cpp +++ b/Telegram/SourceFiles/ui/search_field_controller.cpp @@ -92,6 +92,10 @@ rpl::producer SearchFieldController::queryChanges() const { return _query.changes(); } +void SearchFieldController::setQuery(const QString &query) { + _query = query; +} + base::unique_qptr SearchFieldController::createField( QWidget *parent, const style::InputField &st) { diff --git a/Telegram/SourceFiles/ui/search_field_controller.h b/Telegram/SourceFiles/ui/search_field_controller.h index acae6f192..b471910cf 100644 --- a/Telegram/SourceFiles/ui/search_field_controller.h +++ b/Telegram/SourceFiles/ui/search_field_controller.h @@ -40,6 +40,8 @@ public: rpl::producer queryValue() const; rpl::producer queryChanges() const; + void setQuery(const QString &query); + rpl::lifetime &lifetime() { return _lifetime; }