tdesktop/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp

366 lines
9.6 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 "settings/cloud_password/settings_cloud_password_common.h"
#include "apiwrap.h"
#include "base/timer.h"
#include "core/application.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
#include "main/main_session.h"
#include "settings/cloud_password/settings_cloud_password_email.h"
#include "settings/cloud_password/settings_cloud_password_email_confirm.h"
#include "settings/cloud_password/settings_cloud_password_hint.h"
#include "settings/cloud_password/settings_cloud_password_input.h"
#include "settings/cloud_password/settings_cloud_password_manage.h"
#include "settings/cloud_password/settings_cloud_password_start.h"
#include "settings/settings_common.h"
#include "ui/boxes/confirm_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/fields/password_input.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
namespace Settings::CloudPassword {
void OneEdgeBoxContentDivider::skipEdge(Qt::Edge edge, bool skip) {
const auto was = _skipEdges;
if (skip) {
_skipEdges |= edge;
} else {
_skipEdges &= ~edge;
}
if (was != _skipEdges) {
update();
}
}
void OneEdgeBoxContentDivider::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
p.fillRect(e->rect(), Ui::BoxContentDivider::color());
if (!(_skipEdges & Qt::TopEdge)) {
Ui::BoxContentDivider::paintTop(p);
}
if (!(_skipEdges & Qt::BottomEdge)) {
Ui::BoxContentDivider::paintBottom(p);
}
}
BottomButton CreateBottomDisableButton(
not_null<Ui::RpWidget*> parent,
rpl::producer<QRect> &&sectionGeometryValue,
rpl::producer<QString> &&buttonText,
Fn<void()> &&callback) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(parent.get());
AddSkip(content);
AddButton(
content,
std::move(buttonText),
st::settingsAttentionButton
)->addClickHandler(std::move(callback));
const auto divider = Ui::CreateChild<OneEdgeBoxContentDivider>(
parent.get());
divider->skipEdge(Qt::TopEdge, true);
rpl::combine(
std::move(sectionGeometryValue),
parent->geometryValue(),
content->geometryValue()
) | rpl::start_with_next([=](
const QRect &r,
const QRect &parentRect,
const QRect &bottomRect) {
const auto top = r.y() + r.height();
divider->setGeometry(
0,
top,
r.width(),
parentRect.height() - top - bottomRect.height());
}, divider->lifetime());
divider->show();
return {
.content = Ui::MakeWeak(not_null<Ui::RpWidget*>{ content }),
.isBottomFillerShown = divider->geometryValue(
) | rpl::map([](const QRect &r) {
return r.height() > 0;
}),
};
}
void SetupAutoCloseTimer(rpl::lifetime &lifetime, Fn<void()> callback) {
constexpr auto kTimerCheck = crl::time(1000 * 60);
constexpr auto kAutoCloseTimeout = crl::time(1000 * 60 * 10);
const auto timer = lifetime.make_state<base::Timer>([=] {
const auto idle = crl::now() - Core::App().lastNonIdleTime();
if (idle >= kAutoCloseTimeout) {
callback();
}
});
timer->callEach(kTimerCheck);
}
void SetupHeader(
not_null<Ui::VerticalLayout*> content,
const QString &lottie,
rpl::producer<> &&showFinished,
rpl::producer<QString> &&subtitle,
rpl::producer<QString> &&about) {
if (!lottie.isEmpty()) {
const auto &size = st::settingsCloudPasswordIconSize;
auto icon = CreateLottieIcon(
content,
{ .name = lottie, .sizeOverride = { size, size } },
st::settingLocalPasscodeIconPadding);
content->add(std::move(icon.widget));
std::move(
showFinished
) | rpl::start_with_next([animate = std::move(icon.animate)] {
animate(anim::repeat::once);
}, content->lifetime());
}
AddSkip(content);
content->add(
object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::FlatLabel>(
content,
std::move(subtitle),
st::changePhoneTitle)),
st::changePhoneTitlePadding);
{
const auto &st = st::settingLocalPasscodeDescription;
const auto wrap = content->add(
object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::FlatLabel>(content, std::move(about), st)),
st::changePhoneDescriptionPadding);
wrap->resize(
wrap->width(),
st::settingLocalPasscodeDescriptionHeight);
}
}
not_null<Ui::PasswordInput*> AddPasswordField(
not_null<Ui::VerticalLayout*> content,
rpl::producer<QString> &&placeholder,
const QString &text) {
const auto &st = st::settingLocalPasscodeInputField;
auto container = object_ptr<Ui::RpWidget>(content);
container->resize(container->width(), st.heightMin);
const auto field = Ui::CreateChild<Ui::PasswordInput>(
container.data(),
st,
std::move(placeholder),
text);
container->geometryValue(
) | rpl::start_with_next([=](const QRect &r) {
field->moveToLeft((r.width() - field->width()) / 2, 0);
}, container->lifetime());
content->add(std::move(container));
return field;
}
not_null<Ui::CenterWrap<Ui::InputField>*> AddWrappedField(
not_null<Ui::VerticalLayout*> content,
rpl::producer<QString> &&placeholder,
const QString &text) {
return content->add(object_ptr<Ui::CenterWrap<Ui::InputField>>(
content,
object_ptr<Ui::InputField>(
content,
st::settingLocalPasscodeInputField,
std::move(placeholder),
text)));
}
not_null<Ui::LinkButton*> AddLinkButton(
not_null<Ui::CenterWrap<Ui::InputField>*> wrap,
rpl::producer<QString> &&text) {
const auto button = Ui::CreateChild<Ui::LinkButton>(
wrap->parentWidget(),
QString());
std::move(
text
) | rpl::start_with_next([=](const QString &text) {
button->setText(text);
}, button->lifetime());
wrap->geometryValue(
) | rpl::start_with_next([=](QRect r) {
r.translate(wrap->entity()->pos().x(), 0);
button->moveToLeft(r.x(), r.y() + r.height() + st::passcodeTextLine);
}, button->lifetime());
return button;
}
not_null<Ui::FlatLabel*> AddError(
not_null<Ui::VerticalLayout*> content,
Ui::PasswordInput *input) {
const auto error = content->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
content,
object_ptr<Ui::FlatLabel>(
content,
// Set any text to resize.
tr::lng_language_name(tr::now),
st::settingLocalPasscodeError)),
st::changePhoneDescriptionPadding)->entity();
error->hide();
if (input) {
QObject::connect(input, &Ui::MaskedInputField::changed, [=] {
error->hide();
});
}
return error;
};
not_null<Ui::RoundButton*> AddDoneButton(
not_null<Ui::VerticalLayout*> content,
rpl::producer<QString> &&text) {
const auto button = content->add(
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
content,
object_ptr<Ui::RoundButton>(
content,
std::move(text),
st::changePhoneButton)),
st::settingLocalPasscodeButtonPadding)->entity();
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
return button;
}
void AddSkipInsteadOfField(not_null<Ui::VerticalLayout*> content) {
AddSkip(content, st::settingLocalPasscodeInputField.heightMin);
}
void AddSkipInsteadOfError(not_null<Ui::VerticalLayout*> content) {
auto dummy = base::make_unique_q<Ui::FlatLabel>(
content,
tr::lng_language_name(tr::now),
st::settingLocalPasscodeError);
const auto &padding = st::changePhoneDescriptionPadding;
AddSkip(content, dummy->height() + padding.top() + padding.bottom());
dummy = nullptr;
}
AbstractStep::AbstractStep(
QWidget *parent,
not_null<Window::SessionController*> controller)
: AbstractSection(parent)
, _controller(controller) {
}
not_null<Window::SessionController*> AbstractStep::controller() const {
return _controller;
}
Api::CloudPassword &AbstractStep::cloudPassword() {
return _controller->session().api().cloudPassword();
}
rpl::producer<AbstractStep::Types> AbstractStep::removeTypes() {
return rpl::never<Types>();
}
void AbstractStep::showBack() {
_showBack.fire({});
}
void AbstractStep::showOther(Type type) {
_showOther.fire_copy(type);
}
void AbstractStep::setFocusCallback(Fn<void()> callback) {
_setInnerFocusCallback = callback;
}
rpl::producer<> AbstractStep::showFinishes() const {
return _showFinished.events();
}
void AbstractStep::showFinished() {
_showFinished.fire({});
}
void AbstractStep::setInnerFocus() {
if (_setInnerFocusCallback) {
_setInnerFocusCallback();
}
}
bool AbstractStep::isPasswordInvalidError(const QString &type) {
if (type == u"PASSWORD_HASH_INVALID"_q
|| type == u"SRP_PASSWORD_CHANGED"_q) {
// Most likely the cloud password has been changed on another device.
// Quit.
_quits.fire(AbstractStep::Types{
CloudPasswordStartId(),
CloudPasswordInputId(),
CloudPasswordHintId(),
CloudPasswordEmailId(),
CloudPasswordEmailConfirmId(),
CloudPasswordManageId(),
});
controller()->show(
Ui::MakeInformBox(tr::lng_cloud_password_expired()),
Ui::LayerOption::CloseOther);
setStepData(StepData());
showBack();
return true;
}
return false;
}
rpl::producer<Type> AbstractStep::sectionShowOther() {
return _showOther.events();
}
rpl::producer<> AbstractStep::sectionShowBack() {
return _showBack.events();
}
rpl::producer<std::vector<Type>> AbstractStep::removeFromStack() {
return rpl::merge(removeTypes(), _quits.events());
}
void AbstractStep::setStepDataReference(std::any &data) {
_stepData = &data;
}
StepData AbstractStep::stepData() const {
if (!_stepData || !_stepData->has_value()) {
StepData();
}
const auto my = std::any_cast<StepData>(_stepData);
return my ? (*my) : StepData();
}
void AbstractStep::setStepData(StepData data) {
if (_stepData) {
*_stepData = data;
}
}
AbstractStep::~AbstractStep() = default;
} // namespace Settings::CloudPassword