data_saved_messages.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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_saved_messages.h"
  8. #include "apiwrap.h"
  9. #include "data/data_peer.h"
  10. #include "data/data_saved_sublist.h"
  11. #include "data/data_session.h"
  12. #include "history/history.h"
  13. #include "history/history_item.h"
  14. #include "main/main_session.h"
  15. namespace Data {
  16. namespace {
  17. constexpr auto kPerPage = 50;
  18. constexpr auto kFirstPerPage = 10;
  19. constexpr auto kListPerPage = 100;
  20. constexpr auto kListFirstPerPage = 20;
  21. } // namespace
  22. SavedMessages::SavedMessages(not_null<Session*> owner)
  23. : _owner(owner)
  24. , _chatsList(
  25. &owner->session(),
  26. FilterId(),
  27. owner->maxPinnedChatsLimitValue(this))
  28. , _loadMore([=] { sendLoadMoreRequests(); }) {
  29. }
  30. SavedMessages::~SavedMessages() = default;
  31. bool SavedMessages::supported() const {
  32. return !_unsupported;
  33. }
  34. Session &SavedMessages::owner() const {
  35. return *_owner;
  36. }
  37. Main::Session &SavedMessages::session() const {
  38. return _owner->session();
  39. }
  40. not_null<Dialogs::MainList*> SavedMessages::chatsList() {
  41. return &_chatsList;
  42. }
  43. not_null<SavedSublist*> SavedMessages::sublist(not_null<PeerData*> peer) {
  44. const auto i = _sublists.find(peer);
  45. if (i != end(_sublists)) {
  46. return i->second.get();
  47. }
  48. return _sublists.emplace(
  49. peer,
  50. std::make_unique<SavedSublist>(peer)).first->second.get();
  51. }
  52. void SavedMessages::loadMore() {
  53. _loadMoreScheduled = true;
  54. _loadMore.call();
  55. }
  56. void SavedMessages::loadMore(not_null<SavedSublist*> sublist) {
  57. _loadMoreSublistsScheduled.emplace(sublist);
  58. _loadMore.call();
  59. }
  60. void SavedMessages::sendLoadMore() {
  61. if (_loadMoreRequestId || _chatsList.loaded()) {
  62. return;
  63. } else if (!_pinnedLoaded) {
  64. loadPinned();
  65. }
  66. _loadMoreRequestId = _owner->session().api().request(
  67. MTPmessages_GetSavedDialogs(
  68. MTP_flags(MTPmessages_GetSavedDialogs::Flag::f_exclude_pinned),
  69. MTP_int(_offsetDate),
  70. MTP_int(_offsetId),
  71. _offsetPeer ? _offsetPeer->input : MTP_inputPeerEmpty(),
  72. MTP_int(_offsetId ? kListPerPage : kListFirstPerPage),
  73. MTP_long(0)) // hash
  74. ).done([=](const MTPmessages_SavedDialogs &result) {
  75. apply(result, false);
  76. }).fail([=](const MTP::Error &error) {
  77. if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
  78. _unsupported = true;
  79. }
  80. _chatsList.setLoaded();
  81. _loadMoreRequestId = 0;
  82. }).send();
  83. }
  84. void SavedMessages::loadPinned() {
  85. if (_pinnedRequestId) {
  86. return;
  87. }
  88. _pinnedRequestId = _owner->session().api().request(
  89. MTPmessages_GetPinnedSavedDialogs()
  90. ).done([=](const MTPmessages_SavedDialogs &result) {
  91. apply(result, true);
  92. }).fail([=](const MTP::Error &error) {
  93. if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
  94. _unsupported = true;
  95. } else {
  96. _pinnedLoaded = true;
  97. }
  98. _pinnedRequestId = 0;
  99. }).send();
  100. }
  101. void SavedMessages::sendLoadMore(not_null<SavedSublist*> sublist) {
  102. if (_loadMoreRequests.contains(sublist) || sublist->isFullLoaded()) {
  103. return;
  104. }
  105. const auto &list = sublist->messages();
  106. const auto offsetId = list.empty() ? MsgId(0) : list.back()->id;
  107. const auto offsetDate = list.empty() ? MsgId(0) : list.back()->date();
  108. const auto limit = offsetId ? kPerPage : kFirstPerPage;
  109. const auto requestId = _owner->session().api().request(
  110. MTPmessages_GetSavedHistory(
  111. sublist->peer()->input,
  112. MTP_int(offsetId),
  113. MTP_int(offsetDate),
  114. MTP_int(0), // add_offset
  115. MTP_int(limit),
  116. MTP_int(0), // max_id
  117. MTP_int(0), // min_id
  118. MTP_long(0)) // hash
  119. ).done([=](const MTPmessages_Messages &result) {
  120. auto count = 0;
  121. auto list = (const QVector<MTPMessage>*)nullptr;
  122. result.match([](const MTPDmessages_channelMessages &) {
  123. LOG(("API Error: messages.channelMessages in sublist."));
  124. }, [](const MTPDmessages_messagesNotModified &) {
  125. LOG(("API Error: messages.messagesNotModified in sublist."));
  126. }, [&](const auto &data) {
  127. owner().processUsers(data.vusers());
  128. owner().processChats(data.vchats());
  129. list = &data.vmessages().v;
  130. if constexpr (MTPDmessages_messages::Is<decltype(data)>()) {
  131. count = int(list->size());
  132. } else {
  133. count = data.vcount().v;
  134. }
  135. });
  136. _loadMoreRequests.remove(sublist);
  137. if (!list) {
  138. sublist->setFullLoaded();
  139. return;
  140. }
  141. auto items = std::vector<not_null<HistoryItem*>>();
  142. items.reserve(list->size());
  143. for (const auto &message : *list) {
  144. const auto item = owner().addNewMessage(
  145. message,
  146. {},
  147. NewMessageType::Existing);
  148. if (item) {
  149. items.push_back(item);
  150. }
  151. }
  152. sublist->append(std::move(items), count);
  153. if (result.type() == mtpc_messages_messages) {
  154. sublist->setFullLoaded();
  155. }
  156. }).fail([=](const MTP::Error &error) {
  157. if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
  158. _unsupported = true;
  159. }
  160. sublist->setFullLoaded();
  161. _loadMoreRequests.remove(sublist);
  162. }).send();
  163. _loadMoreRequests[sublist] = requestId;
  164. }
  165. void SavedMessages::apply(
  166. const MTPmessages_SavedDialogs &result,
  167. bool pinned) {
  168. auto list = (const QVector<MTPSavedDialog>*)nullptr;
  169. result.match([](const MTPDmessages_savedDialogsNotModified &) {
  170. LOG(("API Error: messages.savedDialogsNotModified."));
  171. }, [&](const auto &data) {
  172. _owner->processUsers(data.vusers());
  173. _owner->processChats(data.vchats());
  174. _owner->processMessages(
  175. data.vmessages(),
  176. NewMessageType::Existing);
  177. list = &data.vdialogs().v;
  178. });
  179. if (pinned) {
  180. _pinnedRequestId = 0;
  181. _pinnedLoaded = true;
  182. } else {
  183. _loadMoreRequestId = 0;
  184. }
  185. if (!list) {
  186. if (!pinned) {
  187. _chatsList.setLoaded();
  188. }
  189. return;
  190. }
  191. auto lastValid = false;
  192. auto offsetDate = TimeId();
  193. auto offsetId = MsgId();
  194. auto offsetPeer = (PeerData*)nullptr;
  195. const auto selfId = _owner->session().userPeerId();
  196. for (const auto &dialog : *list) {
  197. const auto &data = dialog.data();
  198. const auto peer = _owner->peer(peerFromMTP(data.vpeer()));
  199. const auto topId = MsgId(data.vtop_message().v);
  200. if (const auto item = _owner->message(selfId, topId)) {
  201. offsetPeer = peer;
  202. offsetDate = item->date();
  203. offsetId = topId;
  204. lastValid = true;
  205. const auto entry = sublist(peer);
  206. const auto entryPinned = pinned || data.is_pinned();
  207. entry->applyMaybeLast(item);
  208. _owner->setPinnedFromEntryList(entry, entryPinned);
  209. } else {
  210. lastValid = false;
  211. }
  212. }
  213. if (pinned) {
  214. } else if (!lastValid) {
  215. LOG(("API Error: Unknown message in the end of a slice."));
  216. _chatsList.setLoaded();
  217. } else if (result.type() == mtpc_messages_savedDialogs) {
  218. _chatsList.setLoaded();
  219. } else if ((_offsetDate > 0 && offsetDate > _offsetDate)
  220. || (offsetDate == _offsetDate
  221. && offsetId == _offsetId
  222. && offsetPeer == _offsetPeer)) {
  223. LOG(("API Error: Bad order in messages.savedDialogs."));
  224. _chatsList.setLoaded();
  225. } else {
  226. _offsetDate = offsetDate;
  227. _offsetId = offsetId;
  228. _offsetPeer = offsetPeer;
  229. }
  230. }
  231. void SavedMessages::sendLoadMoreRequests() {
  232. if (_loadMoreScheduled) {
  233. sendLoadMore();
  234. }
  235. for (const auto sublist : base::take(_loadMoreSublistsScheduled)) {
  236. sendLoadMore(sublist);
  237. }
  238. }
  239. void SavedMessages::apply(const MTPDupdatePinnedSavedDialogs &update) {
  240. const auto list = update.vorder();
  241. if (!list) {
  242. loadPinned();
  243. return;
  244. }
  245. const auto &order = list->v;
  246. const auto notLoaded = [&](const MTPDialogPeer &peer) {
  247. return peer.match([&](const MTPDdialogPeer &data) {
  248. const auto peer = _owner->peer(peerFromMTP(data.vpeer()));
  249. return !_sublists.contains(peer);
  250. }, [&](const MTPDdialogPeerFolder &data) {
  251. LOG(("API Error: "
  252. "updatePinnedSavedDialogs has folders."));
  253. return false;
  254. });
  255. };
  256. if (!ranges::none_of(order, notLoaded)) {
  257. loadPinned();
  258. } else {
  259. _chatsList.pinned()->applyList(this, order);
  260. _owner->notifyPinnedDialogsOrderUpdated();
  261. }
  262. }
  263. void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) {
  264. update.vpeer().match([&](const MTPDdialogPeer &data) {
  265. const auto peer = _owner->peer(peerFromMTP(data.vpeer()));
  266. const auto i = _sublists.find(peer);
  267. if (i != end(_sublists)) {
  268. const auto entry = i->second.get();
  269. _owner->setChatPinned(entry, FilterId(), update.is_pinned());
  270. } else {
  271. loadPinned();
  272. }
  273. }, [&](const MTPDdialogPeerFolder &data) {
  274. DEBUG_LOG(("API Error: Folder in updateSavedDialogPinned."));
  275. });
  276. }
  277. } // namespace Data