settings_location.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 "settings/business/settings_location.h"
  8. #include "core/application.h"
  9. #include "core/shortcuts.h"
  10. #include "data/business/data_business_info.h"
  11. #include "data/data_file_origin.h"
  12. #include "data/data_session.h"
  13. #include "data/data_user.h"
  14. #include "lang/lang_keys.h"
  15. #include "main/main_app_config.h"
  16. #include "main/main_session.h"
  17. #include "mainwidget.h"
  18. #include "mainwindow.h"
  19. #include "settings/business/settings_recipients_helper.h"
  20. #include "settings/settings_common.h"
  21. #include "storage/storage_account.h"
  22. #include "ui/controls/location_picker.h"
  23. #include "ui/text/text_utilities.h"
  24. #include "ui/widgets/fields/input_field.h"
  25. #include "ui/widgets/buttons.h"
  26. #include "ui/wrap/slide_wrap.h"
  27. #include "ui/wrap/vertical_layout.h"
  28. #include "ui/vertical_list.h"
  29. #include "window/window_session_controller.h"
  30. #include "styles/style_chat.h"
  31. #include "styles/style_layers.h"
  32. #include "styles/style_menu_icons.h"
  33. #include "styles/style_settings.h"
  34. namespace Settings {
  35. namespace {
  36. class Location : public BusinessSection<Location> {
  37. public:
  38. Location(
  39. QWidget *parent,
  40. not_null<Window::SessionController*> controller);
  41. ~Location();
  42. [[nodiscard]] rpl::producer<QString> title() override;
  43. const Ui::RoundRect *bottomSkipRounding() const override {
  44. return mapSupported() ? nullptr : &_bottomSkipRounding;
  45. }
  46. private:
  47. void setupContent(not_null<Window::SessionController*> controller);
  48. void save();
  49. void setupPicker(not_null<Ui::VerticalLayout*> content);
  50. void setupUnsupported(not_null<Ui::VerticalLayout*> content);
  51. [[nodiscard]] bool mapSupported() const;
  52. void chooseOnMap();
  53. const Ui::LocationPickerConfig _config;
  54. rpl::variable<Data::BusinessLocation> _data;
  55. rpl::variable<Data::CloudImage*> _map = nullptr;
  56. base::weak_ptr<Ui::LocationPicker> _picker;
  57. std::shared_ptr<QImage> _view;
  58. Ui::RoundRect _bottomSkipRounding;
  59. };
  60. [[nodiscard]] Ui::LocationPickerConfig ResolveBusinessMapsConfig(
  61. not_null<Main::Session*> session) {
  62. const auto &appConfig = session->appConfig();
  63. auto map = appConfig.get<base::flat_map<QString, QString>>(
  64. u"tdesktop_config_map"_q,
  65. base::flat_map<QString, QString>());
  66. return {
  67. .mapsToken = map[u"bmaps"_q],
  68. .geoToken = map[u"bgeo"_q],
  69. };
  70. }
  71. Location::Location(
  72. QWidget *parent,
  73. not_null<Window::SessionController*> controller)
  74. : BusinessSection(parent, controller)
  75. , _config(ResolveBusinessMapsConfig(&controller->session()))
  76. , _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
  77. setupContent(controller);
  78. }
  79. Location::~Location() {
  80. if (!Core::Quitting()) {
  81. save();
  82. }
  83. }
  84. rpl::producer<QString> Location::title() {
  85. return tr::lng_location_title();
  86. }
  87. void Location::setupContent(
  88. not_null<Window::SessionController*> controller) {
  89. using namespace rpl::mappers;
  90. const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
  91. if (mapSupported()) {
  92. setupPicker(content);
  93. } else {
  94. setupUnsupported(content);
  95. }
  96. Ui::ResizeFitChild(this, content);
  97. }
  98. void Location::setupPicker(not_null<Ui::VerticalLayout*> content) {
  99. _data = controller()->session().user()->businessDetails().location;
  100. AddDividerTextWithLottie(content, {
  101. .lottie = u"location"_q,
  102. .lottieSize = st::settingsCloudPasswordIconSize,
  103. .lottieMargins = st::peerAppearanceIconPadding,
  104. .showFinished = showFinishes(),
  105. .about = tr::lng_location_about(Ui::Text::WithEntities),
  106. .aboutMargins = st::peerAppearanceCoverLabelMargin,
  107. });
  108. const auto address = content->add(
  109. object_ptr<Ui::InputField>(
  110. content,
  111. st::settingsLocationAddress,
  112. Ui::InputField::Mode::MultiLine,
  113. tr::lng_location_address(),
  114. _data.current().address),
  115. st::settingsChatbotsUsernameMargins);
  116. _data.value(
  117. ) | rpl::start_with_next([=](const Data::BusinessLocation &location) {
  118. address->setText(location.address);
  119. }, address->lifetime());
  120. address->changes() | rpl::start_with_next([=] {
  121. auto copy = _data.current();
  122. copy.address = address->getLastText();
  123. _data = std::move(copy);
  124. }, address->lifetime());
  125. AddDivider(content);
  126. AddSkip(content);
  127. const auto maptoggle = AddButtonWithIcon(
  128. content,
  129. tr::lng_location_set_map(),
  130. st::settingsButton,
  131. { &st::menuIconAddress }
  132. )->toggleOn(_data.value(
  133. ) | rpl::map([](const Data::BusinessLocation &location) {
  134. return location.point.has_value();
  135. }));
  136. maptoggle->toggledValue() | rpl::start_with_next([=](bool toggled) {
  137. if (!toggled) {
  138. auto copy = _data.current();
  139. if (copy.point.has_value()) {
  140. copy.point = std::nullopt;
  141. _data = std::move(copy);
  142. }
  143. } else if (!_data.current().point.has_value()) {
  144. _data.force_assign(_data.current());
  145. chooseOnMap();
  146. }
  147. }, maptoggle->lifetime());
  148. const auto mapSkip = st::defaultVerticalListSkip;
  149. const auto mapWrap = content->add(
  150. object_ptr<Ui::SlideWrap<Ui::AbstractButton>>(
  151. content,
  152. object_ptr<Ui::AbstractButton>(content),
  153. st::boxRowPadding + QMargins(0, mapSkip, 0, mapSkip)));
  154. mapWrap->toggle(_data.current().point.has_value(), anim::type::instant);
  155. const auto map = mapWrap->entity();
  156. map->resize(map->width(), st::locationSize.height());
  157. _data.value(
  158. ) | rpl::start_with_next([=](const Data::BusinessLocation &location) {
  159. const auto image = location.point.has_value()
  160. ? controller()->session().data().location(*location.point).get()
  161. : nullptr;
  162. if (image) {
  163. image->load(&controller()->session(), {});
  164. _view = image->createView();
  165. }
  166. mapWrap->toggle(image != nullptr, anim::type::normal);
  167. _map = image;
  168. }, mapWrap->lifetime());
  169. map->paintRequest() | rpl::start_with_next([=] {
  170. auto p = QPainter(map);
  171. const auto left = (map->width() - st::locationSize.width()) / 2;
  172. const auto rect = QRect(QPoint(left, 0), st::locationSize);
  173. const auto &image = _view ? *_view : QImage();
  174. if (!image.isNull()) {
  175. p.drawImage(rect, image);
  176. }
  177. const auto paintMarker = [&](const style::icon &icon) {
  178. icon.paint(
  179. p,
  180. rect.x() + ((rect.width() - icon.width()) / 2),
  181. rect.y() + (rect.height() / 2) - icon.height(),
  182. width());
  183. };
  184. paintMarker(st::historyMapPoint);
  185. paintMarker(st::historyMapPointInner);
  186. }, map->lifetime());
  187. controller()->session().downloaderTaskFinished(
  188. ) | rpl::start_with_next([=] {
  189. map->update();
  190. }, map->lifetime());
  191. map->setClickedCallback([=] {
  192. chooseOnMap();
  193. });
  194. showFinishes() | rpl::start_with_next([=] {
  195. address->setFocus();
  196. }, address->lifetime());
  197. }
  198. void Location::chooseOnMap() {
  199. if (const auto strong = _picker.get()) {
  200. strong->activate();
  201. return;
  202. }
  203. const auto callback = [=](Data::InputVenue venue) {
  204. auto copy = _data.current();
  205. copy.point = Data::LocationPoint(
  206. venue.lat,
  207. venue.lon,
  208. Data::LocationPoint::NoAccessHash);
  209. copy.address = venue.address;
  210. _data = std::move(copy);
  211. };
  212. const auto session = &controller()->session();
  213. const auto current = _data.current().point;
  214. const auto initial = current
  215. ? Core::GeoLocation{
  216. .point = { current->lat(), current->lon() },
  217. .accuracy = Core::GeoLocationAccuracy::Exact,
  218. }
  219. : Core::GeoLocation();
  220. _picker = Ui::LocationPicker::Show({
  221. .parent = controller()->widget(),
  222. .config = _config,
  223. .chooseLabel = tr::lng_maps_point_set(),
  224. .session = session,
  225. .initial = initial,
  226. .callback = crl::guard(this, callback),
  227. .quit = [] { Shortcuts::Launch(Shortcuts::Command::Quit); },
  228. .storageId = session->local().resolveStorageIdBots(),
  229. .closeRequests = death(),
  230. });
  231. }
  232. void Location::setupUnsupported(not_null<Ui::VerticalLayout*> content) {
  233. AddDividerTextWithLottie(content, {
  234. .lottie = u"phone"_q,
  235. .lottieSize = st::settingsCloudPasswordIconSize,
  236. .lottieMargins = st::peerAppearanceIconPadding,
  237. .showFinished = showFinishes(),
  238. .about = tr::lng_location_fallback(Ui::Text::WithEntities),
  239. .aboutMargins = st::peerAppearanceCoverLabelMargin,
  240. .parts = RectPart::Top,
  241. });
  242. }
  243. void Location::save() {
  244. const auto fail = [=](QString error) {
  245. };
  246. auto value = _data.current();
  247. value.address = value.address.trimmed();
  248. controller()->session().data().businessInfo().saveLocation(value, fail);
  249. }
  250. bool Location::mapSupported() const {
  251. return Ui::LocationPicker::Available(_config);
  252. }
  253. } // namespace
  254. Type LocationId() {
  255. return Location::Id();
  256. }
  257. } // namespace Settings