Search from a user inside forum / topic.

This commit is contained in:
John Preston 2022-10-27 13:22:31 +04:00
parent 46ebbdb547
commit da1e784803
6 changed files with 160 additions and 71 deletions

View File

@ -386,16 +386,20 @@ int InnerWidget::searchedOffset() const {
result += (_peerSearchResults.size() * st::dialogsRowHeight)
+ st::searchedBarHeight;
}
if (_searchInChat) {
result += searchInChatSkip();
}
result += searchInChatSkip();
return result;
}
int InnerWidget::searchInChatSkip() const {
auto result = st::searchedBarHeight + st::dialogsSearchInHeight;
auto result = 0;
if (_searchInChat) {
result += st::searchedBarHeight + st::dialogsSearchInHeight;
}
if (_searchFromPeer) {
result += st::lineWidth + st::dialogsSearchInHeight;
if (_searchInChat) {
result += st::lineWidth;
}
result += st::dialogsSearchInHeight;
}
return result;
}
@ -674,7 +678,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
}
}
if (_searchInChat) {
if (_searchInChat || _searchFromPeer) {
paintSearchInChat(p, {
.st = &st::forumTopicRow,
.now = ms,
@ -936,37 +940,40 @@ void InnerWidget::paintSearchInChat(
const Ui::PaintContext &context) const {
auto height = searchInChatSkip();
auto top = st::searchedBarHeight;
p.fillRect(0, 0, width(), top, st::searchedBarBg);
auto top = 0;
p.setFont(st::searchedBarFont);
p.setPen(st::searchedBarFg);
p.drawTextLeft(st::searchedBarPosition.x(), st::searchedBarPosition.y(), width(), tr::lng_dlg_search_in(tr::now));
if (_searchInChat) {
top += st::searchedBarHeight;
p.fillRect(0, 0, width(), top, st::searchedBarBg);
p.setPen(st::searchedBarFg);
p.drawTextLeft(st::searchedBarPosition.x(), st::searchedBarPosition.y(), width(), tr::lng_dlg_search_in(tr::now));
}
auto fullRect = QRect(0, top, width(), height - top);
p.fillRect(fullRect, st::dialogsBg);
if (_searchFromPeer) {
p.fillRect(QRect(0, top + st::dialogsSearchInHeight, width(), st::lineWidth), st::shadowFg);
}
p.setPen(st::dialogsNameFg);
if (const auto topic = _searchInChat.topic()) {
paintSearchInTopic(p, context, topic, _searchInChatUserpic, top, _searchInChatText);
} else if (const auto peer = _searchInChat.peer()) {
if (peer->isSelf()) {
paintSearchInSaved(p, top, _searchInChatText);
} else if (peer->isRepliesChat()) {
paintSearchInReplies(p, top, _searchInChatText);
} else {
paintSearchInPeer(p, peer, _searchInChatUserpic, top, _searchInChatText);
if (_searchInChat) {
if (_searchFromPeer) {
p.fillRect(QRect(0, top + st::dialogsSearchInHeight, width(), st::lineWidth), st::shadowFg);
}
p.setPen(st::dialogsNameFg);
if (const auto topic = _searchInChat.topic()) {
paintSearchInTopic(p, context, topic, _searchInChatUserpic, top, _searchInChatText);
} else if (const auto peer = _searchInChat.peer()) {
if (peer->isSelf()) {
paintSearchInSaved(p, top, _searchInChatText);
} else if (peer->isRepliesChat()) {
paintSearchInReplies(p, top, _searchInChatText);
} else {
paintSearchInPeer(p, peer, _searchInChatUserpic, top, _searchInChatText);
}
} else {
Unexpected("Empty Key in paintSearchInChat.");
}
} else {
Unexpected("Empty Key in paintSearchInChat.");
}
if (const auto from = _searchFromPeer) {
top += st::dialogsSearchInHeight + st::lineWidth;
}
if (_searchFromPeer) {
p.setPen(st::dialogsTextFg);
p.setTextPalette(st::dialogsSearchFromPalette);
paintSearchInPeer(p, from, _searchFromUserUserpic, top, _searchFromUserText);
paintSearchInPeer(p, _searchFromPeer, _searchFromUserUserpic, top, _searchFromUserText);
p.restoreTextPalette();
}
}
@ -1561,11 +1568,16 @@ void InnerWidget::setSearchedPressed(int pressed) {
void InnerWidget::resizeEvent(QResizeEvent *e) {
resizeEmptyLabel();
moveCancelSearchButtons();
}
void InnerWidget::moveCancelSearchButtons() {
const auto widthForCancelButton = qMax(width(), st::columnMinimalWidthLeft);
const auto left = widthForCancelButton - st::dialogsSearchInSkip - _cancelSearchInChat->width();
const auto top = (st::dialogsSearchInHeight - st::dialogsCancelSearchInPeer.height) / 2;
_cancelSearchInChat->moveToLeft(left, st::searchedBarHeight + top);
_cancelSearchFromUser->moveToLeft(left, st::searchedBarHeight + st::dialogsSearchInHeight + st::lineWidth + top);
const auto skip = _searchInChat ? (st::searchedBarHeight + st::dialogsSearchInHeight + st::lineWidth) : 0;
_cancelSearchFromUser->moveToLeft(left, skip + top);
}
void InnerWidget::dialogRowReplaced(
@ -2034,7 +2046,7 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) {
begin(results),
end(results));
};
if (!_searchInChat && !words.isEmpty()) {
if (!_searchInChat && !_searchFromPeer && !words.isEmpty()) {
if (_openedForum) {
append(_openedForum->topicsList()->indexed());
} else {
@ -2599,7 +2611,6 @@ void InnerWidget::searchInChat(Key key, PeerData *from) {
_controller->closeFolder();
onHashtagFilterUpdate(QStringView());
_cancelSearchInChat->show();
refreshSearchInChatLabel();
} else {
_cancelSearchInChat->hide();
}
@ -2610,12 +2621,16 @@ void InnerWidget::searchInChat(Key key, PeerData *from) {
_cancelSearchFromUser->hide();
_searchFromUserUserpic = nullptr;
}
if (_searchInChat || _searchFromPeer) {
refreshSearchInChatLabel();
}
if (const auto peer = _searchInChat.peer()) {
_searchInChatUserpic = peer->createUserpicView();
} else {
_searchInChatUserpic = nullptr;
}
moveCancelSearchButtons();
_controller->dialogsListDisplayForced().set(
_searchInChat || !_filter.isEmpty(),

View File

@ -354,6 +354,7 @@ private:
void savePinnedOrder();
bool pinnedShiftAnimationCallback(crl::time now);
void handleChatListEntryRefreshes();
void moveCancelSearchButtons();
const not_null<Window::SessionController*> _controller;

View File

@ -76,10 +76,6 @@ namespace {
constexpr auto kSearchPerPage = 50;
[[nodiscard]] QString SwitchToChooseFromQuery() {
return u"from:"_q;
}
} // namespace
class Widget::BottomButton : public Ui::RippleButton {
@ -276,7 +272,9 @@ Widget::Widget(
}, lifetime());
_inner->cancelSearchFromUserRequests(
) | rpl::start_with_next([=] {
setSearchInChat(_searchInChat, nullptr);
setSearchInChat((_openedForum && !_searchInChat)
? Key(_openedForum->forum()->history())
: _searchInChat, nullptr);
applyFilterUpdate(true);
}, lifetime());
_inner->chosenRow(
@ -783,6 +781,10 @@ void Widget::refreshTopBars() {
) | rpl::start_with_next([=] {
showCalendar();
}, _subsectionTopBar->lifetime());
_subsectionTopBar->chooseFromUserRequest(
) | rpl::start_with_next([=] {
showSearchFrom();
}, _subsectionTopBar->lifetime());
updateControlsGeometry();
}
const auto history = _openedForum
@ -796,7 +798,7 @@ void Widget::refreshTopBars() {
.section = Dialogs::EntryState::Section::ChatsList,
}, history ? history->sendActionPainter().get() : nullptr);
if (_forumSearchRequested) {
_subsectionTopBar->toggleSearch(true, anim::type::instant);
showSearchInTopBar(anim::type::instant);
}
} else if (_subsectionTopBar) {
if (_subsectionTopBar->searchHasFocus()) {
@ -872,6 +874,15 @@ void Widget::refreshTopBars() {
}
}
void Widget::showSearchInTopBar(anim::type animated) {
Expects(_subsectionTopBar != nullptr);
_subsectionTopBar->toggleSearch(true, animated);
_subsectionTopBar->searchEnableChooseFromUser(
true,
!_searchFromAuthor);
}
QPixmap Widget::grabForFolderSlideAnimation() {
const auto hidden = _scrollToTop->isHidden();
if (!hidden) {
@ -1289,6 +1300,7 @@ bool Widget::searchMessages(bool searchCache) {
_peerSearchQueries.emplace(_peerSearchRequest, _peerSearchQuery);
}
} else {
_api.request(base::take(_peerSearchRequest)).cancel();
_peerSearchQuery = query;
_peerSearchFull = true;
peerSearchReceived(
@ -1310,6 +1322,7 @@ bool Widget::searchMessages(bool searchCache) {
searchTopics();
}
} else {
_api.request(base::take(_topicSearchRequest)).cancel();
_topicSearchQuery = query;
_topicSearchFull = true;
}
@ -1318,6 +1331,7 @@ bool Widget::searchMessages(bool searchCache) {
bool Widget::searchForPeersRequired(const QString &query) const {
return !_searchInChat
&& !_searchFromAuthor
&& !_openedForum
&& !query.isEmpty()
&& (query[0] != '#');
@ -1325,6 +1339,7 @@ bool Widget::searchForPeersRequired(const QString &query) const {
bool Widget::searchForTopicsRequired(const QString &query) const {
return !_searchInChat
&& !_searchFromAuthor
&& _openedForum
&& !query.isEmpty()
&& (query[0] != '#')
@ -1842,7 +1857,7 @@ void Widget::applyFilterUpdate(bool force) {
}
if (_chooseFromUser->toggled() || _searchFromAuthor) {
auto switchToChooseFrom = SwitchToChooseFromQuery();
auto switchToChooseFrom = HistoryView::SwitchToChooseFromQuery();
if (_lastFilterText != switchToChooseFrom
&& switchToChooseFrom.startsWith(_lastFilterText)
&& filterText == switchToChooseFrom) {
@ -1853,12 +1868,12 @@ void Widget::applyFilterUpdate(bool force) {
}
void Widget::searchInChat(Key chat) {
if (_openedForum && !chat.peer()->forum()) {
controller()->closeForum();
}
if (_openedFolder) {
controller()->closeFolder();
}
if (const auto topic = chat.topic()) {
controller()->openForum(topic->channel());
}
cancelSearch();
setSearchInChat(chat);
applyFilterUpdate(true);
@ -1868,38 +1883,43 @@ void Widget::setSearchInChat(Key chat, PeerData *from) {
const auto peer = chat.peer();
const auto topic = chat.topic();
const auto forum = peer ? peer->forum() : nullptr;
if (chat.folder() || (forum && !topic)) {
chat = Key();
}
const auto searchInPeerUpdated = (_searchInChat != chat);
if (searchInPeerUpdated) {
from = nullptr;
} else if (!chat && !forum) {
from = nullptr;
}
const auto searchFromUpdated = searchInPeerUpdated
|| (_searchFromAuthor != from);
_searchFromAuthor = from;
if (forum) {
if (controller()->openedForum().current() == peer) {
_subsectionTopBar->toggleSearch(true, anim::type::normal);
showSearchInTopBar(anim::type::normal);
} else {
_forumSearchRequested = true;
controller()->openForum(forum->channel());
}
}
if (chat.folder() || (forum && !topic)) {
chat = Key();
}
_searchInMigrated = nullptr;
if (peer) {
if (const auto migrateTo = peer->migrateTo()) {
return setSearchInChat(peer->owner().history(migrateTo), from);
} else if (const auto migrateFrom = peer->migrateFrom()) {
if (!topic) {
if (!forum) {
_searchInMigrated = peer->owner().history(migrateFrom);
}
}
}
const auto searchInPeerUpdated = (_searchInChat != chat);
if (searchInPeerUpdated) {
_searchInChat = chat;
from = nullptr;
controller()->searchInChat = _searchInChat;
updateJumpToDateVisibility();
} else if (!_searchInChat) {
from = nullptr;
}
if (_searchFromAuthor != from || searchInPeerUpdated) {
_searchFromAuthor = from;
if (searchFromUpdated) {
updateSearchFromVisibility();
clearSearchCache();
}
@ -1908,7 +1928,8 @@ void Widget::setSearchInChat(Key chat, PeerData *from) {
_subsectionTopBar->searchEnableJumpToDate(
_openedForum && _searchInChat);
}
if (_searchFromAuthor && _lastFilterText == SwitchToChooseFromQuery()) {
if (_searchFromAuthor
&& _lastFilterText == HistoryView::SwitchToChooseFromQuery()) {
cancelSearch();
}
_filter->setFocus();
@ -1938,14 +1959,19 @@ void Widget::showCalendar() {
void Widget::showSearchFrom() {
if (const auto peer = searchInPeer()) {
const auto chat = _openedForum
const auto weak = base::make_weak(_searchInChat.topic());
const auto chat = (!_searchInChat && _openedForum)
? Key(_openedForum->forum()->history())
: _searchInChat;
auto box = SearchFromBox(
peer,
crl::guard(this, [=](not_null<PeerData*> from) {
Ui::hideLayer();
setSearchInChat(chat, from);
if (!chat.topic()) {
setSearchInChat(chat, from);
} else if (const auto strong = weak.get()) {
setSearchInChat(strong, from);
}
applyFilterUpdate(true);
}),
crl::guard(this, [=] { _filter->setFocus(); }));

View File

@ -60,7 +60,6 @@ class InnerWidget;
enum class SearchRequestType;
class Widget final : public Window::AbstractSectionWidget {
public:
Widget(QWidget *parent, not_null<Window::SessionController*> controller);
@ -165,6 +164,7 @@ private:
void updateSearchFromVisibility(bool fast = false);
void updateControlsGeometry();
void refreshTopBars();
void showSearchInTopBar(anim::type animated);
void checkUpdateStatus();
void changeOpenedSubsection(
FnMut<void()> change,

View File

@ -82,6 +82,10 @@ struct TopBarWidget::EmojiInteractionSeenAnimation {
crl::time till = 0;
};
QString SwitchToChooseFromQuery() {
return u"from:"_q;
}
TopBarWidget::TopBarWidget(
QWidget *parent,
not_null<Window::SessionController*> controller)
@ -898,6 +902,7 @@ void TopBarWidget::updateControlsGeometry() {
_searchField.destroy();
_searchCancel.destroy();
_jumpToDate.destroy();
_chooseFromUser.destroy();
}
auto searchFieldTop = _searchField
? countSelectedButtonsTop(_searchShown.value(_searchMode ? 1. : 0.))
@ -970,12 +975,14 @@ void TopBarWidget::updateControlsGeometry() {
_searchCancel->moveToLeft(
right - _searchCancel->width(),
_searchField->y());
right -= st::dialogsCalendar.width;
if (_jumpToDate) {
_jumpToDate->moveToLeft(
right - _jumpToDate->width(),
_searchField->y());
_jumpToDate->moveToLeft(right, _searchField->y());
}
right -= st::dialogsSearchFrom.width;
if (_chooseFromUser) {
_chooseFromUser->moveToLeft(right, _searchField->y());
}
right -= _searchCancel->width();
}
_rightTaken = 0;
@ -1223,13 +1230,20 @@ bool TopBarWidget::toggleSearch(bool shown, anim::type animated) {
_searchSubmitted.fire({});
});
QObject::connect(_searchField, &Ui::InputField::changed, [=] {
const auto wasEmpty = _searchQuery.current().isEmpty();
const auto query = _searchField->getLastText();
const auto nowEmpty = query.isEmpty();
if (_jumpToDate && nowEmpty != wasEmpty) {
const auto was = _searchQuery.current();
const auto now = _searchField->getLastText();
if (_jumpToDate && was.isEmpty() != now.isEmpty()) {
updateControlsVisibility();
}
_searchQuery = query;
if (_chooseFromUser) {
auto switchToChooseFrom = SwitchToChooseFromQuery();
if (was != switchToChooseFrom
&& switchToChooseFrom.startsWith(was)
&& now == switchToChooseFrom) {
_chooseFromUserRequests.fire({});
}
}
_searchQuery = now;
});
} else {
Assert(_searchField != nullptr);
@ -1253,9 +1267,11 @@ bool TopBarWidget::toggleSearch(bool shown, anim::type animated) {
}
void TopBarWidget::searchEnableJumpToDate(bool enable) {
if (!_searchMode && !enable) {
if (!_searchMode) {
return;
} else if (enable) {
} else if (!enable) {
_jumpToDate.destroy();
} else if (!_jumpToDate) {
_jumpToDate.create(
this,
object_ptr<Ui::IconButton>(this, st::dialogsCalendar));
@ -1266,13 +1282,37 @@ void TopBarWidget::searchEnableJumpToDate(bool enable) {
) | rpl::to_empty | rpl::start_to_stream(
_jumpToDateRequests,
_jumpToDate->lifetime());
} else {
_jumpToDate.destroy();
}
updateControlsVisibility();
updateControlsGeometry();
}
void TopBarWidget::searchEnableChooseFromUser(bool enable, bool visible) {
if (!_searchMode) {
return;
} else if (!enable) {
_chooseFromUser.destroy();
} else if (!_chooseFromUser) {
_chooseFromUser.create(
this,
object_ptr<Ui::IconButton>(this, st::dialogsSearchFrom));
_chooseFromUser->toggle(visible, anim::type::instant);
_chooseFromUser->entity()->clicks(
) | rpl::to_empty | rpl::start_to_stream(
_chooseFromUserRequests,
_chooseFromUser->lifetime());
} else {
_chooseFromUser->toggle(visible, anim::type::normal);
}
auto additional = QMargins();
if (_chooseFromUser && _chooseFromUser->toggled()) {
additional.setRight(_chooseFromUser->width());
}
_searchField->setAdditionalMargins(additional);
updateControlsVisibility();
updateControlsGeometry();
}
bool TopBarWidget::searchSetFocus() {
if (!_searchMode) {
return false;

View File

@ -40,6 +40,8 @@ namespace HistoryView {
class SendActionPainter;
[[nodiscard]] QString SwitchToChooseFromQuery();
class TopBarWidget final : public Ui::RpWidget {
public:
struct SelectedState {
@ -78,6 +80,7 @@ public:
bool toggleSearch(bool shown, anim::type animated);
void searchEnableJumpToDate(bool enable);
void searchEnableChooseFromUser(bool enable, bool visible);
bool searchSetFocus();
[[nodiscard]] bool searchHasFocus() const;
[[nodiscard]] rpl::producer<> searchCancelled() const;
@ -104,6 +107,9 @@ public:
[[nodiscard]] rpl::producer<> jumpToDateRequest() const {
return _jumpToDateRequests.events();
}
[[nodiscard]] rpl::producer<> chooseFromUserRequest() const {
return _chooseFromUserRequests.events();
}
[[nodiscard]] rpl::producer<> searchRequest() const;
protected:
@ -203,6 +209,7 @@ private:
rpl::event_stream<> _searchCancelled;
rpl::event_stream<> _searchSubmitted;
rpl::event_stream<> _jumpToDateRequests;
rpl::event_stream<> _chooseFromUserRequests;
object_ptr<Ui::IconButton> _back;
object_ptr<Ui::IconButton> _cancelChoose;