203 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 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<PhotoId> &&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<UserPhotosResult> 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<PhotoId>(
 | |
| 				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<SliceUpdate> {
 | |
| 	return _sliceUpdated.events();
 | |
| }
 | |
| 
 | |
| rpl::producer<UserPhotosSliceUpdate> UserPhotos::sliceUpdated() const {
 | |
| 	return _sliceUpdated.events();
 | |
| }
 | |
| 
 | |
| std::map<UserId, UserPhotos::List>::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<UserPhotosResult> 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
 | 
