Skip to content

Commit

Permalink
Added ability to reset cloud password without recovery email.
Browse files Browse the repository at this point in the history
  • Loading branch information
23rd committed May 10, 2022
1 parent fedd8be commit bcbf009
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 25 deletions.
1 change: 1 addition & 0 deletions Telegram/Resources/langs/lang.strings
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_cloud_password_skip_hint" = "Skip hint";
"lng_settings_cloud_password_save" = "Save and Finish";
"lng_settings_cloud_password_email_confirm" = "Confirm and Finish";
"lng_settings_cloud_password_reset_in" = "You can reset your password in {duration}.";

"lng_clear_payment_info_title" = "Clear payment info";
"lng_clear_payment_info_sure" = "Are you sure you want to clear your payment and shipping info?";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ For license and copyright information please follow this link:
#include "settings/cloud_password/settings_cloud_password_email_confirm.h"

#include "api/api_cloud_password.h"
#include "base/unixtime.h"
#include "core/core_cloud_password.h"
#include "lang/lang_keys.h"
#include "settings/cloud_password/settings_cloud_password_common.h"
Expand All @@ -17,6 +18,7 @@ For license and copyright information please follow this link:
#include "settings/cloud_password/settings_cloud_password_manage.h"
#include "settings/cloud_password/settings_cloud_password_start.h"
#include "ui/boxes/confirm_box.h"
#include "ui/text/format_values.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/sent_code_field.h"
#include "ui/wrap/padding_wrap.h"
Expand Down Expand Up @@ -155,7 +157,65 @@ void EmailConfirm::setupContent() {
newInput->hideError();
});
});
resend->setVisible(recoverEmailPattern.isEmpty());

if (!recoverEmailPattern.isEmpty()) {
resend->setText(tr::lng_signin_try_password(tr::now));

resend->setClickedCallback([=] {
const auto reset = [=](Fn<void()> close) {
if (_requestLifetime) {
return;
}
_requestLifetime = cloudPassword().resetPassword(
) | rpl::start_with_next_error_done([=](
Api::CloudPassword::ResetRetryDate retryDate) {
_requestLifetime.destroy();
const auto left = std::max(
retryDate - base::unixtime::now(),
60);
controller()->show(Ui::MakeInformBox(
tr::lng_cloud_password_reset_later(
tr::now,
lt_duration,
Ui::FormatResetCloudPasswordIn(left))));
}, [=](const QString &type) {
_requestLifetime.destroy();
}, [=] {
_requestLifetime.destroy();

cloudPassword().reload();
using PasswordState = Core::CloudPasswordState;
_requestLifetime = cloudPassword().state(
) | rpl::filter([=](const PasswordState &s) {
return s.pendingResetDate != 0;
}) | rpl::take(
1
) | rpl::start_with_next([=](const PasswordState &s) {
const auto left = (s.pendingResetDate
- base::unixtime::now());
if (left > 0) {
_requestLifetime.destroy();
controller()->show(Ui::MakeInformBox(
tr::lng_settings_cloud_password_reset_in(
tr::now,
lt_duration,
Ui::FormatResetCloudPasswordIn(left))));
setStepData(StepData());
showBack();
}
});
});
_requestLifetime.add(close);
};

controller()->show(Ui::MakeConfirmBox({
.text = tr::lng_cloud_password_reset_with_email(),
.confirmed = reset,
.confirmText = tr::lng_cloud_password_reset_ok(),
.confirmStyle = &st::attentionBoxButton,
}));
});
}

const auto button = AddDoneButton(
content,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ For license and copyright information please follow this link:

#include "api/api_cloud_password.h"
#include "base/qt_signal_producer.h"
#include "base/timer.h"
#include "base/unixtime.h"
#include "core/core_cloud_password.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
Expand All @@ -17,12 +19,14 @@ For license and copyright information please follow this link:
#include "settings/cloud_password/settings_cloud_password_hint.h"
#include "settings/cloud_password/settings_cloud_password_manage.h"
#include "ui/boxes/confirm_box.h"
#include "ui/text/format_values.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.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 {
Expand Down Expand Up @@ -93,6 +97,12 @@ class Input : public TypedAbstractStep<Input> {
[[nodiscard]] rpl::producer<std::vector<Type>> removeFromStack() override;

private:
void setupRecoverButton(
not_null<Ui::VerticalLayout*> container,
not_null<Ui::LinkButton*> button,
not_null<Ui::FlatLabel*> info,
Fn<void()> recoverCallback);

rpl::variable<std::vector<Type>> _removesFromStack;
rpl::lifetime _requestLifetime;

Expand Down Expand Up @@ -192,32 +202,72 @@ void Input::setupContent() {
}
}, hintInfo->lifetime());

const auto recover = AddLinkButton(content, newInput);
recover->setText(tr::lng_signin_recover(tr::now));
recover->setClickedCallback([=] {
auto recoverCallback = [=] {
if (_requestLifetime) {
return;
}
_requestLifetime = cloudPassword().requestPasswordRecovery(
) | rpl::start_with_next_error([=](const QString &pattern) {
_requestLifetime.destroy();
const auto state = cloudPassword().stateCurrent();
if (!state) {
return;
}
if (state->hasRecovery) {
_requestLifetime = cloudPassword().requestPasswordRecovery(
) | rpl::start_with_next_error([=](const QString &pattern) {
_requestLifetime.destroy();

auto data = stepData();
data.processRecover = currentStepProcessRecover;
data.processRecover.emailPattern = pattern;
setStepData(std::move(data));
showOther(CloudPasswordEmailConfirmId());
}, [=](const QString &type) {
_requestLifetime.destroy();

error->show();
if (MTP::IsFloodError(type)) {
error->setText(tr::lng_flood_error(tr::now));
} else {
error->setText(Lang::Hard::ServerError());
}
});
} else {
const auto callback = [=](Fn<void()> close) {
if (_requestLifetime) {
return;
}
close();
_requestLifetime = cloudPassword().resetPassword(
) | rpl::start_with_error_done([=](const QString &type) {
_requestLifetime.destroy();
}, [=] {
_requestLifetime.destroy();
});
};
controller()->show(Ui::MakeConfirmBox({
.text = tr::lng_cloud_password_reset_no_email(),
.confirmed = callback,
.confirmText = tr::lng_cloud_password_reset_ok(),
.cancelText = tr::lng_cancel(),
.confirmStyle = &st::attentionBoxButton,
}));
}
};

auto data = stepData();
data.processRecover = currentStepProcessRecover;
data.processRecover.emailPattern = pattern;
setStepData(std::move(data));
showOther(CloudPasswordEmailConfirmId());
}, [=](const QString &type) {
_requestLifetime.destroy();
const auto recover = AddLinkButton(content, newInput);
const auto resetInfo = Ui::CreateChild<Ui::FlatLabel>(
content,
QString(),
st::boxDividerLabel);
recover->geometryValue(
) | rpl::start_with_next([=](const QRect &r) {
resetInfo->moveToLeft(r.x(), r.y() + st::passcodeTextLine);
}, resetInfo->lifetime());

error->show();
if (MTP::IsFloodError(type)) {
error->setText(tr::lng_flood_error(tr::now));
} else {
error->setText(Lang::Hard::ServerError());
}
});
});
setupRecoverButton(
content,
recover,
resetInfo,
std::move(recoverCallback));
} else if (currentStepProcessRecover.setNewPassword && reenterInput) {
const auto skip = AddLinkButton(content, reenterInput);
skip->setText(tr::lng_settings_auto_night_disable(tr::now));
Expand Down Expand Up @@ -278,6 +328,14 @@ void Input::setupContent() {
}, [=] {
_requestLifetime.destroy();

if (const auto state = cloudPassword().stateCurrent()) {
if (state->pendingResetDate > 0) {
auto lifetime = rpl::lifetime();
lifetime = cloudPassword().cancelResetPassword(
) | rpl::start_with_next([] {});
}
}

auto data = stepData();
data.currentPassword = pass;
setStepData(std::move(data));
Expand Down Expand Up @@ -352,6 +410,142 @@ void Input::setupContent() {
Ui::ResizeFitChild(this, content);
}

void Input::setupRecoverButton(
not_null<Ui::VerticalLayout*> container,
not_null<Ui::LinkButton*> button,
not_null<Ui::FlatLabel*> info,
Fn<void()> recoverCallback) {

struct Status {
enum class SuggestAction {
Recover,
Reset,
CancelReset,
};
SuggestAction suggest = SuggestAction::Recover;
TimeId left = 0;
};

struct State {
base::Timer timer;
rpl::variable<Status> status;
};

const auto state = container->lifetime().make_state<State>();

const auto updateStatus = [=] {
const auto passwordState = cloudPassword().stateCurrent();
const auto date = passwordState ? passwordState->pendingResetDate : 0;
const auto left = (date - base::unixtime::now());
state->status = Status{
.suggest = ((left > 0)
? Status::SuggestAction::CancelReset
: date
? Status::SuggestAction::Reset
: Status::SuggestAction::Recover),
.left = left,
};
};
state->timer.setCallback(updateStatus);
updateStatus();

state->status.value(
) | rpl::start_with_next([=](const Status &status) {
switch (status.suggest) {
case Status::SuggestAction::Recover: {
info->setText(QString());
button->setText(tr::lng_signin_recover(tr::now));
} break;
case Status::SuggestAction::Reset: {
info->setText(QString());
button->setText(tr::lng_cloud_password_reset_ready(tr::now));
} break;
case Status::SuggestAction::CancelReset: {
info->setText(
tr::lng_settings_cloud_password_reset_in(
tr::now,
lt_duration,
Ui::FormatResetCloudPasswordIn(status.left)));
button->setText(
tr::lng_cloud_password_reset_cancel_title(tr::now));
} break;
}
}, container->lifetime());

cloudPassword().state(
) | rpl::start_with_next([=](const Core::CloudPasswordState &passState) {
updateStatus();
state->timer.cancel();
if (passState.pendingResetDate) {
state->timer.callEach(999);
}
}, container->lifetime());

button->setClickedCallback([=] {
const auto passState = cloudPassword().stateCurrent();
if (_requestLifetime || !passState) {
return;
}
updateStatus();
const auto suggest = state->status.current().suggest;
if (suggest == Status::SuggestAction::Recover) {
recoverCallback();
} else if (suggest == Status::SuggestAction::CancelReset) {
const auto cancel = [=](Fn<void()> close) {
if (_requestLifetime) {
return;
}
close();
_requestLifetime = cloudPassword().cancelResetPassword(
) | rpl::start_with_error_done([=](const QString &error) {
_requestLifetime.destroy();
}, [=] {
_requestLifetime.destroy();
});
};
controller()->show(Ui::MakeConfirmBox({
.text = tr::lng_cloud_password_reset_cancel_sure(),
.confirmed = cancel,
.confirmText = tr::lng_box_yes(),
.cancelText = tr::lng_box_no(),
}));
} else if (suggest == Status::SuggestAction::Reset) {
_requestLifetime = cloudPassword().resetPassword(
) | rpl::start_with_next_error_done([=](
Api::CloudPassword::ResetRetryDate retryDate) {
_requestLifetime.destroy();
const auto left = std::max(
retryDate - base::unixtime::now(),
60);
controller()->show(Ui::MakeInformBox(
tr::lng_cloud_password_reset_later(
tr::now,
lt_duration,
Ui::FormatResetCloudPasswordIn(left))));
}, [=](const QString &type) {
_requestLifetime.destroy();
}, [=] {
_requestLifetime.destroy();

cloudPassword().reload();
using PasswordState = Core::CloudPasswordState;
_requestLifetime = cloudPassword().state(
) | rpl::filter([=](const PasswordState &s) {
return !s.hasPassword;
}) | rpl::take(
1
) | rpl::start_with_next([=](const PasswordState &s) {
_requestLifetime.destroy();
controller()->show(Ui::MakeInformBox(
tr::lng_cloud_password_removed()));
setStepData(StepData());
showBack();
});
});
}
});
}

} // namespace CloudPassword

Type CloudPasswordInputId() {
Expand Down
Loading

0 comments on commit bcbf009

Please sign in to comment.