data_saved_sublist.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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_sublist.h"
  8. #include "data/data_histories.h"
  9. #include "data/data_peer.h"
  10. #include "data/data_saved_messages.h"
  11. #include "data/data_session.h"
  12. #include "history/view/history_view_item_preview.h"
  13. #include "history/history.h"
  14. #include "history/history_item.h"
  15. namespace Data {
  16. SavedSublist::SavedSublist(not_null<PeerData*> peer)
  17. : Entry(&peer->owner(), Dialogs::Entry::Type::SavedSublist)
  18. , _history(peer->owner().history(peer)) {
  19. }
  20. SavedSublist::~SavedSublist() = default;
  21. not_null<History*> SavedSublist::history() const {
  22. return _history;
  23. }
  24. not_null<PeerData*> SavedSublist::peer() const {
  25. return _history->peer;
  26. }
  27. bool SavedSublist::isHiddenAuthor() const {
  28. return peer()->isSavedHiddenAuthor();
  29. }
  30. bool SavedSublist::isFullLoaded() const {
  31. return (_flags & Flag::FullLoaded) != 0;
  32. }
  33. auto SavedSublist::messages() const
  34. -> const std::vector<not_null<HistoryItem*>> & {
  35. return _items;
  36. }
  37. void SavedSublist::applyMaybeLast(not_null<HistoryItem*> item, bool added) {
  38. const auto before = [](
  39. not_null<HistoryItem*> a,
  40. not_null<HistoryItem*> b) {
  41. return IsServerMsgId(a->id)
  42. ? (IsServerMsgId(b->id) ? (a->id < b->id) : true)
  43. : (IsServerMsgId(b->id) ? false : (a->id < b->id));
  44. };
  45. if (_items.empty()) {
  46. _items.push_back(item);
  47. } else if (_items.front() == item) {
  48. return;
  49. } else if (!isFullLoaded()
  50. && _items.size() == 1
  51. && before(_items.front(), item)) {
  52. _items[0] = item;
  53. } else if (before(_items.back(), item)) {
  54. for (auto i = begin(_items); i != end(_items); ++i) {
  55. if (item == *i) {
  56. return;
  57. } else if (before(*i, item)) {
  58. _items.insert(i, item);
  59. break;
  60. }
  61. }
  62. }
  63. if (added && _fullCount) {
  64. ++*_fullCount;
  65. }
  66. if (_items.front() == item) {
  67. setChatListTimeId(item->date());
  68. resolveChatListMessageGroup();
  69. }
  70. _changed.fire({});
  71. }
  72. void SavedSublist::removeOne(not_null<HistoryItem*> item) {
  73. if (_items.empty()) {
  74. return;
  75. }
  76. const auto last = (_items.front() == item);
  77. const auto from = ranges::remove(_items, item);
  78. const auto removed = end(_items) - from;
  79. if (removed) {
  80. _items.erase(from, end(_items));
  81. }
  82. if (_fullCount) {
  83. --*_fullCount;
  84. }
  85. if (last) {
  86. if (_items.empty()) {
  87. if (isFullLoaded()) {
  88. updateChatListExistence();
  89. } else {
  90. updateChatListEntry();
  91. crl::on_main(this, [=] {
  92. owner().savedMessages().loadMore(this);
  93. });
  94. }
  95. } else {
  96. setChatListTimeId(_items.front()->date());
  97. }
  98. }
  99. if (removed || _fullCount) {
  100. _changed.fire({});
  101. }
  102. }
  103. rpl::producer<> SavedSublist::changes() const {
  104. return _changed.events();
  105. }
  106. std::optional<int> SavedSublist::fullCount() const {
  107. return isFullLoaded() ? int(_items.size()) : _fullCount;
  108. }
  109. rpl::producer<int> SavedSublist::fullCountValue() const {
  110. return _changed.events_starting_with({}) | rpl::map([=] {
  111. return fullCount();
  112. }) | rpl::filter_optional();
  113. }
  114. void SavedSublist::append(
  115. std::vector<not_null<HistoryItem*>> &&items,
  116. int fullCount) {
  117. _fullCount = fullCount;
  118. if (items.empty()) {
  119. setFullLoaded();
  120. } else if (_items.empty()) {
  121. _items = std::move(items);
  122. setChatListTimeId(_items.front()->date());
  123. _changed.fire({});
  124. } else if (_items.back()->id > items.front()->id) {
  125. _items.insert(end(_items), begin(items), end(items));
  126. _changed.fire({});
  127. } else {
  128. _items.insert(end(_items), begin(items), end(items));
  129. ranges::stable_sort(
  130. _items,
  131. ranges::greater(),
  132. &HistoryItem::id);
  133. ranges::unique(_items, ranges::greater(), &HistoryItem::id);
  134. _changed.fire({});
  135. }
  136. }
  137. void SavedSublist::setFullLoaded(bool loaded) {
  138. if (loaded != isFullLoaded()) {
  139. if (loaded) {
  140. _flags |= Flag::FullLoaded;
  141. if (_items.empty()) {
  142. updateChatListExistence();
  143. }
  144. } else {
  145. _flags &= ~Flag::FullLoaded;
  146. }
  147. _changed.fire({});
  148. }
  149. }
  150. int SavedSublist::fixedOnTopIndex() const {
  151. return 0;
  152. }
  153. bool SavedSublist::shouldBeInChatList() const {
  154. return isPinnedDialog(FilterId()) || !_items.empty();
  155. }
  156. Dialogs::UnreadState SavedSublist::chatListUnreadState() const {
  157. return {};
  158. }
  159. Dialogs::BadgesState SavedSublist::chatListBadgesState() const {
  160. return {};
  161. }
  162. HistoryItem *SavedSublist::chatListMessage() const {
  163. return _items.empty() ? nullptr : _items.front().get();
  164. }
  165. bool SavedSublist::chatListMessageKnown() const {
  166. return true;
  167. }
  168. const QString &SavedSublist::chatListName() const {
  169. return _history->chatListName();
  170. }
  171. const base::flat_set<QString> &SavedSublist::chatListNameWords() const {
  172. return _history->chatListNameWords();
  173. }
  174. const base::flat_set<QChar> &SavedSublist::chatListFirstLetters() const {
  175. return _history->chatListFirstLetters();
  176. }
  177. const QString &SavedSublist::chatListNameSortKey() const {
  178. return _history->chatListNameSortKey();
  179. }
  180. int SavedSublist::chatListNameVersion() const {
  181. return _history->chatListNameVersion();
  182. }
  183. void SavedSublist::paintUserpic(
  184. Painter &p,
  185. Ui::PeerUserpicView &view,
  186. const Dialogs::Ui::PaintContext &context) const {
  187. _history->paintUserpic(p, view, context);
  188. }
  189. void SavedSublist::chatListPreloadData() {
  190. peer()->loadUserpic();
  191. allowChatListMessageResolve();
  192. }
  193. void SavedSublist::allowChatListMessageResolve() {
  194. if (_flags & Flag::ResolveChatListMessage) {
  195. return;
  196. }
  197. _flags |= Flag::ResolveChatListMessage;
  198. resolveChatListMessageGroup();
  199. }
  200. bool SavedSublist::hasOrphanMediaGroupPart() const {
  201. if (isFullLoaded() || _items.size() != 1) {
  202. return false;
  203. }
  204. return (_items.front()->groupId() != MessageGroupId());
  205. }
  206. void SavedSublist::resolveChatListMessageGroup() {
  207. const auto item = chatListMessage();
  208. if (!(_flags & Flag::ResolveChatListMessage)
  209. || !item
  210. || !hasOrphanMediaGroupPart()) {
  211. return;
  212. }
  213. // If we set a single album part, request the full album.
  214. const auto withImages = !item->toPreview({
  215. .hideSender = true,
  216. .hideCaption = true }).images.empty();
  217. if (withImages) {
  218. owner().histories().requestGroupAround(item);
  219. }
  220. }
  221. } // namespace Data