From 631e51a49312d1640e5a5951daff5439817811d5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Oct 2018 18:36:06 +0300 Subject: [PATCH] Hashtag search results as dialogs (support). --- Telegram/SourceFiles/apiwrap.cpp | 37 +++ Telegram/SourceFiles/apiwrap.h | 1 + .../dialogs/dialogs_inner_widget.cpp | 53 +++- .../dialogs/dialogs_inner_widget.h | 2 + .../SourceFiles/dialogs/dialogs_layout.cpp | 257 ++++++++++++------ Telegram/SourceFiles/dialogs/dialogs_layout.h | 3 +- 6 files changed, 264 insertions(+), 89 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 959923c05..16fcdae1a 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -641,6 +641,43 @@ void ApiWrap::requestDialogEntry( }).send(); } +void ApiWrap::requestDialogEntries( + std::vector> histories) { + const auto already = [&](not_null history) { + const auto [i, ok] = _dialogRequests.try_emplace(history); + return !ok; + }; + histories.erase(ranges::remove_if(histories, already), end(histories)); + if (histories.empty()) { + return; + } + auto peers = QVector(); + peers.reserve(histories.size()); + for (const auto history : histories) { + peers.push_back(MTP_inputDialogPeer(history->peer->input)); + } + const auto finalize = [=](std::vector> histories) { + for (const auto history : histories) { + if (const auto callbacks = _dialogRequests.take(history)) { + for (const auto callback : *callbacks) { + callback(); + } + } + } + }; + request(MTPmessages_GetPeerDialogs( + MTP_vector(std::move(peers)) + )).done([=](const MTPmessages_PeerDialogs &result) { + applyPeerDialogs(result); + for (const auto history : histories) { + historyDialogEntryApplied(history); + } + finalize(histories); + }).fail([=](const RPCError &error) { + finalize(histories); + }).send(); +} + void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) { Expects(dialogs.type() == mtpc_messages_peerDialogs); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index abbd8a811..c1d331d6e 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -87,6 +87,7 @@ public: void requestDialogEntry( not_null history, Fn callback = nullptr); + void requestDialogEntries(std::vector> histories); //void applyFeedSources(const MTPDchannels_feedSources &data); // #feed //void setFeedChannels( // not_null feed, diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 908c235e8..f9b8d0565 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -451,9 +451,12 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } } + const auto showUnreadInSearchResults = uniqueSearchResults(); if (!_waitingForSearch || !_searchResults.empty()) { const auto text = _searchResults.empty() ? lang(lng_search_no_results) + : showUnreadInSearchResults + ? qsl("Search results") : lng_search_found_results( lt_count, _searchedMigratedCount + _searchedCount); @@ -489,7 +492,8 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO active, selected, paintingOther, - ms); + ms, + showUnreadInSearchResults); p.translate(0, st::dialogsRowHeight); } } @@ -674,7 +678,6 @@ void DialogsInner::paintSearchInFeed( paintSearchInFilter(p, paintUserpic, top, fullWidth, icon, text); } - void DialogsInner::activate() { } @@ -1840,28 +1843,53 @@ void DialogsInner::addAllSavedPeers() { addSavedPeersAfter(QDateTime()); } +bool DialogsInner::uniqueSearchResults() const { + return Auth().supportMode() + && _filter.startsWith('#') + && !_searchInChat; +} + +bool DialogsInner::hasHistoryInSearchResults(not_null history) const { + using Result = std::unique_ptr; + return ranges::find( + _searchResults, + history, + [](const Result &result) { return result->item()->history(); } + ) != end(_searchResults); +} + bool DialogsInner::searchReceived( const QVector &messages, DialogsSearchRequestType type, int fullCount) { + const auto uniquePeers = uniqueSearchResults(); if (type == DialogsSearchFromStart || type == DialogsSearchPeerFromStart) { clearSearchResults(false); } auto isGlobalSearch = (type == DialogsSearchFromStart || type == DialogsSearchFromOffset); auto isMigratedSearch = (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset); + auto unknownUnreadCounts = std::vector>(); TimeId lastDateFound = 0; for_const (auto message, messages) { auto msgId = idFromMessage(message); auto peerId = peerFromMessage(message); auto lastDate = dateFromMessage(message); - if (auto peer = App::peerLoaded(peerId)) { + if (const auto peer = App::peerLoaded(peerId)) { if (lastDate) { - auto item = App::histories().addNewMessage(message, NewMessageExisting); - _searchResults.push_back( - std::make_unique( - _searchInChat, - item)); + const auto item = App::histories().addNewMessage( + message, + NewMessageExisting); + const auto history = item->history(); + if (!uniquePeers || !hasHistoryInSearchResults(history)) { + _searchResults.push_back( + std::make_unique( + _searchInChat, + item)); + if (uniquePeers && !history->unreadCountKnown()) { + unknownUnreadCounts.push_back(history); + } + } lastDateFound = lastDate; if (isGlobalSearch) { _lastSearchDate = lastDateFound; @@ -1891,7 +1919,12 @@ bool DialogsInner::searchReceived( || type == DialogsSearchMigratedFromOffset)) { _waitingForSearch = false; } + refresh(); + + if (!unknownUnreadCounts.empty()) { + Auth().api().requestDialogEntries(std::move(unknownUnreadCounts)); + } return lastDateFound != 0; } @@ -2476,7 +2509,9 @@ bool DialogsInner::chooseRow() { if (const auto history = chosen.key.history()) { App::main()->choosePeer( history->peer->id, - chosen.message.fullId.msg); + (uniqueSearchResults() + ? ShowAtUnreadMsgId + : chosen.message.fullId.msg)); } else if (const auto feed = chosen.key.feed()) { _controller->showSection( HistoryFeed::Memento(feed, chosen.message), diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index bb615bd43..48dcdd9bb 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -189,6 +189,8 @@ private: void handlePeerNameChange( not_null peer, const base::flat_set &oldLetters); + bool uniqueSearchResults() const; + bool hasHistoryInSearchResults(not_null history) const; void applyDialog(const MTPDdialog &dialog); // void applyFeedDialog(const MTPDdialogFeed &dialog); // #feed diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index da2815157..e89768168 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -52,6 +52,102 @@ void paintRowDate(Painter &p, QDateTime date, QRect &rectForName, bool active, b paintRowTopRight(p, dt, rectForName, active, selected); } +void PaintNarrowCounter( + Painter &p, + bool displayUnreadCounter, + bool displayUnreadMark, + bool displayMentionBadge, + int unreadCount, + bool active, + bool unreadMuted) { + auto skipBeforeMention = 0; + if (displayUnreadCounter || displayUnreadMark) { + auto counter = (unreadCount > 0) + ? QString::number(unreadCount) + : QString(); + const auto allowDigits = displayMentionBadge ? 1 : 3; + if (counter.size() > allowDigits + 1) { + counter = qsl("..") + counter.mid(counter.size() - allowDigits); + } + auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize; + auto unreadTop = st::dialogsPadding.y() + st::dialogsPhotoSize - st::dialogsUnreadHeight; + auto unreadWidth = 0; + + UnreadBadgeStyle st; + st.active = active; + st.muted = unreadMuted; + paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); + skipBeforeMention += unreadWidth + st.padding; + } + if (displayMentionBadge) { + auto counter = qsl("@"); + auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize - skipBeforeMention; + auto unreadTop = st::dialogsPadding.y() + st::dialogsPhotoSize - st::dialogsUnreadHeight; + auto unreadWidth = 0; + + UnreadBadgeStyle st; + st.active = active; + st.muted = false; + st.padding = 0; + st.textTop = 0; + paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); + } +} + +int PaintWideCounter( + Painter &p, + int texttop, + int availableWidth, + int fullWidth, + bool displayUnreadCounter, + bool displayUnreadMark, + bool displayMentionBadge, + bool displayPinnedIcon, + int unreadCount, + bool active, + bool selected, + bool unreadMuted) { + const auto initial = availableWidth; + auto hadOneBadge = false; + if (displayUnreadCounter || displayUnreadMark) { + auto counter = (unreadCount > 0) + ? QString::number(unreadCount) + : QString(); + auto unreadRight = fullWidth - st::dialogsPadding.x(); + auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; + auto unreadWidth = 0; + + UnreadBadgeStyle st; + st.active = active; + st.muted = unreadMuted; + paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); + availableWidth -= unreadWidth + st.padding; + + hadOneBadge = true; + } else if (displayPinnedIcon) { + auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon)); + icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth); + availableWidth -= icon.width() + st::dialogsUnreadPadding; + + hadOneBadge = true; + } + if (displayMentionBadge) { + auto counter = qsl("@"); + auto unreadRight = fullWidth - st::dialogsPadding.x() - (initial - availableWidth); + auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; + auto unreadWidth = 0; + + UnreadBadgeStyle st; + st.active = active; + st.muted = false; + st.padding = 0; + st.textTop = 0; + paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); + availableWidth -= unreadWidth + st.padding + (hadOneBadge ? st::dialogsUnreadPadding : 0); + } + return availableWidth; +} + enum class Flag { Active = 0x01, Selected = 0x02, @@ -370,7 +466,13 @@ UnreadBadgeStyle::UnreadBadgeStyle() , font(st::dialogsUnreadFont) { } -void paintUnreadCount(Painter &p, const QString &text, int x, int y, const UnreadBadgeStyle &st, int *outUnreadWidth) { +void paintUnreadCount( + Painter &p, + const QString &text, + int x, + int y, + const UnreadBadgeStyle &st, + int *outUnreadWidth) { int unreadWidth = st.font->width(text); int unreadRectWidth = unreadWidth + 2 * st.padding; int unreadRectHeight = st.size; @@ -464,45 +566,22 @@ void RowPainter::paint( | (onlyBackground ? Flag::OnlyBackground : Flag(0)) | (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0)); const auto paintItemCallback = [&](int nameleft, int namewidth) { - auto availableWidth = namewidth; - auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; - auto hadOneBadge = false; - if (displayUnreadCounter || displayUnreadMark) { - auto counter = (unreadCount > 0) - ? QString::number(unreadCount) - : QString(); - auto unreadRight = fullWidth - st::dialogsPadding.x(); - auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; - auto unreadWidth = 0; - - UnreadBadgeStyle st; - st.active = active; - st.muted = unreadMuted; - paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); - availableWidth -= unreadWidth + st.padding; - - hadOneBadge = true; - } else if (displayPinnedIcon) { - auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon)); - icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth); - availableWidth -= icon.width() + st::dialogsUnreadPadding; - - hadOneBadge = true; - } - if (displayMentionBadge) { - auto counter = qsl("@"); - auto unreadRight = fullWidth - st::dialogsPadding.x() - (namewidth - availableWidth); - auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; - auto unreadWidth = 0; - - UnreadBadgeStyle st; - st.active = active; - st.muted = false; - st.padding = 0; - st.textTop = 0; - paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); - availableWidth -= unreadWidth + st.padding + (hadOneBadge ? st::dialogsUnreadPadding : 0); - } + const auto texttop = st::dialogsPadding.y() + + st::msgNameFont->height + + st::dialogsSkip; + const auto availableWidth = PaintWideCounter( + p, + texttop, + namewidth, + fullWidth, + displayUnreadCounter, + displayUnreadMark, + displayMentionBadge, + displayPinnedIcon, + unreadCount, + active, + selected, + unreadMuted); const auto &color = active ? st::dialogsTextFgServiceActive : (selected @@ -517,7 +596,7 @@ void RowPainter::paint( color, ms) : false; if (!actionWasPainted) { - auto itemRect = QRect( + const auto itemRect = QRect( nameleft, texttop, availableWidth, @@ -533,39 +612,14 @@ void RowPainter::paint( } }; const auto paintCounterCallback = [&] { - auto hadOneBadge = false; - auto skipBeforeMention = 0; - if (displayUnreadCounter || displayUnreadMark) { - auto counter = (unreadCount > 0) - ? QString::number(unreadCount) - : QString(); - const auto allowDigits = displayMentionBadge ? 1 : 3; - if (counter.size() > allowDigits + 1) { - counter = qsl("..") + counter.mid(counter.size() - allowDigits); - } - auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize; - auto unreadTop = st::dialogsPadding.y() + st::dialogsPhotoSize - st::dialogsUnreadHeight; - auto unreadWidth = 0; - - UnreadBadgeStyle st; - st.active = active; - st.muted = unreadMuted; - paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); - skipBeforeMention += unreadWidth + st.padding; - } - if (displayMentionBadge) { - auto counter = qsl("@"); - auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize - skipBeforeMention; - auto unreadTop = st::dialogsPadding.y() + st::dialogsPhotoSize - st::dialogsUnreadHeight; - auto unreadWidth = 0; - - UnreadBadgeStyle st; - st.active = active; - st.muted = false; - st.padding = 0; - st.textTop = 0; - paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); - } + PaintNarrowCounter( + p, + displayUnreadCounter, + displayUnreadMark, + displayMentionBadge, + unreadCount, + active, + unreadMuted); }; paintRow( p, @@ -590,7 +644,8 @@ void RowPainter::paint( bool active, bool selected, bool onlyBackground, - TimeMs ms) { + TimeMs ms, + bool displayUnreadInfo) { auto item = row->item(); auto history = item->history(); auto cloudDraft = nullptr; @@ -618,19 +673,63 @@ void RowPainter::paint( } return HistoryItem::DrawInDialog::Normal; }(); + + const auto unreadCount = displayUnreadInfo + ? history->chatListUnreadCount() + : 0; + const auto unreadMark = displayUnreadInfo + && history->chatListUnreadMark(); + const auto unreadMuted = history->chatListMutedBadge(); + const auto displayMentionBadge = displayUnreadInfo + && history->hasUnreadMentions(); + const auto displayUnreadCounter = (unreadCount > 0); + const auto displayUnreadMark = !displayUnreadCounter + && !displayMentionBadge + && unreadMark; + const auto displayPinnedIcon = false; + const auto paintItemCallback = [&](int nameleft, int namewidth) { - auto lastWidth = namewidth; - auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; + const auto texttop = st::dialogsPadding.y() + + st::msgNameFont->height + + st::dialogsSkip; + const auto availableWidth = PaintWideCounter( + p, + texttop, + namewidth, + fullWidth, + displayUnreadCounter, + displayUnreadMark, + displayMentionBadge, + displayPinnedIcon, + unreadCount, + active, + selected, + unreadMuted); + + const auto itemRect = QRect( + nameleft, + texttop, + availableWidth, + st::dialogsTextFont->height); item->drawInDialog( p, - QRect(nameleft, texttop, lastWidth, st::dialogsTextFont->height), + itemRect, active, selected, drawInDialogWay, row->_cacheFor, row->_cache); }; - const auto paintCounterCallback = [] {}; + const auto paintCounterCallback = [&] { + PaintNarrowCounter( + p, + displayUnreadCounter, + displayUnreadMark, + displayMentionBadge, + unreadCount, + active, + unreadMuted); + }; const auto showSavedMessages = history->peer->isSelf() && !row->searchInChat(); const auto flags = (active ? Flag::Active : Flag(0)) diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index dc67d6d73..b75b22d55 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -44,7 +44,8 @@ public: bool active, bool selected, bool onlyBackground, - TimeMs ms); + TimeMs ms, + bool displayUnreadInfo); static QRect sendActionAnimationRect( int animationWidth, int animationHeight,