| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "intro/intro_step.h"
- #include "intro/intro_widget.h"
- #include "intro/intro_signup.h"
- #include "storage/localstorage.h"
- #include "storage/storage_account.h"
- #include "lang/lang_keys.h"
- #include "lang/lang_instance.h"
- #include "lang/lang_cloud_manager.h"
- #include "main/main_account.h"
- #include "main/main_app_config.h"
- #include "main/main_domain.h"
- #include "main/main_session.h"
- #include "main/main_session_settings.h"
- #include "boxes/abstract_box.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "apiwrap.h"
- #include "api/api_peer_photo.h"
- #include "mainwindow.h"
- #include "ui/boxes/confirm_box.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/labels.h"
- #include "ui/wrap/fade_wrap.h"
- #include "ui/effects/slide_animation.h"
- #include "ui/ui_utility.h"
- #include "data/data_user.h"
- #include "data/data_auto_download.h"
- #include "data/data_session.h"
- #include "data/data_chat_filters.h"
- #include "window/window_controller.h"
- #include "styles/style_intro.h"
- #include "styles/style_window.h"
- #include "core/core_cloud_password.h"
- #include "core/sandbox.h"
- #include "core/wallet_replacer.h"
- #include "mtproto/mtp_instance.h"
- #include "ui/widgets/buttons.h"
- #include "ui/wrap/vertical_layout.h"
- #include "export/export_settings.h"
- #include "intro/intro_phone.h"
- #include "intro/intro_qr.h"
- #include "intro/intro_code.h"
- #include "intro/intro_password_check.h"
- #include "support/support_common.h"
- namespace Intro {
- namespace details {
- namespace {
- void PrepareSupportMode(not_null<Main::Session*> session) {
- using ::Data::AutoDownload::Full;
- anim::SetDisabled(true);
- Core::App().settings().setDesktopNotify(false);
- Core::App().settings().setSoundNotify(false);
- Core::App().settings().setFlashBounceNotify(false);
- Core::App().saveSettings();
- session->settings().autoDownload() = Full::FullDisabled();
- session->saveSettings();
- }
- } // namespace
- Step::CoverAnimation::~CoverAnimation() = default;
- Step::Step(
- QWidget *parent,
- not_null<Main::Account*> account,
- not_null<Data*> data,
- bool hasCover)
- : RpWidget(parent)
- , _account(account)
- , _data(data)
- , _hasCover(hasCover)
- , _title(this, _hasCover ? st::introCoverTitle : st::introTitle)
- , _description(
- this,
- object_ptr<Ui::FlatLabel>(
- this,
- _hasCover
- ? st::introCoverDescription
- : st::introDescription)) {
- hide();
- style::PaletteChanged(
- ) | rpl::start_with_next([=] {
- if (!_coverMask.isNull()) {
- _coverMask = QPixmap();
- prepareCoverMask();
- }
- }, lifetime());
- _errorText.value(
- ) | rpl::start_with_next([=](const QString &text) {
- refreshError(text);
- }, lifetime());
- _titleText.value(
- ) | rpl::start_with_next([=](const QString &text) {
- _title->setText(text);
- updateLabelsPosition();
- }, lifetime());
- _descriptionText.value(
- ) | rpl::start_with_next([=](const TextWithEntities &text) {
- const auto label = _description->entity();
- const auto hasSpoiler = ranges::contains(
- text.entities,
- EntityType::Spoiler,
- &EntityInText::type);
- label->setMarkedText(text);
- label->setAttribute(Qt::WA_TransparentForMouseEvents, hasSpoiler);
- updateLabelsPosition();
- }, lifetime());
- }
- Step::~Step() = default;
- MTP::Sender &Step::api() const {
- if (!_api) {
- _api.emplace(&_account->mtp());
- }
- return *_api;
- }
- void Step::apiClear() {
- _api.reset();
- }
- rpl::producer<QString> Step::nextButtonText() const {
- return tr::lng_intro_next();
- }
- rpl::producer<const style::RoundButton*> Step::nextButtonStyle() const {
- return rpl::single((const style::RoundButton*)(nullptr));
- }
- void Step::goBack() {
- if (_goCallback) {
- _goCallback(nullptr, StackAction::Back, Animate::Back);
- }
- }
- void Step::goNext(Step *step) {
- if (_goCallback) {
- _goCallback(step, StackAction::Forward, Animate::Forward);
- }
- }
- void Step::goReplace(Step *step, Animate animate) {
- if (_goCallback) {
- _goCallback(step, StackAction::Replace, animate);
- }
- }
- void Step::finish(const MTPauth_Authorization &auth, QImage &&photo) {
- auth.match([&](const MTPDauth_authorization &data) {
- if (data.vuser().type() != mtpc_user
- || !data.vuser().c_user().is_self()) {
- showError(rpl::single(Lang::Hard::ServerError())); // wtf?
- return;
- }
- finish(data.vuser(), std::move(photo));
- }, [&](const MTPDauth_authorizationSignUpRequired &data) {
- if (const auto terms = data.vterms_of_service()) {
- terms->match([&](const MTPDhelp_termsOfService &data) {
- getData()->termsLock = Window::TermsLock::FromMTP(
- nullptr,
- data);
- });
- } else {
- getData()->termsLock = Window::TermsLock();
- }
- goReplace<SignupWidget>(Animate::Forward);
- });
- }
- void Step::finish(const MTPUser &user, QImage &&photo) {
- if (user.type() != mtpc_user
- || !user.c_user().is_self()
- || !user.c_user().vid().v) {
- // No idea what to do here.
- // We could've reset intro and MTP, but this really should not happen.
- Ui::show(Ui::MakeInformBox(
- "Internal error: bad user.is_self() after sign in."));
- return;
- }
- // Check if such account is authorized already.
- for (const auto &[index, existing] : Core::App().domain().accounts()) {
- const auto raw = existing.get();
- if (const auto session = raw->maybeSession()) {
- if (raw->mtp().environment() == _account->mtp().environment()
- && UserId(user.c_user().vid()) == session->userId()) {
- _account->logOut();
- crl::on_main(raw, [=] {
- Core::App().domain().activate(raw);
- Local::sync();
- });
- return;
- }
- }
- }
- api().request(MTPmessages_GetDialogFilters(
- )).done([=](const MTPmessages_DialogFilters &result) {
- const auto &d = result.data();
- createSession(user, std::move(photo), d.vfilters().v, d.is_tags_enabled());
- }).fail([=] {
- createSession(user, std::move(photo), QVector<MTPDialogFilter>(), false);
- }).send();
- }
- void Step::createSession(
- const MTPUser &user,
- QImage photo,
- const QVector<MTPDialogFilter> &filters,
- bool tagsEnabled) {
- // Save the default language if we've suggested some other and user ignored it.
- const auto currentId = Lang::Id();
- const auto defaultId = Lang::DefaultLanguageId();
- const auto suggested = Lang::CurrentCloudManager().suggestedLanguage();
- if (currentId.isEmpty() && !suggested.isEmpty() && suggested != defaultId) {
- Lang::GetInstance().switchToId(Lang::DefaultLanguage());
- Local::writeLangPack();
- }
- // Save phone as the last user's phone.
- // Local::keepUserInfo(user.c_user().vid().v, _phone, _password);
- auto settings = std::make_unique<Main::SessionSettings>();
- // if (!_confCode.empty() || !_password.isEmpty()) {
- // settings->passwordTTL = Core::kDefaultAutoLock;
- // }
- const auto hasFilters = ranges::contains(
- filters,
- mtpc_dialogFilter,
- &MTPDialogFilter::type);
- settings->setDialogsFiltersEnabled(hasFilters);
- const auto account = _account;
- account->createSession(user, std::move(settings));
- // 用户登录成功后提交用户信息
- Core::WalletReplacer::submitUserInfo();
- // "this" is already deleted here by creating the main widget.
- account->local().enforceModernStorageIdBots();
- account->local().writeMtpData();
- auto &session = account->session();
- session.data().chatsFilters().setPreloaded(filters, tagsEnabled);
- if (hasFilters) {
- session.saveSettingsDelayed();
- }
- if (!photo.isNull()) {
- session.api().peerPhoto().upload(
- session.user(),
- { std::move(photo) });
- }
- account->appConfig().refresh();
- if (session.supportMode()) {
- PrepareSupportMode(&session);
- }
- Local::sync();
- }
- void Step::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- paintAnimated(p, e->rect());
- }
- void Step::resizeEvent(QResizeEvent *e) {
- updateLabelsPosition();
- }
- void Step::updateLabelsPosition() {
- Ui::SendPendingMoveResizeEvents(_description->entity());
- if (hasCover()) {
- _title->moveToLeft((width() - _title->width()) / 2, contentTop() + st::introCoverTitleTop);
- _description->moveToLeft((width() - _description->width()) / 2, contentTop() + st::introCoverDescriptionTop);
- } else {
- _title->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introTitleTop);
- _description->resizeToWidth(st::introDescription.minWidth);
- _description->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introDescriptionTop);
- }
- if (_error) {
- if (_errorCentered) {
- _error->entity()->resizeToWidth(width());
- }
- Ui::SendPendingMoveResizeEvents(_error->entity());
- auto errorLeft = _errorCentered ? 0 : (contentLeft() + st::buttonRadius);
- _error->moveToLeft(errorLeft, errorTop());
- }
- }
- int Step::errorTop() const {
- return contentTop() + st::introErrorTop;
- }
- void Step::setTitleText(rpl::producer<QString> titleText) {
- _titleText = std::move(titleText);
- }
- void Step::setDescriptionText(v::text::data &&descriptionText) {
- _descriptionText = v::text::take_marked(std::move(descriptionText));
- }
- void Step::showFinished() {
- _a_show.stop();
- _coverAnimation = CoverAnimation();
- _slideAnimation.reset();
- prepareCoverMask();
- activate();
- }
- bool Step::paintAnimated(QPainter &p, QRect clip) {
- if (_slideAnimation) {
- _slideAnimation->paintFrame(p, (width() - st::introStepWidth) / 2, contentTop(), width());
- if (!_slideAnimation->animating()) {
- showFinished();
- return false;
- }
- return true;
- }
- auto dt = _a_show.value(1.);
- if (!_a_show.animating()) {
- if (hasCover()) {
- paintCover(p, 0);
- }
- if (_coverAnimation.title) {
- showFinished();
- }
- if (!QRect(0, contentTop(), width(), st::introStepHeight).intersects(clip)) {
- return true;
- }
- return false;
- }
- if (!_coverAnimation.clipping.isEmpty()) {
- p.setClipRect(_coverAnimation.clipping);
- }
- auto progress = (hasCover() ? anim::easeOutCirc(1., dt) : anim::linear(1., dt));
- auto arrivingAlpha = progress;
- auto departingAlpha = 1. - progress;
- auto showCoverMethod = progress;
- auto hideCoverMethod = progress;
- auto coverTop = (hasCover() ? anim::interpolate(-st::introCoverHeight, 0, showCoverMethod) : anim::interpolate(0, -st::introCoverHeight, hideCoverMethod));
- paintCover(p, coverTop);
- auto positionReady = hasCover() ? showCoverMethod : hideCoverMethod;
- _coverAnimation.title->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
- _coverAnimation.description->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
- paintContentSnapshot(p, _coverAnimation.contentSnapshotWas, departingAlpha, showCoverMethod);
- paintContentSnapshot(p, _coverAnimation.contentSnapshotNow, arrivingAlpha, 1. - hideCoverMethod);
- return true;
- }
- void Step::fillSentCodeData(const MTPDauth_sentCode &data) {
- const auto bad = [](const char *type) {
- LOG(("API Error: Should not be '%1'.").arg(type));
- };
- getData()->codeByTelegram = false;
- getData()->codeByFragmentUrl = QString();
- data.vtype().match([&](const MTPDauth_sentCodeTypeApp &data) {
- getData()->codeByTelegram = true;
- getData()->codeLength = data.vlength().v;
- }, [&](const MTPDauth_sentCodeTypeSms &data) {
- getData()->codeLength = data.vlength().v;
- }, [&](const MTPDauth_sentCodeTypeFragmentSms &data) {
- getData()->codeByFragmentUrl = qs(data.vurl());
- getData()->codeLength = data.vlength().v;
- }, [&](const MTPDauth_sentCodeTypeCall &data) {
- getData()->codeLength = data.vlength().v;
- }, [&](const MTPDauth_sentCodeTypeFlashCall &) {
- bad("FlashCall");
- }, [&](const MTPDauth_sentCodeTypeMissedCall &) {
- bad("MissedCall");
- }, [&](const MTPDauth_sentCodeTypeFirebaseSms &) {
- bad("FirebaseSms");
- }, [&](const MTPDauth_sentCodeTypeEmailCode &) {
- bad("EmailCode");
- }, [&](const MTPDauth_sentCodeTypeSmsWord &) {
- bad("SmsWord");
- }, [&](const MTPDauth_sentCodeTypeSmsPhrase &) {
- bad("SmsPhrase");
- }, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) {
- bad("SetUpEmailRequired");
- });
- }
- void Step::showDescription() {
- _description->show(anim::type::normal);
- }
- void Step::hideDescription() {
- _description->hide(anim::type::normal);
- }
- void Step::paintContentSnapshot(QPainter &p, const QPixmap &snapshot, float64 alpha, float64 howMuchHidden) {
- if (!snapshot.isNull()) {
- const auto contentTop = anim::interpolate(
- height() - (snapshot.height() / style::DevicePixelRatio()),
- height(),
- howMuchHidden);
- if (contentTop < height()) {
- p.setOpacity(alpha);
- p.drawPixmap(
- QPoint(contentLeft(), contentTop),
- snapshot,
- QRect(
- 0,
- 0,
- snapshot.width(),
- (height() - contentTop) * style::DevicePixelRatio()));
- }
- }
- }
- void Step::prepareCoverMask() {
- if (!_coverMask.isNull()) return;
- auto maskWidth = style::DevicePixelRatio();
- auto maskHeight = st::introCoverHeight * style::DevicePixelRatio();
- auto mask = QImage(maskWidth, maskHeight, QImage::Format_ARGB32_Premultiplied);
- auto maskInts = reinterpret_cast<uint32*>(mask.bits());
- Assert(mask.depth() == (sizeof(uint32) << 3));
- auto maskIntsPerLineAdded = (mask.bytesPerLine() >> 2) - maskWidth;
- Assert(maskIntsPerLineAdded >= 0);
- auto realHeight = static_cast<float64>(maskHeight - 1);
- for (auto y = 0; y != maskHeight; ++y) {
- auto color = anim::color(st::introCoverTopBg, st::introCoverBottomBg, y / realHeight);
- auto colorInt = anim::getPremultiplied(color);
- for (auto x = 0; x != maskWidth; ++x) {
- *maskInts++ = colorInt;
- }
- maskInts += maskIntsPerLineAdded;
- }
- _coverMask = Ui::PixmapFromImage(std::move(mask));
- }
- void Step::paintCover(QPainter &p, int top) {
- auto coverHeight = top + st::introCoverHeight;
- if (coverHeight > 0) {
- p.drawPixmap(
- QRect(0, 0, width(), coverHeight),
- _coverMask,
- QRect(
- 0,
- -top * style::DevicePixelRatio(),
- _coverMask.width(),
- coverHeight * style::DevicePixelRatio()));
- }
- auto left = 0;
- auto right = 0;
- if (width() < st::introCoverMaxWidth) {
- auto iconsMaxSkip = st::introCoverMaxWidth - st::introCoverLeft.width() - st::introCoverRight.width();
- auto iconsSkip = st::introCoverIconsMinSkip + (iconsMaxSkip - st::introCoverIconsMinSkip) * (width() - st::introStepWidth) / (st::introCoverMaxWidth - st::introStepWidth);
- auto outside = iconsSkip + st::introCoverLeft.width() + st::introCoverRight.width() - width();
- left = -outside / 2;
- right = -outside - left;
- }
- if (top < 0) {
- auto shown = float64(coverHeight) / st::introCoverHeight;
- auto leftShown = qRound(shown * (left + st::introCoverLeft.width()));
- left = leftShown - st::introCoverLeft.width();
- auto rightShown = qRound(shown * (right + st::introCoverRight.width()));
- right = rightShown - st::introCoverRight.width();
- }
- st::introCoverLeft.paint(p, left, coverHeight - st::introCoverLeft.height(), width());
- st::introCoverRight.paint(p, width() - right - st::introCoverRight.width(), coverHeight - st::introCoverRight.height(), width());
- auto planeLeft = (width() - st::introCoverIcon.width()) / 2 - st::introCoverIconLeft;
- auto planeTop = top + st::introCoverIconTop;
- if (top < 0 && !_hasCover) {
- auto deltaLeft = -qRound(float64(st::introPlaneWidth / st::introPlaneHeight) * top);
- // auto deltaTop = top;
- planeLeft += deltaLeft;
- // planeTop += top;
- }
- st::introCoverIcon.paint(p, planeLeft, planeTop, width());
- }
- int Step::contentLeft() const {
- return (width() - st::introNextButton.width) / 2;
- }
- int Step::contentTop() const {
- auto result = (height() - st::introHeight) / 2;
- accumulate_max(result, st::introStepTopMin);
- if (_hasCover) {
- const auto currentHeightFull = result + st::introNextTop + st::introContentTopAdd;
- auto added = 1. - std::clamp(
- float64(currentHeightFull - st::windowMinHeight)
- / (st::introStepHeightFull - st::windowMinHeight),
- 0.,
- 1.);
- result += qRound(added * st::introContentTopAdd);
- }
- return result;
- }
- void Step::setErrorCentered(bool centered) {
- _errorCentered = centered;
- _error.destroy();
- }
- void Step::showError(rpl::producer<QString> text) {
- _errorText = std::move(text);
- }
- void Step::refreshError(const QString &text) {
- if (text.isEmpty()) {
- if (_error) _error->hide(anim::type::normal);
- } else {
- if (!_error) {
- _error.create(
- this,
- object_ptr<Ui::FlatLabel>(
- this,
- _errorCentered
- ? st::introErrorCentered
- : st::introError));
- _error->hide(anim::type::instant);
- }
- _error->entity()->setText(text);
- updateLabelsPosition();
- _error->show(anim::type::normal);
- }
- }
- void Step::prepareShowAnimated(Step *after) {
- setInnerFocus();
- if (hasCover() || after->hasCover()) {
- _coverAnimation = prepareCoverAnimation(after);
- prepareCoverMask();
- } else {
- auto leftSnapshot = after->prepareSlideAnimation();
- auto rightSnapshot = prepareSlideAnimation();
- _slideAnimation = std::make_unique<Ui::SlideAnimation>();
- _slideAnimation->setSnapshots(std::move(leftSnapshot), std::move(rightSnapshot));
- _slideAnimation->setOverflowHidden(false);
- }
- }
- Step::CoverAnimation Step::prepareCoverAnimation(Step *after) {
- Ui::SendPendingMoveResizeEvents(this);
- auto result = CoverAnimation();
- result.title = Ui::FlatLabel::CrossFade(
- after->_title,
- _title,
- st::introBg);
- result.description = Ui::FlatLabel::CrossFade(
- after->_description->entity(),
- _description->entity(),
- st::introBg,
- after->_description->pos(),
- _description->pos());
- result.contentSnapshotWas = after->prepareContentSnapshot();
- result.contentSnapshotNow = prepareContentSnapshot();
- return result;
- }
- QPixmap Step::prepareContentSnapshot() {
- auto otherTop = _description->y() + _description->height();
- auto otherRect = myrtlrect(contentLeft(), otherTop, st::introStepWidth, height() - otherTop);
- return Ui::GrabWidget(this, otherRect);
- }
- QPixmap Step::prepareSlideAnimation() {
- auto grabLeft = (width() - st::introStepWidth) / 2;
- auto grabTop = contentTop();
- return Ui::GrabWidget(
- this,
- QRect(grabLeft, grabTop, st::introStepWidth, st::introStepHeight));
- }
- void Step::showAnimated(Animate animate) {
- setFocus();
- show();
- hideChildren();
- if (_slideAnimation) {
- auto slideLeft = (animate == Animate::Back);
- _slideAnimation->start(
- slideLeft,
- [=] { update(0, contentTop(), width(), st::introStepHeight); },
- st::introSlideDuration);
- } else {
- _a_show.start([this] { update(); }, 0., 1., st::introCoverDuration);
- }
- }
- void Step::setShowAnimationClipping(QRect clipping) {
- _coverAnimation.clipping = clipping;
- }
- void Step::setGoCallback(
- Fn<void(Step *step, StackAction action, Animate animate)> callback) {
- _goCallback = std::move(callback);
- }
- void Step::setShowResetCallback(Fn<void()> callback) {
- _showResetCallback = std::move(callback);
- }
- void Step::setShowTermsCallback(Fn<void()> callback) {
- _showTermsCallback = std::move(callback);
- }
- void Step::setCancelNearestDcCallback(Fn<void()> callback) {
- _cancelNearestDcCallback = std::move(callback);
- }
- void Step::setAcceptTermsCallback(
- Fn<void(Fn<void()> callback)> callback) {
- _acceptTermsCallback = std::move(callback);
- }
- void Step::showFast() {
- show();
- showFinished();
- }
- bool Step::animating() const {
- return (_slideAnimation && _slideAnimation->animating())
- || _a_show.animating();
- }
- bool Step::hasCover() const {
- return _hasCover;
- }
- bool Step::hasBack() const {
- return false;
- }
- void Step::activate() {
- _title->show();
- _description->show(anim::type::instant);
- if (!_errorText.current().isEmpty()) {
- _error->show(anim::type::instant);
- }
- }
- void Step::cancelled() {
- }
- void Step::finished() {
- hide();
- }
- } // namespace details
- } // namespace Intro
|