mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 00:12:09 +03:00
Merge branch '51' into nfc-fixes
This commit is contained in:
commit
3a86564f02
@ -38,7 +38,7 @@ Pane {
|
||||
|
||||
NoCredentialsSection {
|
||||
id: noCredentialsSection
|
||||
visible: entries.count === 0 && (!!yubiKey.currentDevice)
|
||||
visible: entries.count === 0 && (!!yubiKey.currentDevice) && yubiKey.currentDeviceValidated
|
||||
enabled: visible
|
||||
Accessible.ignored: true
|
||||
}
|
||||
|
56
qml/SettingsPanelAppearance.qml
Normal file
56
qml/SettingsPanelAppearance.qml
Normal file
@ -0,0 +1,56 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Appearance")
|
||||
description: qsTr("Change the visual appearance of the application.")
|
||||
metadata: "dark light mode theme"
|
||||
isTopPanel: true
|
||||
|
||||
ListModel {
|
||||
id: themes
|
||||
|
||||
ListElement {
|
||||
text: qsTr("System default")
|
||||
value: Material.System
|
||||
}
|
||||
ListElement {
|
||||
text: qsTr("Light mode")
|
||||
value: Material.Light
|
||||
}
|
||||
ListElement {
|
||||
text: qsTr("Dark mode")
|
||||
value: Material.Dark
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
StyledComboBox {
|
||||
id: themeComboBox
|
||||
label: qsTr("Theme")
|
||||
comboBox.textRole: "text"
|
||||
model: themes
|
||||
onCurrentIndexChanged: {
|
||||
settings.theme = themes.get(currentIndex).value
|
||||
}
|
||||
currentIndex: {
|
||||
switch (settings.theme) {
|
||||
case Material.System:
|
||||
return 0
|
||||
case Material.Light:
|
||||
return 1
|
||||
case Material.Dark:
|
||||
return 2
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
qml/SettingsPanelClearPasswords.qml
Normal file
26
qml/SettingsPanelClearPasswords.qml
Normal file
@ -0,0 +1,26 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Clear passwords")
|
||||
description: qsTr("Delete all saved passwords.")
|
||||
isEnabled: false
|
||||
isBottomPanel: true
|
||||
toolButtonIcon: "../images/delete.svg"
|
||||
toolButtonToolTip: qsTr("Clear")
|
||||
toolButton.onClicked: navigator.confirm({
|
||||
"heading": qsTr("Clear passwords?"),
|
||||
"message": qsTr("This will delete all saved passwords."),
|
||||
"description": qsTr("A password prompt will appear the next time a YubiKey with a password is used."),
|
||||
"buttonAccept": qsTr("Clear passwords"),
|
||||
"acceptedCb": function() {
|
||||
yubiKey.clearLocalPasswords(function (resp) {
|
||||
if (resp.success) {
|
||||
navigator.snackBar(qsTr("Passwords cleared"))
|
||||
}
|
||||
})}
|
||||
})
|
||||
}
|
84
qml/SettingsPanelCurrentDevice.qml
Normal file
84
qml/SettingsPanelCurrentDevice.qml
Normal file
@ -0,0 +1,84 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
StyledExpansionPanel {
|
||||
id: currentDevicePanel
|
||||
label: getDeviceLabel(yubiKey.currentDevice)
|
||||
description: getDeviceDescription(yubiKey.currentDevice)
|
||||
keyImage: !!yubiKey.currentDevice ? yubiKey.getCurrentDeviceImage() : "../images/yubikeys-large-transparent"
|
||||
isTopPanel: true
|
||||
isEnabled: yubiKey.availableDevices.length > 1
|
||||
|
||||
function getDeviceLabel(device) {
|
||||
if (!!device) {
|
||||
return ("%1").arg(device.name)
|
||||
} else {
|
||||
return qsTr("Insert your YubiKey")
|
||||
}
|
||||
}
|
||||
|
||||
function getDeviceDescription(device) {
|
||||
if (!!device) {
|
||||
return qsTr("Serial number: %1").arg(!!device.serial ? device.serial : "Not available")
|
||||
} else {
|
||||
return qsTr("No device found")
|
||||
}
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: deviceButtonGroup
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Repeater {
|
||||
model: yubiKey.availableDevices
|
||||
onModelChanged: {
|
||||
if (yubiKey.availableDevices.length < 2) {
|
||||
currentDevicePanel.isExpanded = false
|
||||
}
|
||||
}
|
||||
StyledRadioButton {
|
||||
Layout.fillWidth: true
|
||||
objectName: index
|
||||
checked: !!yubiKey.currentDevice
|
||||
&& modelData.serial === yubiKey.currentDevice.serial
|
||||
text: getDeviceLabel(modelData)
|
||||
description: getDeviceDescription(modelData)
|
||||
buttonGroup: deviceButtonGroup
|
||||
}
|
||||
}
|
||||
|
||||
StyledButton {
|
||||
id: selectBtn
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
text: "Select"
|
||||
enabled: {
|
||||
if (!!yubiKey.availableDevices && !!deviceButtonGroup.checkedButton) {
|
||||
var dev = yubiKey.availableDevices[deviceButtonGroup.checkedButton.objectName]
|
||||
return dev !== yubiKey.currentDevice
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
yubiKey.refreshDevicesDefault()
|
||||
var dev = yubiKey.availableDevices[deviceButtonGroup.checkedButton.objectName]
|
||||
yubiKey.selectCurrentSerial(dev.serial,
|
||||
function (resp) {
|
||||
if (resp.success) {
|
||||
entries.clear()
|
||||
yubiKey.currentDevice = dev
|
||||
currentDevicePanel.expandAction()
|
||||
} else {
|
||||
console.log("select device failed", resp.error_id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
85
qml/SettingsPanelCustomReader.qml
Normal file
85
qml/SettingsPanelCustomReader.qml
Normal file
@ -0,0 +1,85 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
StyledExpansionPanel {
|
||||
id: customReaderPanel
|
||||
label: qsTr("Custom reader")
|
||||
description: qsTr("Specify a custom reader for YubiKey.")
|
||||
metadata: "ccid otp slot custom readers nfc"
|
||||
|
||||
property bool aboutToChange: customReaderCheckbox.checked !== settings.useCustomReader
|
||||
|| readerFilter.text !== settings.customReaderName && readerFilter.text.length > 0
|
||||
|
||||
function isValidMode() {
|
||||
return aboutToChange
|
||||
}
|
||||
|
||||
function setInterface() {
|
||||
settings.useCustomReader = customReaderCheckbox.checked
|
||||
settings.customReaderName = readerFilter.text
|
||||
yubiKey.clearCurrentDeviceAndEntries()
|
||||
yubiKey.refreshDevicesDefault()
|
||||
navigator.goToSettings()
|
||||
navigator.snackBar(qsTr("Interface changed"))
|
||||
isExpanded = false
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
StyledCheckBox {
|
||||
id: customReaderCheckbox
|
||||
checked: settings.useCustomReader
|
||||
text: qsTr("Enable custom reader")
|
||||
description: qsTr("Useful when using a NFC reader.")
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: 16
|
||||
visible: customReaderCheckbox.checked
|
||||
|
||||
RowLayout {
|
||||
visible: yubiKey.availableReaders.length > 0
|
||||
StyledComboBox {
|
||||
id: connectedReaders
|
||||
enabled: yubiKey.availableReaders.length > 0
|
||||
visible: yubiKey.availableReaders.length > 0
|
||||
label: qsTr("Connected readers")
|
||||
model: yubiKey.availableReaders
|
||||
}
|
||||
StyledButton {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
text: qsTr("Use as filter")
|
||||
flat: true
|
||||
enabled: yubiKey.availableReaders.length > 0
|
||||
visible: yubiKey.availableReaders.length > 0
|
||||
onClicked: readerFilter.text = connectedReaders.currentText
|
||||
}
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: readerFilter
|
||||
labelText: qsTr("Custom reader filter")
|
||||
text: settings.customReaderName
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.topMargin: 16
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
StyledButton {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
text: "Apply"
|
||||
enabled: isValidMode()
|
||||
onClicked: setInterface()
|
||||
}
|
||||
}
|
||||
}
|
150
qml/SettingsPanelPasswordMgmt.qml
Normal file
150
qml/SettingsPanelPasswordMgmt.qml
Normal file
@ -0,0 +1,150 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
StyledExpansionPanel {
|
||||
id: passwordManagementPanel
|
||||
label: qsTr("Manage password")
|
||||
description: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword ? "Password is set" : "Password is not set"
|
||||
isVisible: yubiKey.currentDeviceOathEnabled
|
||||
isTopPanel: true
|
||||
|
||||
function clearPasswordFields() {
|
||||
currentPasswordField.text = ""
|
||||
newPasswordField.text = ""
|
||||
confirmPasswordField.text = ""
|
||||
}
|
||||
|
||||
function submitPassword() {
|
||||
if (acceptableInput()) {
|
||||
if (!!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword) {
|
||||
changePassword()
|
||||
} else {
|
||||
setPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function acceptableInput() {
|
||||
if (!!yubiKey.currentDevice && yubiKey.currentDeviceValidated) {
|
||||
if (!!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword
|
||||
&& currentPasswordField.text.length == 0) {
|
||||
return false
|
||||
}
|
||||
if (newPasswordField.text.length > 0
|
||||
&& (newPasswordField.text === confirmPasswordField.text)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function changePassword() {
|
||||
navigator.goToLoading()
|
||||
yubiKey.validate(currentPasswordField.text, false, function (resp) {
|
||||
if (resp.success) {
|
||||
setPassword()
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("change password failed:", resp.error_id)
|
||||
}
|
||||
clearPasswordFields()
|
||||
navigator.goToSettings()
|
||||
})
|
||||
}
|
||||
|
||||
function setPassword() {
|
||||
navigator.goToLoading()
|
||||
yubiKey.setPassword(newPasswordField.text, false, function (resp) {
|
||||
if (resp.success) {
|
||||
navigator.snackBar(qsTr("Password set"))
|
||||
yubiKey.currentDevice.hasPassword = true
|
||||
passwordManagementPanel.isExpanded = false
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("set password failed:", resp.error_id)
|
||||
}
|
||||
clearPasswordFields()
|
||||
navigator.goToSettings()
|
||||
})
|
||||
}
|
||||
|
||||
function removePassword() {
|
||||
navigator.goToLoading()
|
||||
yubiKey.validate(currentPasswordField.text, false, function (resp) {
|
||||
if (resp.success) {
|
||||
yubiKey.removePassword(function (resp) {
|
||||
if (resp.success) {
|
||||
navigator.snackBar(qsTr("Password removed"))
|
||||
yubiKey.currentDevice.hasPassword = false
|
||||
passwordManagementPanel.isExpanded = false
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("remove password failed:", resp.error_id)
|
||||
}
|
||||
clearPasswordFields()
|
||||
navigator.goToSettings()
|
||||
})
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("remove password failed:", resp.error_id)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
StyledTextField {
|
||||
id: currentPasswordField
|
||||
visible: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword
|
||||
labelText: qsTr("Current password")
|
||||
echoMode: TextInput.Password
|
||||
Keys.onEnterPressed: submitPassword()
|
||||
Keys.onReturnPressed: submitPassword()
|
||||
onSubmit: submitPassword()
|
||||
}
|
||||
StyledTextField {
|
||||
id: newPasswordField
|
||||
labelText: qsTr("New password")
|
||||
echoMode: TextInput.Password
|
||||
Keys.onEnterPressed: submitPassword()
|
||||
Keys.onReturnPressed: submitPassword()
|
||||
onSubmit: submitPassword()
|
||||
}
|
||||
StyledTextField {
|
||||
id: confirmPasswordField
|
||||
labelText: qsTr("Confirm password")
|
||||
echoMode: TextInput.Password
|
||||
Keys.onEnterPressed: submitPassword()
|
||||
Keys.onReturnPressed: submitPassword()
|
||||
onSubmit: submitPassword()
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
StyledButton {
|
||||
id: removePasswordBtn
|
||||
visible: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword
|
||||
enabled: currentPasswordField.text.length > 0
|
||||
text: "Remove"
|
||||
onClicked: navigator.confirm({
|
||||
"heading": qsTr("Remove password?"),
|
||||
"description": qsTr("A password will not be required to access the accounts anymore."),
|
||||
"warning": false,
|
||||
"buttonAccept": qsTr("Remove password"),
|
||||
"acceptedCb": function () {
|
||||
removePassword()
|
||||
}
|
||||
})
|
||||
}
|
||||
StyledButton {
|
||||
id: applyPassword
|
||||
text: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword ? "Change" : "Set"
|
||||
enabled: acceptableInput()
|
||||
onClicked: submitPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
qml/SettingsPanelResetDevice.qml
Normal file
41
qml/SettingsPanelResetDevice.qml
Normal file
@ -0,0 +1,41 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Reset")
|
||||
description: qsTr("Warning: Reset will delete all accounts and restore factory defaults.")
|
||||
isEnabled: false
|
||||
isVisible: yubiKey.currentDeviceOathEnabled
|
||||
|
||||
toolButtonIcon: "../images/reset.svg"
|
||||
toolButtonToolTip: qsTr("Reset device")
|
||||
toolButton.onClicked: navigator.confirm({
|
||||
"heading": qsTr("Reset device?"),
|
||||
"message": qsTr("This will delete all accounts and restore factory defaults of your YubiKey."),
|
||||
"description": qsTr("Before proceeding:<ul style=\"-qt-list-indent: 1;\"><li>There is NO going back after a factory reset.<li>If you do not know what you are doing, do NOT do this.</ul>"),
|
||||
"buttonAccept": qsTr("Reset device"),
|
||||
"acceptedCb": function () {
|
||||
navigator.goToLoading()
|
||||
yubiKey.reset(function (resp) {
|
||||
if (resp.success) {
|
||||
entries.clear()
|
||||
navigator.snackBar(
|
||||
qsTr("Reset completed"))
|
||||
yubiKey.currentDeviceValidated = true
|
||||
yubiKey.currentDevice.hasPassword = false
|
||||
|
||||
} else {
|
||||
navigator.snackBarError(
|
||||
navigator.getErrorMessage(
|
||||
resp.error_id))
|
||||
console.log("reset failed:",
|
||||
resp.error_id)
|
||||
}
|
||||
navigator.goToSettings()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
38
qml/SettingsPanelSystemTray.qml
Normal file
38
qml/SettingsPanelSystemTray.qml
Normal file
@ -0,0 +1,38 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: Qt.platform.os === "osx" ? "Menu bar" : "System tray"
|
||||
description: qsTr("Configure where and how the application is visible.")
|
||||
|
||||
ColumnLayout {
|
||||
CheckBox {
|
||||
id: sysTrayCheckbox
|
||||
checked: settings.closeToTray
|
||||
text: Qt.platform.os === "osx" ? qsTr("Show in menu bar") : qsTr("Show in system tray")
|
||||
padding: 0
|
||||
indicator.width: 16
|
||||
indicator.height: 16
|
||||
onCheckStateChanged: {
|
||||
if(!checked) {
|
||||
hideOnLaunchCheckbox.checked = false
|
||||
}
|
||||
settings.closeToTray = checked
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: hideOnLaunchCheckbox
|
||||
enabled: sysTrayCheckbox.checked
|
||||
checked: settings.hideOnLaunch
|
||||
text: qsTr("Hide on launch")
|
||||
padding: 0
|
||||
indicator.width: 16
|
||||
indicator.height: 16
|
||||
onCheckStateChanged: settings.hideOnLaunch = checked
|
||||
}
|
||||
}
|
||||
}
|
@ -23,476 +23,35 @@ Flickable {
|
||||
|
||||
Keys.onEscapePressed: navigator.home()
|
||||
|
||||
function getDeviceLabel(device) {
|
||||
if (!!device) {
|
||||
return ("%1").arg(device.name)
|
||||
} else {
|
||||
return qsTr("Insert your YubiKey")
|
||||
}
|
||||
}
|
||||
|
||||
function getDeviceDescription(device) {
|
||||
if (!!device) {
|
||||
return qsTr("Serial number: %1").arg(!!device.serial ? device.serial : "Not available")
|
||||
} else {
|
||||
return qsTr("No device found")
|
||||
}
|
||||
}
|
||||
|
||||
function clearPasswordFields() {
|
||||
currentPasswordField.text = ""
|
||||
newPasswordField.text = ""
|
||||
confirmPasswordField.text = ""
|
||||
}
|
||||
|
||||
function submitPassword() {
|
||||
if (acceptableInput()) {
|
||||
if (!!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword) {
|
||||
changePassword()
|
||||
} else {
|
||||
setPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function acceptableInput() {
|
||||
if (!!yubiKey.currentDevice && yubiKey.currentDeviceValidated) {
|
||||
if (!!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword
|
||||
&& currentPasswordField.text.length == 0) {
|
||||
return false
|
||||
}
|
||||
if (newPasswordField.text.length > 0
|
||||
&& (newPasswordField.text === confirmPasswordField.text)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function changePassword() {
|
||||
navigator.goToLoading()
|
||||
yubiKey.validate(currentPasswordField.text, false, function (resp) {
|
||||
if (resp.success) {
|
||||
setPassword()
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("change password failed:", resp.error_id)
|
||||
}
|
||||
clearPasswordFields()
|
||||
navigator.goToSettings()
|
||||
})
|
||||
}
|
||||
|
||||
function setPassword() {
|
||||
navigator.goToLoading()
|
||||
yubiKey.setPassword(newPasswordField.text, false, function (resp) {
|
||||
if (resp.success) {
|
||||
navigator.snackBar(qsTr("Password set"))
|
||||
yubiKey.currentDevice.hasPassword = true
|
||||
passwordManagementPanel.isExpanded = false
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("set password failed:", resp.error_id)
|
||||
}
|
||||
clearPasswordFields()
|
||||
navigator.goToSettings()
|
||||
})
|
||||
}
|
||||
|
||||
function removePassword() {
|
||||
navigator.goToLoading()
|
||||
yubiKey.validate(currentPasswordField.text, false, function (resp) {
|
||||
if (resp.success) {
|
||||
yubiKey.removePassword(function (resp) {
|
||||
if (resp.success) {
|
||||
navigator.snackBar(qsTr("Password removed"))
|
||||
yubiKey.currentDevice.hasPassword = false
|
||||
passwordManagementPanel.isExpanded = false
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("remove password failed:", resp.error_id)
|
||||
}
|
||||
clearPasswordFields()
|
||||
navigator.goToSettings()
|
||||
})
|
||||
} else {
|
||||
navigator.snackBarError(getErrorMessage(resp.error_id))
|
||||
console.log("remove password failed:", resp.error_id)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
property string title: qsTr("")
|
||||
|
||||
ListModel {
|
||||
id: themes
|
||||
|
||||
ListElement {
|
||||
text: qsTr("System default")
|
||||
value: Material.System
|
||||
}
|
||||
ListElement {
|
||||
text: qsTr("Light mode")
|
||||
value: Material.Light
|
||||
}
|
||||
ListElement {
|
||||
text: qsTr("Dark mode")
|
||||
value: Material.Dark
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: 0
|
||||
|
||||
StyledExpansionContainer {
|
||||
id: keyPane
|
||||
sectionTitle: qsTr("Device")
|
||||
|
||||
StyledExpansionPanel {
|
||||
id: currentDevicePanel
|
||||
label: getDeviceLabel(yubiKey.currentDevice)
|
||||
description: getDeviceDescription(yubiKey.currentDevice)
|
||||
keyImage: !!yubiKey.currentDevice ? yubiKey.getCurrentDeviceImage() : "../images/yubikeys-large-transparent"
|
||||
isTopPanel: true
|
||||
Layout.fillWidth: true
|
||||
isEnabled: yubiKey.availableDevices.length > 1
|
||||
|
||||
ButtonGroup {
|
||||
id: deviceButtonGroup
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Repeater {
|
||||
model: yubiKey.availableDevices
|
||||
onModelChanged: {
|
||||
if (yubiKey.availableDevices.length < 2) {
|
||||
currentDevicePanel.isExpanded = false
|
||||
}
|
||||
}
|
||||
StyledRadioButton {
|
||||
Layout.fillWidth: true
|
||||
objectName: index
|
||||
checked: !!yubiKey.currentDevice
|
||||
&& modelData.serial === yubiKey.currentDevice.serial
|
||||
text: getDeviceLabel(modelData)
|
||||
description: getDeviceDescription(modelData)
|
||||
buttonGroup: deviceButtonGroup
|
||||
}
|
||||
}
|
||||
|
||||
StyledButton {
|
||||
id: selectBtn
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
text: "Select"
|
||||
enabled: {
|
||||
if (!!yubiKey.availableDevices && !!deviceButtonGroup.checkedButton) {
|
||||
var dev = yubiKey.availableDevices[deviceButtonGroup.checkedButton.objectName]
|
||||
return dev !== yubiKey.currentDevice
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
yubiKey.refreshDevicesDefault()
|
||||
var dev = yubiKey.availableDevices[deviceButtonGroup.checkedButton.objectName]
|
||||
yubiKey.selectCurrentSerial(dev.serial,
|
||||
function (resp) {
|
||||
if (resp.success) {
|
||||
entries.clear()
|
||||
yubiKey.currentDevice = dev
|
||||
currentDevicePanel.expandAction()
|
||||
} else {
|
||||
console.log("select device failed", resp.error_id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledExpansionPanel {
|
||||
id: passwordManagementPanel
|
||||
label: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword ? qsTr("Change password") : qsTr("Set password")
|
||||
description: qsTr("For additional security the YubiKey may be protected with a password.")
|
||||
isVisible: yubiKey.currentDeviceOathEnabled()
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
StyledTextField {
|
||||
id: currentPasswordField
|
||||
visible: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword
|
||||
labelText: qsTr("Current password")
|
||||
echoMode: TextInput.Password
|
||||
Keys.onEnterPressed: submitPassword()
|
||||
Keys.onReturnPressed: submitPassword()
|
||||
onSubmit: submitPassword()
|
||||
}
|
||||
StyledTextField {
|
||||
id: newPasswordField
|
||||
labelText: qsTr("New password")
|
||||
echoMode: TextInput.Password
|
||||
Keys.onEnterPressed: submitPassword()
|
||||
Keys.onReturnPressed: submitPassword()
|
||||
onSubmit: submitPassword()
|
||||
}
|
||||
StyledTextField {
|
||||
id: confirmPasswordField
|
||||
labelText: qsTr("Confirm password")
|
||||
echoMode: TextInput.Password
|
||||
Keys.onEnterPressed: submitPassword()
|
||||
Keys.onReturnPressed: submitPassword()
|
||||
onSubmit: submitPassword()
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
StyledButton {
|
||||
id: removePasswordBtn
|
||||
visible: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword
|
||||
enabled: currentPasswordField.text.length > 0
|
||||
text: "Remove"
|
||||
onClicked: navigator.confirm({
|
||||
"heading": qsTr("Remove password?"),
|
||||
"description": qsTr("A password will not be required to access the accounts anymore."),
|
||||
"warning": false,
|
||||
"buttonAccept": qsTr("Remove password"),
|
||||
"acceptedCb": function () {
|
||||
removePassword()
|
||||
}
|
||||
})
|
||||
}
|
||||
StyledButton {
|
||||
id: applyPassword
|
||||
text: !!yubiKey.currentDevice && yubiKey.currentDevice.hasPassword ? "Change" : "Set"
|
||||
enabled: acceptableInput()
|
||||
onClicked: submitPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Reset")
|
||||
description: qsTr("Warning: Reset will delete all accounts and restore factory defaults.")
|
||||
isEnabled: false
|
||||
isVisible: yubiKey.currentDeviceOathEnabled()
|
||||
|
||||
toolButtonIcon: "../images/reset.svg"
|
||||
toolButtonToolTip: qsTr("Reset device")
|
||||
toolButton.onClicked: navigator.confirm({
|
||||
"heading": qsTr("Reset device?"),
|
||||
"message": qsTr("This will delete all accounts and restore factory defaults of your YubiKey."),
|
||||
"description": qsTr("Before proceeding:<ul style=\"-qt-list-indent: 1;\"><li>There is NO going back after a factory reset.<li>If you do not know what you are doing, do NOT do this.</ul>"),
|
||||
"buttonAccept": qsTr("Reset device"),
|
||||
"acceptedCb": function () {
|
||||
navigator.goToLoading()
|
||||
yubiKey.reset(function (resp) {
|
||||
if (resp.success) {
|
||||
entries.clear()
|
||||
navigator.snackBar(
|
||||
qsTr("Reset completed"))
|
||||
yubiKey.currentDeviceValidated = true
|
||||
yubiKey.currentDevice.hasPassword = false
|
||||
|
||||
} else {
|
||||
navigator.snackBarError(
|
||||
navigator.getErrorMessage(
|
||||
resp.error_id))
|
||||
console.log("reset failed:",
|
||||
resp.error_id)
|
||||
}
|
||||
navigator.goToSettings()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
title: qsTr("Device")
|
||||
|
||||
SettingsPanelCurrentDevice {}
|
||||
}
|
||||
|
||||
StyledExpansionContainer {
|
||||
id: appPane
|
||||
sectionTitle: qsTr("Application")
|
||||
title: qsTr("Application")
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Appearance")
|
||||
description: qsTr("Change the visual appearance of the application.")
|
||||
metadata: "dark light mode theme"
|
||||
isTopPanel: true
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
StyledComboBox {
|
||||
id: themeComboBox
|
||||
label: qsTr("Theme")
|
||||
comboBox.textRole: "text"
|
||||
model: themes
|
||||
onCurrentIndexChanged: {
|
||||
settings.theme = themes.get(currentIndex).value
|
||||
}
|
||||
currentIndex: {
|
||||
switch (settings.theme) {
|
||||
case Material.System:
|
||||
return 0
|
||||
case Material.Light:
|
||||
return 1
|
||||
case Material.Dark:
|
||||
return 2
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledExpansionPanel {
|
||||
id: interfacePanel
|
||||
label: qsTr("Custom reader")
|
||||
description: qsTr("Specify a custom reader for YubiKey.")
|
||||
metadata: "ccid otp slot custom readers nfc"
|
||||
|
||||
property bool aboutToChange: customReaderCheckbox.checked !== settings.useCustomReader
|
||||
|| readerFilter.text !== settings.customReaderName && readerFilter.text.length > 0
|
||||
|
||||
function isValidMode() {
|
||||
return aboutToChange
|
||||
}
|
||||
|
||||
function setInterface() {
|
||||
settings.useCustomReader = customReaderCheckbox.checked
|
||||
settings.customReaderName = readerFilter.text
|
||||
yubiKey.clearCurrentDeviceAndEntries()
|
||||
yubiKey.refreshDevicesDefault()
|
||||
navigator.goToSettings()
|
||||
navigator.snackBar(qsTr("Interface changed"))
|
||||
interfacePanel.isExpanded = false
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
CheckBox {
|
||||
id: customReaderCheckbox
|
||||
checked: settings.useCustomReader
|
||||
text: qsTr("Enable custom reader")
|
||||
padding: 0
|
||||
indicator.width: 16
|
||||
indicator.height: 16
|
||||
}
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 13
|
||||
color: primaryColor
|
||||
opacity: lowEmphasis
|
||||
text: "Specify a custom reader, useful for example when using a NFC reader."
|
||||
textFormat: TextEdit.RichText
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
lineHeight: 1.1
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: 8
|
||||
visible: customReaderCheckbox.checked
|
||||
|
||||
RowLayout {
|
||||
visible: yubiKey.availableReaders.length > 0
|
||||
StyledComboBox {
|
||||
id: connectedReaders
|
||||
enabled: yubiKey.availableReaders.length > 0
|
||||
visible: yubiKey.availableReaders.length > 0
|
||||
label: qsTr("Connected readers")
|
||||
model: yubiKey.availableReaders
|
||||
}
|
||||
StyledButton {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
text: qsTr("Use as filter")
|
||||
flat: true
|
||||
enabled: yubiKey.availableReaders.length > 0
|
||||
visible: yubiKey.availableReaders.length > 0
|
||||
onClicked: readerFilter.text = connectedReaders.currentText
|
||||
}
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: readerFilter
|
||||
labelText: qsTr("Custom reader filter")
|
||||
text: settings.customReaderName
|
||||
}
|
||||
}
|
||||
|
||||
StyledButton {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
text: "Apply"
|
||||
enabled: interfacePanel.isValidMode()
|
||||
onClicked: interfacePanel.setInterface()
|
||||
}
|
||||
}
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: Qt.platform.os === "osx" ? "Menu bar" : "System tray"
|
||||
description: qsTr("Configure where and how the application is visible.")
|
||||
|
||||
ColumnLayout {
|
||||
CheckBox {
|
||||
id: sysTrayCheckbox
|
||||
checked: settings.closeToTray
|
||||
text: Qt.platform.os === "osx" ? qsTr("Show in menu bar") : qsTr("Show in system tray")
|
||||
padding: 0
|
||||
indicator.width: 16
|
||||
indicator.height: 16
|
||||
onCheckStateChanged: {
|
||||
if(!checked) {
|
||||
hideOnLaunchCheckbox.checked = false
|
||||
}
|
||||
settings.closeToTray = checked
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: hideOnLaunchCheckbox
|
||||
enabled: sysTrayCheckbox.checked
|
||||
checked: settings.hideOnLaunch
|
||||
text: qsTr("Hide on launch")
|
||||
padding: 0
|
||||
indicator.width: 16
|
||||
indicator.height: 16
|
||||
onCheckStateChanged: settings.hideOnLaunch = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Clear passwords")
|
||||
description: qsTr("Delete all saved passwords.")
|
||||
isEnabled: false
|
||||
isBottomPanel: true
|
||||
toolButtonIcon: "../images/delete.svg"
|
||||
toolButtonToolTip: qsTr("Clear")
|
||||
toolButton.onClicked: navigator.confirm({
|
||||
"heading": qsTr("Clear passwords?"),
|
||||
"message": qsTr("This will delete all saved passwords."),
|
||||
"description": qsTr("A password prompt will appear the next time a YubiKey with a password is used."),
|
||||
"buttonAccept": qsTr("Clear passwords"),
|
||||
"acceptedCb": function() {
|
||||
yubiKey.clearLocalPasswords(function (resp) {
|
||||
if (resp.success) {
|
||||
navigator.snackBar(qsTr("Passwords cleared"))
|
||||
}
|
||||
})}
|
||||
})
|
||||
}
|
||||
SettingsPanelAppearance {}
|
||||
SettingsPanelCustomReader {}
|
||||
SettingsPanelSystemTray {}
|
||||
SettingsPanelClearPasswords {}
|
||||
}
|
||||
|
||||
StyledExpansionContainer {
|
||||
title: qsTr("Security Codes (OATH)")
|
||||
|
||||
SettingsPanelPasswordMgmt {}
|
||||
SettingsPanelResetDevice {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,11 @@ Item {
|
||||
property alias checked: control.checked
|
||||
|
||||
height: 40
|
||||
Layout.bottomMargin: 8
|
||||
Layout.bottomMargin: 16
|
||||
activeFocusOnTab: true
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
CheckBox {
|
||||
id: control
|
||||
|
@ -12,7 +12,7 @@ Pane {
|
||||
readonly property int dynamicWidth: 648
|
||||
readonly property int dynamicMargin: 32
|
||||
|
||||
property string sectionTitle
|
||||
property string title
|
||||
|
||||
Layout.alignment: Qt.AlignCenter | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
@ -22,7 +22,7 @@ Pane {
|
||||
visible: {
|
||||
if (toolBar.searchField.text.length > 0) {
|
||||
for (var i = 0; i < children.length; ++i) {
|
||||
if (!!children[i] && children[i].toString().startsWith("StyledExpansionPanel") && children[i].visible) {
|
||||
if (!!children[i] && children[i].toString().startsWith("SettingsPanel") && children[i].visible) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -39,7 +39,7 @@ Pane {
|
||||
RowLayout {
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
text: sectionTitle
|
||||
text: title
|
||||
color: Material.primary
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.Medium
|
||||
|
@ -36,7 +36,7 @@ Pane {
|
||||
|
||||
Layout.alignment: Qt.AlignCenter | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: isExpanded ? panelHeader.height + expandedContent.height + 40 : panelHeader.height + 20
|
||||
Layout.minimumHeight: isExpanded ? panelHeader.height + expandedContent.height + 48 : panelHeader.height + 19
|
||||
Layout.maximumWidth: dynamicWidth + dynamicMargin
|
||||
|
||||
Layout.leftMargin: -16
|
||||
@ -55,7 +55,7 @@ Pane {
|
||||
function expandAction() {
|
||||
function collapseAll() {
|
||||
for (var i = 0; i < parent.children.length; ++i) {
|
||||
if (!!parent.children[i] && parent.children[i].toString().startsWith("StyledExpansionPanel")) {
|
||||
if (!!parent.children[i] && parent.children[i].toString().startsWith("SettingsPanel")) {
|
||||
parent.children[i].isExpanded = false
|
||||
}
|
||||
}
|
||||
@ -72,14 +72,15 @@ Pane {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: panelMouseArea
|
||||
onClicked: expandAction()
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: -16
|
||||
anchors.rightMargin: -16
|
||||
anchors.topMargin: -16
|
||||
height: panelHeader.height + 40
|
||||
anchors.topMargin: -12
|
||||
height: panelHeader.implicitHeight + 19
|
||||
enabled: isEnabled
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
@ -156,15 +157,15 @@ Pane {
|
||||
}
|
||||
Label {
|
||||
id: panelDescription
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 13
|
||||
color: primaryColor
|
||||
opacity: lowEmphasis
|
||||
text: searchQuery.length > 0 ? colorizeMatch(description, searchQuery) : description
|
||||
textFormat: TextEdit.RichText
|
||||
textFormat: searchQuery.length > 0 ? TextEdit.RichText : TextEdit.PlainText
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: isExpanded ? 4 : 2
|
||||
maximumLineCount: isExpanded ? 4 : 1
|
||||
elide: Text.ElideRight
|
||||
lineHeight: 1.1
|
||||
visible: description
|
||||
|
@ -264,7 +264,8 @@ Python {
|
||||
// If oath is enabled, do a calculate all
|
||||
if (currentDeviceOathEnabled) {
|
||||
calculateAll(navigator.goToCredentialsIfNotInSettings)
|
||||
} else {
|
||||
} else {
|
||||
currentDeviceValidated = true
|
||||
navigator.goToCredentialsIfNotInSettings()
|
||||
}
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user