Files
tdesktop/Telegram/SourceFiles/window/window_setup_email.cpp
2025-12-10 21:28:33 +03:00

670 lines
16 KiB
C++

/*
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 "window/window_setup_email.h"
#include "api/api_cloud_password.h"
#include "apiwrap.h"
#include "base/call_delayed.h"
#include "base/event_filter.h"
#include "core/application.h"
#include "data/components/promo_suggestions.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "intro/intro_code_input.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
#include "main/main_account.h"
#include "main/main_domain.h"
#include "main/main_session.h"
#include "settings/settings_common.h"
#include "ui/painter.h"
#include "ui/ui_utility.h"
#include "ui/vertical_list.h"
#include "ui/controls/userpic_button.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/sent_code_field.h"
#include "ui/wrap/vertical_layout.h"
#include "window/window_controller.h"
#include "window/window_slide_animation.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
#include "styles/style_intro.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include "styles/style_window.h"
namespace Window {
namespace {
[[nodiscard]] Settings::LottieIcon CreateEmailIcon(
not_null<Ui::VerticalLayout*> container) {
return Settings::CreateLottieIcon(
container,
{
.name = u"cloud_password/email"_q,
.sizeOverride = st::normalBoxLottieSize,
},
{});
}
} // namespace
SetupEmailLockWidget::SetupEmailLockWidget(
QWidget *parent,
not_null<Controller*> window)
: LockWidget(parent, window)
, _layout(this)
, _confirmWidget(nullptr) {
const auto session = window->maybeSession();
const auto state = session
? session->promoSuggestions().setupEmailState()
: Data::SetupEmailState::Setup;
const auto noSkip = state == Data::SetupEmailState::SetupNoSkip;
const auto accountsCount = session
? session->domain().accountsAuthedCount()
: 0;
if (!window->isPrimary()) {
// if (state == Data::SetupEmailState::SettingUp
// || state == Data::SetupEmailState::SettingUpNoSkip) {
auto icon = CreateEmailIcon(_layout);
_iconAnimate = std::move(icon.animate);
_layout->add(std::move(icon.widget));
base::call_delayed(st::slideDuration, crl::guard(this, [=] {
if (_iconAnimate) {
_iconAnimate(anim::repeat::once);
}
}));
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
_layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
tr::lng_settings_cloud_login_email_title(),
st::lockSetupEmailTitle),
st::boxRowPadding,
style::al_top);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
_layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
tr::lng_settings_cloud_login_email_busy(),
st::lockSetupEmailLabel),
st::boxRowPadding,
style::al_top);
} else {
if (!noSkip) {
_backButton = object_ptr<Ui::IconButton>(
this,
st::introBackButton);
if (session) {
session->promoSuggestions().setSetupEmailState(
Data::SetupEmailState::SettingUp);
_backButton->setClickedCallback([=] {
session->promoSuggestions().dismissSetupEmail([=] {
});
});
}
} else if (accountsCount <= 1) {
_logoutButton = object_ptr<Ui::RoundButton>(
this,
tr::lng_settings_logout(),
st::defaultBoxButton);
_logoutButton->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
if (session) {
session->promoSuggestions().setSetupEmailState(
Data::SetupEmailState::SettingUpNoSkip);
_logoutButton->setClickedCallback([=] {
window->showLogoutConfirmation();
});
}
} else {
_backButton = object_ptr<Ui::IconButton>(
this,
st::introBackButton);
if (session) {
_backButton->setClickedCallback([=] {
showAccountsMenu();
});
}
}
#ifdef _DEBUG
if (session->isTestMode()) {
_debugButton = object_ptr<Ui::RoundButton>(
this,
rpl::single(u"[DEBUG] Clear bio"_q),
st::defaultBoxButton);
_debugButton->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
_debugButton->setClickedCallback([=] {
session->api().saveSelfBio({});
});
}
#endif
auto icon = CreateEmailIcon(_layout);
_iconAnimate = std::move(icon.animate);
_layout->add(std::move(icon.widget));
base::call_delayed(st::slideDuration, crl::guard(this, [=] {
if (_iconAnimate) {
_iconAnimate(anim::repeat::once);
}
}));
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
_layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
tr::lng_settings_cloud_login_email_title(),
st::lockSetupEmailTitle),
st::boxRowPadding,
style::al_top);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
_layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
tr::lng_settings_cloud_login_email_about(),
st::lockSetupEmailLabel),
st::boxRowPadding,
style::al_top);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
auto emailInput = _layout->add(
object_ptr<Ui::InputField>(
_layout,
st::settingLocalPasscodeInputField,
tr::lng_settings_cloud_login_email_placeholder()),
st::boxRowPadding,
style::al_top);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
auto errorLabel = _layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
QString(),
st::lockSetupEmailLabel),
{},
style::al_top);
errorLabel->setTextColorOverride(st::boxTextFgError->c);
errorLabel->hide();
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
auto submit = _layout->add(
object_ptr<Ui::RoundButton>(
_layout,
tr::lng_settings_cloud_login_email_confirm(),
st::changePhoneButton),
st::boxRowPadding,
style::al_top);
submit->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
_emailInput = emailInput;
_errorLabel = errorLabel;
_submit = submit;
submit->setClickedCallback([=] { this->submit(); });
emailInput->changes() | rpl::on_next([=] {
_error = QString();
errorLabel->hide();
}, emailInput->lifetime());
emailInput->submits() | rpl::on_next([=] {
this->submit();
}, emailInput->lifetime());
}
}
void SetupEmailLockWidget::paintContent(QPainter &p) {
if (_backAnimation) {
_backAnimation->paintContents(p);
return;
}
LockWidget::paintContent(p);
}
void SetupEmailLockWidget::submit() {
if (!_emailInput) {
return;
}
const auto email = _emailInput->getLastText().trimmed();
if (email.isEmpty()) {
_emailInput->setFocus();
_emailInput->showError();
return;
}
if (!email.contains('@') || !email.contains('.')) {
_error = tr::lng_cloud_password_bad_email(tr::now);
_errorLabel->setText(_error);
_errorLabel->show();
_emailInput->setFocus();
_emailInput->showError();
_emailInput->selectAll();
return;
}
const auto session = window()->maybeSession();
if (!session) {
showConfirmWidget(email, 6, QString());
return;
}
_api.emplace(&session->mtp());
const auto done = crl::guard(this, [=](
int length,
const QString &pattern) {
_api.reset();
showConfirmWidget(email, length, pattern);
});
const auto fail = crl::guard(this, [=](const QString &type) {
_api.reset();
if (MTP::IsFloodError(type)) {
_error = tr::lng_flood_error(tr::now);
} else if (type == u"EMAIL_INVALID"_q) {
_error = tr::lng_cloud_password_bad_email(tr::now);
} else {
_error = tr::lng_cloud_password_bad_email(tr::now);
}
_errorLabel->setText(_error);
_errorLabel->show();
_emailInput->setFocus();
_emailInput->showError();
_emailInput->selectAll();
});
Api::RequestLoginEmailCode(*_api, email, done, fail);
}
void SetupEmailLockWidget::showConfirmWidget(
const QString &email,
int codeLength,
const QString &emailPattern) {
_layout->hide();
auto oldContentCache = grabContent();
_confirmWidget = Ui::CreateChild<SetupEmailConfirmWidget>(
this,
window(),
email,
codeLength,
emailPattern);
_confirmWidget->resize(size());
_confirmWidget->show();
_confirmWidget->showAnimated(std::move(oldContentCache));
_confirmWidget->setFocus();
_confirmWidget->backRequests(
) | rpl::on_next([=] {
showEmailInput();
}, _confirmWidget->lifetime());
_confirmWidget->confirmations(
) | rpl::on_next([=, controller = window()] {
if (const auto session = controller->maybeSession()) {
session->promoSuggestions().setSetupEmailState(
Data::SetupEmailState::None);
}
}, _confirmWidget->lifetime());
}
void SetupEmailLockWidget::showEmailInput() {
if (!_confirmWidget) {
return;
}
auto oldContentCache = Ui::GrabWidget(_confirmWidget);
_confirmWidget->hide();
_confirmWidget->deleteLater();
_confirmWidget = nullptr;
if (_logoutButton) {
_logoutButton->show();
}
_layout->show();
auto newContentCache = grabContent();
if (_logoutButton) {
_logoutButton->hide();
}
_layout->hide();
_backAnimation = std::make_unique<SlideAnimation>();
_backAnimation->setDirection(SlideDirection::FromLeft);
_backAnimation->setRepaintCallback([=] { update(); });
_backAnimation->setFinishedCallback([=] {
_layout->show();
if (_logoutButton) {
_logoutButton->show();
}
_backAnimation.reset();
if (_iconAnimate) {
_iconAnimate(anim::repeat::once);
}
});
_backAnimation->setPixmaps(
std::move(oldContentCache),
std::move(newContentCache));
_backAnimation->start();
if (_emailInput) {
_emailInput->setFocus();
}
}
QPixmap SetupEmailLockWidget::grabContent() {
return Ui::GrabWidget(this);
}
void SetupEmailLockWidget::resizeEvent(QResizeEvent *e) {
const auto layoutWidth = std::min(
width(),
st::lockSetupEmailLabelMaxWidth);
_layout->resizeToWidth(layoutWidth);
_layout->moveToLeft(
(width() - layoutWidth) / 2,
st::infoLayerTopBarHeight);
if (_backButton) {
_backButton->moveToLeft(0, 0);
}
if (_logoutButton) {
_logoutButton->move(st::lockSetupEmailLogOutPosition);
}
if (_debugButton) {
_debugButton->moveToLeft(
st::lockSetupEmailLogOutPosition.x(),
height() - _debugButton->height()
- st::lockSetupEmailLogOutPosition.y());
}
if (_confirmWidget) {
_confirmWidget->resize(size());
}
}
void SetupEmailLockWidget::setInnerFocus() {
LockWidget::setInnerFocus();
if (_confirmWidget && _confirmWidget->isVisible()) {
_confirmWidget->setFocus();
} else if (_emailInput) {
_emailInput->setFocus();
}
}
SetupEmailConfirmWidget::SetupEmailConfirmWidget(
QWidget *parent,
not_null<Controller*> window,
const QString &email,
int codeLength,
const QString &emailPattern)
: Ui::RpWidget(parent)
, _window(window)
, _email(email)
, _emailPattern(emailPattern)
, _backButton(this, st::introBackButton)
, _layout(this) {
auto icon = CreateEmailIcon(_layout);
_iconAnimate = std::move(icon.animate);
_layout->add(std::move(icon.widget));
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
_layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
tr::lng_settings_cloud_login_email_code_title(),
st::lockSetupEmailTitle),
st::boxRowPadding,
style::al_top);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
_layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
tr::lng_settings_cloud_login_email_code_about(
tr::now,
lt_email,
_emailPattern.isEmpty() ? _email : _emailPattern),
st::lockSetupEmailLabel),
st::boxRowPadding,
style::al_top);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
auto codeInput = _layout->add(
object_ptr<Ui::CodeInput>(_layout),
style::al_top);
codeInput->setDigitsCountMax(codeLength > 0 ? codeLength : 6);
Ui::AddSkip(_layout);
Ui::AddSkip(_layout);
auto errorLabel = _layout->add(
object_ptr<Ui::FlatLabel>(
_layout,
QString(),
st::lockSetupEmailLabel),
st::boxRowPadding,
style::al_top);
errorLabel->setTextColorOverride(st::boxTextFgError->c);
errorLabel->hide();
_codeInput = codeInput;
_errorLabel = errorLabel;
_backButton->clicks(
) | rpl::to_empty | rpl::start_to_stream(_backRequests, lifetime());
base::install_event_filter(codeInput, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::KeyPress) {
const auto k = static_cast<QKeyEvent*>(e.get());
if (k->key() == Qt::Key_Escape) {
_backRequests.fire({});
return base::EventFilterResult::Cancel;
}
}
return base::EventFilterResult::Continue;
});
codeInput->codeCollected(
) | rpl::on_next([=](const QString &code) {
verifyCode(code);
}, codeInput->lifetime());
}
rpl::producer<> SetupEmailConfirmWidget::backRequests() const {
return _backRequests.events();
}
rpl::producer<> SetupEmailConfirmWidget::confirmations() const {
return _confirmations.events();
}
void SetupEmailConfirmWidget::paintEvent(QPaintEvent *e) {
auto p = Painter(this);
if (_showAnimation) {
_showAnimation->paintContents(p);
return;
}
p.fillRect(rect(), st::windowBg);
}
void SetupEmailConfirmWidget::showAnimated(QPixmap oldContentCache) {
_showAnimation = nullptr;
showChildren();
auto newContentCache = Ui::GrabWidget(this);
hideChildren();
_showAnimation = std::make_unique<SlideAnimation>();
_showAnimation->setDirection(SlideDirection::FromRight);
_showAnimation->setRepaintCallback([=] { update(); });
_showAnimation->setFinishedCallback([=] {
showChildren();
_showAnimation.reset();
if (_iconAnimate) {
_iconAnimate(anim::repeat::once);
}
if (_codeInput) {
_codeInput->setFocus();
}
});
_showAnimation->setPixmaps(
std::move(oldContentCache),
std::move(newContentCache));
_showAnimation->start();
}
void SetupEmailConfirmWidget::resizeEvent(QResizeEvent *e) {
const auto layoutWidth = std::min(
width(),
st::lockSetupEmailLabelMaxWidth);
_layout->resizeToWidth(layoutWidth);
_layout->move((width() - layoutWidth) / 2, st::infoLayerTopBarHeight);
_backButton->move(0, 0);
}
void SetupEmailConfirmWidget::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
_backRequests.fire({});
return;
}
Ui::RpWidget::keyPressEvent(e);
}
void SetupEmailConfirmWidget::verifyCode(const QString &code) {
const auto session = _window->maybeSession();
if (!session) {
_confirmations.fire({});
return;
}
_api.emplace(&session->mtp());
const auto done = crl::guard(this, [=] {
#ifdef _DEBUG
if (session->isTestMode()) {
session->api().saveSelfBio({});
}
#endif
_window->uiShow()->showToast(
tr::lng_settings_cloud_login_email_set_success(tr::now));
_api.reset();
_confirmations.fire({});
});
const auto fail = crl::guard(this, [=](const QString &type) {
_api.reset();
if (type.isEmpty()) {
_confirmations.fire({});
return;
}
if (MTP::IsFloodError(type)) {
_error = tr::lng_flood_error(tr::now);
} else if (type == u"EMAIL_NOT_ALLOWED"_q) {
_error = tr::lng_settings_error_email_not_alowed(tr::now);
} else if (type == u"CODE_INVALID"_q) {
_error = tr::lng_signin_wrong_code(tr::now);
} else if (type == u"CODE_EXPIRED"_q
|| type == u"EMAIL_HASH_EXPIRED"_q) {
_error = Lang::Hard::EmailConfirmationExpired();
} else {
_error = Lang::Hard::ServerError();
}
_errorLabel->setText(_error);
_errorLabel->show();
_codeInput->setFocus();
_codeInput->showError();
});
Api::VerifyLoginEmail(*_api, code, done, fail);
}
void SetupEmailLockWidget::showAccountsMenu() {
const auto session = window()->maybeSession();
if (!session) {
return;
}
const auto &st = st::popupMenuWithIcons;
_accountsMenu = base::make_unique_q<Ui::PopupMenu>(this, st);
const auto accounts = session->domain().orderedAccounts();
for (const auto &account : accounts) {
if (!account->sessionExists()) {
continue;
}
const auto user = account->session().user();
if (user == session->user()) {
continue;
}
const auto action = new QAction(user->name(), _accountsMenu);
QObject::connect(action, &QAction::triggered, [=] {
Core::App().domain().maybeActivate(account);
});
auto owned = base::make_unique_q<Ui::Menu::Action>(
_accountsMenu->menu(),
_accountsMenu->menu()->st(),
action,
nullptr,
nullptr);
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
owned.get(),
user,
st::lockSetupEmailUserpicSmall);
userpic->move(st.menu.itemIconPosition);
_accountsMenu->addAction(std::move(owned));
}
_accountsMenu->setForcedOrigin(Ui::PanelAnimation::Origin::TopLeft);
_accountsMenu->popup(_backButton
? mapToGlobal(QPoint(_backButton->x(), _backButton->height()))
: QCursor::pos());
}
} // namespace Window