data_search_controller.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "data/data_search_controller.h"
  8. #include "main/main_session.h"
  9. #include "data/data_session.h"
  10. #include "data/data_messages.h"
  11. #include "data/data_channel.h"
  12. #include "data/data_histories.h"
  13. #include "history/history.h"
  14. #include "history/history_item.h"
  15. #include "apiwrap.h"
  16. namespace Api {
  17. namespace {
  18. constexpr auto kSharedMediaLimit = 100;
  19. constexpr auto kFirstSharedMediaLimit = 0;
  20. constexpr auto kHistoryLimit = 50;
  21. constexpr auto kDefaultSearchTimeoutMs = crl::time(200);
  22. } // namespace
  23. MTPMessagesFilter PrepareSearchFilter(Storage::SharedMediaType type) {
  24. using Type = Storage::SharedMediaType;
  25. switch (type) {
  26. case Type::Photo:
  27. return MTP_inputMessagesFilterPhotos();
  28. case Type::Video:
  29. return MTP_inputMessagesFilterVideo();
  30. case Type::PhotoVideo:
  31. return MTP_inputMessagesFilterPhotoVideo();
  32. case Type::MusicFile:
  33. return MTP_inputMessagesFilterMusic();
  34. case Type::File:
  35. return MTP_inputMessagesFilterDocument();
  36. case Type::VoiceFile:
  37. return MTP_inputMessagesFilterVoice();
  38. case Type::RoundVoiceFile:
  39. return MTP_inputMessagesFilterRoundVoice();
  40. case Type::RoundFile:
  41. return MTP_inputMessagesFilterRoundVideo();
  42. case Type::GIF:
  43. return MTP_inputMessagesFilterGif();
  44. case Type::Link:
  45. return MTP_inputMessagesFilterUrl();
  46. case Type::ChatPhoto:
  47. return MTP_inputMessagesFilterChatPhotos();
  48. case Type::Pinned:
  49. return MTP_inputMessagesFilterPinned();
  50. }
  51. return MTP_inputMessagesFilterEmpty();
  52. }
  53. std::optional<GlobalMediaRequest> PrepareGlobalMediaRequest(
  54. not_null<Main::Session*> session,
  55. int32 offsetRate,
  56. Data::MessagePosition offsetPosition,
  57. Storage::SharedMediaType type,
  58. const QString &query) {
  59. const auto filter = PrepareSearchFilter(type);
  60. if (query.isEmpty() && filter.type() == mtpc_inputMessagesFilterEmpty) {
  61. return std::nullopt;
  62. }
  63. const auto minDate = 0;
  64. const auto maxDate = 0;
  65. const auto folderId = 0;
  66. const auto limit = offsetPosition.fullId.peer
  67. ? kSharedMediaLimit
  68. : kFirstSharedMediaLimit;
  69. return MTPmessages_SearchGlobal(
  70. MTP_flags(MTPmessages_SearchGlobal::Flag::f_folder_id), // No archive
  71. MTP_int(folderId),
  72. MTP_string(query),
  73. filter,
  74. MTP_int(minDate),
  75. MTP_int(maxDate),
  76. MTP_int(offsetRate),
  77. (offsetPosition.fullId.peer
  78. ? session->data().peer(PeerId(offsetPosition.fullId.peer))->input
  79. : MTP_inputPeerEmpty()),
  80. MTP_int(offsetPosition.fullId.msg),
  81. MTP_int(limit));
  82. }
  83. GlobalMediaResult ParseGlobalMediaResult(
  84. not_null<Main::Session*> session,
  85. const MTPmessages_Messages &data) {
  86. auto result = GlobalMediaResult();
  87. auto messages = (const QVector<MTPMessage>*)nullptr;
  88. data.match([&](const MTPDmessages_messagesNotModified &) {
  89. }, [&](const auto &data) {
  90. session->data().processUsers(data.vusers());
  91. session->data().processChats(data.vchats());
  92. messages = &data.vmessages().v;
  93. });
  94. data.match([&](const MTPDmessages_messagesNotModified &) {
  95. }, [&](const MTPDmessages_messages &data) {
  96. result.fullCount = data.vmessages().v.size();
  97. }, [&](const MTPDmessages_messagesSlice &data) {
  98. result.fullCount = data.vcount().v;
  99. result.offsetRate = data.vnext_rate().value_or_empty();
  100. }, [&](const MTPDmessages_channelMessages &data) {
  101. result.fullCount = data.vcount().v;
  102. });
  103. data.match([&](const MTPDmessages_channelMessages &data) {
  104. LOG(("API Error: received messages.channelMessages when "
  105. "no channel was passed! (ParseSearchResult)"));
  106. }, [](const auto &) {});
  107. const auto addType = NewMessageType::Existing;
  108. result.messageIds.reserve(messages->size());
  109. for (const auto &message : *messages) {
  110. const auto item = session->data().addNewMessage(
  111. message,
  112. MessageFlags(),
  113. addType);
  114. if (item) {
  115. result.messageIds.push_back(item->position());
  116. }
  117. }
  118. return result;
  119. }
  120. std::optional<SearchRequest> PrepareSearchRequest(
  121. not_null<PeerData*> peer,
  122. MsgId topicRootId,
  123. Storage::SharedMediaType type,
  124. const QString &query,
  125. MsgId messageId,
  126. Data::LoadDirection direction) {
  127. const auto filter = PrepareSearchFilter(type);
  128. if (query.isEmpty() && filter.type() == mtpc_inputMessagesFilterEmpty) {
  129. return std::nullopt;
  130. }
  131. const auto minId = 0;
  132. const auto maxId = 0;
  133. const auto limit = messageId ? kSharedMediaLimit : kFirstSharedMediaLimit;
  134. const auto offsetId = [&] {
  135. switch (direction) {
  136. case Data::LoadDirection::Before:
  137. case Data::LoadDirection::Around: return messageId;
  138. case Data::LoadDirection::After: return messageId + 1;
  139. }
  140. Unexpected("Direction in PrepareSearchRequest");
  141. }();
  142. const auto addOffset = [&] {
  143. switch (direction) {
  144. case Data::LoadDirection::Before: return 0;
  145. case Data::LoadDirection::Around: return -limit / 2;
  146. case Data::LoadDirection::After: return -limit;
  147. }
  148. Unexpected("Direction in PrepareSearchRequest");
  149. }();
  150. const auto hash = uint64(0);
  151. const auto mtpOffsetId = int(std::clamp(
  152. offsetId.bare,
  153. int64(0),
  154. int64(0x3FFFFFFF)));
  155. using Flag = MTPmessages_Search::Flag;
  156. return MTPmessages_Search(
  157. MTP_flags(topicRootId ? Flag::f_top_msg_id : Flag(0)),
  158. peer->input,
  159. MTP_string(query),
  160. MTP_inputPeerEmpty(),
  161. MTPInputPeer(), // saved_peer_id
  162. MTPVector<MTPReaction>(), // saved_reaction
  163. MTP_int(topicRootId),
  164. filter,
  165. MTP_int(0), // min_date
  166. MTP_int(0), // max_date
  167. MTP_int(mtpOffsetId),
  168. MTP_int(addOffset),
  169. MTP_int(limit),
  170. MTP_int(maxId),
  171. MTP_int(minId),
  172. MTP_long(hash));
  173. }
  174. SearchResult ParseSearchResult(
  175. not_null<PeerData*> peer,
  176. Storage::SharedMediaType type,
  177. MsgId messageId,
  178. Data::LoadDirection direction,
  179. const SearchRequestResult &data) {
  180. auto result = SearchResult();
  181. result.noSkipRange = MsgRange{ messageId, messageId };
  182. auto messages = [&] {
  183. switch (data.type()) {
  184. case mtpc_messages_messages: {
  185. auto &d = data.c_messages_messages();
  186. peer->owner().processUsers(d.vusers());
  187. peer->owner().processChats(d.vchats());
  188. result.fullCount = d.vmessages().v.size();
  189. return &d.vmessages().v;
  190. } break;
  191. case mtpc_messages_messagesSlice: {
  192. auto &d = data.c_messages_messagesSlice();
  193. peer->owner().processUsers(d.vusers());
  194. peer->owner().processChats(d.vchats());
  195. result.fullCount = d.vcount().v;
  196. return &d.vmessages().v;
  197. } break;
  198. case mtpc_messages_channelMessages: {
  199. const auto &d = data.c_messages_channelMessages();
  200. if (const auto channel = peer->asChannel()) {
  201. channel->ptsReceived(d.vpts().v);
  202. channel->processTopics(d.vtopics());
  203. } else {
  204. LOG(("API Error: received messages.channelMessages when "
  205. "no channel was passed! (ParseSearchResult)"));
  206. }
  207. peer->owner().processUsers(d.vusers());
  208. peer->owner().processChats(d.vchats());
  209. result.fullCount = d.vcount().v;
  210. return &d.vmessages().v;
  211. } break;
  212. case mtpc_messages_messagesNotModified: {
  213. LOG(("API Error: received messages.messagesNotModified! "
  214. "(ParseSearchResult)"));
  215. return (const QVector<MTPMessage>*)nullptr;
  216. } break;
  217. }
  218. Unexpected("messages.Messages type in ParseSearchResult()");
  219. }();
  220. if (!messages) {
  221. return result;
  222. }
  223. const auto addType = NewMessageType::Existing;
  224. result.messageIds.reserve(messages->size());
  225. for (const auto &message : *messages) {
  226. const auto item = peer->owner().addNewMessage(
  227. message,
  228. MessageFlags(),
  229. addType);
  230. if (item) {
  231. const auto itemId = item->id;
  232. if ((type == Storage::SharedMediaType::kCount)
  233. || item->sharedMediaTypes().test(type)) {
  234. result.messageIds.push_back(itemId);
  235. }
  236. accumulate_min(result.noSkipRange.from, itemId);
  237. accumulate_max(result.noSkipRange.till, itemId);
  238. }
  239. }
  240. if (messageId && result.messageIds.empty()) {
  241. result.noSkipRange = [&]() -> MsgRange {
  242. switch (direction) {
  243. case Data::LoadDirection::Before: // All old loaded.
  244. return { 0, result.noSkipRange.till };
  245. case Data::LoadDirection::Around: // All loaded.
  246. return { 0, ServerMaxMsgId };
  247. case Data::LoadDirection::After: // All new loaded.
  248. return { result.noSkipRange.from, ServerMaxMsgId };
  249. }
  250. Unexpected("Direction in ParseSearchResult");
  251. }();
  252. }
  253. return result;
  254. }
  255. HistoryRequest PrepareHistoryRequest(
  256. not_null<PeerData*> peer,
  257. MsgId messageId,
  258. Data::LoadDirection direction) {
  259. const auto minId = 0;
  260. const auto maxId = 0;
  261. const auto limit = kHistoryLimit;
  262. const auto offsetId = [&] {
  263. switch (direction) {
  264. case Data::LoadDirection::Before:
  265. case Data::LoadDirection::Around: return messageId;
  266. case Data::LoadDirection::After: return messageId + 1;
  267. }
  268. Unexpected("Direction in PrepareSearchRequest");
  269. }();
  270. const auto addOffset = [&] {
  271. switch (direction) {
  272. case Data::LoadDirection::Before: return 0;
  273. case Data::LoadDirection::Around: return -limit / 2;
  274. case Data::LoadDirection::After: return -limit;
  275. }
  276. Unexpected("Direction in PrepareSearchRequest");
  277. }();
  278. const auto hash = uint64(0);
  279. const auto offsetDate = int32(0);
  280. const auto mtpOffsetId = int(std::clamp(
  281. offsetId.bare,
  282. int64(0),
  283. int64(0x3FFFFFFF)));
  284. return MTPmessages_GetHistory(
  285. peer->input,
  286. MTP_int(mtpOffsetId),
  287. MTP_int(offsetDate),
  288. MTP_int(addOffset),
  289. MTP_int(limit),
  290. MTP_int(maxId),
  291. MTP_int(minId),
  292. MTP_long(hash));
  293. }
  294. HistoryResult ParseHistoryResult(
  295. not_null<PeerData*> peer,
  296. MsgId messageId,
  297. Data::LoadDirection direction,
  298. const HistoryRequestResult &data) {
  299. return ParseSearchResult(
  300. peer,
  301. Storage::SharedMediaType::kCount,
  302. messageId,
  303. direction,
  304. data);
  305. }
  306. SearchController::CacheEntry::CacheEntry(
  307. not_null<Main::Session*> session,
  308. const Query &query)
  309. : peerData(session->data().peer(query.peerId))
  310. , migratedData(query.migratedPeerId
  311. ? base::make_optional(Data(session->data().peer(query.migratedPeerId)))
  312. : std::nullopt) {
  313. }
  314. SearchController::SearchController(not_null<Main::Session*> session)
  315. : _session(session) {
  316. }
  317. bool SearchController::hasInCache(const Query &query) const {
  318. return query.query.isEmpty() || _cache.contains(query);
  319. }
  320. void SearchController::setQuery(const Query &query) {
  321. if (query.query.isEmpty()) {
  322. _cache.clear();
  323. _current = _cache.end();
  324. } else {
  325. _current = _cache.find(query);
  326. }
  327. if (_current == _cache.end()) {
  328. _current = _cache.emplace(
  329. query,
  330. std::make_unique<CacheEntry>(_session, query)).first;
  331. }
  332. }
  333. rpl::producer<SparseIdsMergedSlice> SearchController::idsSlice(
  334. SparseIdsMergedSlice::UniversalMsgId aroundId,
  335. int limitBefore,
  336. int limitAfter) {
  337. Expects(_current != _cache.cend());
  338. auto query = (const Query&)_current->first;
  339. auto createSimpleViewer = [=](
  340. PeerId peerId,
  341. MsgId topicRootId,
  342. SparseIdsSlice::Key simpleKey,
  343. int limitBefore,
  344. int limitAfter) {
  345. return simpleIdsSlice(
  346. peerId,
  347. topicRootId,
  348. simpleKey,
  349. query,
  350. limitBefore,
  351. limitAfter);
  352. };
  353. return SparseIdsMergedSlice::CreateViewer(
  354. SparseIdsMergedSlice::Key(
  355. query.peerId,
  356. query.topicRootId,
  357. query.migratedPeerId,
  358. aroundId),
  359. limitBefore,
  360. limitAfter,
  361. std::move(createSimpleViewer));
  362. }
  363. rpl::producer<SparseIdsSlice> SearchController::simpleIdsSlice(
  364. PeerId peerId,
  365. MsgId topicRootId,
  366. MsgId aroundId,
  367. const Query &query,
  368. int limitBefore,
  369. int limitAfter) {
  370. Expects(peerId != 0);
  371. Expects(IsServerMsgId(aroundId) || (aroundId == 0));
  372. Expects((aroundId != 0)
  373. || (limitBefore == 0 && limitAfter == 0));
  374. Expects((query.peerId == peerId && query.topicRootId == topicRootId)
  375. || (query.migratedPeerId == peerId && MsgId(0) == topicRootId));
  376. auto it = _cache.find(query);
  377. if (it == _cache.end()) {
  378. return [=](auto) { return rpl::lifetime(); };
  379. }
  380. auto listData = (peerId == query.peerId)
  381. ? &it->second->peerData
  382. : &*it->second->migratedData;
  383. return [=](auto consumer) {
  384. auto lifetime = rpl::lifetime();
  385. auto builder = lifetime.make_state<SparseIdsSliceBuilder>(
  386. aroundId,
  387. limitBefore,
  388. limitAfter);
  389. builder->insufficientAround(
  390. ) | rpl::start_with_next([=](
  391. const SparseIdsSliceBuilder::AroundData &data) {
  392. requestMore(data, query, listData);
  393. }, lifetime);
  394. auto pushNextSnapshot = [=] {
  395. consumer.put_next(builder->snapshot());
  396. };
  397. listData->list.sliceUpdated(
  398. ) | rpl::filter([=](const SliceUpdate &update) {
  399. return builder->applyUpdate(update);
  400. }) | rpl::start_with_next(pushNextSnapshot, lifetime);
  401. _session->data().itemRemoved(
  402. ) | rpl::filter([=](not_null<const HistoryItem*> item) {
  403. return (item->history()->peer->id == peerId)
  404. && (!topicRootId || item->topicRootId() == topicRootId);
  405. }) | rpl::filter([=](not_null<const HistoryItem*> item) {
  406. return builder->removeOne(item->id);
  407. }) | rpl::start_with_next(pushNextSnapshot, lifetime);
  408. _session->data().historyCleared(
  409. ) | rpl::filter([=](not_null<const History*> history) {
  410. return (history->peer->id == peerId);
  411. }) | rpl::filter([=] {
  412. return builder->removeAll();
  413. }) | rpl::start_with_next(pushNextSnapshot, lifetime);
  414. using Result = Storage::SparseIdsListResult;
  415. listData->list.query(Storage::SparseIdsListQuery(
  416. aroundId,
  417. limitBefore,
  418. limitAfter
  419. )) | rpl::filter([=](const Result &result) {
  420. return builder->applyInitial(result);
  421. }) | rpl::start_with_next_done(
  422. pushNextSnapshot,
  423. [=] { builder->checkInsufficient(); },
  424. lifetime);
  425. return lifetime;
  426. };
  427. }
  428. auto SearchController::saveState() -> SavedState {
  429. auto result = SavedState();
  430. if (_current != _cache.end()) {
  431. result.query = _current->first;
  432. result.peerList = std::move(_current->second->peerData.list);
  433. if (auto &migrated = _current->second->migratedData) {
  434. result.migratedList = std::move(migrated->list);
  435. }
  436. }
  437. return result;
  438. }
  439. void SearchController::restoreState(SavedState &&state) {
  440. if (!state.query.peerId) {
  441. return;
  442. }
  443. auto it = _cache.find(state.query);
  444. if (it == _cache.end()) {
  445. it = _cache.emplace(
  446. state.query,
  447. std::make_unique<CacheEntry>(_session, state.query)).first;
  448. }
  449. auto replace = Data(it->second->peerData.peer);
  450. replace.list = std::move(state.peerList);
  451. it->second->peerData = std::move(replace);
  452. if (auto &migrated = state.migratedList) {
  453. Assert(it->second->migratedData.has_value());
  454. auto replace = Data(it->second->migratedData->peer);
  455. replace.list = std::move(*migrated);
  456. it->second->migratedData = std::move(replace);
  457. }
  458. _current = it;
  459. }
  460. void SearchController::requestMore(
  461. const SparseIdsSliceBuilder::AroundData &key,
  462. const Query &query,
  463. Data *listData) {
  464. if (listData->requests.contains(key)) {
  465. return;
  466. }
  467. auto prepared = PrepareSearchRequest(
  468. listData->peer,
  469. query.topicRootId,
  470. query.type,
  471. query.query,
  472. key.aroundId,
  473. key.direction);
  474. if (!prepared) {
  475. return;
  476. }
  477. auto &histories = _session->data().histories();
  478. const auto type = ::Data::Histories::RequestType::History;
  479. const auto history = _session->data().history(listData->peer);
  480. auto requestId = histories.sendRequest(history, type, [=](Fn<void()> finish) {
  481. return _session->api().request(
  482. std::move(*prepared)
  483. ).done([=](const SearchRequestResult &result) {
  484. listData->requests.remove(key);
  485. auto parsed = ParseSearchResult(
  486. listData->peer,
  487. query.type,
  488. key.aroundId,
  489. key.direction,
  490. result);
  491. listData->list.addSlice(
  492. std::move(parsed.messageIds),
  493. parsed.noSkipRange,
  494. parsed.fullCount);
  495. finish();
  496. }).fail([=] {
  497. finish();
  498. }).send();
  499. });
  500. listData->requests.emplace(key, [=] {
  501. _session->data().histories().cancelRequest(requestId);
  502. });
  503. }
  504. DelayedSearchController::DelayedSearchController(
  505. not_null<Main::Session*> session)
  506. : _controller(session) {
  507. _timer.setCallback([this] { setQueryFast(_nextQuery); });
  508. }
  509. void DelayedSearchController::setQuery(const Query &query) {
  510. setQuery(query, kDefaultSearchTimeoutMs);
  511. }
  512. void DelayedSearchController::setQuery(
  513. const Query &query,
  514. crl::time delay) {
  515. if (currentQuery() == query) {
  516. _timer.cancel();
  517. return;
  518. }
  519. if (_controller.hasInCache(query)) {
  520. setQueryFast(query);
  521. } else {
  522. _nextQuery = query;
  523. _timer.callOnce(delay);
  524. }
  525. }
  526. void DelayedSearchController::setQueryFast(const Query &query) {
  527. _controller.setQuery(query);
  528. _currentQueryChanges.fire_copy(query.query);
  529. }
  530. } // namespace Api