update: ui to set/change password and hint

This commit is contained in:
MrMirDan
2025-12-12 16:32:16 +02:00
parent fca062ba3c
commit a7ae0bc65e
17 changed files with 688 additions and 27 deletions
@@ -311,20 +311,20 @@ void SecureAppSettingsRepository::setFileEncryption(bool enabled)
QString SecureAppSettingsRepository::getPassword() const
{
return m_settings->getPassword();
return value("Sec/password", "").toString();
}
void SecureAppSettingsRepository::setPassword(const QString &pwd)
{
m_settings->setPassword(pwd);
setValue("Sec/password", pwd);
}
QString SecureAppSettingsRepository::getHint() const
{
return m_settings->getHint();
return value("Sec/hint", "").toString();
}
void SecureAppSettingsRepository::setHint(const QString &hint)
{
m_settings->setHint(hint);
setValue("Sec/hint", hint);
}
bool SecureAppSettingsRepository::isAutoConnect() const
+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 12C2 12 5 5 12 5C19 5 22 12 22 12C22 12 19 19 12 19C5 19 2 12 2 12Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 474 B

+6
View File
@@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.88061 9.87891C9.58587 10.1536 9.34946 10.4848 9.18549 10.8528C9.02152 11.2208 8.93336 11.618 8.92625 12.0208C8.91914 12.4236 8.99324 12.8237 9.14412 13.1973C9.29501 13.5708 9.51959 13.9102 9.80446 14.1951C10.0893 14.4799 10.4287 14.7045 10.8022 14.8554C11.1758 15.0063 11.5759 15.0804 11.9787 15.0733C12.3815 15.0662 12.7788 14.978 13.1468 14.814C13.5148 14.6501 13.846 14.4137 14.1206 14.1189" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.7305 5.08C11.1518 5.02751 11.5759 5.00079 12.0005 5C19.0005 5 22.0005 12 22.0005 12C21.5534 12.9571 20.9927 13.8569 20.3305 14.68" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.61 6.60938C4.62125 7.964 3.02987 9.82463 2 11.9994C2 11.9994 5 18.9994 12 18.9994C13.9159 19.0045 15.7908 18.4445 17.39 17.3894" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 2L22 22" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.5 5V3C2.5 2.33696 2.76339 1.70107 3.23223 1.23223C3.70107 0.763392 4.33696 0.5 5 0.5C5.66304 0.5 6.29893 0.763392 6.76777 1.23223C7.23661 1.70107 7.5 2.33696 7.5 3V5M1.5 5H8.5C9.05229 5 9.5 5.44772 9.5 6V9.5C9.5 10.0523 9.05229 10.5 8.5 10.5H1.5C0.947715 10.5 0.5 10.0523 0.5 9.5V6C0.5 5.44772 0.947715 5 1.5 5Z" stroke="#EB5757" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 494 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.5 5.00252V3.00252C2.49938 2.38254 2.72914 1.78445 3.14469 1.32435C3.56023 0.864252 4.13191 0.574969 4.74875 0.512663C5.36559 0.450356 5.98357 0.61947 6.48274 0.987175C6.9819 1.35488 7.32663 1.89494 7.45 2.50252M1.5 5.00252H8.5C9.05229 5.00252 9.5 5.45023 9.5 6.00252V9.50252C9.5 10.0548 9.05229 10.5025 8.5 10.5025H1.5C0.947715 10.5025 0.5 10.0548 0.5 9.50252V6.00252C0.5 5.45023 0.947715 5.00252 1.5 5.00252Z" stroke="#5CAEE7" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 592 B

+4
View File
@@ -21,6 +21,8 @@
<file>controls/delete.svg</file>
<file>controls/download.svg</file>
<file>controls/edit-3.svg</file>
<file>controls/eye-off-new.svg</file>
<file>controls/eye-new.svg</file>
<file>controls/eye-off.svg</file>
<file>controls/eye.svg</file>
<file>controls/external-link.svg</file>
@@ -36,6 +38,8 @@
<file>controls/home.svg</file>
<file>controls/infinity.svg</file>
<file>controls/info.svg</file>
<file>controls/lock-locked.svg</file>
<file>controls/lock-unlocked.svg</file>
<file>controls/mail.svg</file>
<file>controls/map-pin.svg</file>
<file>controls/more-vertical.svg</file>
-20
View File
@@ -316,26 +316,6 @@ void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data)
}
}
void SecureQSettings::setPassword(const QString &pwd)
{
m_password = pwd;
}
void SecureQSettings::setHint(const QString &hint)
{
m_hint = hint;
}
QString SecureQSettings::getPassword() const
{
return m_password;
}
QString SecureQSettings::getHint() const
{
return m_hint;
}
static QString opensslErrString()
{
unsigned long e = ERR_get_error();
-3
View File
@@ -57,9 +57,6 @@ private:
"Conf/", "Servers/",
};
mutable QString m_password;
mutable QString m_hint;
mutable QByteArray m_key;
mutable QByteArray m_iv;
@@ -37,6 +37,7 @@ namespace PageLoader
PageSettingsSplitTunneling,
PageSettingsAppSplitTunneling,
PageSettingsKillSwitch,
PageSettingsAppEncryption,
PageSettingsAppPassword,
PageSettingsAppPasswordConfirm,
PageSettingsApiServerInfo,
@@ -148,6 +148,7 @@ signals:
void startMinimizedChanged();
void fileEncryptionStateChanged();
void changingPassword();
private:
SettingsController* m_settingsController;
@@ -0,0 +1,66 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
Rectangle {
id: root
property string textColor: AmneziaStyle.color.paleGray
property string textString
property int textFormat: Text.PlainText
property string iconPath
property real iconWidth: 16
property real iconHeight: 16
color: AmneziaStyle.color.onyxBlack
radius: 32
implicitHeight: iconHeight + 8
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
RowLayout {
id: content
width: parent.width
anchors.fill: parent
anchors.leftMargin: content.width / 4
anchors.rightMargin: content.width / 4
anchors.topMargin: 4
anchors.bottomMargin: 4
spacing: 0
Image {
Layout.alignment: Qt.AlignTop
width: iconWidth
height: iconHeight
source: iconPath
}
CaptionTextType {
id: supportingText
Layout.fillWidth: true
Layout.leftMargin: 8
text: textString
textFormat: root.textFormat
color: textColor
}
}
}
@@ -0,0 +1,89 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
id: root
objectName: "passwordDrawer"
property var securedFunc
expandedStateContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
anchors.leftMargin: 16
anchors.rightMargin: 16
Header2TextType {
Layout.fillWidth: true
Layout.bottomMargin: 8
text: qsTr("Enter password to continue")
}
TextFieldWithHeaderType {
id: passwordField
property bool hideContent: true
Layout.fillWidth: true
headerText: qsTr("Password")
textField.echoMode: hideContent ? TextInput.Password : TextInput.Normal
textField.text: textField.text
rightButtonClickedOnEnter: true
clickedFunc: function () {
hideContent = !hideContent
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
}
textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
}
textField.onTextChanged: {
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
}
}
LabelTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.bottomMargin: 16
text: SettingsController.getHint()
}
BasicButtonType {
id: doneButton
Layout.fillWidth: true
text: qsTr("Done")
clickedFunc: function() {
if (passwordField.textField.text !== SettingsController.getPassword()) {
passwordField.errorText = qsTr("Incorrect password")
return
}
if (root.securedFunc && typeof root.securedFunc === "function") {
root.securedFunc()
}
root.closeTriggered()
}
}
}
}
@@ -0,0 +1,148 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
import "../Controls2/TextTypes"
import "../Components"
PageType {
id: root
property bool isChangingPassword: false
Connections {
target: SettingsController
function onFileEncryptionStateChanged() {
PageController.showBusyIndicator(true)
PageController.closePage()
PageController.goToPage(PageEnum.PageSettingsAppEncryption)
PageController.showBusyIndicator(false)
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
backButtonFunction: function() {
PageController.closePage()
if (root.isChangingPassword) {
root.isChangingPassword = false
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout {
width: listView.width
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("File encryption")
descriptionText: qsTr("For encrypting backups, configuration files, subscription keys, and logs")
}
EncryptionIndicator {
id: indicator
textString: SettingsController.isFileEncryptionEnabled() ? qsTr("Password set. Encryption on") : qsTr("Password not set. Encryption off")
iconPath: SettingsController.isFileEncryptionEnabled() ? "qrc:/images/controls/lock-locked.svg" : "qrc:/images/controls/lock-unlocked.svg"
}
BasicButtonType {
id: switchEncryptionButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: SettingsController.isFileEncryptionEnabled() ? qsTr("Turn off encryption") : qsTr("Turn on encryption")
clickedFunc: function() {
SettingsController.isFileEncryptionEnabled() ? SettingsController.toggleFileEncryption(false)
: SettingsController.toggleFileEncryption(true)
}
}
BasicButtonType {
id: changePasswordButton
hoveredColor: AmneziaStyle.color.slateGray
defaultColor: AmneziaStyle.color.midnightBlack
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Change password")
signal changingPassword
clickedFunc: function() {
passwordDrawer.openTriggered()
}
}
PasswordDrawer {
id: passwordDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.45
securedFunc: function() {
root.isChangingPassword = true
PageController.showBusyIndicator(true)
PageController.closePage()
PageController.goToPage(PageEnum.PageSettingsAppPassword)
PageController.showBusyIndicator(false)
SettingsController.changingPassword()
}
}
}
spacing: 16
footer: ColumnLayout {
width: listView.width
// TODO: add text
}
}
}
@@ -0,0 +1,189 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
import "../Controls2/TextTypes"
import "../Components"
PageType {
id: root
property bool isChangingPassword: false
Connections {
target: SettingsController
function onChangingPassword() {
root.isChangingPassword = true
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
backButtonFunction: function() {
PageController.closePage()
if (root.isChangingPassword) {
root.isChangingPassword = false
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout {
width: listView.width
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: root.isChangingPassword ? qsTr("Password changing") : qsTr("File encryption")
descriptionText: root.isChangingPassword ? qsTr("Files encrypted with old password will stay encrypted with old password")
: qsTr("For encrypting backups, configuration files, subscription keys, and logs")
}
}
model: inputFields
spacing: 16
delegate: ColumnLayout {
width: listView.width
TextFieldWithHeaderType {
id: delegate
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: title
textField.echoMode: hideContent ? TextInput.Password : TextInput.Normal
textField.placeholderText: placeholderContent
textField.text: textField.text
rightButtonClickedOnEnter: true
clickedFunc: function () {
clickedHandler()
buttonImageSource = textField.text !== "" ? imageSource : ""
}
textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
}
textField.onTextChanged: {
buttonImageSource = textField.text !== "" ? imageSource : ""
}
}
}
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: continueButton
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Continue")
clickedFunc: function() {
if (!root.isPasswordProperlyFilled()) {
return
}
var _password = listView.itemAtIndex(vars.passwordIndex).children[0].textField.text
var _hint = listView.itemAtIndex(vars.hintIndex).children[0].textField.text
SettingsController.setPassword(_password)
SettingsController.setHint(_hint)
PageController.goToPage(PageEnum.PageSettingsAppPasswordConfirm)
if (root.isChangingPassword) {
SettingsController.changingPassword()
}
}
}
}
}
function isPasswordProperlyFilled() {
var tooShort = false
var secretDataItem = listView.itemAtIndex(vars.passwordIndex).children[0]
if (secretDataItem.textField.text === "") {
secretDataItem.errorText = qsTr("Password cannot be empty")
tooShort = true
} else if (secretDataItem.textField.text.length < 4) {
secretDataItem.errorText = qsTr("Password too short")
tooShort = true
}
return !tooShort
}
property list<QtObject> inputFields: [
passwordObject,
hintObject
]
QtObject {
id: passwordObject
property string title: root.isChangingPassword ? qsTr("New password") : qsTr("Set encryption password")
readonly property string placeholderContent: ""
property string imageSource: "qrc:/images/controls/eye-new.svg"
property bool hideContent: true
readonly property var clickedHandler: function() {
hideContent = !hideContent
imageSource = hideContent ? "qrc:/images/controls/eye-new.svg" : "qrc:/images/controls/eye-off-new.svg"
}
}
QtObject {
id: hintObject
property string title: qsTr("Hint for password")
readonly property string placeholderContent: ""
property string imageSource: ""
property bool hideContent: false
readonly property var clickedHandler: undefined
}
QtObject {
id: vars
readonly property int passwordIndex: 0
readonly property int hintIndex: 1
}
}
@@ -0,0 +1,148 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
import "../Controls2/TextTypes"
PageType {
id: root
property bool isChangingPassword: false
Connections {
target: SettingsController
function onChangingPassword() {
root.isChangingPassword = true
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout {
width: listView.width
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: root.isChangingPassword ? qsTr("Confirm new password") : qsTr("Confirm password")
descriptionText: qsTr("If you forget your password, you'll have to reset all app settings to reset it."
+ " Encrypted files will remain encrypted")
}
}
model: 1 // fake model
spacing: 16
delegate: ColumnLayout {
width: listView.width
TextFieldWithHeaderType {
id: delegate
property bool hideContent: true
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: root.isChangingPassword ? qsTr("Enter new password one more time") : qsTr("Enter password one more time")
textField.echoMode: hideContent ? TextInput.Password : TextInput.Normal
textField.placeholderText: ""
textField.text: textField.text
rightButtonClickedOnEnter: true
clickedFunc: function () {
hideContent = !hideContent
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
}
textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
}
textField.onTextChanged: {
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
}
}
}
footer: ColumnLayout {
width: listView.width
LabelTextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 24
text: SettingsController.getHint()
}
BasicButtonType {
id: continueButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: root.isChangingPassword ? qsTr("Save new password") : qsTr("Turn on encryption")
clickedFunc: function() {
if (!root.isPasswordProperlyFilled()) {
return
}
PageController.closePage()
PageController.goToPage(PageEnum.PageSettings)
PageController.goToPage(PageEnum.PageSettingsAppEncryption)
SettingsController.toggleFileEncryption(true)
}
}
}
}
function isPasswordProperlyFilled() {
var notMatch = false
var secretDataItem = listView.itemAtIndex(0).children[0]
if (secretDataItem.textField.text !== SettingsController.getPassword()) {
secretDataItem.errorText = qsTr("Passwords not match")
notMatch = true
}
return !notMatch
}
}
@@ -213,6 +213,23 @@ PageType {
DividerType {}
LabelWithButtonType {
id: labelWithButtonAppPassword
Layout.fillWidth: true
text: qsTr("File encryption")
descriptionText: qsTr("For encrypting backups, configuration files, subscription keys, and logs")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
SettingsController.getPassword() === "" ? PageController.goToPage(PageEnum.PageSettingsAppPassword)
: PageController.goToPage(PageEnum.PageSettingsAppEncryption)
}
}
DividerType {}
LabelWithButtonType {
id: labelWithButtonLogging
+5
View File
@@ -7,11 +7,13 @@
<file>Components/HomeContainersListView.qml</file>
<file>Components/HomeSplitTunnelingDrawer.qml</file>
<file>Components/InstalledAppsDrawer.qml</file>
<file>Components/PasswordDrawer.qml</file>
<file>Components/ChangelogDrawer.qml</file>
<file>Components/QuestionDrawer.qml</file>
<file>Components/SelectLanguageDrawer.qml</file>
<file>Components/ServersListView.qml</file>
<file>Components/SettingsContainersListView.qml</file>
<file>Components/EncryptionIndicator.qml</file>
<file>Components/BenefitRow.qml</file>
<file>Components/BenefitsPanel.qml</file>
<file>Components/SubscriptionExpiredDrawer.qml</file>
@@ -87,6 +89,9 @@
<file>Pages2/PageSettingsApiServerInfo.qml</file>
<file>Pages2/PageSettingsApplication.qml</file>
<file>Pages2/PageSettingsAppSplitTunneling.qml</file>
<file>Pages2/PageSettingsAppEncryption.qml</file>
<file>Pages2/PageSettingsAppPassword.qml</file>
<file>Pages2/PageSettingsAppPasswordConfirm.qml</file>
<file>Pages2/PageSettingsBackup.qml</file>
<file>Pages2/PageSettingsConnection.qml</file>
<file>Pages2/PageSettingsDns.qml</file>