diff --git a/storybook/PagesModel.qml b/storybook/PagesModel.qml index 991a58b327..a775eb2b12 100644 --- a/storybook/PagesModel.qml +++ b/storybook/PagesModel.qml @@ -69,6 +69,10 @@ ListModel { title: "DeviceSyncingView" section: "Views" } + ListElement { + title: "PasswordView" + section: "Views" + } ListElement { title: "CommunitiesView" section: "Views" diff --git a/storybook/pages/PasswordViewPage.qml b/storybook/pages/PasswordViewPage.qml new file mode 100644 index 0000000000..10a0f68c52 --- /dev/null +++ b/storybook/pages/PasswordViewPage.qml @@ -0,0 +1,69 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Extras 1.4 + +import shared.views 1.0 + +import Storybook 1.0 + +SplitView { + orientation: Qt.Vertical + + Logs { id: logs } + + Item { + id: wrapper + SplitView.fillWidth: true + SplitView.fillHeight: true + + PasswordView { + id: passwordView + width: 460 + height: 416 + anchors.centerIn: parent + onReturnPressed: logs.logEvent("Return pressed", ["Current Password", "New Password", "Confirmation Password"], [passwordView.currentPswText, passwordView.newPswText, passwordView.confirmationPswText]) + + createNewPsw: createNewPassword.checked + titleVisible: titleVisibleSwitch.checked + highSizeIntro: highSizeIntroSwitch.checked + passwordStrengthScoreFunction: (newPass) => Math.min(newPass.length, 4) + } + } + + LogsAndControlsPanel { + id: logsAndControlsPanel + + SplitView.minimumHeight: 100 + SplitView.preferredHeight: 150 + + logsView.logText: logs.logText + + RowLayout { + StatusIndicator { + color: "green" + active: passwordView.ready + } + + Text { + leftPadding: 10 + text: "Ready" + } + + Switch { + id: createNewPassword + text: "Create new password" + } + + Switch { + id: highSizeIntroSwitch + text: "High size Intro" + } + + Switch { + id: titleVisibleSwitch + text: "Title visible" + } + } + } +} diff --git a/ui/imports/shared/views/PasswordView.qml b/ui/imports/shared/views/PasswordView.qml index 9055d51538..72657069fc 100644 --- a/ui/imports/shared/views/PasswordView.qml +++ b/ui/imports/shared/views/PasswordView.qml @@ -54,7 +54,6 @@ ColumnLayout { function checkPasswordMatches(onlyIfConfirmPasswordHasFocus = true) { if (confirmPswInput.text.length === 0) { - errorTxt.text = "" return } @@ -63,7 +62,6 @@ ColumnLayout { } if(newPswInput.text.length >= Constants.minPasswordLength) { - errorTxt.text = "" if(confirmPswInput.text !== newPswInput.text) { errorTxt.text = qsTr("Passwords don't match") } @@ -78,7 +76,8 @@ ColumnLayout { property bool containsNumbers: false property bool containsSymbols: false - readonly property var validator: RegExpValidator { regExp: /^[!-~]{0,64}$/ } // That incudes NOT extended ASCII printable characters less space and a maximum of 64 characters allowed + readonly property var validatorRegexp: /^[!-~]{0,64}$/ + readonly property string validatorErrMessage: qsTr("Only letters, numbers, underscores and hyphens allowed") // Password strength categorization / validation function lowerCaseValidator(text) { return (/[a-z]/.test(text)) } @@ -87,6 +86,15 @@ ColumnLayout { // That incudes NOT extended ASCII printable symbols less space: function symbolsValidator(text) { return (/[!-\/:-@[-`{-~]/.test(text)) } + function validateCharacterSet(text) { + if(!(d.validatorRegexp).test(text)) { + errorTxt.text = d.validatorErrMessage + return false + } + + return true + } + // Used to convert strength from a given score to a specific category function convertStrength(score) { var strength = StatusPasswordStrengthIndicator.Strength.None @@ -104,8 +112,6 @@ ColumnLayout { // Password validation / error message selection: function passwordValidation() { - errorTxt.text = "" - // 3 rules to validate: // * Password is in pwnd passwords database if(isInPwndDatabase()) @@ -168,7 +174,6 @@ ColumnLayout { Layout.fillWidth: true placeholderText: qsTr("Current password") echoMode: showPassword ? TextInput.Normal : TextInput.Password - validator: d.validator rightPadding: showHideCurrentIcon.width + showHideCurrentIcon.anchors.rightMargin + Style.current.padding / 2 onAccepted: root.returnPressed() @@ -201,18 +206,21 @@ ColumnLayout { Layout.fillWidth: true placeholderText: qsTr("New password") echoMode: showPassword ? TextInput.Normal : TextInput.Password - validator: d.validator rightPadding: showHideNewIcon.width + showHideNewIcon.anchors.rightMargin + Style.current.padding / 2 onTextChanged: { // Update password checkers + errorTxt.text = "" + // Update strength indicator: + strengthInditactor.strength = d.convertStrength(root.passwordStrengthScoreFunction(newPswInput.text)) + + if(!d.validateCharacterSet(text)) return + d.containsLower = d.lowerCaseValidator(text) d.containsUpper = d.upperCaseValidator(text) d.containsNumbers = d.numbersValidator(text) d.containsSymbols = d.symbolsValidator(text) - // Update strength indicator: - strengthInditactor.strength = d.convertStrength(root.passwordStrengthScoreFunction(newPswInput.text)) if (text.length === confirmPswInput.text.length) { root.checkPasswordMatches(false) } @@ -302,10 +310,13 @@ ColumnLayout { Layout.fillWidth: true placeholderText: qsTr("Confirm password") echoMode: showPassword ? TextInput.Normal : TextInput.Password - validator: d.validator rightPadding: showHideConfirmIcon.width + showHideConfirmIcon.anchors.rightMargin + Style.current.padding / 2 onTextChanged: { + errorTxt.text = "" + + if(!d.validateCharacterSet(newPswInput.text)) return + d.passwordValidation(); if(text.length === newPswInput.text.length) { root.checkPasswordMatches()