countries_manager.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "countries/countries_manager.h"
  8. #include "core/application.h"
  9. #include "countries/countries_instance.h"
  10. #include "main/main_app_config.h"
  11. #include "main/main_account.h"
  12. #include "main/main_domain.h"
  13. #include "mtproto/mtp_instance.h"
  14. #include <QtCore/QFile>
  15. namespace Countries {
  16. namespace {
  17. struct FileData {
  18. int hash = 0;
  19. std::vector<Info> infos;
  20. };
  21. auto ProcessAlternativeName(Info &&info) {
  22. if (info.name == u"USA"_q) {
  23. info.alternativeName = u"United States of America"_q;
  24. }
  25. return std::move(info);
  26. }
  27. [[nodiscard]] QByteArray SerializeCodeInfo(const CallingCodeInfo &info) {
  28. auto result = QByteArray();
  29. auto stream = QDataStream(&result, QIODevice::WriteOnly);
  30. stream.setVersion(QDataStream::Qt_5_3);
  31. stream
  32. << info.callingCode
  33. << int(info.prefixes.size())
  34. << int(info.patterns.size());
  35. for (const auto &prefix : info.prefixes) {
  36. stream << prefix;
  37. }
  38. for (const auto &pattern : info.patterns) {
  39. stream << pattern;
  40. }
  41. stream.device()->close();
  42. return result;
  43. }
  44. [[nodiscard]] CallingCodeInfo DeserializeCodeInfo(const QByteArray &data) {
  45. auto stream = QDataStream(data);
  46. auto result = CallingCodeInfo();
  47. auto prefixesCount = qint32(0);
  48. auto patternsCount = qint32(0);
  49. stream
  50. >> result.callingCode
  51. >> prefixesCount
  52. >> patternsCount;
  53. for (auto i = 0; i < prefixesCount; i++) {
  54. auto prefix = QString();
  55. stream >> prefix;
  56. result.prefixes.push_back(std::move(prefix));
  57. }
  58. for (auto i = 0; i < patternsCount; i++) {
  59. auto pattern = QString();
  60. stream >> pattern;
  61. result.patterns.push_back(std::move(pattern));
  62. }
  63. return (stream.status() != QDataStream::Ok)
  64. ? CallingCodeInfo()
  65. : result;
  66. }
  67. [[nodiscard]] QByteArray SerializeInfo(const Info &info) {
  68. auto result = QByteArray();
  69. auto stream = QDataStream(&result, QIODevice::WriteOnly);
  70. stream.setVersion(QDataStream::Qt_5_3);
  71. stream
  72. << info.name
  73. << info.iso2
  74. << info.alternativeName
  75. << info.isHidden
  76. << int(info.codes.size());
  77. for (const auto &code : info.codes) {
  78. stream << SerializeCodeInfo(code);
  79. }
  80. stream.device()->close();
  81. return result;
  82. }
  83. [[nodiscard]] Info DeserializeInfo(const QByteArray &data) {
  84. auto stream = QDataStream(data);
  85. auto result = Info();
  86. auto codesCount = qint32(0);
  87. stream
  88. >> result.name
  89. >> result.iso2
  90. >> result.alternativeName
  91. >> result.isHidden
  92. >> codesCount;
  93. for (auto i = 0; i < codesCount; i++) {
  94. auto code = QByteArray();
  95. stream >> code;
  96. result.codes.push_back(DeserializeCodeInfo(code));
  97. }
  98. return (stream.status() != QDataStream::Ok)
  99. ? Info()
  100. : result;
  101. }
  102. [[nodiscard]] QByteArray Serialize(const FileData &data) {
  103. auto result = QByteArray();
  104. auto stream = QDataStream(&result, QIODevice::WriteOnly);
  105. stream.setVersion(QDataStream::Qt_5_3);
  106. stream
  107. << data.hash
  108. << int(data.infos.size());
  109. for (const auto &info : data.infos) {
  110. stream << SerializeInfo(info);
  111. }
  112. stream.device()->close();
  113. return result;
  114. }
  115. [[nodiscard]] FileData Deserialize(const QByteArray &data) {
  116. auto stream = QDataStream(data);
  117. auto hash = int(0);
  118. auto infosCount = qint32(0);
  119. auto infos = std::vector<Info>();
  120. stream >> hash >> infosCount;
  121. for (auto i = 0; i < infosCount; i++) {
  122. auto info = QByteArray();
  123. stream >> info;
  124. infos.push_back(DeserializeInfo(info));
  125. }
  126. return (stream.status() != QDataStream::Ok)
  127. ? FileData()
  128. : FileData{ .hash = hash, .infos = std::move(infos) };
  129. }
  130. } // namespace
  131. Manager::Manager(not_null<Main::Domain*> domain)
  132. : _path(cWorkingDir() + "tdata/countries") {
  133. read();
  134. const auto mtpLifetime = _lifetime.make_state<rpl::lifetime>();
  135. domain->activeValue(
  136. ) | rpl::filter([=](Main::Account *account) {
  137. return (account != nullptr);
  138. }) | rpl::start_with_next_done([=](Main::Account *account) {
  139. *mtpLifetime = account->mtpMainSessionValue(
  140. ) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
  141. _api.emplace(instance);
  142. request();
  143. });
  144. }, [=] {
  145. _api.reset();
  146. }, _lifetime);
  147. }
  148. void Manager::read() {
  149. auto file = QFile(_path);
  150. if (!file.open(QIODevice::ReadOnly)) {
  151. return;
  152. }
  153. auto stream = QDataStream(&file);
  154. auto data = QByteArray();
  155. stream >> data;
  156. auto fileData = Deserialize(data);
  157. _hash = fileData.hash;
  158. Instance().setList(base::take(fileData.infos));
  159. }
  160. void Manager::write() const {
  161. auto file = QFile(_path);
  162. if (!file.open(QIODevice::WriteOnly)) {
  163. return;
  164. }
  165. auto stream = QDataStream(&file);
  166. stream << Serialize({ .hash = _hash, .infos = Instance().list() });
  167. }
  168. void Manager::request() {
  169. Expects(_api.has_value());
  170. const auto convertMTP = [](const auto &vector, bool force = false) {
  171. if (!vector) {
  172. return std::vector<QString>(force ? 1 : 0);
  173. }
  174. return ranges::views::all(
  175. vector->v
  176. ) | ranges::views::transform([](const MTPstring &s) -> QString {
  177. return qs(s);
  178. }) | ranges::to_vector;
  179. };
  180. _api->request(MTPhelp_GetCountriesList(
  181. MTP_string(),
  182. MTP_int(_hash)
  183. )).done([=](const MTPhelp_CountriesList &result) {
  184. result.match([&](const MTPDhelp_countriesList &data) {
  185. _hash = data.vhash().v;
  186. auto infos = std::vector<Info>();
  187. for (const auto &country : data.vcountries().v) {
  188. const auto &countryData = country.c_help_country();
  189. if (countryData.is_hidden()) {
  190. continue;
  191. }
  192. auto info = Info(ProcessAlternativeName({
  193. .name = countryData.vdefault_name().v,
  194. .iso2 = countryData.viso2().v,
  195. .isHidden = countryData.is_hidden(),
  196. }));
  197. for (const auto &code : countryData.vcountry_codes().v) {
  198. const auto &codeData = code.c_help_countryCode();
  199. info.codes.push_back(CallingCodeInfo{
  200. .callingCode = codeData.vcountry_code().v,
  201. .prefixes = convertMTP(codeData.vprefixes(), true),
  202. .patterns = convertMTP(codeData.vpatterns()),
  203. });
  204. }
  205. infos.push_back(std::move(info));
  206. }
  207. Instance().setList(std::move(infos));
  208. write();
  209. }, [](const MTPDhelp_countriesListNotModified &data) {
  210. });
  211. _lifetime.destroy();
  212. }).fail([=](const MTP::Error &error) {
  213. LOG(("API Error: getting countries failed with error %1"
  214. ).arg(error.type()));
  215. _lifetime.destroy();
  216. }).send();
  217. }
  218. rpl::lifetime &Manager::lifetime() {
  219. return _lifetime;
  220. }
  221. Manager::~Manager() {
  222. }
  223. } // namespace Countries