/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_search_calendar.h" #include "apiwrap.h" #include "base/unixtime.h" #include "data/data_document.h" #include "data/data_media_types.h" #include "data/data_peer.h" #include "data/data_search_controller.h" // PrepareSearchFilter #include "data/data_session.h" #include "history/history_item.h" #include "history/history.h" #include "main/main_session.h" #include "ui/dynamic_thumbnails.h" namespace Api { SearchCalendarController::SearchCalendarController( not_null session, PeerId peerId, Storage::SharedMediaType type) : _session(session) , _peerId(peerId) , _type(type) , _api(&session->mtp()) { } void SearchCalendarController::monthThumbnails( TimeId date, Fn)> onFinish) { const auto parsed = base::unixtime::parse(date).date(); const auto key = MonthKey{ .peerId = _peerId, .year = parsed.year(), .month = parsed.month(), }; if (const auto it = _months.find(key); it != _months.end()) { if (!it->second.cache.empty()) { onFinish(it->second.cache); return; } } _months[key].callbacks.push_back(std::move(onFinish)); if (!_months[key].requestId) { performMonthRequest(key); } } void SearchCalendarController::performMonthRequest(const MonthKey &key) { const auto peer = _session->data().peer(key.peerId); const auto filter = PrepareSearchFilter(_type); const auto parsed = QDate(key.year, key.month, 1); const auto endDate = base::unixtime::serialize(QDateTime( parsed.addMonths(1).addDays(-1), QTime(23, 59, 59))); auto &state = _months[key].state; _months[key].requestId = _api.request( MTPmessages_GetSearchResultsCalendar( MTP_flags(0), peer->input(), MTPInputPeer(), filter, MTP_int(state.offsetId), MTP_int(state.offsetDate ? state.offsetDate : endDate) )).done([=](const MTPmessages_SearchResultsCalendar &result) { _months[key].requestId = 0; const auto &data = result.data(); _session->data().processUsers(data.vusers()); _session->data().processChats(data.vchats()); _session->data().processMessages( data.vmessages(), NewMessageType::Existing); auto messageIds = std::vector(); messageIds.reserve(data.vmessages().v.size()); for (const auto &message : data.vmessages().v) { messageIds.push_back( FullMsgId(key.peerId, IdFromMessage(message))); } auto &monthState = _months[key].state; const auto prevOffsetId = monthState.offsetId; const auto prevOffsetDate = monthState.offsetDate; monthState.offsetId = data.vmin_msg_id().v; monthState.offsetDate = data.vmin_date().v; const auto noMoreData = (prevOffsetId == monthState.offsetId && prevOffsetDate == monthState.offsetDate && prevOffsetId != 0); processMonthMessages( key, messageIds, data.vmin_date().v, data.vmin_msg_id().v, noMoreData); }).fail([=] { auto &data = _months[key]; data.requestId = 0; data.cache = {}; for (const auto &callback : data.callbacks) { callback({}); } data.callbacks.clear(); }).send(); } void SearchCalendarController::processMonthMessages( const MonthKey &key, const std::vector &messages, TimeId minDate, MsgId minMsgId, bool noMoreData) { auto result = std::vector(); auto seenDays = base::flat_set(); const auto targetMonth = QDate(key.year, key.month, 1); const auto targetStart = base::unixtime::serialize( QDateTime(targetMonth, QTime())); const auto targetEnd = base::unixtime::serialize(QDateTime( targetMonth.addMonths(1).addDays(-1), QTime(23, 59, 59))); for (const auto &fullId : messages) { const auto item = _session->data().message(fullId); if (!item) { continue; } const auto date = item->date(); if (date < targetStart || date > targetEnd) { continue; } const auto parsed = base::unixtime::parse(date).date(); const auto dayStart = base::unixtime::serialize( QDateTime(parsed, QTime())); if (seenDays.contains(dayStart)) { continue; } const auto media = item->media(); if (!media) { continue; } auto image = std::shared_ptr(); if (const auto photo = media->photo()) { image = Ui::MakePhotoThumbnail(photo, item->fullId()); } else if (const auto document = media->document()) { if (document->isVideoFile()) { image = Ui::MakeDocumentThumbnail(document, item->fullId()); } } if (image) { seenDays.insert(dayStart); result.push_back(DayThumbnail{ .date = dayStart, .image = std::move(image), .msgId = fullId.msg, }); } } if (result.empty() && minDate < targetStart && !_months[key].requestId && !noMoreData) { performMonthRequest(key); } else { auto &data = _months[key]; data.cache = result; for (const auto &callback : data.callbacks) { callback(result); } data.callbacks.clear(); } } std::optional SearchCalendarController::resolveMsgIdByDate( TimeId date) const { const auto parsed = base::unixtime::parse(date).date(); const auto key = MonthKey{ .peerId = _peerId, .year = parsed.year(), .month = parsed.month(), }; const auto it = _months.find(key); if (it == _months.end() || it->second.cache.empty()) { return std::nullopt; } const auto dayStart = base::unixtime::serialize( QDateTime(parsed, QTime())); for (const auto &thumb : it->second.cache) { if (thumb.date == dayStart) { return thumb.msgId; } } return std::nullopt; } } // namespace Api