258 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
	
		
			6.3 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 "countries/countries_manager.h"
 | 
						|
 | 
						|
#include "core/application.h"
 | 
						|
#include "countries/countries_instance.h"
 | 
						|
#include "main/main_app_config.h"
 | 
						|
#include "main/main_account.h"
 | 
						|
#include "main/main_domain.h"
 | 
						|
#include "mtproto/mtp_instance.h"
 | 
						|
 | 
						|
#include <QtCore/QFile>
 | 
						|
 | 
						|
namespace Countries {
 | 
						|
namespace {
 | 
						|
 | 
						|
struct FileData {
 | 
						|
	int hash = 0;
 | 
						|
	std::vector<Info> infos;
 | 
						|
};
 | 
						|
 | 
						|
auto ProcessAlternativeName(Info &&info) {
 | 
						|
	if (info.name == u"USA"_q) {
 | 
						|
		info.alternativeName = u"United States of America"_q;
 | 
						|
	}
 | 
						|
	return std::move(info);
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] QByteArray SerializeCodeInfo(const CallingCodeInfo &info) {
 | 
						|
	auto result = QByteArray();
 | 
						|
	auto stream = QDataStream(&result, QIODevice::WriteOnly);
 | 
						|
	stream.setVersion(QDataStream::Qt_5_3);
 | 
						|
	stream
 | 
						|
		<< info.callingCode
 | 
						|
		<< int(info.prefixes.size())
 | 
						|
		<< int(info.patterns.size());
 | 
						|
	for (const auto &prefix : info.prefixes) {
 | 
						|
		stream << prefix;
 | 
						|
	}
 | 
						|
	for (const auto &pattern : info.patterns) {
 | 
						|
		stream << pattern;
 | 
						|
	}
 | 
						|
	stream.device()->close();
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] CallingCodeInfo DeserializeCodeInfo(const QByteArray &data) {
 | 
						|
	auto stream = QDataStream(data);
 | 
						|
	auto result = CallingCodeInfo();
 | 
						|
	auto prefixesCount = qint32(0);
 | 
						|
	auto patternsCount = qint32(0);
 | 
						|
	stream
 | 
						|
		>> result.callingCode
 | 
						|
		>> prefixesCount
 | 
						|
		>> patternsCount;
 | 
						|
	for (auto i = 0; i < prefixesCount; i++) {
 | 
						|
		auto prefix = QString();
 | 
						|
		stream >> prefix;
 | 
						|
		result.prefixes.push_back(std::move(prefix));
 | 
						|
	}
 | 
						|
	for (auto i = 0; i < patternsCount; i++) {
 | 
						|
		auto pattern = QString();
 | 
						|
		stream >> pattern;
 | 
						|
		result.patterns.push_back(std::move(pattern));
 | 
						|
	}
 | 
						|
	return (stream.status() != QDataStream::Ok)
 | 
						|
		? CallingCodeInfo()
 | 
						|
		: result;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] QByteArray SerializeInfo(const Info &info) {
 | 
						|
	auto result = QByteArray();
 | 
						|
	auto stream = QDataStream(&result, QIODevice::WriteOnly);
 | 
						|
	stream.setVersion(QDataStream::Qt_5_3);
 | 
						|
	stream
 | 
						|
		<< info.name
 | 
						|
		<< info.iso2
 | 
						|
		<< info.alternativeName
 | 
						|
		<< info.isHidden
 | 
						|
		<< int(info.codes.size());
 | 
						|
	for (const auto &code : info.codes) {
 | 
						|
		stream << SerializeCodeInfo(code);
 | 
						|
	}
 | 
						|
	stream.device()->close();
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] Info DeserializeInfo(const QByteArray &data) {
 | 
						|
	auto stream = QDataStream(data);
 | 
						|
	auto result = Info();
 | 
						|
	auto codesCount = qint32(0);
 | 
						|
	stream
 | 
						|
		>> result.name
 | 
						|
		>> result.iso2
 | 
						|
		>> result.alternativeName
 | 
						|
		>> result.isHidden
 | 
						|
		>> codesCount;
 | 
						|
	for (auto i = 0; i < codesCount; i++) {
 | 
						|
		auto code = QByteArray();
 | 
						|
		stream >> code;
 | 
						|
		result.codes.push_back(DeserializeCodeInfo(code));
 | 
						|
	}
 | 
						|
	return (stream.status() != QDataStream::Ok)
 | 
						|
		? Info()
 | 
						|
		: result;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] QByteArray Serialize(const FileData &data) {
 | 
						|
	auto result = QByteArray();
 | 
						|
	auto stream = QDataStream(&result, QIODevice::WriteOnly);
 | 
						|
	stream.setVersion(QDataStream::Qt_5_3);
 | 
						|
	stream
 | 
						|
		<< data.hash
 | 
						|
		<< int(data.infos.size());
 | 
						|
	for (const auto &info : data.infos) {
 | 
						|
		stream << SerializeInfo(info);
 | 
						|
	}
 | 
						|
	stream.device()->close();
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] FileData Deserialize(const QByteArray &data) {
 | 
						|
	auto stream = QDataStream(data);
 | 
						|
	auto hash = int(0);
 | 
						|
	auto infosCount = qint32(0);
 | 
						|
	auto infos = std::vector<Info>();
 | 
						|
	stream >> hash >> infosCount;
 | 
						|
	for (auto i = 0; i < infosCount; i++) {
 | 
						|
		auto info = QByteArray();
 | 
						|
		stream >> info;
 | 
						|
		infos.push_back(DeserializeInfo(info));
 | 
						|
	}
 | 
						|
	return (stream.status() != QDataStream::Ok)
 | 
						|
		? FileData()
 | 
						|
		: FileData{ .hash = hash, .infos = std::move(infos) };
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
Manager::Manager(not_null<Main::Domain*> domain)
 | 
						|
: _path(cWorkingDir() + "tdata/countries") {
 | 
						|
	read();
 | 
						|
 | 
						|
	const auto mtpLifetime = _lifetime.make_state<rpl::lifetime>();
 | 
						|
	domain->activeValue(
 | 
						|
	) | rpl::filter([=](Main::Account *account) {
 | 
						|
		return (account != nullptr);
 | 
						|
	}) | rpl::start_with_next_done([=](Main::Account *account) {
 | 
						|
		*mtpLifetime = account->mtpMainSessionValue(
 | 
						|
		) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
 | 
						|
			_api.emplace(instance);
 | 
						|
			request();
 | 
						|
		});
 | 
						|
	}, [=] {
 | 
						|
		_api.reset();
 | 
						|
	}, _lifetime);
 | 
						|
}
 | 
						|
 | 
						|
void Manager::read() {
 | 
						|
	auto file = QFile(_path);
 | 
						|
	if (!file.open(QIODevice::ReadOnly)) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	auto stream = QDataStream(&file);
 | 
						|
	auto data = QByteArray();
 | 
						|
	stream >> data;
 | 
						|
	auto fileData = Deserialize(data);
 | 
						|
 | 
						|
	_hash = fileData.hash;
 | 
						|
	Instance().setList(base::take(fileData.infos));
 | 
						|
}
 | 
						|
 | 
						|
void Manager::write() const {
 | 
						|
	auto file = QFile(_path);
 | 
						|
	if (!file.open(QIODevice::WriteOnly)) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	auto stream = QDataStream(&file);
 | 
						|
	stream << Serialize({ .hash = _hash, .infos = Instance().list() });
 | 
						|
}
 | 
						|
 | 
						|
void Manager::request() {
 | 
						|
	Expects(_api.has_value());
 | 
						|
 | 
						|
	const auto convertMTP = [](const auto &vector, bool force = false) {
 | 
						|
		if (!vector) {
 | 
						|
			return std::vector<QString>(force ? 1 : 0);
 | 
						|
		}
 | 
						|
		return ranges::views::all(
 | 
						|
			vector->v
 | 
						|
		) | ranges::views::transform([](const MTPstring &s) -> QString {
 | 
						|
			return qs(s);
 | 
						|
		}) | ranges::to_vector;
 | 
						|
	};
 | 
						|
 | 
						|
	_api->request(MTPhelp_GetCountriesList(
 | 
						|
		MTP_string(),
 | 
						|
		MTP_int(_hash)
 | 
						|
	)).done([=](const MTPhelp_CountriesList &result) {
 | 
						|
		result.match([&](const MTPDhelp_countriesList &data) {
 | 
						|
			_hash = data.vhash().v;
 | 
						|
 | 
						|
			auto infos = std::vector<Info>();
 | 
						|
 | 
						|
			for (const auto &country : data.vcountries().v) {
 | 
						|
 | 
						|
				const auto &countryData = country.c_help_country();
 | 
						|
				if (countryData.is_hidden()) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
 | 
						|
				auto info = Info(ProcessAlternativeName({
 | 
						|
					.name = countryData.vdefault_name().v,
 | 
						|
					.iso2 = countryData.viso2().v,
 | 
						|
					.isHidden = countryData.is_hidden(),
 | 
						|
				}));
 | 
						|
				for (const auto &code : countryData.vcountry_codes().v) {
 | 
						|
					const auto &codeData = code.c_help_countryCode();
 | 
						|
					info.codes.push_back(CallingCodeInfo{
 | 
						|
						.callingCode = codeData.vcountry_code().v,
 | 
						|
						.prefixes = convertMTP(codeData.vprefixes(), true),
 | 
						|
						.patterns = convertMTP(codeData.vpatterns()),
 | 
						|
					});
 | 
						|
				}
 | 
						|
 | 
						|
				infos.push_back(std::move(info));
 | 
						|
			}
 | 
						|
 | 
						|
			Instance().setList(std::move(infos));
 | 
						|
			write();
 | 
						|
		}, [](const MTPDhelp_countriesListNotModified &data) {
 | 
						|
		});
 | 
						|
		_lifetime.destroy();
 | 
						|
	}).fail([=](const MTP::Error &error) {
 | 
						|
		LOG(("API Error: getting countries failed with error %1"
 | 
						|
			).arg(error.type()));
 | 
						|
		_lifetime.destroy();
 | 
						|
	}).send();
 | 
						|
}
 | 
						|
 | 
						|
rpl::lifetime &Manager::lifetime() {
 | 
						|
	return _lifetime;
 | 
						|
}
 | 
						|
 | 
						|
Manager::~Manager() {
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Countries
 |