mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-24 03:06:45 +03:00
Merge pull request #487 from Yubico/improved-confirmation-prompts
made critical prompts more obvious and some minor cleanup
This commit is contained in:
commit
1a8fbc52be
@ -6,11 +6,11 @@ Dialog {
|
||||
margins: 0
|
||||
spacing: 0
|
||||
modal: true
|
||||
focus: true
|
||||
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
width: app.width > 600 ? 600 : app.width * 0.90
|
||||
focus: true
|
||||
width: app.width * 0.9 > 600 ? 600 : app.width * 0.9
|
||||
|
||||
background: Rectangle {
|
||||
color: defaultBackground
|
||||
@ -29,7 +29,6 @@ Dialog {
|
||||
|
||||
Component.onCompleted: {
|
||||
navigator.isShowingAbout = true
|
||||
btnCancel.forceActiveFocus()
|
||||
}
|
||||
|
||||
function getDeviceDescription() {
|
||||
@ -171,23 +170,5 @@ Dialog {
|
||||
Layout.maximumWidth: parent.width
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
DialogButtonBox {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
Layout.topMargin: 14
|
||||
Layout.rightMargin: -22
|
||||
Layout.bottomMargin: -22
|
||||
|
||||
StyledButton {
|
||||
id: btnCancel
|
||||
text: qsTr("Close")
|
||||
flat: true
|
||||
enabled: true
|
||||
font.capitalization: Font.capitalization
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||
Keys.onReturnPressed: reject()
|
||||
onClicked: reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,15 @@ import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.2
|
||||
|
||||
Dialog {
|
||||
padding: 16
|
||||
margins: 0
|
||||
spacing: 0
|
||||
modal: true
|
||||
focus: true
|
||||
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
width: app.width > 600 ? 600 : app.width * 0.8
|
||||
focus: true
|
||||
width: app.width * 0.9 > 600 ? 600 : app.width * 0.9
|
||||
|
||||
background: Rectangle {
|
||||
color: defaultBackground
|
||||
@ -35,50 +36,87 @@ Dialog {
|
||||
Component.onCompleted: btnCancel.forceActiveFocus()
|
||||
|
||||
property var acceptedCb
|
||||
|
||||
property string heading
|
||||
property string message
|
||||
property string primaryMessage
|
||||
property string secondaryMessage
|
||||
property string buttonCancel: qsTr("Cancel")
|
||||
property string buttonAccept: qsTr("Accept")
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Label {
|
||||
id: confirmationHeading
|
||||
text: heading
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.Medium
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.maximumWidth: parent.width
|
||||
}
|
||||
|
||||
Pane {
|
||||
padding: 12
|
||||
rightPadding: 16
|
||||
bottomPadding: 8
|
||||
visible: primaryMessage
|
||||
width: parent.width
|
||||
Layout.topMargin: 16
|
||||
Layout.maximumWidth: parent.width
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
color: yubicoRed
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 0
|
||||
width: parent.width
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
StyledImage {
|
||||
source: "../images/warning.svg"
|
||||
color: yubicoWhite
|
||||
iconWidth: 32
|
||||
iconHeight: 32
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.maximumWidth: 32
|
||||
}
|
||||
|
||||
Label {
|
||||
text: primaryMessage
|
||||
color: yubicoWhite
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.Bold
|
||||
lineHeight: 1.2
|
||||
leftPadding: 12
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: confirmationLbl
|
||||
text: message
|
||||
Layout.topMargin: 16
|
||||
text: secondaryMessage
|
||||
color: formText
|
||||
font.pixelSize: 13
|
||||
lineHeight: 1.2
|
||||
visible: secondaryMessage
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.topMargin: 16
|
||||
Layout.maximumWidth: parent.width
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
DialogButtonBox {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
Layout.topMargin: 14
|
||||
Layout.rightMargin: -22
|
||||
Layout.bottomMargin: -22
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: -8
|
||||
Layout.bottomMargin: -8
|
||||
|
||||
StyledButton {
|
||||
id: btnAccept
|
||||
text: qsTr(buttonAccept)
|
||||
flat: true
|
||||
enabled: true
|
||||
critical: primaryMessage
|
||||
font.capitalization: Font.capitalization
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||
KeyNavigation.tab: btnCancel
|
||||
@ -89,6 +127,7 @@ Dialog {
|
||||
id: btnCancel
|
||||
text: qsTr(buttonCancel)
|
||||
flat: true
|
||||
critical: primaryMessage
|
||||
enabled: true
|
||||
font.capitalization: Font.capitalization
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||
|
@ -75,14 +75,6 @@ Pane {
|
||||
text: qsTr("Copy to clipboard")
|
||||
onTriggered: calculateCard(true)
|
||||
}
|
||||
MenuItem {
|
||||
icon.source: "../images/delete.svg"
|
||||
icon.color: iconButtonNormal
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
text: "Delete account"
|
||||
onTriggered: deleteCard()
|
||||
}
|
||||
MenuItem {
|
||||
icon.source: favorite ? "../images/star.svg" : "../images/star_border.svg"
|
||||
icon.color: iconButtonNormal
|
||||
@ -91,32 +83,13 @@ Pane {
|
||||
text: favorite ? qsTr("Remove as favorite") : qsTr("Set as favorite")
|
||||
onTriggered: toggleFavorite()
|
||||
}
|
||||
MenuSeparator {
|
||||
padding: 0
|
||||
topPadding: 4
|
||||
bottomPadding: 4
|
||||
contentItem: Rectangle {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 1
|
||||
color: formUnderline
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
icon.source: "../images/add.svg"
|
||||
icon.source: "../images/delete.svg"
|
||||
icon.color: iconButtonNormal
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
enabled: !!yubiKey.currentDevice && yubiKey.currentDeviceValidated
|
||||
text: qsTr("Add account")
|
||||
onTriggered: yubiKey.scanQr()
|
||||
}
|
||||
MenuItem {
|
||||
icon.source: "../images/cogwheel.svg"
|
||||
icon.color: iconButtonNormal
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
text: qsTr("Settings")
|
||||
onTriggered: navigator.goToSettings()
|
||||
text: "Delete account"
|
||||
onTriggered: deleteCard()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,8 +217,9 @@ Pane {
|
||||
|
||||
function deleteCard() {
|
||||
navigator.confirm(
|
||||
"Delete " + formattedName() + " ?",
|
||||
qsTr("This will permanently delete the account from the YubiKey, as well as your ability to generate security codes for it. Make sure 2FA has been disabled BEFORE proceeding."),
|
||||
qsTr("Delete %1 ?").arg(formattedName()),
|
||||
qsTr("This will permanently delete the account from your YubiKey."),
|
||||
qsTr("You will not be able to generate security codes for the account anymore. Make sure 2FA has been disabled before proceeding."),
|
||||
function () {
|
||||
if (settings.otpMode) {
|
||||
yubiKey.otpDeleteCredential(credential,
|
||||
|
@ -80,10 +80,11 @@ StackView {
|
||||
}), StackView.Immediate)
|
||||
}
|
||||
|
||||
function confirm(heading, message, cb) {
|
||||
function confirm(heading, primaryMessage, secondaryMessage, cb) {
|
||||
var popup = confirmationPopup.createObject(app, {
|
||||
"heading": heading,
|
||||
"message": message,
|
||||
"primaryMessage": primaryMessage,
|
||||
"secondaryMessage": secondaryMessage,
|
||||
"acceptedCb": cb
|
||||
})
|
||||
popup.open()
|
||||
|
@ -51,7 +51,8 @@ Flickable {
|
||||
navigator.confirm(
|
||||
qsTr("Overwrite?"),
|
||||
qsTr("An account with this name already exists, do you want to overwrite it?"),
|
||||
_ccidAddCredentialOverwrite)
|
||||
"",
|
||||
_ccidAddCredentialOverwrite)
|
||||
} else {
|
||||
navigator.snackBarError(navigator.getErrorMessage(resp.error_id))
|
||||
console.log("addCredential failed:", resp.error_id)
|
||||
@ -93,7 +94,8 @@ Flickable {
|
||||
otpSlotComboBox.currentText) - 1]) {
|
||||
navigator.confirm(
|
||||
qsTr("Overwrite?"),
|
||||
qsTr("The slot is already configured, do you want to overwrite it?"),
|
||||
qsTr("This slot is already configured, do you want to overwrite it?"),
|
||||
"",
|
||||
_otpAddCredential)
|
||||
} else {
|
||||
_otpAddCredential()
|
||||
|
@ -27,16 +27,16 @@ Flickable {
|
||||
Keys.onEscapePressed: navigator.home()
|
||||
|
||||
function getDeviceLabel(device) {
|
||||
if (!!device.serial) {
|
||||
return ("%1 [#%2]").arg(device.name).arg(device.serial)
|
||||
} else {
|
||||
if (!!device) {
|
||||
return ("%1").arg(device.name)
|
||||
} else {
|
||||
return qsTr("Insert your YubiKey")
|
||||
}
|
||||
}
|
||||
|
||||
function getDeviceDescription() {
|
||||
if (!!yubiKey.currentDevice) {
|
||||
return yubiKey.currentDevice.usbInterfacesEnabled.join('+')
|
||||
function getDeviceDescription(device) {
|
||||
if (!!device) {
|
||||
return qsTr("Serial number: %1").arg(!!device.serial ? device.serial : "Not Available")
|
||||
} else if (yubiKey.availableDevices.length > 0
|
||||
&& !yubiKey.availableDevices.some(dev => dev.selectable)) {
|
||||
return qsTr("No compatible device found")
|
||||
@ -45,7 +45,6 @@ Flickable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function clearPasswordFields() {
|
||||
currentPasswordField.text = ""
|
||||
newPasswordField.text = ""
|
||||
@ -184,8 +183,8 @@ Flickable {
|
||||
|
||||
StyledExpansionPanel {
|
||||
id: currentDevicePanel
|
||||
label: !!yubiKey.currentDevice ? getDeviceLabel(yubiKey.currentDevice) : qsTr("Insert your YubiKey")
|
||||
description: getDeviceDescription()
|
||||
label: getDeviceLabel(yubiKey.currentDevice)
|
||||
description: getDeviceDescription(yubiKey.currentDevice)
|
||||
keyImage: !!yubiKey.currentDevice ? yubiKey.getCurrentDeviceImage() : "../images/yubikeys-large-transparent"
|
||||
isTopPanel: true
|
||||
Layout.fillWidth: true
|
||||
@ -200,14 +199,15 @@ Flickable {
|
||||
|
||||
Repeater {
|
||||
model: yubiKey.availableDevices
|
||||
RadioButton {
|
||||
StyledRadioButton {
|
||||
Layout.fillWidth: true
|
||||
objectName: index
|
||||
checked: !!yubiKey.currentDevice
|
||||
&& modelData.serial === yubiKey.currentDevice.serial
|
||||
text: getDeviceLabel(modelData)
|
||||
description: getDeviceDescription(modelData)
|
||||
enabled: modelData.selectable
|
||||
ButtonGroup.group: deviceButtonGroup
|
||||
buttonGroup: deviceButtonGroup
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,6 +285,7 @@ Flickable {
|
||||
flat: true
|
||||
onClicked: navigator.confirm(
|
||||
qsTr("Remove password?"),
|
||||
"",
|
||||
qsTr("A password will not be required to access the accounts anymore."),
|
||||
function () {
|
||||
removePassword()
|
||||
@ -302,14 +303,15 @@ Flickable {
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Reset")
|
||||
description: qsTr("Warning: Reset will delete all accounts from the YubiKey and restore factory defaults.")
|
||||
description: qsTr("Warning: Reset will delete all accounts and restore factory defaults.")
|
||||
isEnabled: false
|
||||
visible: !!yubiKey.currentDevice && !settings.otpMode
|
||||
toolButtonIcon: "../images/reset.svg"
|
||||
toolButtonToolTip: qsTr("Reset OATH Application")
|
||||
toolButtonToolTip: qsTr("Reset device")
|
||||
toolButton.onClicked: navigator.confirm(
|
||||
qsTr("Reset OATH application?"),
|
||||
qsTr("This will delete all accounts and restore factory defaults."),
|
||||
qsTr("Reset device?"),
|
||||
qsTr("This will delete all accounts and restore factory defaults of your YubiKey."),
|
||||
qsTr("There is NO going back from here, if you do not know what you are doing, do NOT do this."),
|
||||
function () {
|
||||
navigator.goToLoading()
|
||||
yubiKey.reset(function (resp) {
|
||||
@ -339,7 +341,7 @@ Flickable {
|
||||
|
||||
StyledExpansionPanel {
|
||||
label: qsTr("Appearance")
|
||||
description: qsTr("Change the appearance of the application.")
|
||||
description: qsTr("Change the visual appearance of the application.")
|
||||
isTopPanel: true
|
||||
|
||||
ColumnLayout {
|
||||
|
@ -7,12 +7,13 @@ import QtGraphicalEffects 1.0
|
||||
Button {
|
||||
|
||||
property alias toolTipText: buttonToolTip.text
|
||||
property bool critical: false
|
||||
|
||||
id: button
|
||||
font.capitalization: Font.capitalization
|
||||
font.weight: Font.Medium
|
||||
Material.foreground: button.flat ? Material.primary : yubicoWhite
|
||||
Material.background: button.flat ? "transparent" : Material.primary
|
||||
Material.foreground: button.flat ? (critical ? yubicoRed : Material.primary) : yubicoWhite
|
||||
Material.background: button.flat ? "transparent" : (critical ? yubicoRed : Material.primary)
|
||||
Material.elevation: button.flat ? 0 : 1
|
||||
|
||||
ToolTip {
|
||||
|
@ -150,7 +150,8 @@ Pane {
|
||||
color: formLabel
|
||||
text: description
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.rowSpan: 1
|
||||
maximumLineCount: isExpanded ? 4 : 2
|
||||
elide: Text.ElideRight
|
||||
lineHeight: 1.1
|
||||
visible: description
|
||||
}
|
||||
|
45
qml/StyledRadioButton.qml
Normal file
45
qml/StyledRadioButton.qml
Normal file
@ -0,0 +1,45 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
|
||||
property alias text: controlItem.text
|
||||
property alias description: controlDescription.text
|
||||
property alias enabled: control.enabled
|
||||
property alias objectName: control.objectName
|
||||
property alias checked: control.checked
|
||||
property var buttonGroup
|
||||
|
||||
height: 40
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
RowLayout {
|
||||
|
||||
RadioButton {
|
||||
id: control
|
||||
ButtonGroup.group: buttonGroup
|
||||
indicator.x: 8
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
Label {
|
||||
id: controlItem
|
||||
leftPadding: control.indicator.width + control.spacing + control.x
|
||||
color: formText
|
||||
}
|
||||
|
||||
Label {
|
||||
id: controlDescription
|
||||
leftPadding: control.indicator.width + control.spacing + control.x
|
||||
color: formLabel
|
||||
visible: description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -148,7 +148,7 @@ ToolBar {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
placeholderText: qsTr("Quick Find")
|
||||
placeholderTextColor: hovered || activeFocus ? iconButtonHovered : iconButtonNormal
|
||||
placeholderTextColor: iconButtonNormal
|
||||
leftPadding: 28
|
||||
rightPadding: 8
|
||||
width: parent.width
|
||||
|
@ -27,7 +27,7 @@ ApplicationWindow {
|
||||
readonly property string yubicoBlue: "#284c61"
|
||||
readonly property string yubicoWhite: "#ffffff"
|
||||
readonly property string yubicoGrey: "#939598"
|
||||
readonly property string yubicoRed: "#fd5552"
|
||||
readonly property string yubicoRed: "#dc4b4c"
|
||||
|
||||
readonly property string defaultDark: "#303030"
|
||||
readonly property string defaultDarkLighter: "#383838"
|
||||
|
Loading…
Reference in New Issue
Block a user