intro_code.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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 "intro/intro_code.h"
  8. #include "lang/lang_keys.h"
  9. #include "intro/intro_code_input.h"
  10. #include "intro/intro_signup.h"
  11. #include "intro/intro_password_check.h"
  12. #include "boxes/abstract_box.h"
  13. #include "core/file_utilities.h"
  14. #include "core/update_checker.h"
  15. #include "ui/widgets/buttons.h"
  16. #include "ui/widgets/labels.h"
  17. #include "ui/widgets/fields/masked_input_field.h"
  18. #include "ui/text/format_values.h" // Ui::FormatPhone
  19. #include "ui/text/text_utilities.h"
  20. #include "ui/boxes/confirm_box.h"
  21. #include "main/main_account.h"
  22. #include "mtproto/mtp_instance.h"
  23. #include "styles/style_intro.h"
  24. namespace Intro {
  25. namespace details {
  26. CodeWidget::CodeWidget(
  27. QWidget *parent,
  28. not_null<Main::Account*> account,
  29. not_null<Data*> data)
  30. : Step(parent, account, data)
  31. , _noTelegramCode(this, tr::lng_code_no_telegram(tr::now), st::introLink)
  32. , _code(this)
  33. , _callTimer([=] { sendCall(); })
  34. , _callStatus(getData()->callStatus)
  35. , _callTimeout(getData()->callTimeout)
  36. , _callLabel(this, st::introDescription)
  37. , _checkRequestTimer([=] { checkRequest(); }) {
  38. Lang::Updated(
  39. ) | rpl::start_with_next([=] {
  40. refreshLang();
  41. }, lifetime());
  42. _noTelegramCode->addClickHandler([=] { noTelegramCode(); });
  43. _code->setDigitsCountMax(getData()->codeLength);
  44. updateDescText();
  45. setTitleText(_isFragment.value(
  46. ) | rpl::map([=](bool isFragment) {
  47. return !isFragment
  48. ? rpl::single(Ui::FormatPhone(getData()->phone))
  49. : tr::lng_intro_fragment_title();
  50. }) | rpl::flatten_latest());
  51. account->setHandleLoginCode([=](const QString &code) {
  52. _code->setCode(code);
  53. _code->requestCode();
  54. });
  55. _code->codeCollected(
  56. ) | rpl::start_with_next([=](const QString &code) {
  57. hideError();
  58. submitCode(code);
  59. }, lifetime());
  60. }
  61. void CodeWidget::refreshLang() {
  62. if (_noTelegramCode) {
  63. _noTelegramCode->setText(tr::lng_code_no_telegram(tr::now));
  64. }
  65. updateDescText();
  66. updateControlsGeometry();
  67. }
  68. int CodeWidget::errorTop() const {
  69. return contentTop() + st::introErrorBelowLinkTop;
  70. }
  71. void CodeWidget::updateDescText() {
  72. const auto byTelegram = getData()->codeByTelegram;
  73. const auto isFragment = !getData()->codeByFragmentUrl.isEmpty();
  74. _isFragment = isFragment;
  75. setDescriptionText(
  76. isFragment
  77. ? tr::lng_intro_fragment_about(
  78. lt_phone_number,
  79. rpl::single(TextWithEntities{
  80. .text = Ui::FormatPhone(getData()->phone)
  81. }),
  82. Ui::Text::RichLangValue)
  83. : (byTelegram ? tr::lng_code_from_telegram : tr::lng_code_desc)(
  84. Ui::Text::RichLangValue));
  85. if (getData()->codeByTelegram) {
  86. _noTelegramCode->show();
  87. _callTimer.cancel();
  88. } else {
  89. _noTelegramCode->hide();
  90. _callStatus = getData()->callStatus;
  91. _callTimeout = getData()->callTimeout;
  92. if (_callStatus == CallStatus::Waiting && !_callTimer.isActive()) {
  93. _callTimer.callEach(1000);
  94. }
  95. }
  96. updateCallText();
  97. }
  98. void CodeWidget::updateCallText() {
  99. auto text = ([this]() -> QString {
  100. if (getData()->codeByTelegram) {
  101. return QString();
  102. }
  103. switch (_callStatus) {
  104. case CallStatus::Waiting: {
  105. if (_callTimeout >= 3600) {
  106. return tr::lng_code_call(
  107. tr::now,
  108. lt_minutes,
  109. (u"%1:%2"_q
  110. ).arg(_callTimeout / 3600
  111. ).arg((_callTimeout / 60) % 60, 2, 10, QChar('0')),
  112. lt_seconds,
  113. u"%1"_q.arg(_callTimeout % 60, 2, 10, QChar('0')));
  114. } else {
  115. return tr::lng_code_call(
  116. tr::now,
  117. lt_minutes,
  118. QString::number(_callTimeout / 60),
  119. lt_seconds,
  120. u"%1"_q.arg(_callTimeout % 60, 2, 10, QChar('0')));
  121. }
  122. } break;
  123. case CallStatus::Calling:
  124. return tr::lng_code_calling(tr::now);
  125. case CallStatus::Called:
  126. return tr::lng_code_called(tr::now);
  127. }
  128. return QString();
  129. })();
  130. _callLabel->setText(text);
  131. _callLabel->setVisible(!text.isEmpty() && !animating());
  132. }
  133. void CodeWidget::resizeEvent(QResizeEvent *e) {
  134. Step::resizeEvent(e);
  135. updateControlsGeometry();
  136. }
  137. void CodeWidget::updateControlsGeometry() {
  138. _code->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
  139. auto linkTop = _code->y() + _code->height() + st::introLinkTop;
  140. _noTelegramCode->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
  141. _callLabel->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
  142. }
  143. void CodeWidget::showCodeError(rpl::producer<QString> text) {
  144. _code->showError();
  145. showError(std::move(text));
  146. }
  147. void CodeWidget::setInnerFocus() {
  148. _code->setFocus();
  149. }
  150. void CodeWidget::activate() {
  151. Step::activate();
  152. _code->show();
  153. if (getData()->codeByTelegram) {
  154. _noTelegramCode->show();
  155. } else {
  156. _callLabel->show();
  157. }
  158. setInnerFocus();
  159. }
  160. void CodeWidget::finished() {
  161. Step::finished();
  162. account().setHandleLoginCode(nullptr);
  163. _checkRequestTimer.cancel();
  164. _callTimer.cancel();
  165. apiClear();
  166. cancelled();
  167. _sentCode.clear();
  168. _code->clear();
  169. }
  170. void CodeWidget::cancelled() {
  171. api().request(base::take(_sentRequest)).cancel();
  172. api().request(base::take(_callRequestId)).cancel();
  173. api().request(MTPauth_CancelCode(
  174. MTP_string(getData()->phone),
  175. MTP_bytes(getData()->phoneHash)
  176. )).send();
  177. }
  178. void CodeWidget::stopCheck() {
  179. _checkRequestTimer.cancel();
  180. }
  181. void CodeWidget::checkRequest() {
  182. auto status = api().instance().state(_sentRequest);
  183. if (status < 0) {
  184. auto leftms = -status;
  185. if (leftms >= 1000) {
  186. if (_sentRequest) {
  187. api().request(base::take(_sentRequest)).cancel();
  188. _sentCode.clear();
  189. }
  190. }
  191. }
  192. if (!_sentRequest && status == MTP::RequestSent) {
  193. stopCheck();
  194. }
  195. }
  196. void CodeWidget::codeSubmitDone(const MTPauth_Authorization &result) {
  197. stopCheck();
  198. _code->setEnabled(true);
  199. _sentRequest = 0;
  200. finish(result);
  201. }
  202. void CodeWidget::codeSubmitFail(const MTP::Error &error) {
  203. if (MTP::IsFloodError(error)) {
  204. stopCheck();
  205. _code->setEnabled(true);
  206. _code->setFocus();
  207. _sentRequest = 0;
  208. showCodeError(tr::lng_flood_error());
  209. return;
  210. }
  211. stopCheck();
  212. _code->setEnabled(true);
  213. _code->setFocus();
  214. _sentRequest = 0;
  215. auto &err = error.type();
  216. if (err == u"PHONE_NUMBER_INVALID"_q
  217. || err == u"PHONE_CODE_EXPIRED"_q
  218. || err == u"PHONE_NUMBER_BANNED"_q) { // show error
  219. goBack();
  220. } else if (err == u"PHONE_CODE_EMPTY"_q || err == u"PHONE_CODE_INVALID"_q) {
  221. showCodeError(tr::lng_bad_code());
  222. } else if (err == u"SESSION_PASSWORD_NEEDED"_q) {
  223. _checkRequestTimer.callEach(1000);
  224. _sentRequest = api().request(MTPaccount_GetPassword(
  225. )).done([=](const MTPaccount_Password &result) {
  226. gotPassword(result);
  227. }).fail([=](const MTP::Error &error) {
  228. codeSubmitFail(error);
  229. }).handleFloodErrors().send();
  230. } else if (Logs::DebugEnabled()) { // internal server error
  231. showCodeError(rpl::single(err + ": " + error.description()));
  232. } else {
  233. showCodeError(rpl::single(Lang::Hard::ServerError()));
  234. }
  235. }
  236. void CodeWidget::sendCall() {
  237. if (_callStatus == CallStatus::Waiting) {
  238. if (--_callTimeout <= 0) {
  239. _callStatus = CallStatus::Calling;
  240. _callTimer.cancel();
  241. _callRequestId = api().request(MTPauth_ResendCode(
  242. MTP_flags(0),
  243. MTP_string(getData()->phone),
  244. MTP_bytes(getData()->phoneHash),
  245. MTPstring() // reason
  246. )).done([=](const MTPauth_SentCode &result) {
  247. callDone(result);
  248. }).send();
  249. } else {
  250. getData()->callStatus = _callStatus;
  251. getData()->callTimeout = _callTimeout;
  252. }
  253. updateCallText();
  254. }
  255. }
  256. void CodeWidget::callDone(const MTPauth_SentCode &result) {
  257. result.match([&](const MTPDauth_sentCode &data) {
  258. fillSentCodeData(data);
  259. _code->setDigitsCountMax(getData()->codeLength);
  260. if (_callStatus == CallStatus::Calling) {
  261. _callStatus = CallStatus::Called;
  262. getData()->callStatus = _callStatus;
  263. getData()->callTimeout = _callTimeout;
  264. updateCallText();
  265. }
  266. }, [&](const MTPDauth_sentCodeSuccess &data) {
  267. finish(data.vauthorization());
  268. });
  269. }
  270. void CodeWidget::gotPassword(const MTPaccount_Password &result) {
  271. Expects(result.type() == mtpc_account_password);
  272. stopCheck();
  273. _sentRequest = 0;
  274. const auto &d = result.c_account_password();
  275. getData()->pwdState = Core::ParseCloudPasswordState(d);
  276. if (!d.vcurrent_algo() || !d.vsrp_id() || !d.vsrp_B()) {
  277. LOG(("API Error: No current password received on login."));
  278. _code->setFocus();
  279. return;
  280. } else if (!getData()->pwdState.hasPassword) {
  281. const auto callback = [=](Fn<void()> &&close) {
  282. Core::UpdateApplication();
  283. close();
  284. };
  285. Ui::show(Ui::MakeConfirmBox({
  286. .text = tr::lng_passport_app_out_of_date(),
  287. .confirmed = callback,
  288. .confirmText = tr::lng_menu_update(),
  289. }));
  290. return;
  291. }
  292. goReplace<PasswordCheckWidget>(Animate::Forward);
  293. }
  294. void CodeWidget::submit() {
  295. if (getData()->codeByFragmentUrl.isEmpty()) {
  296. _code->requestCode();
  297. } else {
  298. File::OpenUrl(getData()->codeByFragmentUrl);
  299. }
  300. }
  301. void CodeWidget::submitCode(const QString &text) {
  302. if (_sentRequest
  303. || _sentCode == text
  304. || text.size() != getData()->codeLength) {
  305. return;
  306. }
  307. hideError();
  308. _checkRequestTimer.callEach(1000);
  309. _sentCode = text;
  310. _code->setEnabled(false);
  311. getData()->pwdState = Core::CloudPasswordState();
  312. _sentRequest = api().request(MTPauth_SignIn(
  313. MTP_flags(MTPauth_SignIn::Flag::f_phone_code),
  314. MTP_string(getData()->phone),
  315. MTP_bytes(getData()->phoneHash),
  316. MTP_string(_sentCode),
  317. MTPEmailVerification()
  318. )).done([=](const MTPauth_Authorization &result) {
  319. codeSubmitDone(result);
  320. }).fail([=](const MTP::Error &error) {
  321. codeSubmitFail(error);
  322. }).handleFloodErrors().send();
  323. }
  324. rpl::producer<QString> CodeWidget::nextButtonText() const {
  325. return _isFragment.value(
  326. ) | rpl::map([=](bool isFragment) {
  327. return isFragment
  328. ? tr::lng_intro_fragment_button()
  329. : Step::nextButtonText();
  330. }) | rpl::flatten_latest();
  331. }
  332. rpl::producer<const style::RoundButton*> CodeWidget::nextButtonStyle() const {
  333. return _isFragment.value(
  334. ) | rpl::map([](bool isFragment) {
  335. return isFragment ? &st::introFragmentButton : nullptr;
  336. });
  337. }
  338. void CodeWidget::noTelegramCode() {
  339. if (_noTelegramCodeRequestId) {
  340. return;
  341. }
  342. _noTelegramCodeRequestId = api().request(MTPauth_ResendCode(
  343. MTP_flags(0),
  344. MTP_string(getData()->phone),
  345. MTP_bytes(getData()->phoneHash),
  346. MTPstring() // reason
  347. )).done([=](const MTPauth_SentCode &result) {
  348. noTelegramCodeDone(result);
  349. }).fail([=](const MTP::Error &error) {
  350. noTelegramCodeFail(error);
  351. }).handleFloodErrors().send();
  352. }
  353. void CodeWidget::noTelegramCodeDone(const MTPauth_SentCode &result) {
  354. _noTelegramCodeRequestId = 0;
  355. result.match([&](const MTPDauth_sentCode &data) {
  356. const auto &d = result.c_auth_sentCode();
  357. fillSentCodeData(data);
  358. _code->setDigitsCountMax(getData()->codeLength);
  359. const auto next = data.vnext_type();
  360. if (next && next->type() == mtpc_auth_codeTypeCall) {
  361. getData()->callStatus = CallStatus::Waiting;
  362. getData()->callTimeout = d.vtimeout().value_or(60);
  363. } else {
  364. getData()->callStatus = CallStatus::Disabled;
  365. getData()->callTimeout = 0;
  366. }
  367. getData()->codeByTelegram = false;
  368. updateDescText();
  369. }, [&](const MTPDauth_sentCodeSuccess &data) {
  370. finish(data.vauthorization());
  371. });
  372. }
  373. void CodeWidget::noTelegramCodeFail(const MTP::Error &error) {
  374. if (MTP::IsFloodError(error)) {
  375. _noTelegramCodeRequestId = 0;
  376. showCodeError(tr::lng_flood_error());
  377. return;
  378. } else if (error.type() == u"SEND_CODE_UNAVAILABLE"_q) {
  379. _noTelegramCodeRequestId = 0;
  380. return;
  381. }
  382. _noTelegramCodeRequestId = 0;
  383. if (Logs::DebugEnabled()) { // internal server error
  384. showCodeError(rpl::single(error.type() + ": " + error.description()));
  385. } else {
  386. showCodeError(rpl::single(Lang::Hard::ServerError()));
  387. }
  388. }
  389. } // namespace details
  390. } // namespace Intro