intro_widget.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  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_widget.h"
  8. #include "intro/intro_start.h"
  9. #include "intro/intro_phone.h"
  10. #include "intro/intro_qr.h"
  11. #include "intro/intro_code.h"
  12. #include "intro/intro_signup.h"
  13. #include "intro/intro_password_check.h"
  14. #include "lang/lang_keys.h"
  15. #include "lang/lang_instance.h"
  16. #include "lang/lang_cloud_manager.h"
  17. #include "storage/localstorage.h"
  18. #include "main/main_account.h"
  19. #include "main/main_domain.h"
  20. #include "main/main_session.h"
  21. #include "mainwindow.h"
  22. #include "history/history.h"
  23. #include "history/history_item.h"
  24. #include "data/data_user.h"
  25. #include "countries/countries_instance.h"
  26. #include "ui/boxes/confirm_box.h"
  27. #include "ui/text/format_values.h" // Ui::FormatPhone
  28. #include "ui/text/text_utilities.h"
  29. #include "ui/widgets/buttons.h"
  30. #include "ui/widgets/labels.h"
  31. #include "ui/wrap/fade_wrap.h"
  32. #include "ui/ui_utility.h"
  33. #include "boxes/abstract_box.h"
  34. #include "core/update_checker.h"
  35. #include "core/application.h"
  36. #include "mtproto/mtproto_dc_options.h"
  37. #include "window/window_slide_animation.h"
  38. #include "window/window_connecting_widget.h"
  39. #include "window/window_controller.h"
  40. #include "window/window_session_controller.h"
  41. #include "window/section_widget.h"
  42. #include "base/platform/base_platform_info.h"
  43. #include "api/api_text_entities.h"
  44. #include "styles/style_layers.h"
  45. #include "styles/style_intro.h"
  46. #include "base/qt/qt_common_adapters.h"
  47. namespace Intro {
  48. namespace {
  49. using namespace ::Intro::details;
  50. [[nodiscard]] QString ComputeNewAccountCountry() {
  51. if (const auto parent
  52. = Core::App().domain().maybeLastOrSomeAuthedAccount()) {
  53. if (const auto session = parent->maybeSession()) {
  54. const auto iso = Countries::Instance().countryISO2ByPhone(
  55. session->user()->phone());
  56. if (!iso.isEmpty()) {
  57. return iso;
  58. }
  59. }
  60. }
  61. return Platform::SystemCountry();
  62. }
  63. } // namespace
  64. Widget::Widget(
  65. QWidget *parent,
  66. not_null<Window::Controller*> controller,
  67. not_null<Main::Account*> account,
  68. EnterPoint point)
  69. : RpWidget(parent)
  70. , _account(account)
  71. , _data(details::Data{ .controller = controller })
  72. , _nextStyle(&st::introNextButton)
  73. , _back(this, object_ptr<Ui::IconButton>(this, st::introBackButton))
  74. , _settings(
  75. this,
  76. object_ptr<Ui::RoundButton>(
  77. this,
  78. tr::lng_menu_settings(),
  79. st::defaultBoxButton))
  80. , _next(
  81. this,
  82. object_ptr<Ui::RoundButton>(this, nullptr, *_nextStyle))
  83. , _connecting(std::make_unique<Window::ConnectionState>(
  84. this,
  85. account,
  86. rpl::single(true))) {
  87. controller->setDefaultFloatPlayerDelegate(floatPlayerDelegate());
  88. getData()->country = ComputeNewAccountCountry();
  89. _account->mtpValue(
  90. ) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
  91. _api.emplace(instance);
  92. crl::on_main(this, [=] { createLanguageLink(); });
  93. }, lifetime());
  94. switch (point) {
  95. case EnterPoint::Start:
  96. getNearestDC();
  97. appendStep(new StartWidget(this, _account, getData()));
  98. break;
  99. case EnterPoint::Phone:
  100. appendStep(new PhoneWidget(this, _account, getData()));
  101. break;
  102. case EnterPoint::Qr:
  103. appendStep(new QrWidget(this, _account, getData()));
  104. break;
  105. default: Unexpected("Enter point in Intro::Widget::Widget.");
  106. }
  107. fixOrder();
  108. Lang::CurrentCloudManager().firstLanguageSuggestion(
  109. ) | rpl::start_with_next([=] {
  110. createLanguageLink();
  111. }, lifetime());
  112. _account->mtpUpdates(
  113. ) | rpl::start_with_next([=](const MTPUpdates &updates) {
  114. handleUpdates(updates);
  115. }, lifetime());
  116. _back->entity()->setClickedCallback([=] { backRequested(); });
  117. _back->hide(anim::type::instant);
  118. if (_changeLanguage) {
  119. _changeLanguage->finishAnimating();
  120. }
  121. Lang::Updated(
  122. ) | rpl::start_with_next([=] {
  123. refreshLang();
  124. }, lifetime());
  125. show();
  126. showControls();
  127. getStep()->showFast();
  128. setInnerFocus();
  129. cSetPasswordRecovered(false);
  130. if (!Core::UpdaterDisabled()) {
  131. Core::UpdateChecker checker;
  132. checker.start();
  133. rpl::merge(
  134. rpl::single(rpl::empty),
  135. checker.isLatest(),
  136. checker.failed(),
  137. checker.ready()
  138. ) | rpl::start_with_next([=] {
  139. checkUpdateStatus();
  140. }, lifetime());
  141. }
  142. }
  143. rpl::producer<> Widget::showSettingsRequested() const {
  144. return _settings->entity()->clicks() | rpl::to_empty;
  145. }
  146. not_null<Media::Player::FloatDelegate*> Widget::floatPlayerDelegate() {
  147. return static_cast<Media::Player::FloatDelegate*>(this);
  148. }
  149. auto Widget::floatPlayerSectionDelegate()
  150. -> not_null<Media::Player::FloatSectionDelegate*> {
  151. return static_cast<Media::Player::FloatSectionDelegate*>(this);
  152. }
  153. not_null<Ui::RpWidget*> Widget::floatPlayerWidget() {
  154. return this;
  155. }
  156. void Widget::floatPlayerToggleGifsPaused(bool paused) {
  157. }
  158. auto Widget::floatPlayerGetSection(Window::Column column)
  159. -> not_null<Media::Player::FloatSectionDelegate*> {
  160. return this;
  161. }
  162. void Widget::floatPlayerEnumerateSections(Fn<void(
  163. not_null<Media::Player::FloatSectionDelegate*> widget,
  164. Window::Column widgetColumn)> callback) {
  165. callback(this, Window::Column::Second);
  166. }
  167. bool Widget::floatPlayerIsVisible(not_null<HistoryItem*> item) {
  168. return false;
  169. }
  170. void Widget::floatPlayerDoubleClickEvent(not_null<const HistoryItem*> item) {
  171. getData()->controller->invokeForSessionController(
  172. &item->history()->peer->session().account(),
  173. item->history()->peer,
  174. [&](not_null<Window::SessionController*> controller) {
  175. controller->showMessage(item);
  176. });
  177. }
  178. QRect Widget::floatPlayerAvailableRect() {
  179. return mapToGlobal(rect());
  180. }
  181. bool Widget::floatPlayerHandleWheelEvent(QEvent *e) {
  182. return false;
  183. }
  184. void Widget::refreshLang() {
  185. _changeLanguage.destroy();
  186. createLanguageLink();
  187. InvokeQueued(this, [this] { updateControlsGeometry(); });
  188. }
  189. void Widget::handleUpdates(const MTPUpdates &updates) {
  190. updates.match([&](const MTPDupdateShort &data) {
  191. handleUpdate(data.vupdate());
  192. }, [&](const MTPDupdates &data) {
  193. for (const auto &update : data.vupdates().v) {
  194. handleUpdate(update);
  195. }
  196. }, [&](const MTPDupdatesCombined &data) {
  197. for (const auto &update : data.vupdates().v) {
  198. handleUpdate(update);
  199. }
  200. }, [](const auto &) {});
  201. }
  202. void Widget::handleUpdate(const MTPUpdate &update) {
  203. update.match([&](const MTPDupdateDcOptions &data) {
  204. _account->mtp().dcOptions().addFromList(data.vdc_options());
  205. }, [&](const MTPDupdateConfig &data) {
  206. _account->mtp().requestConfig();
  207. }, [&](const MTPDupdateServiceNotification &data) {
  208. const auto text = TextWithEntities{
  209. qs(data.vmessage()),
  210. Api::EntitiesFromMTP(nullptr, data.ventities().v)
  211. };
  212. Ui::show(Ui::MakeInformBox(text));
  213. }, [](const auto &) {});
  214. }
  215. void Widget::createLanguageLink() {
  216. if (_changeLanguage
  217. || Core::App().domain().maybeLastOrSomeAuthedAccount()) {
  218. return;
  219. }
  220. const auto createLink = [=](
  221. const QString &text,
  222. const QString &languageId) {
  223. _changeLanguage.create(
  224. this,
  225. object_ptr<Ui::LinkButton>(this, text));
  226. _changeLanguage->hide(anim::type::instant);
  227. _changeLanguage->entity()->setClickedCallback([=] {
  228. Lang::CurrentCloudManager().switchToLanguage(languageId);
  229. });
  230. _changeLanguage->toggle(
  231. !_resetAccount && !_terms && _nextShown,
  232. anim::type::normal);
  233. updateControlsGeometry();
  234. };
  235. const auto currentId = Lang::LanguageIdOrDefault(Lang::Id());
  236. const auto defaultId = Lang::DefaultLanguageId();
  237. const auto suggested = Lang::CurrentCloudManager().suggestedLanguage();
  238. if (currentId != defaultId) {
  239. createLink(
  240. Lang::GetOriginalValue(tr::lng_switch_to_this.base),
  241. defaultId);
  242. } else if (!suggested.isEmpty() && suggested != currentId && _api) {
  243. _api->request(MTPlangpack_GetStrings(
  244. MTP_string(Lang::CloudLangPackName()),
  245. MTP_string(suggested),
  246. MTP_vector<MTPstring>(1, MTP_string("lng_switch_to_this"))
  247. )).done([=](const MTPVector<MTPLangPackString> &result) {
  248. const auto strings = Lang::Instance::ParseStrings(result);
  249. const auto i = strings.find(tr::lng_switch_to_this.base);
  250. if (i != strings.end()) {
  251. createLink(i->second, suggested);
  252. }
  253. }).send();
  254. }
  255. }
  256. void Widget::checkUpdateStatus() {
  257. Expects(!Core::UpdaterDisabled());
  258. if (Core::UpdateChecker().state() == Core::UpdateChecker::State::Ready) {
  259. if (_update) return;
  260. _update.create(
  261. this,
  262. object_ptr<Ui::RoundButton>(
  263. this,
  264. tr::lng_menu_update(),
  265. st::defaultBoxButton));
  266. if (!_showAnimation) {
  267. _update->setVisible(true);
  268. }
  269. const auto stepHasCover = getStep()->hasCover();
  270. _update->toggle(!stepHasCover, anim::type::instant);
  271. _update->entity()->setClickedCallback([] {
  272. Core::checkReadyUpdate();
  273. Core::Restart();
  274. });
  275. } else {
  276. if (!_update) return;
  277. _update.destroy();
  278. }
  279. updateControlsGeometry();
  280. }
  281. void Widget::setInnerFocus() {
  282. if (getStep()->animating()) {
  283. setFocus();
  284. } else {
  285. getStep()->setInnerFocus();
  286. }
  287. }
  288. void Widget::historyMove(StackAction action, Animate animate) {
  289. Expects(_stepHistory.size() > 1);
  290. if (getStep()->animating()) {
  291. return;
  292. }
  293. auto wasStep = getStep((action == StackAction::Back) ? 0 : 1);
  294. if (action == StackAction::Back) {
  295. _stepHistory.pop_back();
  296. wasStep->cancelled();
  297. } else if (action == StackAction::Replace) {
  298. _stepHistory.erase(_stepHistory.end() - 2);
  299. }
  300. if (_resetAccount) {
  301. hideAndDestroy(std::exchange(_resetAccount, { nullptr }));
  302. }
  303. if (_terms) {
  304. hideAndDestroy(std::exchange(_terms, { nullptr }));
  305. }
  306. {
  307. getStep()->nextButtonStyle(
  308. ) | rpl::start_with_next([=](const style::RoundButton *st) {
  309. const auto nextStyle = st ? st : &st::introNextButton;
  310. if (_nextStyle != nextStyle) {
  311. _nextStyle = nextStyle;
  312. const auto wasShown = _next->toggled();
  313. _next.destroy();
  314. _next.create(
  315. this,
  316. object_ptr<Ui::RoundButton>(this, nullptr, *nextStyle));
  317. showControls();
  318. updateControlsGeometry();
  319. _next->toggle(wasShown, anim::type::instant);
  320. }
  321. }, _next->lifetime());
  322. }
  323. getStep()->finishInit();
  324. getStep()->prepareShowAnimated(wasStep);
  325. if (wasStep->hasCover() != getStep()->hasCover()) {
  326. _nextTopFrom = wasStep->contentTop() + st::introNextTop;
  327. _controlsTopFrom = wasStep->hasCover() ? st::introCoverHeight : 0;
  328. _coverShownAnimation.start(
  329. [this] { updateControlsGeometry(); },
  330. 0.,
  331. 1.,
  332. st::introCoverDuration,
  333. wasStep->hasCover() ? anim::linear : anim::easeOutCirc);
  334. }
  335. _stepLifetime.destroy();
  336. if (action == StackAction::Forward || action == StackAction::Replace) {
  337. wasStep->finished();
  338. }
  339. if (action == StackAction::Back || action == StackAction::Replace) {
  340. delete base::take(wasStep);
  341. }
  342. _back->toggle(getStep()->hasBack(), anim::type::normal);
  343. auto stepHasCover = getStep()->hasCover();
  344. _settings->toggle(!stepHasCover, anim::type::normal);
  345. if (_update) {
  346. _update->toggle(!stepHasCover, anim::type::normal);
  347. }
  348. setupNextButton();
  349. if (_resetAccount) _resetAccount->show(anim::type::normal);
  350. if (_terms) _terms->show(anim::type::normal);
  351. getStep()->showAnimated(animate);
  352. fixOrder();
  353. }
  354. void Widget::hideAndDestroy(object_ptr<Ui::FadeWrap<Ui::RpWidget>> widget) {
  355. const auto weak = Ui::MakeWeak(widget.data());
  356. widget->hide(anim::type::normal);
  357. widget->shownValue(
  358. ) | rpl::start_with_next([=](bool shown) {
  359. if (!shown && weak) {
  360. weak->deleteLater();
  361. }
  362. }, widget->lifetime());
  363. }
  364. void Widget::fixOrder() {
  365. _next->raise();
  366. if (_update) _update->raise();
  367. if (_changeLanguage) _changeLanguage->raise();
  368. _settings->raise();
  369. _back->raise();
  370. floatPlayerRaiseAll();
  371. _connecting->raise();
  372. }
  373. void Widget::moveToStep(Step *step, StackAction action, Animate animate) {
  374. appendStep(step);
  375. _back->raise();
  376. _settings->raise();
  377. if (_update) {
  378. _update->raise();
  379. }
  380. _connecting->raise();
  381. historyMove(action, animate);
  382. }
  383. void Widget::appendStep(Step *step) {
  384. _stepHistory.push_back(step);
  385. step->setGeometry(rect());
  386. step->setGoCallback([=](Step *step, StackAction action, Animate animate) {
  387. if (action == StackAction::Back) {
  388. backRequested();
  389. } else {
  390. moveToStep(step, action, animate);
  391. }
  392. });
  393. step->setShowResetCallback([=] {
  394. showResetButton();
  395. });
  396. step->setShowTermsCallback([=] {
  397. showTerms();
  398. });
  399. step->setCancelNearestDcCallback([=] {
  400. if (_api) {
  401. _api->request(base::take(_nearestDcRequestId)).cancel();
  402. }
  403. });
  404. step->setAcceptTermsCallback([=](Fn<void()> callback) {
  405. acceptTerms(callback);
  406. });
  407. }
  408. void Widget::showResetButton() {
  409. if (!_resetAccount) {
  410. auto entity = object_ptr<Ui::RoundButton>(
  411. this,
  412. tr::lng_signin_reset_account(),
  413. st::introResetButton);
  414. _resetAccount.create(this, std::move(entity));
  415. _resetAccount->hide(anim::type::instant);
  416. _resetAccount->entity()->setClickedCallback([this] { resetAccount(); });
  417. updateControlsGeometry();
  418. }
  419. _resetAccount->show(anim::type::normal);
  420. if (_changeLanguage) {
  421. _changeLanguage->hide(anim::type::normal);
  422. }
  423. }
  424. void Widget::showTerms() {
  425. if (getData()->termsLock.text.text.isEmpty()) {
  426. _terms.destroy();
  427. } else if (!_terms) {
  428. auto entity = object_ptr<Ui::FlatLabel>(
  429. this,
  430. tr::lng_terms_signup(
  431. lt_link,
  432. tr::lng_terms_signup_link() | Ui::Text::ToLink(),
  433. Ui::Text::WithEntities),
  434. st::introTermsLabel);
  435. _terms.create(this, std::move(entity));
  436. _terms->entity()->overrideLinkClickHandler([=] {
  437. showTerms(nullptr);
  438. });
  439. updateControlsGeometry();
  440. _terms->hide(anim::type::instant);
  441. }
  442. if (_changeLanguage) {
  443. _changeLanguage->toggle(
  444. !_terms && !_resetAccount && _nextShown,
  445. anim::type::normal);
  446. }
  447. }
  448. void Widget::acceptTerms(Fn<void()> callback) {
  449. showTerms(callback);
  450. }
  451. void Widget::resetAccount() {
  452. if (_resetRequest || !_api) {
  453. return;
  454. }
  455. const auto callback = crl::guard(this, [this] {
  456. if (_resetRequest) {
  457. return;
  458. }
  459. _resetRequest = _api->request(MTPaccount_DeleteAccount(
  460. MTP_flags(0),
  461. MTP_string("Forgot password"),
  462. MTPInputCheckPasswordSRP()
  463. )).done([=] {
  464. _resetRequest = 0;
  465. getData()->controller->hideLayer();
  466. if (getData()->phone.isEmpty()) {
  467. moveToStep(
  468. new QrWidget(this, _account, getData()),
  469. StackAction::Replace,
  470. Animate::Back);
  471. } else {
  472. moveToStep(
  473. new SignupWidget(this, _account, getData()),
  474. StackAction::Replace,
  475. Animate::Forward);
  476. }
  477. }).fail([=](const MTP::Error &error) {
  478. _resetRequest = 0;
  479. const auto &type = error.type();
  480. if (type.startsWith(u"2FA_CONFIRM_WAIT_"_q)) {
  481. const auto seconds = base::StringViewMid(
  482. type,
  483. u"2FA_CONFIRM_WAIT_"_q.size()).toInt();
  484. const auto days = (seconds + 59) / 86400;
  485. const auto hours = ((seconds + 59) % 86400) / 3600;
  486. const auto minutes = ((seconds + 59) % 3600) / 60;
  487. auto when = tr::lng_minutes(tr::now, lt_count, minutes);
  488. if (days > 0) {
  489. const auto daysCount = tr::lng_days(
  490. tr::now,
  491. lt_count,
  492. days);
  493. const auto hoursCount = tr::lng_hours(
  494. tr::now,
  495. lt_count,
  496. hours);
  497. when = tr::lng_signin_reset_in_days(
  498. tr::now,
  499. lt_days_count,
  500. daysCount,
  501. lt_hours_count,
  502. hoursCount,
  503. lt_minutes_count,
  504. when);
  505. } else if (hours > 0) {
  506. const auto hoursCount = tr::lng_hours(
  507. tr::now,
  508. lt_count,
  509. hours);
  510. when = tr::lng_signin_reset_in_hours(
  511. tr::now,
  512. lt_hours_count,
  513. hoursCount,
  514. lt_minutes_count,
  515. when);
  516. }
  517. Ui::show(Ui::MakeInformBox(tr::lng_signin_reset_wait(
  518. tr::now,
  519. lt_phone_number,
  520. Ui::FormatPhone(getData()->phone),
  521. lt_when,
  522. when)));
  523. } else if (type == u"2FA_RECENT_CONFIRM"_q) {
  524. Ui::show(Ui::MakeInformBox(
  525. tr::lng_signin_reset_cancelled()));
  526. } else {
  527. getData()->controller->hideLayer();
  528. getStep()->showError(rpl::single(Lang::Hard::ServerError()));
  529. }
  530. }).send();
  531. });
  532. Ui::show(Ui::MakeConfirmBox({
  533. .text = tr::lng_signin_sure_reset(),
  534. .confirmed = callback,
  535. .confirmText = tr::lng_signin_reset(),
  536. .confirmStyle = &st::attentionBoxButton,
  537. }));
  538. }
  539. void Widget::getNearestDC() {
  540. if (!_api) {
  541. return;
  542. }
  543. _nearestDcRequestId = _api->request(MTPhelp_GetNearestDc(
  544. )).done([=](const MTPNearestDc &result) {
  545. _nearestDcRequestId = 0;
  546. const auto &nearest = result.c_nearestDc();
  547. DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3"
  548. ).arg(qs(nearest.vcountry())
  549. ).arg(nearest.vnearest_dc().v
  550. ).arg(nearest.vthis_dc().v));
  551. _account->suggestMainDcId(nearest.vnearest_dc().v);
  552. const auto nearestCountry = qs(nearest.vcountry());
  553. if (getData()->country != nearestCountry) {
  554. getData()->country = nearestCountry;
  555. getData()->updated.fire({});
  556. }
  557. }).send();
  558. }
  559. void Widget::showTerms(Fn<void()> callback) {
  560. if (getData()->termsLock.text.text.isEmpty()) {
  561. return;
  562. }
  563. const auto weak = Ui::MakeWeak(this);
  564. const auto box = Ui::show(callback
  565. ? Box<Window::TermsBox>(
  566. getData()->termsLock,
  567. tr::lng_terms_agree(),
  568. tr::lng_terms_decline())
  569. : Box<Window::TermsBox>(
  570. getData()->termsLock.text,
  571. tr::lng_box_ok(),
  572. nullptr));
  573. box->setCloseByEscape(false);
  574. box->setCloseByOutsideClick(false);
  575. box->agreeClicks(
  576. ) | rpl::start_with_next([=] {
  577. if (callback) {
  578. callback();
  579. }
  580. if (box) {
  581. box->closeBox();
  582. }
  583. }, box->lifetime());
  584. box->cancelClicks(
  585. ) | rpl::start_with_next([=] {
  586. const auto box = Ui::show(Box<Window::TermsBox>(
  587. TextWithEntities{ tr::lng_terms_signup_sorry(tr::now) },
  588. tr::lng_intro_finish(),
  589. tr::lng_terms_decline()));
  590. box->agreeClicks(
  591. ) | rpl::start_with_next([=] {
  592. if (weak) {
  593. showTerms(callback);
  594. }
  595. }, box->lifetime());
  596. box->cancelClicks(
  597. ) | rpl::start_with_next([=] {
  598. if (box) {
  599. box->closeBox();
  600. }
  601. }, box->lifetime());
  602. }, box->lifetime());
  603. }
  604. void Widget::showControls() {
  605. getStep()->show();
  606. setupNextButton();
  607. _next->toggle(_nextShown, anim::type::instant);
  608. _nextShownAnimation.stop();
  609. _connecting->setForceHidden(false);
  610. auto hasCover = getStep()->hasCover();
  611. _settings->toggle(!hasCover, anim::type::instant);
  612. if (_update) {
  613. _update->toggle(!hasCover, anim::type::instant);
  614. }
  615. if (_changeLanguage) {
  616. _changeLanguage->toggle(
  617. !_resetAccount && !_terms && _nextShown,
  618. anim::type::instant);
  619. }
  620. if (_terms) {
  621. _terms->show(anim::type::instant);
  622. }
  623. _back->toggle(getStep()->hasBack(), anim::type::instant);
  624. }
  625. void Widget::setupNextButton() {
  626. _next->entity()->setClickedCallback([=] { getStep()->submit(); });
  627. _next->entity()->setTextTransform(
  628. Ui::RoundButton::TextTransform::NoTransform);
  629. _next->entity()->setText(getStep()->nextButtonText(
  630. ) | rpl::filter([](const QString &text) {
  631. return !text.isEmpty();
  632. }));
  633. getStep()->nextButtonText(
  634. ) | rpl::map([](const QString &text) {
  635. return !text.isEmpty();
  636. }) | rpl::filter([=](bool visible) {
  637. return visible != _nextShown;
  638. }) | rpl::start_with_next([=](bool visible) {
  639. _next->toggle(visible, anim::type::normal);
  640. _nextShown = visible;
  641. if (_changeLanguage) {
  642. _changeLanguage->toggle(
  643. !_resetAccount && !_terms && _nextShown,
  644. anim::type::normal);
  645. }
  646. _nextShownAnimation.start(
  647. [=] { updateControlsGeometry(); },
  648. _nextShown ? 0. : 1.,
  649. _nextShown ? 1. : 0.,
  650. st::slideDuration);
  651. }, _stepLifetime);
  652. }
  653. void Widget::hideControls() {
  654. getStep()->hide();
  655. _next->hide(anim::type::instant);
  656. _connecting->setForceHidden(true);
  657. _settings->hide(anim::type::instant);
  658. if (_update) _update->hide(anim::type::instant);
  659. if (_changeLanguage) _changeLanguage->hide(anim::type::instant);
  660. if (_terms) _terms->hide(anim::type::instant);
  661. _back->hide(anim::type::instant);
  662. }
  663. void Widget::showAnimated(QPixmap oldContentCache, bool back) {
  664. _showAnimation = nullptr;
  665. showControls();
  666. floatPlayerHideAll();
  667. auto newContentCache = Ui::GrabWidget(this);
  668. hideControls();
  669. floatPlayerShowVisible();
  670. _showAnimation = std::make_unique<Window::SlideAnimation>();
  671. _showAnimation->setDirection(back
  672. ? Window::SlideDirection::FromLeft
  673. : Window::SlideDirection::FromRight);
  674. _showAnimation->setRepaintCallback([=] { update(); });
  675. _showAnimation->setFinishedCallback([=] { showFinished(); });
  676. _showAnimation->setPixmaps(oldContentCache, newContentCache);
  677. _showAnimation->start();
  678. show();
  679. }
  680. void Widget::showFinished() {
  681. _showAnimation = nullptr;
  682. showControls();
  683. getStep()->activate();
  684. }
  685. void Widget::paintEvent(QPaintEvent *e) {
  686. const auto trivial = (rect() == e->rect());
  687. setMouseTracking(true);
  688. QPainter p(this);
  689. if (!trivial) {
  690. p.setClipRect(e->rect());
  691. }
  692. if (_showAnimation) {
  693. _showAnimation->paintContents(p);
  694. return;
  695. }
  696. p.fillRect(e->rect(), st::windowBg);
  697. }
  698. void Widget::resizeEvent(QResizeEvent *e) {
  699. if (_stepHistory.empty()) {
  700. return;
  701. }
  702. for (const auto step : _stepHistory) {
  703. step->setGeometry(rect());
  704. }
  705. updateControlsGeometry();
  706. floatPlayerAreaUpdated();
  707. }
  708. void Widget::updateControlsGeometry() {
  709. const auto skip = st::introSettingsSkip;
  710. const auto shown = _coverShownAnimation.value(1.);
  711. const auto controlsTop = anim::interpolate(
  712. _controlsTopFrom,
  713. getStep()->hasCover() ? st::introCoverHeight : 0,
  714. shown);
  715. _settings->moveToRight(skip, controlsTop + skip);
  716. if (_update) {
  717. _update->moveToRight(
  718. skip + _settings->width() + skip,
  719. _settings->y());
  720. }
  721. _back->moveToLeft(0, controlsTop);
  722. auto nextTopTo = getStep()->contentTop() + st::introNextTop;
  723. auto nextTop = anim::interpolate(_nextTopFrom, nextTopTo, shown);
  724. const auto shownAmount = _nextShownAnimation.value(_nextShown ? 1. : 0.);
  725. const auto realNextTop = anim::interpolate(
  726. nextTop + st::introNextSlide,
  727. nextTop,
  728. shownAmount);
  729. _next->moveToLeft((width() - _next->width()) / 2, realNextTop);
  730. getStep()->setShowAnimationClipping(shownAmount > 0
  731. ? QRect(0, 0, width(), realNextTop)
  732. : QRect());
  733. if (_changeLanguage) {
  734. _changeLanguage->moveToLeft(
  735. (width() - _changeLanguage->width()) / 2,
  736. _next->y() + _next->height() + _changeLanguage->height());
  737. }
  738. if (_resetAccount) {
  739. _resetAccount->moveToLeft(
  740. (width() - _resetAccount->width()) / 2,
  741. height() - st::introResetBottom - _resetAccount->height());
  742. }
  743. if (_terms) {
  744. _terms->moveToLeft(
  745. (width() - _terms->width()) / 2,
  746. height() - st::introTermsBottom - _terms->height());
  747. }
  748. }
  749. void Widget::keyPressEvent(QKeyEvent *e) {
  750. if (_showAnimation || getStep()->animating()) return;
  751. if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
  752. if (getStep()->hasBack()) {
  753. backRequested();
  754. }
  755. } else if (e->key() == Qt::Key_Enter
  756. || e->key() == Qt::Key_Return
  757. || e->key() == Qt::Key_Space) {
  758. getStep()->submit();
  759. }
  760. }
  761. void Widget::backRequested() {
  762. if (_stepHistory.size() > 1) {
  763. historyMove(StackAction::Back, Animate::Back);
  764. } else if (const auto parent
  765. = Core::App().domain().maybeLastOrSomeAuthedAccount()) {
  766. Core::App().domain().activate(parent);
  767. } else {
  768. moveToStep(
  769. Ui::CreateChild<StartWidget>(this, _account, getData()),
  770. StackAction::Replace,
  771. Animate::Back);
  772. }
  773. }
  774. Widget::~Widget() {
  775. for (auto step : base::take(_stepHistory)) {
  776. delete step;
  777. }
  778. }
  779. } // namespace Intro