Append server-side topic search results.

This commit is contained in:
John Preston 2022-10-26 18:18:00 +04:00
parent d6ee5b3456
commit 60aef7871a
9 changed files with 224 additions and 73 deletions

View file

@ -34,7 +34,7 @@ constexpr auto kTopicsFirstLoad = 20;
constexpr auto kLoadedTopicsMinCount = 20; constexpr auto kLoadedTopicsMinCount = 20;
constexpr auto kTopicsPerPage = 500; constexpr auto kTopicsPerPage = 500;
constexpr auto kStalePerRequest = 100; constexpr auto kStalePerRequest = 100;
constexpr auto kGeneralColorId = 0xA9A9A9; // constexpr auto kGeneralColorId = 0xA9A9A9;
} // namespace } // namespace
@ -113,8 +113,7 @@ void Forum::preloadTopics() {
void Forum::reloadTopics() { void Forum::reloadTopics() {
_topicsList.setLoaded(false); _topicsList.setLoaded(false);
session().api().request(base::take(_requestId)).cancel(); session().api().request(base::take(_requestId)).cancel();
_offsetDate = 0; _offset = {};
_offsetId = _offsetTopicId = 0;
for (const auto &[rootId, topic] : _topics) { for (const auto &[rootId, topic] : _topics) {
if (!topic->creating()) { if (!topic->creating()) {
_staleRootIds.emplace(topic->rootId()); _staleRootIds.emplace(topic->rootId());
@ -127,18 +126,22 @@ void Forum::requestTopics() {
if (_topicsList.loaded() || _requestId) { if (_topicsList.loaded() || _requestId) {
return; return;
} }
const auto firstLoad = !_offsetDate; const auto firstLoad = !_offset.date;
const auto loadCount = firstLoad ? kTopicsFirstLoad : kTopicsPerPage; const auto loadCount = firstLoad ? kTopicsFirstLoad : kTopicsPerPage;
_requestId = session().api().request(MTPchannels_GetForumTopics( _requestId = session().api().request(MTPchannels_GetForumTopics(
MTP_flags(0), MTP_flags(0),
channel()->inputChannel, channel()->inputChannel,
MTPstring(), // q MTPstring(), // q
MTP_int(_offsetDate), MTP_int(_offset.date),
MTP_int(_offsetId), MTP_int(_offset.id),
MTP_int(_offsetTopicId), MTP_int(_offset.topicId),
MTP_int(loadCount) MTP_int(loadCount)
)).done([=](const MTPmessages_ForumTopics &result) { )).done([=](const MTPmessages_ForumTopics &result) {
applyReceivedTopics(result, true); applyReceivedTopics(result, _offset);
const auto &list = result.data().vtopics().v;
if (list.isEmpty() || list.size() == result.data().vcount().v) {
_topicsList.setLoaded();
}
_requestId = 0; _requestId = 0;
_chatsListChanges.fire({}); _chatsListChanges.fire({});
if (_topicsList.loaded()) { if (_topicsList.loaded()) {
@ -155,10 +158,6 @@ void Forum::requestTopics() {
}).send(); }).send();
} }
void Forum::applyReceivedTopics(const MTPmessages_ForumTopics &result) {
applyReceivedTopics(result, false);
}
void Forum::applyTopicDeleted(MsgId rootId) { void Forum::applyTopicDeleted(MsgId rootId) {
_topicsDeleted.emplace(rootId); _topicsDeleted.emplace(rootId);
@ -176,7 +175,19 @@ void Forum::applyTopicDeleted(MsgId rootId) {
void Forum::applyReceivedTopics( void Forum::applyReceivedTopics(
const MTPmessages_ForumTopics &topics, const MTPmessages_ForumTopics &topics,
bool updateOffset) { ForumOffsets &updateOffsets) {
applyReceivedTopics(topics, [&](not_null<ForumTopic*> topic) {
if (const auto last = topic->lastServerMessage()) {
updateOffsets.date = last->date();
updateOffsets.id = last->id;
}
updateOffsets.topicId = topic->rootId();
});
}
void Forum::applyReceivedTopics(
const MTPmessages_ForumTopics &topics,
Fn<void(not_null<ForumTopic*>)> callback) {
const auto &data = topics.data(); const auto &data = topics.data();
owner().processUsers(data.vusers()); owner().processUsers(data.vusers());
owner().processChats(data.vchats()); owner().processChats(data.vchats());
@ -189,9 +200,6 @@ void Forum::applyReceivedTopics(
}); });
_staleRootIds.remove(rootId); _staleRootIds.remove(rootId);
topic.match([&](const MTPDforumTopicDeleted &data) { topic.match([&](const MTPDforumTopicDeleted &data) {
if (updateOffset) {
LOG(("API Error: Got a deleted topic in getForumTopics."));
}
applyTopicDeleted(rootId); applyTopicDeleted(rootId);
}, [&](const MTPDforumTopic &data) { }, [&](const MTPDforumTopic &data) {
_topicsDeleted.remove(rootId); _topicsDeleted.remove(rootId);
@ -204,26 +212,18 @@ void Forum::applyReceivedTopics(
).first->second.get() ).first->second.get()
: i->second.get(); : i->second.get();
raw->applyTopic(data); raw->applyTopic(data);
if (updateOffset) { if (callback) {
if (const auto last = raw->lastServerMessage()) { callback(raw);
_offsetDate = last->date();
_offsetId = last->id;
}
_offsetTopicId = rootId;
} }
}); });
} }
if (updateOffset
&& (list.isEmpty() || list.size() == data.vcount().v)) {
_topicsList.setLoaded();
}
if (!_staleRootIds.empty()) { if (!_staleRootIds.empty()) {
requestSomeStale(); requestSomeStale();
} }
} }
void Forum::requestSomeStale() { void Forum::requestSomeStale() {
if (_staleRequestId || (!_offsetId && _requestId)) { if (_staleRequestId || (!_offset.id && _requestId)) {
return; return;
} }
const auto type = Histories::RequestType::History; const auto type = Histories::RequestType::History;

View file

@ -24,6 +24,16 @@ namespace Data {
class Session; class Session;
struct ForumOffsets {
TimeId date = 0;
MsgId id = 0;
MsgId topicId = 0;
friend inline constexpr auto operator<=>(
ForumOffsets,
ForumOffsets) = default;
};
class Forum final { class Forum final {
public: public:
explicit Forum(not_null<History*> history); explicit Forum(not_null<History*> history);
@ -57,7 +67,12 @@ public:
[[nodiscard]] ForumTopic *enforceTopicFor(MsgId rootId); [[nodiscard]] ForumTopic *enforceTopicFor(MsgId rootId);
[[nodiscard]] bool topicDeleted(MsgId rootId) const; [[nodiscard]] bool topicDeleted(MsgId rootId) const;
void applyReceivedTopics(const MTPmessages_ForumTopics &topics); void applyReceivedTopics(
const MTPmessages_ForumTopics &topics,
ForumOffsets &updateOffsets);
void applyReceivedTopics(
const MTPmessages_ForumTopics &topics,
Fn<void(not_null<ForumTopic*>)> callback = nullptr);
[[nodiscard]] MsgId reserveCreatingId( [[nodiscard]] MsgId reserveCreatingId(
const QString &title, const QString &title,
@ -84,10 +99,6 @@ private:
void requestSomeStale(); void requestSomeStale();
void finishTopicRequest(MsgId rootId); void finishTopicRequest(MsgId rootId);
void applyReceivedTopics(
const MTPmessages_ForumTopics &topics,
bool updateOffset);
const not_null<History*> _history; const not_null<History*> _history;
base::flat_map<MsgId, std::unique_ptr<ForumTopic>> _topics; base::flat_map<MsgId, std::unique_ptr<ForumTopic>> _topics;
@ -100,9 +111,7 @@ private:
mtpRequestId _staleRequestId = 0; mtpRequestId _staleRequestId = 0;
mtpRequestId _requestId = 0; mtpRequestId _requestId = 0;
TimeId _offsetDate = 0; ForumOffsets _offset;
MsgId _offsetId = 0;
MsgId _offsetTopicId = 0;
base::flat_set<MsgId> _creatingRootIds; base::flat_set<MsgId> _creatingRootIds;

View file

@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_unread_things.h" #include "history/history_unread_things.h"
#include "history/view/history_view_item_preview.h" #include "history/view/history_view_item_preview.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "base/unixtime.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/color_int_conversion.h" #include "ui/color_int_conversion.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
@ -148,6 +149,7 @@ ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
rootId)) rootId))
, _rootId(rootId) , _rootId(rootId)
, _lastKnownServerMessageId(rootId) , _lastKnownServerMessageId(rootId)
, _creationDate(creating() ? base::unixtime::now() : 0)
, _flags(creating() ? Flag::My : Flag()) { , _flags(creating() ? Flag::My : Flag()) {
Thread::setMuted(owner().notifySettings().isMuted(this)); Thread::setMuted(owner().notifySettings().isMuted(this));
@ -201,6 +203,10 @@ MsgId ForumTopic::rootId() const {
return _rootId; return _rootId;
} }
TimeId ForumTopic::creationDate() const {
return _creationDate;
}
bool ForumTopic::my() const { bool ForumTopic::my() const {
return (_flags & Flag::My); return (_flags & Flag::My);
} }
@ -261,6 +267,8 @@ void ForumTopic::readTillEnd() {
void ForumTopic::applyTopic(const MTPDforumTopic &data) { void ForumTopic::applyTopic(const MTPDforumTopic &data) {
Expects(_rootId == data.vid().v); Expects(_rootId == data.vid().v);
_creationDate = data.vdate().v;
applyTitle(qs(data.vtitle())); applyTitle(qs(data.vtitle()));
if (const auto iconId = data.vicon_emoji_id()) { if (const auto iconId = data.vicon_emoji_id()) {
applyIconId(iconId->v); applyIconId(iconId->v);

View file

@ -60,6 +60,7 @@ public:
[[nodiscard]] not_null<Forum*> forum() const; [[nodiscard]] not_null<Forum*> forum() const;
[[nodiscard]] rpl::producer<> destroyed() const; [[nodiscard]] rpl::producer<> destroyed() const;
[[nodiscard]] MsgId rootId() const; [[nodiscard]] MsgId rootId() const;
[[nodiscard]] TimeId creationDate() const;
[[nodiscard]] bool my() const; [[nodiscard]] bool my() const;
[[nodiscard]] bool canWrite() const; [[nodiscard]] bool canWrite() const;
@ -171,6 +172,7 @@ private:
base::flat_set<QString> _titleWords; base::flat_set<QString> _titleWords;
base::flat_set<QChar> _titleFirstLetters; base::flat_set<QChar> _titleFirstLetters;
int _titleVersion = 0; int _titleVersion = 0;
TimeId _creationDate = 0;
int32 _colorId = 0; int32 _colorId = 0;
Flags _flags; Flags _flags;

View file

@ -2061,6 +2061,18 @@ void InnerWidget::onHashtagFilterUpdate(QStringView newFilter) {
clearMouseSelection(true); clearMouseSelection(true);
} }
void InnerWidget::appendToFiltered(Key key) {
for (const auto &row : _filterResults) {
if (row->key() == key) {
return;
}
}
auto row = std::make_unique<Row>(key, _filterResults.size());
const auto [i, ok] = _filterResultsGlobal.emplace(key, std::move(row));
_filterResults.emplace_back(i->second.get());
trackSearchResultsHistory(key.owningHistory());
}
InnerWidget::~InnerWidget() { InnerWidget::~InnerWidget() {
clearSearchResults(); clearSearchResults();
} }
@ -2069,10 +2081,14 @@ void InnerWidget::clearSearchResults(bool clearPeerSearchResults) {
if (clearPeerSearchResults) _peerSearchResults.clear(); if (clearPeerSearchResults) _peerSearchResults.clear();
_searchResults.clear(); _searchResults.clear();
_searchResultsLifetime.destroy(); _searchResultsLifetime.destroy();
_searchResultsHistories.clear();
_searchedCount = _searchedMigratedCount = 0; _searchedCount = _searchedMigratedCount = 0;
} }
void InnerWidget::trackSearchResultsHistory(not_null<History*> history) { void InnerWidget::trackSearchResultsHistory(not_null<History*> history) {
if (!_searchResultsHistories.emplace(history).second) {
return;
}
const auto channel = history->peer->asChannel(); const auto channel = history->peer->asChannel();
if (!channel || channel->isBroadcast()) { if (!channel || channel->isBroadcast()) {
return; return;
@ -2088,19 +2104,51 @@ void InnerWidget::trackSearchResultsHistory(not_null<History*> history) {
row->invalidateTopic(); row->invalidateTopic();
} }
} }
auto removed = false;
for (auto i = begin(_filterResultsGlobal)
; i != end(_filterResultsGlobal);) {
if (const auto topic = i->first.topic()) {
if (topic->channel() == channel) {
removed = true;
_filterResults.erase(
ranges::remove(_filterResults, i->first, &Row::key),
end(_filterResults));
i = _filterResultsGlobal.erase(i);
continue;
}
}
++i;
}
if (removed) {
refresh();
clearMouseSelection(true);
}
update(); update();
}, _searchResultsLifetime); }, _searchResultsLifetime);
if (const auto forum = channel->forum()) { if (const auto forum = channel->forum()) {
forum->topicDestroyed( forum->topicDestroyed(
) | rpl::start_with_next([=](not_null<Data::ForumTopic*> topic) { ) | rpl::start_with_next([=](not_null<Data::ForumTopic*> topic) {
const auto from = ranges::remove( auto removed = false;
const auto sfrom = ranges::remove(
_searchResults, _searchResults,
topic.get(), topic.get(),
&FakeRow::topic); &FakeRow::topic);
if (from != end(_searchResults)) { if (sfrom != end(_searchResults)) {
_searchResults.erase(from, end(_searchResults)); _searchResults.erase(sfrom, end(_searchResults));
refresh(true); removed = true;
}
const auto ffrom = ranges::remove(
_filterResults,
topic.get(),
&Row::topic);
if (ffrom != end(_filterResults)) {
_filterResults.erase(ffrom, end(_filterResults));
removed = true;
}
_filterResultsGlobal.erase(Key(topic));
if (removed) {
refresh();
clearMouseSelection(true); clearMouseSelection(true);
} }
}, _searchResultsLifetime); }, _searchResultsLifetime);
@ -2133,6 +2181,10 @@ void InnerWidget::setLoadMoreCallback(Fn<void()> callback) {
_loadMoreCallback = std::move(callback); _loadMoreCallback = std::move(callback);
} }
void InnerWidget::setLoadMoreFilteredCallback(Fn<void()> callback) {
_loadMoreFilteredCallback = std::move(callback);
}
auto InnerWidget::chosenRow() const -> rpl::producer<ChosenRow> { auto InnerWidget::chosenRow() const -> rpl::producer<ChosenRow> {
return _chosenRow.events(); return _chosenRow.events();
} }
@ -2183,8 +2235,14 @@ void InnerWidget::visibleTopBottomUpdated(
_visibleTop = visibleTop; _visibleTop = visibleTop;
_visibleBottom = visibleBottom; _visibleBottom = visibleBottom;
loadPeerPhotos(); loadPeerPhotos();
if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop) const auto loadTill = _visibleTop
>= height()) { + PreloadHeightsCount * (_visibleBottom - _visibleTop);
if (_state == WidgetState::Filtered && loadTill >= peerSearchOffset()) {
if (_loadMoreFilteredCallback) {
_loadMoreFilteredCallback();
}
}
if (loadTill >= height()) {
if (_loadMoreCallback) { if (_loadMoreCallback) {
_loadMoreCallback(); _loadMoreCallback();
} }
@ -2310,31 +2368,12 @@ void InnerWidget::peerSearchReceived(
return; return;
} }
const auto alreadyAdded = [&](not_null<PeerData*> peer) {
for (const auto &row : _filterResults) {
if (const auto history = row->history()) {
if (history->peer == peer) {
return true;
}
}
}
return false;
};
_peerSearchQuery = query.toLower().trimmed(); _peerSearchQuery = query.toLower().trimmed();
_peerSearchResults.clear(); _peerSearchResults.clear();
_peerSearchResults.reserve(result.size()); _peerSearchResults.reserve(result.size());
for (const auto &mtpPeer : my) { for (const auto &mtpPeer : my) {
if (const auto peer = session().data().peerLoaded(peerFromMTP(mtpPeer))) { if (const auto peer = session().data().peerLoaded(peerFromMTP(mtpPeer))) {
if (alreadyAdded(peer)) { appendToFiltered(peer->owner().history(peer));
continue;
}
auto row = std::make_unique<Row>(
peer->owner().history(peer),
_filterResults.size());
const auto [i, ok] = _filterResultsGlobal.emplace(
peer,
std::move(row));
_filterResults.emplace_back(i->second.get());
} else { } else {
LOG(("API Error: " LOG(("API Error: "
"user %1 was not loaded in InnerWidget::peopleReceived()" "user %1 was not loaded in InnerWidget::peopleReceived()"

View file

@ -131,10 +131,12 @@ public:
void applyFilterUpdate(QString newFilter, bool force = false); void applyFilterUpdate(QString newFilter, bool force = false);
void onHashtagFilterUpdate(QStringView newFilter); void onHashtagFilterUpdate(QStringView newFilter);
void appendToFiltered(Key key);
PeerData *updateFromParentDrag(QPoint globalPosition); PeerData *updateFromParentDrag(QPoint globalPosition);
void setLoadMoreCallback(Fn<void()> callback); void setLoadMoreCallback(Fn<void()> callback);
void setLoadMoreFilteredCallback(Fn<void()> callback);
[[nodiscard]] rpl::producer<> listBottomReached() const; [[nodiscard]] rpl::producer<> listBottomReached() const;
[[nodiscard]] rpl::producer<> cancelSearchFromUserRequests() const; [[nodiscard]] rpl::producer<> cancelSearchFromUserRequests() const;
[[nodiscard]] rpl::producer<ChosenRow> chosenRow() const; [[nodiscard]] rpl::producer<ChosenRow> chosenRow() const;
@ -389,9 +391,7 @@ private:
bool _hashtagDeletePressed = false; bool _hashtagDeletePressed = false;
std::vector<not_null<Row*>> _filterResults; std::vector<not_null<Row*>> _filterResults;
base::flat_map< base::flat_map<Key, std::unique_ptr<Row>> _filterResultsGlobal;
not_null<PeerData*>,
std::unique_ptr<Row>> _filterResultsGlobal;
int _filteredSelected = -1; int _filteredSelected = -1;
int _filteredPressed = -1; int _filteredPressed = -1;
@ -404,6 +404,7 @@ private:
int _peerSearchPressed = -1; int _peerSearchPressed = -1;
std::vector<std::unique_ptr<FakeRow>> _searchResults; std::vector<std::unique_ptr<FakeRow>> _searchResults;
base::flat_set<not_null<History*>> _searchResultsHistories;
rpl::lifetime _searchResultsLifetime; rpl::lifetime _searchResultsLifetime;
int _searchedCount = 0; int _searchedCount = 0;
int _searchedMigratedCount = 0; int _searchedMigratedCount = 0;
@ -432,6 +433,7 @@ private:
std::unique_ptr<Ui::VideoUserpic>> _videoUserpics; std::unique_ptr<Ui::VideoUserpic>> _videoUserpics;
Fn<void()> _loadMoreCallback; Fn<void()> _loadMoreCallback;
Fn<void()> _loadMoreFilteredCallback;
rpl::event_stream<> _listBottomReached; rpl::event_stream<> _listBottomReached;
rpl::event_stream<ChosenRow> _chosenRow; rpl::event_stream<ChosenRow> _chosenRow;
rpl::event_stream<> _updated; rpl::event_stream<> _updated;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "dialogs/dialogs_entry.h" #include "dialogs/dialogs_entry.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h"
#include "history/view/history_view_top_bar_widget.h" #include "history/view/history_view_top_bar_widget.h"
#include "history/view/history_view_contact_status.h" #include "history/view/history_view_contact_status.h"
#include "history/view/history_view_requests_bar.h" #include "history/view/history_view_requests_bar.h"
@ -350,6 +351,14 @@ Widget::Widget(
setAcceptDrops(true); setAcceptDrops(true);
_inner->setLoadMoreFilteredCallback([=] {
const auto state = _inner->state();
if (state == WidgetState::Filtered
&& !_topicSearchFull
&& searchForTopicsRequired(_topicSearchQuery)) {
searchTopics();
}
});
_inner->setLoadMoreCallback([=] { _inner->setLoadMoreCallback([=] {
const auto state = _inner->state(); const auto state = _inner->state();
if (state == WidgetState::Filtered if (state == WidgetState::Filtered
@ -358,7 +367,7 @@ Widget::Widget(
&& _searchFull && _searchFull
&& !_searchFullMigrated))) { && !_searchFullMigrated))) {
searchMore(); searchMore();
} else if (_openedForum) { } else if (_openedForum && state == WidgetState::Default) {
_openedForum->forum()->requestTopics(); _openedForum->forum()->requestTopics();
} else { } else {
const auto folder = _inner->shownFolder(); const auto folder = _inner->shownFolder();
@ -728,6 +737,7 @@ void Widget::changeOpenedSubsection(
refreshTopBars(); refreshTopBars();
updateControlsVisibility(true); updateControlsVisibility(true);
_peerSearchRequest = 0; _peerSearchRequest = 0;
_api.request(base::take(_topicSearchRequest)).cancel();
if (animated == anim::type::normal) { if (animated == anim::type::normal) {
_connecting->setForceHidden(true); _connecting->setForceHidden(true);
_cacheOver = grabForFolderSlideAnimation(); _cacheOver = grabForFolderSlideAnimation();
@ -749,6 +759,7 @@ void Widget::changeOpenedForum(ChannelData *forum, anim::type animated) {
cancelSearch(); cancelSearch();
} }
_openedForum = forum; _openedForum = forum;
_api.request(base::take(_topicSearchRequest)).cancel();
_inner->changeOpenedForum(forum); _inner->changeOpenedForum(forum);
}, (forum != nullptr), animated); }, (forum != nullptr), animated);
} }
@ -1157,6 +1168,7 @@ bool Widget::searchMessages(bool searchCache) {
if (q.isEmpty() && !_searchFromAuthor) { if (q.isEmpty() && !_searchFromAuthor) {
cancelSearchRequest(); cancelSearchRequest();
_api.request(base::take(_peerSearchRequest)).cancel(); _api.request(base::take(_peerSearchRequest)).cancel();
_api.request(base::take(_topicSearchRequest)).cancel();
return true; return true;
} }
if (searchCache) { if (searchCache) {
@ -1284,14 +1296,36 @@ bool Widget::searchMessages(bool searchCache) {
MTP_vector<MTPUser>(0)), MTP_vector<MTPUser>(0)),
0); 0);
} }
if (searchForTopicsRequired(query)) {
if (searchCache) {
if (_topicSearchQuery != query) {
result = false;
}
} else if (_topicSearchQuery != query) {
_topicSearchQuery = query;
_topicSearchFull = false;
searchTopics();
}
} else {
_topicSearchQuery = query;
_topicSearchFull = true;
}
return result; return result;
} }
bool Widget::searchForPeersRequired(const QString &query) const { bool Widget::searchForPeersRequired(const QString &query) const {
if (_searchInChat || _openedForum || query.isEmpty()) { return !_searchInChat
return false; && !_openedForum
} && !query.isEmpty()
return (query[0] != '#'); && (query[0] != '#');
}
bool Widget::searchForTopicsRequired(const QString &query) const {
return !_searchInChat
&& _openedForum
&& !query.isEmpty()
&& (query[0] != '#')
&& !_openedForum->forum()->topicsList()->loaded();
} }
void Widget::needSearchMessages() { void Widget::needSearchMessages() {
@ -1337,6 +1371,45 @@ void Widget::searchMessages(
} }
} }
void Widget::searchTopics() {
if (_topicSearchRequest || _topicSearchFull) {
return;
}
_api.request(base::take(_topicSearchRequest)).cancel();
_topicSearchRequest = _api.request(MTPchannels_GetForumTopics(
MTP_flags(MTPchannels_GetForumTopics::Flag::f_q),
_openedForum->inputChannel,
MTP_string(_topicSearchQuery),
MTP_int(_topicSearchOffsetDate),
MTP_int(_topicSearchOffsetId),
MTP_int(_topicSearchOffsetTopicId),
MTP_int(kSearchPerPage)
)).done([=](const MTPmessages_ForumTopics &result) {
_topicSearchRequest = 0;
const auto savedTopicId = _topicSearchOffsetId;
const auto byCreation = result.data().is_order_by_create_date();
_openedForum->forum()->applyReceivedTopics(result, [&](
not_null<Data::ForumTopic*> topic) {
_topicSearchOffsetTopicId = topic->rootId();
if (byCreation) {
_topicSearchOffsetId = _topicSearchOffsetTopicId;
_topicSearchOffsetDate = topic->creationDate();
} else if (const auto last = topic->lastServerMessage()) {
_topicSearchOffsetId = last->id;
_topicSearchOffsetDate = last->date();
}
_inner->appendToFiltered(topic);
});
if (_topicSearchOffsetTopicId != savedTopicId) {
_inner->refresh();
} else {
_topicSearchFull = true;
}
}).fail([=] {
_topicSearchFull = true;
}).send();
}
void Widget::searchMore() { void Widget::searchMore() {
if (_searchRequest || _searchInHistoryRequest) { if (_searchRequest || _searchInHistoryRequest) {
return; return;
@ -1833,6 +1906,11 @@ void Widget::clearSearchCache() {
} }
_searchQuery = QString(); _searchQuery = QString();
_searchQueryFrom = nullptr; _searchQueryFrom = nullptr;
_topicSearchQuery = QString();
_topicSearchOffsetDate = 0;
_topicSearchOffsetId = _topicSearchOffsetTopicId = 0;
_api.request(base::take(_peerSearchRequest)).cancel();
_api.request(base::take(_topicSearchRequest)).cancel();
cancelSearchRequest(); cancelSearchRequest();
} }

View file

@ -88,6 +88,7 @@ public:
void scrollToEntry(const RowDescriptor &entry); void scrollToEntry(const RowDescriptor &entry);
void searchMessages(const QString &query, Key inChat = {}); void searchMessages(const QString &query, Key inChat = {});
void searchTopics();
void searchMore(); void searchMore();
void updateForwardBar(); void updateForwardBar();
@ -150,7 +151,8 @@ private:
void setupMainMenuToggle(); void setupMainMenuToggle();
void setupDownloadBar(); void setupDownloadBar();
void setupShortcuts(); void setupShortcuts();
bool searchForPeersRequired(const QString &query) const; [[nodiscard]] bool searchForPeersRequired(const QString &query) const;
[[nodiscard]] bool searchForTopicsRequired(const QString &query) const;
void setSearchInChat(Key chat, PeerData *from = nullptr); void setSearchInChat(Key chat, PeerData *from = nullptr);
void showCalendar(); void showCalendar();
void showSearchFrom(); void showSearchFrom();
@ -244,6 +246,13 @@ private:
bool _peerSearchFull = false; bool _peerSearchFull = false;
mtpRequestId _peerSearchRequest = 0; mtpRequestId _peerSearchRequest = 0;
QString _topicSearchQuery;
TimeId _topicSearchOffsetDate = 0;
MsgId _topicSearchOffsetId = 0;
MsgId _topicSearchOffsetTopicId = 0;
bool _topicSearchFull = false;
mtpRequestId _topicSearchRequest = 0;
QString _searchQuery; QString _searchQuery;
PeerData *_searchQueryFrom = nullptr; PeerData *_searchQueryFrom = nullptr;
int32 _searchNextRate = 0; int32 _searchNextRate = 0;

View file

@ -624,9 +624,13 @@ void PaintRow(
} else { } else {
p.setPen(context.active p.setPen(context.active
? st::dialogsNameFgActive ? st::dialogsNameFgActive
: context.selected : entry->folder()
? st::dialogsArchiveFgOver ? (context.selected
: st::dialogsArchiveFg); ? st::dialogsArchiveFgOver
: st::dialogsArchiveFg)
: (context.selected
? st::dialogsNameFgOver
: st::dialogsNameFg));
auto text = entry->chatListName(); // TODO feed name with emoji auto text = entry->chatListName(); // TODO feed name with emoji
auto textWidth = st::semiboldFont->width(text); auto textWidth = st::semiboldFont->width(text);
if (textWidth > rectForName.width()) { if (textWidth > rectForName.width()) {