edit_contact_box.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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 "boxes/peers/edit_contact_box.h"
  8. #include "data/data_user.h"
  9. #include "data/data_session.h"
  10. #include "boxes/peers/edit_peer_common.h"
  11. #include "ui/wrap/vertical_layout.h"
  12. #include "ui/widgets/labels.h"
  13. #include "ui/widgets/checkbox.h"
  14. #include "ui/widgets/fields/input_field.h"
  15. #include "ui/text/format_values.h" // Ui::FormatPhone
  16. #include "ui/text/text_utilities.h"
  17. #include "info/profile/info_profile_cover.h"
  18. #include "lang/lang_keys.h"
  19. #include "window/window_controller.h"
  20. #include "ui/toast/toast.h"
  21. #include "main/main_session.h"
  22. #include "apiwrap.h"
  23. #include "api/api_peer_photo.h"
  24. #include "styles/style_layers.h"
  25. #include "styles/style_boxes.h"
  26. #include "styles/style_info.h"
  27. namespace {
  28. QString UserPhone(not_null<UserData*> user) {
  29. const auto phone = user->phone();
  30. return phone.isEmpty()
  31. ? user->owner().findContactPhone(peerToUser(user->id))
  32. : phone;
  33. }
  34. void SendRequest(
  35. QPointer<Ui::GenericBox> box,
  36. not_null<UserData*> user,
  37. bool sharePhone,
  38. const QString &first,
  39. const QString &last,
  40. const QString &phone,
  41. Fn<void()> done) {
  42. const auto wasContact = user->isContact();
  43. using Flag = MTPcontacts_AddContact::Flag;
  44. user->session().api().request(MTPcontacts_AddContact(
  45. MTP_flags(sharePhone
  46. ? Flag::f_add_phone_privacy_exception
  47. : Flag(0)),
  48. user->inputUser,
  49. MTP_string(first),
  50. MTP_string(last),
  51. MTP_string(phone)
  52. )).done([=](const MTPUpdates &result) {
  53. user->setName(
  54. first,
  55. last,
  56. user->nameOrPhone,
  57. user->username());
  58. user->session().api().applyUpdates(result);
  59. if (const auto settings = user->barSettings()) {
  60. const auto flags = PeerBarSetting::AddContact
  61. | PeerBarSetting::BlockContact
  62. | PeerBarSetting::ReportSpam;
  63. user->setBarSettings(*settings & ~flags);
  64. }
  65. if (box) {
  66. if (!wasContact) {
  67. box->showToast(
  68. tr::lng_new_contact_add_done(tr::now, lt_user, first));
  69. }
  70. box->closeBox();
  71. }
  72. done();
  73. }).send();
  74. }
  75. class Controller {
  76. public:
  77. Controller(
  78. not_null<Ui::GenericBox*> box,
  79. not_null<Window::SessionController*> window,
  80. not_null<UserData*> user);
  81. void prepare();
  82. private:
  83. void setupContent();
  84. void setupCover();
  85. void setupNameFields();
  86. void setupWarning();
  87. void setupSharePhoneNumber();
  88. void initNameFields(
  89. not_null<Ui::InputField*> first,
  90. not_null<Ui::InputField*> last,
  91. bool inverted);
  92. not_null<Ui::GenericBox*> _box;
  93. not_null<Window::SessionController*> _window;
  94. not_null<UserData*> _user;
  95. Ui::Checkbox *_sharePhone = nullptr;
  96. QString _phone;
  97. Fn<void()> _focus;
  98. Fn<void()> _save;
  99. Fn<std::optional<QImage>()> _updatedPersonalPhoto;
  100. };
  101. Controller::Controller(
  102. not_null<Ui::GenericBox*> box,
  103. not_null<Window::SessionController*> window,
  104. not_null<UserData*> user)
  105. : _box(box)
  106. , _window(window)
  107. , _user(user)
  108. , _phone(UserPhone(user)) {
  109. }
  110. void Controller::prepare() {
  111. setupContent();
  112. _box->setTitle(_user->isContact()
  113. ? tr::lng_edit_contact_title()
  114. : tr::lng_enter_contact_data());
  115. _box->addButton(tr::lng_box_done(), _save);
  116. _box->addButton(tr::lng_cancel(), [=] { _box->closeBox(); });
  117. _box->setFocusCallback(_focus);
  118. }
  119. void Controller::setupContent() {
  120. setupCover();
  121. setupNameFields();
  122. setupWarning();
  123. setupSharePhoneNumber();
  124. }
  125. void Controller::setupCover() {
  126. const auto cover = _box->addRow(
  127. object_ptr<Info::Profile::Cover>(
  128. _box,
  129. _window,
  130. _user,
  131. Info::Profile::Cover::Role::EditContact,
  132. (_phone.isEmpty()
  133. ? tr::lng_contact_mobile_hidden()
  134. : rpl::single(Ui::FormatPhone(_phone)))),
  135. style::margins());
  136. _updatedPersonalPhoto = [=] { return cover->updatedPersonalPhoto(); };
  137. }
  138. void Controller::setupNameFields() {
  139. const auto inverted = langFirstNameGoesSecond();
  140. const auto first = _box->addRow(
  141. object_ptr<Ui::InputField>(
  142. _box,
  143. st::defaultInputField,
  144. tr::lng_signup_firstname(),
  145. _user->firstName),
  146. st::addContactFieldMargin);
  147. auto preparedLast = object_ptr<Ui::InputField>(
  148. _box,
  149. st::defaultInputField,
  150. tr::lng_signup_lastname(),
  151. _user->lastName);
  152. const auto last = inverted
  153. ? _box->insertRow(
  154. _box->rowsCount() - 1,
  155. std::move(preparedLast),
  156. st::addContactFieldMargin)
  157. : _box->addRow(std::move(preparedLast), st::addContactFieldMargin);
  158. initNameFields(first, last, inverted);
  159. }
  160. void Controller::initNameFields(
  161. not_null<Ui::InputField*> first,
  162. not_null<Ui::InputField*> last,
  163. bool inverted) {
  164. const auto getValue = [](not_null<Ui::InputField*> field) {
  165. return TextUtilities::SingleLine(field->getLastText()).trimmed();
  166. };
  167. if (inverted) {
  168. _box->setTabOrder(last, first);
  169. }
  170. _focus = [=] {
  171. const auto firstValue = getValue(first);
  172. const auto lastValue = getValue(last);
  173. const auto empty = firstValue.isEmpty() && lastValue.isEmpty();
  174. const auto focusFirst = (inverted != empty);
  175. (focusFirst ? first : last)->setFocusFast();
  176. };
  177. _save = [=] {
  178. const auto firstValue = getValue(first);
  179. const auto lastValue = getValue(last);
  180. const auto empty = firstValue.isEmpty() && lastValue.isEmpty();
  181. if (empty) {
  182. _focus();
  183. (inverted ? last : first)->showError();
  184. return;
  185. }
  186. const auto user = _user;
  187. const auto personal = _updatedPersonalPhoto
  188. ? _updatedPersonalPhoto()
  189. : std::nullopt;
  190. const auto done = [=] {
  191. if (personal) {
  192. if (personal->isNull()) {
  193. user->session().api().peerPhoto().clearPersonal(user);
  194. } else {
  195. user->session().api().peerPhoto().upload(
  196. user,
  197. { base::duplicate(*personal) });
  198. }
  199. }
  200. };
  201. SendRequest(
  202. Ui::MakeWeak(_box),
  203. user,
  204. _sharePhone && _sharePhone->checked(),
  205. firstValue,
  206. lastValue,
  207. _phone,
  208. done);
  209. };
  210. const auto submit = [=] {
  211. const auto firstValue = first->getLastText().trimmed();
  212. const auto lastValue = last->getLastText().trimmed();
  213. const auto empty = firstValue.isEmpty() && lastValue.isEmpty();
  214. if (inverted ? last->hasFocus() : empty) {
  215. first->setFocus();
  216. } else if (inverted ? empty : first->hasFocus()) {
  217. last->setFocus();
  218. } else {
  219. _save();
  220. }
  221. };
  222. first->submits() | rpl::start_with_next(submit, first->lifetime());
  223. last->submits() | rpl::start_with_next(submit, last->lifetime());
  224. first->setMaxLength(Ui::EditPeer::kMaxUserFirstLastName);
  225. first->setMaxLength(Ui::EditPeer::kMaxUserFirstLastName);
  226. }
  227. void Controller::setupWarning() {
  228. if (_user->isContact() || !_phone.isEmpty()) {
  229. return;
  230. }
  231. _box->addRow(
  232. object_ptr<Ui::FlatLabel>(
  233. _box,
  234. tr::lng_contact_phone_after(tr::now, lt_user, _user->shortName()),
  235. st::changePhoneLabel),
  236. st::addContactWarningMargin);
  237. }
  238. void Controller::setupSharePhoneNumber() {
  239. const auto settings = _user->barSettings();
  240. if (!settings
  241. || !((*settings) & PeerBarSetting::NeedContactsException)) {
  242. return;
  243. }
  244. _sharePhone = _box->addRow(
  245. object_ptr<Ui::Checkbox>(
  246. _box,
  247. tr::lng_contact_share_phone(tr::now),
  248. true,
  249. st::defaultBoxCheckbox),
  250. st::addContactWarningMargin);
  251. _box->addRow(
  252. object_ptr<Ui::FlatLabel>(
  253. _box,
  254. tr::lng_contact_phone_will_be_shared(tr::now, lt_user, _user->shortName()),
  255. st::changePhoneLabel),
  256. st::addContactWarningMargin);
  257. }
  258. } // namespace
  259. void EditContactBox(
  260. not_null<Ui::GenericBox*> box,
  261. not_null<Window::SessionController*> window,
  262. not_null<UserData*> user) {
  263. box->lifetime().make_state<Controller>(box, window, user)->prepare();
  264. }