data_user_photos.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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_user_photos.h"
  8. #include "main/main_session.h"
  9. #include "apiwrap.h"
  10. #include "api/api_peer_photo.h"
  11. #include "data/data_session.h"
  12. #include "data/data_user.h"
  13. #include "storage/storage_facade.h"
  14. #include "storage/storage_user_photos.h"
  15. class UserPhotosSliceBuilder {
  16. public:
  17. using Key = UserPhotosSlice::Key;
  18. UserPhotosSliceBuilder(Key key, int limitBefore, int limitAfter);
  19. bool applyUpdate(const Storage::UserPhotosResult &update);
  20. bool applyUpdate(const Storage::UserPhotosSliceUpdate &update);
  21. void checkInsufficientPhotos();
  22. auto insufficientPhotosAround() const {
  23. return _insufficientPhotosAround.events();
  24. }
  25. UserPhotosSlice snapshot() const;
  26. private:
  27. void mergeSliceData(
  28. std::optional<int> count,
  29. const std::deque<PhotoId> &photoIds,
  30. std::optional<int> skippedBefore,
  31. int skippedAfter);
  32. void sliceToLimits();
  33. Key _key;
  34. std::deque<PhotoId> _ids;
  35. std::optional<int> _fullCount;
  36. std::optional<int> _skippedBefore;
  37. int _skippedAfter = 0;
  38. int _limitBefore = 0;
  39. int _limitAfter = 0;
  40. rpl::event_stream<Api::PeerPhoto::UserPhotoId> _insufficientPhotosAround;
  41. };
  42. UserPhotosSlice::UserPhotosSlice(Key key)
  43. : UserPhotosSlice(
  44. key,
  45. {},
  46. std::nullopt,
  47. std::nullopt,
  48. std::nullopt) {
  49. }
  50. UserPhotosSlice::UserPhotosSlice(
  51. Key key,
  52. std::deque<PhotoId> &&ids,
  53. std::optional<int> fullCount,
  54. std::optional<int> skippedBefore,
  55. std::optional<int> skippedAfter)
  56. : AbstractSparseIds<std::deque<PhotoId>>(
  57. ids,
  58. fullCount,
  59. skippedBefore,
  60. skippedAfter)
  61. , _key(key) {
  62. }
  63. std::optional<int> UserPhotosSlice::distance(
  64. const Key &a,
  65. const Key &b) const {
  66. if (a.userId != _key.userId
  67. || b.userId != _key.userId) {
  68. return std::nullopt;
  69. }
  70. if (const auto i = indexOf(a.photoId)) {
  71. if (const auto j = indexOf(b.photoId)) {
  72. return *j - *i;
  73. }
  74. }
  75. return std::nullopt;
  76. }
  77. UserPhotosSliceBuilder::UserPhotosSliceBuilder(
  78. Key key,
  79. int limitBefore,
  80. int limitAfter)
  81. : _key(key)
  82. , _limitBefore(limitBefore)
  83. , _limitAfter(limitAfter) {
  84. }
  85. bool UserPhotosSliceBuilder::applyUpdate(const Storage::UserPhotosResult &update) {
  86. mergeSliceData(
  87. update.count,
  88. update.photoIds,
  89. update.skippedBefore,
  90. update.skippedAfter);
  91. return true;
  92. }
  93. bool UserPhotosSliceBuilder::applyUpdate(const Storage::UserPhotosSliceUpdate &update) {
  94. if (update.userId != _key.userId) {
  95. return false;
  96. }
  97. const auto idsCount = update.photoIds ? int(update.photoIds->size()) : 0;
  98. mergeSliceData(
  99. update.count,
  100. update.photoIds ? *update.photoIds : std::deque<PhotoId> {},
  101. update.count | func::add(-idsCount),
  102. 0);
  103. return true;
  104. }
  105. void UserPhotosSliceBuilder::checkInsufficientPhotos() {
  106. sliceToLimits();
  107. }
  108. void UserPhotosSliceBuilder::mergeSliceData(
  109. std::optional<int> count,
  110. const std::deque<PhotoId> &photoIds,
  111. std::optional<int> skippedBefore,
  112. int skippedAfter) {
  113. if (photoIds.empty()) {
  114. if (_fullCount != count) {
  115. _fullCount = count;
  116. if (_fullCount && *_fullCount <= _ids.size()) {
  117. _fullCount = _ids.size();
  118. _skippedBefore = _skippedAfter = 0;
  119. }
  120. }
  121. } else {
  122. if (count) {
  123. _fullCount = count;
  124. }
  125. _skippedAfter = skippedAfter;
  126. _ids = photoIds;
  127. if (_fullCount) {
  128. _skippedBefore = *_fullCount
  129. - _skippedAfter
  130. - int(_ids.size());
  131. }
  132. }
  133. sliceToLimits();
  134. }
  135. void UserPhotosSliceBuilder::sliceToLimits() {
  136. const auto aroundIt = ranges::find(_ids, _key.photoId);
  137. const auto removeFromBegin = (aroundIt - _ids.begin() - _limitBefore);
  138. const auto removeFromEnd = (_ids.end() - aroundIt - _limitAfter - 1);
  139. if (removeFromEnd > 0) {
  140. _ids.erase(_ids.end() - removeFromEnd, _ids.end());
  141. _skippedAfter += removeFromEnd;
  142. }
  143. if (removeFromBegin > 0) {
  144. _ids.erase(_ids.begin(), _ids.begin() + removeFromBegin);
  145. if (_skippedBefore) {
  146. *_skippedBefore += removeFromBegin;
  147. }
  148. } else if (removeFromBegin < 0
  149. && (!_skippedBefore || *_skippedBefore > 0)) {
  150. _insufficientPhotosAround.fire(_ids.empty() ? 0 : _ids.front());
  151. }
  152. }
  153. UserPhotosSlice UserPhotosSliceBuilder::snapshot() const {
  154. return UserPhotosSlice(
  155. _key,
  156. base::duplicate(_ids),
  157. _fullCount,
  158. _skippedBefore,
  159. _skippedAfter);
  160. }
  161. rpl::producer<UserPhotosSlice> UserPhotosViewer(
  162. not_null<Main::Session*> session,
  163. UserPhotosSlice::Key key,
  164. int limitBefore,
  165. int limitAfter) {
  166. return [=](auto consumer) {
  167. auto lifetime = rpl::lifetime();
  168. const auto builder = lifetime.make_state<UserPhotosSliceBuilder>(
  169. key,
  170. limitBefore,
  171. limitAfter);
  172. const auto applyUpdate = [=](auto &&update) {
  173. if (builder->applyUpdate(std::forward<decltype(update)>(update))) {
  174. consumer.put_next(builder->snapshot());
  175. }
  176. };
  177. auto requestPhotosAround = [user = session->data().user(key.userId)](
  178. Api::PeerPhoto::UserPhotoId photoId) {
  179. user->session().api().peerPhoto().requestUserPhotos(
  180. user,
  181. photoId);
  182. };
  183. builder->insufficientPhotosAround()
  184. | rpl::start_with_next(std::move(requestPhotosAround), lifetime);
  185. session->storage().userPhotosSliceUpdated()
  186. | rpl::start_with_next(applyUpdate, lifetime);
  187. session->storage().query(Storage::UserPhotosQuery(
  188. key,
  189. limitBefore,
  190. limitAfter
  191. )) | rpl::start_with_next_done(
  192. applyUpdate,
  193. [=] { builder->checkInsufficientPhotos(); },
  194. lifetime);
  195. return lifetime;
  196. };
  197. }
  198. rpl::producer<UserPhotosSlice> UserPhotosReversedViewer(
  199. not_null<Main::Session*> session,
  200. UserPhotosSlice::Key key,
  201. int limitBefore,
  202. int limitAfter) {
  203. return UserPhotosViewer(
  204. session,
  205. key,
  206. limitBefore,
  207. limitAfter
  208. ) | rpl::map([](UserPhotosSlice &&slice) {
  209. slice.reverse();
  210. return std::move(slice);
  211. });
  212. }
  213. std::optional<PhotoId> SyncUserFallbackPhotoViewer(not_null<UserData*> user) {
  214. auto syncLifetime = rpl::lifetime();
  215. auto result = std::optional<PhotoId>(std::nullopt);
  216. constexpr auto kFallbackCount = 1;
  217. user->session().storage().query(Storage::UserPhotosQuery(
  218. Storage::UserPhotosKey(peerToUser(user->id), true),
  219. kFallbackCount,
  220. kFallbackCount
  221. )) | rpl::start_with_next([&](Storage::UserPhotosResult &&slice) {
  222. if (slice.photoIds.empty()) {
  223. return;
  224. }
  225. result = slice.photoIds.front();
  226. }, syncLifetime);
  227. return result;
  228. }