summary refs log tree commit diff
path: root/resources
diff options
context:
space:
mode:
authorDeepBlueV7.X <nicolas.werner@hotmail.de>2021-10-09 23:35:09 +0000
committerGitHub <noreply@github.com>2021-10-09 23:35:09 +0000
commit281d764aa3ce0ea55536a6356e1ed3511aaff6f4 (patch)
treeaa8c88bdf2788c84053e7009adafea4c1b4a4c75 /resources
parentMerge pull request #743 from LorenDB/qmlLogout (diff)
parentSupport bootstrapping crosssigning (diff)
downloadnheko-281d764aa3ce0ea55536a6356e1ed3511aaff6f4.tar.xz
Merge pull request #755 from Nheko-Reborn/bootstrapping
Support bootstrapping crosssigning
Diffstat (limited to 'resources')
-rw-r--r--resources/qml/Avatar.qml1
-rw-r--r--resources/qml/ChatPage.qml9
-rw-r--r--resources/qml/MessageView.qml9
-rw-r--r--resources/qml/Root.qml23
-rw-r--r--resources/qml/SelfVerificationCheck.qml261
-rw-r--r--resources/qml/delegates/Reply.qml14
-rw-r--r--resources/qml/delegates/TextMessage.qml1
-rw-r--r--resources/qml/dialogs/InputDialog.qml1
-rw-r--r--resources/qml/dialogs/RoomDirectory.qml2
-rw-r--r--resources/qml/dialogs/UserProfile.qml47
-rw-r--r--resources/res.qrc1
11 files changed, 333 insertions, 36 deletions
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index 5d2b583a..58b22863 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -42,6 +42,7 @@ Rectangle {
 
     Image {
         id: identicon
+
         anchors.fill: parent
         visible: Settings.useIdenticon && img.status != Image.Ready
         source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
diff --git a/resources/qml/ChatPage.qml b/resources/qml/ChatPage.qml
index e56d7d46..082fa8d6 100644
--- a/resources/qml/ChatPage.qml
+++ b/resources/qml/ChatPage.qml
@@ -2,12 +2,15 @@
 //
 // SPDX-License-Identifier: GPL-3.0-or-later
 
-import QtQuick 2.9
-import QtQuick.Controls 2.5
+import QtQuick 2.15
+import QtQuick.Controls 2.15
 import QtQuick.Layouts 1.3
 import "components"
 import im.nheko 1.0
 
+// this needs to be last
+import QtQml 2.15
+
 Rectangle {
     id: chatPage
 
@@ -41,6 +44,7 @@ Rectangle {
                 value: communityListC.preferredWidth
                 when: !adaptiveView.singlePageMode
                 delayed: true
+                restoreMode: Binding.RestoreBindingOrValue
             }
 
         }
@@ -66,6 +70,7 @@ Rectangle {
                 value: roomListC.preferredWidth
                 when: !adaptiveView.singlePageMode
                 delayed: true
+                restoreMode: Binding.RestoreBindingOrValue
             }
 
         }
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 60c04098..7ed30112 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -20,12 +20,11 @@ ScrollView {
 
     ListView {
         id: chat
-        
-        displayMarginBeginning: height/2
-        displayMarginEnd: height/2
 
         property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < parent.availableWidth) ? Settings.timelineMaxWidth : parent.availableWidth) - parent.padding * 2
 
+        displayMarginBeginning: height / 2
+        displayMarginEnd: height / 2
         model: room
         // reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107
         //onModelChanged: if (room) room.sendReset()
@@ -415,8 +414,6 @@ ScrollView {
             Loader {
                 id: section
 
-                z: 4
-
                 property int parentWidth: parent.width
                 property string userId: wrapper.userId
                 property string previousMessageUserId: wrapper.previousMessageUserId
@@ -425,6 +422,7 @@ ScrollView {
                 property string userName: wrapper.userName
                 property var timestamp: wrapper.timestamp
 
+                z: 4
                 active: previousMessageUserId !== undefined && previousMessageUserId !== userId || previousMessageDay !== day
                 //asynchronous: true
                 sourceComponent: sectionHeader
@@ -685,6 +683,7 @@ ScrollView {
             text: qsTr("&Go to quoted message")
             onTriggered: chat.model.showEvent(eventId)
         }
+
     }
 
 }
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index 29da45eb..1b910592 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -211,6 +211,29 @@ Page {
         target: CallManager
     }
 
+    SelfVerificationCheck {
+    }
+
+    InputDialog {
+        id: uiaPassPrompt
+
+        echoMode: TextInput.Password
+        title: UIA.title
+        prompt: qsTr("Please enter your login password to continue:")
+        onAccepted: (t) => {
+            return UIA.continuePassword(t);
+        }
+    }
+
+    Connections {
+        function onPassword() {
+            console.log("UIA: password needed");
+            uiaPassPrompt.show();
+        }
+
+        target: UIA
+    }
+
     ChatPage {
         anchors.fill: parent
     }
diff --git a/resources/qml/SelfVerificationCheck.qml b/resources/qml/SelfVerificationCheck.qml
new file mode 100644
index 00000000..a12bfc61
--- /dev/null
+++ b/resources/qml/SelfVerificationCheck.qml
@@ -0,0 +1,261 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import Qt.labs.platform 1.1 as P
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.3
+import im.nheko 1.0
+
+Item {
+    visible: false
+    enabled: false
+
+    Dialog {
+        id: showRecoverKeyDialog
+
+        property string recoveryKey: ""
+
+        parent: Overlay.overlay
+        anchors.centerIn: parent
+        height: content.height + implicitFooterHeight + implicitHeaderHeight
+        width: content.width
+        padding: 0
+        modal: true
+        standardButtons: Dialog.Ok
+        closePolicy: Popup.NoAutoClose
+
+        ColumnLayout {
+            id: content
+
+            spacing: 0
+
+            Label {
+                Layout.margins: Nheko.paddingMedium
+                Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
+                Layout.fillWidth: true
+                text: qsTr("This is your recovery key. You will need it to restore access to your encrypted messages and verification keys. Keep this safe. Don't share it with anyone and don't lose it! Don't go to start! Don't draw $200 from the bank!")
+                color: Nheko.colors.text
+                wrapMode: Text.Wrap
+            }
+
+            TextEdit {
+                Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
+                Layout.alignment: Qt.AlignHCenter
+                horizontalAlignment: TextEdit.AlignHCenter
+                verticalAlignment: TextEdit.AlignVCenter
+                readOnly: true
+                selectByMouse: true
+                text: showRecoverKeyDialog.recoveryKey
+                color: Nheko.colors.text
+                font.bold: true
+                wrapMode: TextEdit.Wrap
+            }
+
+        }
+
+        background: Rectangle {
+            color: Nheko.colors.window
+            border.color: Nheko.theme.separator
+            border.width: 1
+            radius: Nheko.paddingSmall
+        }
+
+    }
+
+    P.MessageDialog {
+        id: successDialog
+
+        buttons: P.MessageDialog.Ok
+        text: qsTr("Encryption setup successfully")
+    }
+
+    P.MessageDialog {
+        id: failureDialog
+
+        property string errorMessage
+
+        buttons: P.MessageDialog.Ok
+        text: qsTr("Failed to setup encryption: %1").arg(errorMessage)
+    }
+
+    Dialog {
+        id: bootstrapCrosssigning
+
+        parent: Overlay.overlay
+        anchors.centerIn: parent
+        height: (Math.floor(parent.height / 2) - Nheko.paddingLarge) * 2
+        width: (Math.floor(parent.width / 2) - Nheko.paddingLarge) * 2
+        padding: 0
+        modal: true
+        standardButtons: Dialog.Ok | Dialog.Cancel
+        closePolicy: Popup.NoAutoClose
+        onAccepted: SelfVerificationStatus.setupCrosssigning(storeSecretsOnline.checked, usePassword.checked ? passwordField.text : "", useOnlineKeyBackup.checked)
+
+        ScrollView {
+            id: scroll
+
+            clip: true
+            anchors.fill: parent
+            ScrollBar.horizontal.visible: false
+            ScrollBar.vertical.visible: true
+
+            GridLayout {
+                id: grid
+
+                width: scroll.width - scroll.ScrollBar.vertical.width
+                columns: 2
+                rowSpacing: 0
+                columnSpacing: 0
+
+                Label {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.alignment: Qt.AlignHCenter
+                    Layout.columnSpan: 2
+                    font.pointSize: fontMetrics.font.pointSize * 2
+                    text: qsTr("Setup Encryption")
+                    color: Nheko.colors.text
+                    wrapMode: Text.Wrap
+                }
+
+                Label {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.alignment: Qt.AlignLeft
+                    Layout.columnSpan: 2
+                    Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
+                    text: qsTr("Hello and welcome to Matrix!\nIt seems like you are new. Before you can securely encrypt your messages, we need to setup a few small things. You can either press accept immediately or adjust a few basic options. We also try to explain a few of the basics. You can skip those parts, but they might prove to be helpful!")
+                    color: Nheko.colors.text
+                    wrapMode: Text.Wrap
+                }
+
+                Label {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.alignment: Qt.AlignLeft
+                    Layout.columnSpan: 1
+                    Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
+                    text: "Store secrets online.\nYou have a few secrets to make all the encryption magic work. While you can keep them stored only locally, we recommend storing them encrypted on the server. Otherwise it will be painful to recover them. Only disable this if you are paranoid and like losing your data!"
+                    color: Nheko.colors.text
+                    wrapMode: Text.Wrap
+                }
+
+                Item {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.preferredHeight: storeSecretsOnline.height
+                    Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
+                    Layout.fillWidth: true
+
+                    ToggleButton {
+                        id: storeSecretsOnline
+
+                        checked: true
+                        onClicked: console.log("Store secrets toggled: " + checked)
+                    }
+
+                }
+
+                Label {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.alignment: Qt.AlignLeft
+                    Layout.columnSpan: 1
+                    Layout.rowSpan: 2
+                    Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
+                    visible: storeSecretsOnline.checked
+                    text: "Set an online backup password.\nWe recommend you DON'T set a password and instead only rely on the recovery key. You will get a recovery key in any case when storing the cross-signing secrets online, but passwords are usually not very random, so they are easier to attack than a completely random recovery key. If you choose to use a password, DON'T make it the same as your login password, otherwise your server can read all your encrypted messages. (You don't want that.)"
+                    color: Nheko.colors.text
+                    wrapMode: Text.Wrap
+                }
+
+                Item {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.topMargin: Nheko.paddingLarge
+                    Layout.preferredHeight: storeSecretsOnline.height
+                    Layout.alignment: Qt.AlignLeft | Qt.AlignTop
+                    Layout.rowSpan: usePassword.checked ? 1 : 2
+                    Layout.fillWidth: true
+                    visible: storeSecretsOnline.checked
+
+                    ToggleButton {
+                        id: usePassword
+
+                        checked: false
+                    }
+
+                }
+
+                MatrixTextField {
+                    id: passwordField
+
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
+                    Layout.alignment: Qt.AlignLeft | Qt.AlignTop
+                    Layout.columnSpan: 1
+                    Layout.fillWidth: true
+                    visible: storeSecretsOnline.checked && usePassword.checked
+                    echoMode: TextInput.Password
+                }
+
+                Label {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.alignment: Qt.AlignLeft
+                    Layout.columnSpan: 1
+                    Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
+                    text: "Use online key backup.\nStore the keys for your messages securely encrypted online. In general you do want this, because it protects your messages from becoming unreadable, if you log out by accident. It does however carry a small security risk, if you ever share your recovery key by accident. Currently this also has some other weaknesses, that might allow the server to insert new keys into your backup. The server will however never be able to read your messages."
+                    color: Nheko.colors.text
+                    wrapMode: Text.Wrap
+                }
+
+                Item {
+                    Layout.margins: Nheko.paddingMedium
+                    Layout.preferredHeight: storeSecretsOnline.height
+                    Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
+                    Layout.fillWidth: true
+
+                    ToggleButton {
+                        id: useOnlineKeyBackup
+
+                        checked: true
+                        onClicked: console.log("Online key backup toggled: " + checked)
+                    }
+
+                }
+
+            }
+
+        }
+
+        background: Rectangle {
+            color: Nheko.colors.window
+            border.color: Nheko.theme.separator
+            border.width: 1
+            radius: Nheko.paddingSmall
+        }
+
+    }
+
+    Connections {
+        function onStatusChanged() {
+            console.log("STATUS CHANGED: " + SelfVerificationStatus.status);
+            if (SelfVerificationStatus.status == SelfVerificationStatus.NoMasterKey)
+                bootstrapCrosssigning.open();
+
+        }
+
+        function onShowRecoveryKey(key) {
+            showRecoverKeyDialog.recoveryKey = key;
+            showRecoverKeyDialog.open();
+        }
+
+        function onSetupCompleted() {
+            successDialog.open();
+        }
+
+        function onSetupFailed(m) {
+            failureDialog.errorMessage = m;
+            failureDialog.open();
+        }
+
+        target: SelfVerificationStatus
+    }
+
+}
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index 6c0a6da4..60154837 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -2,12 +2,12 @@
 //
 // SPDX-License-Identifier: GPL-3.0-or-later
 
+import Qt.labs.platform 1.1 as Platform
 import QtQuick 2.12
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.2
 import QtQuick.Window 2.13
 import im.nheko 1.0
-import Qt.labs.platform 1.1 as Platform
 
 Item {
     id: r
@@ -66,14 +66,8 @@ Item {
 
         TapHandler {
             acceptedButtons: Qt.RightButton
-            onLongPressed: replyContextMenu.show(
-              reply.child.copyText,
-              reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight)
-            )
-            onSingleTapped: replyContextMenu.show(
-              reply.child.copyText,
-              reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight)
-            )
+            onLongPressed: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight))
+            onSingleTapped: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight))
             gesturePolicy: TapHandler.ReleaseWithinBounds
         }
 
@@ -88,6 +82,7 @@ Item {
                 onSingleTapped: chat.model.openUserProfile(userId)
                 gesturePolicy: TapHandler.ReleaseWithinBounds
             }
+
         }
 
         MessageDelegate {
@@ -118,6 +113,7 @@ Item {
             width: parent.width
             isReply: true
         }
+
     }
 
     Rectangle {
diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index c37314fd..11ad3aeb 100644
--- a/resources/qml/delegates/TextMessage.qml
+++ b/resources/qml/delegates/TextMessage.qml
@@ -43,4 +43,5 @@ MatrixText {
         anchors.fill: parent
         cursorShape: Qt.PointingHandCursor
     }
+
 }
diff --git a/resources/qml/dialogs/InputDialog.qml b/resources/qml/dialogs/InputDialog.qml
index 1efdbcde..12211c60 100644
--- a/resources/qml/dialogs/InputDialog.qml
+++ b/resources/qml/dialogs/InputDialog.qml
@@ -12,6 +12,7 @@ ApplicationWindow {
     id: inputDialog
 
     property alias prompt: promptLabel.text
+    property alias echoMode: statusInput.echoMode
     property var onAccepted: undefined
 
     modality: Qt.NonModal
diff --git a/resources/qml/dialogs/RoomDirectory.qml b/resources/qml/dialogs/RoomDirectory.qml
index 5c27fc26..67842720 100644
--- a/resources/qml/dialogs/RoomDirectory.qml
+++ b/resources/qml/dialogs/RoomDirectory.qml
@@ -195,9 +195,9 @@ ApplicationWindow {
 
         MatrixTextField {
             id: chooseServer
+
             Layout.minimumWidth: 0.3 * header.width
             Layout.maximumWidth: 0.3 * header.width
-
             padding: Nheko.paddingMedium
             color: Nheko.colors.text
             placeholderText: qsTr("Choose custom homeserver")
diff --git a/resources/qml/dialogs/UserProfile.qml b/resources/qml/dialogs/UserProfile.qml
index d5442382..c921278e 100644
--- a/resources/qml/dialogs/UserProfile.qml
+++ b/resources/qml/dialogs/UserProfile.qml
@@ -35,8 +35,18 @@ ApplicationWindow {
         onActivated: userProfileDialog.close()
     }
 
-
     ListView {
+        id: devicelist
+
+        Layout.fillHeight: true
+        Layout.fillWidth: true
+        clip: true
+        spacing: 8
+        boundsBehavior: Flickable.StopAtBounds
+        model: profile.deviceList
+        anchors.fill: parent
+        anchors.margins: 10
+        footerPositioning: ListView.OverlayFooter
 
         ScrollHelper {
             flickable: parent
@@ -46,16 +56,17 @@ ApplicationWindow {
 
         header: ColumnLayout {
             id: contentL
-            width: devicelist.width
 
+            width: devicelist.width
             spacing: 10
 
             Avatar {
+                id: displayAvatar
+
                 url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
                 height: 130
                 width: 130
                 displayName: profile.displayName
-                id: displayAvatar
                 userid: profile.userid
                 Layout.alignment: Qt.AlignHCenter
                 onClicked: TimelineManager.openImageOverlay(profile.avatarUrl, "")
@@ -72,6 +83,7 @@ ApplicationWindow {
                     image: ":/icons/icons/ui/edit.png"
                     onClicked: profile.changeAvatar()
                 }
+
             }
 
             Spinner {
@@ -163,19 +175,22 @@ ApplicationWindow {
                 Layout.alignment: Qt.AlignHCenter
             }
 
-
             RowLayout {
                 visible: !profile.isGlobalUserProfile
                 Layout.alignment: Qt.AlignHCenter
                 spacing: Nheko.paddingSmall
+
                 MatrixText {
                     id: displayRoomname
-                    text: qsTr("Room: %1").arg(profile.room?profile.room.roomName:"")
+
+                    text: qsTr("Room: %1").arg(profile.room ? profile.room.roomName : "")
                     ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.")
                     ToolTip.visible: ma.hovered
+
                     HoverHandler {
                         id: ma
                     }
+
                 }
 
                 ImageButton {
@@ -185,6 +200,7 @@ ApplicationWindow {
                     ToolTip.text: qsTr("Open the global profile for this user.")
                     onClicked: profile.openGlobalProfile()
                 }
+
             }
 
             Button {
@@ -254,27 +270,18 @@ ApplicationWindow {
                     hoverEnabled: true
                     ToolTip.visible: hovered
                     ToolTip.text: qsTr("Refresh device list.")
-                    onClicked: profile.refreshDevices();
+                    onClicked: profile.refreshDevices()
                 }
 
             }
-        }
-
-        id: devicelist
-        Layout.fillHeight: true
-        Layout.fillWidth: true
-        clip: true
-        spacing: 8
-        boundsBehavior: Flickable.StopAtBounds
-        model: profile.deviceList
-        anchors.fill: parent
-        anchors.margins: 10
 
+        }
 
         delegate: RowLayout {
             required property int verificationStatus
             required property string deviceId
             required property string deviceName
+
             width: devicelist.width
             spacing: 4
 
@@ -304,7 +311,7 @@ ApplicationWindow {
                 Layout.preferredHeight: 16
                 Layout.preferredWidth: 16
                 source: {
-                    switch (verificationStatus){
+                    switch (verificationStatus) {
                     case VerificationStatus.VERIFIED:
                         return "image://colorimage/:/icons/icons/ui/lock.png?green";
                     case VerificationStatus.UNVERIFIED:
@@ -331,17 +338,19 @@ ApplicationWindow {
             }
 
         }
-        footerPositioning: ListView.OverlayFooter
+
         footer: DialogButtonBox {
             z: 2
             width: devicelist.width
             alignment: Qt.AlignRight
             standardButtons: DialogButtonBox.Ok
             onAccepted: userProfileDialog.close()
+
             background: Rectangle {
                 anchors.fill: parent
                 color: Nheko.colors.window
             }
+
         }
 
     }
diff --git a/resources/res.qrc b/resources/res.qrc
index c18a6109..173206a7 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -138,6 +138,7 @@
         <file>qml/TopBar.qml</file>
         <file>qml/QuickSwitcher.qml</file>
         <file>qml/ForwardCompleter.qml</file>
+        <file>qml/SelfVerificationCheck.qml</file>
         <file>qml/TypingIndicator.qml</file>
         <file>qml/NotificationWarning.qml</file>
         <file>qml/emoji/EmojiPicker.qml</file>