diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 3d7cc7f56..dd4a7fe51 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1074,6 +1074,8 @@ PRIVATE
profile/profile_cover_drop_area.h
settings/cloud_password/settings_cloud_password_common.cpp
settings/cloud_password/settings_cloud_password_common.h
+ settings/cloud_password/settings_cloud_password_input.cpp
+ settings/cloud_password/settings_cloud_password_input.h
settings/cloud_password/settings_cloud_password_start.cpp
settings/cloud_password/settings_cloud_password_start.h
settings/settings_advanced.cpp
diff --git a/Telegram/Resources/animations/cloud_password/password_input.tgs b/Telegram/Resources/animations/cloud_password/password_input.tgs
new file mode 100644
index 000000000..f16eca5ad
Binary files /dev/null and b/Telegram/Resources/animations/cloud_password/password_input.tgs differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index bdd301827..9060e836b 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -699,8 +699,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cloud_password_reset_cancel" = "Cancel password reset";
"lng_cloud_password_enter_old" = "Enter current password";
"lng_cloud_password_enter_first" = "Enter a password";
-"lng_cloud_password_enter_new" = "Enter new password";
-"lng_cloud_password_confirm_new" = "Re-enter new password";
+"lng_cloud_password_enter_new" = "Enter password";
+"lng_cloud_password_confirm_new" = "Re-enter password";
"lng_cloud_password_hint" = "Enter password hint";
"lng_cloud_password_change_hint" = "Enter new password hint";
"lng_cloud_password_bad" = "Password and hint cannot be the same.";
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index 889f17357..3b435db30 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -5,5 +5,6 @@
../../animations/filters.tgs
../../animations/local_passcode_enter.tgs
../../animations/cloud_password/intro.tgs
+ ../../animations/cloud_password/password_input.tgs
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp
index 8874b8d09..fc9a3c9fc 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp
@@ -244,6 +244,24 @@ rpl::producer<> AbstractStep::sectionShowBack() {
return _showBack.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);
+ return my ? (*my) : StepData();
+}
+
+void AbstractStep::setStepData(StepData data) {
+ if (_stepData) {
+ *_stepData = data;
+ }
+}
+
AbstractStep::~AbstractStep() = default;
} // namespace Settings::CloudPassword
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h
index 9e676f76e..19db8ab35 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h
@@ -19,6 +19,10 @@ class VerticalLayout;
namespace Settings::CloudPassword {
+struct StepData {
+ QString password;
+};
+
void SetupHeader(
not_null content,
const QString &lottie,
@@ -79,6 +83,8 @@ public:
[[nodiscard]] rpl::producer sectionShowOther() override;
[[nodiscard]] rpl::producer<> sectionShowBack() override;
+ void setStepDataReference(std::any &data) override;
+
protected:
[[nodiscard]] not_null controller() const;
@@ -89,6 +95,9 @@ protected:
[[nodiscard]] rpl::producer<> showFinishes() const;
+ StepData stepData() const;
+ void setStepData(StepData data);
+
private:
const not_null _controller;
@@ -98,15 +107,17 @@ private:
rpl::event_stream _showOther;
rpl::event_stream<> _showBack;
+ std::any *_stepData;
+
};
template
class TypedAbstractStep : public AbstractStep {
public:
- TypedAbstractStep(
- QWidget *parent,
- not_null controller)
- : AbstractStep(parent, controller) {
+ using AbstractStep::AbstractStep;
+
+ void setStepDataReference(std::any &data) override final {
+ AbstractStep::setStepDataReference(data);
static_cast(this)->setupContent();
}
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.cpp
new file mode 100644
index 000000000..907ced39c
--- /dev/null
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.cpp
@@ -0,0 +1,177 @@
+/*
+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_input.h"
+
+#include "base/qt_signal_producer.h"
+#include "lang/lang_keys.h"
+#include "lottie/lottie_icon.h"
+#include "settings/cloud_password/settings_cloud_password_common.h"
+#include "ui/widgets/buttons.h"
+#include "ui/widgets/input_fields.h"
+#include "ui/widgets/labels.h"
+#include "ui/wrap/vertical_layout.h"
+#include "styles/style_boxes.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace CloudPassword {
+namespace {
+
+struct Icon {
+ not_null icon;
+ Fn update;
+};
+
+Icon CreateInteractiveLottieIcon(
+ not_null container,
+ Lottie::IconDescriptor &&descriptor,
+ style::margins padding) {
+ auto object = object_ptr(container);
+ const auto raw = object.data();
+
+ const auto width = descriptor.sizeOverride.width();
+ raw->resize(QRect(
+ QPoint(),
+ descriptor.sizeOverride).marginsAdded(padding).size());
+
+ auto owned = Lottie::MakeIcon(std::move(descriptor));
+ const auto icon = owned.get();
+
+ raw->lifetime().add([kept = std::move(owned)]{});
+
+ raw->paintRequest(
+ ) | rpl::start_with_next([=] {
+ auto p = QPainter(raw);
+ const auto left = (raw->width() - width) / 2;
+ icon->paint(p, left, padding.top());
+ }, raw->lifetime());
+
+ container->add(std::move(object));
+ return { .icon = icon, .update = [=] { raw->update(); } };
+}
+
+} // namespace
+
+class Input : public TypedAbstractStep {
+public:
+ using TypedAbstractStep::TypedAbstractStep;
+
+ [[nodiscard]] rpl::producer title() override;
+ void setupContent();
+
+};
+
+rpl::producer Input::title() {
+ return tr::lng_settings_cloud_password_password_title();
+}
+
+void Input::setupContent() {
+ const auto content = Ui::CreateChild(this);
+ auto currentStepData = stepData();
+ const auto currentStepDataPassword = base::take(currentStepData.password);
+ setStepData(currentStepData);
+
+ const auto icon = CreateInteractiveLottieIcon(
+ content,
+ {
+ .name = u"cloud_password/password_input"_q,
+ .sizeOverride = {
+ st::changePhoneIconSize,
+ st::changePhoneIconSize
+ },
+ },
+ st::settingLocalPasscodeIconPadding);
+
+ SetupHeader(
+ content,
+ QString(),
+ rpl::never<>(),
+ tr::lng_settings_cloud_password_password_subtitle(),
+ tr::lng_cloud_password_about());
+
+ AddSkip(content, st::settingLocalPasscodeDescriptionBottomSkip);
+
+ const auto newInput = AddPasswordField(
+ content,
+ tr::lng_cloud_password_enter_new(),
+ currentStepDataPassword);
+ const auto reenterInput = AddPasswordField(
+ content,
+ tr::lng_cloud_password_confirm_new(),
+ currentStepDataPassword);
+ const auto error = AddError(content, reenterInput);
+
+ if (!newInput->text().isEmpty()) {
+ icon.icon->jumpTo(icon.icon->framesCount() / 2, icon.update);
+ }
+
+ const auto button = AddDoneButton(content, tr::lng_continue());
+ button->setClickedCallback([=] {
+ const auto newText = newInput->text();
+ const auto reenterText = reenterInput->text();
+ if (newText.isEmpty()) {
+ newInput->setFocus();
+ newInput->showError();
+ } else if (reenterText.isEmpty()) {
+ reenterInput->setFocus();
+ reenterInput->showError();
+ } else if (newText != reenterText) {
+ reenterInput->setFocus();
+ reenterInput->showError();
+ reenterInput->selectAll();
+ error->show();
+ error->setText(tr::lng_cloud_password_differ(tr::now));
+ } else {
+ auto data = stepData();
+ data.password = newText;
+ setStepData(std::move(data));
+ }
+ });
+
+ base::qt_signal_producer(
+ newInput.get(),
+ &QLineEdit::textChanged // Covers Undo.
+ ) | rpl::map([=] {
+ return newInput->text().isEmpty();
+ }) | rpl::distinct_until_changed(
+ ) | rpl::start_with_next([=](bool empty) {
+ const auto from = icon.icon->frameIndex();
+ const auto to = empty ? 0 : (icon.icon->framesCount() / 2 - 1);
+ icon.icon->animate(icon.update, from, to);
+ }, content->lifetime());
+
+ const auto submit = [=] {
+ if (reenterInput->hasFocus()) {
+ button->clicked({}, Qt::LeftButton);
+ } else {
+ reenterInput->setFocus();
+ }
+ };
+ QObject::connect(newInput, &Ui::MaskedInputField::submitted, submit);
+ QObject::connect(reenterInput, &Ui::MaskedInputField::submitted, submit);
+
+ setFocusCallback([=] {
+ if (newInput->text().isEmpty()) {
+ newInput->setFocus();
+ } else if (reenterInput->text().isEmpty()) {
+ reenterInput->setFocus();
+ } else {
+ newInput->setFocus();
+ }
+ });
+
+ Ui::ResizeFitChild(this, content);
+}
+
+} // namespace CloudPassword
+
+Type CloudPasswordInputId() {
+ return CloudPassword::Input::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.h b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.h
new file mode 100644
index 000000000..1103047df
--- /dev/null
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.h
@@ -0,0 +1,17 @@
+/*
+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
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+Type CloudPasswordInputId();
+
+} // namespace Settings
+
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_start.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_start.cpp
index a728efee3..0bb13778d 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_start.cpp
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_start.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "settings/cloud_password/settings_cloud_password_common.h"
+#include "settings/cloud_password/settings_cloud_password_input.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/vertical_layout.h"
#include "styles/style_settings.h"
@@ -49,6 +50,7 @@ void Start::setupContent() {
content,
tr::lng_settings_cloud_password_password_subtitle()
)->setClickedCallback([=] {
+ showOther(CloudPasswordInputId());
});
Ui::ResizeFitChild(this, content);