Improve import keys password popup
This commit is contained in:
parent
05b248e31a
commit
ebfebbeae1
7
TODO.md
7
TODO.md
|
@ -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,9 +41,7 @@
|
||||||
- Adapt UI for small heights
|
- Adapt UI for small heights
|
||||||
|
|
||||||
- Popup:
|
- Popup:
|
||||||
- label size
|
- Transitions
|
||||||
- Accept/cancel buttons
|
|
||||||
- Transitions
|
|
||||||
|
|
||||||
- Restoring UI state:
|
- Restoring UI state:
|
||||||
- Sendbox content
|
- Sendbox content
|
||||||
|
|
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
|
# 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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HTextField {
|
HRowLayout {
|
||||||
id: passwordField
|
spacing: box.horizontalSpacing
|
||||||
echoMode: TextInput.Password
|
|
||||||
focus: true
|
HTextField {
|
||||||
onAccepted: {
|
id: passwordField
|
||||||
popup.password = text
|
placeholderText: qsTr("Passphrase")
|
||||||
popup.close()
|
echoMode: TextInput.Password
|
||||||
|
focus: true
|
||||||
|
error: passwordValid === false
|
||||||
|
|
||||||
|
onTextChanged: passwordValid =
|
||||||
|
validateWhileTyping ? verifyPassword(text) : null
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
HIcon {
|
||||||
Layout.fillWidth: true
|
svgName: passwordValid ? "ok" : "cancel"
|
||||||
|
visible: Layout.preferredWidth > 0
|
||||||
|
|
||||||
Layout.preferredWidth: popupLabel.width
|
Layout.preferredWidth:
|
||||||
Layout.maximumWidth: popupLabel.width
|
passwordValid == null ||
|
||||||
|
(validateWhileTyping && ! okClicked && ! passwordValid) ?
|
||||||
|
0 :implicitWidth
|
||||||
|
|
||||||
|
Behavior on Layout.preferredWidth { HNumberAnimation {} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 } }
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user