mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement public posts hashtag search preview.
This commit is contained in:
parent
81492b7d3a
commit
cec9688d58
4 changed files with 684 additions and 368 deletions
|
@ -88,6 +88,7 @@ namespace {
|
||||||
constexpr auto kHashtagResultsLimit = 5;
|
constexpr auto kHashtagResultsLimit = 5;
|
||||||
constexpr auto kStartReorderThreshold = 30;
|
constexpr auto kStartReorderThreshold = 30;
|
||||||
constexpr auto kQueryPreviewLimit = 32;
|
constexpr auto kQueryPreviewLimit = 32;
|
||||||
|
constexpr auto kPreviewPostsLimit = 3;
|
||||||
|
|
||||||
[[nodiscard]] int FixedOnTopDialogsCount(not_null<Dialogs::IndexedList*> list) {
|
[[nodiscard]] int FixedOnTopDialogsCount(not_null<Dialogs::IndexedList*> list) {
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
|
@ -555,7 +556,7 @@ int InnerWidget::searchInChatSkip() const {
|
||||||
return _searchIn ? _searchIn->height() : 0;
|
return _searchIn ? _searchIn->height() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int InnerWidget::searchedOffset() const {
|
int InnerWidget::previewOffset() const {
|
||||||
auto result = peerSearchOffset();
|
auto result = peerSearchOffset();
|
||||||
if (!_peerSearchResults.empty()) {
|
if (!_peerSearchResults.empty()) {
|
||||||
result += (_peerSearchResults.size() * st::dialogsRowHeight)
|
result += (_peerSearchResults.size() * st::dialogsRowHeight)
|
||||||
|
@ -564,6 +565,15 @@ int InnerWidget::searchedOffset() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int InnerWidget::searchedOffset() const {
|
||||||
|
auto result = previewOffset();
|
||||||
|
if (!_previewResults.empty()) {
|
||||||
|
result += (_previewResults.size() * st::dialogsRowHeight)
|
||||||
|
+ st::searchedBarHeight;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
||||||
Expects(!folder || !_savedSublists);
|
Expects(!folder || !_savedSublists);
|
||||||
|
|
||||||
|
@ -810,7 +820,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
if (_searchIn) {
|
if (_searchIn) {
|
||||||
p.translate(0, searchInChatSkip());
|
p.translate(0, searchInChatSkip());
|
||||||
if (_searchResults.empty()) {
|
if (_previewResults.empty() && _searchResults.empty()) {
|
||||||
p.fillRect(0, 0, fullWidth, st::lineWidth, st::shadowFg);
|
p.fillRect(0, 0, fullWidth, st::lineWidth, st::shadowFg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -924,7 +934,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto showUnreadInSearchResults = uniqueSearchResults();
|
const auto showUnreadInSearchResults = uniqueSearchResults();
|
||||||
if (_searchResults.empty()) {
|
if (_previewResults.empty() && _searchResults.empty()) {
|
||||||
if (_loadingAnimation) {
|
if (_loadingAnimation) {
|
||||||
const auto text = tr::lng_contacts_loading(tr::now);
|
const auto text = tr::lng_contacts_loading(tr::now);
|
||||||
p.fillRect(0, 0, fullWidth, st::searchedBarHeight, st::searchedBarBg);
|
p.fillRect(0, 0, fullWidth, st::searchedBarHeight, st::searchedBarBg);
|
||||||
|
@ -933,7 +943,68 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
p.drawTextLeft(st::searchedBarPosition.x(), st::searchedBarPosition.y(), width(), text);
|
p.drawTextLeft(st::searchedBarPosition.x(), st::searchedBarPosition.y(), width(), text);
|
||||||
p.translate(0, st::searchedBarHeight);
|
p.translate(0, st::searchedBarHeight);
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
if (!_previewResults.empty()) {
|
||||||
|
const auto text = tr::lng_search_tab_public_posts(tr::now);
|
||||||
|
p.fillRect(0, 0, fullWidth, st::searchedBarHeight, st::searchedBarBg);
|
||||||
|
p.setFont(st::searchedBarFont);
|
||||||
|
p.setPen(st::searchedBarFg);
|
||||||
|
p.drawTextLeft(st::searchedBarPosition.x(), st::searchedBarPosition.y(), width(), text);
|
||||||
|
const auto moreFont = (_selectedMorePosts || _pressedMorePosts)
|
||||||
|
? st::searchedBarFont->underline()
|
||||||
|
: st::searchedBarFont;
|
||||||
|
{
|
||||||
|
const auto text = tr::lng_channels_your_more(tr::now);
|
||||||
|
if (!_morePostsWidth) {
|
||||||
|
_morePostsWidth = moreFont->width(text);
|
||||||
|
}
|
||||||
|
p.setFont(moreFont);
|
||||||
|
p.drawTextLeft(
|
||||||
|
width() - st::searchedBarPosition.x() - _morePostsWidth,
|
||||||
|
st::searchedBarPosition.y(),
|
||||||
|
width(),
|
||||||
|
text);
|
||||||
|
p.translate(0, st::searchedBarHeight);
|
||||||
|
}
|
||||||
|
auto skip = previewOffset();
|
||||||
|
auto from = floorclamp(r.y() - skip, _st->height, 0, _previewResults.size());
|
||||||
|
auto to = ceilclamp(r.y() + r.height() - skip, _st->height, 0, _previewResults.size());
|
||||||
|
p.translate(0, from * _st->height);
|
||||||
|
if (from < _previewResults.size()) {
|
||||||
|
for (; from < to; ++from) {
|
||||||
|
const auto &result = _previewResults[from];
|
||||||
|
const auto active = isSearchResultActive(result.get(), activeEntry);
|
||||||
|
const auto selected = _menuRow.key
|
||||||
|
? isSearchResultActive(result.get(), _menuRow)
|
||||||
|
: _chatPreviewRow.key
|
||||||
|
? isSearchResultActive(result.get(), _chatPreviewRow)
|
||||||
|
: (from == (isPressed()
|
||||||
|
? _previewPressed
|
||||||
|
: _previewSelected));
|
||||||
|
Ui::RowPainter::Paint(p, result.get(), {
|
||||||
|
.st = _st,
|
||||||
|
.folder = _openedFolder,
|
||||||
|
.forum = _openedForum,
|
||||||
|
.currentBg = currentBg(),
|
||||||
|
.filter = _filterId,
|
||||||
|
.now = ms,
|
||||||
|
.width = fullWidth,
|
||||||
|
.active = active,
|
||||||
|
.selected = selected,
|
||||||
|
.paused = videoPaused,
|
||||||
|
.search = true,
|
||||||
|
.narrow = (fullWidth < st::columnMinimalWidthLeft / 2),
|
||||||
|
.displayUnreadInfo = showUnreadInSearchResults,
|
||||||
|
});
|
||||||
|
p.translate(0, _st->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (to < _previewResults.size()) {
|
||||||
|
p.translate(0, (_previewResults.size() - to) * _st->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_searchResults.empty()) {
|
||||||
const auto text = showUnreadInSearchResults
|
const auto text = showUnreadInSearchResults
|
||||||
? u"Search results"_q
|
? u"Search results"_q
|
||||||
: tr::lng_search_found_results(
|
: tr::lng_search_found_results(
|
||||||
|
@ -1330,6 +1401,8 @@ void InnerWidget::clearIrrelevantState() {
|
||||||
setFilteredPressed(-1, false);
|
setFilteredPressed(-1, false);
|
||||||
_peerSearchSelected = -1;
|
_peerSearchSelected = -1;
|
||||||
setPeerSearchPressed(-1);
|
setPeerSearchPressed(-1);
|
||||||
|
_previewSelected = -1;
|
||||||
|
setPreviewPressed(-1);
|
||||||
_searchedSelected = -1;
|
_searchedSelected = -1;
|
||||||
setSearchedPressed(-1);
|
setSearchedPressed(-1);
|
||||||
} else if (_state == WidgetState::Filtered) {
|
} else if (_state == WidgetState::Filtered) {
|
||||||
|
@ -1446,6 +1519,32 @@ void InnerWidget::selectByMouse(QPoint globalPosition) {
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!_previewResults.empty()) {
|
||||||
|
auto skip = previewOffset();
|
||||||
|
auto previewSelected = (mouseY >= skip) ? ((mouseY - skip) / _st->height) : -1;
|
||||||
|
if (previewSelected < 0 || previewSelected >= _previewResults.size()) {
|
||||||
|
previewSelected = -1;
|
||||||
|
}
|
||||||
|
if (_previewSelected != previewSelected) {
|
||||||
|
updateSelectedRow();
|
||||||
|
_previewSelected = previewSelected;
|
||||||
|
updateSelectedRow();
|
||||||
|
}
|
||||||
|
auto selectedMorePosts = false;
|
||||||
|
const auto from = skip - st::searchedBarHeight;
|
||||||
|
if (mouseY <= skip && mouseY >= from) {
|
||||||
|
const auto left = width()
|
||||||
|
- _morePostsWidth
|
||||||
|
- 2 * st::searchedBarPosition.x();
|
||||||
|
if (_morePostsWidth > 0 && local.x() >= left) {
|
||||||
|
selectedMorePosts = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_selectedMorePosts != selectedMorePosts) {
|
||||||
|
update(0, from, width(), st::searchedBarHeight);
|
||||||
|
_selectedMorePosts = selectedMorePosts;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!_searchResults.empty()) {
|
if (!_searchResults.empty()) {
|
||||||
auto skip = searchedOffset();
|
auto skip = searchedOffset();
|
||||||
auto searchedSelected = (mouseY >= skip) ? ((mouseY - skip) / _st->height) : -1;
|
auto searchedSelected = (mouseY >= skip) ? ((mouseY - skip) / _st->height) : -1;
|
||||||
|
@ -1495,7 +1594,9 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
_hashtagDeletePressed = _hashtagDeleteSelected;
|
_hashtagDeletePressed = _hashtagDeleteSelected;
|
||||||
setFilteredPressed(_filteredSelected, _selectedTopicJump);
|
setFilteredPressed(_filteredSelected, _selectedTopicJump);
|
||||||
setPeerSearchPressed(_peerSearchSelected);
|
setPeerSearchPressed(_peerSearchSelected);
|
||||||
|
setPreviewPressed(_previewSelected);
|
||||||
setSearchedPressed(_searchedSelected);
|
setSearchedPressed(_searchedSelected);
|
||||||
|
_pressedMorePosts = _selectedMorePosts;
|
||||||
|
|
||||||
const auto alt = (e->modifiers() & Qt::AltModifier);
|
const auto alt = (e->modifiers() & Qt::AltModifier);
|
||||||
if (alt && showChatPreview()) {
|
if (alt && showChatPreview()) {
|
||||||
|
@ -1862,8 +1963,12 @@ void InnerWidget::mousePressReleased(
|
||||||
setFilteredPressed(-1, false);
|
setFilteredPressed(-1, false);
|
||||||
auto peerSearchPressed = _peerSearchPressed;
|
auto peerSearchPressed = _peerSearchPressed;
|
||||||
setPeerSearchPressed(-1);
|
setPeerSearchPressed(-1);
|
||||||
|
auto previewPressed = _previewPressed;
|
||||||
|
setPreviewPressed(-1);
|
||||||
auto searchedPressed = _searchedPressed;
|
auto searchedPressed = _searchedPressed;
|
||||||
setSearchedPressed(-1);
|
setSearchedPressed(-1);
|
||||||
|
const auto pressedMorePosts = _pressedMorePosts;
|
||||||
|
_pressedMorePosts = false;
|
||||||
if (wasDragging) {
|
if (wasDragging) {
|
||||||
selectByMouse(globalPosition);
|
selectByMouse(globalPosition);
|
||||||
}
|
}
|
||||||
|
@ -1879,8 +1984,12 @@ void InnerWidget::mousePressReleased(
|
||||||
|| (filteredPressed >= 0 && filteredPressed == _filteredSelected)
|
|| (filteredPressed >= 0 && filteredPressed == _filteredSelected)
|
||||||
|| (peerSearchPressed >= 0
|
|| (peerSearchPressed >= 0
|
||||||
&& peerSearchPressed == _peerSearchSelected)
|
&& peerSearchPressed == _peerSearchSelected)
|
||||||
|
|| (previewPressed >= 0
|
||||||
|
&& previewPressed == _previewSelected)
|
||||||
|| (searchedPressed >= 0
|
|| (searchedPressed >= 0
|
||||||
&& searchedPressed == _searchedSelected)) {
|
&& searchedPressed == _searchedSelected)
|
||||||
|
|| (pressedMorePosts
|
||||||
|
&& pressedMorePosts == _selectedMorePosts)) {
|
||||||
chooseRow(modifiers, pressedTopicRootId);
|
chooseRow(modifiers, pressedTopicRootId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1955,6 +2064,13 @@ void InnerWidget::setPeerSearchPressed(int pressed) {
|
||||||
_peerSearchPressed = pressed;
|
_peerSearchPressed = pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InnerWidget::setPreviewPressed(int pressed) {
|
||||||
|
if (base::in_range(_previewPressed, 0, _previewResults.size())) {
|
||||||
|
_previewResults[_previewPressed]->stopLastRipple();
|
||||||
|
}
|
||||||
|
_previewPressed = pressed;
|
||||||
|
}
|
||||||
|
|
||||||
void InnerWidget::setSearchedPressed(int pressed) {
|
void InnerWidget::setSearchedPressed(int pressed) {
|
||||||
if (base::in_range(_searchedPressed, 0, _searchResults.size())) {
|
if (base::in_range(_searchedPressed, 0, _searchResults.size())) {
|
||||||
_searchResults[_searchedPressed]->stopLastRipple();
|
_searchResults[_searchedPressed]->stopLastRipple();
|
||||||
|
@ -2313,6 +2429,8 @@ void InnerWidget::updateSelectedRow(Key key) {
|
||||||
}
|
}
|
||||||
} else if (_peerSearchSelected >= 0) {
|
} else if (_peerSearchSelected >= 0) {
|
||||||
update(0, peerSearchOffset() + _peerSearchSelected * st::dialogsRowHeight, width(), st::dialogsRowHeight);
|
update(0, peerSearchOffset() + _peerSearchSelected * st::dialogsRowHeight, width(), st::dialogsRowHeight);
|
||||||
|
} else if (_previewSelected >= 0) {
|
||||||
|
update(0, previewOffset() + _previewSelected * _st->height, width(), _st->height);
|
||||||
} else if (_searchedSelected >= 0) {
|
} else if (_searchedSelected >= 0) {
|
||||||
update(0, searchedOffset() + _searchedSelected * _st->height, width(), _st->height);
|
update(0, searchedOffset() + _searchedSelected * _st->height, width(), _st->height);
|
||||||
}
|
}
|
||||||
|
@ -2354,9 +2472,11 @@ void InnerWidget::clearSelection() {
|
||||||
if (isSelected()) {
|
if (isSelected()) {
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
_collapsedSelected = -1;
|
_collapsedSelected = -1;
|
||||||
|
_selectedMorePosts = false;
|
||||||
_selected = nullptr;
|
_selected = nullptr;
|
||||||
_filteredSelected
|
_filteredSelected
|
||||||
= _searchedSelected
|
= _searchedSelected
|
||||||
|
= _previewSelected
|
||||||
= _peerSearchSelected
|
= _peerSearchSelected
|
||||||
= _hashtagSelected
|
= _hashtagSelected
|
||||||
= -1;
|
= -1;
|
||||||
|
@ -2449,6 +2569,11 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
} else if (_state == WidgetState::Filtered) {
|
} else if (_state == WidgetState::Filtered) {
|
||||||
if (base::in_range(_filteredSelected, 0, _filterResults.size())) {
|
if (base::in_range(_filteredSelected, 0, _filterResults.size())) {
|
||||||
return { _filterResults[_filteredSelected].key(), FullMsgId() };
|
return { _filterResults[_filteredSelected].key(), FullMsgId() };
|
||||||
|
} else if (base::in_range(_previewSelected, 0, _previewResults.size())) {
|
||||||
|
return {
|
||||||
|
_previewResults[_previewSelected]->item()->history(),
|
||||||
|
_previewResults[_previewSelected]->item()->fullId()
|
||||||
|
};
|
||||||
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
||||||
return {
|
return {
|
||||||
_searchResults[_searchedSelected]->item()->history(),
|
_searchResults[_searchedSelected]->item()->history(),
|
||||||
|
@ -2597,6 +2722,7 @@ void InnerWidget::searchRequested(bool loading) {
|
||||||
_searchLoading = loading;
|
_searchLoading = loading;
|
||||||
if (loading) {
|
if (loading) {
|
||||||
clearSearchResults(true);
|
clearSearchResults(true);
|
||||||
|
clearPreviewResults();
|
||||||
}
|
}
|
||||||
refresh(true);
|
refresh(true);
|
||||||
}
|
}
|
||||||
|
@ -2660,6 +2786,7 @@ void InnerWidget::applySearchState(SearchState state) {
|
||||||
}
|
}
|
||||||
_searchState = std::move(state);
|
_searchState = std::move(state);
|
||||||
_searchHashOrCashtag = IsHashOrCashtagSearchQuery(_searchState.query);
|
_searchHashOrCashtag = IsHashOrCashtagSearchQuery(_searchState.query);
|
||||||
|
_searchWithPostsPreview = computeSearchWithPostsPreview();
|
||||||
|
|
||||||
updateSearchIn();
|
updateSearchIn();
|
||||||
moveSearchIn();
|
moveSearchIn();
|
||||||
|
@ -2767,7 +2894,7 @@ void InnerWidget::appendToFiltered(Key key) {
|
||||||
const auto height = filteredHeight();
|
const auto height = filteredHeight();
|
||||||
_filterResults.emplace_back(i->second.get());
|
_filterResults.emplace_back(i->second.get());
|
||||||
_filterResults.back().top = height;
|
_filterResults.back().top = height;
|
||||||
trackSearchResultsHistory(key.owningHistory());
|
trackResultsHistory(key.owningHistory());
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerWidget::~InnerWidget() {
|
InnerWidget::~InnerWidget() {
|
||||||
|
@ -2780,13 +2907,16 @@ void InnerWidget::clearSearchResults(bool clearPeerSearchResults) {
|
||||||
_peerSearchResults.clear();
|
_peerSearchResults.clear();
|
||||||
}
|
}
|
||||||
_searchResults.clear();
|
_searchResults.clear();
|
||||||
_searchResultsLifetime.destroy();
|
|
||||||
_searchResultsHistories.clear();
|
|
||||||
_searchedCount = _searchedMigratedCount = 0;
|
_searchedCount = _searchedMigratedCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::trackSearchResultsHistory(not_null<History*> history) {
|
void InnerWidget::clearPreviewResults() {
|
||||||
if (!_searchResultsHistories.emplace(history).second) {
|
_previewResults.clear();
|
||||||
|
_previewCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::trackResultsHistory(not_null<History*> history) {
|
||||||
|
if (!_trackedHistories.emplace(history).second) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto channel = history->peer->asChannel();
|
const auto channel = history->peer->asChannel();
|
||||||
|
@ -2827,7 +2957,7 @@ void InnerWidget::trackSearchResultsHistory(not_null<History*> history) {
|
||||||
clearMouseSelection(true);
|
clearMouseSelection(true);
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
}, _searchResultsLifetime);
|
}, _trackedLifetime);
|
||||||
|
|
||||||
if (const auto forum = channel->forum()) {
|
if (const auto forum = channel->forum()) {
|
||||||
forum->topicDestroyed(
|
forum->topicDestroyed(
|
||||||
|
@ -2857,7 +2987,7 @@ void InnerWidget::trackSearchResultsHistory(not_null<History*> history) {
|
||||||
if (_chatPreviewRow.key.topic() == topic) {
|
if (_chatPreviewRow.key.topic() == topic) {
|
||||||
_chatPreviewRow = {};
|
_chatPreviewRow = {};
|
||||||
}
|
}
|
||||||
}, _searchResultsLifetime);
|
}, _trackedLifetime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2875,6 +3005,13 @@ Data::Thread *InnerWidget::updateFromParentDrag(QPoint globalPosition) {
|
||||||
} else if (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())) {
|
} else if (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())) {
|
||||||
return session().data().history(
|
return session().data().history(
|
||||||
_peerSearchResults[_peerSearchSelected]->peer);
|
_peerSearchResults[_peerSearchSelected]->peer);
|
||||||
|
} else if (base::in_range(_previewSelected, 0, _previewResults.size())) {
|
||||||
|
if (const auto item = _previewResults[_previewSelected]->item()) {
|
||||||
|
if (const auto topic = item->topic()) {
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
return item->history();
|
||||||
|
}
|
||||||
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
||||||
if (const auto item = _searchResults[_searchedSelected]->item()) {
|
if (const auto item = _searchResults[_searchedSelected]->item()) {
|
||||||
if (const auto topic = item->topic()) {
|
if (const auto topic = item->topic()) {
|
||||||
|
@ -3028,12 +3165,14 @@ void InnerWidget::searchReceived(
|
||||||
_searchLoading = false;
|
_searchLoading = false;
|
||||||
|
|
||||||
const auto uniquePeers = uniqueSearchResults();
|
const auto uniquePeers = uniqueSearchResults();
|
||||||
if (type == SearchRequestType::FromStart
|
const auto withPreview = _searchWithPostsPreview;
|
||||||
|| type == SearchRequestType::PeerFromStart) {
|
const auto toPreview = withPreview && type.posts;
|
||||||
|
if (type.start && !type.migrated && (!withPreview || !type.posts)) {
|
||||||
clearSearchResults(false);
|
clearSearchResults(false);
|
||||||
}
|
}
|
||||||
const auto isMigratedSearch = (type == SearchRequestType::MigratedFromStart)
|
if (!withPreview || toPreview) {
|
||||||
|| (type == SearchRequestType::MigratedFromOffset);
|
clearPreviewResults();
|
||||||
|
}
|
||||||
|
|
||||||
const auto key = (!_openedForum || _searchState.inChat.topic())
|
const auto key = (!_openedForum || _searchState.inChat.topic())
|
||||||
? _searchState.inChat
|
? _searchState.inChat
|
||||||
|
@ -3042,34 +3181,40 @@ void InnerWidget::searchReceived(
|
||||||
&& (!_searchState.inChat
|
&& (!_searchState.inChat
|
||||||
|| inject->history() == _searchState.inChat.history())) {
|
|| inject->history() == _searchState.inChat.history())) {
|
||||||
Assert(_searchResults.empty());
|
Assert(_searchResults.empty());
|
||||||
|
Assert(!toPreview);
|
||||||
const auto index = int(_searchResults.size());
|
const auto index = int(_searchResults.size());
|
||||||
_searchResults.push_back(
|
_searchResults.push_back(
|
||||||
std::make_unique<FakeRow>(
|
std::make_unique<FakeRow>(
|
||||||
key,
|
key,
|
||||||
inject,
|
inject,
|
||||||
[=] { repaintSearchResult(index); }));
|
[=] { repaintSearchResult(index); }));
|
||||||
trackSearchResultsHistory(inject->history());
|
trackResultsHistory(inject->history());
|
||||||
++fullCount;
|
++fullCount;
|
||||||
}
|
}
|
||||||
|
auto &results = toPreview ? _previewResults : _searchResults;
|
||||||
for (const auto &item : messages) {
|
for (const auto &item : messages) {
|
||||||
const auto history = item->history();
|
const auto history = item->history();
|
||||||
if (!uniquePeers || !hasHistoryInResults(history)) {
|
if (toPreview || !uniquePeers || !hasHistoryInResults(history)) {
|
||||||
const auto index = int(_searchResults.size());
|
const auto index = int(results.size());
|
||||||
_searchResults.push_back(
|
const auto repaint = toPreview
|
||||||
std::make_unique<FakeRow>(
|
? Fn<void()>([=] { repaintSearchResult(index); })
|
||||||
key,
|
: [=] { repaintPreviewResult(index); };
|
||||||
item,
|
results.push_back(
|
||||||
[=] { repaintSearchResult(index); }));
|
std::make_unique<FakeRow>(key, item, repaint));
|
||||||
trackSearchResultsHistory(history);
|
trackResultsHistory(history);
|
||||||
if (uniquePeers && !history->unreadCountKnown()) {
|
if (!toPreview && uniquePeers && !history->unreadCountKnown()) {
|
||||||
history->owner().histories().requestDialogEntry(history);
|
history->owner().histories().requestDialogEntry(history);
|
||||||
|
} else if (toPreview && results.size() >= kPreviewPostsLimit) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isMigratedSearch) {
|
if (type.migrated) {
|
||||||
_searchedMigratedCount = fullCount;
|
_searchedMigratedCount = fullCount;
|
||||||
} else {
|
} else if (!withPreview || !toPreview) {
|
||||||
_searchedCount = fullCount;
|
_searchedCount = fullCount;
|
||||||
|
} else {
|
||||||
|
_previewCount = fullCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
@ -3313,6 +3458,7 @@ void InnerWidget::clearMouseSelection(bool clearSelection) {
|
||||||
} else if (_state == WidgetState::Filtered) {
|
} else if (_state == WidgetState::Filtered) {
|
||||||
_filteredSelected
|
_filteredSelected
|
||||||
= _peerSearchSelected
|
= _peerSearchSelected
|
||||||
|
= _previewSelected
|
||||||
= _searchedSelected
|
= _searchedSelected
|
||||||
= _hashtagSelected = -1;
|
= _hashtagSelected = -1;
|
||||||
}
|
}
|
||||||
|
@ -3416,6 +3562,19 @@ void InnerWidget::repaintSearchResult(int index) {
|
||||||
_st->height);
|
_st->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InnerWidget::repaintPreviewResult(int index) {
|
||||||
|
rtlupdate(
|
||||||
|
0,
|
||||||
|
previewOffset() + index * _st->height,
|
||||||
|
width(),
|
||||||
|
_st->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InnerWidget::computeSearchWithPostsPreview() const {
|
||||||
|
return (_searchHashOrCashtag != HashOrCashtag::None)
|
||||||
|
&& (_searchState.tab == ChatSearchTab::MyMessages);
|
||||||
|
}
|
||||||
|
|
||||||
void InnerWidget::clearFilter() {
|
void InnerWidget::clearFilter() {
|
||||||
if (_state == WidgetState::Filtered || _searchState.inChat) {
|
if (_state == WidgetState::Filtered || _searchState.inChat) {
|
||||||
if (_searchState.inChat) {
|
if (_searchState.inChat) {
|
||||||
|
@ -3428,6 +3587,9 @@ void InnerWidget::clearFilter() {
|
||||||
_filterResultsGlobal.clear();
|
_filterResultsGlobal.clear();
|
||||||
_peerSearchResults.clear();
|
_peerSearchResults.clear();
|
||||||
_searchResults.clear();
|
_searchResults.clear();
|
||||||
|
_previewResults.clear();
|
||||||
|
_trackedHistories.clear();
|
||||||
|
_trackedLifetime.destroy();
|
||||||
_filter = QString();
|
_filter = QString();
|
||||||
refresh(true);
|
refresh(true);
|
||||||
}
|
}
|
||||||
|
@ -3474,15 +3636,22 @@ void InnerWidget::selectSkip(int32 direction) {
|
||||||
}
|
}
|
||||||
scrollToDefaultSelected();
|
scrollToDefaultSelected();
|
||||||
} else if (_state == WidgetState::Filtered) {
|
} else if (_state == WidgetState::Filtered) {
|
||||||
if (_hashtagResults.empty() && _filterResults.empty() && _peerSearchResults.empty() && _searchResults.empty()) {
|
if (_hashtagResults.empty()
|
||||||
|
&& _filterResults.empty()
|
||||||
|
&& _peerSearchResults.empty()
|
||||||
|
&& _previewResults.empty()
|
||||||
|
&& _searchResults.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((_hashtagSelected < 0 || _hashtagSelected >= _hashtagResults.size()) &&
|
if ((_hashtagSelected < 0 || _hashtagSelected >= _hashtagResults.size())
|
||||||
(_filteredSelected < 0 || _filteredSelected >= _filterResults.size()) &&
|
&& (_filteredSelected < 0 || _filteredSelected >= _filterResults.size())
|
||||||
(_peerSearchSelected < 0 || _peerSearchSelected >= _peerSearchResults.size()) &&
|
&& (_peerSearchSelected < 0 || _peerSearchSelected >= _peerSearchResults.size())
|
||||||
(_searchedSelected < 0 || _searchedSelected >= _searchResults.size())) {
|
&& (_previewSelected < 0 || _previewSelected >= _previewResults.size())
|
||||||
if (_hashtagResults.empty() && _filterResults.empty() && _peerSearchResults.empty()) {
|
&& (_searchedSelected < 0 || _searchedSelected >= _searchResults.size())) {
|
||||||
|
if (_hashtagResults.empty() && _filterResults.empty() && _peerSearchResults.empty() && _previewResults.empty()) {
|
||||||
_searchedSelected = 0;
|
_searchedSelected = 0;
|
||||||
|
} else if (_hashtagResults.empty() && _filterResults.empty() && _peerSearchResults.empty()) {
|
||||||
|
_previewSelected = 0;
|
||||||
} else if (_hashtagResults.empty() && _filterResults.empty()) {
|
} else if (_hashtagResults.empty() && _filterResults.empty()) {
|
||||||
_peerSearchSelected = 0;
|
_peerSearchSelected = 0;
|
||||||
} else if (_hashtagResults.empty()) {
|
} else if (_hashtagResults.empty()) {
|
||||||
|
@ -3493,30 +3662,36 @@ void InnerWidget::selectSkip(int32 direction) {
|
||||||
} else {
|
} else {
|
||||||
int32 cur = base::in_range(_hashtagSelected, 0, _hashtagResults.size())
|
int32 cur = base::in_range(_hashtagSelected, 0, _hashtagResults.size())
|
||||||
? _hashtagSelected
|
? _hashtagSelected
|
||||||
: (base::in_range(_filteredSelected, 0, _filterResults.size())
|
: base::in_range(_filteredSelected, 0, _filterResults.size())
|
||||||
? (_hashtagResults.size() + _filteredSelected)
|
? (_hashtagResults.size() + _filteredSelected)
|
||||||
: (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())
|
: base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())
|
||||||
? (_peerSearchSelected + _filterResults.size() + _hashtagResults.size())
|
? (_peerSearchSelected + _filterResults.size() + _hashtagResults.size())
|
||||||
: (_searchedSelected + _peerSearchResults.size() + _filterResults.size() + _hashtagResults.size())));
|
: base::in_range(_previewSelected, 0, _previewResults.size())
|
||||||
|
? (_previewSelected + _peerSearchResults.size() + _filterResults.size() + _hashtagResults.size())
|
||||||
|
: (_searchedSelected + _previewResults.size() + _peerSearchResults.size() + _filterResults.size() + _hashtagResults.size());
|
||||||
cur = std::clamp(
|
cur = std::clamp(
|
||||||
cur + direction,
|
cur + direction,
|
||||||
0,
|
0,
|
||||||
static_cast<int>(_hashtagResults.size()
|
static_cast<int>(_hashtagResults.size()
|
||||||
+ _filterResults.size()
|
+ _filterResults.size()
|
||||||
+ _peerSearchResults.size()
|
+ _peerSearchResults.size()
|
||||||
|
+ _previewResults.size()
|
||||||
+ _searchResults.size()) - 1);
|
+ _searchResults.size()) - 1);
|
||||||
if (cur < _hashtagResults.size()) {
|
if (cur < _hashtagResults.size()) {
|
||||||
_hashtagSelected = cur;
|
_hashtagSelected = cur;
|
||||||
_filteredSelected = _peerSearchSelected = _searchedSelected = -1;
|
_filteredSelected = _peerSearchSelected = _previewSelected = _searchedSelected = -1;
|
||||||
} else if (cur < _hashtagResults.size() + _filterResults.size()) {
|
} else if (cur < _hashtagResults.size() + _filterResults.size()) {
|
||||||
_filteredSelected = cur - _hashtagResults.size();
|
_filteredSelected = cur - _hashtagResults.size();
|
||||||
_hashtagSelected = _peerSearchSelected = _searchedSelected = -1;
|
_hashtagSelected = _peerSearchSelected = _previewSelected = _searchedSelected = -1;
|
||||||
} else if (cur < _hashtagResults.size() + _filterResults.size() + _peerSearchResults.size()) {
|
} else if (cur < _hashtagResults.size() + _filterResults.size() + _peerSearchResults.size()) {
|
||||||
_peerSearchSelected = cur - _hashtagResults.size() - _filterResults.size();
|
_peerSearchSelected = cur - _hashtagResults.size() - _filterResults.size();
|
||||||
_hashtagSelected = _filteredSelected = _searchedSelected = -1;
|
_hashtagSelected = _filteredSelected = _previewSelected = _searchedSelected = -1;
|
||||||
|
} else if (cur < _hashtagResults.size() + _filterResults.size() + _peerSearchResults.size() + _previewResults.size()) {
|
||||||
|
_previewSelected = cur - _hashtagResults.size() - _filterResults.size() - _peerSearchResults.size();
|
||||||
|
_hashtagSelected = _filteredSelected = _peerSearchSelected = _searchedSelected = -1;
|
||||||
} else {
|
} else {
|
||||||
_hashtagSelected = _filteredSelected = _peerSearchSelected = -1;
|
_searchedSelected = cur - _hashtagResults.size() - _filterResults.size() - _peerSearchResults.size() - _previewResults.size();
|
||||||
_searchedSelected = cur - _hashtagResults.size() - _filterResults.size() - _peerSearchResults.size();
|
_hashtagSelected = _filteredSelected = _peerSearchSelected = _previewSelected = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (base::in_range(_hashtagSelected, 0, _hashtagResults.size())) {
|
if (base::in_range(_hashtagSelected, 0, _hashtagResults.size())) {
|
||||||
|
@ -3533,6 +3708,13 @@ void InnerWidget::selectSkip(int32 direction) {
|
||||||
const auto height = st::dialogsRowHeight
|
const auto height = st::dialogsRowHeight
|
||||||
+ (_peerSearchSelected ? 0 : st::searchedBarHeight);
|
+ (_peerSearchSelected ? 0 : st::searchedBarHeight);
|
||||||
scrollToItem(from, height);
|
scrollToItem(from, height);
|
||||||
|
} else if (base::in_range(_previewSelected, 0, _previewResults.size())) {
|
||||||
|
const auto from = previewOffset()
|
||||||
|
+ _previewSelected * _st->height
|
||||||
|
+ (_previewSelected ? 0 : -st::searchedBarHeight);
|
||||||
|
const auto height = _st->height
|
||||||
|
+ (_previewSelected ? 0 : st::searchedBarHeight);
|
||||||
|
scrollToItem(from, height);
|
||||||
} else {
|
} else {
|
||||||
const auto from = searchedOffset()
|
const auto from = searchedOffset()
|
||||||
+ _searchedSelected * _st->height
|
+ _searchedSelected * _st->height
|
||||||
|
@ -3551,7 +3733,14 @@ void InnerWidget::scrollToEntry(const RowDescriptor &entry) {
|
||||||
scrollToItem(dialogsOffset() + row->top(), row->height());
|
scrollToItem(dialogsOffset() + row->top(), row->height());
|
||||||
}
|
}
|
||||||
} else if (_state == WidgetState::Filtered) {
|
} else if (_state == WidgetState::Filtered) {
|
||||||
for (int32 i = 0, c = _searchResults.size(); i < c; ++i) {
|
for (auto i = 0, c = int(_previewResults.size()); i != c; ++i) {
|
||||||
|
if (isSearchResultActive(_previewResults[i].get(), entry)) {
|
||||||
|
const auto from = previewOffset() + i * _st->height;
|
||||||
|
scrollToItem(from, _st->height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto i = 0, c = int(_searchResults.size()); i != c; ++i) {
|
||||||
if (isSearchResultActive(_searchResults[i].get(), entry)) {
|
if (isSearchResultActive(_searchResults[i].get(), entry)) {
|
||||||
const auto from = searchedOffset() + i * _st->height;
|
const auto from = searchedOffset() + i * _st->height;
|
||||||
scrollToItem(from, _st->height);
|
scrollToItem(from, _st->height);
|
||||||
|
@ -3646,33 +3835,45 @@ void InnerWidget::preloadRowsData() {
|
||||||
}
|
}
|
||||||
yTo -= otherStart;
|
yTo -= otherStart;
|
||||||
} else if (_state == WidgetState::Filtered) {
|
} else if (_state == WidgetState::Filtered) {
|
||||||
int32 from = (yFrom - filteredOffset()) / _st->height;
|
auto from = (yFrom - filteredOffset()) / _st->height;
|
||||||
if (from < 0) from = 0;
|
if (from < 0) from = 0;
|
||||||
if (from < _filterResults.size()) {
|
if (from < _filterResults.size()) {
|
||||||
int32 to = (yTo / _st->height) + 1;
|
const auto to = std::min(
|
||||||
if (to > _filterResults.size()) to = _filterResults.size();
|
((yTo - filteredOffset()) / _st->height) + 1,
|
||||||
|
int(_filterResults.size()));
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
_filterResults[from].key().entry()->chatListPreloadData();
|
_filterResults[from].key().entry()->chatListPreloadData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from = (yFrom > filteredOffset() + st::searchedBarHeight ? ((yFrom - filteredOffset() - st::searchedBarHeight) / st::dialogsRowHeight) : 0) - _filterResults.size();
|
from = (yFrom - peerSearchOffset()) / st::dialogsRowHeight;
|
||||||
if (from < 0) from = 0;
|
if (from < 0) from = 0;
|
||||||
if (from < _peerSearchResults.size()) {
|
if (from < _peerSearchResults.size()) {
|
||||||
int32 to = (yTo > filteredOffset() + st::searchedBarHeight ? ((yTo - filteredOffset() - st::searchedBarHeight) / st::dialogsRowHeight) : 0) - _filterResults.size() + 1;
|
const auto to = std::min(
|
||||||
if (to > _peerSearchResults.size()) to = _peerSearchResults.size();
|
((yTo - peerSearchOffset()) / st::dialogsRowHeight) + 1,
|
||||||
|
int(_peerSearchResults.size()));
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
_peerSearchResults[from]->peer->loadUserpic();
|
_peerSearchResults[from]->peer->loadUserpic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
from = (yFrom > filteredOffset() + ((_peerSearchResults.empty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - filteredOffset() - (_peerSearchResults.empty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / st::dialogsRowHeight) : 0) - _filterResults.size() - _peerSearchResults.size();
|
|
||||||
|
from = (yFrom - previewOffset()) / _st->height;
|
||||||
|
if (from < 0) from = 0;
|
||||||
|
if (from < _previewResults.size()) {
|
||||||
|
const auto to = std::min(
|
||||||
|
((yTo - previewOffset()) / _st->height) + 1,
|
||||||
|
int(_previewResults.size()));
|
||||||
|
for (; from < to; ++from) {
|
||||||
|
_previewResults[from]->item()->history()->peer->loadUserpic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from = (yFrom - searchedOffset()) / _st->height;
|
||||||
if (from < 0) from = 0;
|
if (from < 0) from = 0;
|
||||||
if (from < _searchResults.size()) {
|
if (from < _searchResults.size()) {
|
||||||
int32 to = (yTo > filteredOffset() + (_peerSearchResults.empty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - filteredOffset() - (_peerSearchResults.empty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / st::dialogsRowHeight) : 0) - _filterResults.size() - _peerSearchResults.size() + 1;
|
const auto to = std::min(
|
||||||
if (to > _searchResults.size()) to = _searchResults.size();
|
((yTo - searchedOffset()) / _st->height) + 1,
|
||||||
|
int(_searchResults.size()));
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
_searchResults[from]->item()->history()->peer->loadUserpic();
|
_searchResults[from]->item()->history()->peer->loadUserpic();
|
||||||
}
|
}
|
||||||
|
@ -3803,6 +4004,14 @@ ChosenRow InnerWidget::computeChosenRow() const {
|
||||||
.key = session().data().history(peer),
|
.key = session().data().history(peer),
|
||||||
.message = Data::UnreadMessagePosition
|
.message = Data::UnreadMessagePosition
|
||||||
};
|
};
|
||||||
|
} else if (base::in_range(_previewSelected, 0, _previewResults.size())) {
|
||||||
|
const auto result = _previewResults[_previewSelected].get();
|
||||||
|
const auto topic = result->topic();
|
||||||
|
const auto item = result->item();
|
||||||
|
return {
|
||||||
|
.key = (topic ? (Entry*)topic : (Entry*)item->history()),
|
||||||
|
.message = item->position()
|
||||||
|
};
|
||||||
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
||||||
const auto result = _searchResults[_searchedSelected].get();
|
const auto result = _searchResults[_searchedSelected].get();
|
||||||
const auto topic = result->topic();
|
const auto topic = result->topic();
|
||||||
|
@ -3832,6 +4041,11 @@ bool InnerWidget::chooseRow(
|
||||||
MsgId pressedTopicRootId) {
|
MsgId pressedTopicRootId) {
|
||||||
if (chooseHashtag()) {
|
if (chooseHashtag()) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (_selectedMorePosts) {
|
||||||
|
if (_searchHashOrCashtag != HashOrCashtag::None) {
|
||||||
|
_changeSearchTabRequests.fire(ChatSearchTab::PublicPosts);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
const auto modifyChosenRow = [&](
|
const auto modifyChosenRow = [&](
|
||||||
ChosenRow row,
|
ChosenRow row,
|
||||||
|
|
|
@ -72,13 +72,18 @@ struct ChosenRow {
|
||||||
bool newWindow : 1 = false;
|
bool newWindow : 1 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SearchRequestType : uchar {
|
struct SearchRequestType {
|
||||||
FromStart,
|
bool migrated : 1 = false;
|
||||||
FromOffset,
|
bool posts : 1 = false;
|
||||||
PeerFromStart,
|
bool start : 1 = false;
|
||||||
PeerFromOffset,
|
bool peer : 1 = false;
|
||||||
MigratedFromStart,
|
|
||||||
MigratedFromOffset,
|
friend inline constexpr auto operator<=>(
|
||||||
|
SearchRequestType a,
|
||||||
|
SearchRequestType b) = default;
|
||||||
|
friend inline constexpr bool operator==(
|
||||||
|
SearchRequestType a,
|
||||||
|
SearchRequestType b) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SearchRequestDelay : uchar {
|
enum class SearchRequestDelay : uchar {
|
||||||
|
@ -283,6 +288,7 @@ private:
|
||||||
void setHashtagPressed(int pressed);
|
void setHashtagPressed(int pressed);
|
||||||
void setFilteredPressed(int pressed, bool pressedTopicJump);
|
void setFilteredPressed(int pressed, bool pressedTopicJump);
|
||||||
void setPeerSearchPressed(int pressed);
|
void setPeerSearchPressed(int pressed);
|
||||||
|
void setPreviewPressed(int pressed);
|
||||||
void setSearchedPressed(int pressed);
|
void setSearchedPressed(int pressed);
|
||||||
bool isPressed() const {
|
bool isPressed() const {
|
||||||
return (_collapsedPressed >= 0)
|
return (_collapsedPressed >= 0)
|
||||||
|
@ -290,7 +296,9 @@ private:
|
||||||
|| (_hashtagPressed >= 0)
|
|| (_hashtagPressed >= 0)
|
||||||
|| (_filteredPressed >= 0)
|
|| (_filteredPressed >= 0)
|
||||||
|| (_peerSearchPressed >= 0)
|
|| (_peerSearchPressed >= 0)
|
||||||
|| (_searchedPressed >= 0);
|
|| (_previewPressed >= 0)
|
||||||
|
|| (_searchedPressed >= 0)
|
||||||
|
|| _pressedMorePosts;
|
||||||
}
|
}
|
||||||
bool isSelected() const {
|
bool isSelected() const {
|
||||||
return (_collapsedSelected >= 0)
|
return (_collapsedSelected >= 0)
|
||||||
|
@ -298,7 +306,9 @@ private:
|
||||||
|| (_hashtagSelected >= 0)
|
|| (_hashtagSelected >= 0)
|
||||||
|| (_filteredSelected >= 0)
|
|| (_filteredSelected >= 0)
|
||||||
|| (_peerSearchSelected >= 0)
|
|| (_peerSearchSelected >= 0)
|
||||||
|| (_searchedSelected >= 0);
|
|| (_previewSelected >= 0)
|
||||||
|
|| (_searchedSelected >= 0)
|
||||||
|
|| _selectedMorePosts;
|
||||||
}
|
}
|
||||||
bool uniqueSearchResults() const;
|
bool uniqueSearchResults() const;
|
||||||
bool hasHistoryInResults(not_null<History*> history) const;
|
bool hasHistoryInResults(not_null<History*> history) const;
|
||||||
|
@ -352,6 +362,7 @@ private:
|
||||||
[[nodiscard]] int filteredHeight(int till = -1) const;
|
[[nodiscard]] int filteredHeight(int till = -1) const;
|
||||||
[[nodiscard]] int peerSearchOffset() const;
|
[[nodiscard]] int peerSearchOffset() const;
|
||||||
[[nodiscard]] int searchInChatOffset() const;
|
[[nodiscard]] int searchInChatOffset() const;
|
||||||
|
[[nodiscard]] int previewOffset() const;
|
||||||
[[nodiscard]] int searchedOffset() const;
|
[[nodiscard]] int searchedOffset() const;
|
||||||
[[nodiscard]] int searchInChatSkip() const;
|
[[nodiscard]] int searchInChatSkip() const;
|
||||||
[[nodiscard]] int hashtagsOffset() const;
|
[[nodiscard]] int hashtagsOffset() const;
|
||||||
|
@ -403,14 +414,18 @@ private:
|
||||||
// const Ui::Text::String &text) const;
|
// const Ui::Text::String &text) const;
|
||||||
void updateSearchIn();
|
void updateSearchIn();
|
||||||
void repaintSearchResult(int index);
|
void repaintSearchResult(int index);
|
||||||
|
void repaintPreviewResult(int index);
|
||||||
|
|
||||||
|
[[nodiscard]] bool computeSearchWithPostsPreview() const;
|
||||||
|
|
||||||
Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row);
|
Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row);
|
||||||
Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);
|
Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);
|
||||||
|
|
||||||
Row *shownRowByKey(Key key);
|
Row *shownRowByKey(Key key);
|
||||||
void clearSearchResults(bool clearPeerSearchResults = true);
|
void clearSearchResults(bool clearPeerSearchResults = true);
|
||||||
|
void clearPreviewResults();
|
||||||
void updateSelectedRow(Key key = Key());
|
void updateSelectedRow(Key key = Key());
|
||||||
void trackSearchResultsHistory(not_null<History*> history);
|
void trackResultsHistory(not_null<History*> history);
|
||||||
|
|
||||||
[[nodiscard]] QBrush currentBg() const;
|
[[nodiscard]] QBrush currentBg() const;
|
||||||
[[nodiscard]] RowDescriptor computeChatPreviewRow() const;
|
[[nodiscard]] RowDescriptor computeChatPreviewRow() const;
|
||||||
|
@ -449,6 +464,8 @@ private:
|
||||||
std::vector<std::unique_ptr<CollapsedRow>> _collapsedRows;
|
std::vector<std::unique_ptr<CollapsedRow>> _collapsedRows;
|
||||||
not_null<const style::DialogRow*> _st;
|
not_null<const style::DialogRow*> _st;
|
||||||
mutable std::unique_ptr<Ui::TopicJumpCache> _topicJumpCache;
|
mutable std::unique_ptr<Ui::TopicJumpCache> _topicJumpCache;
|
||||||
|
bool _selectedMorePosts = false;
|
||||||
|
bool _pressedMorePosts = false;
|
||||||
int _collapsedSelected = -1;
|
int _collapsedSelected = -1;
|
||||||
int _collapsedPressed = -1;
|
int _collapsedPressed = -1;
|
||||||
bool _skipTopDialog = false;
|
bool _skipTopDialog = false;
|
||||||
|
@ -487,14 +504,21 @@ private:
|
||||||
|
|
||||||
EmptyState _emptyState = EmptyState::None;
|
EmptyState _emptyState = EmptyState::None;
|
||||||
|
|
||||||
|
base::flat_set<not_null<History*>> _trackedHistories;
|
||||||
|
rpl::lifetime _trackedLifetime;
|
||||||
|
|
||||||
QString _peerSearchQuery;
|
QString _peerSearchQuery;
|
||||||
std::vector<std::unique_ptr<PeerSearchResult>> _peerSearchResults;
|
std::vector<std::unique_ptr<PeerSearchResult>> _peerSearchResults;
|
||||||
int _peerSearchSelected = -1;
|
int _peerSearchSelected = -1;
|
||||||
int _peerSearchPressed = -1;
|
int _peerSearchPressed = -1;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<FakeRow>> _previewResults;
|
||||||
|
int _previewCount = 0;
|
||||||
|
int _previewSelected = -1;
|
||||||
|
int _previewPressed = -1;
|
||||||
|
int _morePostsWidth = 0;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<FakeRow>> _searchResults;
|
std::vector<std::unique_ptr<FakeRow>> _searchResults;
|
||||||
base::flat_set<not_null<History*>> _searchResultsHistories;
|
|
||||||
rpl::lifetime _searchResultsLifetime;
|
|
||||||
int _searchedCount = 0;
|
int _searchedCount = 0;
|
||||||
int _searchedMigratedCount = 0;
|
int _searchedMigratedCount = 0;
|
||||||
int _searchedSelected = -1;
|
int _searchedSelected = -1;
|
||||||
|
@ -516,6 +540,7 @@ private:
|
||||||
|
|
||||||
SearchState _searchState;
|
SearchState _searchState;
|
||||||
HashOrCashtag _searchHashOrCashtag = {};
|
HashOrCashtag _searchHashOrCashtag = {};
|
||||||
|
bool _searchWithPostsPreview = false;
|
||||||
History *_searchInMigrated = nullptr;
|
History *_searchInMigrated = nullptr;
|
||||||
PeerData *_searchFromShown = nullptr;
|
PeerData *_searchFromShown = nullptr;
|
||||||
Ui::Text::String _searchFromUserText;
|
Ui::Text::String _searchFromUserText;
|
||||||
|
|
|
@ -234,7 +234,9 @@ void Widget::BottomButton::radialAnimationCallback() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::BottomButton::onStateChanged(State was, StateChangeSource source) {
|
void Widget::BottomButton::onStateChanged(
|
||||||
|
State was,
|
||||||
|
StateChangeSource source) {
|
||||||
RippleButton::onStateChanged(was, source);
|
RippleButton::onStateChanged(was, source);
|
||||||
if ((was & StateFlag::Disabled) != (state() & StateFlag::Disabled)) {
|
if ((was & StateFlag::Disabled) != (state() & StateFlag::Disabled)) {
|
||||||
_loading = isDisabled()
|
_loading = isDisabled()
|
||||||
|
@ -532,11 +534,11 @@ Widget::Widget(
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
_cancelSearch->setClickedCallback([this] {
|
_cancelSearch->setClickedCallback([=] {
|
||||||
cancelSearch({ .jumpBackToSearchedChat = true });
|
cancelSearch({ .jumpBackToSearchedChat = true });
|
||||||
});
|
});
|
||||||
_jumpToDate->entity()->setClickedCallback([this] { showCalendar(); });
|
_jumpToDate->entity()->setClickedCallback([=] { showCalendar(); });
|
||||||
_chooseFromUser->entity()->setClickedCallback([this] { showSearchFrom(); });
|
_chooseFromUser->entity()->setClickedCallback([=] { showSearchFrom(); });
|
||||||
rpl::single(rpl::empty) | rpl::then(
|
rpl::single(rpl::empty) | rpl::then(
|
||||||
session().domain().local().localPasscodeChanged()
|
session().domain().local().localPasscodeChanged()
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
|
@ -576,11 +578,10 @@ Widget::Widget(
|
||||||
});
|
});
|
||||||
_inner->setLoadMoreCallback([=] {
|
_inner->setLoadMoreCallback([=] {
|
||||||
const auto state = _inner->state();
|
const auto state = _inner->state();
|
||||||
|
const auto process = currentSearchProcess();
|
||||||
if (state == WidgetState::Filtered
|
if (state == WidgetState::Filtered
|
||||||
&& (!_searchFull
|
&& (!process->full
|
||||||
|| (_searchInMigrated
|
|| (_searchInMigrated && !_migratedProcess.full))) {
|
||||||
&& _searchFull
|
|
||||||
&& !_searchFullMigrated))) {
|
|
||||||
searchMore();
|
searchMore();
|
||||||
} else if (_openedForum && state == WidgetState::Default) {
|
} else if (_openedForum && state == WidgetState::Default) {
|
||||||
_openedForum->requestTopics();
|
_openedForum->requestTopics();
|
||||||
|
@ -1199,11 +1200,12 @@ void Widget::fullSearchRefreshOn(rpl::producer<> events) {
|
||||||
return !_searchQuery.isEmpty();
|
return !_searchQuery.isEmpty();
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
_searchTimer.cancel();
|
_searchTimer.cancel();
|
||||||
_searchCache.clear();
|
_searchProcess.cache.clear();
|
||||||
_singleMessageSearch.clear();
|
const auto queries = base::take(_searchProcess.queries);
|
||||||
for (const auto &[requestId, query] : base::take(_searchQueries)) {
|
for (const auto &[requestId, query] : queries) {
|
||||||
session().api().request(requestId).cancel();
|
session().api().request(requestId).cancel();
|
||||||
}
|
}
|
||||||
|
_singleMessageSearch.clear();
|
||||||
_searchQuery = QString();
|
_searchQuery = QString();
|
||||||
_scroll->scrollToY(0);
|
_scroll->scrollToY(0);
|
||||||
cancelSearchRequest();
|
cancelSearchRequest();
|
||||||
|
@ -1529,6 +1531,7 @@ void Widget::changeOpenedForum(Data::Forum *forum, anim::type animated) {
|
||||||
_searchState.tab = forum
|
_searchState.tab = forum
|
||||||
? ChatSearchTab::ThisPeer
|
? ChatSearchTab::ThisPeer
|
||||||
: ChatSearchTab::MyMessages;
|
: ChatSearchTab::MyMessages;
|
||||||
|
_searchWithPostsPreview = computeSearchWithPostsPreview();
|
||||||
_api.request(base::take(_topicSearchRequest)).cancel();
|
_api.request(base::take(_topicSearchRequest)).cancel();
|
||||||
_inner->changeOpenedForum(forum);
|
_inner->changeOpenedForum(forum);
|
||||||
storiesToggleExplicitExpand(false);
|
storiesToggleExplicitExpand(false);
|
||||||
|
@ -2110,12 +2113,18 @@ bool Widget::search(bool inCache, SearchRequestDelay delay) {
|
||||||
const auto fromPeer = searchFromPeer();
|
const auto fromPeer = searchFromPeer();
|
||||||
const auto &inTags = searchInTags();
|
const auto &inTags = searchInTags();
|
||||||
const auto tab = _searchState.tab;
|
const auto tab = _searchState.tab;
|
||||||
const auto fromStartType = inPeer
|
const auto fromStartType = SearchRequestType{
|
||||||
? SearchRequestType::PeerFromStart
|
.start = true,
|
||||||
: SearchRequestType::FromStart;
|
.peer = (inPeer != nullptr),
|
||||||
|
};
|
||||||
if (trimmed.isEmpty() && !fromPeer && inTags.empty()) {
|
if (trimmed.isEmpty() && !fromPeer && inTags.empty()) {
|
||||||
cancelSearchRequest();
|
cancelSearchRequest();
|
||||||
searchApplyEmpty(fromStartType, 0);
|
searchApplyEmpty(fromStartType, currentSearchProcess());
|
||||||
|
if (_searchWithPostsPreview) {
|
||||||
|
searchApplyEmpty(
|
||||||
|
{ .posts = true, .start = true },
|
||||||
|
&_postsProcess);
|
||||||
|
}
|
||||||
_api.request(base::take(_peerSearchRequest)).cancel();
|
_api.request(base::take(_peerSearchRequest)).cancel();
|
||||||
_peerSearchQuery = QString();
|
_peerSearchQuery = QString();
|
||||||
peerSearchApplyEmpty(0);
|
peerSearchApplyEmpty(0);
|
||||||
|
@ -2128,28 +2137,32 @@ bool Widget::search(bool inCache, SearchRequestDelay delay) {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto i = _searchCache.find(query);
|
const auto process = currentSearchProcess();
|
||||||
if (i != _searchCache.end()) {
|
const auto i = process->cache.find(query);
|
||||||
|
if (i != process->cache.end()) {
|
||||||
_searchQuery = query;
|
_searchQuery = query;
|
||||||
_searchQueryFrom = fromPeer;
|
_searchQueryFrom = fromPeer;
|
||||||
_searchQueryTags = inTags;
|
_searchQueryTags = inTags;
|
||||||
_searchQueryTab = tab;
|
_searchQueryTab = tab;
|
||||||
_searchNextRate = 0;
|
process->nextRate = 0;
|
||||||
_searchFull = _searchFullMigrated = false;
|
process->full = false;
|
||||||
|
_migratedProcess.full = false;
|
||||||
cancelSearchRequest();
|
cancelSearchRequest();
|
||||||
searchReceived(fromStartType, i->second, 0);
|
searchReceived(fromStartType, i->second, process, true);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
} else if (_searchQuery != query
|
} else if (_searchQuery != query
|
||||||
|| _searchQueryFrom != fromPeer
|
|| _searchQueryFrom != fromPeer
|
||||||
|| _searchQueryTags != inTags
|
|| _searchQueryTags != inTags
|
||||||
|| _searchQueryTab != tab) {
|
|| _searchQueryTab != tab) {
|
||||||
|
const auto process = currentSearchProcess();
|
||||||
_searchQuery = query;
|
_searchQuery = query;
|
||||||
_searchQueryFrom = fromPeer;
|
_searchQueryFrom = fromPeer;
|
||||||
_searchQueryTags = inTags;
|
_searchQueryTags = inTags;
|
||||||
_searchQueryTab = tab;
|
_searchQueryTab = tab;
|
||||||
_searchNextRate = 0;
|
process->nextRate = 0;
|
||||||
_searchFull = _searchFullMigrated = false;
|
process->full = false;
|
||||||
|
_migratedProcess.full = false;
|
||||||
cancelSearchRequest();
|
cancelSearchRequest();
|
||||||
if (inPeer) {
|
if (inPeer) {
|
||||||
const auto topic = searchInTopic();
|
const auto topic = searchInTopic();
|
||||||
|
@ -2163,83 +2176,55 @@ bool Widget::search(bool inCache, SearchRequestDelay delay) {
|
||||||
const auto savedPeer = sublist
|
const auto savedPeer = sublist
|
||||||
? sublist->peer().get()
|
? sublist->peer().get()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
_searchInHistoryRequest = histories.sendRequest(history, type, [=](Fn<void()> finish) {
|
_historiesRequest = histories.sendRequest(history, type, [=](
|
||||||
const auto type = SearchRequestType::PeerFromStart;
|
Fn<void()> finish) {
|
||||||
|
const auto type = SearchRequestType{
|
||||||
|
.start = true,
|
||||||
|
.peer = true,
|
||||||
|
};
|
||||||
using Flag = MTPmessages_Search::Flag;
|
using Flag = MTPmessages_Search::Flag;
|
||||||
_searchRequest = session().api().request(MTPmessages_Search(
|
process->requestId = session().api().request(
|
||||||
MTP_flags((topic ? Flag::f_top_msg_id : Flag())
|
MTPmessages_Search(
|
||||||
| (fromPeer ? Flag::f_from_id : Flag())
|
MTP_flags((topic ? Flag::f_top_msg_id : Flag())
|
||||||
| (savedPeer ? Flag::f_saved_peer_id : Flag())
|
| (fromPeer ? Flag::f_from_id : Flag())
|
||||||
| (_searchQueryTags.empty()
|
| (savedPeer ? Flag::f_saved_peer_id : Flag())
|
||||||
? Flag()
|
| (_searchQueryTags.empty()
|
||||||
: Flag::f_saved_reaction)),
|
? Flag()
|
||||||
inPeer->input,
|
: Flag::f_saved_reaction)),
|
||||||
MTP_string(_searchQuery),
|
inPeer->input,
|
||||||
(fromPeer ? fromPeer->input : MTP_inputPeerEmpty()),
|
MTP_string(_searchQuery),
|
||||||
(savedPeer ? savedPeer->input : MTP_inputPeerEmpty()),
|
(fromPeer ? fromPeer->input : MTP_inputPeerEmpty()),
|
||||||
MTP_vector_from_range(
|
(savedPeer ? savedPeer->input : MTP_inputPeerEmpty()),
|
||||||
_searchQueryTags | ranges::views::transform(
|
MTP_vector_from_range(
|
||||||
Data::ReactionToMTP
|
_searchQueryTags | ranges::views::transform(
|
||||||
)),
|
Data::ReactionToMTP
|
||||||
MTP_int(topic ? topic->rootId() : 0),
|
)),
|
||||||
MTP_inputMessagesFilterEmpty(),
|
MTP_int(topic ? topic->rootId() : 0),
|
||||||
MTP_int(0), // min_date
|
MTP_inputMessagesFilterEmpty(),
|
||||||
MTP_int(0), // max_date
|
MTP_int(0), // min_date
|
||||||
MTP_int(0), // offset_id
|
MTP_int(0), // max_date
|
||||||
MTP_int(0), // add_offset
|
MTP_int(0), // offset_id
|
||||||
MTP_int(kSearchPerPage),
|
MTP_int(0), // add_offset
|
||||||
MTP_int(0), // max_id
|
MTP_int(kSearchPerPage),
|
||||||
MTP_int(0), // min_id
|
MTP_int(0), // max_id
|
||||||
MTP_long(0) // hash
|
MTP_int(0), // min_id
|
||||||
)).done([=](const MTPmessages_Messages &result) {
|
MTP_long(0)) // hash
|
||||||
_searchInHistoryRequest = 0;
|
).done([=](const MTPmessages_Messages &result) {
|
||||||
searchReceived(type, result, _searchRequest);
|
_historiesRequest = 0;
|
||||||
|
searchReceived(type, result, process);
|
||||||
finish();
|
finish();
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_searchInHistoryRequest = 0;
|
_historiesRequest = 0;
|
||||||
searchFailed(type, error, _searchRequest);
|
searchFailed(type, error, process);
|
||||||
finish();
|
finish();
|
||||||
}).send();
|
}).send();
|
||||||
_searchQueries.emplace(_searchRequest, _searchQuery);
|
process->queries.emplace(process->requestId, _searchQuery);
|
||||||
return _searchRequest;
|
return process->requestId;
|
||||||
});
|
});
|
||||||
} else if (_searchState.tab == ChatSearchTab::PublicPosts) {
|
} else if (_searchState.tab == ChatSearchTab::PublicPosts) {
|
||||||
const auto type = SearchRequestType::FromStart;
|
requestPublicPosts(true);
|
||||||
_searchRequest = session().api().request(MTPchannels_SearchPosts(
|
|
||||||
MTP_string(_searchState.query.trimmed().mid(1)),
|
|
||||||
MTP_int(0), // offset_rate
|
|
||||||
MTP_inputPeerEmpty(), // offset_peer
|
|
||||||
MTP_int(0), // offset_id
|
|
||||||
MTP_int(kSearchPerPage)
|
|
||||||
)).done([=](const MTPmessages_Messages &result) {
|
|
||||||
searchReceived(type, result, _searchRequest);
|
|
||||||
}).fail([=](const MTP::Error &error) {
|
|
||||||
searchFailed(type, error, _searchRequest);
|
|
||||||
}).send();
|
|
||||||
_searchQueries.emplace(_searchRequest, _searchQuery);
|
|
||||||
} else {
|
} else {
|
||||||
const auto type = SearchRequestType::FromStart;
|
requestMessages(true);
|
||||||
const auto flags = session().settings().skipArchiveInSearch()
|
|
||||||
? MTPmessages_SearchGlobal::Flag::f_folder_id
|
|
||||||
: MTPmessages_SearchGlobal::Flag(0);
|
|
||||||
const auto folderId = 0;
|
|
||||||
_searchRequest = session().api().request(MTPmessages_SearchGlobal(
|
|
||||||
MTP_flags(flags),
|
|
||||||
MTP_int(folderId),
|
|
||||||
MTP_string(_searchQuery),
|
|
||||||
MTP_inputMessagesFilterEmpty(),
|
|
||||||
MTP_int(0), // min_date
|
|
||||||
MTP_int(0), // max_date
|
|
||||||
MTP_int(0), // offset_rate
|
|
||||||
MTP_inputPeerEmpty(), // offset_peer
|
|
||||||
MTP_int(0), // offset_id
|
|
||||||
MTP_int(kSearchPerPage)
|
|
||||||
)).done([=](const MTPmessages_Messages &result) {
|
|
||||||
searchReceived(type, result, _searchRequest);
|
|
||||||
}).fail([=](const MTP::Error &error) {
|
|
||||||
searchFailed(type, error, _searchRequest);
|
|
||||||
}).send();
|
|
||||||
_searchQueries.emplace(_searchRequest, _searchQuery);
|
|
||||||
}
|
}
|
||||||
_inner->searchRequested(true);
|
_inner->searchRequested(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2261,7 +2246,9 @@ bool Widget::search(bool inCache, SearchRequestDelay delay) {
|
||||||
_peerSearchRequest = _api.request(MTPcontacts_Search(
|
_peerSearchRequest = _api.request(MTPcontacts_Search(
|
||||||
MTP_string(_peerSearchQuery),
|
MTP_string(_peerSearchQuery),
|
||||||
MTP_int(SearchPeopleLimit)
|
MTP_int(SearchPeopleLimit)
|
||||||
)).done([=](const MTPcontacts_Found &result, mtpRequestId requestId) {
|
)).done([=](
|
||||||
|
const MTPcontacts_Found &result,
|
||||||
|
mtpRequestId requestId) {
|
||||||
peerSearchReceived(result, requestId);
|
peerSearchReceived(result, requestId);
|
||||||
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
||||||
peerSearchFailed(error, requestId);
|
peerSearchFailed(error, requestId);
|
||||||
|
@ -2368,11 +2355,12 @@ void Widget::searchTopics() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::searchMore() {
|
void Widget::searchMore() {
|
||||||
if (_searchRequest
|
const auto process = currentSearchProcess();
|
||||||
|| _searchInHistoryRequest
|
if (process->requestId
|
||||||
|
|| _historiesRequest
|
||||||
|| _searchTimer.isActive()) {
|
|| _searchTimer.isActive()) {
|
||||||
return;
|
return;
|
||||||
} else if (!_searchFull) {
|
} else if (!process->full) {
|
||||||
if (const auto peer = searchInPeer()) {
|
if (const auto peer = searchInPeer()) {
|
||||||
auto &histories = session().data().histories();
|
auto &histories = session().data().histories();
|
||||||
const auto topic = searchInTopic();
|
const auto topic = searchInTopic();
|
||||||
|
@ -2385,154 +2373,213 @@ void Widget::searchMore() {
|
||||||
const auto savedPeer = sublist
|
const auto savedPeer = sublist
|
||||||
? sublist->peer().get()
|
? sublist->peer().get()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
_searchInHistoryRequest = histories.sendRequest(history, type, [=](Fn<void()> finish) {
|
_historiesRequest = histories.sendRequest(history, type, [=](
|
||||||
const auto type = _lastSearchId
|
Fn<void()> finish) {
|
||||||
? SearchRequestType::PeerFromOffset
|
const auto type = SearchRequestType{
|
||||||
: SearchRequestType::PeerFromStart;
|
.start = !process->lastId,
|
||||||
|
.peer = true,
|
||||||
|
};
|
||||||
using Flag = MTPmessages_Search::Flag;
|
using Flag = MTPmessages_Search::Flag;
|
||||||
_searchRequest = session().api().request(MTPmessages_Search(
|
process->requestId = session().api().request(
|
||||||
MTP_flags((topic ? Flag::f_top_msg_id : Flag())
|
MTPmessages_Search(
|
||||||
| (fromPeer ? Flag::f_from_id : Flag())
|
MTP_flags((topic ? Flag::f_top_msg_id : Flag())
|
||||||
| (savedPeer ? Flag::f_saved_peer_id : Flag())
|
| (fromPeer ? Flag::f_from_id : Flag())
|
||||||
| (_searchQueryTags.empty()
|
| (savedPeer ? Flag::f_saved_peer_id : Flag())
|
||||||
? Flag()
|
| (_searchQueryTags.empty()
|
||||||
: Flag::f_saved_reaction)),
|
? Flag()
|
||||||
peer->input,
|
: Flag::f_saved_reaction)),
|
||||||
|
peer->input,
|
||||||
|
MTP_string(_searchQuery),
|
||||||
|
(fromPeer ? fromPeer->input : MTP_inputPeerEmpty()),
|
||||||
|
(savedPeer
|
||||||
|
? savedPeer->input
|
||||||
|
: MTP_inputPeerEmpty()),
|
||||||
|
MTP_vector_from_range(
|
||||||
|
_searchQueryTags | ranges::views::transform(
|
||||||
|
Data::ReactionToMTP
|
||||||
|
)),
|
||||||
|
MTP_int(topic ? topic->rootId() : 0),
|
||||||
|
MTP_inputMessagesFilterEmpty(),
|
||||||
|
MTP_int(0), // min_date
|
||||||
|
MTP_int(0), // max_date
|
||||||
|
MTP_int(process->lastId),
|
||||||
|
MTP_int(0), // add_offset
|
||||||
|
MTP_int(kSearchPerPage),
|
||||||
|
MTP_int(0), // max_id
|
||||||
|
MTP_int(0), // min_id
|
||||||
|
MTP_long(0)) // hash
|
||||||
|
).done([=](const MTPmessages_Messages &result) {
|
||||||
|
searchReceived(type, result, process);
|
||||||
|
_historiesRequest = 0;
|
||||||
|
finish();
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
searchFailed(type, error, process);
|
||||||
|
_historiesRequest = 0;
|
||||||
|
finish();
|
||||||
|
}).send();
|
||||||
|
if (!process->lastId) {
|
||||||
|
process->queries.emplace(
|
||||||
|
process->requestId,
|
||||||
|
_searchQuery);
|
||||||
|
}
|
||||||
|
return process->requestId;
|
||||||
|
});
|
||||||
|
} else if (_searchState.tab == ChatSearchTab::PublicPosts) {
|
||||||
|
requestPublicPosts(false);
|
||||||
|
} else {
|
||||||
|
requestMessages(false);
|
||||||
|
}
|
||||||
|
} else if (_searchInMigrated && !_migratedProcess.full) {
|
||||||
|
auto &histories = session().data().histories();
|
||||||
|
const auto type = Data::Histories::RequestType::History;
|
||||||
|
const auto history = _searchInMigrated;
|
||||||
|
_historiesRequest = histories.sendRequest(history, type, [=](
|
||||||
|
Fn<void()> finish) {
|
||||||
|
const auto type = SearchRequestType{
|
||||||
|
.migrated = true,
|
||||||
|
.start = !_migratedProcess.lastId,
|
||||||
|
};
|
||||||
|
const auto flags = _searchQueryFrom
|
||||||
|
? MTP_flags(MTPmessages_Search::Flag::f_from_id)
|
||||||
|
: MTP_flags(0);
|
||||||
|
_migratedProcess.requestId = session().api().request(
|
||||||
|
MTPmessages_Search(
|
||||||
|
flags,
|
||||||
|
_searchInMigrated->peer->input,
|
||||||
MTP_string(_searchQuery),
|
MTP_string(_searchQuery),
|
||||||
(fromPeer ? fromPeer->input : MTP_inputPeerEmpty()),
|
(_searchQueryFrom
|
||||||
(savedPeer ? savedPeer->input : MTP_inputPeerEmpty()),
|
? _searchQueryFrom->input
|
||||||
MTP_vector_from_range(
|
: MTP_inputPeerEmpty()),
|
||||||
_searchQueryTags | ranges::views::transform(
|
MTPInputPeer(), // saved_peer_id
|
||||||
Data::ReactionToMTP
|
MTPVector<MTPReaction>(), // saved_reaction
|
||||||
)),
|
MTPint(), // top_msg_id
|
||||||
MTP_int(topic ? topic->rootId() : 0),
|
|
||||||
MTP_inputMessagesFilterEmpty(),
|
MTP_inputMessagesFilterEmpty(),
|
||||||
MTP_int(0), // min_date
|
MTP_int(0), // min_date
|
||||||
MTP_int(0), // max_date
|
MTP_int(0), // max_date
|
||||||
MTP_int(_lastSearchId),
|
MTP_int(_migratedProcess.lastId),
|
||||||
MTP_int(0), // add_offset
|
MTP_int(0), // add_offset
|
||||||
MTP_int(kSearchPerPage),
|
MTP_int(kSearchPerPage),
|
||||||
MTP_int(0), // max_id
|
MTP_int(0), // max_id
|
||||||
MTP_int(0), // min_id
|
MTP_int(0), // min_id
|
||||||
MTP_long(0) // hash
|
MTP_long(0)) // hash
|
||||||
)).done([=](const MTPmessages_Messages &result) {
|
).done([=](const MTPmessages_Messages &result) {
|
||||||
searchReceived(type, result, _searchRequest);
|
searchReceived(type, result, &_migratedProcess);
|
||||||
_searchInHistoryRequest = 0;
|
_historiesRequest = 0;
|
||||||
finish();
|
|
||||||
}).fail([=](const MTP::Error &error) {
|
|
||||||
searchFailed(type, error, _searchRequest);
|
|
||||||
_searchInHistoryRequest = 0;
|
|
||||||
finish();
|
|
||||||
}).send();
|
|
||||||
if (!_lastSearchId) {
|
|
||||||
_searchQueries.emplace(_searchRequest, _searchQuery);
|
|
||||||
}
|
|
||||||
return _searchRequest;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const auto type = _lastSearchId
|
|
||||||
? SearchRequestType::FromOffset
|
|
||||||
: SearchRequestType::FromStart;
|
|
||||||
const auto flags = session().settings().skipArchiveInSearch()
|
|
||||||
? MTPmessages_SearchGlobal::Flag::f_folder_id
|
|
||||||
: MTPmessages_SearchGlobal::Flag(0);
|
|
||||||
const auto folderId = 0;
|
|
||||||
_searchRequest = session().api().request(MTPmessages_SearchGlobal(
|
|
||||||
MTP_flags(flags),
|
|
||||||
MTP_int(folderId),
|
|
||||||
MTP_string(_searchQuery),
|
|
||||||
MTP_inputMessagesFilterEmpty(),
|
|
||||||
MTP_int(0), // min_date
|
|
||||||
MTP_int(0), // max_date
|
|
||||||
MTP_int(_searchNextRate),
|
|
||||||
(_lastSearchPeer
|
|
||||||
? _lastSearchPeer->input
|
|
||||||
: MTP_inputPeerEmpty()),
|
|
||||||
MTP_int(_lastSearchId),
|
|
||||||
MTP_int(kSearchPerPage)
|
|
||||||
)).done([=](const MTPmessages_Messages &result) {
|
|
||||||
searchReceived(type, result, _searchRequest);
|
|
||||||
}).fail([=](const MTP::Error &error) {
|
|
||||||
searchFailed(type, error, _searchRequest);
|
|
||||||
}).send();
|
|
||||||
if (!_lastSearchId) {
|
|
||||||
_searchQueries.emplace(_searchRequest, _searchQuery);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (_searchInMigrated && !_searchFullMigrated) {
|
|
||||||
auto &histories = session().data().histories();
|
|
||||||
const auto type = Data::Histories::RequestType::History;
|
|
||||||
const auto history = _searchInMigrated;
|
|
||||||
_searchInHistoryRequest = histories.sendRequest(history, type, [=](Fn<void()> finish) {
|
|
||||||
const auto type = _lastSearchMigratedId
|
|
||||||
? SearchRequestType::MigratedFromOffset
|
|
||||||
: SearchRequestType::MigratedFromStart;
|
|
||||||
const auto flags = _searchQueryFrom
|
|
||||||
? MTP_flags(MTPmessages_Search::Flag::f_from_id)
|
|
||||||
: MTP_flags(0);
|
|
||||||
_searchRequest = session().api().request(MTPmessages_Search(
|
|
||||||
flags,
|
|
||||||
_searchInMigrated->peer->input,
|
|
||||||
MTP_string(_searchQuery),
|
|
||||||
(_searchQueryFrom
|
|
||||||
? _searchQueryFrom->input
|
|
||||||
: MTP_inputPeerEmpty()),
|
|
||||||
MTPInputPeer(), // saved_peer_id
|
|
||||||
MTPVector<MTPReaction>(), // saved_reaction
|
|
||||||
MTPint(), // top_msg_id
|
|
||||||
MTP_inputMessagesFilterEmpty(),
|
|
||||||
MTP_int(0), // min_date
|
|
||||||
MTP_int(0), // max_date
|
|
||||||
MTP_int(_lastSearchMigratedId),
|
|
||||||
MTP_int(0), // add_offset
|
|
||||||
MTP_int(kSearchPerPage),
|
|
||||||
MTP_int(0), // max_id
|
|
||||||
MTP_int(0), // min_id
|
|
||||||
MTP_long(0) // hash
|
|
||||||
)).done([=](const MTPmessages_Messages &result) {
|
|
||||||
searchReceived(type, result, _searchRequest);
|
|
||||||
_searchInHistoryRequest = 0;
|
|
||||||
finish();
|
finish();
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
searchFailed(type, error, _searchRequest);
|
searchFailed(type, error, &_migratedProcess);
|
||||||
_searchInHistoryRequest = 0;
|
_historiesRequest = 0;
|
||||||
finish();
|
finish();
|
||||||
}).send();
|
}).send();
|
||||||
return _searchRequest;
|
return _migratedProcess.requestId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::requestPublicPosts(bool fromStart) {
|
||||||
|
if (!_postsProcess.lastId || !_postsProcess.lastPeer) {
|
||||||
|
fromStart = true;
|
||||||
|
}
|
||||||
|
const auto type = SearchRequestType{
|
||||||
|
.posts = true,
|
||||||
|
.start = fromStart,
|
||||||
|
};
|
||||||
|
_postsProcess.requestId = session().api().request(
|
||||||
|
MTPchannels_SearchPosts(
|
||||||
|
MTP_string(_searchState.query.trimmed().mid(1)),
|
||||||
|
MTP_int(fromStart ? 0 : _postsProcess.nextRate),
|
||||||
|
(fromStart
|
||||||
|
? MTP_inputPeerEmpty()
|
||||||
|
: _postsProcess.lastPeer->input),
|
||||||
|
MTP_int(fromStart ? 0 : _postsProcess.lastId),
|
||||||
|
MTP_int(kSearchPerPage))
|
||||||
|
).done([=](const MTPmessages_Messages &result) {
|
||||||
|
searchReceived(type, result, &_postsProcess);
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
searchFailed(type, error, &_postsProcess);
|
||||||
|
}).send();
|
||||||
|
if (fromStart) {
|
||||||
|
_postsProcess.queries.emplace(_postsProcess.requestId, _searchQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::requestMessages(bool fromStart) {
|
||||||
|
if (!_searchProcess.lastId || !_searchProcess.lastPeer) {
|
||||||
|
fromStart = true;
|
||||||
|
}
|
||||||
|
const auto type = SearchRequestType{
|
||||||
|
.start = fromStart,
|
||||||
|
};
|
||||||
|
const auto flags = session().settings().skipArchiveInSearch()
|
||||||
|
? MTPmessages_SearchGlobal::Flag::f_folder_id
|
||||||
|
: MTPmessages_SearchGlobal::Flag(0);
|
||||||
|
const auto folderId = 0;
|
||||||
|
_searchProcess.requestId = session().api().request(
|
||||||
|
MTPmessages_SearchGlobal(
|
||||||
|
MTP_flags(flags),
|
||||||
|
MTP_int(folderId),
|
||||||
|
MTP_string(_searchQuery),
|
||||||
|
MTP_inputMessagesFilterEmpty(),
|
||||||
|
MTP_int(0), // min_date
|
||||||
|
MTP_int(0), // max_date
|
||||||
|
MTP_int(fromStart ? 0 : _searchProcess.nextRate),
|
||||||
|
(fromStart
|
||||||
|
? MTP_inputPeerEmpty()
|
||||||
|
: _searchProcess.lastPeer->input),
|
||||||
|
MTP_int(fromStart ? 0 : _searchProcess.lastId),
|
||||||
|
MTP_int(kSearchPerPage))
|
||||||
|
).done([=](const MTPmessages_Messages &result) {
|
||||||
|
searchReceived(type, result, &_searchProcess);
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
searchFailed(type, error, &_searchProcess);
|
||||||
|
}).send();
|
||||||
|
if (!_searchProcess.lastId) {
|
||||||
|
_searchProcess.queries.emplace(
|
||||||
|
_searchProcess.requestId,
|
||||||
|
_searchQuery);
|
||||||
|
}
|
||||||
|
if (fromStart && _searchWithPostsPreview) {
|
||||||
|
requestPublicPosts(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Widget::currentSearchProcess() -> not_null<SearchProcessState*> {
|
||||||
|
return (_searchState.tab == ChatSearchTab::PublicPosts)
|
||||||
|
? &_postsProcess
|
||||||
|
: &_searchProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Widget::computeSearchWithPostsPreview() const {
|
||||||
|
return (_searchHashOrCashtag != HashOrCashtag::None)
|
||||||
|
&& (_searchState.tab == ChatSearchTab::MyMessages);
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::searchReceived(
|
void Widget::searchReceived(
|
||||||
SearchRequestType type,
|
SearchRequestType type,
|
||||||
const MTPmessages_Messages &result,
|
const MTPmessages_Messages &result,
|
||||||
mtpRequestId requestId) {
|
not_null<SearchProcessState*> process,
|
||||||
|
bool cacheResults) {
|
||||||
const auto state = _inner->state();
|
const auto state = _inner->state();
|
||||||
if (state == WidgetState::Filtered) {
|
if (!cacheResults
|
||||||
if (type == SearchRequestType::FromStart || type == SearchRequestType::PeerFromStart) {
|
&& (state == WidgetState::Filtered)
|
||||||
auto i = _searchQueries.find(requestId);
|
&& type.start) {
|
||||||
if (i != _searchQueries.end()) {
|
const auto i = process->queries.find(process->requestId);
|
||||||
_searchCache[i->second] = result;
|
if (i != process->queries.end()) {
|
||||||
_searchQueries.erase(i);
|
process->cache[i->second] = result;
|
||||||
}
|
process->queries.erase(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto inject = (type == SearchRequestType::FromStart
|
const auto inject = (type.start && !type.posts)
|
||||||
|| type == SearchRequestType::PeerFromStart)
|
|
||||||
? *_singleMessageSearch.lookup(_searchQuery)
|
? *_singleMessageSearch.lookup(_searchQuery)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
if (cacheResults && process->requestId) {
|
||||||
if (_searchRequest != requestId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type == SearchRequestType::FromStart
|
if (type.start) {
|
||||||
|| type == SearchRequestType::PeerFromStart) {
|
process->lastPeer = nullptr;
|
||||||
_lastSearchPeer = nullptr;
|
process->lastId = 0;
|
||||||
_lastSearchId = _lastSearchMigratedId = 0;
|
|
||||||
}
|
}
|
||||||
const auto isMigratedSearch = (type == SearchRequestType::MigratedFromStart)
|
const auto processList = [&](const MTPVector<MTPMessage> &messages) {
|
||||||
|| (type == SearchRequestType::MigratedFromOffset);
|
|
||||||
const auto process = [&](const MTPVector<MTPMessage> &messages) {
|
|
||||||
auto result = std::vector<not_null<HistoryItem*>>();
|
auto result = std::vector<not_null<HistoryItem*>>();
|
||||||
for (const auto &message : messages.v) {
|
for (const auto &message : messages.v) {
|
||||||
const auto msgId = IdFromMessage(message);
|
const auto msgId = IdFromMessage(message);
|
||||||
|
@ -2546,55 +2593,44 @@ void Widget::searchReceived(
|
||||||
NewMessageType::Existing);
|
NewMessageType::Existing);
|
||||||
result.push_back(item);
|
result.push_back(item);
|
||||||
}
|
}
|
||||||
_lastSearchPeer = peer;
|
process->lastPeer = peer;
|
||||||
} else {
|
} else {
|
||||||
LOG(("API Error: a search results with not loaded peer %1"
|
LOG(("API Error: a search results with not loaded peer %1"
|
||||||
).arg(peerId.value));
|
).arg(peerId.value));
|
||||||
}
|
}
|
||||||
if (isMigratedSearch) {
|
process->lastId = msgId;
|
||||||
_lastSearchMigratedId = msgId;
|
|
||||||
} else {
|
|
||||||
_lastSearchId = msgId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
auto fullCount = 0;
|
auto fullCount = 0;
|
||||||
auto messages = result.match([&](const MTPDmessages_messages &data) {
|
auto messages = result.match([&](const MTPDmessages_messages &data) {
|
||||||
if (_searchRequest != 0) {
|
if (!cacheResults) {
|
||||||
// Don't apply cached data!
|
// Don't apply cached data!
|
||||||
session().data().processUsers(data.vusers());
|
session().data().processUsers(data.vusers());
|
||||||
session().data().processChats(data.vchats());
|
session().data().processChats(data.vchats());
|
||||||
}
|
}
|
||||||
if (type == SearchRequestType::MigratedFromStart || type == SearchRequestType::MigratedFromOffset) {
|
process->full = true;
|
||||||
_searchFullMigrated = true;
|
auto list = processList(data.vmessages());
|
||||||
} else {
|
|
||||||
_searchFull = true;
|
|
||||||
}
|
|
||||||
auto list = process(data.vmessages());
|
|
||||||
fullCount = list.size();
|
fullCount = list.size();
|
||||||
return list;
|
return list;
|
||||||
}, [&](const MTPDmessages_messagesSlice &data) {
|
}, [&](const MTPDmessages_messagesSlice &data) {
|
||||||
if (_searchRequest != 0) {
|
if (!cacheResults) {
|
||||||
// Don't apply cached data!
|
// Don't apply cached data!
|
||||||
session().data().processUsers(data.vusers());
|
session().data().processUsers(data.vusers());
|
||||||
session().data().processChats(data.vchats());
|
session().data().processChats(data.vchats());
|
||||||
}
|
}
|
||||||
auto list = process(data.vmessages());
|
auto list = processList(data.vmessages());
|
||||||
const auto nextRate = data.vnext_rate();
|
const auto nextRate = data.vnext_rate();
|
||||||
const auto rateUpdated = nextRate && (nextRate->v != _searchNextRate);
|
const auto rateUpdated = nextRate
|
||||||
const auto finished = (type == SearchRequestType::FromStart || type == SearchRequestType::FromOffset)
|
&& (nextRate->v != process->nextRate);
|
||||||
? !rateUpdated
|
const auto finished = (type.peer || type.migrated || type.posts)
|
||||||
: list.empty();
|
? list.empty()
|
||||||
|
: !rateUpdated;
|
||||||
if (rateUpdated) {
|
if (rateUpdated) {
|
||||||
_searchNextRate = nextRate->v;
|
process->nextRate = nextRate->v;
|
||||||
}
|
}
|
||||||
if (finished) {
|
if (finished) {
|
||||||
if (type == SearchRequestType::MigratedFromStart || type == SearchRequestType::MigratedFromOffset) {
|
process->full = true;
|
||||||
_searchFullMigrated = true;
|
|
||||||
} else {
|
|
||||||
_searchFull = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fullCount = data.vcount().v;
|
fullCount = data.vcount().v;
|
||||||
return list;
|
return list;
|
||||||
|
@ -2613,33 +2649,26 @@ void Widget::searchReceived(
|
||||||
"received messages.channelMessages when no channel "
|
"received messages.channelMessages when no channel "
|
||||||
"was passed! (Widget::searchReceived)"));
|
"was passed! (Widget::searchReceived)"));
|
||||||
}
|
}
|
||||||
if (_searchRequest != 0) {
|
if (!cacheResults) {
|
||||||
// Don't apply cached data!
|
// Don't apply cached data!
|
||||||
session().data().processUsers(data.vusers());
|
session().data().processUsers(data.vusers());
|
||||||
session().data().processChats(data.vchats());
|
session().data().processChats(data.vchats());
|
||||||
}
|
}
|
||||||
auto list = process(data.vmessages());
|
auto list = processList(data.vmessages());
|
||||||
if (list.empty()) {
|
if (list.empty()) {
|
||||||
if (type == SearchRequestType::MigratedFromStart || type == SearchRequestType::MigratedFromOffset) {
|
process->full = true;
|
||||||
_searchFullMigrated = true;
|
|
||||||
} else {
|
|
||||||
_searchFull = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fullCount = data.vcount().v;
|
fullCount = data.vcount().v;
|
||||||
return list;
|
return list;
|
||||||
}, [&](const MTPDmessages_messagesNotModified &) {
|
}, [&](const MTPDmessages_messagesNotModified &) {
|
||||||
LOG(("API Error: received messages.messagesNotModified! (Widget::searchReceived)"));
|
LOG(("API Error: received messages.messagesNotModified! "
|
||||||
if (type == SearchRequestType::MigratedFromStart || type == SearchRequestType::MigratedFromOffset) {
|
"(Widget::searchReceived)"));
|
||||||
_searchFullMigrated = true;
|
process->full = true;
|
||||||
} else {
|
|
||||||
_searchFull = true;
|
|
||||||
}
|
|
||||||
return std::vector<not_null<HistoryItem*>>();
|
return std::vector<not_null<HistoryItem*>>();
|
||||||
});
|
});
|
||||||
_inner->searchReceived(messages, inject, type, fullCount);
|
_inner->searchReceived(messages, inject, type, fullCount);
|
||||||
|
|
||||||
_searchRequest = 0;
|
process->requestId = 0;
|
||||||
listScrollUpdated();
|
listScrollUpdated();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
@ -2671,15 +2700,17 @@ void Widget::peerSearchReceived(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::searchApplyEmpty(SearchRequestType type, mtpRequestId id) {
|
void Widget::searchApplyEmpty(
|
||||||
_searchFull = _searchFullMigrated = true;
|
SearchRequestType type,
|
||||||
|
not_null<SearchProcessState*> process) {
|
||||||
|
process->full = true;
|
||||||
searchReceived(
|
searchReceived(
|
||||||
type,
|
type,
|
||||||
MTP_messages_messages(
|
MTP_messages_messages(
|
||||||
MTP_vector<MTPMessage>(),
|
MTP_vector<MTPMessage>(),
|
||||||
MTP_vector<MTPChat>(),
|
MTP_vector<MTPChat>(),
|
||||||
MTP_vector<MTPUser>()),
|
MTP_vector<MTPUser>()),
|
||||||
id);
|
process);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::peerSearchApplyEmpty(mtpRequestId id) {
|
void Widget::peerSearchApplyEmpty(mtpRequestId id) {
|
||||||
|
@ -2696,16 +2727,12 @@ void Widget::peerSearchApplyEmpty(mtpRequestId id) {
|
||||||
void Widget::searchFailed(
|
void Widget::searchFailed(
|
||||||
SearchRequestType type,
|
SearchRequestType type,
|
||||||
const MTP::Error &error,
|
const MTP::Error &error,
|
||||||
mtpRequestId requestId) {
|
not_null<SearchProcessState*> process) {
|
||||||
if (error.type() == u"SEARCH_QUERY_EMPTY"_q) {
|
if (error.type() == u"SEARCH_QUERY_EMPTY"_q) {
|
||||||
searchApplyEmpty(type, requestId);
|
searchApplyEmpty(type, process);
|
||||||
} else if (_searchRequest == requestId) {
|
} else {
|
||||||
_searchRequest = 0;
|
process->requestId = 0;
|
||||||
if (type == SearchRequestType::MigratedFromStart || type == SearchRequestType::MigratedFromOffset) {
|
process->full = true;
|
||||||
_searchFullMigrated = true;
|
|
||||||
} else {
|
|
||||||
_searchFull = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2838,6 +2865,7 @@ QString Widget::validateSearchQuery() {
|
||||||
} else {
|
} else {
|
||||||
_searchHashOrCashtag = IsHashOrCashtagSearchQuery(query);
|
_searchHashOrCashtag = IsHashOrCashtagSearchQuery(query);
|
||||||
}
|
}
|
||||||
|
_searchWithPostsPreview = computeSearchWithPostsPreview();
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3096,6 +3124,7 @@ bool Widget::applySearchState(SearchState state) {
|
||||||
? peer->owner().history(migrateFrom).get()
|
? peer->owner().history(migrateFrom).get()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
_searchState = state;
|
_searchState = state;
|
||||||
|
_searchWithPostsPreview = computeSearchWithPostsPreview();
|
||||||
if (queryChanged) {
|
if (queryChanged) {
|
||||||
updateLockUnlockVisibility(anim::type::normal);
|
updateLockUnlockVisibility(anim::type::normal);
|
||||||
updateLoadMoreChatsVisibility();
|
updateLoadMoreChatsVisibility();
|
||||||
|
@ -3111,16 +3140,20 @@ bool Widget::applySearchState(SearchState state) {
|
||||||
updateSearchFromVisibility();
|
updateSearchFromVisibility();
|
||||||
updateLockUnlockPosition();
|
updateLockUnlockPosition();
|
||||||
|
|
||||||
if ((state.query.isEmpty() && !state.fromPeer && state.tags.empty())
|
const auto searchCleared = state.query.isEmpty()
|
||||||
|
&& !state.fromPeer
|
||||||
|
&& state.tags.empty();
|
||||||
|
if (searchCleared
|
||||||
|| inChatChanged
|
|| inChatChanged
|
||||||
|| fromPeerChanged
|
|| fromPeerChanged
|
||||||
|| tagsChanged
|
|| tagsChanged
|
||||||
|| tabChanged) {
|
|| tabChanged) {
|
||||||
clearSearchCache();
|
clearSearchCache(searchCleared);
|
||||||
}
|
}
|
||||||
if (state.query.isEmpty()) {
|
if (state.query.isEmpty()) {
|
||||||
_peerSearchCache.clear();
|
_peerSearchCache.clear();
|
||||||
for (const auto &[requestId, query] : base::take(_peerSearchQueries)) {
|
const auto queries = base::take(_peerSearchQueries);
|
||||||
|
for (const auto &[requestId, query] : queries) {
|
||||||
_api.request(requestId).cancel();
|
_api.request(requestId).cancel();
|
||||||
}
|
}
|
||||||
_peerSearchQuery = QString();
|
_peerSearchQuery = QString();
|
||||||
|
@ -3158,15 +3191,23 @@ bool Widget::applySearchState(SearchState state) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::clearSearchCache() {
|
void Widget::clearSearchCache(bool clearPosts) {
|
||||||
_searchCache.clear();
|
_searchProcess.cache.clear();
|
||||||
_singleMessageSearch.clear();
|
_singleMessageSearch.clear();
|
||||||
for (const auto &[requestId, query] : base::take(_searchQueries)) {
|
const auto queries = base::take(_searchProcess.queries);
|
||||||
|
for (const auto &[requestId, query] : queries) {
|
||||||
session().api().request(requestId).cancel();
|
session().api().request(requestId).cancel();
|
||||||
}
|
}
|
||||||
_searchQuery = QString();
|
_searchQuery = QString();
|
||||||
_searchQueryFrom = nullptr;
|
_searchQueryFrom = nullptr;
|
||||||
_searchQueryTags.clear();
|
_searchQueryTags.clear();
|
||||||
|
if (clearPosts) {
|
||||||
|
_postsProcess.cache.clear();
|
||||||
|
const auto queries = base::take(_postsProcess.queries);
|
||||||
|
for (const auto &[requestId, query] : queries) {
|
||||||
|
session().api().request(requestId).cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
_topicSearchQuery = QString();
|
_topicSearchQuery = QString();
|
||||||
_topicSearchOffsetDate = 0;
|
_topicSearchOffsetDate = 0;
|
||||||
_topicSearchOffsetId = _topicSearchOffsetTopicId = 0;
|
_topicSearchOffsetId = _topicSearchOffsetTopicId = 0;
|
||||||
|
@ -3241,10 +3282,17 @@ void Widget::completeHashtag(QString tag) {
|
||||||
if (cur == start + 1
|
if (cur == start + 1
|
||||||
|| base::StringViewMid(t, start + 1, cur - start - 1)
|
|| base::StringViewMid(t, start + 1, cur - start - 1)
|
||||||
== base::StringViewMid(tag, 0, cur - start - 1)) {
|
== base::StringViewMid(tag, 0, cur - start - 1)) {
|
||||||
for (; cur < t.size() && cur - start - 1 < tag.size(); ++cur) {
|
while (cur < t.size() && cur - start - 1 < tag.size()) {
|
||||||
if (t.at(cur) != tag.at(cur - start - 1)) break;
|
if (t.at(cur) != tag.at(cur - start - 1)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++cur;
|
||||||
|
}
|
||||||
|
if (cur - start - 1 == tag.size()
|
||||||
|
&& cur < t.size()
|
||||||
|
&& t.at(cur) == ' ') {
|
||||||
|
++cur;
|
||||||
}
|
}
|
||||||
if (cur - start - 1 == tag.size() && cur < t.size() && t.at(cur) == ' ') ++cur;
|
|
||||||
hashtag = t.mid(0, start + 1) + tag + ' ' + t.mid(cur);
|
hashtag = t.mid(0, start + 1) + tag + ' ' + t.mid(cur);
|
||||||
setSearchQuery(hashtag, start + 1 + tag.size() + 1);
|
setSearchQuery(hashtag, start + 1 + tag.size() + 1);
|
||||||
applySearchUpdate();
|
applySearchUpdate();
|
||||||
|
@ -3356,7 +3404,8 @@ void Widget::updateControlsGeometry() {
|
||||||
? st::dialogsFilterSkip
|
? st::dialogsFilterSkip
|
||||||
: (st::dialogsFilterPadding.x() + _mainMenu.toggle->width()))
|
: (st::dialogsFilterPadding.x() + _mainMenu.toggle->width()))
|
||||||
+ st::dialogsFilterPadding.x();
|
+ st::dialogsFilterPadding.x();
|
||||||
const auto filterRight = st::dialogsFilterSkip + st::dialogsFilterPadding.x();
|
const auto filterRight = st::dialogsFilterSkip
|
||||||
|
+ st::dialogsFilterPadding.x();
|
||||||
const auto filterWidth = qMax(ratiow, smallw) - filterLeft - filterRight;
|
const auto filterWidth = qMax(ratiow, smallw) - filterLeft - filterRight;
|
||||||
const auto filterAreaHeight = st::topBarHeight;
|
const auto filterAreaHeight = st::topBarHeight;
|
||||||
_searchControls->setGeometry(0, filterAreaTop, ratiow, filterAreaHeight);
|
_searchControls->setGeometry(0, filterAreaTop, ratiow, filterAreaHeight);
|
||||||
|
@ -3369,7 +3418,11 @@ void Widget::updateControlsGeometry() {
|
||||||
|
|
||||||
auto filterTop = (filterAreaHeight - _search->height()) / 2;
|
auto filterTop = (filterAreaHeight - _search->height()) / 2;
|
||||||
filterLeft = anim::interpolate(filterLeft, _narrowWidth, narrowRatio);
|
filterLeft = anim::interpolate(filterLeft, _narrowWidth, narrowRatio);
|
||||||
_search->setGeometryToLeft(filterLeft, filterTop, filterWidth, _search->height());
|
_search->setGeometryToLeft(
|
||||||
|
filterLeft,
|
||||||
|
filterTop,
|
||||||
|
filterWidth,
|
||||||
|
_search->height());
|
||||||
|
|
||||||
auto mainMenuLeft = anim::interpolate(
|
auto mainMenuLeft = anim::interpolate(
|
||||||
st::dialogsFilterPadding.x(),
|
st::dialogsFilterPadding.x(),
|
||||||
|
@ -3387,12 +3440,16 @@ void Widget::updateControlsGeometry() {
|
||||||
-_searchForNarrowLayout->width(),
|
-_searchForNarrowLayout->width(),
|
||||||
(_narrowWidth - _searchForNarrowLayout->width()) / 2,
|
(_narrowWidth - _searchForNarrowLayout->width()) / 2,
|
||||||
narrowRatio);
|
narrowRatio);
|
||||||
_searchForNarrowLayout->moveToLeft(searchLeft, st::dialogsFilterPadding.y());
|
_searchForNarrowLayout->moveToLeft(
|
||||||
|
searchLeft,
|
||||||
|
st::dialogsFilterPadding.y());
|
||||||
|
|
||||||
auto right = filterLeft + filterWidth;
|
auto right = filterLeft + filterWidth;
|
||||||
_cancelSearch->moveToLeft(right - _cancelSearch->width(), _search->y());
|
_cancelSearch->moveToLeft(right - _cancelSearch->width(), _search->y());
|
||||||
right -= _jumpToDate->width(); _jumpToDate->moveToLeft(right, _search->y());
|
right -= _jumpToDate->width();
|
||||||
right -= _chooseFromUser->width(); _chooseFromUser->moveToLeft(right, _search->y());
|
_jumpToDate->moveToLeft(right, _search->y());
|
||||||
|
right -= _chooseFromUser->width();
|
||||||
|
_chooseFromUser->moveToLeft(right, _search->y());
|
||||||
|
|
||||||
const auto barw = width();
|
const auto barw = width();
|
||||||
const auto expandedStoriesTop = filterAreaTop + filterAreaHeight;
|
const auto expandedStoriesTop = filterAreaTop + filterAreaHeight;
|
||||||
|
@ -3691,9 +3748,11 @@ void Widget::scrollToEntry(const RowDescriptor &entry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::cancelSearchRequest() {
|
void Widget::cancelSearchRequest() {
|
||||||
session().api().request(base::take(_searchRequest)).cancel();
|
session().api().request(base::take(_searchProcess.requestId)).cancel();
|
||||||
|
session().api().request(base::take(_migratedProcess.requestId)).cancel();
|
||||||
|
session().api().request(base::take(_postsProcess.requestId)).cancel();
|
||||||
session().data().histories().cancelRequest(
|
session().data().histories().cancelRequest(
|
||||||
base::take(_searchInHistoryRequest));
|
base::take(_historiesRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerData *Widget::searchInPeer() const {
|
PeerData *Widget::searchInPeer() const {
|
||||||
|
@ -3810,8 +3869,12 @@ bool Widget::cancelSearch(CancelSearchOptions options) {
|
||||||
// Don't create suggestions in unfocus case.
|
// Don't create suggestions in unfocus case.
|
||||||
setInnerFocus(true);
|
setInnerFocus(true);
|
||||||
}
|
}
|
||||||
_lastSearchPeer = nullptr;
|
_searchProcess.lastPeer = nullptr;
|
||||||
_lastSearchId = _lastSearchMigratedId = 0;
|
_searchProcess.lastId = 0;
|
||||||
|
_migratedProcess.lastPeer = nullptr;
|
||||||
|
_migratedProcess.lastId = 0;
|
||||||
|
_postsProcess.lastPeer = nullptr;
|
||||||
|
_postsProcess.lastId = 0;
|
||||||
_inner->clearFilter();
|
_inner->clearFilter();
|
||||||
applySearchState(std::move(updatedState));
|
applySearchState(std::move(updatedState));
|
||||||
if (_suggestions && clearSearchFocus) {
|
if (_suggestions && clearSearchFocus) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ class FakeRow;
|
||||||
class Key;
|
class Key;
|
||||||
struct ChosenRow;
|
struct ChosenRow;
|
||||||
class InnerWidget;
|
class InnerWidget;
|
||||||
enum class SearchRequestType : uchar;
|
struct SearchRequestType;
|
||||||
enum class SearchRequestDelay : uchar;
|
enum class SearchRequestDelay : uchar;
|
||||||
class Suggestions;
|
class Suggestions;
|
||||||
class ChatSearchIn;
|
class ChatSearchIn;
|
||||||
|
@ -151,10 +151,26 @@ protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct SearchProcessState {
|
||||||
|
base::flat_map<QString, MTPmessages_Messages> cache;
|
||||||
|
base::flat_map<mtpRequestId, QString> queries;
|
||||||
|
|
||||||
|
PeerData *lastPeer = nullptr;
|
||||||
|
MsgId lastId = 0;
|
||||||
|
int32 nextRate = 0;
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
|
bool full = false;
|
||||||
|
};
|
||||||
|
|
||||||
void chosenRow(const ChosenRow &row);
|
void chosenRow(const ChosenRow &row);
|
||||||
void listScrollUpdated();
|
void listScrollUpdated();
|
||||||
void searchCursorMoved();
|
void searchCursorMoved();
|
||||||
void completeHashtag(QString tag);
|
void completeHashtag(QString tag);
|
||||||
|
void requestPublicPosts(bool fromStart);
|
||||||
|
void requestMessages(bool fromStart);
|
||||||
|
[[nodiscard]] not_null<SearchProcessState*> currentSearchProcess();
|
||||||
|
|
||||||
|
[[nodiscard]] bool computeSearchWithPostsPreview() const;
|
||||||
|
|
||||||
[[nodiscard]] QString currentSearchQuery() const;
|
[[nodiscard]] QString currentSearchQuery() const;
|
||||||
[[nodiscard]] int currentSearchQueryCursorPosition() const;
|
[[nodiscard]] int currentSearchQueryCursorPosition() const;
|
||||||
|
@ -168,7 +184,8 @@ private:
|
||||||
void searchReceived(
|
void searchReceived(
|
||||||
SearchRequestType type,
|
SearchRequestType type,
|
||||||
const MTPmessages_Messages &result,
|
const MTPmessages_Messages &result,
|
||||||
mtpRequestId requestId);
|
not_null<SearchProcessState*> process,
|
||||||
|
bool cacheResults = false);
|
||||||
void peerSearchReceived(
|
void peerSearchReceived(
|
||||||
const MTPcontacts_Found &result,
|
const MTPcontacts_Found &result,
|
||||||
mtpRequestId requestId);
|
mtpRequestId requestId);
|
||||||
|
@ -201,7 +218,7 @@ private:
|
||||||
void showCalendar();
|
void showCalendar();
|
||||||
void showSearchFrom();
|
void showSearchFrom();
|
||||||
void showMainMenu();
|
void showMainMenu();
|
||||||
void clearSearchCache();
|
void clearSearchCache(bool clearPosts);
|
||||||
void setSearchQuery(const QString &query, int cursorPosition = -1);
|
void setSearchQuery(const QString &query, int cursorPosition = -1);
|
||||||
void updateControlsVisibility(bool fast = false);
|
void updateControlsVisibility(bool fast = false);
|
||||||
void updateLockUnlockVisibility(
|
void updateLockUnlockVisibility(
|
||||||
|
@ -244,9 +261,11 @@ private:
|
||||||
void searchFailed(
|
void searchFailed(
|
||||||
SearchRequestType type,
|
SearchRequestType type,
|
||||||
const MTP::Error &error,
|
const MTP::Error &error,
|
||||||
mtpRequestId requestId);
|
not_null<SearchProcessState*> process);
|
||||||
void peerSearchFailed(const MTP::Error &error, mtpRequestId requestId);
|
void peerSearchFailed(const MTP::Error &error, mtpRequestId requestId);
|
||||||
void searchApplyEmpty(SearchRequestType type, mtpRequestId id);
|
void searchApplyEmpty(
|
||||||
|
SearchRequestType type,
|
||||||
|
not_null<SearchProcessState*> process);
|
||||||
void peerSearchApplyEmpty(mtpRequestId id);
|
void peerSearchApplyEmpty(mtpRequestId id);
|
||||||
|
|
||||||
void updateForceDisplayWide();
|
void updateForceDisplayWide();
|
||||||
|
@ -318,6 +337,7 @@ private:
|
||||||
bool _scrollToTopIsShown = false;
|
bool _scrollToTopIsShown = false;
|
||||||
bool _forumSearchRequested = false;
|
bool _forumSearchRequested = false;
|
||||||
HashOrCashtag _searchHashOrCashtag = {};
|
HashOrCashtag _searchHashOrCashtag = {};
|
||||||
|
bool _searchWithPostsPreview = false;
|
||||||
|
|
||||||
Data::Folder *_openedFolder = nullptr;
|
Data::Folder *_openedFolder = nullptr;
|
||||||
Data::Forum *_openedForum = nullptr;
|
Data::Forum *_openedForum = nullptr;
|
||||||
|
@ -358,19 +378,13 @@ private:
|
||||||
PeerData *_searchQueryFrom = nullptr;
|
PeerData *_searchQueryFrom = nullptr;
|
||||||
std::vector<Data::ReactionId> _searchQueryTags;
|
std::vector<Data::ReactionId> _searchQueryTags;
|
||||||
ChatSearchTab _searchQueryTab = {};
|
ChatSearchTab _searchQueryTab = {};
|
||||||
int32 _searchNextRate = 0;
|
|
||||||
bool _searchFull = false;
|
|
||||||
bool _searchFullMigrated = false;
|
|
||||||
int _searchInHistoryRequest = 0; // Not real mtpRequestId.
|
|
||||||
mtpRequestId _searchRequest = 0;
|
|
||||||
|
|
||||||
PeerData *_lastSearchPeer = nullptr;
|
SearchProcessState _searchProcess;
|
||||||
MsgId _lastSearchId = 0;
|
SearchProcessState _migratedProcess;
|
||||||
MsgId _lastSearchMigratedId = 0;
|
SearchProcessState _postsProcess;
|
||||||
|
int _historiesRequest = 0; // Not real mtpRequestId.
|
||||||
|
|
||||||
base::flat_map<QString, MTPmessages_Messages> _searchCache;
|
|
||||||
Api::SingleMessageSearch _singleMessageSearch;
|
Api::SingleMessageSearch _singleMessageSearch;
|
||||||
base::flat_map<mtpRequestId, QString> _searchQueries;
|
|
||||||
base::flat_map<QString, MTPcontacts_Found> _peerSearchCache;
|
base::flat_map<QString, MTPcontacts_Found> _peerSearchCache;
|
||||||
base::flat_map<mtpRequestId, QString> _peerSearchQueries;
|
base::flat_map<mtpRequestId, QString> _peerSearchQueries;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue