Implement jump-to-date in topics.

This commit is contained in:
John Preston 2022-10-27 11:14:49 +04:00
parent ee8f997c14
commit 5d76415a5d
7 changed files with 182 additions and 59 deletions

View File

@ -2834,13 +2834,16 @@ void ApiWrap::resolveJumpToDate(
const QDate &date,
Fn<void(not_null<PeerData*>, 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 <typename Callback>
void ApiWrap::requestMessageAfterDate(
not_null<PeerData*> 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>(callback)
](const MTPmessages_Messages &result) {
auto getMessagesList = [&]() -> const QVector<MTPMessage>* {
auto handleMessages = [&](auto &messages) {
auto send = [&](auto &&serialized) {
request(std::move(serialized)).done([
=,
callback = std::forward<Callback>(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<MTPMessage>*)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<PeerData*> peer,
MsgId topicRootId,
const QDate &date,
Fn<void(not_null<PeerData*>, 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();
}

View File

@ -454,11 +454,13 @@ private:
void resolveJumpToHistoryDate(
not_null<PeerData*> peer,
MsgId topicRootId,
const QDate &date,
Fn<void(not_null<PeerData*>, MsgId)> callback);
template <typename Callback>
void requestMessageAfterDate(
not_null<PeerData*> peer,
MsgId topicRootId,
const QDate &date,
Callback &&callback);

View File

@ -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);

View File

@ -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<Data::ForumTopic*> 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();
}

View File

@ -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<Ui::IconButton>(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;

View File

@ -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<QString> _searchQuery;
rpl::event_stream<> _searchCancelled;
rpl::event_stream<> _searchSubmitted;
rpl::event_stream<> _jumpToDateRequests;
object_ptr<Ui::IconButton> _back;
object_ptr<Ui::IconButton> _cancelChoose;

View File

@ -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<PeerData*> 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::CalendarBox>(Ui::CalendarBoxArgs{
.month = highlighted,