| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "data/data_user_photos.h"
- #include "main/main_session.h"
- #include "apiwrap.h"
- #include "api/api_peer_photo.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "storage/storage_facade.h"
- #include "storage/storage_user_photos.h"
- class UserPhotosSliceBuilder {
- public:
- using Key = UserPhotosSlice::Key;
- UserPhotosSliceBuilder(Key key, int limitBefore, int limitAfter);
- bool applyUpdate(const Storage::UserPhotosResult &update);
- bool applyUpdate(const Storage::UserPhotosSliceUpdate &update);
- void checkInsufficientPhotos();
- auto insufficientPhotosAround() const {
- return _insufficientPhotosAround.events();
- }
- UserPhotosSlice snapshot() const;
- private:
- void mergeSliceData(
- std::optional<int> count,
- const std::deque<PhotoId> &photoIds,
- std::optional<int> skippedBefore,
- int skippedAfter);
- void sliceToLimits();
- Key _key;
- std::deque<PhotoId> _ids;
- std::optional<int> _fullCount;
- std::optional<int> _skippedBefore;
- int _skippedAfter = 0;
- int _limitBefore = 0;
- int _limitAfter = 0;
- rpl::event_stream<Api::PeerPhoto::UserPhotoId> _insufficientPhotosAround;
- };
- UserPhotosSlice::UserPhotosSlice(Key key)
- : UserPhotosSlice(
- key,
- {},
- std::nullopt,
- std::nullopt,
- std::nullopt) {
- }
- UserPhotosSlice::UserPhotosSlice(
- Key key,
- std::deque<PhotoId> &&ids,
- std::optional<int> fullCount,
- std::optional<int> skippedBefore,
- std::optional<int> skippedAfter)
- : AbstractSparseIds<std::deque<PhotoId>>(
- ids,
- fullCount,
- skippedBefore,
- skippedAfter)
- , _key(key) {
- }
- std::optional<int> UserPhotosSlice::distance(
- const Key &a,
- const Key &b) const {
- if (a.userId != _key.userId
- || b.userId != _key.userId) {
- return std::nullopt;
- }
- if (const auto i = indexOf(a.photoId)) {
- if (const auto j = indexOf(b.photoId)) {
- return *j - *i;
- }
- }
- return std::nullopt;
- }
- UserPhotosSliceBuilder::UserPhotosSliceBuilder(
- Key key,
- int limitBefore,
- int limitAfter)
- : _key(key)
- , _limitBefore(limitBefore)
- , _limitAfter(limitAfter) {
- }
- bool UserPhotosSliceBuilder::applyUpdate(const Storage::UserPhotosResult &update) {
- mergeSliceData(
- update.count,
- update.photoIds,
- update.skippedBefore,
- update.skippedAfter);
- return true;
- }
- bool UserPhotosSliceBuilder::applyUpdate(const Storage::UserPhotosSliceUpdate &update) {
- if (update.userId != _key.userId) {
- return false;
- }
- const auto idsCount = update.photoIds ? int(update.photoIds->size()) : 0;
- mergeSliceData(
- update.count,
- update.photoIds ? *update.photoIds : std::deque<PhotoId> {},
- update.count | func::add(-idsCount),
- 0);
- return true;
- }
- void UserPhotosSliceBuilder::checkInsufficientPhotos() {
- sliceToLimits();
- }
- void UserPhotosSliceBuilder::mergeSliceData(
- std::optional<int> count,
- const std::deque<PhotoId> &photoIds,
- std::optional<int> skippedBefore,
- int skippedAfter) {
- if (photoIds.empty()) {
- if (_fullCount != count) {
- _fullCount = count;
- if (_fullCount && *_fullCount <= _ids.size()) {
- _fullCount = _ids.size();
- _skippedBefore = _skippedAfter = 0;
- }
- }
- } else {
- if (count) {
- _fullCount = count;
- }
- _skippedAfter = skippedAfter;
- _ids = photoIds;
- if (_fullCount) {
- _skippedBefore = *_fullCount
- - _skippedAfter
- - int(_ids.size());
- }
- }
- sliceToLimits();
- }
- void UserPhotosSliceBuilder::sliceToLimits() {
- const auto aroundIt = ranges::find(_ids, _key.photoId);
- const auto removeFromBegin = (aroundIt - _ids.begin() - _limitBefore);
- const auto removeFromEnd = (_ids.end() - aroundIt - _limitAfter - 1);
- if (removeFromEnd > 0) {
- _ids.erase(_ids.end() - removeFromEnd, _ids.end());
- _skippedAfter += removeFromEnd;
- }
- if (removeFromBegin > 0) {
- _ids.erase(_ids.begin(), _ids.begin() + removeFromBegin);
- if (_skippedBefore) {
- *_skippedBefore += removeFromBegin;
- }
- } else if (removeFromBegin < 0
- && (!_skippedBefore || *_skippedBefore > 0)) {
- _insufficientPhotosAround.fire(_ids.empty() ? 0 : _ids.front());
- }
- }
- UserPhotosSlice UserPhotosSliceBuilder::snapshot() const {
- return UserPhotosSlice(
- _key,
- base::duplicate(_ids),
- _fullCount,
- _skippedBefore,
- _skippedAfter);
- }
- rpl::producer<UserPhotosSlice> UserPhotosViewer(
- not_null<Main::Session*> session,
- UserPhotosSlice::Key key,
- int limitBefore,
- int limitAfter) {
- return [=](auto consumer) {
- auto lifetime = rpl::lifetime();
- const auto builder = lifetime.make_state<UserPhotosSliceBuilder>(
- key,
- limitBefore,
- limitAfter);
- const auto applyUpdate = [=](auto &&update) {
- if (builder->applyUpdate(std::forward<decltype(update)>(update))) {
- consumer.put_next(builder->snapshot());
- }
- };
- auto requestPhotosAround = [user = session->data().user(key.userId)](
- Api::PeerPhoto::UserPhotoId photoId) {
- user->session().api().peerPhoto().requestUserPhotos(
- user,
- photoId);
- };
- builder->insufficientPhotosAround()
- | rpl::start_with_next(std::move(requestPhotosAround), lifetime);
- session->storage().userPhotosSliceUpdated()
- | rpl::start_with_next(applyUpdate, lifetime);
- session->storage().query(Storage::UserPhotosQuery(
- key,
- limitBefore,
- limitAfter
- )) | rpl::start_with_next_done(
- applyUpdate,
- [=] { builder->checkInsufficientPhotos(); },
- lifetime);
- return lifetime;
- };
- }
- rpl::producer<UserPhotosSlice> UserPhotosReversedViewer(
- not_null<Main::Session*> session,
- UserPhotosSlice::Key key,
- int limitBefore,
- int limitAfter) {
- return UserPhotosViewer(
- session,
- key,
- limitBefore,
- limitAfter
- ) | rpl::map([](UserPhotosSlice &&slice) {
- slice.reverse();
- return std::move(slice);
- });
- }
- std::optional<PhotoId> SyncUserFallbackPhotoViewer(not_null<UserData*> user) {
- auto syncLifetime = rpl::lifetime();
- auto result = std::optional<PhotoId>(std::nullopt);
- constexpr auto kFallbackCount = 1;
- user->session().storage().query(Storage::UserPhotosQuery(
- Storage::UserPhotosKey(peerToUser(user->id), true),
- kFallbackCount,
- kFallbackCount
- )) | rpl::start_with_next([&](Storage::UserPhotosResult &&slice) {
- if (slice.photoIds.empty()) {
- return;
- }
- result = slice.photoIds.front();
- }, syncLifetime);
- return result;
- }
|