diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6e45bd766..f8920317a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -490,7 +490,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_forwards_privacy" = "Forwarded messages"; "lng_settings_profile_photo_privacy" = "Profile photo"; "lng_settings_sessions_about" = "Control your sessions on other devices."; -"lng_settings_passcode_disable" = "Disable passcode"; +"lng_settings_passcode_disable" = "Disable Passcode"; +"lng_settings_passcode_disable_sure" = "Are you sure you want to disable passcode?"; "lng_settings_password_disable" = "Disable cloud password"; "lng_settings_password_abort" = "Abort two-step verification setup"; "lng_settings_password_reenter_email" = "Re-enter recovery email"; @@ -634,7 +635,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_passcode_remove_button" = "Remove"; "lng_passcode_turn_on" = "Turn on local passcode"; -"lng_passcode_change" = "Change local passcode"; +"lng_passcode_change" = "Change Passcode"; "lng_passcode_create" = "Local passcode"; "lng_passcode_remove" = "Remove local passcode"; "lng_passcode_turn_off" = "Turn off"; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 90336e8ba..5f95d04f2 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -153,7 +153,7 @@ settingLocalPasscodeError: FlatLabel(changePhoneError) { } settingLocalPasscodeDescriptionBottomSkip: 15px; settingLocalPasscodeIconPadding: margins(0px, 19px, 0px, 5px); -settingLocalPasscodeButtonPadding: margins(0px, 19px, 0px, 15px); +settingLocalPasscodeButtonPadding: margins(0px, 19px, 0px, 35px); settingsInfoPhotoHeight: 161px; settingsInfoPhotoSize: 100px; diff --git a/Telegram/SourceFiles/settings/settings_local_passcode.cpp b/Telegram/SourceFiles/settings/settings_local_passcode.cpp index 505625b28..342244e98 100644 --- a/Telegram/SourceFiles/settings/settings_local_passcode.cpp +++ b/Telegram/SourceFiles/settings/settings_local_passcode.cpp @@ -7,20 +7,88 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_local_passcode.h" +#include "base/platform/base_platform_last_input.h" +#include "base/timer.h" +#include "boxes/auto_lock_box.h" +#include "core/application.h" #include "lang/lang_keys.h" #include "lottie/lottie_icon.h" #include "main/main_domain.h" #include "main/main_session.h" #include "storage/storage_domain.h" +#include "ui/boxes/confirm_box.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_settings.h" #include "styles/style_boxes.h" +#include "styles/style_layers.h" +#include "styles/style_settings.h" namespace Settings { +namespace { + +constexpr auto kTimerCheck = crl::time(1000 * 60); +constexpr auto kAutoCloseTimeout = crl::time(1000 * 60 * 10); + +void SetPasscode( + not_null controller, + const QString &pass) { + cSetPasscodeBadTries(0); + controller->session().domain().local().setPasscode(pass.toUtf8()); + Core::App().localPasscodeChanged(); +} + +void SetupAutoCloseTimer(rpl::lifetime &lifetime, Fn callback) { + const auto timer = lifetime.make_state([=] { + const auto idle = crl::now() - Core::App().lastNonIdleTime(); + if (idle >= kAutoCloseTimeout) { + callback(); + } + }); + timer->callEach(kTimerCheck); +} + +class Divider : public Ui::BoxContentDivider { +public: + using Ui::BoxContentDivider::BoxContentDivider; + + void skipEdge(Qt::Edge edge, bool skip); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + Qt::Edges _skipEdges; + +}; + +void Divider::skipEdge(Qt::Edge edge, bool skip) { + const auto was = _skipEdges; + if (skip) { + _skipEdges |= edge; + } else { + _skipEdges &= ~edge; + } + if (was != _skipEdges) { + update(); + } +} + +void Divider::paintEvent(QPaintEvent *e) { + Painter p(this); + p.fillRect(e->rect(), Ui::BoxContentDivider::color()); + if (!(_skipEdges & Qt::TopEdge)) { + Ui::BoxContentDivider::paintTop(p); + } + if (!(_skipEdges & Qt::BottomEdge)) { + Ui::BoxContentDivider::paintBottom(p); + } +} + +} // namespace + namespace details { LocalPasscodeEnter::LocalPasscodeEnter( @@ -39,6 +107,7 @@ void LocalPasscodeEnter::setupContent() { const auto isCreate = (enterType() == EnterType::Create); const auto isCheck = (enterType() == EnterType::Check); + [[maybe_unused]] const auto isChange = (enterType() == EnterType::Change); auto icon = CreateLottieIcon( content, @@ -56,6 +125,10 @@ void LocalPasscodeEnter::setupContent() { animate(anim::repeat::once); }, content->lifetime()); + if (isChange) { + SetupAutoCloseTimer(content->lifetime(), [=] { _showBack.fire({}); }); + } + AddSkip(content); content->add( @@ -126,9 +199,7 @@ void LocalPasscodeEnter::setupContent() { const auto reenterPasscode = isCheck ? (Ui::PasswordInput*)(nullptr) : addField(tr::lng_passcode_confirm_new()); - const auto reenterError = isCheck - ? (Ui::FlatLabel*)(nullptr) - : addError(reenterPasscode); + const auto error = addError(isCheck ? newPasscode : reenterPasscode); const auto button = content->add( object_ptr>( @@ -148,7 +219,7 @@ void LocalPasscodeEnter::setupContent() { const auto reenterText = reenterPasscode ? reenterPasscode->text() : QString(); - if (isCreate) { + if (isCreate || isChange) { if (newText.isEmpty()) { newPasscode->setFocus(); newPasscode->showError(); @@ -159,14 +230,64 @@ void LocalPasscodeEnter::setupContent() { reenterPasscode->setFocus(); reenterPasscode->showError(); reenterPasscode->selectAll(); - reenterError->show(); - reenterError->setText(tr::lng_passcode_differ(tr::now)); + error->show(); + error->setText(tr::lng_passcode_differ(tr::now)); } else { - // showOther + if (isChange) { + const auto &domain = _controller->session().domain(); + if (domain.local().checkPasscode(newText.toUtf8())) { + newPasscode->setFocus(); + newPasscode->showError(); + newPasscode->selectAll(); + error->show(); + error->setText(tr::lng_passcode_is_same(tr::now)); + return; + } + } + SetPasscode(_controller, newText); + if (isCreate) { + _showOther.fire(LocalPasscodeManage::Id()); + } else if (isChange) { + _showBack.fire({}); + } + } + } else if (isCheck) { + if (!passcodeCanTry()) { + newPasscode->setFocus(); + newPasscode->showError(); + error->show(); + error->setText(tr::lng_flood_error(tr::now)); + return; + } + const auto &domain = _controller->session().domain(); + if (domain.local().checkPasscode(newText.toUtf8())) { + cSetPasscodeBadTries(0); + _showOther.fire(LocalPasscodeManage::Id()); + } else { + cSetPasscodeBadTries(cPasscodeBadTries() + 1); + cSetPasscodeLastTry(crl::now()); + + newPasscode->selectAll(); + newPasscode->setFocus(); + newPasscode->showError(); + error->show(); + error->setText(tr::lng_passcode_wrong(tr::now)); } } }); + const auto submit = [=] { + if (!reenterPasscode || reenterPasscode->hasFocus()) { + button->clicked({}, Qt::LeftButton); + } else { + reenterPasscode->setFocus(); + } + }; + connect(newPasscode, &Ui::MaskedInputField::submitted, submit); + if (reenterPasscode) { + connect(reenterPasscode, &Ui::MaskedInputField::submitted, submit); + } + _setInnerFocus.events( ) | rpl::start_with_next([=] { if (newPasscode->text().isEmpty()) { @@ -189,7 +310,185 @@ void LocalPasscodeEnter::setInnerFocus() { _setInnerFocus.fire({}); } +rpl::producer LocalPasscodeEnter::sectionShowOther() { + return _showOther.events(); +} + +rpl::producer<> LocalPasscodeEnter::sectionShowBack() { + return _showBack.events(); +} + LocalPasscodeEnter::~LocalPasscodeEnter() = default; } // namespace details + +LocalPasscodeManage::LocalPasscodeManage( + QWidget *parent, + not_null controller) +: Section(parent) +, _controller(controller) { + setupContent(); +} + +rpl::producer LocalPasscodeManage::title() { + return tr::lng_settings_passcode_title(); +} + +rpl::producer> LocalPasscodeManage::removeFromStack() { + return rpl::single(std::vector{ + LocalPasscodeManage::Id(), + LocalPasscodeCreate::Id(), + LocalPasscodeCheck::Id(), + LocalPasscodeChange::Id(), + }); +} + +void LocalPasscodeManage::setupContent() { + const auto content = Ui::CreateChild(this); + + struct State { + rpl::event_stream<> autoLockBoxClosing; + }; + const auto state = content->lifetime().make_state(); + + SetupAutoCloseTimer(content->lifetime(), [=] { _showBack.fire({}); }); + + AddSkip(content); + + AddButton( + content, + tr::lng_passcode_change(), + st::settingsButton, + { &st::settingsIconLock, kIconLightBlue } + )->addClickHandler([=] { + _showOther.fire(LocalPasscodeChange::Id()); + }); + + auto autolockLabel = state->autoLockBoxClosing.events_starting_with( + {} + ) | rpl::map([] { + const auto autolock = Core::App().settings().autoLock(); + const auto hours = autolock / 3600; + const auto minutes = (autolock - (hours * 3600)) / 60; + + return (hours && minutes) + ? tr::lng_passcode_autolock_hours_minutes( + tr::now, + lt_hours_count, + QString::number(hours), + lt_minutes_count, + QString::number(minutes)) + : minutes + ? tr::lng_minutes(tr::now, lt_count, minutes) + : tr::lng_hours(tr::now, lt_count, hours); + }); + + AddButtonWithLabel( + content, + (base::Platform::LastUserInputTimeSupported() + ? tr::lng_passcode_autolock_away + : tr::lng_passcode_autolock_inactive)(), + std::move(autolockLabel), + st::settingsButton, + { &st::settingsIconTimer, kIconGreen } + )->addClickHandler([=] { + const auto box = _controller->show(Box()); + box->boxClosing( + ) | rpl::start_to_stream(state->autoLockBoxClosing, box->lifetime()); + }); + + AddSkip(content); + + const auto divider = Ui::CreateChild(this); + divider->lower(); + const auto about = content->add( + object_ptr>( + content, + object_ptr( + content, + rpl::combine( + tr::lng_passcode_about1(), + tr::lng_passcode_about3() + ) | rpl::map([](const QString &s1, const QString &s2) { + return s1 + "\n\n" + s2; + }), + st::boxDividerLabel), + st::settingsDividerLabelPadding)); + about->geometryValue( + ) | rpl::start_with_next([=](const QRect &r) { + divider->setGeometry(r); + }, divider->lifetime()); + _isBottomFillerShown.value( + ) | rpl::start_with_next([=](bool shown) { + divider->skipEdge(Qt::BottomEdge, shown); + }, divider->lifetime()); + + Ui::ResizeFitChild(this, content); +} + +QPointer LocalPasscodeManage::createPinnedToBottom( + not_null parent) { + const auto content = Ui::CreateChild(parent.get()); + + AddSkip(content); + + AddButton( + content, + tr::lng_settings_passcode_disable(), + st::settingsAttentionButton + )->addClickHandler([=] { + _controller->show( + Ui::MakeConfirmBox({ + .text = tr::lng_settings_passcode_disable_sure(), + .confirmed = [=](Fn &&close) { + SetPasscode(_controller, QString()); + + close(); + _showBack.fire({}); + }, + .confirmText = tr::lng_settings_auto_night_disable(), + .confirmStyle = &st::attentionBoxButton, + })); + }); + + const auto divider = Ui::CreateChild(parent.get()); + divider->skipEdge(Qt::TopEdge, true); + rpl::combine( + geometryValue(), + 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(); + _isBottomFillerShown = divider->geometryValue( + ) | rpl::map([](const QRect &r) { + return r.height() > 0; + }); + + return Ui::MakeWeak(not_null{ content }); +} + +void LocalPasscodeManage::showFinished() { + _showFinished.fire({}); +} + +rpl::producer LocalPasscodeManage::sectionShowOther() { + return _showOther.events(); +} + +rpl::producer<> LocalPasscodeManage::sectionShowBack() { + return _showBack.events(); +} + +LocalPasscodeManage::~LocalPasscodeManage() = default; + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_local_passcode.h b/Telegram/SourceFiles/settings/settings_local_passcode.h index ad12e1ee4..a8103c271 100644 --- a/Telegram/SourceFiles/settings/settings_local_passcode.h +++ b/Telegram/SourceFiles/settings/settings_local_passcode.h @@ -27,6 +27,8 @@ public: void showFinished() override; void setInnerFocus() override; + [[nodiscard]] rpl::producer sectionShowOther() override; + [[nodiscard]] rpl::producer<> sectionShowBack() override; [[nodiscard]] rpl::producer title() override; @@ -41,6 +43,8 @@ private: rpl::event_stream<> _showFinished; rpl::event_stream<> _setInnerFocus; + rpl::event_stream _showOther; + rpl::event_stream<> _showBack; }; @@ -104,5 +108,36 @@ public: }; +class LocalPasscodeManage : public Section { +public: + LocalPasscodeManage( + QWidget *parent, + not_null controller); + ~LocalPasscodeManage(); + + [[nodiscard]] rpl::producer title() override; + + void showFinished() override; + [[nodiscard]] rpl::producer sectionShowOther() override; + [[nodiscard]] rpl::producer<> sectionShowBack() override; + + [[nodiscard]] rpl::producer> removeFromStack() override; + + [[nodiscard]] QPointer createPinnedToBottom( + not_null parent) override; + +private: + void setupContent(); + + const not_null _controller; + + rpl::variable _isBottomFillerShown; + + rpl::event_stream<> _showFinished; + rpl::event_stream _showOther; + rpl::event_stream<> _showBack; + +}; + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index f0507396e..08c283f00 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_global_privacy.h" #include "settings/settings_blocked_peers.h" #include "settings/settings_common.h" +#include "settings/settings_local_passcode.h" #include "settings/settings_privacy_controllers.h" #include "base/timer_rpl.h" #include "base/unixtime.h" @@ -223,7 +224,8 @@ void SetupArchiveAndMute( void SetupLocalPasscode( not_null controller, - not_null container) { + not_null container, + Fn showOther) { AddSkip(container); AddDivider(container); AddSkip(container); @@ -237,7 +239,7 @@ void SetupLocalPasscode( auto text = rpl::combine( tr::lng_passcode_change(), tr::lng_passcode_turn_on(), - base::duplicate(has), + std::move(has), [](const QString &change, const QString &create, bool has) { return has ? change : create; }); @@ -247,60 +249,12 @@ void SetupLocalPasscode( st::settingsButton, { &st::settingsIconLock, kIconGreen } )->addClickHandler([=] { - controller->show(Box(&controller->session(), false)); + if (controller->session().domain().local().hasLocalPasscode()) { + showOther(LocalPasscodeCheck::Id()); + } else { + showOther(LocalPasscodeCreate::Id()); + } }); - - const auto wrap = container->add( - object_ptr>( - container, - object_ptr(container))); - const auto inner = wrap->entity(); - AddButton( - inner, - tr::lng_settings_passcode_disable(), - st::settingsButton, - { &st::settingsIconMinus, kIconRed } - )->addClickHandler([=] { - controller->show(Box(&controller->session(), true)); - }); - - const auto autoLockBoxClosing = - container->lifetime().make_state>(); - const auto label = base::Platform::LastUserInputTimeSupported() - ? tr::lng_passcode_autolock_away - : tr::lng_passcode_autolock_inactive; - auto value = autoLockBoxClosing->events_starting_with( - {} - ) | rpl::map([] { - const auto autolock = Core::App().settings().autoLock(); - const auto hours = autolock / 3600; - const auto minutes = (autolock - (hours * 3600)) / 60; - - return (hours && minutes) - ? tr::lng_passcode_autolock_hours_minutes( - tr::now, - lt_hours_count, - QString::number(hours), - lt_minutes_count, - QString::number(minutes)) - : minutes - ? tr::lng_minutes(tr::now, lt_count, minutes) - : tr::lng_hours(tr::now, lt_count, hours); - }); - - AddButtonWithLabel( - inner, - label(), - std::move(value), - st::settingsButton, - { &st::settingsIconTimer, kIconGreen } - )->addClickHandler([=] { - const auto box = controller->show(Box()); - box->boxClosing( - ) | rpl::start_to_stream(*autoLockBoxClosing, box->lifetime()); - }); - - wrap->toggleOn(base::duplicate(has)); } void SetupCloudPassword( @@ -826,7 +780,7 @@ void SetupSecurity( container, rpl::duplicate(updateTrigger), showOther); - SetupLocalPasscode(controller, container); + SetupLocalPasscode(controller, container, showOther); SetupCloudPassword(controller, container); } diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 9b6e11db6..83553d082 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 9b6e11db62810626a56431cdc8eeb771817f6560 +Subproject commit 83553d08264effc9909e6674fa19c9d8d74a71de