/* 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 "storage/storage_user_photos.h" namespace Storage { void UserPhotos::List::setBack(PhotoId photoId) { if (_backPhotoId != photoId) { detachBack(); _backPhotoId = photoId; attachBack(); sendUpdate(); } } void UserPhotos::List::detachBack() { if (_backPhotoId) { removeOne(_backPhotoId); } } void UserPhotos::List::attachBack() { if (_backPhotoId) { _photoIds.push_front(_backPhotoId); if (_count) { ++*_count; } } } void UserPhotos::List::addNew(PhotoId photoId) { if (!base::contains(_photoIds, photoId)) { detachBack(); _photoIds.push_back(photoId); if (_count) { ++*_count; } attachBack(); sendUpdate(); } } void UserPhotos::List::addSlice( std::vector &&photoIds, int count) { detachBack(); for (auto photoId : photoIds) { if (!base::contains(_photoIds, photoId)) { _photoIds.push_front(photoId); } } _count = count; if ((_count && *_count < _photoIds.size()) || photoIds.empty()) { _count = _photoIds.size(); } attachBack(); sendUpdate(); } void UserPhotos::List::removeOne(PhotoId photoId) { auto position = ranges::find(_photoIds, photoId); if (position == _photoIds.end()) { _count = std::nullopt; } else { if (_count) { --*_count; } _photoIds.erase(position); } sendUpdate(); } void UserPhotos::List::removeAfter(PhotoId photoId) { auto position = ranges::find(_photoIds, photoId); if (position == _photoIds.end()) { _count = std::nullopt; _photoIds.clear(); } else { if (_count) { *_count -= (_photoIds.end() - position); } _photoIds.erase(position, _photoIds.end()); } sendUpdate(); } void UserPhotos::List::sendUpdate() { auto update = SliceUpdate(); update.photoIds = &_photoIds; update.count = _count; _sliceUpdated.fire(std::move(update)); } rpl::producer UserPhotos::List::query( UserPhotosQuery &&query) const { return [this, query = std::move(query)](auto consumer) { auto result = UserPhotosResult(); result.count = _count; auto position = ranges::find(_photoIds, query.key.photoId); if (position != _photoIds.end()) { auto haveBefore = int(position - _photoIds.begin()); auto haveEqualOrAfter = int(_photoIds.end() - position); auto before = qMin(haveBefore, query.limitBefore); auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1); result.photoIds = std::deque( position - before, position + equalOrAfter); auto skippedInIds = (haveBefore - before); result.skippedBefore = _count | func::add(-int(_photoIds.size()) + skippedInIds); result.skippedBefore = haveBefore - before; result.skippedAfter = (haveEqualOrAfter - equalOrAfter); consumer.put_next(std::move(result)); } else if (query.key.back && _backPhotoId) { result.photoIds.push_front(_backPhotoId); result.count = 1; consumer.put_next(std::move(result)); } else if (_count) { consumer.put_next(std::move(result)); } consumer.put_done(); return rpl::lifetime(); }; } auto UserPhotos::List::sliceUpdated() const -> rpl::producer { return _sliceUpdated.events(); } rpl::producer UserPhotos::sliceUpdated() const { return _sliceUpdated.events(); } std::map::iterator UserPhotos::enforceLists( UserId user) { auto result = _lists.find(user); if (result != _lists.end()) { return result; } result = _lists.emplace(user, List {}).first; result->second.sliceUpdated( ) | rpl::start_with_next([this, user]( const SliceUpdate &update) { _sliceUpdated.fire(UserPhotosSliceUpdate( user, update.photoIds, update.count)); }, _lifetime); return result; } void UserPhotos::add(UserPhotosSetBack &&query) { auto userIt = enforceLists(query.userId); userIt->second.setBack(query.photoId); } void UserPhotos::add(UserPhotosAddNew &&query) { auto userIt = enforceLists(query.userId); userIt->second.addNew(query.photoId); } void UserPhotos::add(UserPhotosAddSlice &&query) { auto userIt = enforceLists(query.userId); userIt->second.addSlice( std::move(query.photoIds), query.count); } void UserPhotos::remove(UserPhotosRemoveOne &&query) { auto userIt = _lists.find(query.userId); if (userIt != _lists.end()) { userIt->second.removeOne(query.photoId); } } void UserPhotos::remove(UserPhotosRemoveAfter &&query) { auto userIt = _lists.find(query.userId); if (userIt != _lists.end()) { userIt->second.removeAfter(query.photoId); } } rpl::producer UserPhotos::query( UserPhotosQuery &&query) const { auto userIt = _lists.find(query.key.userId); if (userIt != _lists.end()) { return userIt->second.query(std::move(query)); } return [](auto consumer) { consumer.put_done(); return rpl::lifetime(); }; } } // namespace Storage