Improve import keys password popup
This commit is contained in:
parent
05b248e31a
commit
ebfebbeae1
5
TODO.md
5
TODO.md
|
@ -8,8 +8,6 @@
|
|||
- Remove the filled theme
|
||||
- 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
|
||||
- Room Sidepane
|
||||
- Hide when window too small
|
||||
|
@ -34,6 +32,7 @@
|
|||
- Terrible performance using `QT_QPA_PLATFORM=wayland-egl`, must use `xcb`
|
||||
|
||||
- UI
|
||||
- Choose a better default easing type for animations
|
||||
- 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
|
||||
or clicked on it once
|
||||
|
@ -42,8 +41,6 @@
|
|||
- Adapt UI for small heights
|
||||
|
||||
- Popup:
|
||||
- label size
|
||||
- Accept/cancel buttons
|
||||
- Transitions
|
||||
|
||||
- Restoring UI state:
|
||||
|
|
51
src/icons/light-thin/ok.svg
Normal file
51
src/icons/light-thin/ok.svg
Normal 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 |
|
@ -121,13 +121,21 @@ class Backend:
|
|||
|
||||
# General functions
|
||||
|
||||
|
||||
@staticmethod
|
||||
def hsluv(hue: int, saturation: int, lightness: int) -> List[float]:
|
||||
# (0-360, 0-100, 0-100) -> [0-1, 0-1, 0-1]
|
||||
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:
|
||||
from .config_files import Theme
|
||||
settings = await self.ui_settings.read()
|
||||
|
|
|
@ -59,11 +59,15 @@ HRectangle {
|
|||
spacing: interfaceBox.verticalSpacing
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin:
|
||||
interfaceTitle.visible ? 0 : interfaceBox.verticalSpacing
|
||||
Layout.leftMargin: interfaceBox.horizontalSpacing
|
||||
Layout.rightMargin: interfaceBox.horizontalSpacing
|
||||
}
|
||||
|
||||
HRowLayout {
|
||||
visible: buttonModel.length > 0
|
||||
|
||||
Repeater {
|
||||
id: interfaceButtonsRepeater
|
||||
model: []
|
||||
|
@ -74,7 +78,9 @@ HRectangle {
|
|||
id: button
|
||||
text: modelData.text
|
||||
icon.name: modelData.iconName || ""
|
||||
enabled: modelData.enabled && ! button.loading
|
||||
enabled: (modelData.enabled == undefined ?
|
||||
true : modelData.enabled) &&
|
||||
! button.loading
|
||||
onClicked: buttonCallbacks[modelData.name](button)
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
|
|
@ -5,48 +5,105 @@ import "../SidePane"
|
|||
|
||||
Popup {
|
||||
id: popup
|
||||
width: window.width
|
||||
anchors.centerIn: Overlay.overlay
|
||||
modal: true
|
||||
padding: 0
|
||||
|
||||
onOpened: passwordField.forceActiveFocus()
|
||||
|
||||
|
||||
property bool validateWhileTyping: false
|
||||
|
||||
property string acceptedPassword: ""
|
||||
property var passwordValid: null
|
||||
property bool okClicked: false
|
||||
|
||||
property alias label: popupLabel
|
||||
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 {
|
||||
color: theme.controls.popup.background
|
||||
}
|
||||
|
||||
HColumnLayout {
|
||||
width: parent.width
|
||||
spacing: theme.spacing
|
||||
contentItem: HInterfaceBox {
|
||||
id: box
|
||||
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 {
|
||||
id: popupLabel
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.minimumWidth: theme.minimumSupportedWidth
|
||||
Layout.maximumWidth:
|
||||
Math.min(480, window.width - theme.spacing * 2)
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
HRowLayout {
|
||||
spacing: box.horizontalSpacing
|
||||
|
||||
HTextField {
|
||||
id: passwordField
|
||||
placeholderText: qsTr("Passphrase")
|
||||
echoMode: TextInput.Password
|
||||
focus: true
|
||||
onAccepted: {
|
||||
popup.password = text
|
||||
popup.close()
|
||||
error: passwordValid === false
|
||||
|
||||
onTextChanged: passwordValid =
|
||||
validateWhileTyping ? verifyPassword(text) : null
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.fillWidth: true
|
||||
HIcon {
|
||||
svgName: passwordValid ? "ok" : "cancel"
|
||||
visible: Layout.preferredWidth > 0
|
||||
|
||||
Layout.preferredWidth: popupLabel.width
|
||||
Layout.maximumWidth: popupLabel.width
|
||||
Layout.preferredWidth:
|
||||
passwordValid == null ||
|
||||
(validateWhileTyping && ! okClicked && ! passwordValid) ?
|
||||
0 :implicitWidth
|
||||
|
||||
Behavior on Layout.preferredWidth { HNumberAnimation {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,12 @@ TextField {
|
|||
|
||||
readonly property QtObject _tf: theme.controls.textField
|
||||
|
||||
property bool error: false
|
||||
|
||||
property bool bordered: true
|
||||
property color backgroundColor: _tf.background
|
||||
property color borderColor: _tf.border
|
||||
property color errorBorder: _tf.errorBorder
|
||||
property color focusedBackgroundColor: _tf.focusedBackground
|
||||
property color focusedBorderColor: _tf.focusedBorder
|
||||
property alias radius: textFieldBackground.radius
|
||||
|
@ -20,7 +23,8 @@ TextField {
|
|||
background: Rectangle {
|
||||
id: textFieldBackground
|
||||
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
|
||||
|
||||
Behavior on color { HColorAnimation { factor: 0.25 } }
|
||||
|
|
|
@ -63,14 +63,16 @@ HColumnLayout {
|
|||
HPasswordPopup {
|
||||
property url file: ""
|
||||
|
||||
function verifyPassword(pass) {
|
||||
return py.callSync(
|
||||
"check_exported_keys_password", [file.toString(), pass]
|
||||
)
|
||||
}
|
||||
|
||||
id: importPasswordPopup
|
||||
label.text: qsTr(
|
||||
"Please enter the passphrase that was used to protect this " +
|
||||
"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."
|
||||
"Please enter the passphrase that was used to protect this file:"
|
||||
)
|
||||
onPasswordChanged: importKeys(file, password)
|
||||
onAcceptedPasswordChanged: importKeys(file, password)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ controls:
|
|||
int borderWidth: 1
|
||||
color border: "transparent"
|
||||
color focusedBorder: colors.strongAccentBackground
|
||||
color errorBorder: colors.errorText
|
||||
|
||||
color text: colors.text
|
||||
color focusedText: colors.text
|
||||
|
|
Loading…
Reference in New Issue
Block a user