diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d5920414b4..af662f42d7 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2834,13 +2834,16 @@ void ApiWrap::resolveJumpToDate( const QDate &date, Fn, MsgId)> callback) { if (const auto peer = chat.peer()) { - resolveJumpToHistoryDate(peer, date, std::move(callback)); + const auto topic = chat.topic(); + const auto rootId = topic ? topic->rootId() : 0; + resolveJumpToHistoryDate(peer, rootId, date, std::move(callback)); } } template void ApiWrap::requestMessageAfterDate( not_null peer, + MsgId topicRootId, const QDate &date, Callback &&callback) { // API returns a message with date <= offset_date. @@ -2853,75 +2856,95 @@ void ApiWrap::requestMessageAfterDate( const auto maxId = 0; const auto minId = 0; const auto historyHash = uint64(0); - request(MTPmessages_GetHistory( - peer->input, - MTP_int(offsetId), - MTP_int(offsetDate), - MTP_int(addOffset), - MTP_int(limit), - MTP_int(maxId), - MTP_int(minId), - MTP_long(historyHash) - )).done([ - =, - callback = std::forward(callback) - ](const MTPmessages_Messages &result) { - auto getMessagesList = [&]() -> const QVector* { - auto handleMessages = [&](auto &messages) { + + auto send = [&](auto &&serialized) { + request(std::move(serialized)).done([ + =, + callback = std::forward(callback) + ](const MTPmessages_Messages &result) { + const auto handleMessages = [&](auto &messages) { _session->data().processUsers(messages.vusers()); _session->data().processChats(messages.vchats()); return &messages.vmessages().v; }; - switch (result.type()) { - case mtpc_messages_messages: + const auto list = result.match([&]( + const MTPDmessages_messages &data) { return handleMessages(result.c_messages_messages()); - case mtpc_messages_messagesSlice: + }, [&](const MTPDmessages_messagesSlice &data) { return handleMessages(result.c_messages_messagesSlice()); - case mtpc_messages_channelMessages: { - auto &messages = result.c_messages_channelMessages(); + }, [&](const MTPDmessages_channelMessages &data) { + const auto &messages = result.c_messages_channelMessages(); if (peer && peer->isChannel()) { peer->asChannel()->ptsReceived(messages.vpts().v); } else { - LOG(("API Error: received messages.channelMessages when no channel was passed! (ApiWrap::jumpToDate)")); + LOG(("API Error: received messages.channelMessages when " + "no channel was passed! (ApiWrap::jumpToDate)")); } return handleMessages(messages); - } break; - case mtpc_messages_messagesNotModified: { - LOG(("API Error: received messages.messagesNotModified! (ApiWrap::jumpToDate)")); - } break; - } - return nullptr; - }; - - if (const auto list = getMessagesList()) { - _session->data().processMessages(*list, NewMessageType::Existing); - for (const auto &message : *list) { - if (DateFromMessage(message) >= offsetDate) { - callback(IdFromMessage(message)); - return; + }, [&](const MTPDmessages_messagesNotModified &) { + LOG(("API Error: received messages.messagesNotModified! " + "(ApiWrap::jumpToDate)")); + return (const QVector*)nullptr; + }); + if (list) { + _session->data().processMessages( + *list, + NewMessageType::Existing); + for (const auto &message : *list) { + if (DateFromMessage(message) >= offsetDate) { + callback(IdFromMessage(message)); + return; + } } } - } - callback(ShowAtUnreadMsgId); - }).send(); + callback(ShowAtUnreadMsgId); + }).send(); + }; + if (topicRootId) { + send(MTPmessages_GetReplies( + peer->input, + MTP_int(topicRootId), + MTP_int(offsetId), + MTP_int(offsetDate), + MTP_int(addOffset), + MTP_int(limit), + MTP_int(maxId), + MTP_int(minId), + MTP_long(historyHash))); + } else { + send(MTPmessages_GetHistory( + peer->input, + MTP_int(offsetId), + MTP_int(offsetDate), + MTP_int(addOffset), + MTP_int(limit), + MTP_int(maxId), + MTP_int(minId), + MTP_long(historyHash))); + } } void ApiWrap::resolveJumpToHistoryDate( not_null peer, + MsgId topicRootId, const QDate &date, Fn, MsgId)> callback) { if (const auto channel = peer->migrateTo()) { - return resolveJumpToHistoryDate(channel, date, std::move(callback)); + return resolveJumpToHistoryDate( + channel, + topicRootId, + date, + std::move(callback)); } const auto jumpToDateInPeer = [=] { - requestMessageAfterDate(peer, date, [=](MsgId resultId) { - callback(peer, resultId); + requestMessageAfterDate(peer, topicRootId, date, [=](MsgId itemId) { + callback(peer, itemId); }); }; - if (const auto chat = peer->migrateFrom()) { - requestMessageAfterDate(chat, date, [=](MsgId resultId) { - if (resultId) { - callback(chat, resultId); + if (const auto chat = topicRootId ? nullptr : peer->migrateFrom()) { + requestMessageAfterDate(chat, 0, date, [=](MsgId itemId) { + if (itemId) { + callback(chat, itemId); } else { jumpToDateInPeer(); } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index e56a4ebc25..5bbaabf02b 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -454,11 +454,13 @@ private: void resolveJumpToHistoryDate( not_null peer, + MsgId topicRootId, const QDate &date, Fn, MsgId)> callback); template void requestMessageAfterDate( not_null peer, + MsgId topicRootId, const QDate &date, Callback &&callback); diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index 6d143851da..43aa89c3c1 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -223,7 +223,9 @@ void Forum::applyReceivedTopics( } void Forum::requestSomeStale() { - if (_staleRequestId || (!_offset.id && _requestId)) { + if (_staleRequestId + || (!_offset.id && _requestId) + || _staleRootIds.empty()) { return; } const auto type = Histories::RequestType::History; @@ -241,6 +243,9 @@ void Forum::requestSomeStale() { break; } } + if (rootIds.empty()) { + return; + } const auto call = [=] { for (const auto &id : rootIds) { finishTopicRequest(id.v); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 826cfd7c96..0cfe0ff035 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -779,7 +779,11 @@ void Widget::refreshTopBars() { _subsectionTopBar->searchQuery( ) | rpl::start_with_next([=](QString query) { applyFilterUpdate(); - }, lifetime()); + }, _subsectionTopBar->lifetime()); + _subsectionTopBar->jumpToDateRequest( + ) | rpl::start_with_next([=] { + showCalendar(); + }, _subsectionTopBar->lifetime()); updateControlsGeometry(); } const auto history = _openedForum @@ -1386,14 +1390,16 @@ void Widget::searchTopics() { MTP_int(kSearchPerPage) )).done([=](const MTPmessages_ForumTopics &result) { _topicSearchRequest = 0; - const auto savedTopicId = _topicSearchOffsetId; + const auto savedTopicId = _topicSearchOffsetTopicId; const auto byCreation = result.data().is_order_by_create_date(); _openedForum->forum()->applyReceivedTopics(result, [&]( not_null topic) { _topicSearchOffsetTopicId = topic->rootId(); if (byCreation) { - _topicSearchOffsetId = _topicSearchOffsetTopicId; _topicSearchOffsetDate = topic->creationDate(); + if (const auto last = topic->lastServerMessage()) { + _topicSearchOffsetId = last->id; + } } else if (const auto last = topic->lastServerMessage()) { _topicSearchOffsetId = last->id; _topicSearchOffsetDate = last->date(); @@ -1899,6 +1905,10 @@ void Widget::setSearchInChat(Key chat, PeerData *from) { clearSearchCache(); } _inner->searchInChat(_searchInChat, _searchFromAuthor); + if (_subsectionTopBar) { + _subsectionTopBar->searchEnableJumpToDate( + _openedForum && _searchInChat); + } if (_searchFromAuthor && _lastFilterText == SwitchToChooseFromQuery()) { cancelSearch(); } diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index f540ace3df..0f547b4dba 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/shortcuts.h" #include "core/application.h" #include "core/core_settings.h" +#include "ui/wrap/fade_wrap.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/popup_menu.h" @@ -880,8 +881,10 @@ int TopBarWidget::countSelectedButtonsTop(float64 selectedShown) { } void TopBarWidget::updateSearchVisibility() { - const auto historyMode = (_activeChat.section == Section::History); - _search->setVisible(historyMode && !_chooseForReportReason); + const auto searchAllowedMode = (_activeChat.section == Section::History) + || (_activeChat.section == Section::Replies + && _activeChat.key.topic()); + _search->setVisible(searchAllowedMode && !_chooseForReportReason); } void TopBarWidget::updateControlsGeometry() { @@ -894,6 +897,7 @@ void TopBarWidget::updateControlsGeometry() { if (!_searchMode && !_searchShown.animating() && _searchField) { _searchField.destroy(); _searchCancel.destroy(); + _jumpToDate.destroy(); } auto searchFieldTop = _searchField ? countSelectedButtonsTop(_searchShown.value(_searchMode ? 1. : 0.)) @@ -966,6 +970,11 @@ void TopBarWidget::updateControlsGeometry() { _searchCancel->moveToLeft( right - _searchCancel->width(), _searchField->y()); + if (_jumpToDate) { + _jumpToDate->moveToLeft( + right - _jumpToDate->width(), + _searchField->y()); + } right -= _searchCancel->width(); } @@ -1054,6 +1063,19 @@ void TopBarWidget::updateControlsVisibility() { ? (_activeChat.key.topic() != nullptr) : false); updateSearchVisibility(); + if (_searchMode) { + const auto hasSearchQuery = _searchField + && !_searchField->getLastText().isEmpty(); + if (!_jumpToDate || hasSearchQuery) { + _searchCancel->show(anim::type::normal); + if (_jumpToDate) { + _jumpToDate->hide(anim::type::normal); + } + } else { + _searchCancel->hide(anim::type::normal); + _jumpToDate->show(anim::type::normal); + } + } _menuToggle->setVisible(hasMenu && !_chooseForReportReason && !_narrowMode); @@ -1201,7 +1223,13 @@ bool TopBarWidget::toggleSearch(bool shown, anim::type animated) { _searchSubmitted.fire({}); }); QObject::connect(_searchField, &Ui::InputField::changed, [=] { - _searchQuery = _searchField->getLastText(); + const auto wasEmpty = _searchQuery.current().isEmpty(); + const auto query = _searchField->getLastText(); + const auto nowEmpty = query.isEmpty(); + if (_jumpToDate && nowEmpty != wasEmpty) { + updateControlsVisibility(); + } + _searchQuery = query; }); } else { Assert(_searchField != nullptr); @@ -1224,6 +1252,27 @@ bool TopBarWidget::toggleSearch(bool shown, anim::type animated) { return true; } +void TopBarWidget::searchEnableJumpToDate(bool enable) { + if (!_searchMode && !enable) { + return; + } else if (enable) { + _jumpToDate.create( + this, + object_ptr(this, st::dialogsCalendar)); + _jumpToDate->toggle( + _searchField->getLastText().isEmpty(), + anim::type::instant); + _jumpToDate->entity()->clicks( + ) | rpl::to_empty | rpl::start_to_stream( + _jumpToDateRequests, + _jumpToDate->lifetime()); + } else { + _jumpToDate.destroy(); + } + updateControlsVisibility(); + updateControlsGeometry(); +} + bool TopBarWidget::searchSetFocus() { if (!_searchMode) { return false; diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h index 29333510bd..9ff074cf24 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -77,6 +77,7 @@ public: void clearChooseMessagesForReport(); bool toggleSearch(bool shown, anim::type animated); + void searchEnableJumpToDate(bool enable); bool searchSetFocus(); [[nodiscard]] bool searchHasFocus() const; [[nodiscard]] rpl::producer<> searchCancelled() const; @@ -100,6 +101,9 @@ public: [[nodiscard]] rpl::producer<> cancelChooseForReportRequest() const { return _cancelChooseForReport.events(); } + [[nodiscard]] rpl::producer<> jumpToDateRequest() const { + return _jumpToDateRequests.events(); + } [[nodiscard]] rpl::producer<> searchRequest() const; protected: @@ -198,6 +202,7 @@ private: rpl::variable _searchQuery; rpl::event_stream<> _searchCancelled; rpl::event_stream<> _searchSubmitted; + rpl::event_stream<> _jumpToDateRequests; object_ptr _back; object_ptr _cancelChoose; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index fbc3c3f6ce..69c4594558 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1396,12 +1396,18 @@ void SessionController::startOrJoinGroupCall( } void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { - const auto history = chat.history(); + const auto topic = chat.topic(); + const auto history = chat.owningHistory(); if (!history) { return; } const auto currentPeerDate = [&] { - if (history->scrollTopItem) { + if (topic) { + if (const auto item = topic->lastMessage()) { + return base::unixtime::parse(item->date()).date(); + } + return QDate(); + } else if (history->scrollTopItem) { return history->scrollTopItem->dateTime().date(); } else if (history->loadedAtTop() && !history->isEmpty() @@ -1419,6 +1425,12 @@ void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { return QDate(); }(); const auto maxPeerDate = [&] { + if (topic) { + if (const auto item = topic->lastMessage()) { + return base::unixtime::parse(item->date()).date(); + } + return QDate(); + } const auto check = history->peer->migrateTo() ? history->owner().historyLoaded(history->peer->migrateTo()) : history; @@ -1428,11 +1440,13 @@ void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { return QDate(); }(); const auto minPeerDate = [&] { - const auto startDate = [] { + const auto startDate = [&] { // Telegram was launched in August 2013 :) return QDate(2013, 8, 1); }; - if (const auto chat = history->peer->migrateFrom()) { + if (topic) { + return base::unixtime::parse(topic->creationDate()).date(); + } else if (const auto chat = history->peer->migrateFrom()) { if (const auto history = chat->owner().historyLoaded(chat)) { if (history->loadedAtTop()) { if (!history->isEmpty()) { @@ -1515,13 +1529,28 @@ void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { } }; const auto weak = base::make_weak(this); + const auto weakTopic = base::make_weak(topic); const auto jump = [=](const QDate &date) { const auto open = [=](not_null peer, MsgId id) { if (const auto strong = weak.get()) { - strong->showPeerHistory(peer, SectionShow::Way::Forward, id); + if (!topic) { + strong->showPeerHistory( + peer, + SectionShow::Way::Forward, + id); + } else if (const auto strongTopic = weakTopic.get()) { + strong->showRepliesForMessage( + strongTopic->history(), + strongTopic->rootId(), + id, + SectionShow::Way::Forward); + strong->hideLayer(anim::type::normal); + } } }; - session().api().resolveJumpToDate(chat, date, open); + if (!topic || weakTopic) { + session().api().resolveJumpToDate(chat, date, open); + } }; show(Box(Ui::CalendarBoxArgs{ .month = highlighted,