Add cache for global media search requests.

This commit is contained in:
John Preston 2024-12-17 21:09:46 +04:00
parent d59eb8e731
commit 3f0d687656
9 changed files with 119 additions and 110 deletions

View file

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

View file

@ -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<Ui::VerticalLayout>(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;
}

View file

@ -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<void(bool)> callback);
void updateControlsGeometry();
void applySearchQuery();
const not_null<Window::SessionController*> _controller;
@ -231,6 +236,9 @@ private:
const std::unique_ptr<ObjectList> _popularApps;
base::flat_map<Key, MediaList> _mediaLists;
rpl::event_stream<> _clearSearchQueryRequests;
QString _searchQuery;
base::Timer _searchQueryTimer;
Ui::Animations::Simple _shownAnimation;
Fn<void()> _showFinished;

View file

@ -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<int> fullHeightValue);
void setSearchQuery(const QString &query);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
object_ptr<Ui::FlatLabel> _text;
int _height = 0;
};
EmptyWidget::EmptyWidget(QWidget *parent)
: RpWidget(parent)
, _text(this, st::infoEmptyLabel) {
}
void EmptyWidget::setFullHeight(rpl::producer<int> 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*> controller)
: RpWidget(parent)
, _controller(controller)
, _empty(this) {
_empty->setType(type());
_empty->heightValue(
) | rpl::start_with_next(
[this] { refreshHeight(); },

View file

@ -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*> _controller;
object_ptr<Media::ListWidget> _list = { nullptr };
object_ptr<EmptyWidget> _empty;
object_ptr<Media::EmptyWidget> _empty;
bool _inResize = false;
@ -81,5 +82,4 @@ private:
};
} // namespace GlobalMedia
} // namespace Info
} // namespace Info::GlobalMedia

View file

@ -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<GlobalMediaSlice> Provider::source(
Type type,
Data::MessagePosition aroundId,
@ -247,7 +235,7 @@ rpl::producer<GlobalMediaSlice> 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<GlobalMediaSlice> 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<GlobalMediaSlice> Provider::source(
mtpRequestId Provider::requestMore(Fn<void()> 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<Data::MessagePosition>{
auto messages = std::vector<Data::MessagePosition>{
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<int>()),
hasAfter - takeAfter),
.notEnough = (takeBefore < limitBefore),
@ -400,6 +391,10 @@ void Provider::clearStaleLayouts() {
}
}
Provider::List *Provider::currentList() {
return &_totalLists[_totalListQuery];
}
rpl::producer<not_null<Media::BaseLayout*>> Provider::layoutRemoved() {
return _layoutRemoved.events();
}

View file

@ -126,6 +126,13 @@ private:
GlobalMediaSlice slice;
bool notEnough = false;
};
struct List {
std::vector<Data::MessagePosition> list;
Data::MessagePosition offsetPosition;
int32 offsetRate = 0;
int fullCount = 0;
bool loaded = false;
};
bool sectionHasFloatingHeader() override;
QString sectionTitle(not_null<const BaseLayout*> item) override;
@ -154,7 +161,7 @@ private:
void itemRemoved(not_null<const HistoryItem*> 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<Data::MessagePosition> _totalList;
Data::MessagePosition _totalOffsetPosition;
int32 _totalOffsetRate = 0;
int _totalFullCount = 0;
bool _totalLoaded = false;
base::flat_map<QString, List> _totalLists;
rpl::lifetime _lifetime;
rpl::lifetime _viewerLifetime;

View file

@ -92,6 +92,10 @@ rpl::producer<QString> SearchFieldController::queryChanges() const {
return _query.changes();
}
void SearchFieldController::setQuery(const QString &query) {
_query = query;
}
base::unique_qptr<Ui::InputField> SearchFieldController::createField(
QWidget *parent,
const style::InputField &st) {

View file

@ -40,6 +40,8 @@ public:
rpl::producer<QString> queryValue() const;
rpl::producer<QString> queryChanges() const;
void setQuery(const QString &query);
rpl::lifetime &lifetime() {
return _lifetime;
}