data_groups.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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_groups.h"
  8. #include "history/history.h"
  9. #include "history/history_item.h"
  10. #include "dialogs/ui/dialogs_message_view.h"
  11. #include "data/data_media_types.h"
  12. #include "data/data_session.h"
  13. #include "data/data_forum.h"
  14. #include "data/data_forum_topic.h"
  15. namespace Data {
  16. namespace {
  17. constexpr auto kMaxItemsInGroup = 10;
  18. } // namespace
  19. Groups::Groups(not_null<Session*> data) : _data(data) {
  20. }
  21. bool Groups::isGrouped(not_null<const HistoryItem*> item) const {
  22. if (!item->groupId()) {
  23. return false;
  24. }
  25. const auto media = item->media();
  26. return media && media->canBeGrouped();
  27. }
  28. bool Groups::isGroupOfOne(not_null<const HistoryItem*> item) const {
  29. if (const auto groupId = item->groupId()) {
  30. const auto i = _groups.find(groupId);
  31. return (i != _groups.end()) && (i->second.items.size() == 1);
  32. }
  33. return false;
  34. }
  35. void Groups::registerMessage(not_null<HistoryItem*> item) {
  36. if (!isGrouped(item)) {
  37. return;
  38. }
  39. const auto i = _groups.emplace(item->groupId(), Group()).first;
  40. auto &items = i->second.items;
  41. if (items.size() < kMaxItemsInGroup) {
  42. items.insert(findPositionForItem(items, item), item);
  43. if (items.size() > 1) {
  44. refreshViews(items);
  45. }
  46. }
  47. }
  48. void Groups::unregisterMessage(not_null<const HistoryItem*> item) {
  49. const auto groupId = item->groupId();
  50. if (!groupId) {
  51. return;
  52. }
  53. const auto i = _groups.find(groupId);
  54. if (i != end(_groups)) {
  55. auto &items = i->second.items;
  56. const auto removed = ranges::remove(items, item);
  57. const auto last = end(items);
  58. if (removed != last) {
  59. items.erase(removed, last);
  60. if (!items.empty()) {
  61. refreshViews(items);
  62. } else {
  63. _groups.erase(i);
  64. }
  65. }
  66. }
  67. }
  68. void Groups::refreshMessage(
  69. not_null<HistoryItem*> item,
  70. bool justRefreshViews) {
  71. if (!isGrouped(item)) {
  72. unregisterMessage(item);
  73. _data->requestItemViewRefresh(item);
  74. return;
  75. }
  76. if (!item->isRegular() && !item->isScheduled()) {
  77. return;
  78. }
  79. const auto groupId = item->groupId();
  80. const auto i = _groups.find(groupId);
  81. if (i == end(_groups)) {
  82. registerMessage(item);
  83. return;
  84. }
  85. auto &items = i->second.items;
  86. if (justRefreshViews) {
  87. refreshViews(items);
  88. return;
  89. }
  90. const auto position = findPositionForItem(items, item);
  91. auto current = ranges::find(items, item);
  92. if (current == end(items)) {
  93. items.insert(position, item);
  94. } else if (position == current + 1) {
  95. return;
  96. } else if (position > current + 1) {
  97. for (++current; current != position; ++current) {
  98. std::swap(*(current - 1), *current);
  99. }
  100. } else if (position < current) {
  101. for (; current != position; --current) {
  102. std::swap(*(current - 1), *current);
  103. }
  104. } else {
  105. Unexpected("Position of item in Groups::refreshMessage().");
  106. }
  107. refreshViews(items);
  108. }
  109. HistoryItemsList::const_iterator Groups::findPositionForItem(
  110. const HistoryItemsList &group,
  111. not_null<HistoryItem*> item) {
  112. const auto last = end(group);
  113. const auto itemId = item->id;
  114. for (auto result = begin(group); result != last; ++result) {
  115. if ((*result)->id > itemId) {
  116. return result;
  117. }
  118. }
  119. return last;
  120. }
  121. const Group *Groups::find(not_null<const HistoryItem*> item) const {
  122. const auto groupId = item->groupId();
  123. if (!groupId) {
  124. return nullptr;
  125. }
  126. const auto i = _groups.find(groupId);
  127. if (i != _groups.end()) {
  128. const auto &result = i->second;
  129. if (result.items.size() > 1) {
  130. return &result;
  131. }
  132. }
  133. return nullptr;
  134. }
  135. void Groups::refreshViews(const HistoryItemsList &items) {
  136. if (items.empty()) {
  137. return;
  138. }
  139. for (const auto &item : items) {
  140. _data->requestItemViewRefresh(item);
  141. item->invalidateChatListEntry();
  142. }
  143. }
  144. not_null<HistoryItem*> Groups::findItemToEdit(
  145. not_null<HistoryItem*> item) const {
  146. const auto group = find(item);
  147. if (!group) {
  148. return item;
  149. }
  150. const auto &list = group->items;
  151. const auto it = ranges::find_if(
  152. list,
  153. ranges::not_fn(&HistoryItem::emptyText));
  154. if (it == end(list)) {
  155. return list.front();
  156. }
  157. return (*it);
  158. }
  159. } // namespace Data