data_business_info.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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 "data/business/data_business_info.h"
  8. #include "apiwrap.h"
  9. #include "base/unixtime.h"
  10. #include "data/business/data_business_common.h"
  11. #include "data/data_document.h"
  12. #include "data/data_session.h"
  13. #include "data/data_user.h"
  14. #include "main/main_session.h"
  15. namespace Data {
  16. namespace {
  17. [[nodiscard]] MTPBusinessWorkHours ToMTP(const WorkingHours &data) {
  18. const auto list = data.intervals.normalized().list;
  19. const auto proj = [](const WorkingInterval &data) {
  20. return MTPBusinessWeeklyOpen(MTP_businessWeeklyOpen(
  21. MTP_int(data.start / 60),
  22. MTP_int(data.end / 60)));
  23. };
  24. return MTP_businessWorkHours(
  25. MTP_flags(0),
  26. MTP_string(data.timezoneId),
  27. MTP_vector_from_range(list | ranges::views::transform(proj)));
  28. }
  29. [[nodiscard]] MTPBusinessAwayMessageSchedule ToMTP(
  30. const AwaySchedule &data) {
  31. Expects(data.type != AwayScheduleType::Never);
  32. return (data.type == AwayScheduleType::Always)
  33. ? MTP_businessAwayMessageScheduleAlways()
  34. : (data.type == AwayScheduleType::OutsideWorkingHours)
  35. ? MTP_businessAwayMessageScheduleOutsideWorkHours()
  36. : MTP_businessAwayMessageScheduleCustom(
  37. MTP_int(data.customInterval.start),
  38. MTP_int(data.customInterval.end));
  39. }
  40. [[nodiscard]] MTPInputBusinessAwayMessage ToMTP(const AwaySettings &data) {
  41. using Flag = MTPDinputBusinessAwayMessage::Flag;
  42. return MTP_inputBusinessAwayMessage(
  43. MTP_flags(data.offlineOnly ? Flag::f_offline_only : Flag()),
  44. MTP_int(data.shortcutId),
  45. ToMTP(data.schedule),
  46. ForMessagesToMTP(data.recipients));
  47. }
  48. [[nodiscard]] MTPInputBusinessGreetingMessage ToMTP(
  49. const GreetingSettings &data) {
  50. return MTP_inputBusinessGreetingMessage(
  51. MTP_int(data.shortcutId),
  52. ForMessagesToMTP(data.recipients),
  53. MTP_int(data.noActivityDays));
  54. }
  55. } // namespace
  56. BusinessInfo::BusinessInfo(not_null<Session*> owner)
  57. : _owner(owner) {
  58. }
  59. BusinessInfo::~BusinessInfo() = default;
  60. void BusinessInfo::saveWorkingHours(
  61. WorkingHours data,
  62. Fn<void(QString)> fail) {
  63. const auto session = &_owner->session();
  64. auto details = session->user()->businessDetails();
  65. const auto &was = details.hours;
  66. if (was == data) {
  67. return;
  68. }
  69. using Flag = MTPaccount_UpdateBusinessWorkHours::Flag;
  70. session->api().request(MTPaccount_UpdateBusinessWorkHours(
  71. MTP_flags(data ? Flag::f_business_work_hours : Flag()),
  72. ToMTP(data)
  73. )).fail([=](const MTP::Error &error) {
  74. auto details = session->user()->businessDetails();
  75. details.hours = was;
  76. session->user()->setBusinessDetails(std::move(details));
  77. if (fail) {
  78. fail(error.type());
  79. }
  80. }).send();
  81. details.hours = std::move(data);
  82. session->user()->setBusinessDetails(std::move(details));
  83. }
  84. void BusinessInfo::saveChatIntro(ChatIntro data, Fn<void(QString)> fail) {
  85. const auto session = &_owner->session();
  86. auto details = session->user()->businessDetails();
  87. const auto &was = details.intro;
  88. if (was == data) {
  89. return;
  90. } else {
  91. const auto session = &_owner->session();
  92. using Flag = MTPaccount_UpdateBusinessIntro::Flag;
  93. session->api().request(MTPaccount_UpdateBusinessIntro(
  94. MTP_flags(data ? Flag::f_intro : Flag()),
  95. MTP_inputBusinessIntro(
  96. MTP_flags(data.sticker
  97. ? MTPDinputBusinessIntro::Flag::f_sticker
  98. : MTPDinputBusinessIntro::Flag()),
  99. MTP_string(data.title),
  100. MTP_string(data.description),
  101. (data.sticker
  102. ? data.sticker->mtpInput()
  103. : MTP_inputDocumentEmpty()))
  104. )).fail([=](const MTP::Error &error) {
  105. auto details = session->user()->businessDetails();
  106. details.intro = was;
  107. session->user()->setBusinessDetails(std::move(details));
  108. if (fail) {
  109. fail(error.type());
  110. }
  111. }).send();
  112. }
  113. details.intro = std::move(data);
  114. session->user()->setBusinessDetails(std::move(details));
  115. }
  116. void BusinessInfo::saveLocation(
  117. BusinessLocation data,
  118. Fn<void(QString)> fail) {
  119. const auto session = &_owner->session();
  120. auto details = session->user()->businessDetails();
  121. const auto &was = details.location;
  122. if (was == data) {
  123. return;
  124. } else {
  125. const auto session = &_owner->session();
  126. using Flag = MTPaccount_UpdateBusinessLocation::Flag;
  127. session->api().request(MTPaccount_UpdateBusinessLocation(
  128. MTP_flags((data.point ? Flag::f_geo_point : Flag())
  129. | (data.address.isEmpty() ? Flag() : Flag::f_address)),
  130. (data.point
  131. ? MTP_inputGeoPoint(
  132. MTP_flags(0),
  133. MTP_double(data.point->lat()),
  134. MTP_double(data.point->lon()),
  135. MTPint()) // accuracy_radius
  136. : MTP_inputGeoPointEmpty()),
  137. MTP_string(data.address)
  138. )).fail([=](const MTP::Error &error) {
  139. auto details = session->user()->businessDetails();
  140. details.location = was;
  141. session->user()->setBusinessDetails(std::move(details));
  142. if (fail) {
  143. fail(error.type());
  144. }
  145. }).send();
  146. }
  147. details.location = std::move(data);
  148. session->user()->setBusinessDetails(std::move(details));
  149. }
  150. void BusinessInfo::applyAwaySettings(AwaySettings data) {
  151. if (_awaySettings == data) {
  152. return;
  153. }
  154. _awaySettings = data;
  155. _awaySettingsChanged.fire({});
  156. }
  157. void BusinessInfo::saveAwaySettings(
  158. AwaySettings data,
  159. Fn<void(QString)> fail) {
  160. const auto &was = _awaySettings;
  161. if (was == data) {
  162. return;
  163. } else if (!data || data.shortcutId) {
  164. using Flag = MTPaccount_UpdateBusinessAwayMessage::Flag;
  165. const auto session = &_owner->session();
  166. session->api().request(MTPaccount_UpdateBusinessAwayMessage(
  167. MTP_flags(data ? Flag::f_message : Flag()),
  168. data ? ToMTP(data) : MTPInputBusinessAwayMessage()
  169. )).fail([=](const MTP::Error &error) {
  170. _awaySettings = was;
  171. _awaySettingsChanged.fire({});
  172. if (fail) {
  173. fail(error.type());
  174. }
  175. }).send();
  176. }
  177. _awaySettings = std::move(data);
  178. _awaySettingsChanged.fire({});
  179. }
  180. bool BusinessInfo::awaySettingsLoaded() const {
  181. return _awaySettings.has_value();
  182. }
  183. AwaySettings BusinessInfo::awaySettings() const {
  184. return _awaySettings.value_or(AwaySettings());
  185. }
  186. rpl::producer<> BusinessInfo::awaySettingsChanged() const {
  187. return _awaySettingsChanged.events();
  188. }
  189. void BusinessInfo::applyGreetingSettings(GreetingSettings data) {
  190. if (_greetingSettings == data) {
  191. return;
  192. }
  193. _greetingSettings = data;
  194. _greetingSettingsChanged.fire({});
  195. }
  196. void BusinessInfo::saveGreetingSettings(
  197. GreetingSettings data,
  198. Fn<void(QString)> fail) {
  199. const auto &was = _greetingSettings;
  200. if (was == data) {
  201. return;
  202. } else if (!data || data.shortcutId) {
  203. using Flag = MTPaccount_UpdateBusinessGreetingMessage::Flag;
  204. _owner->session().api().request(
  205. MTPaccount_UpdateBusinessGreetingMessage(
  206. MTP_flags(data ? Flag::f_message : Flag()),
  207. data ? ToMTP(data) : MTPInputBusinessGreetingMessage())
  208. ).fail([=](const MTP::Error &error) {
  209. _greetingSettings = was;
  210. _greetingSettingsChanged.fire({});
  211. if (fail) {
  212. fail(error.type());
  213. }
  214. }).send();
  215. }
  216. _greetingSettings = std::move(data);
  217. _greetingSettingsChanged.fire({});
  218. }
  219. bool BusinessInfo::greetingSettingsLoaded() const {
  220. return _greetingSettings.has_value();
  221. }
  222. GreetingSettings BusinessInfo::greetingSettings() const {
  223. return _greetingSettings.value_or(GreetingSettings());
  224. }
  225. rpl::producer<> BusinessInfo::greetingSettingsChanged() const {
  226. return _greetingSettingsChanged.events();
  227. }
  228. void BusinessInfo::preload() {
  229. preloadTimezones();
  230. }
  231. void BusinessInfo::preloadTimezones() {
  232. if (!_timezones.current().list.empty() || _timezonesRequestId) {
  233. return;
  234. }
  235. _timezonesRequestId = _owner->session().api().request(
  236. MTPhelp_GetTimezonesList(MTP_int(_timezonesHash))
  237. ).done([=](const MTPhelp_TimezonesList &result) {
  238. result.match([&](const MTPDhelp_timezonesList &data) {
  239. _timezonesHash = data.vhash().v;
  240. const auto proj = [](const MTPtimezone &result) {
  241. return Timezone{
  242. .id = qs(result.data().vid()),
  243. .name = qs(result.data().vname()),
  244. .utcOffset = result.data().vutc_offset().v,
  245. };
  246. };
  247. _timezones = Timezones{
  248. .list = ranges::views::all(
  249. data.vtimezones().v
  250. ) | ranges::views::transform(
  251. proj
  252. ) | ranges::to_vector,
  253. };
  254. }, [](const MTPDhelp_timezonesListNotModified &) {
  255. });
  256. }).send();
  257. }
  258. rpl::producer<Timezones> BusinessInfo::timezonesValue() const {
  259. const_cast<BusinessInfo*>(this)->preloadTimezones();
  260. return _timezones.value();
  261. }
  262. bool BusinessInfo::timezonesLoaded() const {
  263. return !_timezones.current().list.empty();
  264. }
  265. QString FindClosestTimezoneId(const std::vector<Timezone> &list) {
  266. const auto local = QDateTime::currentDateTime();
  267. const auto utc = QDateTime(local.date(), local.time(), Qt::UTC);
  268. const auto shift = base::unixtime::now() - (TimeId)::time(nullptr);
  269. const auto delta = int(utc.toSecsSinceEpoch())
  270. - int(local.toSecsSinceEpoch())
  271. - shift;
  272. const auto proj = [&](const Timezone &value) {
  273. auto distance = value.utcOffset - delta;
  274. while (distance > 12 * 3600) {
  275. distance -= 24 * 3600;
  276. }
  277. while (distance < -12 * 3600) {
  278. distance += 24 * 3600;
  279. }
  280. return std::abs(distance);
  281. };
  282. return ranges::min_element(list, ranges::less(), proj)->id;
  283. }
  284. } // namespace Data