226 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
	
		
			6 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 "api/api_user_names.h"
 | |
| 
 | |
| #include "apiwrap.h"
 | |
| #include "data/data_channel.h"
 | |
| #include "data/data_peer.h"
 | |
| #include "data/data_user.h"
 | |
| #include "main/main_session.h"
 | |
| 
 | |
| namespace Api {
 | |
| namespace {
 | |
| 
 | |
| [[nodiscard]] Data::Username UsernameFromTL(const MTPUsername &username) {
 | |
| 	return {
 | |
| 		.username = qs(username.data().vusername()),
 | |
| 		.active = username.data().is_active(),
 | |
| 		.editable = username.data().is_editable(),
 | |
| 	};
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| Usernames::Usernames(not_null<ApiWrap*> api)
 | |
| : _session(&api->session())
 | |
| , _api(&api->instance()) {
 | |
| }
 | |
| 
 | |
| rpl::producer<Data::Usernames> Usernames::loadUsernames(
 | |
| 		not_null<PeerData*> peer) const {
 | |
| 	return [=](auto consumer) {
 | |
| 		auto lifetime = rpl::lifetime();
 | |
| 
 | |
| 		const auto push = [consumer](
 | |
| 				const auto &usernames,
 | |
| 				const auto &username) {
 | |
| 			if (usernames) {
 | |
| 				if (usernames->v.empty()) {
 | |
| 					// Probably will never happen.
 | |
| 					consumer.put_next({});
 | |
| 				} else {
 | |
| 					auto parsed = FromTL(*usernames);
 | |
| 					if ((parsed.size() == 1)
 | |
| 						&& username
 | |
| 						&& (parsed.front().username == qs(*username))) {
 | |
| 						// Probably will never happen.
 | |
| 						consumer.put_next({});
 | |
| 					} else {
 | |
| 						consumer.put_next(std::move(parsed));
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				consumer.put_next({});
 | |
| 			}
 | |
| 		};
 | |
| 
 | |
| 		const auto requestUser = [&](const MTPInputUser &data) {
 | |
| 			_session->api().request(MTPusers_GetUsers(
 | |
| 				MTP_vector<MTPInputUser>(1, data)
 | |
| 			)).done([=](const MTPVector<MTPUser> &result) {
 | |
| 				result.v.front().match([&](const MTPDuser &data) {
 | |
| 					push(data.vusernames(), data.vusername());
 | |
| 					consumer.put_done();
 | |
| 				}, [&](const MTPDuserEmpty&) {
 | |
| 					consumer.put_next({});
 | |
| 					consumer.put_done();
 | |
| 				});
 | |
| 			}).send();
 | |
| 		};
 | |
| 		const auto requestChannel = [&](const MTPInputChannel &data) {
 | |
| 			_session->api().request(MTPchannels_GetChannels(
 | |
| 				MTP_vector<MTPInputChannel>(1, data)
 | |
| 			)).done([=](const MTPmessages_Chats &result) {
 | |
| 				result.match([&](const auto &data) {
 | |
| 					data.vchats().v.front().match([&](const MTPDchannel &c) {
 | |
| 						push(c.vusernames(), c.vusername());
 | |
| 						consumer.put_done();
 | |
| 					}, [&](auto &&) {
 | |
| 						consumer.put_next({});
 | |
| 						consumer.put_done();
 | |
| 					});
 | |
| 				});
 | |
| 			}).send();
 | |
| 		};
 | |
| 		if (peer->isSelf()) {
 | |
| 			requestUser(MTP_inputUserSelf());
 | |
| 		} else if (const auto user = peer->asUser()) {
 | |
| 			requestUser(user->inputUser);
 | |
| 		} else if (const auto channel = peer->asChannel()) {
 | |
| 			requestChannel(channel->inputChannel);
 | |
| 		}
 | |
| 		return lifetime;
 | |
| 	};
 | |
| }
 | |
| 
 | |
| rpl::producer<> Usernames::toggle(
 | |
| 		not_null<PeerData*> peer,
 | |
| 		const QString &username,
 | |
| 		bool active) {
 | |
| 	const auto peerId = peer->id;
 | |
| 	const auto it = _toggleRequests.find(peerId);
 | |
| 	const auto found = (it != end(_toggleRequests));
 | |
| 	auto &entry = (!found
 | |
| 		? _toggleRequests.emplace(
 | |
| 			peerId,
 | |
| 			Entry{ .usernames = { username } }).first
 | |
| 		: it)->second;
 | |
| 	if (ranges::contains(entry.usernames, username)) {
 | |
| 		if (found) {
 | |
| 			return entry.done.events();
 | |
| 		}
 | |
| 	} else {
 | |
| 		entry.usernames.push_back(username);
 | |
| 	}
 | |
| 
 | |
| 	const auto finish = [=] {
 | |
| 		const auto it = _toggleRequests.find(peerId);
 | |
| 		if (it != end(_toggleRequests)) {
 | |
| 			auto &list = it->second.usernames;
 | |
| 			list.erase(ranges::remove(list, username), end(list));
 | |
| 			if (list.empty()) {
 | |
| 				it->second.done.fire_done();
 | |
| 				_toggleRequests.remove(peerId);
 | |
| 			}
 | |
| 		}
 | |
| 	};
 | |
| 
 | |
| 	if (peer->isSelf()) {
 | |
| 		_api.request(MTPaccount_ToggleUsername(
 | |
| 			MTP_string(username),
 | |
| 			MTP_bool(active)
 | |
| 		)).done(finish).fail(finish).send();
 | |
| 	} else if (const auto channel = peer->asChannel()) {
 | |
| 		_api.request(MTPchannels_ToggleUsername(
 | |
| 			channel->inputChannel,
 | |
| 			MTP_string(username),
 | |
| 			MTP_bool(active)
 | |
| 		)).done(finish).fail(finish).send();
 | |
| 	} else {
 | |
| 		return rpl::never<>();
 | |
| 	}
 | |
| 	return entry.done.events();
 | |
| }
 | |
| 
 | |
| rpl::producer<> Usernames::reorder(
 | |
| 		not_null<PeerData*> peer,
 | |
| 		const std::vector<QString> &usernames) {
 | |
| 	const auto peerId = peer->id;
 | |
| 	const auto it = _reorderRequests.find(peerId);
 | |
| 	if (it != end(_reorderRequests)) {
 | |
| 		_api.request(it->second).cancel();
 | |
| 		_reorderRequests.erase(peerId);
 | |
| 	}
 | |
| 
 | |
| 	return [=](auto consumer) {
 | |
| 		auto lifetime = rpl::lifetime();
 | |
| 
 | |
| 		auto tlUsernames = ranges::views::all(
 | |
| 			usernames
 | |
| 		) | ranges::views::transform([](const QString &username) {
 | |
| 			return MTP_string(username);
 | |
| 		}) | ranges::to<QVector<MTPstring>>;
 | |
| 
 | |
| 		const auto finish = [=] {
 | |
| 			if (_reorderRequests.contains(peerId)) {
 | |
| 				_reorderRequests.erase(peerId);
 | |
| 			}
 | |
| 			consumer.put_done();
 | |
| 		};
 | |
| 		if (usernames.empty()) {
 | |
| 			crl::on_main([=] { consumer.put_done(); });
 | |
| 			return lifetime;
 | |
| 		}
 | |
| 
 | |
| 		if (peer->isSelf()) {
 | |
| 			const auto requestId = _api.request(MTPaccount_ReorderUsernames(
 | |
| 				MTP_vector<MTPstring>(std::move(tlUsernames))
 | |
| 			)).done(finish).fail(finish).send();
 | |
| 			_reorderRequests.emplace(peerId, requestId);
 | |
| 		} else if (const auto channel = peer->asChannel()) {
 | |
| 			const auto requestId = _api.request(MTPchannels_ReorderUsernames(
 | |
| 				channel->inputChannel,
 | |
| 				MTP_vector<MTPstring>(std::move(tlUsernames))
 | |
| 			)).done(finish).fail(finish).send();
 | |
| 			_reorderRequests.emplace(peerId, requestId);
 | |
| 		}
 | |
| 		return lifetime;
 | |
| 	};
 | |
| }
 | |
| 
 | |
| Data::Usernames Usernames::FromTL(const MTPVector<MTPUsername> &usernames) {
 | |
| 	return ranges::views::all(
 | |
| 		usernames.v
 | |
| 	) | ranges::views::transform(UsernameFromTL) | ranges::to_vector;
 | |
| }
 | |
| 
 | |
| void Usernames::requestToCache(not_null<PeerData*> peer) {
 | |
| 	_tinyCache = {};
 | |
| 	if (const auto user = peer->asUser()) {
 | |
| 		if (user->usernames().empty()) {
 | |
| 			return;
 | |
| 		}
 | |
| 	} else if (const auto channel = peer->asChannel()) {
 | |
| 		if (channel->usernames().empty()) {
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	const auto lifetime = std::make_shared<rpl::lifetime>();
 | |
| 	*lifetime = loadUsernames(
 | |
| 		peer
 | |
| 	) | rpl::start_with_next([=, id = peer->id](Data::Usernames usernames) {
 | |
| 		_tinyCache = std::make_pair(id, std::move(usernames));
 | |
| 		lifetime->destroy();
 | |
| 	});
 | |
| }
 | |
| 
 | |
| Data::Usernames Usernames::cacheFor(PeerId id) {
 | |
| 	return (_tinyCache.first == id) ? _tinyCache.second : Data::Usernames();
 | |
| }
 | |
| 
 | |
| } // namespace Api
 | 
