1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "main/main_app_config.h"
9 
10 #include "main/main_account.h"
11 #include "base/call_delayed.h"
12 #include "apiwrap.h"
13 
14 namespace Main {
15 namespace {
16 
17 constexpr auto kRefreshTimeout = 3600 * crl::time(1000);
18 
19 } // namespace
20 
AppConfig(not_null<Account * > account)21 AppConfig::AppConfig(not_null<Account*> account) : _account(account) {
22 	account->mtpMainSessionValue(
23 	) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
24 		_api.emplace(instance);
25 		refresh();
26 	}, _lifetime);
27 
28 	account->sessionChanges(
29 	) | rpl::filter([=](Session *session) {
30 		return (session != nullptr);
31 	}) | rpl::start_with_next([=] {
32 		refresh();
33 	}, _lifetime);
34 }
35 
refresh()36 void AppConfig::refresh() {
37 	if (_requestId || !_api) {
38 		return;
39 	}
40 	_requestId = _api->request(MTPhelp_GetAppConfig(
41 	)).done([=](const MTPJSONValue &result) {
42 		_requestId = 0;
43 		refreshDelayed();
44 		if (result.type() == mtpc_jsonObject) {
45 			_data.clear();
46 			for (const auto &element : result.c_jsonObject().vvalue().v) {
47 				element.match([&](const MTPDjsonObjectValue &data) {
48 					_data.emplace_or_assign(qs(data.vkey()), data.vvalue());
49 				});
50 			}
51 			DEBUG_LOG(("getAppConfig result handled."));
52 		}
53 		_refreshed.fire({});
54 	}).fail([=](const MTP::Error &error) {
55 		_requestId = 0;
56 		refreshDelayed();
57 	}).send();
58 }
59 
refreshDelayed()60 void AppConfig::refreshDelayed() {
61 	base::call_delayed(kRefreshTimeout, _account, [=] {
62 		refresh();
63 	});
64 }
65 
refreshed() const66 rpl::producer<> AppConfig::refreshed() const {
67 	return _refreshed.events();
68 }
69 
value() const70 rpl::producer<> AppConfig::value() const {
71 	return _refreshed.events_starting_with({});
72 }
73 
74 template <typename Extractor>
getValue(const QString & key,Extractor && extractor) const75 auto AppConfig::getValue(const QString &key, Extractor &&extractor) const {
76 	const auto i = _data.find(key);
77 	return extractor((i != end(_data))
78 		? i->second
79 		: MTPJSONValue(MTP_jsonNull()));
80 }
81 
getBool(const QString & key,bool fallback) const82 bool AppConfig::getBool(const QString &key, bool fallback) const {
83 	return getValue(key, [&](const MTPJSONValue &value) {
84 		return value.match([&](const MTPDjsonBool &data) {
85 			return mtpIsTrue(data.vvalue());
86 		}, [&](const auto &data) {
87 			return fallback;
88 		});
89 	});
90 }
91 
getDouble(const QString & key,double fallback) const92 double AppConfig::getDouble(const QString &key, double fallback) const {
93 	return getValue(key, [&](const MTPJSONValue &value) {
94 		return value.match([&](const MTPDjsonNumber &data) {
95 			return data.vvalue().v;
96 		}, [&](const auto &data) {
97 			return fallback;
98 		});
99 	});
100 }
101 
getString(const QString & key,const QString & fallback) const102 QString AppConfig::getString(
103 		const QString &key,
104 		const QString &fallback) const {
105 	return getValue(key, [&](const MTPJSONValue &value) {
106 		return value.match([&](const MTPDjsonString &data) {
107 			return qs(data.vvalue());
108 		}, [&](const auto &data) {
109 			return fallback;
110 		});
111 	});
112 }
113 
getStringArray(const QString & key,std::vector<QString> && fallback) const114 std::vector<QString> AppConfig::getStringArray(
115 		const QString &key,
116 		std::vector<QString> &&fallback) const {
117 	return getValue(key, [&](const MTPJSONValue &value) {
118 		return value.match([&](const MTPDjsonArray &data) {
119 			auto result = std::vector<QString>();
120 			result.reserve(data.vvalue().v.size());
121 			for (const auto &entry : data.vvalue().v) {
122 				if (entry.type() != mtpc_jsonString) {
123 					return std::move(fallback);
124 				}
125 				result.push_back(qs(entry.c_jsonString().vvalue()));
126 			}
127 			return result;
128 		}, [&](const auto &data) {
129 			return std::move(fallback);
130 		});
131 	});
132 }
133 
getStringMapArray(const QString & key,std::vector<std::map<QString,QString>> && fallback) const134 std::vector<std::map<QString, QString>> AppConfig::getStringMapArray(
135 		const QString &key,
136 		std::vector<std::map<QString, QString>> &&fallback) const {
137 	return getValue(key, [&](const MTPJSONValue &value) {
138 		return value.match([&](const MTPDjsonArray &data) {
139 			auto result = std::vector<std::map<QString, QString>>();
140 			result.reserve(data.vvalue().v.size());
141 			for (const auto &entry : data.vvalue().v) {
142 				if (entry.type() != mtpc_jsonObject) {
143 					return std::move(fallback);
144 				}
145 				auto element = std::map<QString, QString>();
146 				for (const auto &field : entry.c_jsonObject().vvalue().v) {
147 					const auto &data = field.c_jsonObjectValue();
148 					if (data.vvalue().type() != mtpc_jsonString) {
149 						return std::move(fallback);
150 					}
151 					element.emplace(
152 						qs(data.vkey()),
153 						qs(data.vvalue().c_jsonString().vvalue()));
154 				}
155 				result.push_back(std::move(element));
156 			}
157 			return result;
158 		}, [&](const auto &data) {
159 			return std::move(fallback);
160 		});
161 	});
162 }
163 
suggestionCurrent(const QString & key) const164 bool AppConfig::suggestionCurrent(const QString &key) const {
165 	return !_dismissedSuggestions.contains(key)
166 		&& ranges::contains(
167 			get<std::vector<QString>>(
168 				u"pending_suggestions"_q,
169 				std::vector<QString>()),
170 			key);
171 }
172 
suggestionRequested(const QString & key) const173 rpl::producer<> AppConfig::suggestionRequested(const QString &key) const {
174 	return value(
175 	) | rpl::filter([=] {
176 		return suggestionCurrent(key);
177 	});
178 }
179 
dismissSuggestion(const QString & key)180 void AppConfig::dismissSuggestion(const QString &key) {
181 	if (!_dismissedSuggestions.emplace(key).second) {
182 		return;
183 	}
184 	_api->request(MTPhelp_DismissSuggestion(
185 		MTP_inputPeerEmpty(),
186 		MTP_string(key)
187 	)).send();
188 }
189 
190 } // namespace Main
191