Added ability to reset cloud password without recovery email.

This commit is contained in:
23rd 2022-05-09 19:24:34 +03:00
parent fedd8bece3
commit bcbf009a62
6 changed files with 282 additions and 25 deletions

View File

@ -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?";

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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"
@ -17,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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"
@ -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,

View File

@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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"
@ -17,12 +19,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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 {
@ -93,6 +97,12 @@ public:
[[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;
@ -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();
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());
}
});
});
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,
}));
}
};
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());
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));
@ -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));
@ -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() {

View File

@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_local_passcode.h"
#include "settings/settings_privacy_controllers.h"
#include "base/timer_rpl.h"
#include "base/unixtime.h"
#include "boxes/edit_privacy_box.h"
#include "boxes/passcode_box.h"
#include "boxes/auto_lock_box.h"
@ -457,7 +456,6 @@ void SetupCloudPassword(
rpl::duplicate(noconfirmed),
_1 && !_2));
disable->entity()->addClickHandler(remove);
#endif
auto resetAt = session->api().cloudPassword().state(
) | rpl::map([](const State &state) {
@ -574,7 +572,6 @@ void SetupCloudPassword(
}
});
#if 0
const auto abort = container->add(
object_ptr<Ui::SlideWrap<Button>>(
container,

View File

@ -436,4 +436,8 @@ QString FormatMuteForTiny(float64 sec) {
: QString();
}
QString FormatResetCloudPasswordIn(float64 sec) {
return (sec >= 3600) ? FormatTTL(sec) : FormatDurationText(sec);
}
} // namespace Ui

View File

@ -31,6 +31,7 @@ inline constexpr auto FileStatusSizeFailed = 0x7FFFFFF2;
[[nodiscard]] QString FormatTTLTiny(float64 ttl);
[[nodiscard]] QString FormatMuteFor(float64 sec);
[[nodiscard]] QString FormatMuteForTiny(float64 sec);
[[nodiscard]] QString FormatResetCloudPasswordIn(float64 sec);
struct CurrencyRule {
const char *international = "";