Improve import keys password popup

This commit is contained in:
miruka 2019-08-27 22:25:13 -04:00
parent 05b248e31a
commit ebfebbeae1
8 changed files with 160 additions and 34 deletions

View File

@ -8,8 +8,6 @@
- Remove the filled theme - Remove the filled theme
- Have a default background - Have a default background
- Use [Animators](https://doc.qt.io/qt-5/qml-qtquick-animator.html)
- Choose a better default easing type for animations
- Sendbox - Sendbox
- Room Sidepane - Room Sidepane
- Hide when window too small - Hide when window too small
@ -34,6 +32,7 @@
- Terrible performance using `QT_QPA_PLATFORM=wayland-egl`, must use `xcb` - Terrible performance using `QT_QPA_PLATFORM=wayland-egl`, must use `xcb`
- UI - UI
- Choose a better default easing type for animations
- Make invite icon blink if there's no one but ourself in the room, - Make invite icon blink if there's no one but ourself in the room,
but never do it again once the user hovered it long enough to show tooltip but never do it again once the user hovered it long enough to show tooltip
or clicked on it once or clicked on it once
@ -42,8 +41,6 @@
- Adapt UI for small heights - Adapt UI for small heights
- Popup: - Popup:
- label size
- Accept/cancel buttons
- Transitions - Transitions
- Restoring UI state: - Restoring UI state:

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
version="1.1"
id="svg4"
sodipodi:docname="invite_accept.svg"
inkscape:version="">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview6"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="-28.271186"
inkscape:cy="12"
inkscape:current-layer="svg4" />
<path
d="M9 21.035l-9-8.638 2.791-2.87 6.156 5.874 12.21-12.436 2.843 2.817z"
id="path2"
style="fill:#0d8967;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -121,13 +121,21 @@ class Backend:
# General functions # General functions
@staticmethod @staticmethod
def hsluv(hue: int, saturation: int, lightness: int) -> List[float]: def hsluv(hue: int, saturation: int, lightness: int) -> List[float]:
# (0-360, 0-100, 0-100) -> [0-1, 0-1, 0-1] # (0-360, 0-100, 0-100) -> [0-1, 0-1, 0-1]
return hsluv.hsluv_to_rgb([hue, saturation, lightness]) return hsluv.hsluv_to_rgb([hue, saturation, lightness])
@staticmethod
def check_exported_keys_password(file_path: str, password: str) -> bool:
try:
nio.crypto.key_export.decrypt_and_read(file_path, password)
return True
except (FileNotFoundError, ValueError):
return False
async def load_settings(self) -> tuple: async def load_settings(self) -> tuple:
from .config_files import Theme from .config_files import Theme
settings = await self.ui_settings.read() settings = await self.ui_settings.read()

View File

@ -59,11 +59,15 @@ HRectangle {
spacing: interfaceBox.verticalSpacing spacing: interfaceBox.verticalSpacing
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin:
interfaceTitle.visible ? 0 : interfaceBox.verticalSpacing
Layout.leftMargin: interfaceBox.horizontalSpacing Layout.leftMargin: interfaceBox.horizontalSpacing
Layout.rightMargin: interfaceBox.horizontalSpacing Layout.rightMargin: interfaceBox.horizontalSpacing
} }
HRowLayout { HRowLayout {
visible: buttonModel.length > 0
Repeater { Repeater {
id: interfaceButtonsRepeater id: interfaceButtonsRepeater
model: [] model: []
@ -74,7 +78,9 @@ HRectangle {
id: button id: button
text: modelData.text text: modelData.text
icon.name: modelData.iconName || "" icon.name: modelData.iconName || ""
enabled: modelData.enabled && ! button.loading enabled: (modelData.enabled == undefined ?
true : modelData.enabled) &&
! button.loading
onClicked: buttonCallbacks[modelData.name](button) onClicked: buttonCallbacks[modelData.name](button)
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -5,48 +5,105 @@ import "../SidePane"
Popup { Popup {
id: popup id: popup
width: window.width
anchors.centerIn: Overlay.overlay anchors.centerIn: Overlay.overlay
modal: true modal: true
padding: 0
onOpened: passwordField.forceActiveFocus() onOpened: passwordField.forceActiveFocus()
property bool validateWhileTyping: false
property string acceptedPassword: ""
property var passwordValid: null
property bool okClicked: false
property alias label: popupLabel property alias label: popupLabel
property alias field: passwordField property alias field: passwordField
property string password: ""
function verifyPassword(pass) {
// Implement me when using this component
return false
}
enter: Transition {
HNumberAnimation { property: "scale"; from: 0; to: 1; overshoot: 4 }
}
exit: Transition {
HNumberAnimation { property: "scale"; to: 0 }
}
background: HRectangle { background: HRectangle {
color: theme.controls.popup.background color: theme.controls.popup.background
} }
HColumnLayout { contentItem: HInterfaceBox {
width: parent.width id: box
spacing: theme.spacing implicitWidth: theme.minimumSupportedWidthPlusSpacing
enterButtonTarget: "ok"
buttonModel: [
{ name: "ok", text: qsTr("OK"), iconName: "ok",
enabled: passwordField.text &&
(validateWhileTyping ? passwordValid : true) },
{ name: "cancel", text: qsTr("Cancel"), iconName: "cancel" },
]
buttonCallbacks: ({
ok: button => {
let password = passwordField.text
okClicked = true
button.loading = true
if (verifyPassword(password)) {
passwordValid = true
popup.acceptedPassword = password
popup.close()
} else {
passwordValid = false
}
button.loading = false
},
cancel: button => { popup.close() },
})
HLabel { HLabel {
id: popupLabel id: popupLabel
wrapMode: Text.Wrap wrapMode: Text.Wrap
Layout.alignment: Qt.AlignCenter Layout.fillWidth: true
Layout.minimumWidth: theme.minimumSupportedWidth
Layout.maximumWidth:
Math.min(480, window.width - theme.spacing * 2)
} }
HRowLayout {
spacing: box.horizontalSpacing
HTextField { HTextField {
id: passwordField id: passwordField
placeholderText: qsTr("Passphrase")
echoMode: TextInput.Password echoMode: TextInput.Password
focus: true focus: true
onAccepted: { error: passwordValid === false
popup.password = text
popup.close() onTextChanged: passwordValid =
} validateWhileTyping ? verifyPassword(text) : null
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true Layout.fillWidth: true
}
Layout.preferredWidth: popupLabel.width HIcon {
Layout.maximumWidth: popupLabel.width svgName: passwordValid ? "ok" : "cancel"
visible: Layout.preferredWidth > 0
Layout.preferredWidth:
passwordValid == null ||
(validateWhileTyping && ! okClicked && ! passwordValid) ?
0 :implicitWidth
Behavior on Layout.preferredWidth { HNumberAnimation {} }
}
} }
} }
} }

View File

@ -8,9 +8,12 @@ TextField {
readonly property QtObject _tf: theme.controls.textField readonly property QtObject _tf: theme.controls.textField
property bool error: false
property bool bordered: true property bool bordered: true
property color backgroundColor: _tf.background property color backgroundColor: _tf.background
property color borderColor: _tf.border property color borderColor: _tf.border
property color errorBorder: _tf.errorBorder
property color focusedBackgroundColor: _tf.focusedBackground property color focusedBackgroundColor: _tf.focusedBackground
property color focusedBorderColor: _tf.focusedBorder property color focusedBorderColor: _tf.focusedBorder
property alias radius: textFieldBackground.radius property alias radius: textFieldBackground.radius
@ -20,7 +23,8 @@ TextField {
background: Rectangle { background: Rectangle {
id: textFieldBackground id: textFieldBackground
color: field.activeFocus ? focusedBackgroundColor : backgroundColor color: field.activeFocus ? focusedBackgroundColor : backgroundColor
border.color: field.activeFocus ? focusedBorderColor : borderColor border.color: error ? errorBorder :
field.activeFocus ? focusedBorderColor : borderColor
border.width: bordered ? theme.controls.textField.borderWidth : 0 border.width: bordered ? theme.controls.textField.borderWidth : 0
Behavior on color { HColorAnimation { factor: 0.25 } } Behavior on color { HColorAnimation { factor: 0.25 } }

View File

@ -63,14 +63,16 @@ HColumnLayout {
HPasswordPopup { HPasswordPopup {
property url file: "" property url file: ""
function verifyPassword(pass) {
return py.callSync(
"check_exported_keys_password", [file.toString(), pass]
)
}
id: importPasswordPopup id: importPasswordPopup
label.text: qsTr( label.text: qsTr(
"Please enter the passphrase that was used to protect this " + "Please enter the passphrase that was used to protect this file:"
"file.\n\n" +
"The import can take a few minutes. " +
"You can leave the account settings page while it is running. " +
"Messages may not be sent or received until the operation is done."
) )
onPasswordChanged: importKeys(file, password) onAcceptedPasswordChanged: importKeys(file, password)
} }
} }

View File

@ -133,6 +133,7 @@ controls:
int borderWidth: 1 int borderWidth: 1
color border: "transparent" color border: "transparent"
color focusedBorder: colors.strongAccentBackground color focusedBorder: colors.strongAccentBackground
color errorBorder: colors.errorText
color text: colors.text color text: colors.text
color focusedText: colors.text color focusedText: colors.text