From 2e0afde8fe3eef540608424cf538fc9ec700483a Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 9 Mar 2020 00:25:00 +0100 Subject: [PATCH 01/28] First design iteration of device verification dialogs --- .../DeviceVerification.qml | 392 ++++++++++++++++++ .../DeviceVerificationTest.qml | 13 + .../qml/device-verification/sas-emoji.json | 66 +++ 3 files changed, 471 insertions(+) create mode 100644 resources/qml/device-verification/DeviceVerification.qml create mode 100644 resources/qml/device-verification/DeviceVerificationTest.qml create mode 100644 resources/qml/device-verification/sas-emoji.json diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml new file mode 100644 index 000000000..c32e3414b --- /dev/null +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -0,0 +1,392 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.10 + +Window { + title: stack.currentItem.title + id: dialog + + flags: Qt.Dialog + + + height: stack.implicitHeight + width: stack.implicitWidth + StackView { + id: stack + initialItem: newVerificationRequest + implicitWidth: currentItem.implicitWidth + implicitHeight: currentItem.implicitHeight + } + + onClosing: stack.replace(newVerificationRequest) + + Component { + id: newVerificationRequest + Pane { + property string title: "Device Verification Request" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "A new device was added." + + verticalAlignment: Text.AlignVCenter + } + + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "The device may have been added by you signing in from another client or physical device. To ensure that no malicious user can eavesdrop on your encrypted communications, you should verify the new device." + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Cancel" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Start verification" + onClicked: stack.replace(awaitingVerificationRequestAccept) + } + } + } + } + } + + Component { + id: awaitingVerificationRequestAccept + Pane { + property string title: "Waiting for other party" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Waiting for other side to accept the verification request." + + verticalAlignment: Text.AlignVCenter + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Cancel" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + } + Timer { + // temporary, until it is bound to a backend + interval: 5000; running: true; + onTriggered: if (Math.random() > 0.5) stack.replace(emojiVerification); else stack.replace(digitVerification); + } + } + } + } + + Component { + id: digitVerification + Pane { + property string title: "Verification Code" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Layout.alignment: Qt.AlignHCenter + Text { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: "1234" + } + Text { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: "1234" + } + Text { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: "1234" + } + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "They do not match!" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "They match." + onClicked: stack.replace(awaitingVerificationConfirmation) + } + } + } + } + } + + Component { + id: emojiVerification + Pane { + property string title: "Verification Code" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Layout.alignment: Qt.AlignHCenter + + id: emojis + + property var mapping: [ + {"number": 0, "emoji": "🐶", "description": "Dog", "unicode": "U+1F436"}, + {"number": 1, "emoji": "🐱", "description": "Cat", "unicode": "U+1F431"}, + {"number": 2, "emoji": "🦁", "description": "Lion", "unicode": "U+1F981"}, + {"number": 3, "emoji": "🐎", "description": "Horse", "unicode": "U+1F40E"}, + {"number": 4, "emoji": "🦄", "description": "Unicorn", "unicode": "U+1F984"}, + {"number": 5, "emoji": "🐷", "description": "Pig", "unicode": "U+1F437"}, + {"number": 6, "emoji": "🐘", "description": "Elephant", "unicode": "U+1F418"}, + {"number": 7, "emoji": "🐰", "description": "Rabbit", "unicode": "U+1F430"}, + {"number": 8, "emoji": "🐼", "description": "Panda", "unicode": "U+1F43C"}, + {"number": 9, "emoji": "🐓", "description": "Rooster", "unicode": "U+1F413"}, + {"number": 10, "emoji": "🐧", "description": "Penguin", "unicode": "U+1F427"}, + {"number": 11, "emoji": "🐢", "description": "Turtle", "unicode": "U+1F422"}, + {"number": 12, "emoji": "🐟", "description": "Fish", "unicode": "U+1F41F"}, + {"number": 13, "emoji": "🐙", "description": "Octopus", "unicode": "U+1F419"}, + {"number": 14, "emoji": "🦋", "description": "Butterfly", "unicode": "U+1F98B"}, + {"number": 15, "emoji": "🌷", "description": "Flower", "unicode": "U+1F337"}, + {"number": 16, "emoji": "🌳", "description": "Tree", "unicode": "U+1F333"}, + {"number": 17, "emoji": "🌵", "description": "Cactus", "unicode": "U+1F335"}, + {"number": 18, "emoji": "🍄", "description": "Mushroom", "unicode": "U+1F344"}, + {"number": 19, "emoji": "🌏", "description": "Globe", "unicode": "U+1F30F"}, + {"number": 20, "emoji": "🌙", "description": "Moon", "unicode": "U+1F319"}, + {"number": 21, "emoji": "☁️", "description": "Cloud", "unicode": "U+2601U+FE0F"}, + {"number": 22, "emoji": "🔥", "description": "Fire", "unicode": "U+1F525"}, + {"number": 23, "emoji": "🍌", "description": "Banana", "unicode": "U+1F34C"}, + {"number": 24, "emoji": "🍎", "description": "Apple", "unicode": "U+1F34E"}, + {"number": 25, "emoji": "🍓", "description": "Strawberry", "unicode": "U+1F353"}, + {"number": 26, "emoji": "🌽", "description": "Corn", "unicode": "U+1F33D"}, + {"number": 27, "emoji": "🍕", "description": "Pizza", "unicode": "U+1F355"}, + {"number": 28, "emoji": "🎂", "description": "Cake", "unicode": "U+1F382"}, + {"number": 29, "emoji": "❤️", "description": "Heart", "unicode": "U+2764U+FE0F"}, + {"number": 30, "emoji": "😀", "description": "Smiley", "unicode": "U+1F600"}, + {"number": 31, "emoji": "🤖", "description": "Robot", "unicode": "U+1F916"}, + {"number": 32, "emoji": "🎩", "description": "Hat", "unicode": "U+1F3A9"}, + {"number": 33, "emoji": "👓", "description": "Glasses", "unicode": "U+1F453"}, + {"number": 34, "emoji": "🔧", "description": "Spanner", "unicode": "U+1F527"}, + {"number": 35, "emoji": "🎅", "description": "Santa", "unicode": "U+1F385"}, + {"number": 36, "emoji": "👍", "description": "Thumbs Up", "unicode": "U+1F44D"}, + {"number": 37, "emoji": "☂️", "description": "Umbrella", "unicode": "U+2602U+FE0F"}, + {"number": 38, "emoji": "⌛", "description": "Hourglass", "unicode": "U+231B"}, + {"number": 39, "emoji": "⏰", "description": "Clock", "unicode": "U+23F0"}, + {"number": 40, "emoji": "🎁", "description": "Gift", "unicode": "U+1F381"}, + {"number": 41, "emoji": "💡", "description": "Light Bulb", "unicode": "U+1F4A1"}, + {"number": 42, "emoji": "📕", "description": "Book", "unicode": "U+1F4D5"}, + {"number": 43, "emoji": "✏️", "description": "Pencil", "unicode": "U+270FU+FE0F"}, + {"number": 44, "emoji": "📎", "description": "Paperclip", "unicode": "U+1F4CE"}, + {"number": 45, "emoji": "✂️", "description": "Scissors", "unicode": "U+2702U+FE0F"}, + {"number": 46, "emoji": "🔒", "description": "Lock", "unicode": "U+1F512"}, + {"number": 47, "emoji": "🔑", "description": "Key", "unicode": "U+1F511"}, + {"number": 48, "emoji": "🔨", "description": "Hammer", "unicode": "U+1F528"}, + {"number": 49, "emoji": "☎️", "description": "Telephone", "unicode": "U+260EU+FE0F"}, + {"number": 50, "emoji": "🏁", "description": "Flag", "unicode": "U+1F3C1"}, + {"number": 51, "emoji": "🚂", "description": "Train", "unicode": "U+1F682"}, + {"number": 52, "emoji": "🚲", "description": "Bicycle", "unicode": "U+1F6B2"}, + {"number": 53, "emoji": "✈️", "description": "Aeroplane", "unicode": "U+2708U+FE0F"}, + {"number": 54, "emoji": "🚀", "description": "Rocket", "unicode": "U+1F680"}, + {"number": 55, "emoji": "🏆", "description": "Trophy", "unicode": "U+1F3C6"}, + {"number": 56, "emoji": "⚽", "description": "Ball", "unicode": "U+26BD"}, + {"number": 57, "emoji": "🎸", "description": "Guitar", "unicode": "U+1F3B8"}, + {"number": 58, "emoji": "🎺", "description": "Trumpet", "unicode": "U+1F3BA"}, + {"number": 59, "emoji": "🔔", "description": "Bell", "unicode": "U+1F514"}, + {"number": 60, "emoji": "⚓", "description": "Anchor", "unicode": "U+2693"}, + {"number": 61, "emoji": "🎧", "description": "Headphones", "unicode": "U+1F3A7"}, + {"number": 62, "emoji": "📁", "description": "Folder", "unicode": "U+1F4C1"}, + {"number": 63, "emoji": "📌", "description": "Pin", "unicode": "U+1F4CC"} + ] + + Repeater { + id: repeater + model: 7 + delegate: Rectangle { + color: "red" + implicitHeight: Qt.application.font.pixelSize * 8 + implicitWidth: col.width + ColumnLayout { + id: col + anchors.bottom: parent.bottom + property var emoji: emojis.mapping[Math.floor(Math.random()*64)] + Text { + height: font.pixelSize * 2 + Layout.alignment: Qt.AlignHCenter + text: col.emoji.emoji + font.pixelSize: Qt.application.font.pixelSize * 4 + } + Text { + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + text: col.emoji.description + } + } + } + } + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "They do not match!" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "They match." + onClicked: stack.replace(awaitingVerificationConfirmation) + } + } + } + } + } + + Component { + id: awaitingVerificationConfirmation + Pane { + property string title: "Awaiting Confirmation" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Waiting for other side to complete verification." + + verticalAlignment: Text.AlignVCenter + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Cancel" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + } + Timer { + // temporary, until it is bound to a backend + interval: 5000; running: true; + onTriggered: Math.random() > 0.5 ? stack.replace(verificationSuccess) : stack.replace(partnerAborted) + } + } + } + } + + Component { + id: verificationSuccess + Pane { + property string title: "Successful Verification" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Verification successful! Both sides verified their devices!" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Close" + onClicked: dialog.close() + } + } + } + } + } + + Component { + id: partnerAborted + Pane { + property string title: "Verification aborted!" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Verification canceled by the other party!" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Close" + onClicked: dialog.close() + } + } + } + } + } +} diff --git a/resources/qml/device-verification/DeviceVerificationTest.qml b/resources/qml/device-verification/DeviceVerificationTest.qml new file mode 100644 index 000000000..6682e7ece --- /dev/null +++ b/resources/qml/device-verification/DeviceVerificationTest.qml @@ -0,0 +1,13 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +Item { + DeviceVerification { + id: deviceVerification + } + + Button { + text: "Test DeviceVerification" + onClicked: deviceVerification.show() + } +} diff --git a/resources/qml/device-verification/sas-emoji.json b/resources/qml/device-verification/sas-emoji.json new file mode 100644 index 000000000..060fbd490 --- /dev/null +++ b/resources/qml/device-verification/sas-emoji.json @@ -0,0 +1,66 @@ +[ + {"number": 0, "emoji": "🐶", "description": "Dog", "unicode": "U+1F436"}, + {"number": 1, "emoji": "🐱", "description": "Cat", "unicode": "U+1F431"}, + {"number": 2, "emoji": "🦁", "description": "Lion", "unicode": "U+1F981"}, + {"number": 3, "emoji": "🐎", "description": "Horse", "unicode": "U+1F40E"}, + {"number": 4, "emoji": "🦄", "description": "Unicorn", "unicode": "U+1F984"}, + {"number": 5, "emoji": "🐷", "description": "Pig", "unicode": "U+1F437"}, + {"number": 6, "emoji": "🐘", "description": "Elephant", "unicode": "U+1F418"}, + {"number": 7, "emoji": "🐰", "description": "Rabbit", "unicode": "U+1F430"}, + {"number": 8, "emoji": "🐼", "description": "Panda", "unicode": "U+1F43C"}, + {"number": 9, "emoji": "🐓", "description": "Rooster", "unicode": "U+1F413"}, + {"number": 10, "emoji": "🐧", "description": "Penguin", "unicode": "U+1F427"}, + {"number": 11, "emoji": "🐢", "description": "Turtle", "unicode": "U+1F422"}, + {"number": 12, "emoji": "🐟", "description": "Fish", "unicode": "U+1F41F"}, + {"number": 13, "emoji": "🐙", "description": "Octopus", "unicode": "U+1F419"}, + {"number": 14, "emoji": "🦋", "description": "Butterfly", "unicode": "U+1F98B"}, + {"number": 15, "emoji": "🌷", "description": "Flower", "unicode": "U+1F337"}, + {"number": 16, "emoji": "🌳", "description": "Tree", "unicode": "U+1F333"}, + {"number": 17, "emoji": "🌵", "description": "Cactus", "unicode": "U+1F335"}, + {"number": 18, "emoji": "🍄", "description": "Mushroom", "unicode": "U+1F344"}, + {"number": 19, "emoji": "🌏", "description": "Globe", "unicode": "U+1F30F"}, + {"number": 20, "emoji": "🌙", "description": "Moon", "unicode": "U+1F319"}, + {"number": 21, "emoji": "☁️", "description": "Cloud", "unicode": "U+2601U+FE0F"}, + {"number": 22, "emoji": "🔥", "description": "Fire", "unicode": "U+1F525"}, + {"number": 23, "emoji": "🍌", "description": "Banana", "unicode": "U+1F34C"}, + {"number": 24, "emoji": "🍎", "description": "Apple", "unicode": "U+1F34E"}, + {"number": 25, "emoji": "🍓", "description": "Strawberry", "unicode": "U+1F353"}, + {"number": 26, "emoji": "🌽", "description": "Corn", "unicode": "U+1F33D"}, + {"number": 27, "emoji": "🍕", "description": "Pizza", "unicode": "U+1F355"}, + {"number": 28, "emoji": "🎂", "description": "Cake", "unicode": "U+1F382"}, + {"number": 29, "emoji": "❤️", "description": "Heart", "unicode": "U+2764U+FE0F"}, + {"number": 30, "emoji": "😀", "description": "Smiley", "unicode": "U+1F600"}, + {"number": 31, "emoji": "🤖", "description": "Robot", "unicode": "U+1F916"}, + {"number": 32, "emoji": "🎩", "description": "Hat", "unicode": "U+1F3A9"}, + {"number": 33, "emoji": "👓", "description": "Glasses", "unicode": "U+1F453"}, + {"number": 34, "emoji": "🔧", "description": "Spanner", "unicode": "U+1F527"}, + {"number": 35, "emoji": "🎅", "description": "Santa", "unicode": "U+1F385"}, + {"number": 36, "emoji": "👍", "description": "Thumbs Up", "unicode": "U+1F44D"}, + {"number": 37, "emoji": "☂️", "description": "Umbrella", "unicode": "U+2602U+FE0F"}, + {"number": 38, "emoji": "⌛", "description": "Hourglass", "unicode": "U+231B"}, + {"number": 39, "emoji": "⏰", "description": "Clock", "unicode": "U+23F0"}, + {"number": 40, "emoji": "🎁", "description": "Gift", "unicode": "U+1F381"}, + {"number": 41, "emoji": "💡", "description": "Light Bulb", "unicode": "U+1F4A1"}, + {"number": 42, "emoji": "📕", "description": "Book", "unicode": "U+1F4D5"}, + {"number": 43, "emoji": "✏️", "description": "Pencil", "unicode": "U+270FU+FE0F"}, + {"number": 44, "emoji": "📎", "description": "Paperclip", "unicode": "U+1F4CE"}, + {"number": 45, "emoji": "✂️", "description": "Scissors", "unicode": "U+2702U+FE0F"}, + {"number": 46, "emoji": "🔒", "description": "Lock", "unicode": "U+1F512"}, + {"number": 47, "emoji": "🔑", "description": "Key", "unicode": "U+1F511"}, + {"number": 48, "emoji": "🔨", "description": "Hammer", "unicode": "U+1F528"}, + {"number": 49, "emoji": "☎️", "description": "Telephone", "unicode": "U+260EU+FE0F"}, + {"number": 50, "emoji": "🏁", "description": "Flag", "unicode": "U+1F3C1"}, + {"number": 51, "emoji": "🚂", "description": "Train", "unicode": "U+1F682"}, + {"number": 52, "emoji": "🚲", "description": "Bicycle", "unicode": "U+1F6B2"}, + {"number": 53, "emoji": "✈️", "description": "Aeroplane", "unicode": "U+2708U+FE0F"}, + {"number": 54, "emoji": "🚀", "description": "Rocket", "unicode": "U+1F680"}, + {"number": 55, "emoji": "🏆", "description": "Trophy", "unicode": "U+1F3C6"}, + {"number": 56, "emoji": "⚽", "description": "Ball", "unicode": "U+26BD"}, + {"number": 57, "emoji": "🎸", "description": "Guitar", "unicode": "U+1F3B8"}, + {"number": 58, "emoji": "🎺", "description": "Trumpet", "unicode": "U+1F3BA"}, + {"number": 59, "emoji": "🔔", "description": "Bell", "unicode": "U+1F514"}, + {"number": 60, "emoji": "⚓", "description": "Anchor", "unicode": "U+2693"}, + {"number": 61, "emoji": "🎧", "description": "Headphones", "unicode": "U+1F3A7"}, + {"number": 62, "emoji": "📁", "description": "Folder", "unicode": "U+1F4C1"}, + {"number": 63, "emoji": "📌", "description": "Pin", "unicode": "U+1F4CC"} +] From b6997dbafd28583abdbd85335c7f4ff3a7f6f2df Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 13 Mar 2020 21:05:18 +0100 Subject: [PATCH 02/28] Add DeviceVerificationFlow dummy and verification test button --- CMakeLists.txt | 2 + resources/qml/TimelineView.qml | 19 +++++ .../DeviceVerification.qml | 77 ++++++++++++++----- .../DeviceVerificationTest.qml | 13 ---- resources/res.qrc | 1 + src/DeviceVerificationFlow.cpp | 36 +++++++++ src/DeviceVerificationFlow.h | 38 +++++++++ src/timeline/TimelineViewManager.cpp | 7 ++ src/timeline/TimelineViewManager.h | 3 + 9 files changed, 163 insertions(+), 33 deletions(-) delete mode 100644 resources/qml/device-verification/DeviceVerificationTest.qml create mode 100644 src/DeviceVerificationFlow.cpp create mode 100644 src/DeviceVerificationFlow.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 04baf360d..7d3c814fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,6 +285,7 @@ set(SRC_FILES src/ColorImageProvider.cpp src/CommunitiesList.cpp src/CommunitiesListItem.cpp + src/DeviceVerificationFlow.cpp src/EventAccessors.cpp src/InviteeItem.cpp src/Logging.cpp @@ -488,6 +489,7 @@ qt5_wrap_cpp(MOC_HEADERS src/ChatPage.h src/CommunitiesList.h src/CommunitiesListItem.h + src/DeviceVerificationFlow.h src/InviteeItem.h src/LoginPage.h src/MainWindow.h diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index f3d5d2196..ad2fcebda 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -9,6 +9,7 @@ import im.nheko.EmojiModel 1.0 import "./delegates" import "./emoji" +import "./device-verification" Page { property var colors: currentActivePalette @@ -93,6 +94,24 @@ Page { anchors.fill: parent color: colors.window + Component { + id: deviceVerificationDialog + DeviceVerification {} + } + Connections { + target: timelineManager + onDeviceVerificationRequest: { + var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: deviceVerificationFlow}); + dialog.show(); + } + } + + Button { + text: "test device verification" + onClicked: timelineManager.startDummyVerification() + z: 5 + } + Label { visible: !timelineManager.timeline && !timelineManager.isInitialSync anchors.centerIn: parent diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index c32e3414b..d87523160 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -3,12 +3,15 @@ import QtQuick.Controls 2.10 import QtQuick.Window 2.2 import QtQuick.Layouts 1.10 -Window { +import im.nheko 1.0 + +ApplicationWindow { title: stack.currentItem.title id: dialog flags: Qt.Dialog + palette: colors height: stack.implicitHeight width: stack.implicitWidth @@ -21,6 +24,19 @@ Window { onClosing: stack.replace(newVerificationRequest) + property var flow + Connections { + target: flow + onVerificationCanceled: stack.replace(partnerAborted) + onTimedout: stack.replace(timedout) + onDeviceVerified: stack.replace(verificationSuccess) + + onVerificationRequestAccepted: switch(method) { + case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break; + case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break; + } + } + Component { id: newVerificationRequest Pane { @@ -51,7 +67,7 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true @@ -59,7 +75,7 @@ Window { Button { Layout.alignment: Qt.AlignRight text: "Start verification" - onClicked: stack.replace(awaitingVerificationRequestAccept) + onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.acceptVerificationRequest(); } } } } @@ -90,17 +106,12 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true } } - Timer { - // temporary, until it is bound to a backend - interval: 5000; running: true; - onTriggered: if (Math.random() > 0.5) stack.replace(emojiVerification); else stack.replace(digitVerification); - } } } } @@ -141,7 +152,7 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true @@ -149,7 +160,7 @@ Window { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: stack.replace(awaitingVerificationConfirmation) + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } } } } @@ -248,7 +259,7 @@ Window { id: repeater model: 7 delegate: Rectangle { - color: "red" + color: "transparent" implicitHeight: Qt.application.font.pixelSize * 8 implicitWidth: col.width ColumnLayout { @@ -274,7 +285,7 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true @@ -282,7 +293,7 @@ Window { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: stack.replace(awaitingVerificationConfirmation) + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } } } } @@ -313,17 +324,12 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true } } - Timer { - // temporary, until it is bound to a backend - interval: 5000; running: true; - onTriggered: Math.random() > 0.5 ? stack.replace(verificationSuccess) : stack.replace(partnerAborted) - } } } } @@ -389,4 +395,35 @@ Window { } } } + + Component { + id: timedout + Pane { + property string title: "Verification timed out" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Device verification timed out." + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Close" + onClicked: dialog.close() + } + } + } + } + } } diff --git a/resources/qml/device-verification/DeviceVerificationTest.qml b/resources/qml/device-verification/DeviceVerificationTest.qml deleted file mode 100644 index 6682e7ece..000000000 --- a/resources/qml/device-verification/DeviceVerificationTest.qml +++ /dev/null @@ -1,13 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -Item { - DeviceVerification { - id: deviceVerification - } - - Button { - text: "Test DeviceVerification" - onClicked: deviceVerification.show() - } -} diff --git a/resources/res.qrc b/resources/res.qrc index 439ed97bb..ec086b3a2 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -135,5 +135,6 @@ qml/delegates/Pill.qml qml/delegates/Placeholder.qml qml/delegates/Reply.qml + qml/device-verification/DeviceVerification.qml diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp new file mode 100644 index 000000000..69d6ab9cc --- /dev/null +++ b/src/DeviceVerificationFlow.cpp @@ -0,0 +1,36 @@ +#include "DeviceVerificationFlow.h" + +#include + +static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes + +DeviceVerificationFlow::DeviceVerificationFlow(QObject *) +{ + timeout = new QTimer(this); + timeout->setSingleShot(true); + connect(timeout, &QTimer::timeout, this, [this]() { + emit timedout(); + this->deleteLater(); + }); + timeout->start(TIMEOUT); +} + +//! accepts a verification and starts the verification flow +void +DeviceVerificationFlow::acceptVerificationRequest() +{ + emit verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); +} +//! cancels a verification flow +void +DeviceVerificationFlow::cancelVerification() +{ + this->deleteLater(); +} +//! Completes the verification flow +void +DeviceVerificationFlow::acceptDevice() +{ + emit deviceVerified(); + this->deleteLater(); +} diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h new file mode 100644 index 000000000..038f1e13a --- /dev/null +++ b/src/DeviceVerificationFlow.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +class QTimer; + +class DeviceVerificationFlow : public QObject +{ + Q_OBJECT + // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + +public: + enum Method + { + Decimal, + Emoji + }; + Q_ENUM(Method) + + DeviceVerificationFlow(QObject *parent = nullptr); + +public slots: + //! accepts a verification and starts the verification flow + void acceptVerificationRequest(); + //! cancels a verification flow + void cancelVerification(); + //! Completes the verification flow + void acceptDevice(); + +signals: + void verificationRequestAccepted(Method method); + void deviceVerified(); + void timedout(); + void verificationCanceled(); + +private: + QTimer *timeout = nullptr; +}; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 151cfb4e1..74bffcfdf 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -85,6 +85,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin "Can't instantiate enum!"); qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); + qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); qRegisterMetaType(); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); @@ -440,3 +441,9 @@ TimelineViewManager::queueVideoMessage(const QString &roomid, model->sendMessage(video); } + +void +TimelineViewManager::startDummyVerification() +{ + emit deviceVerificationRequest(new DeviceVerificationFlow(this)); +} diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index ed0950589..a41747f5b 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -10,6 +10,7 @@ #include #include "Cache.h" +#include "DeviceVerificationFlow.h" #include "Logging.h" #include "TimelineModel.h" #include "Utils.h" @@ -43,6 +44,7 @@ class TimelineViewManager : public QObject Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); + Q_INVOKABLE void startDummyVerification(); Q_INVOKABLE QString userPresence(QString id) const; Q_INVOKABLE QString userStatus(QString id) const; @@ -54,6 +56,7 @@ class TimelineViewManager : public QObject void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); + void deviceVerificationRequest(DeviceVerificationFlow *deviceVerificationFlow); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); From 01a7e0ca15185023dda1ede416fde5470601a3e4 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 14 Mar 2020 11:17:10 +0100 Subject: [PATCH 03/28] Set proper emoji font for device verification --- resources/qml/device-verification/DeviceVerification.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index d87523160..d0f444639 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -2,6 +2,7 @@ import QtQuick 2.3 import QtQuick.Controls 2.10 import QtQuick.Window 2.2 import QtQuick.Layouts 1.10 +import Qt.labs.settings 1.0 import im.nheko 1.0 @@ -13,6 +14,12 @@ ApplicationWindow { palette: colors + Settings { + id: settings + category: "user" + property bool emoji_font_family: true + } + height: stack.implicitHeight width: stack.implicitWidth StackView { @@ -271,6 +278,7 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji font.pixelSize: Qt.application.font.pixelSize * 4 + font.family: settings.emoji_font_family } Text { Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom From b3495bda0a0275195437caa971852efbbebc3e13 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 20 Mar 2020 21:07:26 +0100 Subject: [PATCH 04/28] Make emojis a bit smaller --- .../DeviceVerification.qml | 6 ++--- .../qml/device-verification/EmojiElement.qml | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 resources/qml/device-verification/EmojiElement.qml diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index d0f444639..f8fe2bd4a 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -267,17 +267,17 @@ ApplicationWindow { model: 7 delegate: Rectangle { color: "transparent" - implicitHeight: Qt.application.font.pixelSize * 8 + implicitHeight: Qt.application.font.pixelSize * 3 implicitWidth: col.width ColumnLayout { id: col anchors.bottom: parent.bottom property var emoji: emojis.mapping[Math.floor(Math.random()*64)] Text { - height: font.pixelSize * 2 + //height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji - font.pixelSize: Qt.application.font.pixelSize * 4 + font.pixelSize: Qt.application.font.pixelSize * 2 font.family: settings.emoji_font_family } Text { diff --git a/resources/qml/device-verification/EmojiElement.qml b/resources/qml/device-verification/EmojiElement.qml new file mode 100644 index 000000000..fa207b91b --- /dev/null +++ b/resources/qml/device-verification/EmojiElement.qml @@ -0,0 +1,25 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.10 + +Rectangle { + color: "red" + implicitHeight: Qt.application.font.pixelSize * 4 + implicitWidth: col.width + height: Qt.application.font.pixelSize * 4 + width: col.width + ColumnLayout { + id: col + anchors.bottom: parent.bottom + property var emoji: emojis.mapping[Math.floor(Math.random()*64)] + Text { + height: font.pixelSize * 2 + Layout.alignment: Qt.AlignHCenter + text: col.emoji.emoji + font.pixelSize: Qt.application.font.pixelSize * 2 + } + Text { + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + text: col.emoji.description + } + } +} From 42a7ac215336da266d870e13235e3d74f30b53ac Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 31 Mar 2020 00:47:56 +0200 Subject: [PATCH 05/28] Use label in device verification dialogs (for proper theming) --- .../DeviceVerification.qml | 28 +++++++++---------- .../qml/device-verification/EmojiElement.qml | 4 +-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index f8fe2bd4a..19e0c7dde 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -50,7 +50,7 @@ ApplicationWindow { property string title: "Device Verification Request" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -60,7 +60,7 @@ ApplicationWindow { verticalAlignment: Text.AlignVCenter } - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -95,7 +95,7 @@ ApplicationWindow { property string title: "Waiting for other party" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -129,7 +129,7 @@ ApplicationWindow { property string title: "Verification Code" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -141,15 +141,15 @@ ApplicationWindow { RowLayout { Layout.alignment: Qt.AlignHCenter - Text { + Label { font.pixelSize: Qt.application.font.pixelSize * 2 text: "1234" } - Text { + Label { font.pixelSize: Qt.application.font.pixelSize * 2 text: "1234" } - Text { + Label { font.pixelSize: Qt.application.font.pixelSize * 2 text: "1234" } @@ -180,7 +180,7 @@ ApplicationWindow { property string title: "Verification Code" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -273,14 +273,14 @@ ApplicationWindow { id: col anchors.bottom: parent.bottom property var emoji: emojis.mapping[Math.floor(Math.random()*64)] - Text { + Label { //height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji font.pixelSize: Qt.application.font.pixelSize * 2 font.family: settings.emoji_font_family } - Text { + Label { Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom text: col.emoji.description } @@ -314,7 +314,7 @@ ApplicationWindow { property string title: "Awaiting Confirmation" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -348,7 +348,7 @@ ApplicationWindow { property string title: "Successful Verification" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -379,7 +379,7 @@ ApplicationWindow { property string title: "Verification aborted!" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -410,7 +410,7 @@ ApplicationWindow { property string title: "Verification timed out" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true diff --git a/resources/qml/device-verification/EmojiElement.qml b/resources/qml/device-verification/EmojiElement.qml index fa207b91b..22f9e414e 100644 --- a/resources/qml/device-verification/EmojiElement.qml +++ b/resources/qml/device-verification/EmojiElement.qml @@ -11,13 +11,13 @@ Rectangle { id: col anchors.bottom: parent.bottom property var emoji: emojis.mapping[Math.floor(Math.random()*64)] - Text { + Label { height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji font.pixelSize: Qt.application.font.pixelSize * 2 } - Text { + Label { Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom text: col.emoji.description } From 689894f45c9c0d2ec47042e55ad5a92d138b9f46 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 13 Mar 2020 21:05:18 +0100 Subject: [PATCH 06/28] Add DeviceVerificationFlow dummy and verification test button --- resources/qml/device-verification/DeviceVerification.qml | 8 ++++++++ src/timeline/TimelineViewManager.cpp | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 19e0c7dde..2c9486ae2 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -267,7 +267,11 @@ ApplicationWindow { model: 7 delegate: Rectangle { color: "transparent" +<<<<<<< HEAD implicitHeight: Qt.application.font.pixelSize * 3 +======= + implicitHeight: Qt.application.font.pixelSize * 8 +>>>>>>> Add DeviceVerificationFlow dummy and verification test button implicitWidth: col.width ColumnLayout { id: col @@ -410,7 +414,11 @@ ApplicationWindow { property string title: "Verification timed out" ColumnLayout { spacing: 16 +<<<<<<< HEAD Label { +======= + Text { +>>>>>>> Add DeviceVerificationFlow dummy and verification test button Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 74bffcfdf..6ad3cee1c 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -447,3 +447,9 @@ TimelineViewManager::startDummyVerification() { emit deviceVerificationRequest(new DeviceVerificationFlow(this)); } + +void +TimelineViewManager::startDummyVerification() +{ + emit deviceVerificationRequest(new DeviceVerificationFlow(this)); +} From 07823f83657a3da58d1a98e234231ef1f1cfcbd5 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sun, 17 May 2020 19:04:47 +0530 Subject: [PATCH 07/28] Rewrite UserProfile in qml --- CMakeLists.txt | 2 + resources/qml/TimelineView.qml | 13 +++++- resources/qml/UserProfile.qml | 85 ++++++++++++++++++++++++++++++++++ resources/res.qrc | 1 + src/Olm.cpp | 16 +++++++ src/ui/UserProfile.cpp | 58 +++++++++++++++++++++++ src/ui/UserProfile.h | 29 ++++++++++++ 7 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 resources/qml/UserProfile.qml create mode 100644 src/ui/UserProfile.cpp create mode 100644 src/ui/UserProfile.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d3c814fe..a1c2b7983 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -277,6 +277,7 @@ set(SRC_FILES src/ui/ToggleButton.cpp src/ui/Theme.cpp src/ui/ThemeManager.cpp + src/ui/UserProfile.cpp src/AvatarProvider.cpp src/BlurhashProvider.cpp @@ -480,6 +481,7 @@ qt5_wrap_cpp(MOC_HEADERS src/ui/ToggleButton.h src/ui/Theme.h src/ui/ThemeManager.h + src/ui/UserProfile.h src/notifications/Manager.h diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index ad2fcebda..bf26341db 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -280,7 +280,10 @@ Page { MouseArea { anchors.fill: parent - onClicked: chat.model.openUserProfile(modelData.userId) + onClicked: { + userProfile.user_data = modelData + userProfile.show() + } cursorShape: Qt.PointingHandCursor propagateComposedEvents: true } @@ -294,7 +297,10 @@ Page { MouseArea { anchors.fill: parent - onClicked: chat.model.openUserProfile(section.split(" ")[0]) + onClicked: { + userProfile.user_data = modelData + userProfile.show() + } cursorShape: Qt.PointingHandCursor propagateComposedEvents: true } @@ -308,6 +314,9 @@ Page { width: chat.delegateMaxWidth - parent.spacing*2 - userName.implicitWidth - avatarSize font.italic: true } + UserProfile{ + id: userProfile + } } } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml new file mode 100644 index 000000000..f019ee250 --- /dev/null +++ b/resources/qml/UserProfile.qml @@ -0,0 +1,85 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Window 2.3 + +ApplicationWindow{ + property var user_data + property var colors: currentActivePalette + + id:userProfileDialog + height: 500 + width: 500 + modality:Qt.WindowModal + Layout.alignment: Qt.AlignHCenter + palette: colors + + onAfterRendering: { + userProfileAvatar.url = chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") + userProfileName.text = user_data.userName + matrixUserID.text = user_data.userId + console.log("this is happening"); + } + + background: Item{ + id: userProfileItem + width: userProfileDialog.width + height: userProfileDialog.height + anchors.margins: { + top:20 + } + + ColumnLayout{ + anchors.fill: userProfileItem + width: userProfileDialog.width + spacing: 10 + + Avatar{ + id: userProfileAvatar + height: 130 + width: 130 + displayName: modelData.userName + Layout.alignment: Qt.AlignHCenter + } + + Label{ + id: userProfileName + fontSizeMode: Text.HorizontalFit + Layout.alignment: Qt.AlignHCenter + } + + Label{ + id: matrixUserID + fontSizeMode: Text.HorizontalFit + Layout.alignment: Qt.AlignHCenter + } + + ScrollView { + implicitHeight: userProfileDialog.height/2+20 + implicitWidth: userProfileDialog.width-20 + clip: true + Layout.alignment: Qt.AlignHCenter + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + Label { + text: "ABC" + font.pixelSize: 700 + } + } + + Button{ + text:"OK" + onClicked: userProfileDialog.close() + anchors.margins: { + right:10 + bottom:10 + } + + Layout.alignment: Qt.AlignRight | Qt.AlignBottom + } + } + + Item { Layout.fillHeight: true } + } +} diff --git a/resources/res.qrc b/resources/res.qrc index ec086b3a2..cb724dd36 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -126,6 +126,7 @@ qml/TimelineRow.qml qml/emoji/EmojiButton.qml qml/emoji/EmojiPicker.qml + qml/UserProfile.qml qml/delegates/MessageDelegate.qml qml/delegates/TextMessage.qml qml/delegates/NoticeMessage.qml diff --git a/src/Olm.cpp b/src/Olm.cpp index 2c4f6186f..1a7e9786f 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -52,6 +52,11 @@ handle_to_device_messages(const std::vectorwarn("validation error for olm message: {} {}", e.what(), j_msg.dump(2)); + + nhlog::crypto()->warn("validation error for olm message: {} {}", + e.what(), + j_msg.dump(2)); + } } else if (msg_type == to_string(mtx::events::EventType::RoomKeyRequest)) { @@ -367,6 +372,10 @@ handle_key_request_message(const mtx::events::DeviceEventwarn("requested session not found in room: {}", req.content.room_id); + + nhlog::crypto()->warn("requested session not found in room: {}", + req.content.room_id); + return; } @@ -389,6 +398,13 @@ handle_key_request_message(const mtx::events::DeviceEventdebug("ignoring all key requests for room {}", + req.content.room_id); + + nhlog::crypto()->debug("ignoring all key requests for room {}", + req.content.room_id); + nhlog::crypto()->debug("ignoring all key requests for room {}", req.content.room_id); return; diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp new file mode 100644 index 000000000..ac35f1d49 --- /dev/null +++ b/src/ui/UserProfile.cpp @@ -0,0 +1,58 @@ +#include "UserProfile.h" +#include "Logging.h" +#include "MatrixClient.h" +#include "Utils.h" + +UserProfile::UserProfile(QObject *parent) + : QObject(parent) +{} + +QMap +UserProfile::getDeviceList() +{ + return this->deviceList; +} + +void +UserProfile::fetchDeviceList(const QString &userId) +{ + auto localUser = utils::localUser(); + mtx::requests::QueryKeys req; + req.device_keys[userId.toStdString()] = {}; + + http::client()->query_keys( + req, + [user_id = userId.toStdString()](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + return; + } + + if (res.device_keys.empty() || + (res.device_keys.find(user_id) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } + + auto devices = res.device_keys.at(user_id); + + std::vector deviceInfo; + for (const auto &d : devices) { + auto device = d.second; + + // TODO: Verify signatures and ignore those that don't pass. + deviceInfo.emplace_back(DeviceInfo{ + QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name)}); + } + + std::sort(deviceInfo.begin(), + deviceInfo.end(), + [](const DeviceInfo &a, const DeviceInfo &b) { + return a.device_id > b.device_id; + }); + }); +} diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h new file mode 100644 index 000000000..d003e6caa --- /dev/null +++ b/src/ui/UserProfile.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +struct DeviceInfo +{ + QString device_id; + QString display_name; +}; + +class UserProfile : public QObject +{ + Q_OBJECT + Q_PROPERTY(QMap deviceList READ getDeviceList NOTIFY DeviceListUpdated) + +public: + explicit UserProfile(QObject *parent = 0); + QMap getDeviceList(); + + Q_INVOKABLE void fetchDeviceList(const QString &userID); + +signals: + void DeviceListUpdated(); + +private: + QMap deviceList; +}; \ No newline at end of file From 2d23353a7897ad1128f5e650bbeba768df818b4a Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Fri, 22 May 2020 11:17:02 +0530 Subject: [PATCH 08/28] Adding DeviceList for userprofile --- resources/qml/UserProfile.qml | 18 +++++++- .../DeviceVerification.qml | 8 ---- src/timeline/TimelineViewManager.cpp | 9 ++-- src/ui/UserProfile.cpp | 44 ++++++++++++++----- src/ui/UserProfile.h | 32 +++++++++++--- 5 files changed, 79 insertions(+), 32 deletions(-) diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index f019ee250..ae91abc49 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -3,6 +3,8 @@ import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtQuick.Window 2.3 +import im.nheko 1.0 + ApplicationWindow{ property var user_data property var colors: currentActivePalette @@ -18,7 +20,21 @@ ApplicationWindow{ userProfileAvatar.url = chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") userProfileName.text = user_data.userName matrixUserID.text = user_data.userId - console.log("this is happening"); + userProfile.userId = user_data.userId + log_devices() + } + + function log_devices() + { + console.log(userProfile.deviceList); + userProfile.deviceList.forEach((item,index)=>{ + console.log(item.device_id) + console.log(item.display_name) + }) + } + + UserProfileContent{ + id: userProfile } background: Item{ diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 2c9486ae2..dd637e596 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -267,11 +267,7 @@ ApplicationWindow { model: 7 delegate: Rectangle { color: "transparent" -<<<<<<< HEAD - implicitHeight: Qt.application.font.pixelSize * 3 -======= implicitHeight: Qt.application.font.pixelSize * 8 ->>>>>>> Add DeviceVerificationFlow dummy and verification test button implicitWidth: col.width ColumnLayout { id: col @@ -414,11 +410,7 @@ ApplicationWindow { property string title: "Verification timed out" ColumnLayout { spacing: 16 -<<<<<<< HEAD - Label { -======= Text { ->>>>>>> Add DeviceVerificationFlow dummy and verification test button Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 6ad3cee1c..53f4c5aea 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -15,6 +15,7 @@ #include "dialogs/ImageOverlay.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" +#include "../ui/UserProfile.h" Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) @@ -86,6 +87,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); + qmlRegisterType("im.nheko",1,0,"UserProfileContent"); + qRegisterMetaType(); qRegisterMetaType(); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); @@ -447,9 +450,3 @@ TimelineViewManager::startDummyVerification() { emit deviceVerificationRequest(new DeviceVerificationFlow(this)); } - -void -TimelineViewManager::startDummyVerification() -{ - emit deviceVerificationRequest(new DeviceVerificationFlow(this)); -} diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index ac35f1d49..30785699d 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -1,28 +1,43 @@ #include "UserProfile.h" #include "Logging.h" -#include "MatrixClient.h" #include "Utils.h" +#include "mtx/responses/crypto.hpp" +#include UserProfile::UserProfile(QObject *parent) : QObject(parent) {} -QMap -UserProfile::getDeviceList() -{ +QVector +UserProfile::getDeviceList(){ + UserProfile::fetchDeviceList(this->userId); return this->deviceList; } +QString +UserProfile::getUserId (){ + return this->userId; +} + +void +UserProfile::setUserId (const QString &user_id){ + if(this->userId != userId) + return; + else + this->userId = user_id; +} + void -UserProfile::fetchDeviceList(const QString &userId) +UserProfile::fetchDeviceList(const QString &userID) { auto localUser = utils::localUser(); mtx::requests::QueryKeys req; - req.device_keys[userId.toStdString()] = {}; + mtx::responses::QueryKeys res; + req.device_keys[userID.toStdString()] = {}; http::client()->query_keys( req, - [user_id = userId.toStdString()](const mtx::responses::QueryKeys &res, + [user_id = userID.toStdString(),this](const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {} {}", @@ -39,14 +54,18 @@ UserProfile::fetchDeviceList(const QString &userId) auto devices = res.device_keys.at(user_id); - std::vector deviceInfo; + QVector deviceInfo; for (const auto &d : devices) { auto device = d.second; // TODO: Verify signatures and ignore those that don't pass. - deviceInfo.emplace_back(DeviceInfo{ - QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name)}); + // std::cout<device_id = QString::fromStdString(d.first); + newdevice->display_name = QString::fromStdString(device.unsigned_info.device_display_name) + + deviceInfo.append(std::move(newdevice)); } std::sort(deviceInfo.begin(), @@ -54,5 +73,8 @@ UserProfile::fetchDeviceList(const QString &userId) [](const DeviceInfo &a, const DeviceInfo &b) { return a.device_id > b.device_id; }); + + this->deviceList = deviceInfo; + emit UserProfile::deviceListUpdated(); }); } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index d003e6caa..bbf57c7b0 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -1,29 +1,49 @@ #pragma once -#include #include #include -struct DeviceInfo +#include "MatrixClient.h" + +class DeviceInfo { +public: + explicit DeviceInfo(QString device_id,QString display_name){ + this->device_id = device_id; + this->display_name = display_name; + } + ~DeviceInfo() = default; + DeviceInfo(const DeviceInfo &device){ + this->device_id = device.device_id; + this->display_name = device.display_name; + } + QString device_id; QString display_name; }; +Q_DECLARE_METATYPE(DeviceInfo); class UserProfile : public QObject { Q_OBJECT - Q_PROPERTY(QMap deviceList READ getDeviceList NOTIFY DeviceListUpdated) + Q_PROPERTY(QVector deviceList READ getDeviceList NOTIFY deviceListUpdated) + Q_PROPERTY(QString userId READ getUserId WRITE setUserId) public: + // constructor explicit UserProfile(QObject *parent = 0); - QMap getDeviceList(); + // getters + QVector getDeviceList(); + QString getUserId(); + // setters + void setUserId(const QString &userId); Q_INVOKABLE void fetchDeviceList(const QString &userID); signals: - void DeviceListUpdated(); + void deviceListUpdated(); private: - QMap deviceList; + QVector deviceList; + QString userId; }; \ No newline at end of file From a4d9cc4ef7c303d3ef5c51ff18a9261e9ccb0aff Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Wed, 27 May 2020 14:19:26 +0530 Subject: [PATCH 09/28] Add C++ Model for DeviceList --- CMakeLists.txt | 2 + resources/qml/TimelineView.qml | 28 +++++------- resources/qml/UserProfile.qml | 58 ++++++++++++++---------- src/Olm.cpp | 2 - src/timeline/TimelineViewManager.cpp | 8 +++- src/ui/UserProfile.cpp | 46 +++++++++++-------- src/ui/UserProfile.h | 22 +++++----- src/ui/UserProfileModel.cpp | 66 ++++++++++++++++++++++++++++ src/ui/UserProfileModel.h | 29 ++++++++++++ 9 files changed, 188 insertions(+), 73 deletions(-) create mode 100644 src/ui/UserProfileModel.cpp create mode 100644 src/ui/UserProfileModel.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a1c2b7983..d71ba001c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,6 +278,7 @@ set(SRC_FILES src/ui/Theme.cpp src/ui/ThemeManager.cpp src/ui/UserProfile.cpp + src/ui/UserProfileModel.cpp src/AvatarProvider.cpp src/BlurhashProvider.cpp @@ -482,6 +483,7 @@ qt5_wrap_cpp(MOC_HEADERS src/ui/Theme.h src/ui/ThemeManager.h src/ui/UserProfile.h + src/ui/UserProfileModel.h src/notifications/Manager.h diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index bf26341db..99890ddee 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -267,6 +267,9 @@ Page { color: colors.base } } + + property variant userProfile + Row { height: userName.height spacing: 8 @@ -281,8 +284,10 @@ Page { MouseArea { anchors.fill: parent onClicked: { - userProfile.user_data = modelData - userProfile.show() + if(userProfile) userProfile.destroy() + var component = Qt.createComponent("UserProfile.qml"); + userProfile = component.createObject(timelineRoot,{user_data : modelData}); + userProfile.show(); } cursorShape: Qt.PointingHandCursor propagateComposedEvents: true @@ -297,26 +302,17 @@ Page { MouseArea { anchors.fill: parent + Layout.alignment: Qt.AlignHCenter onClicked: { - userProfile.user_data = modelData - userProfile.show() + if(userProfile) userProfile.destroy() + var component = Qt.createComponent("UserProfile.qml") + userProfile = component.createObject(timelineRoot,{user_data : modelData}) + userProfile.show() } cursorShape: Qt.PointingHandCursor propagateComposedEvents: true } } - - Label { - color: colors.buttonText - text: timelineManager.userStatus(modelData.userId) - textFormat: Text.PlainText - elide: Text.ElideRight - width: chat.delegateMaxWidth - parent.spacing*2 - userName.implicitWidth - avatarSize - font.italic: true - } - UserProfile{ - id: userProfile - } } } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index ae91abc49..f29fb4c16 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -16,25 +16,16 @@ ApplicationWindow{ Layout.alignment: Qt.AlignHCenter palette: colors - onAfterRendering: { - userProfileAvatar.url = chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") - userProfileName.text = user_data.userName - matrixUserID.text = user_data.userId - userProfile.userId = user_data.userId - log_devices() - } - - function log_devices() - { - console.log(userProfile.deviceList); - userProfile.deviceList.forEach((item,index)=>{ - console.log(item.device_id) - console.log(item.display_name) - }) - } - - UserProfileContent{ - id: userProfile + UserProfileList{ + id: userProfileList + userId: user_data.userId + onUserIdChanged : { + console.log(userId) + userProfileList.updateDeviceList() + } + onDeviceListUpdated : { + modelDeviceList.deviceList = userProfileList + } } background: Item{ @@ -52,6 +43,7 @@ ApplicationWindow{ Avatar{ id: userProfileAvatar + url:chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") height: 130 width: 130 displayName: modelData.userName @@ -60,12 +52,14 @@ ApplicationWindow{ Label{ id: userProfileName + text: user_data.userName fontSizeMode: Text.HorizontalFit Layout.alignment: Qt.AlignHCenter } Label{ id: matrixUserID + text: user_data.userId fontSizeMode: Text.HorizontalFit Layout.alignment: Qt.AlignHCenter } @@ -78,9 +72,29 @@ ApplicationWindow{ ScrollBar.horizontal.policy: ScrollBar.AlwaysOn ScrollBar.vertical.policy: ScrollBar.AlwaysOn - Label { - text: "ABC" - font.pixelSize: 700 + ListView{ + id: deviceList + anchors.fill: parent + clip: true + spacing: 10 + + model: UserProfileModel{ + id: modelDeviceList + } + + delegate: RowLayout{ + width: parent.width + Text{ + Layout.fillWidth: true + color: colors.text + text: deviceID + } + Text{ + Layout.fillWidth: true + color:colors.text + text: displayName + } + } } } diff --git a/src/Olm.cpp b/src/Olm.cpp index 1a7e9786f..4fb14ca64 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -56,7 +56,6 @@ handle_to_device_messages(const std::vectorwarn("validation error for olm message: {} {}", e.what(), j_msg.dump(2)); - } } else if (msg_type == to_string(mtx::events::EventType::RoomKeyRequest)) { @@ -398,7 +397,6 @@ handle_key_request_message(const mtx::events::DeviceEventdebug("ignoring all key requests for room {}", req.content.room_id); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 53f4c5aea..82d50e576 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "BlurhashProvider.h" #include "ChatPage.h" @@ -16,6 +17,8 @@ #include "emoji/EmojiModel.h" #include "emoji/Provider.h" #include "../ui/UserProfile.h" +#include "src/ui/UserProfile.h" +#include "src/ui/UserProfileModel.h" Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) @@ -87,8 +90,9 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); - qmlRegisterType("im.nheko",1,0,"UserProfileContent"); - qRegisterMetaType(); + qmlRegisterType("im.nheko", 1, 0, "UserProfileModel"); + qmlRegisterType("im.nheko", 1, 0, "UserProfileList"); + qRegisterMetaType(); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 30785699d..588d69692 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -2,29 +2,32 @@ #include "Logging.h" #include "Utils.h" #include "mtx/responses/crypto.hpp" -#include UserProfile::UserProfile(QObject *parent) : QObject(parent) {} QVector -UserProfile::getDeviceList(){ - UserProfile::fetchDeviceList(this->userId); +UserProfile::getDeviceList() +{ return this->deviceList; } QString -UserProfile::getUserId (){ +UserProfile::getUserId() +{ return this->userId; } void -UserProfile::setUserId (const QString &user_id){ - if(this->userId != userId) +UserProfile::setUserId(const QString &user_id) +{ + if (this->userId != userId) return; - else + else { this->userId = user_id; + emit UserProfile::userIdChanged(); + } } void @@ -37,11 +40,11 @@ UserProfile::fetchDeviceList(const QString &userID) http::client()->query_keys( req, - [user_id = userID.toStdString(),this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { + [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { if (err) { - nhlog::net()->warn("failed to query device keys: {} {}", - err->matrix_error.error, + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, static_cast(err->status_code)); return; } @@ -53,17 +56,16 @@ UserProfile::fetchDeviceList(const QString &userID) } auto devices = res.device_keys.at(user_id); - QVector deviceInfo; + for (const auto &d : devices) { auto device = d.second; // TODO: Verify signatures and ignore those that don't pass. - // std::cout<device_id = QString::fromStdString(d.first); - newdevice->display_name = QString::fromStdString(device.unsigned_info.device_display_name) + DeviceInfo newdevice( + QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name)); + QString::fromStdString(device.unsigned_info.device_display_name); deviceInfo.append(std::move(newdevice)); } @@ -74,7 +76,13 @@ UserProfile::fetchDeviceList(const QString &userID) return a.device_id > b.device_id; }); - this->deviceList = deviceInfo; - emit UserProfile::deviceListUpdated(); + this->deviceList = std::move(deviceInfo); + emit UserProfile::deviceListUpdated(); }); } + +void +UserProfile::updateDeviceList() +{ + fetchDeviceList(this->userId); +} diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index bbf57c7b0..c37e23aed 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -2,33 +2,29 @@ #include #include +#include #include "MatrixClient.h" class DeviceInfo { public: - explicit DeviceInfo(QString device_id,QString display_name){ - this->device_id = device_id; - this->display_name = display_name; - } - ~DeviceInfo() = default; - DeviceInfo(const DeviceInfo &device){ - this->device_id = device.device_id; - this->display_name = device.display_name; - } + DeviceInfo(const QString deviceID, const QString displayName) + : device_id(deviceID) + , display_name(displayName) + {} + + DeviceInfo() {} QString device_id; QString display_name; }; -Q_DECLARE_METATYPE(DeviceInfo); class UserProfile : public QObject { Q_OBJECT + Q_PROPERTY(QString userId READ getUserId WRITE setUserId NOTIFY userIdChanged) Q_PROPERTY(QVector deviceList READ getDeviceList NOTIFY deviceListUpdated) - Q_PROPERTY(QString userId READ getUserId WRITE setUserId) - public: // constructor explicit UserProfile(QObject *parent = 0); @@ -39,8 +35,10 @@ class UserProfile : public QObject void setUserId(const QString &userId); Q_INVOKABLE void fetchDeviceList(const QString &userID); + Q_INVOKABLE void updateDeviceList(); signals: + void userIdChanged(); void deviceListUpdated(); private: diff --git a/src/ui/UserProfileModel.cpp b/src/ui/UserProfileModel.cpp new file mode 100644 index 000000000..ec0456cd9 --- /dev/null +++ b/src/ui/UserProfileModel.cpp @@ -0,0 +1,66 @@ +#include "UserProfileModel.h" +#include "UserProfile.h" +#include + +UserProfileModel::UserProfileModel(QObject *parent) + : QAbstractListModel(parent) + , deviceList(nullptr) +{} + +int +UserProfileModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid() || !this->deviceList) + return 0; + return this->deviceList->getDeviceList().size(); +} + +QVariant +UserProfileModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || !this->deviceList) + return QVariant(); + + const DeviceInfo device = this->deviceList->getDeviceList().at(index.row()); + switch (role) { + case DEVICEID: + return QVariant(device.device_id); + case DISPLAYNAME: + return QVariant(device.display_name); + } + return QVariant(); +} + +QHash +UserProfileModel::roleNames() const +{ + QHash names; + names[DEVICEID] = "deviceID"; + names[DISPLAYNAME] = "displayName"; + return names; +} + +UserProfile * +UserProfileModel::getList() const +{ + return (this->deviceList); +} + +void +UserProfileModel::setList(UserProfile *devices) +{ + beginResetModel(); + + if (devices) + devices->disconnect(this); + + if (this->deviceList) { + const int index = this->deviceList->getDeviceList().size(); + beginInsertRows(QModelIndex(), index, index); + endInsertRows(); + } + + this->deviceList = devices; + + endResetModel(); +} \ No newline at end of file diff --git a/src/ui/UserProfileModel.h b/src/ui/UserProfileModel.h new file mode 100644 index 000000000..c21a806dd --- /dev/null +++ b/src/ui/UserProfileModel.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +class UserProfile; // forward declaration of the class UserProfile + +class UserProfileModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(UserProfile *deviceList READ getList WRITE setList) + +public: + explicit UserProfileModel(QObject *parent = nullptr); + + enum + { + DEVICEID, + DISPLAYNAME + }; + UserProfile *getList() const; + void setList(UserProfile *devices); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + virtual QHash roleNames() const override; + +private: + UserProfile *deviceList; +}; \ No newline at end of file From 24f59ae143eccf2ee6321b72157d7a4a42126a3e Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Thu, 4 Jun 2020 19:14:15 +0530 Subject: [PATCH 10/28] Tweak UI for device verification and Add more slots --- resources/qml/UserProfile.qml | 38 +++++++--- .../DeviceVerification.qml | 41 ++++++++++- src/DeviceVerificationFlow.cpp | 70 ++++++++++++++++++- src/DeviceVerificationFlow.h | 10 ++- 4 files changed, 146 insertions(+), 13 deletions(-) diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index f29fb4c16..a85c41c33 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -5,6 +5,8 @@ import QtQuick.Window 2.3 import im.nheko 1.0 +import "./device-verification" + ApplicationWindow{ property var user_data property var colors: currentActivePalette @@ -20,7 +22,6 @@ ApplicationWindow{ id: userProfileList userId: user_data.userId onUserIdChanged : { - console.log(userId) userProfileList.updateDeviceList() } onDeviceListUpdated : { @@ -84,15 +85,34 @@ ApplicationWindow{ delegate: RowLayout{ width: parent.width - Text{ - Layout.fillWidth: true - color: colors.text - text: deviceID + ColumnLayout{ + Text{ + Layout.fillWidth: true + color: colors.text + Layout.alignment: Qt.AlignRight + text: deviceID + } + Text{ + Layout.fillWidth: true + color:colors.text + Layout.alignment: Qt.AlignRight + text: displayName + } + Component { + id: deviceVerificationDialog + DeviceVerification {} + } + DeviceVerificationFlow { + id: deviceVerificationFlow + } } - Text{ - Layout.fillWidth: true - color:colors.text - text: displayName + Button{ + text:"Verify" + onClicked: { + var dialog = deviceVerificationDialog.createObject(userProfileDialog, + {flow: deviceVerificationFlow,sender: true}); + dialog.show(); + } } } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index dd637e596..ce2485ff1 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -7,6 +7,7 @@ import Qt.labs.settings 1.0 import im.nheko 1.0 ApplicationWindow { + property bool sender: true title: stack.currentItem.title id: dialog @@ -24,7 +25,7 @@ ApplicationWindow { width: stack.implicitWidth StackView { id: stack - initialItem: newVerificationRequest + initialItem: sender == true?newVerificationRequest:acceptNewVerificationRequest implicitWidth: currentItem.implicitWidth implicitHeight: currentItem.implicitHeight } @@ -47,7 +48,7 @@ ApplicationWindow { Component { id: newVerificationRequest Pane { - property string title: "Device Verification Request" + property string title: "Sending Device Verification Request" ColumnLayout { spacing: 16 Label { @@ -82,6 +83,42 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Start verification" + onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.sendVerificationRequest(); } + } + } + } + } + } + + Component { + id: acceptNewVerificationRequest + Pane { + property string title: "Recieving Device Verification Request" + ColumnLayout { + spacing: 16 + + Label { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "The device was requested to be verified" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Deny" + onClicked: { dialog.close(); flow.cancelVerification(); } + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Accept" onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.acceptVerificationRequest(); } } } diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 69d6ab9cc..12e31c048 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,5 +1,8 @@ #include "DeviceVerificationFlow.h" +#include +#include +#include // only for debugging #include static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -15,17 +18,80 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) timeout->start(TIMEOUT); } -//! accepts a verification and starts the verification flow +std::string +DeviceVerificationFlow::generate_txn_id() +{ + this->transaction_id = mtx::client::utils::random_token(32, false); + return this->transaction_id; +} + +//! accepts a verification void DeviceVerificationFlow::acceptVerificationRequest() { + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationAccept req; + + req.transaction_id = this->transaction_id; + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocol = ""; + req.hash = ""; + req.message_authentication_code = ""; + // req.short_authentication_string = ""; + req.commitment = ""; + emit verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); + + // Yet to add send to_device message +} +//! starts the verification flow +void +DeviceVerificationFlow::startVerificationRequest() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationAccept req; + + // req.from_device = ""; + req.transaction_id = this->transaction_id; + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocol = {}; + // req.hashes = {}; + req.message_authentication_code = {}; + // req.short_authentication_string = ""; + + // Yet to add send to_device message +} +//! sends a verification request +void +DeviceVerificationFlow::sendVerificationRequest() +{ + QDateTime CurrentTime = QDateTime::currentDateTimeUtc(); + + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationRequest req; + + req.from_device = ""; + req.transaction_id = generate_txn_id(); + req.methods.resize(1); + req.methods[0] = mtx::events::msg::VerificationMethods::SASv1; + req.timestamp = (uint64_t)CurrentTime.toTime_t(); + + // Yet to add send to_device message } //! cancels a verification flow void DeviceVerificationFlow::cancelVerification() { + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationCancel req; + + req.transaction_id = this->transaction_id; + req.reason = ""; + req.code = ""; + this->deleteLater(); + + // Yet to add send to_device message } //! Completes the verification flow void @@ -33,4 +99,6 @@ DeviceVerificationFlow::acceptDevice() { emit deviceVerified(); this->deleteLater(); + + // Yet to add send to_device message } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 038f1e13a..71c40cd5e 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -20,8 +20,12 @@ class DeviceVerificationFlow : public QObject DeviceVerificationFlow(QObject *parent = nullptr); public slots: - //! accepts a verification and starts the verification flow + //! sends a verification request + void sendVerificationRequest(); + //! accepts a verification void acceptVerificationRequest(); + //! starts the verification flow + void startVerificationRequest(); //! cancels a verification flow void cancelVerification(); //! Completes the verification flow @@ -34,5 +38,9 @@ public slots: void verificationCanceled(); private: + //! generates a unique transaction id + std::string generate_txn_id(); + QTimer *timeout = nullptr; + std::string transaction_id; }; From 0035cc682d9fc7455144b016307d8668581c9390 Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Sun, 7 Jun 2020 17:05:32 +0530 Subject: [PATCH 11/28] Add SAS Method choice and Add send_to_device API call --- resources/qml/UserProfile.qml | 19 ++- .../DeviceVerification.qml | 16 +++ src/DeviceVerificationFlow.cpp | 131 ++++++++++++++---- src/DeviceVerificationFlow.h | 16 ++- 4 files changed, 151 insertions(+), 31 deletions(-) diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index a85c41c33..6bfee09c0 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -107,18 +107,28 @@ ApplicationWindow{ } } Button{ + id: verifyButton text:"Verify" onClicked: { var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: deviceVerificationFlow,sender: true}); + {flow: deviceVerificationFlow,sender: false}); + deviceVerificationFlow.userId = user_data.userId + deviceVerificationFlow.deviceId = model.deviceID dialog.show(); } + contentItem: Text { + text: verifyButton.text + color: colors.background + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } } } } } Button{ + id: okbutton text:"OK" onClicked: userProfileDialog.close() anchors.margins: { @@ -126,6 +136,13 @@ ApplicationWindow{ bottom:10 } + contentItem: Text { + text: okbutton.text + color: colors.background + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + Layout.alignment: Qt.AlignRight | Qt.AlignBottom } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index ce2485ff1..31f6f9c1e 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -107,6 +107,22 @@ ApplicationWindow { verticalAlignment: Text.AlignVCenter } + RowLayout { + RadioButton { + Layout.alignment: Qt.AlignLeft + text: "Decimal" + onClicked: { flow.method = DeviceVerificationFlow.Decimal } + } + Item { + Layout.fillWidth: true + } + RadioButton { + Layout.alignment: Qt.AlignRight + text: "Emoji" + onClicked: { flow.method = DeviceVerificationFlow.Emoji } + } + } + RowLayout { Button { Layout.alignment: Qt.AlignLeft diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 12e31c048..5bbe2a71a 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,9 +1,10 @@ #include "DeviceVerificationFlow.h" -#include +#include "Logging.h" #include #include // only for debugging #include +#include // only for debugging static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -18,11 +19,42 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) timeout->start(TIMEOUT); } -std::string -DeviceVerificationFlow::generate_txn_id() +QString +DeviceVerificationFlow::getUserId() { - this->transaction_id = mtx::client::utils::random_token(32, false); - return this->transaction_id; + toClient = mtx::identifiers::parse((this->userId).toStdString()); + std::cout << http::client()->device_id() << std::endl; + return this->userId; +} + +QString +DeviceVerificationFlow::getDeviceId() +{ + return this->deviceId; +} + +DeviceVerificationFlow::Method +DeviceVerificationFlow::getMethod() +{ + return this->method; +} + +void +DeviceVerificationFlow::setUserId(QString userID) +{ + this->userId = userID; +} + +void +DeviceVerificationFlow::setDeviceId(QString deviceID) +{ + this->deviceId = deviceID; +} + +void +DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_) +{ + this->method = method_; } //! accepts a verification @@ -34,32 +66,53 @@ DeviceVerificationFlow::acceptVerificationRequest() req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocol = ""; - req.hash = ""; + req.key_agreement_protocol = "curve25519"; + req.hash = "sha256"; req.message_authentication_code = ""; // req.short_authentication_string = ""; req.commitment = ""; - emit verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); + emit this->verificationRequestAccepted(this->method); - // Yet to add send to_device message + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.accept", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to accept verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + // emit this->verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); + }); } //! starts the verification flow void DeviceVerificationFlow::startVerificationRequest() { - mtx::requests::ToDeviceMessages body; - mtx::events::msg::KeyVerificationAccept req; - - // req.from_device = ""; - req.transaction_id = this->transaction_id; - req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocol = {}; - // req.hashes = {}; - req.message_authentication_code = {}; + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationStart req; + + req.from_device = http::client()->device_id(); + req.transaction_id = this->transaction_id; + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocols = {}; + req.hashes = {}; + req.message_authentication_codes = {}; // req.short_authentication_string = ""; - // Yet to add send to_device message + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.start", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to start verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); } //! sends a verification request void @@ -70,13 +123,25 @@ DeviceVerificationFlow::sendVerificationRequest() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationRequest req; - req.from_device = ""; - req.transaction_id = generate_txn_id(); + this->transaction_id = http::client()->generate_txn_id(); + + req.from_device = http::client()->device_id(); + req.transaction_id = this->transaction_id; req.methods.resize(1); req.methods[0] = mtx::events::msg::VerificationMethods::SASv1; req.timestamp = (uint64_t)CurrentTime.toTime_t(); - // Yet to add send to_device message + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.request", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); } //! cancels a verification flow void @@ -86,12 +151,22 @@ DeviceVerificationFlow::cancelVerification() mtx::events::msg::KeyVerificationCancel req; req.transaction_id = this->transaction_id; - req.reason = ""; - req.code = ""; - - this->deleteLater(); - - // Yet to add send to_device message + // TODO: Add Proper Error Messages and Code + req.reason = "Device Verification Cancelled"; + req.code = "400"; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.cancel", body, [this](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to cancel verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + this->deleteLater(); + }); } //! Completes the verification flow void diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 71c40cd5e..c7701196f 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -1,5 +1,6 @@ #pragma once +#include #include class QTimer; @@ -8,6 +9,9 @@ class DeviceVerificationFlow : public QObject { Q_OBJECT // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + Q_PROPERTY(QString userId READ getUserId WRITE setUserId) + Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) + Q_PROPERTY(Method method READ getMethod WRITE setMethod) public: enum Method @@ -18,6 +22,12 @@ class DeviceVerificationFlow : public QObject Q_ENUM(Method) DeviceVerificationFlow(QObject *parent = nullptr); + QString getUserId(); + QString getDeviceId(); + Method getMethod(); + void setUserId(QString userID); + void setDeviceId(QString deviceID); + void setMethod(Method method_); public slots: //! sends a verification request @@ -38,9 +48,11 @@ public slots: void verificationCanceled(); private: - //! generates a unique transaction id - std::string generate_txn_id(); + QString userId; + QString deviceId; + Method method; QTimer *timeout = nullptr; std::string transaction_id; + mtx::identifiers::User toClient; }; From bbaf15b9062f284df1d04dc1b61500f13c3b9649 Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 9 Jun 2020 22:06:41 +0530 Subject: [PATCH 12/28] Handle Device Verification related to_device messages --- resources/qml/UserProfile.qml | 19 ++-- .../DeviceVerification.qml | 2 +- src/ChatPage.cpp | 1 + src/ChatPage.h | 12 ++ src/DeviceVerificationFlow.cpp | 105 +++++++++++++++++- src/DeviceVerificationFlow.h | 5 + src/Olm.cpp | 23 +++- 7 files changed, 153 insertions(+), 14 deletions(-) diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 6bfee09c0..c40e6765c 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -29,6 +29,14 @@ ApplicationWindow{ } } + Component { + id: deviceVerificationDialog + DeviceVerification {} + } + DeviceVerificationFlow { + id: deviceVerificationFlow + } + background: Item{ id: userProfileItem width: userProfileDialog.width @@ -98,22 +106,15 @@ ApplicationWindow{ Layout.alignment: Qt.AlignRight text: displayName } - Component { - id: deviceVerificationDialog - DeviceVerification {} - } - DeviceVerificationFlow { - id: deviceVerificationFlow - } } Button{ id: verifyButton text:"Verify" onClicked: { - var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: deviceVerificationFlow,sender: false}); deviceVerificationFlow.userId = user_data.userId deviceVerificationFlow.deviceId = model.deviceID + var dialog = deviceVerificationDialog.createObject(userProfileDialog, + {flow: deviceVerificationFlow,sender: true}); dialog.show(); } contentItem: Text { diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 31f6f9c1e..fca360f7f 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -83,7 +83,7 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Start verification" - onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.sendVerificationRequest(); } + onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.startVerificationRequest(); } } } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 3b8af33a5..c388d5a79 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -27,6 +27,7 @@ #include "Cache_p.h" #include "ChatPage.h" #include "EventAccessors.h" +#include "DeviceVerificationFlow.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" diff --git a/src/ChatPage.h b/src/ChatPage.h index c38d7717e..e245502ac 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -163,6 +164,17 @@ public slots: void themeChanged(); void decryptSidebarChanged(); + //! Signals for device verificaiton + void recievedDeviceVerificationAccept( + const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationRequest( + const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationCancel( + const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationKey(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationMac(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationStart(const mtx::events::collections::DeviceEvents &message); + private slots: void showUnreadMessageNotification(int count); void updateTopBarAvatar(const QString &roomid, const QString &img); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 5bbe2a71a..c6652d03a 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,4 +1,5 @@ #include "DeviceVerificationFlow.h" +#include "ChatPage.h" #include "Logging.h" #include @@ -8,22 +9,73 @@ static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes +namespace msgs = mtx::events::msg; + DeviceVerificationFlow::DeviceVerificationFlow(QObject *) { + qRegisterMetaType(); timeout = new QTimer(this); timeout->setSingleShot(true); connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->deleteLater(); }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationAccept, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Accept" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationRequest, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Request" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationCancel, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Cancel" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationKey, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Key" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationMac, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Mac" << std::endl; + } + }); timeout->start(TIMEOUT); } QString DeviceVerificationFlow::getUserId() { - toClient = mtx::identifiers::parse((this->userId).toStdString()); - std::cout << http::client()->device_id() << std::endl; return this->userId; } @@ -43,6 +95,7 @@ void DeviceVerificationFlow::setUserId(QString userID) { this->userId = userID; + this->toClient = mtx::identifiers::parse(userID.toStdString()); } void @@ -101,7 +154,8 @@ DeviceVerificationFlow::startVerificationRequest() req.hashes = {}; req.message_authentication_codes = {}; // req.short_authentication_string = ""; - + qDebug()<<"Inside Start Verification"; + qDebug()<userId; body[this->toClient][this->deviceId.toStdString()] = req; http::client() @@ -168,6 +222,51 @@ DeviceVerificationFlow::cancelVerification() this->deleteLater(); }); } +//! sends the verification key +void +DeviceVerificationFlow::sendVerificationKey() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationKey req; + + req.key = ""; + req.transaction_id = this->transaction_id; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification key: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} +//! sends the mac of the keys +void +DeviceVerificationFlow::sendVerificationMac() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationMac req; + + req.transaction_id = this->transaction_id; + // req.mac = ""; + req.keys = ""; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification MAC: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} //! Completes the verification flow void DeviceVerificationFlow::acceptDevice() diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index c7701196f..561a37174 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -38,6 +38,10 @@ public slots: void startVerificationRequest(); //! cancels a verification flow void cancelVerification(); + //! sends the verification key + void sendVerificationKey(); + //! sends the mac of the keys + void sendVerificationMac(); //! Completes the verification flow void acceptDevice(); @@ -56,3 +60,4 @@ public slots: std::string transaction_id; mtx::identifiers::User toClient; }; +Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents) \ No newline at end of file diff --git a/src/Olm.cpp b/src/Olm.cpp index 4fb14ca64..632e678c6 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -1,11 +1,15 @@ +#include #include #include "Olm.h" #include "Cache.h" +#include "ChatPage.h" #include "Logging.h" #include "MatrixClient.h" #include "Utils.h" +#include +#include // only for debugging static const std::string STORAGE_SECRET_KEY("secret"); constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2"; @@ -27,7 +31,6 @@ handle_to_device_messages(const std::vectorinfo("received {} to_device messages", msgs.size()); nlohmann::json j_msg; @@ -74,6 +77,24 @@ handle_to_device_messages(const std::vectorrecievedDeviceVerificationAccept(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { + ChatPage::instance()->recievedDeviceVerificationRequest(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { + ChatPage::instance()->recievedDeviceVerificationCancel(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { + ChatPage::instance()->recievedDeviceVerificationKey(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { + ChatPage::instance()->recievedDeviceVerificationMac(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { + ChatPage::instance()->recievedDeviceVerificationStart(msg); + std::cout << j_msg.dump(2) << std::endl; } else { nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2)); } From 8da77bccf5d88ddcfc801dc55210fc54b19b1cf2 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Wed, 17 Jun 2020 23:58:35 +0530 Subject: [PATCH 13/28] Add DeviceVerificationList to keep track of all flows and Popup on recieving start or request --- resources/qml/Avatar.qml | 12 +-- resources/qml/TimelineView.qml | 18 +++-- resources/qml/UserProfile.qml | 10 ++- .../DeviceVerification.qml | 60 ++++++++++++--- src/ChatPage.cpp | 2 +- src/DeviceVerificationFlow.cpp | 69 +++++++++++------ src/DeviceVerificationFlow.h | 10 ++- src/emoji/EmojiModel.h | 1 - src/timeline/TimelineViewManager.cpp | 75 +++++++++++++++---- src/timeline/TimelineViewManager.h | 20 ++++- 10 files changed, 209 insertions(+), 68 deletions(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 9ac7b5627..0c53f94e7 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -53,12 +53,12 @@ Rectangle { height: avatar.height / 6 width: height radius: settings.avatarCircles ? height / 2 : height / 4 - color: switch (timelineManager.userPresence(userid)) { - case "online": return "#00cc66" - case "unavailable": return "#ff9933" - case "offline": return "#a82353" - default: "transparent" - } + // color: switch (timelineManager.userPresence(userid)) { + // case "online": return "#00cc66" + // case "unavailable": return "#ff9933" + // case "offline": return "#a82353" + // default: "transparent" + // } } color: colors.base diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 99890ddee..193dc7a99 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -98,20 +98,22 @@ Page { id: deviceVerificationDialog DeviceVerification {} } + Component{ + id: deviceVerificationFlow + DeviceVerificationFlow {} + } Connections { target: timelineManager - onDeviceVerificationRequest: { - var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: deviceVerificationFlow}); + onNewDeviceVerificationRequest: { + var newFlow = deviceVerificationFlow.createObject(timelineRoot, + {userId : userId,sender: false,deviceId : deviceId,tranId:transactionId}); + deviceVerificationList.add(newFlow.tranId); + var dialog = deviceVerificationDialog.createObject(timelineRoot, + {flow: newFlow,sender: false}); dialog.show(); } } - Button { - text: "test device verification" - onClicked: timelineManager.startDummyVerification() - z: 5 - } - Label { visible: !timelineManager.timeline && !timelineManager.isInitialSync anchors.centerIn: parent diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index c40e6765c..80415a292 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -33,8 +33,9 @@ ApplicationWindow{ id: deviceVerificationDialog DeviceVerification {} } - DeviceVerificationFlow { + Component{ id: deviceVerificationFlow + DeviceVerificationFlow {} } background: Item{ @@ -111,10 +112,11 @@ ApplicationWindow{ id: verifyButton text:"Verify" onClicked: { - deviceVerificationFlow.userId = user_data.userId - deviceVerificationFlow.deviceId = model.deviceID + var newFlow = deviceVerificationFlow.createObject(userProfileDialog, + {userId : user_data.userId,sender: true,deviceId : model.deviceID}); + deviceVerificationList.add(newFlow.tranId); var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: deviceVerificationFlow,sender: true}); + {flow: newFlow,sender: true}); dialog.show(); } contentItem: Text { diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index fca360f7f..ad0edeb7f 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -75,7 +75,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -127,7 +132,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Deny" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -166,7 +176,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -212,7 +227,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -346,7 +366,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -385,7 +410,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -419,7 +449,11 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Close" - onClicked: dialog.close() + onClicked: { + dialog.close() + deviceVerificationList.remove(flow.tranId); + delete flow; + } } } } @@ -450,7 +484,11 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Close" - onClicked: dialog.close() + onClicked: { + dialog.close() + deviceVerificationList.remove(flow.tranId); + delete flow; + } } } } @@ -481,7 +519,11 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Close" - onClicked: dialog.close() + onClicked: { + dialog.close() + deviceVerificationList.remove(flow.tranId); + delete flow; + } } } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index c388d5a79..732cb9f51 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -26,8 +26,8 @@ #include "Cache.h" #include "Cache_p.h" #include "ChatPage.h" -#include "EventAccessors.h" #include "DeviceVerificationFlow.h" +#include "EventAccessors.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index c6652d03a..efb9882bc 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,7 +1,7 @@ #include "DeviceVerificationFlow.h" #include "ChatPage.h" - #include "Logging.h" + #include #include // only for debugging #include @@ -13,9 +13,10 @@ namespace msgs = mtx::events::msg; DeviceVerificationFlow::DeviceVerificationFlow(QObject *) { - qRegisterMetaType(); timeout = new QTimer(this); timeout->setSingleShot(true); + if (this->sender == true) + this->transaction_id = http::client()->generate_txn_id(); connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->deleteLater(); @@ -73,6 +74,12 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) timeout->start(TIMEOUT); } +QString +DeviceVerificationFlow::getTransactionId() +{ + return QString::fromStdString(this->transaction_id); +} + QString DeviceVerificationFlow::getUserId() { @@ -91,10 +98,22 @@ DeviceVerificationFlow::getMethod() return this->method; } +bool +DeviceVerificationFlow::getSender() +{ + return this->sender; +} + +void +DeviceVerificationFlow::setTransactionId(QString transaction_id_) +{ + this->transaction_id = transaction_id_.toStdString(); +} + void DeviceVerificationFlow::setUserId(QString userID) { - this->userId = userID; + this->userId = userID; this->toClient = mtx::identifiers::parse(userID.toStdString()); } @@ -110,6 +129,12 @@ DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_) this->method = method_; } +void +DeviceVerificationFlow::setSender(bool sender_) +{ + this->sender = sender_; +} + //! accepts a verification void DeviceVerificationFlow::acceptVerificationRequest() @@ -119,11 +144,12 @@ DeviceVerificationFlow::acceptVerificationRequest() req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocol = "curve25519"; + req.key_agreement_protocol = "curve25519-hkdf-sha256"; req.hash = "sha256"; - req.message_authentication_code = ""; - // req.short_authentication_string = ""; - req.commitment = ""; + req.message_authentication_code = "hkdf-hmac-sha256"; + req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, + mtx::events::msg::SASMethods::Emoji}; + req.commitment = ""; emit this->verificationRequestAccepted(this->method); @@ -132,12 +158,12 @@ DeviceVerificationFlow::acceptVerificationRequest() http::client() ->send_to_device( - "m.key.verification.accept", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [this](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to accept verification request: {} {}", err->matrix_error.error, static_cast(err->status_code)); - // emit this->verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); + emit this->verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); }); } //! starts the verification flow @@ -150,22 +176,23 @@ DeviceVerificationFlow::startVerificationRequest() req.from_device = http::client()->device_id(); req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocols = {}; - req.hashes = {}; - req.message_authentication_codes = {}; - // req.short_authentication_string = ""; - qDebug()<<"Inside Start Verification"; - qDebug()<userId; + req.key_agreement_protocols = {"curve25519-hkdf-sha256"}; + req.hashes = {"sha256"}; + req.message_authentication_codes = {"hkdf-hmac-sha256", "hmac-sha256"}; + req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, + mtx::events::msg::SASMethods::Emoji}; + body[this->toClient][this->deviceId.toStdString()] = req; http::client() ->send_to_device( - "m.key.verification.start", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [body](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to start verification request: {} {}", err->matrix_error.error, static_cast(err->status_code)); + std::cout << nlohmann::json(body).dump(2) << std::endl; }); } //! sends a verification request @@ -177,8 +204,6 @@ DeviceVerificationFlow::sendVerificationRequest() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationRequest req; - this->transaction_id = http::client()->generate_txn_id(); - req.from_device = http::client()->device_id(); req.transaction_id = this->transaction_id; req.methods.resize(1); @@ -190,7 +215,7 @@ DeviceVerificationFlow::sendVerificationRequest() http::client() ->send_to_device( - "m.key.verification.request", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to send verification request: {} {}", err->matrix_error.error, @@ -214,7 +239,7 @@ DeviceVerificationFlow::cancelVerification() http::client() ->send_to_device( - "m.key.verification.cancel", body, [this](mtx::http::RequestErr err) { + this->transaction_id, body, [this](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to cancel verification request: {} {}", err->matrix_error.error, @@ -237,7 +262,7 @@ DeviceVerificationFlow::sendVerificationKey() http::client() ->send_to_device( - "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to send verification key: {} {}", err->matrix_error.error, @@ -260,7 +285,7 @@ DeviceVerificationFlow::sendVerificationMac() http::client() ->send_to_device( - "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to send verification MAC: {} {}", err->matrix_error.error, diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 561a37174..b651394ba 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -1,5 +1,7 @@ #pragma once +#include "Olm.h" + #include #include @@ -9,6 +11,8 @@ class DeviceVerificationFlow : public QObject { Q_OBJECT // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + Q_PROPERTY(QString tranId READ getTransactionId WRITE setTransactionId) + Q_PROPERTY(bool sender READ getSender WRITE setSender) Q_PROPERTY(QString userId READ getUserId WRITE setUserId) Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) Q_PROPERTY(Method method READ getMethod WRITE setMethod) @@ -22,12 +26,16 @@ class DeviceVerificationFlow : public QObject Q_ENUM(Method) DeviceVerificationFlow(QObject *parent = nullptr); + QString getTransactionId(); QString getUserId(); QString getDeviceId(); Method getMethod(); + void setTransactionId(QString transaction_id_); + bool getSender(); void setUserId(QString userID); void setDeviceId(QString deviceID); void setMethod(Method method_); + void setSender(bool sender_); public slots: //! sends a verification request @@ -55,9 +63,9 @@ public slots: QString userId; QString deviceId; Method method; + bool sender; QTimer *timeout = nullptr; std::string transaction_id; mtx::identifiers::User toClient; }; -Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents) \ No newline at end of file diff --git a/src/emoji/EmojiModel.h b/src/emoji/EmojiModel.h index 8d43e0006..88bacdee4 100644 --- a/src/emoji/EmojiModel.h +++ b/src/emoji/EmojiModel.h @@ -60,5 +60,4 @@ class EmojiProxyModel : public QSortFilterProxyModel EmojiCategory category_ = EmojiCategory::Search; emoji::Provider emoji_provider_; }; - } \ No newline at end of file diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 82d50e576..e2e875634 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -16,12 +16,38 @@ #include "dialogs/ImageOverlay.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" -#include "../ui/UserProfile.h" #include "src/ui/UserProfile.h" #include "src/ui/UserProfileModel.h" Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) +namespace msgs = mtx::events::msg; + +void +DeviceVerificationList::add(QString tran_id) +{ + this->dv_list.push_back(tran_id); +} +void +DeviceVerificationList::remove(QString tran_id) +{ + for (QVector::iterator it = 0; it != (this->dv_list).end(); ++it) { + if (*it == tran_id) { + this->dv_list.erase(it); + break; + } + } +} +bool +DeviceVerificationList::exist(QString tran_id) +{ + for (int i = 0; i < (this->dv_list).size(); ++i) { + if (dv_list[i] == tran_id) + return true; + } + return false; +} + void TimelineViewManager::updateEncryptedDescriptions() { @@ -63,12 +89,12 @@ TimelineViewManager::userColor(QString id, QColor background) return userColors.value(id); } -QString -TimelineViewManager::userPresence(QString id) const -{ - return QString::fromStdString( - mtx::presence::to_string(cache::presenceState(id.toStdString()))); -} +// QString +// TimelineViewManager::userPresence(QString id) const +// { +// return QString::fromStdString( +// mtx::presence::to_string(cache::presenceState(id.toStdString()))); +// } QString TimelineViewManager::userStatus(QString id) const { @@ -81,6 +107,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin , blurhashProvider(new BlurhashProvider()) , settings(userSettings) { + qRegisterMetaType(); qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject, "im.nheko", 1, @@ -106,6 +133,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin 0, "EmojiCategory", "Error: Only enums"); + this->dvList = new DeviceVerificationList; #ifdef USE_QUICK_VIEW view = new QQuickView(); @@ -123,6 +151,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin container->setMinimumSize(200, 200); view->rootContext()->setContextProperty("timelineManager", this); view->rootContext()->setContextProperty("settings", settings.data()); + view->rootContext()->setContextProperty("deviceVerificationList", this->dvList); updateColorPalette(); view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider); @@ -137,6 +166,32 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin &ChatPage::decryptSidebarChanged, this, &TimelineViewManager::updateEncryptedDescriptions); + connect(dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationRequest, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + QString tranID = QString::fromStdString(msg.content.transaction_id); + QString deviceId = QString::fromStdString(msg.content.from_device); + QString userId = QString::fromStdString(msg.sender); + if (!(this->dvList->exist(tranID))) { + emit newDeviceVerificationRequest(tranID, userId, deviceId); + } + }); + connect(dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationStart, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + QString tranID = QString::fromStdString(msg.content.transaction_id); + QString deviceId = QString::fromStdString(msg.content.from_device); + QString userId = QString::fromStdString(msg.sender); + if (!(this->dvList->exist(tranID))) { + emit newDeviceVerificationRequest(tranID, userId, deviceId); + } + }); } void @@ -448,9 +503,3 @@ TimelineViewManager::queueVideoMessage(const QString &roomid, model->sendMessage(video); } - -void -TimelineViewManager::startDummyVerification() -{ - emit deviceVerificationRequest(new DeviceVerificationFlow(this)); -} diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index a41747f5b..1343e4564 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -22,6 +22,18 @@ class BlurhashProvider; class ColorImageProvider; class UserSettings; +class DeviceVerificationList : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE void add(QString tran_id); + Q_INVOKABLE void remove(QString tran_id); + Q_INVOKABLE bool exist(QString tran_id); + +private: + QVector dv_list; +}; + class TimelineViewManager : public QObject { Q_OBJECT @@ -44,9 +56,8 @@ class TimelineViewManager : public QObject Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); - Q_INVOKABLE void startDummyVerification(); - Q_INVOKABLE QString userPresence(QString id) const; + // Q_INVOKABLE QString userPresence(QString id) const; Q_INVOKABLE QString userStatus(QString id) const; signals: @@ -56,7 +67,7 @@ class TimelineViewManager : public QObject void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); - void deviceVerificationRequest(DeviceVerificationFlow *deviceVerificationFlow); + void newDeviceVerificationRequest(QString transactionId, QString userId, QString deviceId); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); @@ -120,4 +131,7 @@ public slots: QSharedPointer settings; QHash userColors; + + DeviceVerificationList *dvList; }; +Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents) \ No newline at end of file From 30aaf8c0cbec1eb46c24101d6fd4b28f5f12058f Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sat, 20 Jun 2020 17:50:43 +0530 Subject: [PATCH 14/28] Shared secret with decimal and emoji works! --- resources/qml/TimelineView.qml | 13 +- .../DeviceVerification.qml | 26 +-- src/DeviceVerificationFlow.cpp | 167 ++++++++++++++---- src/DeviceVerificationFlow.h | 10 ++ src/timeline/TimelineViewManager.cpp | 64 ++++--- src/timeline/TimelineViewManager.h | 5 +- 6 files changed, 202 insertions(+), 83 deletions(-) diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 193dc7a99..33d2180d5 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -98,18 +98,15 @@ Page { id: deviceVerificationDialog DeviceVerification {} } - Component{ - id: deviceVerificationFlow - DeviceVerificationFlow {} - } Connections { target: timelineManager onNewDeviceVerificationRequest: { - var newFlow = deviceVerificationFlow.createObject(timelineRoot, - {userId : userId,sender: false,deviceId : deviceId,tranId:transactionId}); - deviceVerificationList.add(newFlow.tranId); + flow.userId = userId; + flow.sender = false; + flow.deviceId = deviceId; + flow.tranId = transactionId; var dialog = deviceVerificationDialog.createObject(timelineRoot, - {flow: newFlow,sender: false}); + {flow: flow,sender: false}); dialog.show(); } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index ad0edeb7f..316fbe400 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -78,7 +78,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -135,7 +135,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -179,7 +179,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -211,15 +211,15 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter Label { font.pixelSize: Qt.application.font.pixelSize * 2 - text: "1234" + text: flow.sasList[0] } Label { font.pixelSize: Qt.application.font.pixelSize * 2 - text: "1234" + text: flow.sasList[1] } Label { font.pixelSize: Qt.application.font.pixelSize * 2 - text: "1234" + text: flow.sasList[2] } } @@ -230,7 +230,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -345,7 +345,7 @@ ApplicationWindow { ColumnLayout { id: col anchors.bottom: parent.bottom - property var emoji: emojis.mapping[Math.floor(Math.random()*64)] + property var emoji: emojis.mapping[flow.sasList[index]] Label { //height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter @@ -369,7 +369,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -413,7 +413,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -451,7 +451,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -486,7 +486,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -521,7 +521,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index efb9882bc..607cc2796 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -15,32 +15,80 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) { timeout = new QTimer(this); timeout->setSingleShot(true); - if (this->sender == true) - this->transaction_id = http::client()->generate_txn_id(); + this->sas = olm::client()->sas_init(); connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->deleteLater(); }); + connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationAccept, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Accept" << std::endl; - } - }); - connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationRequest, + &ChatPage::recievedDeviceVerificationStart, this, [this](const mtx::events::collections::DeviceEvents &message) { auto msg = - std::get>(message); + std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Request" << std::endl; + if (std::find(msg.content.key_agreement_protocols.begin(), + msg.content.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != + msg.content.key_agreement_protocols.end() && + std::find(msg.content.hashes.begin(), + msg.content.hashes.end(), + "sha256") != msg.content.hashes.end() && + (std::find(msg.content.message_authentication_codes.begin(), + msg.content.message_authentication_codes.end(), + "hmac-sha256") != + msg.content.message_authentication_codes.end() || + std::find(msg.content.message_authentication_codes.begin(), + msg.content.message_authentication_codes.end(), + "hkdf-hmac-sha256") != + msg.content.message_authentication_codes.end()) && + (std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.content.short_authentication_string.end() || + std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.content.short_authentication_string.end())) { + this->sendVerificationKey(); // Not sure about this maybe + // those optional methods + this->canonical_json = nlohmann::json(msg); + } else { + this->cancelVerification(); + } } }); + connect( + ChatPage::instance(), + &ChatPage::recievedDeviceVerificationAccept, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + if ((msg.content.method == + mtx::events::msg::VerificationMethods::SASv1) && + (msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") && + (msg.content.hash == "sha256") && + ((msg.content.message_authentication_code == "hkdf-hmac-sha256") || + (msg.content.message_authentication_code == "hmac-sha256"))) { + this->commitment = msg.content.commitment; + if (std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.content.short_authentication_string.end()) { + this->method = DeviceVerificationFlow::Method::Emoji; + } else { + this->method = DeviceVerificationFlow::Method::Decimal; + } + this->mac_method = msg.content.message_authentication_code; + this->sendVerificationKey(); + } else { + this->cancelVerification(); + } + } + }); connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationCancel, this, @@ -48,19 +96,55 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Cancel" << std::endl; - } - }); - connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationKey, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Key" << std::endl; + emit verificationCanceled(); } }); + connect( + ChatPage::instance(), + &ChatPage::recievedDeviceVerificationKey, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + this->sas->set_their_key(msg.content.key); + std::string info; + if (this->sender == true) { + info = "MATRIX_KEY_VERIFICATION_SAS|" + + http::client()->user_id().to_string() + "|" + + http::client()->device_id() + "|" + + this->sas->public_key() + "|" + + this->toClient.to_string() + "|" + + this->deviceId.toStdString() + "|" + msg.content.key + + "|" + this->transaction_id; + } else { + info = "MATRIX_KEY_VERIFICATION_SAS|" + + this->toClient.to_string() + "|" + + this->deviceId.toStdString() + "|" + msg.content.key + + "|" + http::client()->user_id().to_string() + "|" + + http::client()->device_id() + "|" + + this->sas->public_key() + "|" + this->transaction_id; + } + + if (this->method == DeviceVerificationFlow::Method::Emoji) { + this->sasList = this->sas->generate_bytes_emoji(info); + } else if (this->method == DeviceVerificationFlow::Method::Decimal) { + this->sasList = this->sas->generate_bytes_decimal(info); + } + if (this->sender == false) { + emit this->verificationRequestAccepted(this->method); + this->sendVerificationKey(); + } else { + if (this->commitment == + mtx::crypto::bin2base64_unpadded(mtx::crypto::sha256( + msg.content.key + + this->canonical_json["content"].dump()))) { + emit this->verificationRequestAccepted(this->method); + } else { + this->cancelVerification(); + } + } + } + }); connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationMac, this, @@ -104,6 +188,12 @@ DeviceVerificationFlow::getSender() return this->sender; } +std::vector +DeviceVerificationFlow::getSasList() +{ + return this->sasList; +} + void DeviceVerificationFlow::setTransactionId(QString transaction_id_) { @@ -133,6 +223,8 @@ void DeviceVerificationFlow::setSender(bool sender_) { this->sender = sender_; + if (this->sender == true) + this->transaction_id = http::client()->generate_txn_id(); } //! accepts a verification @@ -147,23 +239,26 @@ DeviceVerificationFlow::acceptVerificationRequest() req.key_agreement_protocol = "curve25519-hkdf-sha256"; req.hash = "sha256"; req.message_authentication_code = "hkdf-hmac-sha256"; - req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, - mtx::events::msg::SASMethods::Emoji}; - req.commitment = ""; - - emit this->verificationRequestAccepted(this->method); + if (this->method == DeviceVerificationFlow::Method::Emoji) + req.short_authentication_string = {mtx::events::msg::SASMethods::Emoji}; + else if (this->method == DeviceVerificationFlow::Method::Decimal) + req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal}; + req.commitment = mtx::crypto::bin2base64_unpadded( + mtx::crypto::sha256(this->sas->public_key() + this->canonical_json.dump())); body[this->toClient][this->deviceId.toStdString()] = req; + std::cout << "Accepting the Verification" << std::endl; + std::cout << json(body) << std::endl; + http::client() ->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { + this->transaction_id, body, [](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to accept verification request: {} {}", err->matrix_error.error, static_cast(err->status_code)); - emit this->verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); }); } //! starts the verification flow @@ -183,6 +278,7 @@ DeviceVerificationFlow::startVerificationRequest() mtx::events::msg::SASMethods::Emoji}; body[this->toClient][this->deviceId.toStdString()] = req; + this->canonical_json = nlohmann::json(req); http::client() ->send_to_devicewarn("failed to start verification request: {} {}", err->matrix_error.error, static_cast(err->status_code)); - std::cout << nlohmann::json(body).dump(2) << std::endl; }); } //! sends a verification request @@ -236,6 +331,8 @@ DeviceVerificationFlow::cancelVerification() body[this->toClient][deviceId.toStdString()] = req; + emit this->verificationCanceled(); + http::client() ->send_to_device( @@ -254,7 +351,7 @@ DeviceVerificationFlow::sendVerificationKey() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationKey req; - req.key = ""; + req.key = this->sas->public_key(); req.transaction_id = this->transaction_id; body[this->toClient][deviceId.toStdString()] = req; diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index b651394ba..bddf8edd1 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -7,6 +7,8 @@ class QTimer; +using sas_ptr = std::unique_ptr; + class DeviceVerificationFlow : public QObject { Q_OBJECT @@ -16,6 +18,7 @@ class DeviceVerificationFlow : public QObject Q_PROPERTY(QString userId READ getUserId WRITE setUserId) Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) Q_PROPERTY(Method method READ getMethod WRITE setMethod) + Q_PROPERTY(std::vector sasList READ getSasList) public: enum Method @@ -30,6 +33,7 @@ class DeviceVerificationFlow : public QObject QString getUserId(); QString getDeviceId(); Method getMethod(); + std::vector getSasList(); void setTransactionId(QString transaction_id_); bool getSender(); void setUserId(QString userID); @@ -37,6 +41,8 @@ class DeviceVerificationFlow : public QObject void setMethod(Method method_); void setSender(bool sender_); + nlohmann::json canonical_json; + public slots: //! sends a verification request void sendVerificationRequest(); @@ -66,6 +72,10 @@ public slots: bool sender; QTimer *timeout = nullptr; + sas_ptr sas; + std::string mac_method; std::string transaction_id; + std::string commitment; mtx::identifiers::User toClient; + std::vector sasList; }; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index e2e875634..6a41c9085 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -166,32 +166,44 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin &ChatPage::decryptSidebarChanged, this, &TimelineViewManager::updateEncryptedDescriptions); - connect(dynamic_cast(parent), - &ChatPage::recievedDeviceVerificationRequest, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - QString tranID = QString::fromStdString(msg.content.transaction_id); - QString deviceId = QString::fromStdString(msg.content.from_device); - QString userId = QString::fromStdString(msg.sender); - if (!(this->dvList->exist(tranID))) { - emit newDeviceVerificationRequest(tranID, userId, deviceId); - } - }); - connect(dynamic_cast(parent), - &ChatPage::recievedDeviceVerificationStart, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - QString tranID = QString::fromStdString(msg.content.transaction_id); - QString deviceId = QString::fromStdString(msg.content.from_device); - QString userId = QString::fromStdString(msg.sender); - if (!(this->dvList->exist(tranID))) { - emit newDeviceVerificationRequest(tranID, userId, deviceId); - } - }); + connect( + dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationRequest, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + auto flow = new DeviceVerificationFlow(this); + if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) { + if (std::find(msg.content.methods.begin(), + msg.content.methods.end(), + mtx::events::msg::VerificationMethods::SASv1) != + msg.content.methods.end()) { + emit newDeviceVerificationRequest( + std::move(flow), + QString::fromStdString(msg.content.transaction_id), + QString::fromStdString(msg.sender), + QString::fromStdString(msg.content.from_device)); + } + } + }); + connect( + dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationStart, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + auto flow = new DeviceVerificationFlow(this); + flow->canonical_json = nlohmann::json(msg.content); + if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) { + emit newDeviceVerificationRequest( + std::move(flow), + QString::fromStdString(msg.content.transaction_id), + QString::fromStdString(msg.sender), + QString::fromStdString(msg.content.from_device)); + } + }); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 1343e4564..67c073960 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -67,7 +67,10 @@ class TimelineViewManager : public QObject void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); - void newDeviceVerificationRequest(QString transactionId, QString userId, QString deviceId); + void newDeviceVerificationRequest(DeviceVerificationFlow *flow, + QString transactionId, + QString userId, + QString deviceId); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); From 33c13eeb057b7003404bb27dfb33007256170669 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 23 Jun 2020 03:35:56 +0530 Subject: [PATCH 15/28] Add some more slots and mac --- .../DeviceVerification.qml | 4 +- src/ChatPage.h | 2 + src/DeviceVerificationFlow.cpp | 195 ++++++++++++++---- src/DeviceVerificationFlow.h | 6 + src/Olm.cpp | 11 +- src/timeline/TimelineViewManager.cpp | 1 + 6 files changed, 172 insertions(+), 47 deletions(-) diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 316fbe400..1a3d14329 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -240,7 +240,7 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } } } } @@ -379,7 +379,7 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } } } } diff --git a/src/ChatPage.h b/src/ChatPage.h index e245502ac..0b395033e 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -174,6 +174,8 @@ public slots: void recievedDeviceVerificationKey(const mtx::events::collections::DeviceEvents &message); void recievedDeviceVerificationMac(const mtx::events::collections::DeviceEvents &message); void recievedDeviceVerificationStart(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationReady(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationDone(const mtx::events::collections::DeviceEvents &message); private slots: void showUnreadMessageNotification(int count); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 607cc2796..0de94c504 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,11 +1,10 @@ #include "DeviceVerificationFlow.h" #include "ChatPage.h" #include "Logging.h" +#include "Utils.h" #include -#include // only for debugging #include -#include // only for debugging static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -28,32 +27,27 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - if (std::find(msg.content.key_agreement_protocols.begin(), - msg.content.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != - msg.content.key_agreement_protocols.end() && - std::find(msg.content.hashes.begin(), - msg.content.hashes.end(), - "sha256") != msg.content.hashes.end() && + if ((std::find(msg.content.key_agreement_protocols.begin(), + msg.content.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != + msg.content.key_agreement_protocols.end()) && + (std::find(msg.content.hashes.begin(), + msg.content.hashes.end(), + "sha256") != msg.content.hashes.end()) && (std::find(msg.content.message_authentication_codes.begin(), msg.content.message_authentication_codes.end(), "hmac-sha256") != - msg.content.message_authentication_codes.end() || - std::find(msg.content.message_authentication_codes.begin(), - msg.content.message_authentication_codes.end(), - "hkdf-hmac-sha256") != - msg.content.message_authentication_codes.end()) && - (std::find(msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.content.short_authentication_string.end() || - std::find(msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.content.short_authentication_string.end())) { - this->sendVerificationKey(); // Not sure about this maybe - // those optional methods - this->canonical_json = nlohmann::json(msg); + msg.content.message_authentication_codes.end()) && + ((std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.content.short_authentication_string.end()) || + (std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.content.short_authentication_string.end()))) { + this->acceptVerificationRequest(); + this->canonical_json = nlohmann::json(msg.content); } else { this->cancelVerification(); } @@ -67,12 +61,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - if ((msg.content.method == - mtx::events::msg::VerificationMethods::SASv1) && - (msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") && + if ((msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") && (msg.content.hash == "sha256") && - ((msg.content.message_authentication_code == "hkdf-hmac-sha256") || - (msg.content.message_authentication_code == "hmac-sha256"))) { + (msg.content.message_authentication_code == "hkdf-hmac-sha256")) { this->commitment = msg.content.commitment; if (std::find(msg.content.short_authentication_string.begin(), msg.content.short_authentication_string.end(), @@ -136,8 +127,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) } else { if (this->commitment == mtx::crypto::bin2base64_unpadded(mtx::crypto::sha256( - msg.content.key + - this->canonical_json["content"].dump()))) { + msg.content.key + this->canonical_json.dump()))) { emit this->verificationRequestAccepted(this->method); } else { this->cancelVerification(); @@ -152,7 +142,51 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Mac" << std::endl; + std::string info = + "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() + + this->deviceId.toStdString() + + http::client()->user_id().to_string() + + http::client()->device_id() + this->transaction_id; + + std::vector key_list; + std::string key_string; + for (auto mac : msg.content.mac) { + if (mac.second == + this->sas->calculate_mac(this->device_keys[mac.first], + info + mac.first)) { + key_string += mac.first; + } else { + this->cancelVerification(); + return; + } + } + if (msg.content.keys == + this->sas->calculate_mac(key_string, info + "KEY_IDS")) { + this->sendVerificationDone(); + } else { + this->cancelVerification(); + } + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationReady, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + this->startVerificationRequest(); + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationDone, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + this->startVerificationRequest(); + emit this->deviceVerified(); } }); timeout->start(TIMEOUT); @@ -205,6 +239,34 @@ DeviceVerificationFlow::setUserId(QString userID) { this->userId = userID; this->toClient = mtx::identifiers::parse(userID.toStdString()); + + mtx::responses::QueryKeys res; + mtx::requests::QueryKeys req; + req.device_keys[userID.toStdString()] = {}; + http::client()->query_keys( + req, + [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + for (auto x : res.device_keys) { + for (auto y : x.second) { + auto z = y.second; + if (z.user_id == user_id && + z.device_id == this->deviceId.toStdString()) { + for (auto a : z.keys) { + // TODO: Verify Signatures + this->device_keys[a.first] = a.second; + } + } + } + } + }); } void @@ -248,9 +310,6 @@ DeviceVerificationFlow::acceptVerificationRequest() body[this->toClient][this->deviceId.toStdString()] = req; - std::cout << "Accepting the Verification" << std::endl; - std::cout << json(body) << std::endl; - http::client() ->send_to_device( @@ -261,6 +320,50 @@ DeviceVerificationFlow::acceptVerificationRequest() static_cast(err->status_code)); }); } +//! responds verification request +void +DeviceVerificationFlow::sendVerificationReady() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationReady req; + + req.from_device = http::client()->device_id(); + req.transaction_id = this->transaction_id; + req.methods = {mtx::events::msg::VerificationMethods::SASv1}; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification ready: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} +//! accepts a verification +void +DeviceVerificationFlow::sendVerificationDone() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationDone req; + + req.transaction_id = this->transaction_id; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification done: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} //! starts the verification flow void DeviceVerificationFlow::startVerificationRequest() @@ -373,9 +476,25 @@ DeviceVerificationFlow::sendVerificationMac() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationMac req; + std::string info = "MATRIX_KEY_VERIFICATION_MAC" + http::client()->user_id().to_string() + + http::client()->device_id() + this->toClient.to_string() + + this->deviceId.toStdString() + this->transaction_id; + req.transaction_id = this->transaction_id; - // req.mac = ""; - req.keys = ""; + //! this vector stores the type of the key and the key + std::vector> key_list; + key_list.push_back(make_pair("ed25519", olm::client()->identity_keys().ed25519)); + std::sort(key_list.begin(), key_list.end()); + for (auto x : key_list) { + req.mac.insert( + std::make_pair(x.first + ":" + http::client()->device_id(), + this->sas->calculate_mac( + x.second, info + x.first + ":" + http::client()->device_id()))); + req.keys += x.first + ":" + http::client()->device_id() + ","; + } + + req.keys = + this->sas->calculate_mac(req.keys.substr(0, req.keys.size() - 1), info + "KEY_IDS"); body[this->toClient][deviceId.toStdString()] = req; diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index bddf8edd1..81ab9c990 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -2,6 +2,7 @@ #include "Olm.h" +#include "mtx/responses/crypto.hpp" #include #include @@ -46,6 +47,10 @@ class DeviceVerificationFlow : public QObject public slots: //! sends a verification request void sendVerificationRequest(); + //! accepts a verification request + void sendVerificationReady(); + //! completes the verification flow(); + void sendVerificationDone(); //! accepts a verification void acceptVerificationRequest(); //! starts the verification flow @@ -78,4 +83,5 @@ public slots: std::string commitment; mtx::identifiers::User toClient; std::vector sasList; + std::map device_keys; }; diff --git a/src/Olm.cpp b/src/Olm.cpp index 632e678c6..0364b959f 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -9,7 +9,6 @@ #include "MatrixClient.h" #include "Utils.h" #include -#include // only for debugging static const std::string STORAGE_SECRET_KEY("secret"); constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2"; @@ -79,22 +78,20 @@ handle_to_device_messages(const std::vectorrecievedDeviceVerificationAccept(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { ChatPage::instance()->recievedDeviceVerificationRequest(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { ChatPage::instance()->recievedDeviceVerificationCancel(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { ChatPage::instance()->recievedDeviceVerificationKey(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { ChatPage::instance()->recievedDeviceVerificationMac(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { ChatPage::instance()->recievedDeviceVerificationStart(msg); - std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) { + ChatPage::instance()->recievedDeviceVerificationReady(msg); + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) { + ChatPage::instance()->recievedDeviceVerificationDone(msg); } else { nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2)); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 6a41c9085..15c1e3f66 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -179,6 +179,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin msg.content.methods.end(), mtx::events::msg::VerificationMethods::SASv1) != msg.content.methods.end()) { + flow->sendVerificationReady(); emit newDeviceVerificationRequest( std::move(flow), QString::fromStdString(msg.content.transaction_id), From 0d8bbfe03ff646148a7d1f7f5ca1394a6f37228a Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 23 Jun 2020 23:29:00 +0530 Subject: [PATCH 16/28] Some Improvements - DeviceVerificationList change to LinkedList to improve time complexity while deleting - Downgrade the flow to not use key.verification.done and key.verification.ready --- resources/qml/TimelineView.qml | 1 + .../DeviceVerification.qml | 26 +++++++++++-------- src/DeviceVerificationFlow.cpp | 6 +++-- src/timeline/TimelineViewManager.cpp | 17 +++--------- src/timeline/TimelineViewManager.h | 3 ++- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 33d2180d5..1658b0424 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -105,6 +105,7 @@ Page { flow.sender = false; flow.deviceId = deviceId; flow.tranId = transactionId; + deviceVerificationList.add(flow.tranId); var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,sender: false}); dialog.show(); diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 1a3d14329..03fc50553 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -30,7 +30,11 @@ ApplicationWindow { implicitHeight: currentItem.implicitHeight } - onClosing: stack.replace(newVerificationRequest) + onClosing: { + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } property var flow Connections { @@ -78,7 +82,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -135,7 +139,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -179,7 +183,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -230,7 +234,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -369,7 +373,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -413,7 +417,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -451,7 +455,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -485,8 +489,8 @@ ApplicationWindow { Layout.alignment: Qt.AlignRight text: "Close" onClicked: { - dialog.close() - // deviceVerificationList.remove(flow.tranId); + dialog.close(); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -521,7 +525,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 0de94c504..db76aeb18 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,7 +1,6 @@ #include "DeviceVerificationFlow.h" #include "ChatPage.h" #include "Logging.h" -#include "Utils.h" #include #include @@ -162,7 +161,10 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) } if (msg.content.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) { - this->sendVerificationDone(); + // uncomment this in future to be compatible with the + // MSC2366 this->sendVerificationDone(); and remoeve the + // below line + emit this->deviceVerified(); } else { this->cancelVerification(); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 15c1e3f66..a82eadf84 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -26,26 +26,17 @@ namespace msgs = mtx::events::msg; void DeviceVerificationList::add(QString tran_id) { - this->dv_list.push_back(tran_id); + this->dv_list.append(tran_id); } void DeviceVerificationList::remove(QString tran_id) { - for (QVector::iterator it = 0; it != (this->dv_list).end(); ++it) { - if (*it == tran_id) { - this->dv_list.erase(it); - break; - } - } + this->dv_list.removeOne(tran_id); } bool DeviceVerificationList::exist(QString tran_id) { - for (int i = 0; i < (this->dv_list).size(); ++i) { - if (dv_list[i] == tran_id) - return true; - } - return false; + return this->dv_list.contains(tran_id); } void @@ -179,7 +170,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin msg.content.methods.end(), mtx::events::msg::VerificationMethods::SASv1) != msg.content.methods.end()) { - flow->sendVerificationReady(); + // flow->sendVerificationReady(); emit newDeviceVerificationRequest( std::move(flow), QString::fromStdString(msg.content.transaction_id), diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 67c073960..9fcf21a66 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -31,7 +32,7 @@ class DeviceVerificationList : public QObject Q_INVOKABLE bool exist(QString tran_id); private: - QVector dv_list; + QLinkedList dv_list; }; class TimelineViewManager : public QObject From 7cc2df9effca3d311e173b7406dcd2daeddf73cd Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Wed, 24 Jun 2020 23:05:32 +0530 Subject: [PATCH 17/28] Some more changes - remove unnecessary field sender in userprofile.qml - cover user facing string with qsTr to get picked by translations - add spacing and fix theming issue - increase and add color to username - change back to QVector from QLinkedList cause I have mistaken better time complexity to give better benchmark red --- resources/qml/TimelineView.qml | 2 +- resources/qml/UserProfile.qml | 36 ++- .../DeviceVerification.qml | 209 ++++++++++++++---- src/timeline/TimelineViewManager.cpp | 6 +- src/timeline/TimelineViewManager.h | 3 +- 5 files changed, 193 insertions(+), 63 deletions(-) diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 1658b0424..00c375e7b 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -107,7 +107,7 @@ Page { flow.tranId = transactionId; deviceVerificationList.add(flow.tranId); var dialog = deviceVerificationDialog.createObject(timelineRoot, - {flow: flow,sender: false}); + {flow: flow}); dialog.show(); } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 80415a292..f060b0e37 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -13,7 +13,7 @@ ApplicationWindow{ id:userProfileDialog height: 500 - width: 500 + width: 400 modality:Qt.WindowModal Layout.alignment: Qt.AlignHCenter palette: colors @@ -64,6 +64,9 @@ ApplicationWindow{ id: userProfileName text: user_data.userName fontSizeMode: Text.HorizontalFit + font.pixelSize: 16 + color:timelineManager.userColor(modelData.userId, colors.window) + font.bold: true Layout.alignment: Qt.AlignHCenter } @@ -71,6 +74,8 @@ ApplicationWindow{ id: matrixUserID text: user_data.userId fontSizeMode: Text.HorizontalFit + font.pixelSize: 16 + color:colors.text Layout.alignment: Qt.AlignHCenter } @@ -79,8 +84,6 @@ ApplicationWindow{ implicitWidth: userProfileDialog.width-20 clip: true Layout.alignment: Qt.AlignHCenter - ScrollBar.horizontal.policy: ScrollBar.AlwaysOn - ScrollBar.vertical.policy: ScrollBar.AlwaysOn ListView{ id: deviceList @@ -98,6 +101,7 @@ ApplicationWindow{ Text{ Layout.fillWidth: true color: colors.text + font.bold: true Layout.alignment: Qt.AlignRight text: deviceID } @@ -116,12 +120,15 @@ ApplicationWindow{ {userId : user_data.userId,sender: true,deviceId : model.deviceID}); deviceVerificationList.add(newFlow.tranId); var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: newFlow,sender: true}); + {flow: newFlow}); dialog.show(); } + palette { + button: "white" + } contentItem: Text { text: verifyButton.text - color: colors.background + color: "black" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } @@ -134,19 +141,26 @@ ApplicationWindow{ id: okbutton text:"OK" onClicked: userProfileDialog.close() - anchors.margins: { - right:10 - bottom:10 + anchors { + right: parent.right + bottom: parent.bottom + } + + anchors.margins : { + right : 10 + bottom : 10 + } + + palette { + button: "white" } contentItem: Text { text: okbutton.text - color: colors.background + color: "black" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } - - Layout.alignment: Qt.AlignRight | Qt.AlignBottom } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 03fc50553..12e38f294 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -7,7 +7,6 @@ import Qt.labs.settings 1.0 import im.nheko 1.0 ApplicationWindow { - property bool sender: true title: stack.currentItem.title id: dialog @@ -25,7 +24,7 @@ ApplicationWindow { width: stack.implicitWidth StackView { id: stack - initialItem: sender == true?newVerificationRequest:acceptNewVerificationRequest + initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest implicitWidth: currentItem.implicitWidth implicitHeight: currentItem.implicitHeight } @@ -52,7 +51,7 @@ ApplicationWindow { Component { id: newVerificationRequest Pane { - property string title: "Sending Device Verification Request" + property string title: qsTr("Sending Device Verification Request") ColumnLayout { spacing: 16 Label { @@ -60,8 +59,8 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "A new device was added." - + text: qsTr("A new device was added.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -70,15 +69,24 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "The device may have been added by you signing in from another client or physical device. To ensure that no malicious user can eavesdrop on your encrypted communications, you should verify the new device." - + text: qsTr("The device may have been added by you signing in from another client or physical device. To ensure that no malicious user can eavesdrop on your encrypted communications, you should verify the new device.") + color:colors.text verticalAlignment: Text.AlignVCenter } RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "Cancel" + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -91,7 +99,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Start verification" + text: qsTr("Start verification") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.startVerificationRequest(); } } } @@ -102,7 +119,7 @@ ApplicationWindow { Component { id: acceptNewVerificationRequest Pane { - property string title: "Recieving Device Verification Request" + property string title: qsTr("Recieving Device Verification Request") ColumnLayout { spacing: 16 @@ -111,15 +128,15 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "The device was requested to be verified" - + text: qsTr("The device was requested to be verified") + color:colors.text verticalAlignment: Text.AlignVCenter } RowLayout { RadioButton { Layout.alignment: Qt.AlignLeft - text: "Decimal" + text: qsTr("Decimal") onClicked: { flow.method = DeviceVerificationFlow.Decimal } } Item { @@ -127,7 +144,7 @@ ApplicationWindow { } RadioButton { Layout.alignment: Qt.AlignRight - text: "Emoji" + text: qsTr("Emoji") onClicked: { flow.method = DeviceVerificationFlow.Emoji } } } @@ -135,7 +152,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "Deny" + text: qsTr("Deny") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -148,7 +174,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Accept" + text: qsTr("Accept") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.acceptVerificationRequest(); } } } @@ -159,7 +194,7 @@ ApplicationWindow { Component { id: awaitingVerificationRequestAccept Pane { - property string title: "Waiting for other party" + property string title: qsTr("Waiting for other party") ColumnLayout { spacing: 16 Label { @@ -168,8 +203,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Waiting for other side to accept the verification request." - + text: qsTr("Waiting for other side to accept the verification request.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -179,7 +214,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "Cancel" + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -198,7 +242,7 @@ ApplicationWindow { Component { id: digitVerification Pane { - property string title: "Verification Code" + property string title: qsTr("Verification Code") ColumnLayout { spacing: 16 Label { @@ -206,8 +250,8 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!" - + text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -230,7 +274,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "They do not match!" + text: qsTr("They do not match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -243,7 +296,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "They match." + text: qsTr("They match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } } } @@ -254,7 +316,7 @@ ApplicationWindow { Component { id: emojiVerification Pane { - property string title: "Verification Code" + property string title: qsTr("Verification Code") ColumnLayout { spacing: 16 Label { @@ -262,8 +324,8 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!" - + text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -369,7 +431,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "They do not match!" + text: qsTr("They do not match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -382,7 +453,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "They match." + text: qsTr("They match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } } } @@ -393,7 +473,7 @@ ApplicationWindow { Component { id: awaitingVerificationConfirmation Pane { - property string title: "Awaiting Confirmation" + property string title: qsTr("Awaiting Confirmation") ColumnLayout { spacing: 16 Label { @@ -402,8 +482,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Waiting for other side to complete verification." - + text: qsTr("Waiting for other side to complete verification.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -413,7 +493,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "Cancel" + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -432,7 +521,7 @@ ApplicationWindow { Component { id: verificationSuccess Pane { - property string title: "Successful Verification" + property string title: qsTr("Successful Verification") ColumnLayout { spacing: 16 Label { @@ -441,8 +530,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Verification successful! Both sides verified their devices!" - + text: qsTr("Verification successful! Both sides verified their devices!") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -452,7 +541,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Close" + text: qsTr("Close") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close() deviceVerificationList.remove(flow.tranId); @@ -467,7 +565,7 @@ ApplicationWindow { Component { id: partnerAborted Pane { - property string title: "Verification aborted!" + property string title: qsTr("Verification aborted!") ColumnLayout { spacing: 16 Label { @@ -476,8 +574,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Verification canceled by the other party!" - + text: qsTr("Verification canceled by the other party!") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -487,7 +585,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Close" + text: qsTr("Close") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); deviceVerificationList.remove(flow.tranId); @@ -502,7 +609,7 @@ ApplicationWindow { Component { id: timedout Pane { - property string title: "Verification timed out" + property string title: qsTr("Verification timed out") ColumnLayout { spacing: 16 Text { @@ -511,8 +618,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Device verification timed out." - + text: qsTr("Device verification timed out.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -521,8 +628,18 @@ ApplicationWindow { Layout.fillWidth: true } Button { + id: timedOutCancel Layout.alignment: Qt.AlignRight - text: "Close" + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + text: qsTr("Close") onClicked: { dialog.close() deviceVerificationList.remove(flow.tranId); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index a82eadf84..d467af37d 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -26,17 +26,17 @@ namespace msgs = mtx::events::msg; void DeviceVerificationList::add(QString tran_id) { - this->dv_list.append(tran_id); + this->deviceVerificationList.push_back(tran_id); } void DeviceVerificationList::remove(QString tran_id) { - this->dv_list.removeOne(tran_id); + this->deviceVerificationList.removeOne(tran_id); } bool DeviceVerificationList::exist(QString tran_id) { - return this->dv_list.contains(tran_id); + return this->deviceVerificationList.contains(tran_id); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 9fcf21a66..1d7a383f2 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -32,7 +31,7 @@ class DeviceVerificationList : public QObject Q_INVOKABLE bool exist(QString tran_id); private: - QLinkedList dv_list; + QVector deviceVerificationList; }; class TimelineViewManager : public QObject From ff1176e3d2a640ee5046dbaffcc5d9168fbb4547 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 24 Jun 2020 16:24:22 +0200 Subject: [PATCH 18/28] Fix presence indicator --- resources/qml/Avatar.qml | 20 ++++++++++--------- resources/qml/MatrixText.qml | 6 ++++-- resources/qml/Reactions.qml | 8 +++++--- resources/qml/TimelineRow.qml | 10 +++++----- resources/qml/TimelineView.qml | 18 ++++++++--------- resources/qml/UserProfile.qml | 3 ++- resources/qml/delegates/FileMessage.qml | 4 +++- resources/qml/delegates/ImageMessage.qml | 2 +- resources/qml/delegates/MessageDelegate.qml | 12 +++++------ .../qml/delegates/PlayableMediaMessage.qml | 4 ++-- resources/qml/delegates/Reply.qml | 4 +++- resources/qml/delegates/TextMessage.qml | 4 +++- .../DeviceVerification.qml | 9 +-------- resources/qml/emoji/EmojiPicker.qml | 4 ++-- src/timeline/TimelineViewManager.cpp | 20 +++++++++++-------- src/timeline/TimelineViewManager.h | 4 ++-- 16 files changed, 71 insertions(+), 61 deletions(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 0c53f94e7..e687e1705 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -2,11 +2,13 @@ import QtQuick 2.6 import QtQuick.Controls 2.3 import QtGraphicalEffects 1.0 +import im.nheko 1.0 + Rectangle { id: avatar width: 48 height: 48 - radius: settings.avatarCircles ? height/2 : 3 + radius: Settings.avatarCircles ? height/2 : 3 property alias url: img.source property string userid @@ -40,7 +42,7 @@ Rectangle { anchors.fill: parent width: avatar.width height: avatar.height - radius: settings.avatarCircles ? height/2 : 3 + radius: Settings.avatarCircles ? height/2 : 3 } } @@ -52,13 +54,13 @@ Rectangle { height: avatar.height / 6 width: height - radius: settings.avatarCircles ? height / 2 : height / 4 - // color: switch (timelineManager.userPresence(userid)) { - // case "online": return "#00cc66" - // case "unavailable": return "#ff9933" - // case "offline": return "#a82353" - // default: "transparent" - // } + radius: Settings.avatarCircles ? height / 2 : height / 4 + color: switch (TimelineManager.userPresence(userid)) { + case "online": return "#00cc66" + case "unavailable": return "#ff9933" + case "offline": // return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled + default: "transparent" + } } color: colors.base diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml index 9a4f73488..a50f2a712 100644 --- a/resources/qml/MatrixText.qml +++ b/resources/qml/MatrixText.qml @@ -1,6 +1,8 @@ import QtQuick 2.5 import QtQuick.Controls 2.3 +import im.nheko 1.0 + TextEdit { textFormat: TextEdit.RichText readOnly: true @@ -10,10 +12,10 @@ TextEdit { onLinkActivated: { if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1]) - else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) timelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1]) + else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) TimelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1]) else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) { var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link) - timelineManager.setHistoryView(match[1]) + TimelineManager.setHistoryView(match[1]) chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain) } else Qt.openUrlExternally(link) diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml index c06dc826b..32a5313f8 100644 --- a/resources/qml/Reactions.qml +++ b/resources/qml/Reactions.qml @@ -1,6 +1,8 @@ import QtQuick 2.6 import QtQuick.Controls 2.2 +import im.nheko 1.0 + // This class is for showing Reactions in the timeline row, not for // adding new reactions via the emoji picker Flow { @@ -34,7 +36,7 @@ Flow { onClicked: { console.debug("Picked " + model.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + model.selfReactedEvent) - timelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, model.key, model.selfReactedEvent) + TimelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, model.key, model.selfReactedEvent) } @@ -46,7 +48,7 @@ Flow { TextMetrics { id: textMetrics - font.family: settings.emojiFont + font.family: Settings.emojiFont elide: Text.ElideRight elideWidth: 150 text: model.key @@ -56,7 +58,7 @@ Flow { anchors.baseline: reactionCounter.baseline id: reactionText text: textMetrics.elidedText + (textMetrics.elidedText == model.key ? "" : "…") - font.family: settings.emojiFont + font.family: Settings.emojiFont color: reaction.hovered ? colors.highlight : colors.text maximumLineCount: 1 } diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index dfee62dc5..50f7df0c5 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -26,7 +26,7 @@ MouseArea { messageContextMenu.show(model.id, model.type, model.isEncrypted, row) } Rectangle { - color: (settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent" + color: (Settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent" anchors.fill: row } RowLayout { @@ -46,7 +46,7 @@ MouseArea { Reply { visible: model.replyTo modelData: chat.model.getDump(model.replyTo) - userColor: timelineManager.userColor(modelData.userId, colors.window) + userColor: TimelineManager.userColor(modelData.userId, colors.window) } // actual message content @@ -81,7 +81,7 @@ MouseArea { width: 16 } EmojiButton { - visible: settings.buttonsInTimeline + visible: Settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 @@ -94,7 +94,7 @@ MouseArea { event_id: model.id } ImageButton { - visible: settings.buttonsInTimeline + visible: Settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 @@ -110,7 +110,7 @@ MouseArea { onClicked: chat.model.replyAction(model.id) } ImageButton { - visible: settings.buttonsInTimeline + visible: Settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 00c375e7b..e782e2084 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -84,7 +84,7 @@ Page { visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker height: visible ? implicitHeight : 0 text: qsTr("Save as") - onTriggered: timelineManager.timeline.saveMedia(messageContextMenu.eventId) + onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId) } } @@ -99,7 +99,7 @@ Page { DeviceVerification {} } Connections { - target: timelineManager + target: TimelineManager onNewDeviceVerificationRequest: { flow.userId = userId; flow.sender = false; @@ -113,7 +113,7 @@ Page { } Label { - visible: !timelineManager.timeline && !timelineManager.isInitialSync + visible: !TimelineManager.timeline && !TimelineManager.isInitialSync anchors.centerIn: parent text: qsTr("No room open") font.pointSize: 24 @@ -123,7 +123,7 @@ Page { BusyIndicator { visible: running anchors.centerIn: parent - running: timelineManager.isInitialSync + running: TimelineManager.isInitialSync height: 200 width: 200 z: 3 @@ -132,7 +132,7 @@ Page { ListView { id: chat - visible: timelineManager.timeline != null + visible: TimelineManager.timeline != null cacheBuffer: 400 @@ -144,7 +144,7 @@ Page { anchors.leftMargin: 4 anchors.rightMargin: scrollbar.width - model: timelineManager.timeline + model: TimelineManager.timeline boundsBehavior: Flickable.StopAtBounds @@ -190,7 +190,7 @@ Page { onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom - property int delegateMaxWidth: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > 32) ? settings.timelineMaxWidth : (parent.width - 32) + property int delegateMaxWidth: (Settings.timelineMaxWidth > 100 && (parent.width - Settings.timelineMaxWidth) > 32) ? Settings.timelineMaxWidth : (parent.width - 32) delegate: Rectangle { // This would normally be previousSection, but our model's order is inverted. @@ -297,7 +297,7 @@ Page { Label { id: userName text: chat.model.escapeEmoji(modelData.userName) - color: timelineManager.userColor(modelData.userId, colors.window) + color: TimelineManager.userColor(modelData.userId, colors.window) textFormat: Text.RichText MouseArea { @@ -376,7 +376,7 @@ Page { anchors.bottom: parent.bottom modelData: chat.model ? chat.model.getDump(chat.model.reply) : {} - userColor: timelineManager.userColor(modelData.userId, colors.window) + userColor: TimelineManager.userColor(modelData.userId, colors.window) } ImageButton { diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index f060b0e37..a7ff8a35e 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -57,6 +57,7 @@ ApplicationWindow{ height: 130 width: 130 displayName: modelData.userName + userid: modelData.userId Layout.alignment: Qt.AlignHCenter } @@ -65,7 +66,7 @@ ApplicationWindow{ text: user_data.userName fontSizeMode: Text.HorizontalFit font.pixelSize: 16 - color:timelineManager.userColor(modelData.userId, colors.window) + color:TimelineManager.userColor(modelData.userId, colors.window) font.bold: true Layout.alignment: Qt.AlignHCenter } diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml index d8e4215fe..158daf453 100644 --- a/resources/qml/delegates/FileMessage.qml +++ b/resources/qml/delegates/FileMessage.qml @@ -1,6 +1,8 @@ import QtQuick 2.6 import QtQuick.Layouts 1.2 +import im.nheko 1.0 + Item { height: row.height + 24 width: parent ? parent.width : undefined @@ -29,7 +31,7 @@ Item { } MouseArea { anchors.fill: parent - onClicked: timelineManager.timeline.saveMedia(model.data.id) + onClicked: TimelineManager.timeline.saveMedia(model.data.id) cursorShape: Qt.PointingHandCursor } } diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml index 62d9de600..48644f063 100644 --- a/resources/qml/delegates/ImageMessage.qml +++ b/resources/qml/delegates/ImageMessage.qml @@ -36,7 +36,7 @@ Item { MouseArea { enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready anchors.fill: parent - onClicked: timelineManager.openImageOverlay(model.data.url, model.data.id) + onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id) } } } diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml index 17fe7360b..3ce752836 100644 --- a/resources/qml/delegates/MessageDelegate.qml +++ b/resources/qml/delegates/MessageDelegate.qml @@ -37,7 +37,7 @@ Item { roleValue: MtxEvent.EmoteMessage NoticeMessage { formatted: chat.model.escapeEmoji(modelData.userName) + " " + model.data.formattedBody - color: timelineManager.userColor(modelData.userId, colors.window) + color: TimelineManager.userColor(modelData.userId, colors.window) } } DelegateChoice { @@ -94,31 +94,31 @@ Item { // TODO: make a more complex formatter for the power levels. roleValue: MtxEvent.PowerLevels NoticeMessage { - text: timelineManager.timeline.formatPowerLevelEvent(model.data.id) + text: TimelineManager.timeline.formatPowerLevelEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.RoomJoinRules NoticeMessage { - text: timelineManager.timeline.formatJoinRuleEvent(model.data.id) + text: TimelineManager.timeline.formatJoinRuleEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.RoomHistoryVisibility NoticeMessage { - text: timelineManager.timeline.formatHistoryVisibilityEvent(model.data.id) + text: TimelineManager.timeline.formatHistoryVisibilityEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.RoomGuestAccess NoticeMessage { - text: timelineManager.timeline.formatGuestAccessEvent(model.data.id) + text: TimelineManager.timeline.formatGuestAccessEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.Member NoticeMessage { - text: timelineManager.timeline.formatMemberEvent(model.data.id); + text: TimelineManager.timeline.formatMemberEvent(model.data.id); } } DelegateChoice { diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml index bab524eb2..e6e59ecb8 100644 --- a/resources/qml/delegates/PlayableMediaMessage.qml +++ b/resources/qml/delegates/PlayableMediaMessage.qml @@ -106,7 +106,7 @@ Rectangle { anchors.fill: parent onClicked: { switch (button.state) { - case "": timelineManager.timeline.cacheMedia(model.data.id); break; + case "": TimelineManager.timeline.cacheMedia(model.data.id); break; case "stopped": media.play(); console.log("play"); button.state = "playing" @@ -127,7 +127,7 @@ Rectangle { } Connections { - target: timelineManager.timeline + target: TimelineManager.timeline onMediaCached: { if (mxcUrl == model.data.url) { media.source = "file://" + cacheUrl diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml index f9fd3f111..d4fffb06d 100644 --- a/resources/qml/delegates/Reply.qml +++ b/resources/qml/delegates/Reply.qml @@ -3,6 +3,8 @@ import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 +import im.nheko 1.0 + Item { id: replyComponent @@ -26,7 +28,7 @@ Item { anchors.bottom: replyContainer.bottom width: 4 - color: timelineManager.userColor(reply.modelData.userId, colors.window) + color: TimelineManager.userColor(reply.modelData.userId, colors.window) } Column { diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml index b3c45c36b..ab9cffaff 100644 --- a/resources/qml/delegates/TextMessage.qml +++ b/resources/qml/delegates/TextMessage.qml @@ -1,10 +1,12 @@ import ".." +import im.nheko 1.0 + MatrixText { property string formatted: model.data.formattedBody text: "" + formatted.replace("
", "
")
 	width: parent ? parent.width : undefined
 	height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
 	clip: true
-	font.pointSize: (settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? settings.fontSize * 3 : settings.fontSize
+	font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
 }
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 12e38f294..516bc74a5 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -2,7 +2,6 @@ import QtQuick 2.3
 import QtQuick.Controls 2.10
 import QtQuick.Window 2.2
 import QtQuick.Layouts 1.10
-import Qt.labs.settings 1.0
 
 import im.nheko 1.0
 
@@ -14,12 +13,6 @@ ApplicationWindow {
 
 	palette: colors
 
-	Settings {
-		id: settings
-		category: "user"
-		property bool emoji_font_family: true
-	}
-
 	height: stack.implicitHeight
 	width: stack.implicitWidth
 	StackView {
@@ -417,7 +410,7 @@ ApplicationWindow {
 									Layout.alignment: Qt.AlignHCenter
 									text: col.emoji.emoji
 									font.pixelSize: Qt.application.font.pixelSize * 2
-									font.family: settings.emoji_font_family
+									font.family: Settings.emojiFont
 								}
 								Label {
 									Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
diff --git a/resources/qml/emoji/EmojiPicker.qml b/resources/qml/emoji/EmojiPicker.qml
index b70923aef..2c3f47c0c 100644
--- a/resources/qml/emoji/EmojiPicker.qml
+++ b/resources/qml/emoji/EmojiPicker.qml
@@ -73,7 +73,7 @@ Popup {
                 contentItem: Text {
                     horizontalAlignment: Text.AlignHCenter
                     verticalAlignment: Text.AlignVCenter
-                    font.family: settings.emojiFont
+                    font.family: Settings.emojiFont
                     
                     font.pixelSize: 36
                     text: model.unicode
@@ -104,7 +104,7 @@ Popup {
                 onClicked: {
                     console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id + " in room " + emojiPopup.room_id)
                     emojiPopup.close()
-                    timelineManager.queueReactionMessage(emojiPopup.room_id, emojiPopup.event_id, model.unicode)
+                    TimelineManager.queueReactionMessage(emojiPopup.room_id, emojiPopup.event_id, model.unicode)
                 }
             }
 
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index d467af37d..461de0158 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -80,12 +80,16 @@ TimelineViewManager::userColor(QString id, QColor background)
         return userColors.value(id);
 }
 
-// QString
-// TimelineViewManager::userPresence(QString id) const
-// {
-//         return QString::fromStdString(
-//           mtx::presence::to_string(cache::presenceState(id.toStdString())));
-// }
+QString
+TimelineViewManager::userPresence(QString id) const
+{
+        if (id.isEmpty())
+                return "";
+        else
+                return QString::fromStdString(
+                  mtx::presence::to_string(cache::presenceState(id.toStdString())));
+}
+
 QString
 TimelineViewManager::userStatus(QString id) const
 {
@@ -110,6 +114,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
         qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow");
         qmlRegisterType("im.nheko", 1, 0, "UserProfileModel");
         qmlRegisterType("im.nheko", 1, 0, "UserProfileList");
+        qmlRegisterSingletonInstance("im.nheko", 1, 0, "TimelineManager", this);
+        qmlRegisterSingletonInstance("im.nheko", 1, 0, "Settings", settings.data());
 
         qRegisterMetaType();
         qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel");
@@ -140,8 +146,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
         });
 #endif
         container->setMinimumSize(200, 200);
-        view->rootContext()->setContextProperty("timelineManager", this);
-        view->rootContext()->setContextProperty("settings", settings.data());
         view->rootContext()->setContextProperty("deviceVerificationList", this->dvList);
         updateColorPalette();
         view->engine()->addImageProvider("MxcImage", imgProvider);
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 1d7a383f2..92c540fd6 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -57,7 +57,7 @@ class TimelineViewManager : public QObject
         Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
         Q_INVOKABLE QColor userColor(QString id, QColor background);
 
-        // Q_INVOKABLE QString userPresence(QString id) const;
+        Q_INVOKABLE QString userPresence(QString id) const;
         Q_INVOKABLE QString userStatus(QString id) const;
 
 signals:
@@ -137,4 +137,4 @@ public slots:
 
         DeviceVerificationList *dvList;
 };
-Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents)
\ No newline at end of file
+Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents)

From 26874eb429ee339aeb6cf1d3c2557a73455d4fad Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Thu, 25 Jun 2020 22:38:48 +0530
Subject: [PATCH 19/28] Some more fixes

---
 resources/qml/UserProfile.qml  | 30 +++++++++++++++++-------------
 src/DeviceVerificationFlow.cpp | 12 ++++++++++--
 src/DeviceVerificationFlow.h   |  1 +
 3 files changed, 28 insertions(+), 15 deletions(-)

diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index a7ff8a35e..e5d01625f 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -13,7 +13,7 @@ ApplicationWindow{
 
     id:userProfileDialog
     height: 500
-    width: 400
+    width: 420
     modality:Qt.WindowModal
     Layout.alignment: Qt.AlignHCenter
     palette: colors
@@ -42,9 +42,8 @@ ApplicationWindow{
         id: userProfileItem
         width: userProfileDialog.width
         height: userProfileDialog.height
-        anchors.margins: {
-            top:20
-        }
+
+        Layout.fillHeight : true
 
         ColumnLayout{
             anchors.fill: userProfileItem
@@ -57,15 +56,18 @@ ApplicationWindow{
                 height: 130
                 width: 130
                 displayName: modelData.userName
-		userid: modelData.userId
+		        userid: modelData.userId
                 Layout.alignment: Qt.AlignHCenter
+                Layout.margins : {
+                    top: 10
+                }
             }
 
             Label{
                 id: userProfileName
                 text: user_data.userName
                 fontSizeMode: Text.HorizontalFit
-                font.pixelSize: 16
+                font.pixelSize: 20
                 color:TimelineManager.userColor(modelData.userId, colors.window)
                 font.bold: true
                 Layout.alignment: Qt.AlignHCenter
@@ -75,7 +77,7 @@ ApplicationWindow{
                 id: matrixUserID
                 text: user_data.userId
                 fontSizeMode: Text.HorizontalFit
-                font.pixelSize: 16
+                font.pixelSize: 15
                 color:colors.text
                 Layout.alignment: Qt.AlignHCenter
             }
@@ -90,7 +92,7 @@ ApplicationWindow{
                     id: deviceList
                     anchors.fill: parent
                     clip: true
-                    spacing: 10
+                    spacing: 4
 
                     model: UserProfileModel{
                         id: modelDeviceList
@@ -98,6 +100,9 @@ ApplicationWindow{
 
                     delegate: RowLayout{
                         width: parent.width
+                        Layout.margins : {
+                            top : 50
+                        }
                         ColumnLayout{
                             Text{
                                 Layout.fillWidth: true
@@ -124,6 +129,9 @@ ApplicationWindow{
                                     {flow: newFlow});
 				                dialog.show();
                             }
+                            Layout.margins:{
+                                right: 10
+                            }
                             palette {
                                 button: "white"
                             }
@@ -142,12 +150,8 @@ ApplicationWindow{
                 id: okbutton
                 text:"OK"
                 onClicked: userProfileDialog.close()
-                anchors {
-                    right: parent.right
-                    bottom: parent.bottom
-                }
 
-                anchors.margins : {
+                Layout.margins : {
                     right : 10
                     bottom : 10
                 }
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index db76aeb18..9f120a00b 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -164,7 +164,10 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                         // uncomment this in future to be compatible with the
                                         // MSC2366 this->sendVerificationDone(); and remoeve the
                                         // below line
-                                        emit this->deviceVerified();
+                                        if (this->isMacVerified == true)
+                                                emit this->deviceVerified();
+                                        else
+                                                this->isMacVerified = true;
                                 } else {
                                         this->cancelVerification();
                                 }
@@ -503,11 +506,16 @@ DeviceVerificationFlow::sendVerificationMac()
         http::client()
           ->send_to_device(
-            this->transaction_id, body, [](mtx::http::RequestErr err) {
+            this->transaction_id, body, [this](mtx::http::RequestErr err) {
                     if (err)
                             nhlog::net()->warn("failed to send verification MAC: {} {}",
                                                err->matrix_error.error,
                                                static_cast(err->status_code));
+
+                    if (this->isMacVerified == true)
+                            emit this->deviceVerified();
+                    else
+                            this->isMacVerified = true;
             });
 }
 //! Completes the verification flow
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 81ab9c990..5830e705a 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -78,6 +78,7 @@ public slots:
 
         QTimer *timeout = nullptr;
         sas_ptr sas;
+        bool isMacVerified;
         std::string mac_method;
         std::string transaction_id;
         std::string commitment;

From 303ceb99d49dfdeb4442509605ca9feafaacf485 Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Thu, 25 Jun 2020 23:29:50 +0530
Subject: [PATCH 20/28] Fix the Weird auto-confirmation and cancellation

---
 src/DeviceVerificationFlow.cpp | 1 -
 src/DeviceVerificationFlow.h   | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 9f120a00b..9b2608922 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -190,7 +190,6 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                         auto msg =
                           std::get>(message);
                         if (msg.content.transaction_id == this->transaction_id) {
-                                this->startVerificationRequest();
                                 emit this->deviceVerified();
                         }
                 });
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 5830e705a..ea86a10b5 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -78,7 +78,7 @@ public slots:
 
         QTimer *timeout = nullptr;
         sas_ptr sas;
-        bool isMacVerified;
+        bool isMacVerified = false;
         std::string mac_method;
         std::string transaction_id;
         std::string commitment;

From 2edbf5718c6806c61ed36f402ba502366b865dd6 Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Fri, 26 Jun 2020 04:24:42 +0530
Subject: [PATCH 21/28] Add some Userprofile buttons

---
 resources/qml/UserProfile.qml                 | 62 +++++++++++++-
 .../DeviceVerification.qml                    | 15 +++-
 src/DeviceVerificationFlow.cpp                | 83 ++++++++++---------
 src/ui/UserProfile.cpp                        | 28 +++++++
 src/ui/UserProfile.h                          |  5 +-
 5 files changed, 146 insertions(+), 47 deletions(-)

diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index e5d01625f..36c8586d0 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -12,7 +12,7 @@ ApplicationWindow{
     property var colors: currentActivePalette
 
     id:userProfileDialog
-    height: 500
+    height: 650
     width: 420
     modality:Qt.WindowModal
     Layout.alignment: Qt.AlignHCenter
@@ -43,7 +43,7 @@ ApplicationWindow{
         width: userProfileDialog.width
         height: userProfileDialog.height
 
-        Layout.fillHeight : true
+        // Layout.fillHeight : true
 
         ColumnLayout{
             anchors.fill: userProfileItem
@@ -82,8 +82,62 @@ ApplicationWindow{
                 Layout.alignment: Qt.AlignHCenter
             }
 
+            RowLayout{
+                Layout.alignment: Qt.AlignHCenter
+                ImageButton{
+                    image:":/icons/icons/ui/do-not-disturb-rounded-sign.png"
+                    Layout.margins: {
+                        left: 5
+                        right: 5
+                    }
+                    ToolTip.visible: hovered
+			        ToolTip.text: qsTr("Ban the user")
+                    onClicked : {
+                        userProfileList.banUser()
+                    }
+                }
+                // ImageButton{
+                //     image:":/icons/icons/ui/volume-off-indicator.png"
+                //     Layout.margins: {
+                //         left: 5
+                //         right: 5
+                //     }
+                //     ToolTip.visible: hovered
+			    //     ToolTip.text: qsTr("Ignore messages from this user")
+                //     onClicked : {
+                //         userProfileList.ignoreUser()
+                //     }
+                // }
+
+                ImageButton{
+                    image:":/icons/icons/ui/round-remove-button.png"
+                    Layout.margins: {
+                        left: 5
+                        right: 5
+                    }
+                    ToolTip.visible: hovered
+			        ToolTip.text: qsTr("Kick the user")
+                    onClicked : {
+                        userProfileList.kickUser()
+                    }
+                }
+
+                ImageButton{
+                    image:":/icons/icons/ui/black-bubble-speech.png"
+                    Layout.margins: {
+                        left: 5
+                        right: 5
+                    }
+                    ToolTip.visible: hovered
+			        ToolTip.text: qsTr("Start a conversation")
+                    onClicked : {
+                        userProfileList.startChat()
+                    }
+                }
+            }
+
             ScrollView {
-                implicitHeight: userProfileDialog.height/2+20
+                implicitHeight: userProfileDialog.height/2 + 20
                 implicitWidth: userProfileDialog.width-20
                 clip: true
                 Layout.alignment: Qt.AlignHCenter
@@ -150,6 +204,8 @@ ApplicationWindow{
                 id: okbutton
                 text:"OK"
                 onClicked: userProfileDialog.close()
+                
+                Layout.alignment: Qt.AlignRight
 
                 Layout.margins : {
                     right : 10
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 516bc74a5..ca21f484d 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -82,8 +82,6 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
-							deviceVerificationList.remove(flow.tranId);
 							delete flow; 
 						}
 					}
@@ -128,16 +126,26 @@ ApplicationWindow {
 
 				RowLayout {
 					RadioButton {
+						id: decimalRadio
 						Layout.alignment: Qt.AlignLeft
 						text: qsTr("Decimal")
+						contentItem: Text {
+    					    text: decimalRadio.text
+    					    color: colors.text
+    					}
 						onClicked: { flow.method = DeviceVerificationFlow.Decimal }
 					}
 					Item {
 						Layout.fillWidth: true
 					}
 					RadioButton {
+						id: emojiRadio
 						Layout.alignment: Qt.AlignRight
 						text: qsTr("Emoji")
+						contentItem: Text {
+    					    text: emojiRadio.text
+    					    color: colors.text
+    					}
 						onClicked: { flow.method = DeviceVerificationFlow.Emoji }
 					}
 				}
@@ -156,7 +164,7 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: { 
-							dialog.close(); 
+							dialog.close();
 							flow.cancelVerification();
 							deviceVerificationList.remove(flow.tranId);
 							delete flow; 
@@ -411,6 +419,7 @@ ApplicationWindow {
 									text: col.emoji.emoji
 									font.pixelSize: Qt.application.font.pixelSize * 2
 									font.family: Settings.emojiFont
+									color:colors.text
 								}
 								Label {
 									Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 9b2608922..2c6e9c1ea 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -13,7 +13,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
 {
         timeout = new QTimer(this);
         timeout->setSingleShot(true);
-        this->sas = olm::client()->sas_init();
+        this->sas           = olm::client()->sas_init();
+        this->isMacVerified = false;
         connect(timeout, &QTimer::timeout, this, [this]() {
                 emit timedout();
                 this->deleteLater();
@@ -134,45 +135,47 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                           }
                   }
           });
-        connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationMac,
-                this,
-                [this](const mtx::events::collections::DeviceEvents &message) {
-                        auto msg =
-                          std::get>(message);
-                        if (msg.content.transaction_id == this->transaction_id) {
-                                std::string info =
-                                  "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() +
-                                  this->deviceId.toStdString() +
-                                  http::client()->user_id().to_string() +
-                                  http::client()->device_id() + this->transaction_id;
-
-                                std::vector key_list;
-                                std::string key_string;
-                                for (auto mac : msg.content.mac) {
-                                        if (mac.second ==
-                                            this->sas->calculate_mac(this->device_keys[mac.first],
-                                                                     info + mac.first)) {
-                                                key_string += mac.first;
-                                        } else {
-                                                this->cancelVerification();
-                                                return;
-                                        }
-                                }
-                                if (msg.content.keys ==
-                                    this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
-                                        // uncomment this in future to be compatible with the
-                                        // MSC2366 this->sendVerificationDone(); and remoeve the
-                                        // below line
-                                        if (this->isMacVerified == true)
-                                                emit this->deviceVerified();
-                                        else
-                                                this->isMacVerified = true;
-                                } else {
-                                        this->cancelVerification();
-                                }
-                        }
-                });
+        connect(
+          ChatPage::instance(),
+          &ChatPage::recievedDeviceVerificationMac,
+          this,
+          [this](const mtx::events::collections::DeviceEvents &message) {
+                  auto msg = std::get>(message);
+                  if (msg.content.transaction_id == this->transaction_id) {
+                          std::string info =
+                            "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() +
+                            this->deviceId.toStdString() + http::client()->user_id().to_string() +
+                            http::client()->device_id() + this->transaction_id;
+
+                          std::vector key_list;
+                          std::string key_string;
+                          for (auto mac : msg.content.mac) {
+                                  key_string += mac.first + ",";
+                                  if (device_keys[mac.first] != "") {
+                                          if (mac.second ==
+                                              this->sas->calculate_mac(this->device_keys[mac.first],
+                                                                       info + mac.first)) {
+                                          } else {
+                                                  this->cancelVerification();
+                                                  return;
+                                          }
+                                  }
+                          }
+                          key_string = key_string.substr(0, key_string.length() - 1);
+                          if (msg.content.keys ==
+                              this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
+                                  // uncomment this in future to be compatible with the
+                                  // MSC2366 this->sendVerificationDone(); and remove the
+                                  // below line
+                                  if (this->isMacVerified == true)
+                                          emit this->deviceVerified();
+                                  else
+                                          this->isMacVerified = true;
+                          } else {
+                                  this->cancelVerification();
+                          }
+                  }
+          });
         connect(ChatPage::instance(),
                 &ChatPage::recievedDeviceVerificationReady,
                 this,
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 588d69692..6aa4deff4 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -1,4 +1,5 @@
 #include "UserProfile.h"
+#include "ChatPage.h"
 #include "Logging.h"
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
@@ -86,3 +87,30 @@ UserProfile::updateDeviceList()
 {
         fetchDeviceList(this->userId);
 }
+
+void
+UserProfile::banUser()
+{
+        ChatPage::instance()->banUser(this->userId, "");
+}
+
+// void ignoreUser(){
+
+// }
+
+void
+UserProfile::kickUser()
+{
+        ChatPage::instance()->kickUser(this->userId, "");
+}
+
+void
+UserProfile::startChat()
+{
+        mtx::requests::CreateRoom req;
+        req.preset     = mtx::requests::Preset::PrivateChat;
+        req.visibility = mtx::requests::Visibility::Private;
+        if (utils::localUser() != this->userId)
+                req.invite = {this->userId.toStdString()};
+        emit ChatPage::instance()->createRoom(req);
+}
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index c37e23aed..ad92d1827 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -5,7 +5,6 @@
 #include 
 
 #include "MatrixClient.h"
-
 class DeviceInfo
 {
 public:
@@ -36,6 +35,10 @@ class UserProfile : public QObject
 
         Q_INVOKABLE void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void updateDeviceList();
+        Q_INVOKABLE void banUser();
+        // Q_INVOKABLE void ignoreUser();
+        Q_INVOKABLE void kickUser();
+        Q_INVOKABLE void startChat();
 
 signals:
         void userIdChanged();

From 2f1d026f041b56e090b65ca0386c38baac666f43 Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Fri, 26 Jun 2020 15:10:37 +0530
Subject: [PATCH 22/28] Error Handling and some fixes

---
 resources/qml/UserProfile.qml                 |  16 ++-
 .../DeviceVerification.qml                    |  52 ++-------
 src/DeviceVerificationFlow.cpp                | 109 +++++++++++-------
 src/DeviceVerificationFlow.h                  |  12 +-
 src/timeline/TimelineViewManager.cpp          |  45 +++++++-
 5 files changed, 138 insertions(+), 96 deletions(-)

diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index 36c8586d0..6ef750319 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -108,30 +108,28 @@ ApplicationWindow{
                 //         userProfileList.ignoreUser()
                 //     }
                 // }
-
                 ImageButton{
-                    image:":/icons/icons/ui/round-remove-button.png"
+                    image:":/icons/icons/ui/black-bubble-speech.png"
                     Layout.margins: {
                         left: 5
                         right: 5
                     }
                     ToolTip.visible: hovered
-			        ToolTip.text: qsTr("Kick the user")
+			        ToolTip.text: qsTr("Start a private chat")
                     onClicked : {
-                        userProfileList.kickUser()
+                        userProfileList.startChat()
                     }
                 }
-
                 ImageButton{
-                    image:":/icons/icons/ui/black-bubble-speech.png"
+                    image:":/icons/icons/ui/round-remove-button.png"
                     Layout.margins: {
                         left: 5
                         right: 5
                     }
                     ToolTip.visible: hovered
-			        ToolTip.text: qsTr("Start a conversation")
+			        ToolTip.text: qsTr("Kick the user")
                     onClicked : {
-                        userProfileList.startChat()
+                        userProfileList.kickUser()
                     }
                 }
             }
@@ -205,7 +203,7 @@ ApplicationWindow{
                 text:"OK"
                 onClicked: userProfileDialog.close()
                 
-                Layout.alignment: Qt.AlignRight
+                Layout.alignment: Qt.AlignRight | Qt.AlignBottom
 
                 Layout.margins : {
                     right : 10
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index ca21f484d..15c2d7a25 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -22,12 +22,6 @@ ApplicationWindow {
 		implicitHeight: currentItem.implicitHeight
 	}
 
-	onClosing: {
-		flow.cancelVerification();
-		deviceVerificationList.remove(flow.tranId);
-		delete flow; 
-	}
-
 	property var flow
 	Connections {
 		target: flow
@@ -123,33 +117,6 @@ ApplicationWindow {
 					color:colors.text
 					verticalAlignment: Text.AlignVCenter
 				}
-
-				RowLayout {
-					RadioButton {
-						id: decimalRadio
-						Layout.alignment: Qt.AlignLeft
-						text: qsTr("Decimal")
-						contentItem: Text {
-    					    text: decimalRadio.text
-    					    color: colors.text
-    					}
-						onClicked: { flow.method = DeviceVerificationFlow.Decimal }
-					}
-					Item {
-						Layout.fillWidth: true
-					}
-					RadioButton {
-						id: emojiRadio
-						Layout.alignment: Qt.AlignRight
-						text: qsTr("Emoji")
-						contentItem: Text {
-    					    text: emojiRadio.text
-    					    color: colors.text
-    					}
-						onClicked: { flow.method = DeviceVerificationFlow.Emoji }
-					}
-				}
-
 				RowLayout {
 					Button {
 						Layout.alignment: Qt.AlignLeft
@@ -165,9 +132,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close();
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.User);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -227,9 +193,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.User);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -261,14 +226,17 @@ ApplicationWindow {
 					Label {
 						font.pixelSize: Qt.application.font.pixelSize * 2
 						text: flow.sasList[0]
+						color:colors.text
 					}
 					Label {
 						font.pixelSize: Qt.application.font.pixelSize * 2
 						text: flow.sasList[1]
+						color:colors.text
 					}
 					Label {
 						font.pixelSize: Qt.application.font.pixelSize * 2
 						text: flow.sasList[2]
+						color:colors.text
 					}
 				}
 
@@ -287,9 +255,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -411,6 +378,7 @@ ApplicationWindow {
 							implicitWidth: col.width
 							ColumnLayout {
 								id: col
+								Layout.fillWidth: true
 								anchors.bottom: parent.bottom
 								property var emoji: emojis.mapping[flow.sasList[index]]
 								Label {
@@ -424,6 +392,7 @@ ApplicationWindow {
 								Label {
 									Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
 									text: col.emoji.description
+									color:colors.text
 								}
 							}
 						}
@@ -445,9 +414,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -507,7 +475,7 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification(); 
+							flow.cancelVerification(DeviceVerificationFlow.User); 
 							deviceVerificationList.remove(flow.tranId);
 							delete flow;
 						}
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 2c6e9c1ea..b5134a3b1 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -17,42 +17,53 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
         this->isMacVerified = false;
         connect(timeout, &QTimer::timeout, this, [this]() {
                 emit timedout();
+                this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
                 this->deleteLater();
         });
 
-        connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationStart,
-                this,
-                [this](const mtx::events::collections::DeviceEvents &message) {
-                        auto msg =
-                          std::get>(message);
-                        if (msg.content.transaction_id == this->transaction_id) {
-                                if ((std::find(msg.content.key_agreement_protocols.begin(),
-                                               msg.content.key_agreement_protocols.end(),
-                                               "curve25519-hkdf-sha256") !=
-                                     msg.content.key_agreement_protocols.end()) &&
-                                    (std::find(msg.content.hashes.begin(),
-                                               msg.content.hashes.end(),
-                                               "sha256") != msg.content.hashes.end()) &&
-                                    (std::find(msg.content.message_authentication_codes.begin(),
-                                               msg.content.message_authentication_codes.end(),
-                                               "hmac-sha256") !=
-                                     msg.content.message_authentication_codes.end()) &&
-                                    ((std::find(msg.content.short_authentication_string.begin(),
+        connect(
+          ChatPage::instance(),
+          &ChatPage::recievedDeviceVerificationStart,
+          this,
+          [this](const mtx::events::collections::DeviceEvents &message) {
+                  auto msg =
+                    std::get>(message);
+                  if (msg.content.transaction_id == this->transaction_id) {
+                          if ((std::find(msg.content.key_agreement_protocols.begin(),
+                                         msg.content.key_agreement_protocols.end(),
+                                         "curve25519-hkdf-sha256") !=
+                               msg.content.key_agreement_protocols.end()) &&
+                              (std::find(msg.content.hashes.begin(),
+                                         msg.content.hashes.end(),
+                                         "sha256") != msg.content.hashes.end()) &&
+                              (std::find(msg.content.message_authentication_codes.begin(),
+                                         msg.content.message_authentication_codes.end(),
+                                         "hmac-sha256") !=
+                               msg.content.message_authentication_codes.end())) {
+                                  if (std::find(msg.content.short_authentication_string.begin(),
                                                 msg.content.short_authentication_string.end(),
                                                 mtx::events::msg::SASMethods::Decimal) !=
-                                      msg.content.short_authentication_string.end()) ||
-                                     (std::find(msg.content.short_authentication_string.begin(),
-                                                msg.content.short_authentication_string.end(),
-                                                mtx::events::msg::SASMethods::Emoji) !=
-                                      msg.content.short_authentication_string.end()))) {
-                                        this->acceptVerificationRequest();
-                                        this->canonical_json = nlohmann::json(msg.content);
-                                } else {
-                                        this->cancelVerification();
-                                }
-                        }
-                });
+                                      msg.content.short_authentication_string.end()) {
+                                          this->method = DeviceVerificationFlow::Method::Emoji;
+                                  } else if (std::find(
+                                               msg.content.short_authentication_string.begin(),
+                                               msg.content.short_authentication_string.end(),
+                                               mtx::events::msg::SASMethods::Emoji) !=
+                                             msg.content.short_authentication_string.end()) {
+                                          this->method = DeviceVerificationFlow::Method::Decimal;
+                                  } else {
+                                          this->cancelVerification(
+                                            DeviceVerificationFlow::Error::UnknownMethod);
+                                          return;
+                                  }
+                                  this->acceptVerificationRequest();
+                                  this->canonical_json = nlohmann::json(msg.content);
+                          } else {
+                                  this->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
+                          }
+                  }
+          });
         connect(
           ChatPage::instance(),
           &ChatPage::recievedDeviceVerificationAccept,
@@ -76,7 +87,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                   this->mac_method = msg.content.message_authentication_code;
                                   this->sendVerificationKey();
                           } else {
-                                  this->cancelVerification();
+                                  this->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
                           }
                   }
           });
@@ -130,7 +142,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                         msg.content.key + this->canonical_json.dump()))) {
                                           emit this->verificationRequestAccepted(this->method);
                                   } else {
-                                          this->cancelVerification();
+                                          this->cancelVerification(
+                                            DeviceVerificationFlow::Error::MismatchedCommitment);
                                   }
                           }
                   }
@@ -156,7 +169,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                               this->sas->calculate_mac(this->device_keys[mac.first],
                                                                        info + mac.first)) {
                                           } else {
-                                                  this->cancelVerification();
+                                                  this->cancelVerification(
+                                                    DeviceVerificationFlow::Error::KeyMismatch);
                                                   return;
                                           }
                                   }
@@ -172,7 +186,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                   else
                                           this->isMacVerified = true;
                           } else {
-                                  this->cancelVerification();
+                                  this->cancelVerification(
+                                    DeviceVerificationFlow::Error::KeyMismatch);
                           }
                   }
           });
@@ -429,15 +444,31 @@ DeviceVerificationFlow::sendVerificationRequest()
 }
 //! cancels a verification flow
 void
-DeviceVerificationFlow::cancelVerification()
+DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_code)
 {
         mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationCancel req;
 
         req.transaction_id = this->transaction_id;
-        // TODO: Add Proper Error Messages and Code
-        req.reason = "Device Verification Cancelled";
-        req.code   = "400";
+        if (error_code == DeviceVerificationFlow::Error::UnknownMethod) {
+                req.code   = "m.unknown_method";
+                req.reason = "unknown method recieved";
+        } else if (error_code == DeviceVerificationFlow::Error::MismatchedCommitment) {
+                req.code   = "m.mismatched_commitment";
+                req.reason = "commitment didn't match";
+        } else if (error_code == DeviceVerificationFlow::Error::MismatchedSAS) {
+                req.code   = "m.mismatched_sas";
+                req.reason = "sas didn't match";
+        } else if (error_code == DeviceVerificationFlow::Error::KeyMismatch) {
+                req.code   = "m.key_match";
+                req.reason = "keys did not match";
+        } else if (error_code == DeviceVerificationFlow::Error::Timeout) {
+                req.code   = "m.timeout";
+                req.reason = "timed out";
+        } else if (error_code == DeviceVerificationFlow::Error::User) {
+                req.code   = "m.user";
+                req.reason = "user cancelled the verification";
+        }
 
         body[this->toClient][deviceId.toStdString()] = req;
 
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index ea86a10b5..891c6aea9 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -28,6 +28,16 @@ class DeviceVerificationFlow : public QObject
                 Emoji
         };
         Q_ENUM(Method)
+        enum Error
+        {
+                UnknownMethod,
+                MismatchedCommitment,
+                MismatchedSAS,
+                KeyMismatch,
+                Timeout,
+                User
+        };
+        Q_ENUM(Error)
 
         DeviceVerificationFlow(QObject *parent = nullptr);
         QString getTransactionId();
@@ -56,7 +66,7 @@ public slots:
         //! starts the verification flow
         void startVerificationRequest();
         //! cancels a verification flow
-        void cancelVerification();
+        void cancelVerification(DeviceVerificationFlow::Error error_code);
         //! sends the verification key
         void sendVerificationKey();
         //! sends the mac of the keys
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 461de0158..17023c976 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -180,6 +180,9 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                                     QString::fromStdString(msg.content.transaction_id),
                                     QString::fromStdString(msg.sender),
                                     QString::fromStdString(msg.content.from_device));
+                          } else {
+                                  flow->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
                           }
                   }
           });
@@ -193,11 +196,43 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                   auto flow            = new DeviceVerificationFlow(this);
                   flow->canonical_json = nlohmann::json(msg.content);
                   if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) {
-                          emit newDeviceVerificationRequest(
-                            std::move(flow),
-                            QString::fromStdString(msg.content.transaction_id),
-                            QString::fromStdString(msg.sender),
-                            QString::fromStdString(msg.content.from_device));
+                          if ((std::find(msg.content.key_agreement_protocols.begin(),
+                                         msg.content.key_agreement_protocols.end(),
+                                         "curve25519-hkdf-sha256") !=
+                               msg.content.key_agreement_protocols.end()) &&
+                              (std::find(msg.content.hashes.begin(),
+                                         msg.content.hashes.end(),
+                                         "sha256") != msg.content.hashes.end()) &&
+                              (std::find(msg.content.message_authentication_codes.begin(),
+                                         msg.content.message_authentication_codes.end(),
+                                         "hmac-sha256") !=
+                               msg.content.message_authentication_codes.end())) {
+                                  if (std::find(msg.content.short_authentication_string.begin(),
+                                                msg.content.short_authentication_string.end(),
+                                                mtx::events::msg::SASMethods::Emoji) !=
+                                      msg.content.short_authentication_string.end()) {
+                                          flow->setMethod(DeviceVerificationFlow::Method::Emoji);
+                                  } else if (std::find(
+                                               msg.content.short_authentication_string.begin(),
+                                               msg.content.short_authentication_string.end(),
+                                               mtx::events::msg::SASMethods::Decimal) !=
+                                             msg.content.short_authentication_string.end()) {
+                                          flow->setMethod(DeviceVerificationFlow::Method::Decimal);
+                                  } else {
+                                          flow->cancelVerification(
+                                            DeviceVerificationFlow::Error::UnknownMethod);
+                                          return;
+                                  }
+                                  emit newDeviceVerificationRequest(
+                                    std::move(flow),
+                                    QString::fromStdString(msg.content.transaction_id),
+                                    QString::fromStdString(msg.sender),
+                                    QString::fromStdString(msg.content.from_device));
+                                  flow->canonical_json = nlohmann::json(msg.content);
+                          } else {
+                                  flow->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
+                          }
                   }
           });
 }

From 362ab16cacf870e1553365ad97a966357be5663d Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Sun, 28 Jun 2020 21:01:34 +0530
Subject: [PATCH 23/28] [WIP] Add Caching for users

---
 resources/qml/TimelineView.qml |  18 ++---
 resources/qml/UserProfile.qml  |  11 +--
 src/Cache.cpp                  | 138 +++++++++++++++++++++++++++++++++
 src/Cache.h                    |  17 ++++
 src/CacheCryptoStructs.h       |  30 +++++++
 src/Cache_p.h                  |  19 +++++
 src/ui/UserProfile.cpp         | 106 ++++++++++++++-----------
 src/ui/UserProfile.h           |   4 +
 8 files changed, 283 insertions(+), 60 deletions(-)

diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index e782e2084..b86ff211a 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -19,6 +19,7 @@ Page {
 	property real highlightHue: colors.highlight.hslHue
 	property real highlightSat: colors.highlight.hslSaturation
 	property real highlightLight: colors.highlight.hslLightness
+	property variant userProfile
 
 	palette: colors
 
@@ -232,6 +233,11 @@ Page {
 
 			}
 
+			Component{
+				id: userProfileComponent
+				UserProfile{}
+			}
+
 			section {
 				property: "section"
 			}
@@ -268,8 +274,6 @@ Page {
 						}
 					}
 
-					property variant userProfile
-
 					Row {
 						height: userName.height
 						spacing: 8
@@ -284,9 +288,7 @@ Page {
 							MouseArea {
 								anchors.fill: parent
                                 onClicked: {
-									if(userProfile) userProfile.destroy()
-									var component = Qt.createComponent("UserProfile.qml");
-									userProfile = component.createObject(timelineRoot,{user_data : modelData});
+									userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)});
 									userProfile.show();
                                 }
 								cursorShape: Qt.PointingHandCursor
@@ -304,10 +306,8 @@ Page {
 								anchors.fill: parent
 								Layout.alignment: Qt.AlignHCenter
                                 onClicked: {
-									if(userProfile) userProfile.destroy()
-									var component = Qt.createComponent("UserProfile.qml")
-									userProfile = component.createObject(timelineRoot,{user_data : modelData})
-									userProfile.show()
+									userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)});
+									userProfile.show();
                                 }
 								cursorShape: Qt.PointingHandCursor
 								propagateComposedEvents: true
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index 6ef750319..a0b0f9936 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -9,6 +9,7 @@ import "./device-verification"
 
 ApplicationWindow{
     property var user_data
+    property var avatarUrl
     property var colors: currentActivePalette
 
     id:userProfileDialog
@@ -52,11 +53,11 @@ ApplicationWindow{
 
             Avatar{
                 id: userProfileAvatar
-                url:chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/")
+                url: avatarUrl.replace("mxc://", "image://MxcImage/")
                 height: 130
                 width: 130
-                displayName: modelData.userName
-		        userid: modelData.userId
+                displayName: user_data.userName
+		        userid: user_data.userId
                 Layout.alignment: Qt.AlignHCenter
                 Layout.margins : {
                     top: 10
@@ -68,7 +69,7 @@ ApplicationWindow{
                 text: user_data.userName
                 fontSizeMode: Text.HorizontalFit
                 font.pixelSize: 20
-                color:TimelineManager.userColor(modelData.userId, colors.window)
+                color:TimelineManager.userColor(user_data.userId, colors.window)
                 font.bold: true
                 Layout.alignment: Qt.AlignHCenter
             }
@@ -207,7 +208,7 @@ ApplicationWindow{
 
                 Layout.margins : {
                     right : 10
-                    bottom : 10
+                    bottom: 5
                 }
 
                 palette {
diff --git a/src/Cache.cpp b/src/Cache.cpp
index d9d1134e3..d89973769 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -2315,6 +2315,115 @@ Cache::statusMessage(const std::string &user_id)
         return status_msg;
 }
 
+void
+to_json(json &j, const UserCache &info)
+{
+        j["user_id"]          = info.user_id;
+        j["is_user_verified"] = info.is_user_verified;
+        j["cross_verified"]   = info.cross_verified;
+        j["keys"]             = info.keys;
+}
+
+void
+from_json(const json &j, UserCache &info)
+{
+        info.user_id          = j.at("user_id");
+        info.is_user_verified = j.at("is_user_verified");
+        info.cross_verified   = j.at("cross_verified").get>();
+        info.keys             = j.at("keys").get();
+}
+
+UserCache
+Cache::getUserCache(const std::string &user_id)
+{
+        lmdb::val verifiedVal;
+
+        auto txn = lmdb::txn::begin(env_);
+        auto db  = getUserCacheDb(txn);
+        auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal);
+
+        UserCache verified_state;
+        if (res) {
+                verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size()));
+        }
+
+        txn.commit();
+
+        return verified_state;
+}
+
+//! be careful when using make sure is_user_verified is not changed
+int
+Cache::setUserCache(const std::string &user_id, const UserCache &body)
+{
+        auto txn = lmdb::txn::begin(env_);
+        auto db  = getUserCacheDb(txn);
+
+        auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump()));
+
+        txn.commit();
+
+        return res;
+}
+
+int
+Cache::deleteUserCache(const std::string &user_id)
+{
+        auto txn = lmdb::txn::begin(env_);
+        auto db  = getUserCacheDb(txn);
+        auto res = lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr);
+
+        txn.commit();
+
+        return res;
+}
+
+void
+to_json(json &j, const DeviceVerifiedCache &info)
+{
+        j["user_id"]         = info.user_id;
+        j["device_verified"] = info.device_verified;
+}
+
+void
+from_json(const json &j, DeviceVerifiedCache &info)
+{
+        info.user_id         = j.at("user_id");
+        info.device_verified = j.at("device_verified").get>();
+}
+
+DeviceVerifiedCache
+Cache::getVerifiedCache(const std::string &user_id)
+{
+        lmdb::val verifiedVal;
+
+        auto txn = lmdb::txn::begin(env_);
+        auto db  = getDeviceVerifiedDb(txn);
+        auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal);
+
+        DeviceVerifiedCache verified_state;
+        if (res) {
+                verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size()));
+        }
+
+        txn.commit();
+
+        return verified_state;
+}
+
+int
+Cache::setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body)
+{
+        auto txn = lmdb::txn::begin(env_);
+        auto db  = getDeviceVerifiedDb(txn);
+
+        auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump()));
+
+        txn.commit();
+
+        return res;
+}
+
 void
 to_json(json &j, const RoomInfo &info)
 {
@@ -2496,6 +2605,35 @@ statusMessage(const std::string &user_id)
 {
         return instance_->statusMessage(user_id);
 }
+UserCache
+getUserCache(const std::string &user_id)
+{
+        return instance_->getUserCache(user_id);
+}
+
+int
+setUserCache(const std::string &user_id, const UserCache &body)
+{
+        return instance_->setUserCache(user_id, body);
+}
+
+int
+deleteUserCache(const std::string &user_id)
+{
+        return instance_->deleteUserCache(user_id);
+}
+
+DeviceVerifiedCache
+getVerifiedCache(const std::string &user_id)
+{
+        return instance_->getVerifiedCache(user_id);
+}
+
+int
+setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body)
+{
+        return instance_->setVerifiedCache(user_id, body);
+}
 
 //! Load saved data for the display names & avatars.
 void
diff --git a/src/Cache.h b/src/Cache.h
index b5275623f..34e79ab57 100644
--- a/src/Cache.h
+++ b/src/Cache.h
@@ -60,6 +60,23 @@ presenceState(const std::string &user_id);
 std::string
 statusMessage(const std::string &user_id);
 
+//! user Cache
+UserCache
+getUserCache(const std::string &user_id);
+
+int
+setUserCache(const std::string &user_id, const UserCache &body);
+
+int
+deleteUserCache(const std::string &user_id);
+
+//! verified Cache
+DeviceVerifiedCache
+getVerifiedCache(const std::string &user_id);
+
+int
+setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body);
+
 //! Load saved data for the display names & avatars.
 void
 populateMembers();
diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index 14c9c86b2..7344aef97 100644
--- a/src/CacheCryptoStructs.h
+++ b/src/CacheCryptoStructs.h
@@ -65,3 +65,33 @@ struct OlmSessionStorage
         std::mutex group_outbound_mtx;
         std::mutex group_inbound_mtx;
 };
+
+struct UserCache
+{
+        //! user_id of the user
+        std::string user_id;
+        //! this stores if the user is verified (with cross-signing)
+        bool is_user_verified = false;
+        //! list of verified device_ids with cross-signing
+        std::vector cross_verified;
+        //! map of public key key_ids and their public_key
+        mtx::responses::QueryKeys keys;
+};
+
+void
+to_json(nlohmann::json &j, const UserCache &info);
+void
+from_json(const nlohmann::json &j, UserCache &info);
+
+struct DeviceVerifiedCache
+{
+        //! user_id of the user
+        std::string user_id;
+        //! list of verified device_ids with device-verification
+        std::vector device_verified;
+};
+
+void
+to_json(nlohmann::json &j, const DeviceVerifiedCache &info);
+void
+from_json(const nlohmann::json &j, DeviceVerifiedCache &info);
diff --git a/src/Cache_p.h b/src/Cache_p.h
index 892b66a5d..fdc9ff860 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -56,6 +56,15 @@ class Cache : public QObject
         mtx::presence::PresenceState presenceState(const std::string &user_id);
         std::string statusMessage(const std::string &user_id);
 
+        // user cache stores user keys
+        UserCache getUserCache(const std::string &user_id);
+        int setUserCache(const std::string &user_id, const UserCache &body);
+        int deleteUserCache(const std::string &user_id);
+
+        // device verified cache
+        DeviceVerifiedCache getVerifiedCache(const std::string &user_id);
+        int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body);
+
         static void removeDisplayName(const QString &room_id, const QString &user_id);
         static void removeAvatarUrl(const QString &room_id, const QString &user_id);
 
@@ -443,6 +452,16 @@ class Cache : public QObject
                 return lmdb::dbi::open(txn, "presence", MDB_CREATE);
         }
 
+        lmdb::dbi getUserCacheDb(lmdb::txn &txn)
+        {
+                return lmdb::dbi::open(txn, std::string("user_cache").c_str(), MDB_CREATE);
+        }
+
+        lmdb::dbi getDeviceVerifiedDb(lmdb::txn &txn)
+        {
+                return lmdb::dbi::open(txn, std::string("verified").c_str(), MDB_CREATE);
+        }
+
         //! Retrieves or creates the database that stores the open OLM sessions between our device
         //! and the given curve25519 key which represents another device.
         //!
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 6aa4deff4..c637280b8 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -1,9 +1,12 @@
 #include "UserProfile.h"
+#include "Cache.h"
 #include "ChatPage.h"
 #include "Logging.h"
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
 
+#include  // only for debugging
+
 UserProfile::UserProfile(QObject *parent)
   : QObject(parent)
 {}
@@ -32,54 +35,65 @@ UserProfile::setUserId(const QString &user_id)
 }
 
 void
-UserProfile::fetchDeviceList(const QString &userID)
+UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
+                         mtx::http::RequestErr err,
+                         std::string user_id)
 {
-        auto localUser = utils::localUser();
-        mtx::requests::QueryKeys req;
-        mtx::responses::QueryKeys res;
-        req.device_keys[userID.toStdString()] = {};
-
-        http::client()->query_keys(
-          req,
-          [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
-                                                 mtx::http::RequestErr err) {
-                  if (err) {
-                          nhlog::net()->warn("failed to query device keys: {},{}",
-                                             err->matrix_error.errcode,
-                                             static_cast(err->status_code));
-                          return;
-                  }
-
-                  if (res.device_keys.empty() ||
-                      (res.device_keys.find(user_id) == res.device_keys.end())) {
-                          nhlog::net()->warn("no devices retrieved {}", user_id);
-                          return;
-                  }
-
-                  auto devices = res.device_keys.at(user_id);
-                  QVector deviceInfo;
-
-                  for (const auto &d : devices) {
-                          auto device = d.second;
-
-                          // TODO: Verify signatures and ignore those that don't pass.
-                          DeviceInfo newdevice(
-                            QString::fromStdString(d.first),
-                            QString::fromStdString(device.unsigned_info.device_display_name));
-                          QString::fromStdString(device.unsigned_info.device_display_name);
-
-                          deviceInfo.append(std::move(newdevice));
-                  }
-
-                  std::sort(deviceInfo.begin(),
-                            deviceInfo.end(),
-                            [](const DeviceInfo &a, const DeviceInfo &b) {
-                                    return a.device_id > b.device_id;
-                            });
-
-                  this->deviceList = std::move(deviceInfo);
-                  emit UserProfile::deviceListUpdated();
+        if (err) {
+                nhlog::net()->warn("failed to query device keys: {},{}",
+                                   err->matrix_error.errcode,
+                                   static_cast(err->status_code));
+                return;
+        }
+
+        if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) {
+                nhlog::net()->warn("no devices retrieved {}", user_id);
+                return;
+        }
+
+        auto devices = res.device_keys.at(user_id);
+        QVector deviceInfo;
+
+        for (const auto &d : devices) {
+                auto device = d.second;
+
+                // TODO: Verify signatures and ignore those that don't pass.
+                DeviceInfo newdevice(
+                  QString::fromStdString(d.first),
+                  QString::fromStdString(device.unsigned_info.device_display_name));
+                QString::fromStdString(device.unsigned_info.device_display_name);
+
+                deviceInfo.append(std::move(newdevice));
+        }
+
+        std::sort(
+          deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) {
+                  return a.device_id > b.device_id;
           });
+
+        this->deviceList = std::move(deviceInfo);
+        emit UserProfile::deviceListUpdated();
+}
+
+void
+UserProfile::fetchDeviceList(const QString &userID)
+{
+        auto localUser  = utils::localUser();
+        auto user_cache = cache::getUserCache(userID.toStdString());
+
+        if (user_cache.user_id == userID.toStdString()) {
+                mtx::http::ClientError error;
+                this->callback_fn(user_cache.keys, std::move(error), userID.toStdString());
+        } else {
+                mtx::requests::QueryKeys req;
+                req.device_keys[userID.toStdString()] = {};
+                http::client()->query_keys(
+                  req,
+                  [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
+                                                         mtx::http::RequestErr err) {
+                          this->callback_fn(res, err, user_id);
+                  });
+        }
 }
 
 void
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index ad92d1827..befd82ec7 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -47,4 +47,8 @@ class UserProfile : public QObject
 private:
         QVector deviceList;
         QString userId;
+
+        void callback_fn(const mtx::responses::QueryKeys &res,
+                         mtx::http::RequestErr err,
+                         std::string user_id);
 };
\ No newline at end of file

From 9e3f900c1440381d779b91f2baa632c0379ebb1f Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Wed, 1 Jul 2020 17:47:10 +0530
Subject: [PATCH 24/28] Some issue with UserProfile

---
 CMakeLists.txt                |  3 ++
 resources/qml/UserProfile.qml | 44 +++++++++++-----------
 src/Cache.cpp                 | 32 ++++++++--------
 src/Cache.h                   |  4 +-
 src/CacheCryptoStructs.h      |  5 +--
 src/Cache_p.h                 |  4 +-
 src/ui/UserProfile.cpp        | 71 +++++++++++++++++++++++------------
 src/ui/UserProfile.h          | 54 +++++++++++++++++---------
 src/ui/UserProfileModel.cpp   | 45 +++++++++++-----------
 src/ui/UserProfileModel.h     |  7 ++--
 10 files changed, 154 insertions(+), 115 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d71ba001c..f69b98e93 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,9 @@ set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard")
 set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require C++ standard to be supported")
 set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "compile as PIC by default")
 
+# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
+# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
+
 option(HUNTER_ENABLED "Enable Hunter package manager" OFF)
 include("cmake/HunterGate.cmake")
 HunterGate(
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index a0b0f9936..db44ec159 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -19,17 +19,6 @@ ApplicationWindow{
     Layout.alignment: Qt.AlignHCenter
     palette: colors
 
-    UserProfileList{
-        id: userProfileList
-        userId: user_data.userId
-        onUserIdChanged : {
-            userProfileList.updateDeviceList()
-        }
-        onDeviceListUpdated : {
-            modelDeviceList.deviceList = userProfileList
-        }
-    }
-
     Component {
 		id: deviceVerificationDialog
 		DeviceVerification {}
@@ -94,7 +83,7 @@ ApplicationWindow{
                     ToolTip.visible: hovered
 			        ToolTip.text: qsTr("Ban the user")
                     onClicked : {
-                        userProfileList.banUser()
+                        modelDeviceList.deviceList.banUser()
                     }
                 }
                 // ImageButton{
@@ -106,7 +95,7 @@ ApplicationWindow{
                 //     ToolTip.visible: hovered
 			    //     ToolTip.text: qsTr("Ignore messages from this user")
                 //     onClicked : {
-                //         userProfileList.ignoreUser()
+                //         modelDeviceList.deviceList.ignoreUser()
                 //     }
                 // }
                 ImageButton{
@@ -118,7 +107,7 @@ ApplicationWindow{
                     ToolTip.visible: hovered
 			        ToolTip.text: qsTr("Start a private chat")
                     onClicked : {
-                        userProfileList.startChat()
+                        modelDeviceList.deviceList.startChat()
                     }
                 }
                 ImageButton{
@@ -130,7 +119,7 @@ ApplicationWindow{
                     ToolTip.visible: hovered
 			        ToolTip.text: qsTr("Kick the user")
                     onClicked : {
-                        userProfileList.kickUser()
+                        modelDeviceList.deviceList.kickUser()
                     }
                 }
             }
@@ -142,14 +131,15 @@ ApplicationWindow{
                 Layout.alignment: Qt.AlignHCenter
 
                 ListView{
-                    id: deviceList
+                    id: devicelist
                     anchors.fill: parent
                     clip: true
                     spacing: 4
 
                     model: UserProfileModel{
                         id: modelDeviceList
-                    } 
+                        deviceList.userId : user_data.userId
+                    }
 
                     delegate: RowLayout{
                         width: parent.width
@@ -157,12 +147,20 @@ ApplicationWindow{
                             top : 50
                         }
                         ColumnLayout{
-                            Text{
-                                Layout.fillWidth: true
-                                color: colors.text
-                                font.bold: true
-                                Layout.alignment: Qt.AlignRight
-                                text: deviceID
+                            RowLayout{
+                                Text{
+                                    Layout.fillWidth: true
+                                    color: colors.text
+                                    font.bold: true
+                                    Layout.alignment: Qt.AlignLeft
+                                    text: deviceID
+                                }
+                                Text{
+                                    Layout.fillWidth: true
+                                    color:colors.text
+                                    Layout.alignment: Qt.AlignLeft
+                                    text: (verified_status ==  UserProfileList.VERIFIED?"V":(verified_status ==  UserProfileList.UNVERIFIED?"NV":"B"))
+                                }
                             }
                             Text{
                                 Layout.fillWidth: true
diff --git a/src/Cache.cpp b/src/Cache.cpp
index d89973769..5761cfe12 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -2318,7 +2318,6 @@ Cache::statusMessage(const std::string &user_id)
 void
 to_json(json &j, const UserCache &info)
 {
-        j["user_id"]          = info.user_id;
         j["is_user_verified"] = info.is_user_verified;
         j["cross_verified"]   = info.cross_verified;
         j["keys"]             = info.keys;
@@ -2327,13 +2326,12 @@ to_json(json &j, const UserCache &info)
 void
 from_json(const json &j, UserCache &info)
 {
-        info.user_id          = j.at("user_id");
         info.is_user_verified = j.at("is_user_verified");
         info.cross_verified   = j.at("cross_verified").get>();
         info.keys             = j.at("keys").get();
 }
 
-UserCache
+std::optional
 Cache::getUserCache(const std::string &user_id)
 {
         lmdb::val verifiedVal;
@@ -2342,14 +2340,15 @@ Cache::getUserCache(const std::string &user_id)
         auto db  = getUserCacheDb(txn);
         auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal);
 
+        txn.commit();
+
         UserCache verified_state;
         if (res) {
                 verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size()));
+                return verified_state;
+        } else {
+                return {};
         }
-
-        txn.commit();
-
-        return verified_state;
 }
 
 //! be careful when using make sure is_user_verified is not changed
@@ -2381,18 +2380,18 @@ Cache::deleteUserCache(const std::string &user_id)
 void
 to_json(json &j, const DeviceVerifiedCache &info)
 {
-        j["user_id"]         = info.user_id;
         j["device_verified"] = info.device_verified;
+        j["device_blocked"]  = info.device_blocked;
 }
 
 void
 from_json(const json &j, DeviceVerifiedCache &info)
 {
-        info.user_id         = j.at("user_id");
         info.device_verified = j.at("device_verified").get>();
+        info.device_blocked  = j.at("device_blocked").get>();
 }
 
-DeviceVerifiedCache
+std::optional
 Cache::getVerifiedCache(const std::string &user_id)
 {
         lmdb::val verifiedVal;
@@ -2401,14 +2400,15 @@ Cache::getVerifiedCache(const std::string &user_id)
         auto db  = getDeviceVerifiedDb(txn);
         auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal);
 
+        txn.commit();
+
         DeviceVerifiedCache verified_state;
         if (res) {
                 verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size()));
+                return verified_state;
+        } else {
+                return {};
         }
-
-        txn.commit();
-
-        return verified_state;
 }
 
 int
@@ -2605,7 +2605,7 @@ statusMessage(const std::string &user_id)
 {
         return instance_->statusMessage(user_id);
 }
-UserCache
+std::optional
 getUserCache(const std::string &user_id)
 {
         return instance_->getUserCache(user_id);
@@ -2623,7 +2623,7 @@ deleteUserCache(const std::string &user_id)
         return instance_->deleteUserCache(user_id);
 }
 
-DeviceVerifiedCache
+std::optional
 getVerifiedCache(const std::string &user_id)
 {
         return instance_->getVerifiedCache(user_id);
diff --git a/src/Cache.h b/src/Cache.h
index 34e79ab57..0c955c926 100644
--- a/src/Cache.h
+++ b/src/Cache.h
@@ -61,7 +61,7 @@ std::string
 statusMessage(const std::string &user_id);
 
 //! user Cache
-UserCache
+std::optional
 getUserCache(const std::string &user_id);
 
 int
@@ -71,7 +71,7 @@ int
 deleteUserCache(const std::string &user_id);
 
 //! verified Cache
-DeviceVerifiedCache
+std::optional
 getVerifiedCache(const std::string &user_id);
 
 int
diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index 7344aef97..241cac764 100644
--- a/src/CacheCryptoStructs.h
+++ b/src/CacheCryptoStructs.h
@@ -68,8 +68,6 @@ struct OlmSessionStorage
 
 struct UserCache
 {
-        //! user_id of the user
-        std::string user_id;
         //! this stores if the user is verified (with cross-signing)
         bool is_user_verified = false;
         //! list of verified device_ids with cross-signing
@@ -85,10 +83,9 @@ from_json(const nlohmann::json &j, UserCache &info);
 
 struct DeviceVerifiedCache
 {
-        //! user_id of the user
-        std::string user_id;
         //! list of verified device_ids with device-verification
         std::vector device_verified;
+        std::vector device_blocked;
 };
 
 void
diff --git a/src/Cache_p.h b/src/Cache_p.h
index fdc9ff860..4af332200 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -57,12 +57,12 @@ class Cache : public QObject
         std::string statusMessage(const std::string &user_id);
 
         // user cache stores user keys
-        UserCache getUserCache(const std::string &user_id);
+        std::optional getUserCache(const std::string &user_id);
         int setUserCache(const std::string &user_id, const UserCache &body);
         int deleteUserCache(const std::string &user_id);
 
         // device verified cache
-        DeviceVerifiedCache getVerifiedCache(const std::string &user_id);
+        std::optional getVerifiedCache(const std::string &user_id);
         int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body);
 
         static void removeDisplayName(const QString &room_id, const QString &user_id);
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index c637280b8..8c6fb8e40 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -7,11 +7,25 @@
 
 #include  // only for debugging
 
+Q_DECLARE_METATYPE(UserProfile::Status)
+
 UserProfile::UserProfile(QObject *parent)
   : QObject(parent)
-{}
+{
+        qRegisterMetaType();
+        connect(
+          this, &UserProfile::updateDeviceList, this, [this]() { fetchDeviceList(this->userId); });
+        connect(
+          this,
+          &UserProfile::appendDeviceList,
+          this,
+          [this](QString device_id, QString device_name, UserProfile::Status verification_status) {
+                  this->deviceList.push_back(
+                    DeviceInfo{device_id, device_name, verification_status});
+          });
+}
 
-QVector
+std::vector
 UserProfile::getDeviceList()
 {
         return this->deviceList;
@@ -37,7 +51,8 @@ UserProfile::setUserId(const QString &user_id)
 void
 UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,
-                         std::string user_id)
+                         std::string user_id,
+                         std::optional> cross_verified)
 {
         if (err) {
                 nhlog::net()->warn("failed to query device keys: {},{}",
@@ -52,24 +67,40 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
         }
 
         auto devices = res.device_keys.at(user_id);
-        QVector deviceInfo;
+        std::vector deviceInfo;
+        auto device_verified = cache::getVerifiedCache(user_id);
 
         for (const auto &d : devices) {
                 auto device = d.second;
 
                 // TODO: Verify signatures and ignore those that don't pass.
-                DeviceInfo newdevice(
+                UserProfile::Status verified = UserProfile::Status::UNVERIFIED;
+                if (cross_verified.has_value()) {
+                        if (std::find(cross_verified->begin(), cross_verified->end(), d.first) !=
+                            cross_verified->end())
+                                verified = UserProfile::Status::VERIFIED;
+                } else if (device_verified.has_value()) {
+                        if (std::find(device_verified->device_verified.begin(),
+                                      device_verified->device_verified.end(),
+                                      d.first) != device_verified->device_verified.end())
+                                verified = UserProfile::Status::VERIFIED;
+                } else if (device_verified.has_value()) {
+                        if (std::find(device_verified->device_blocked.begin(),
+                                      device_verified->device_blocked.end(),
+                                      d.first) != device_verified->device_blocked.end())
+                                verified = UserProfile::Status::BLOCKED;
+                }
+
+                emit UserProfile::appendDeviceList(
                   QString::fromStdString(d.first),
-                  QString::fromStdString(device.unsigned_info.device_display_name));
-                QString::fromStdString(device.unsigned_info.device_display_name);
-
-                deviceInfo.append(std::move(newdevice));
+                  QString::fromStdString(device.unsigned_info.device_display_name),
+                  verified);
         }
 
-        std::sort(
-          deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) {
-                  return a.device_id > b.device_id;
-          });
+        // std::sort(
+        //   deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) {
+        //           return a.device_id > b.device_id;
+        //   });
 
         this->deviceList = std::move(deviceInfo);
         emit UserProfile::deviceListUpdated();
@@ -81,9 +112,9 @@ UserProfile::fetchDeviceList(const QString &userID)
         auto localUser  = utils::localUser();
         auto user_cache = cache::getUserCache(userID.toStdString());
 
-        if (user_cache.user_id == userID.toStdString()) {
-                mtx::http::ClientError error;
-                this->callback_fn(user_cache.keys, std::move(error), userID.toStdString());
+        if (user_cache.has_value()) {
+                this->callback_fn(
+                  user_cache->keys, {}, userID.toStdString(), user_cache->cross_verified);
         } else {
                 mtx::requests::QueryKeys req;
                 req.device_keys[userID.toStdString()] = {};
@@ -91,17 +122,11 @@ UserProfile::fetchDeviceList(const QString &userID)
                   req,
                   [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
                                                          mtx::http::RequestErr err) {
-                          this->callback_fn(res, err, user_id);
+                          this->callback_fn(res, err, user_id, {});
                   });
         }
 }
 
-void
-UserProfile::updateDeviceList()
-{
-        fetchDeviceList(this->userId);
-}
-
 void
 UserProfile::banUser()
 {
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index befd82ec7..1725b961b 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -5,36 +5,32 @@
 #include 
 
 #include "MatrixClient.h"
-class DeviceInfo
-{
-public:
-        DeviceInfo(const QString deviceID, const QString displayName)
-          : device_id(deviceID)
-          , display_name(displayName)
-        {}
 
-        DeviceInfo() {}
-
-        QString device_id;
-        QString display_name;
-};
+class DeviceInfo;
 
 class UserProfile : public QObject
 {
         Q_OBJECT
         Q_PROPERTY(QString userId READ getUserId WRITE setUserId NOTIFY userIdChanged)
-        Q_PROPERTY(QVector deviceList READ getDeviceList NOTIFY deviceListUpdated)
+        Q_PROPERTY(std::vector deviceList READ getDeviceList NOTIFY deviceListUpdated)
 public:
         // constructor
         explicit UserProfile(QObject *parent = 0);
         // getters
-        QVector getDeviceList();
+        std::vector getDeviceList();
         QString getUserId();
         // setters
         void setUserId(const QString &userId);
 
-        Q_INVOKABLE void fetchDeviceList(const QString &userID);
-        Q_INVOKABLE void updateDeviceList();
+        enum Status
+        {
+                VERIFIED,
+                UNVERIFIED,
+                BLOCKED
+        };
+        Q_ENUM(Status)
+
+        void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void banUser();
         // Q_INVOKABLE void ignoreUser();
         Q_INVOKABLE void kickUser();
@@ -43,12 +39,34 @@ class UserProfile : public QObject
 signals:
         void userIdChanged();
         void deviceListUpdated();
+        void updateDeviceList();
+        void appendDeviceList(const QString device_id,
+                              const QString device_naem,
+                              const UserProfile::Status verification_status);
 
 private:
-        QVector deviceList;
+        std::vector deviceList;
         QString userId;
+        std::optional cross_verified;
 
         void callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,
-                         std::string user_id);
+                         std::string user_id,
+                         std::optional> cross_verified);
+};
+
+class DeviceInfo
+{
+public:
+        DeviceInfo(const QString deviceID,
+                   const QString displayName,
+                   UserProfile::Status verification_status_)
+          : device_id(deviceID)
+          , display_name(displayName)
+          , verification_status(verification_status_)
+        {}
+
+        QString device_id;
+        QString display_name;
+        UserProfile::Status verification_status;
 };
\ No newline at end of file
diff --git a/src/ui/UserProfileModel.cpp b/src/ui/UserProfileModel.cpp
index ec0456cd9..3fa8fe2d8 100644
--- a/src/ui/UserProfileModel.cpp
+++ b/src/ui/UserProfileModel.cpp
@@ -1,11 +1,23 @@
 #include "UserProfileModel.h"
-#include "UserProfile.h"
 #include 
 
 UserProfileModel::UserProfileModel(QObject *parent)
   : QAbstractListModel(parent)
   , deviceList(nullptr)
-{}
+{
+        this->deviceList = new UserProfile(this);
+
+        connect(this->deviceList, &UserProfile::userIdChanged, this, [this]() {
+                emit this->deviceList->updateDeviceList();
+        });
+        connect(this->deviceList, &UserProfile::deviceListUpdated, this, [this]() {
+                beginResetModel();
+                this->beginInsertRows(
+                  QModelIndex(), 0, this->deviceList->getDeviceList().size() - 1);
+                this->endInsertRows();
+                endResetModel();
+        });
+}
 
 int
 UserProfileModel::rowCount(const QModelIndex &parent) const
@@ -18,7 +30,8 @@ UserProfileModel::rowCount(const QModelIndex &parent) const
 QVariant
 UserProfileModel::data(const QModelIndex &index, int role) const
 {
-        if (!index.isValid() || !this->deviceList)
+        if (!index.isValid() &&
+            static_cast(this->deviceList->getDeviceList().size()) <= index.row())
                 return QVariant();
 
         const DeviceInfo device = this->deviceList->getDeviceList().at(index.row());
@@ -27,6 +40,8 @@ UserProfileModel::data(const QModelIndex &index, int role) const
                 return QVariant(device.device_id);
         case DISPLAYNAME:
                 return QVariant(device.display_name);
+        case VERIFIED_STATUS:
+                return device.verification_status;
         }
         return QVariant();
 }
@@ -35,8 +50,9 @@ QHash
 UserProfileModel::roleNames() const
 {
         QHash names;
-        names[DEVICEID]    = "deviceID";
-        names[DISPLAYNAME] = "displayName";
+        names[DEVICEID]        = "deviceID";
+        names[DISPLAYNAME]     = "displayName";
+        names[VERIFIED_STATUS] = "verified_status";
         return names;
 }
 
@@ -45,22 +61,3 @@ UserProfileModel::getList() const
 {
         return (this->deviceList);
 }
-
-void
-UserProfileModel::setList(UserProfile *devices)
-{
-        beginResetModel();
-
-        if (devices)
-                devices->disconnect(this);
-
-        if (this->deviceList) {
-                const int index = this->deviceList->getDeviceList().size();
-                beginInsertRows(QModelIndex(), index, index);
-                endInsertRows();
-        }
-
-        this->deviceList = devices;
-
-        endResetModel();
-}
\ No newline at end of file
diff --git a/src/ui/UserProfileModel.h b/src/ui/UserProfileModel.h
index c21a806dd..ba7a25255 100644
--- a/src/ui/UserProfileModel.h
+++ b/src/ui/UserProfileModel.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "UserProfile.h"
 #include 
 
 class UserProfile; // forward declaration of the class UserProfile
@@ -7,7 +8,7 @@ class UserProfile; // forward declaration of the class UserProfile
 class UserProfileModel : public QAbstractListModel
 {
         Q_OBJECT
-        Q_PROPERTY(UserProfile *deviceList READ getList WRITE setList)
+        Q_PROPERTY(UserProfile *deviceList READ getList)
 
 public:
         explicit UserProfileModel(QObject *parent = nullptr);
@@ -15,10 +16,10 @@ class UserProfileModel : public QAbstractListModel
         enum
         {
                 DEVICEID,
-                DISPLAYNAME
+                DISPLAYNAME,
+                VERIFIED_STATUS
         };
         UserProfile *getList() const;
-        void setList(UserProfile *devices);
 
         int rowCount(const QModelIndex &parent = QModelIndex()) const override;
         QVariant data(const QModelIndex &index, int role) const override;

From c7e16002ca522c93ac43376abdc4a8761b8d398a Mon Sep 17 00:00:00 2001
From: Nicolas Werner 
Date: Sat, 4 Jul 2020 04:24:28 +0200
Subject: [PATCH 25/28] Refactor UserProfile

---
 CMakeLists.txt                       |   4 -
 resources/qml/TimelineView.qml       |  22 +-
 resources/qml/UserProfile.qml        | 417 +++++++++++++--------------
 src/MainWindow.cpp                   |   9 -
 src/MainWindow.h                     |   2 -
 src/dialogs/UserProfile.cpp          | 305 --------------------
 src/dialogs/UserProfile.h            |  69 -----
 src/timeline/TimelineModel.cpp       |   4 +-
 src/timeline/TimelineModel.h         |   5 +-
 src/timeline/TimelineViewManager.cpp |  20 +-
 src/ui/UserProfile.cpp               | 108 ++++---
 src/ui/UserProfile.h                 | 120 +++++---
 src/ui/UserProfileModel.cpp          |  63 ----
 src/ui/UserProfileModel.h            |  30 --
 14 files changed, 380 insertions(+), 798 deletions(-)
 delete mode 100644 src/dialogs/UserProfile.cpp
 delete mode 100644 src/dialogs/UserProfile.h
 delete mode 100644 src/ui/UserProfileModel.cpp
 delete mode 100644 src/ui/UserProfileModel.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f69b98e93..7e7ee96f5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -242,7 +242,6 @@ set(SRC_FILES
 	src/dialogs/ReCaptcha.cpp
 	src/dialogs/ReadReceipts.cpp
 	src/dialogs/RoomSettings.cpp
-	src/dialogs/UserProfile.cpp
 
 	# Emoji
 	src/emoji/Category.cpp
@@ -281,7 +280,6 @@ set(SRC_FILES
 	src/ui/Theme.cpp
 	src/ui/ThemeManager.cpp
 	src/ui/UserProfile.cpp
-	src/ui/UserProfileModel.cpp
 
 	src/AvatarProvider.cpp
 	src/BlurhashProvider.cpp
@@ -450,7 +448,6 @@ qt5_wrap_cpp(MOC_HEADERS
 	src/dialogs/ReCaptcha.h
 	src/dialogs/ReadReceipts.h
 	src/dialogs/RoomSettings.h
-	src/dialogs/UserProfile.h
 
 	# Emoji
 	src/emoji/Category.h
@@ -486,7 +483,6 @@ qt5_wrap_cpp(MOC_HEADERS
 	src/ui/Theme.h
 	src/ui/ThemeManager.h
 	src/ui/UserProfile.h
-	src/ui/UserProfileModel.h
 
 	src/notifications/Manager.h
 
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index b86ff211a..046312247 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -101,17 +101,23 @@ Page {
 		}
 		Connections {
 			target: TimelineManager
-			onNewDeviceVerificationRequest: {
+			function onNewDeviceVerificationRequest(flow) {
 				flow.userId = userId;
 				flow.sender = false;
 				flow.deviceId = deviceId;
 				flow.tranId = transactionId;
 				deviceVerificationList.add(flow.tranId);
-				var dialog = deviceVerificationDialog.createObject(timelineRoot, 
-                    {flow: flow});
+				var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow});
 				dialog.show();
 			}
 		}
+		Connections {
+			target: TimelineManager.timeline
+			function onOpenProfile(profile) {
+				var userProfile = userProfileComponent.createObject(timelineRoot,{profile: profile});
+				userProfile.show();
+			}
+		}
 
 		Label {
 			visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
@@ -287,10 +293,7 @@ Page {
 
 							MouseArea {
 								anchors.fill: parent
-                                onClicked: {
-									userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)});
-									userProfile.show();
-                                }
+								onClicked: chat.model.openUserProfile(modelData.userId)
 								cursorShape: Qt.PointingHandCursor
 								propagateComposedEvents: true
 							}
@@ -305,10 +308,7 @@ Page {
 							MouseArea {
 								anchors.fill: parent
 								Layout.alignment: Qt.AlignHCenter
-                                onClicked: {
-									userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)});
-									userProfile.show();
-                                }
+								onClicked: chat.model.openUserProfile(modelData.userId)
 								cursorShape: Qt.PointingHandCursor
 								propagateComposedEvents: true
 							}
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index db44ec159..df54367b3 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -8,220 +8,211 @@ import im.nheko 1.0
 import "./device-verification"
 
 ApplicationWindow{
-    property var user_data
-    property var avatarUrl
-    property var colors: currentActivePalette
-
-    id:userProfileDialog
-    height: 650
-    width: 420
-    modality:Qt.WindowModal
-    Layout.alignment: Qt.AlignHCenter
-    palette: colors
-
-    Component {
+	property var profile
+
+	id: userProfileDialog
+	height: 650
+	width: 420
+	modality: Qt.WindowModal
+	Layout.alignment: Qt.AlignHCenter
+	palette: colors
+
+	Component {
 		id: deviceVerificationDialog
 		DeviceVerification {}
 	}
-    Component{
-        id: deviceVerificationFlow
-        DeviceVerificationFlow {}
-    }
-
-    background: Item{
-        id: userProfileItem
-        width: userProfileDialog.width
-        height: userProfileDialog.height
-
-        // Layout.fillHeight : true
-
-        ColumnLayout{
-            anchors.fill: userProfileItem
-            width: userProfileDialog.width
-            spacing: 10
-
-            Avatar{
-                id: userProfileAvatar
-                url: avatarUrl.replace("mxc://", "image://MxcImage/")
-                height: 130
-                width: 130
-                displayName: user_data.userName
-		        userid: user_data.userId
-                Layout.alignment: Qt.AlignHCenter
-                Layout.margins : {
-                    top: 10
-                }
-            }
-
-            Label{
-                id: userProfileName
-                text: user_data.userName
-                fontSizeMode: Text.HorizontalFit
-                font.pixelSize: 20
-                color:TimelineManager.userColor(user_data.userId, colors.window)
-                font.bold: true
-                Layout.alignment: Qt.AlignHCenter
-            }
-
-            Label{
-                id: matrixUserID
-                text: user_data.userId
-                fontSizeMode: Text.HorizontalFit
-                font.pixelSize: 15
-                color:colors.text
-                Layout.alignment: Qt.AlignHCenter
-            }
-
-            RowLayout{
-                Layout.alignment: Qt.AlignHCenter
-                ImageButton{
-                    image:":/icons/icons/ui/do-not-disturb-rounded-sign.png"
-                    Layout.margins: {
-                        left: 5
-                        right: 5
-                    }
-                    ToolTip.visible: hovered
-			        ToolTip.text: qsTr("Ban the user")
-                    onClicked : {
-                        modelDeviceList.deviceList.banUser()
-                    }
-                }
-                // ImageButton{
-                //     image:":/icons/icons/ui/volume-off-indicator.png"
-                //     Layout.margins: {
-                //         left: 5
-                //         right: 5
-                //     }
-                //     ToolTip.visible: hovered
-			    //     ToolTip.text: qsTr("Ignore messages from this user")
-                //     onClicked : {
-                //         modelDeviceList.deviceList.ignoreUser()
-                //     }
-                // }
-                ImageButton{
-                    image:":/icons/icons/ui/black-bubble-speech.png"
-                    Layout.margins: {
-                        left: 5
-                        right: 5
-                    }
-                    ToolTip.visible: hovered
-			        ToolTip.text: qsTr("Start a private chat")
-                    onClicked : {
-                        modelDeviceList.deviceList.startChat()
-                    }
-                }
-                ImageButton{
-                    image:":/icons/icons/ui/round-remove-button.png"
-                    Layout.margins: {
-                        left: 5
-                        right: 5
-                    }
-                    ToolTip.visible: hovered
-			        ToolTip.text: qsTr("Kick the user")
-                    onClicked : {
-                        modelDeviceList.deviceList.kickUser()
-                    }
-                }
-            }
-
-            ScrollView {
-                implicitHeight: userProfileDialog.height/2 + 20
-                implicitWidth: userProfileDialog.width-20
-                clip: true
-                Layout.alignment: Qt.AlignHCenter
-
-                ListView{
-                    id: devicelist
-                    anchors.fill: parent
-                    clip: true
-                    spacing: 4
-
-                    model: UserProfileModel{
-                        id: modelDeviceList
-                        deviceList.userId : user_data.userId
-                    }
-
-                    delegate: RowLayout{
-                        width: parent.width
-                        Layout.margins : {
-                            top : 50
-                        }
-                        ColumnLayout{
-                            RowLayout{
-                                Text{
-                                    Layout.fillWidth: true
-                                    color: colors.text
-                                    font.bold: true
-                                    Layout.alignment: Qt.AlignLeft
-                                    text: deviceID
-                                }
-                                Text{
-                                    Layout.fillWidth: true
-                                    color:colors.text
-                                    Layout.alignment: Qt.AlignLeft
-                                    text: (verified_status ==  UserProfileList.VERIFIED?"V":(verified_status ==  UserProfileList.UNVERIFIED?"NV":"B"))
-                                }
-                            }
-                            Text{
-                                Layout.fillWidth: true
-                                color:colors.text
-                                Layout.alignment: Qt.AlignRight
-                                text: displayName
-                            }
-                        }
-                        Button{
-                            id: verifyButton
-                            text:"Verify"
-                            onClicked: {
-                                var newFlow = deviceVerificationFlow.createObject(userProfileDialog,
-                                {userId : user_data.userId,sender: true,deviceId : model.deviceID});
-                                deviceVerificationList.add(newFlow.tranId);
-								var dialog = deviceVerificationDialog.createObject(userProfileDialog, 
-                                    {flow: newFlow});
-				                dialog.show();
-                            }
-                            Layout.margins:{
-                                right: 10
-                            }
-                            palette {
-                                button: "white"
-                            }
-                            contentItem: Text {
-                                text: verifyButton.text
-                                color: "black"
-                                horizontalAlignment: Text.AlignHCenter
-                                verticalAlignment: Text.AlignVCenter
-                            }
-                        }
-                    }
-                }
-            }
-
-            Button{
-                id: okbutton
-                text:"OK"
-                onClicked: userProfileDialog.close()
-                
-                Layout.alignment: Qt.AlignRight | Qt.AlignBottom
-
-                Layout.margins : {
-                    right : 10
-                    bottom: 5
-                }
-
-                palette {
-                    button: "white"
-                }
-
-                contentItem: Text {
-                    text: okbutton.text
-                    color: "black"
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
-                }
-            }
-        }
-
-        Item { Layout.fillHeight: true }
-    }
+	Component{
+		id: deviceVerificationFlow
+		DeviceVerificationFlow {}
+	}
+
+	background: Item{
+		id: userProfileItem
+		width: userProfileDialog.width
+		height: userProfileDialog.height
+
+		// Layout.fillHeight : true
+
+		ColumnLayout{
+			anchors.fill: userProfileItem
+			width: userProfileDialog.width
+			spacing: 10
+
+			Avatar {
+				url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
+				height: 130
+				width: 130
+				displayName: profile.displayName
+				userid: profile.userid
+				Layout.alignment: Qt.AlignHCenter
+				Layout.margins : {
+					top: 10
+				}
+			}
+
+			Label {
+				text: profile.displayName
+				fontSizeMode: Text.HorizontalFit
+				font.pixelSize: 20
+				color: TimelineManager.userColor(profile.userid, colors.window)
+				font.bold: true
+				Layout.alignment: Qt.AlignHCenter
+			}
+
+			Label {
+				text: profile.userid
+				fontSizeMode: Text.HorizontalFit
+				font.pixelSize: 15
+				color: colors.text
+				Layout.alignment: Qt.AlignHCenter
+			}
+
+			RowLayout {
+				Layout.alignment: Qt.AlignHCenter
+				ImageButton {
+					image:":/icons/icons/ui/do-not-disturb-rounded-sign.png"
+					Layout.margins: {
+						left: 5
+						right: 5
+					}
+					ToolTip.visible: hovered
+					ToolTip.text: qsTr("Ban the user")
+					onClicked : {
+						profile.banUser()
+					}
+				}
+				// ImageButton{
+				//     image:":/icons/icons/ui/volume-off-indicator.png"
+				//     Layout.margins: {
+				//         left: 5
+				//         right: 5
+				//     }
+				//     ToolTip.visible: hovered
+				//     ToolTip.text: qsTr("Ignore messages from this user")
+				//     onClicked : {
+				//         profile.ignoreUser()
+				//     }
+				// }
+				ImageButton{
+					image:":/icons/icons/ui/black-bubble-speech.png"
+					Layout.margins: {
+						left: 5
+						right: 5
+					}
+					ToolTip.visible: hovered
+					ToolTip.text: qsTr("Start a private chat")
+					onClicked : {
+						profile.startChat()
+					}
+				}
+				ImageButton{
+					image:":/icons/icons/ui/round-remove-button.png"
+					Layout.margins: {
+						left: 5
+						right: 5
+					}
+					ToolTip.visible: hovered
+					ToolTip.text: qsTr("Kick the user")
+					onClicked : {
+						profile.kickUser()
+					}
+				}
+			}
+
+			ScrollView {
+				implicitHeight: userProfileDialog.height/2 + 20
+				implicitWidth: userProfileDialog.width-20
+				clip: true
+				Layout.alignment: Qt.AlignHCenter
+
+				ListView{
+					id: devicelist
+					anchors.fill: parent
+					clip: true
+					spacing: 4
+
+					model: profile.deviceList
+
+					delegate: RowLayout{
+						width: parent.width
+						Layout.margins : {
+							top : 50
+						}
+						ColumnLayout{
+							RowLayout{
+								Text{
+									Layout.fillWidth: true
+									color: colors.text
+									font.bold: true
+									Layout.alignment: Qt.AlignLeft
+									text: model.deviceId
+								}
+								Text{
+									Layout.fillWidth: true
+									color:colors.text
+									Layout.alignment: Qt.AlignLeft
+									text: (model.verificationStatus ==  VerificationStatus.VERIFIED?"V":(model.verificationStatus ==  VerificationStatus.UNVERIFIED?"NV":"B"))
+								}
+							}
+							Text{
+								Layout.fillWidth: true
+								color:colors.text
+								Layout.alignment: Qt.AlignRight
+								text: model.deviceName
+							}
+						}
+						Button{
+							id: verifyButton
+							text:"Verify"
+							onClicked: {
+								var newFlow = deviceVerificationFlow.createObject(userProfileDialog,
+								{userId : profile.userid, sender: true, deviceId : model.deviceID});
+								deviceVerificationList.add(newFlow.tranId);
+								var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow});
+								dialog.show();
+							}
+							Layout.margins:{
+								right: 10
+							}
+							palette {
+								button: "white"
+							}
+							contentItem: Text {
+								text: verifyButton.text
+								color: "black"
+								horizontalAlignment: Text.AlignHCenter
+								verticalAlignment: Text.AlignVCenter
+							}
+						}
+					}
+				}
+			}
+
+			Button{
+				id: okbutton
+				text:"OK"
+				onClicked: userProfileDialog.close()
+
+				Layout.alignment: Qt.AlignRight | Qt.AlignBottom
+
+				Layout.margins : {
+					right : 10
+					bottom: 5
+				}
+
+				palette {
+					button: "white"
+				}
+
+				contentItem: Text {
+					text: okbutton.text
+					color: "black"
+					horizontalAlignment: Text.AlignHCenter
+					verticalAlignment: Text.AlignVCenter
+				}
+			}
+		}
+
+		Item { Layout.fillHeight: true }
+	}
 }
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index cc1d868bc..63b524c8a 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -317,15 +317,6 @@ MainWindow::hasActiveUser()
                settings.contains("auth/user_id");
 }
 
-void
-MainWindow::openUserProfile(const QString &user_id, const QString &room_id)
-{
-        auto dialog = new dialogs::UserProfile(this);
-        dialog->init(user_id, room_id);
-
-        showDialog(dialog);
-}
-
 void
 MainWindow::openRoomSettings(const QString &room_id)
 {
diff --git a/src/MainWindow.h b/src/MainWindow.h
index e3e046981..2fc2d00f4 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -25,7 +25,6 @@
 #include 
 
 #include "UserSettingsPage.h"
-#include "dialogs/UserProfile.h"
 #include "ui/OverlayModal.h"
 
 #include "jdenticoninterface.h"
@@ -76,7 +75,6 @@ class MainWindow : public QMainWindow
         void openLogoutDialog();
         void openRoomSettings(const QString &room_id = "");
         void openMemberListDialog(const QString &room_id = "");
-        void openUserProfile(const QString &user_id, const QString &room_id);
         void openReadReceiptsDialog(const QString &event_id);
 
         void hideOverlay();
diff --git a/src/dialogs/UserProfile.cpp b/src/dialogs/UserProfile.cpp
deleted file mode 100644
index 3415b127d..000000000
--- a/src/dialogs/UserProfile.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#include "Cache.h"
-#include "ChatPage.h"
-#include "Logging.h"
-#include "MatrixClient.h"
-#include "Utils.h"
-#include "dialogs/UserProfile.h"
-#include "ui/Avatar.h"
-#include "ui/FlatButton.h"
-
-using namespace dialogs;
-
-Q_DECLARE_METATYPE(std::vector)
-
-constexpr int BUTTON_SIZE       = 36;
-constexpr int BUTTON_RADIUS     = BUTTON_SIZE / 2;
-constexpr int WIDGET_MARGIN     = 20;
-constexpr int TOP_WIDGET_MARGIN = 2 * WIDGET_MARGIN;
-constexpr int WIDGET_SPACING    = 15;
-constexpr int TEXT_SPACING      = 4;
-constexpr int DEVICE_SPACING    = 5;
-
-DeviceItem::DeviceItem(DeviceInfo device, QWidget *parent)
-  : QWidget(parent)
-  , info_(std::move(device))
-{
-        QFont font;
-        font.setBold(true);
-
-        auto deviceIdLabel = new QLabel(info_.device_id, this);
-        deviceIdLabel->setFont(font);
-
-        auto layout = new QVBoxLayout{this};
-        layout->addWidget(deviceIdLabel);
-
-        if (!info_.display_name.isEmpty())
-                layout->addWidget(new QLabel(info_.display_name, this));
-
-        layout->setMargin(0);
-        layout->setSpacing(4);
-}
-
-UserProfile::UserProfile(QWidget *parent)
-  : QWidget(parent)
-{
-        setAutoFillBackground(true);
-        setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
-        setAttribute(Qt::WA_DeleteOnClose, true);
-
-        QIcon banIcon, kickIcon, ignoreIcon, startChatIcon;
-
-        banIcon.addFile(":/icons/icons/ui/do-not-disturb-rounded-sign.png");
-        banBtn_ = new FlatButton(this);
-        banBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
-        banBtn_->setCornerRadius(BUTTON_RADIUS);
-        banBtn_->setIcon(banIcon);
-        banBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
-        banBtn_->setToolTip(tr("Ban the user from the room"));
-
-        ignoreIcon.addFile(":/icons/icons/ui/volume-off-indicator.png");
-        ignoreBtn_ = new FlatButton(this);
-        ignoreBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
-        ignoreBtn_->setCornerRadius(BUTTON_RADIUS);
-        ignoreBtn_->setIcon(ignoreIcon);
-        ignoreBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
-        ignoreBtn_->setToolTip(tr("Ignore messages from this user"));
-        ignoreBtn_->setDisabled(true); // Not used yet.
-
-        kickIcon.addFile(":/icons/icons/ui/round-remove-button.png");
-        kickBtn_ = new FlatButton(this);
-        kickBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
-        kickBtn_->setCornerRadius(BUTTON_RADIUS);
-        kickBtn_->setIcon(kickIcon);
-        kickBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
-        kickBtn_->setToolTip(tr("Kick the user from the room"));
-
-        startChatIcon.addFile(":/icons/icons/ui/black-bubble-speech.png");
-        startChat_ = new FlatButton(this);
-        startChat_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
-        startChat_->setCornerRadius(BUTTON_RADIUS);
-        startChat_->setIcon(startChatIcon);
-        startChat_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
-        startChat_->setToolTip(tr("Start a conversation"));
-
-        connect(startChat_, &QPushButton::clicked, this, [this]() {
-                auto user_id = userIdLabel_->text();
-
-                mtx::requests::CreateRoom req;
-                req.preset     = mtx::requests::Preset::PrivateChat;
-                req.visibility = mtx::requests::Visibility::Private;
-
-                if (utils::localUser() != user_id)
-                        req.invite = {user_id.toStdString()};
-
-                emit ChatPage::instance()->createRoom(req);
-        });
-
-        connect(banBtn_, &QPushButton::clicked, this, [this] {
-                ChatPage::instance()->banUser(userIdLabel_->text(), "");
-        });
-        connect(kickBtn_, &QPushButton::clicked, this, [this] {
-                ChatPage::instance()->kickUser(userIdLabel_->text(), "");
-        });
-
-        // Button line
-        auto btnLayout = new QHBoxLayout;
-        btnLayout->addStretch(1);
-        btnLayout->addWidget(startChat_);
-        btnLayout->addWidget(ignoreBtn_);
-
-        btnLayout->addWidget(kickBtn_);
-        btnLayout->addWidget(banBtn_);
-        btnLayout->addStretch(1);
-        btnLayout->setSpacing(8);
-        btnLayout->setMargin(0);
-
-        avatar_ = new Avatar(this, 128);
-        avatar_->setLetter("X");
-
-        QFont font;
-        font.setPointSizeF(font.pointSizeF() * 2);
-
-        userIdLabel_      = new QLabel(this);
-        displayNameLabel_ = new QLabel(this);
-        displayNameLabel_->setFont(font);
-
-        auto textLayout = new QVBoxLayout;
-        textLayout->addWidget(displayNameLabel_);
-        textLayout->addWidget(userIdLabel_);
-        textLayout->setAlignment(displayNameLabel_, Qt::AlignCenter | Qt::AlignTop);
-        textLayout->setAlignment(userIdLabel_, Qt::AlignCenter | Qt::AlignTop);
-        textLayout->setSpacing(TEXT_SPACING);
-        textLayout->setMargin(0);
-
-        devices_ = new QListWidget{this};
-        devices_->setFrameStyle(QFrame::NoFrame);
-        devices_->setSelectionMode(QAbstractItemView::NoSelection);
-        devices_->setAttribute(Qt::WA_MacShowFocusRect, 0);
-        devices_->setSpacing(DEVICE_SPACING);
-
-        QFont descriptionLabelFont;
-        descriptionLabelFont.setWeight(65);
-
-        devicesLabel_ = new QLabel(tr("Devices").toUpper(), this);
-        devicesLabel_->setFont(descriptionLabelFont);
-        devicesLabel_->hide();
-        devicesLabel_->setFixedSize(devicesLabel_->sizeHint());
-
-        auto okBtn = new QPushButton("OK", this);
-
-        auto closeLayout = new QHBoxLayout();
-        closeLayout->setSpacing(15);
-        closeLayout->addStretch(1);
-        closeLayout->addWidget(okBtn);
-
-        auto vlayout = new QVBoxLayout{this};
-        vlayout->addWidget(avatar_, 0, Qt::AlignCenter | Qt::AlignTop);
-        vlayout->addLayout(textLayout);
-        vlayout->addLayout(btnLayout);
-        vlayout->addWidget(devicesLabel_, 0, Qt::AlignLeft);
-        vlayout->addWidget(devices_, 1);
-        vlayout->addLayout(closeLayout);
-
-        QFont largeFont;
-        largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5);
-
-        setMinimumWidth(
-          std::max(devices_->sizeHint().width() + 4 * WIDGET_MARGIN, conf::window::minModalWidth));
-        setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
-
-        vlayout->setSpacing(WIDGET_SPACING);
-        vlayout->setContentsMargins(WIDGET_MARGIN, TOP_WIDGET_MARGIN, WIDGET_MARGIN, WIDGET_MARGIN);
-
-        qRegisterMetaType>();
-
-        auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
-        connect(closeShortcut, &QShortcut::activated, this, &UserProfile::close);
-        connect(okBtn, &QPushButton::clicked, this, &UserProfile::close);
-}
-
-void
-UserProfile::resetToDefaults()
-{
-        avatar_->setLetter("X");
-        devices_->clear();
-
-        ignoreBtn_->show();
-        devices_->hide();
-        devicesLabel_->hide();
-}
-
-void
-UserProfile::init(const QString &userId, const QString &roomId)
-{
-        resetToDefaults();
-
-        auto displayName = cache::displayName(roomId, userId);
-
-        userIdLabel_->setText(userId);
-        displayNameLabel_->setText(displayName);
-        avatar_->setLetter(utils::firstChar(displayName));
-
-        avatar_->setImage(roomId, userId);
-
-        auto localUser = utils::localUser();
-
-        try {
-                bool hasMemberRights =
-                  cache::hasEnoughPowerLevel({mtx::events::EventType::RoomMember},
-                                             roomId.toStdString(),
-                                             localUser.toStdString());
-                if (!hasMemberRights) {
-                        kickBtn_->hide();
-                        banBtn_->hide();
-                } else {
-                        kickBtn_->show();
-                        banBtn_->show();
-                }
-        } catch (const lmdb::error &e) {
-                nhlog::db()->warn("lmdb error: {}", e.what());
-        }
-
-        if (localUser == userId) {
-                // TODO: click on display name & avatar to change.
-                kickBtn_->hide();
-                banBtn_->hide();
-                ignoreBtn_->hide();
-        }
-
-        mtx::requests::QueryKeys req;
-        req.device_keys[userId.toStdString()] = {};
-
-        // A proxy object is used to emit the signal instead of the original object
-        // which might be destroyed by the time the http call finishes.
-        auto proxy = std::make_shared();
-        QObject::connect(proxy.get(), &Proxy::done, this, &UserProfile::updateDeviceList);
-
-        http::client()->query_keys(
-          req,
-          [user_id = userId.toStdString(), proxy = std::move(proxy)](
-            const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) {
-                  if (err) {
-                          nhlog::net()->warn("failed to query device keys: {} {}",
-                                             err->matrix_error.error,
-                                             static_cast(err->status_code));
-                          // TODO: Notify the UI.
-                          return;
-                  }
-
-                  if (res.device_keys.empty() ||
-                      (res.device_keys.find(user_id) == res.device_keys.end())) {
-                          nhlog::net()->warn("no devices retrieved {}", user_id);
-                          return;
-                  }
-
-                  auto devices = res.device_keys.at(user_id);
-
-                  std::vector deviceInfo;
-                  for (const auto &d : devices) {
-                          auto device = d.second;
-
-                          // TODO: Verify signatures and ignore those that don't pass.
-                          deviceInfo.emplace_back(DeviceInfo{
-                            QString::fromStdString(d.first),
-                            QString::fromStdString(device.unsigned_info.device_display_name)});
-                  }
-
-                  std::sort(deviceInfo.begin(),
-                            deviceInfo.end(),
-                            [](const DeviceInfo &a, const DeviceInfo &b) {
-                                    return a.device_id > b.device_id;
-                            });
-
-                  if (!deviceInfo.empty())
-                          emit proxy->done(QString::fromStdString(user_id), deviceInfo);
-          });
-}
-
-void
-UserProfile::updateDeviceList(const QString &user_id, const std::vector &devices)
-{
-        if (user_id != userIdLabel_->text())
-                return;
-
-        for (const auto &dev : devices) {
-                auto deviceItem = new DeviceItem(dev, this);
-                auto item       = new QListWidgetItem;
-
-                item->setSizeHint(deviceItem->minimumSizeHint());
-                item->setFlags(Qt::NoItemFlags);
-                item->setTextAlignment(Qt::AlignCenter);
-
-                devices_->insertItem(devices_->count() - 1, item);
-                devices_->setItemWidget(item, deviceItem);
-        }
-
-        devicesLabel_->show();
-        devices_->show();
-        adjustSize();
-}
diff --git a/src/dialogs/UserProfile.h b/src/dialogs/UserProfile.h
deleted file mode 100644
index 81276d2ae..000000000
--- a/src/dialogs/UserProfile.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-
-#include 
-#include 
-
-class Avatar;
-class FlatButton;
-class QLabel;
-class QListWidget;
-class Toggle;
-
-struct DeviceInfo
-{
-        QString device_id;
-        QString display_name;
-};
-
-class Proxy : public QObject
-{
-        Q_OBJECT
-
-signals:
-        void done(const QString &user_id, const std::vector &devices);
-};
-
-namespace dialogs {
-
-class DeviceItem : public QWidget
-{
-        Q_OBJECT
-
-public:
-        explicit DeviceItem(DeviceInfo device, QWidget *parent);
-
-private:
-        DeviceInfo info_;
-
-        // Toggle *verifyToggle_;
-};
-
-class UserProfile : public QWidget
-{
-        Q_OBJECT
-public:
-        explicit UserProfile(QWidget *parent = nullptr);
-
-        void init(const QString &userId, const QString &roomId);
-
-private slots:
-        void updateDeviceList(const QString &user_id, const std::vector &devices);
-
-private:
-        void resetToDefaults();
-
-        Avatar *avatar_;
-
-        QLabel *userIdLabel_;
-        QLabel *displayNameLabel_;
-
-        FlatButton *banBtn_;
-        FlatButton *kickBtn_;
-        FlatButton *ignoreBtn_;
-        FlatButton *startChat_;
-
-        QLabel *devicesLabel_;
-        QListWidget *devices_;
-};
-
-} // dialogs
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 16e4f207e..7d6e0e911 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -848,9 +848,9 @@ TimelineModel::viewDecryptedRawMessage(QString id) const
 }
 
 void
-TimelineModel::openUserProfile(QString userid) const
+TimelineModel::openUserProfile(QString userid)
 {
-        MainWindow::instance()->openUserProfile(userid, room_id_);
+        emit openProfile(new UserProfile(room_id_, userid, this));
 }
 
 DecryptionResult
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index a3b92f83f..edf33c7d9 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -10,6 +10,7 @@
 
 #include "CacheCryptoStructs.h"
 #include "ReactionsModel.h"
+#include "ui/UserProfile.h"
 
 namespace mtx::http {
 using RequestErr = const std::optional &;
@@ -188,7 +189,7 @@ class TimelineModel : public QAbstractListModel
         Q_INVOKABLE QString escapeEmoji(QString str) const;
         Q_INVOKABLE void viewRawMessage(QString id) const;
         Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
-        Q_INVOKABLE void openUserProfile(QString userid) const;
+        Q_INVOKABLE void openUserProfile(QString userid);
         Q_INVOKABLE void replyAction(QString id);
         Q_INVOKABLE void readReceiptsAction(QString id) const;
         Q_INVOKABLE void redactEvent(QString id);
@@ -256,6 +257,8 @@ private slots:
         void replyChanged(QString reply);
         void paginationInProgressChanged(const bool);
 
+        void openProfile(UserProfile *profile);
+
 private:
         DecryptionResult decryptEvent(
           const mtx::events::EncryptedEvent &e) const;
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 17023c976..b7c734ee9 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -16,10 +16,9 @@
 #include "dialogs/ImageOverlay.h"
 #include "emoji/EmojiModel.h"
 #include "emoji/Provider.h"
-#include "src/ui/UserProfile.h"
-#include "src/ui/UserProfileModel.h"
 
 Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
+Q_DECLARE_METATYPE(std::vector)
 
 namespace msgs = mtx::events::msg;
 
@@ -109,15 +108,28 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                                          0,
                                          "MtxEvent",
                                          "Can't instantiate enum!");
+        qmlRegisterUncreatableMetaObject(verification::staticMetaObject,
+                                         "im.nheko",
+                                         1,
+                                         0,
+                                         "VerificationStatus",
+                                         "Can't instantiate enum!");
+
         qmlRegisterType("im.nheko", 1, 0, "DelegateChoice");
         qmlRegisterType("im.nheko", 1, 0, "DelegateChooser");
         qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow");
-        qmlRegisterType("im.nheko", 1, 0, "UserProfileModel");
-        qmlRegisterType("im.nheko", 1, 0, "UserProfileList");
+        qmlRegisterUncreatableType(
+          "im.nheko",
+          1,
+          0,
+          "UserProfileModel",
+          "UserProfile needs to be instantiated on the C++ side");
         qmlRegisterSingletonInstance("im.nheko", 1, 0, "TimelineManager", this);
         qmlRegisterSingletonInstance("im.nheko", 1, 0, "Settings", settings.data());
 
         qRegisterMetaType();
+        qRegisterMetaType>();
+
         qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel");
         qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel");
         qmlRegisterUncreatableType(
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 8c6fb8e40..fde0044bc 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -5,47 +5,72 @@
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
 
-#include  // only for debugging
+UserProfile::UserProfile(QString roomid, QString userid, QObject *parent)
+  : QObject(parent)
+  , roomid_(roomid)
+  , userid_(userid)
+{
+        fetchDeviceList(this->userid_);
+}
 
-Q_DECLARE_METATYPE(UserProfile::Status)
+QHash
+DeviceInfoModel::roleNames() const
+{
+        return {
+          {DeviceId, "deviceId"},
+          {DeviceName, "deviceName"},
+          {VerificationStatus, "verificationStatus"},
+        };
+}
 
-UserProfile::UserProfile(QObject *parent)
-  : QObject(parent)
+QVariant
+DeviceInfoModel::data(const QModelIndex &index, int role) const
 {
-        qRegisterMetaType();
-        connect(
-          this, &UserProfile::updateDeviceList, this, [this]() { fetchDeviceList(this->userId); });
-        connect(
-          this,
-          &UserProfile::appendDeviceList,
-          this,
-          [this](QString device_id, QString device_name, UserProfile::Status verification_status) {
-                  this->deviceList.push_back(
-                    DeviceInfo{device_id, device_name, verification_status});
-          });
+        if (!index.isValid() || index.row() >= (int)deviceList_.size() || index.row() < 0)
+                return {};
+
+        switch (role) {
+        case DeviceId:
+                return deviceList_[index.row()].device_id;
+        case DeviceName:
+                return deviceList_[index.row()].display_name;
+        case VerificationStatus:
+                return QVariant::fromValue(deviceList_[index.row()].verification_status);
+        default:
+                return {};
+        }
 }
 
-std::vector
-UserProfile::getDeviceList()
+void
+DeviceInfoModel::reset(const std::vector &deviceList)
 {
-        return this->deviceList;
+        beginResetModel();
+        this->deviceList_ = std::move(deviceList);
+        endResetModel();
+}
+
+DeviceInfoModel *
+UserProfile::deviceList()
+{
+        return &this->deviceList_;
 }
 
 QString
-UserProfile::getUserId()
+UserProfile::userid()
 {
-        return this->userId;
+        return this->userid_;
 }
 
-void
-UserProfile::setUserId(const QString &user_id)
+QString
+UserProfile::displayName()
 {
-        if (this->userId != userId)
-                return;
-        else {
-                this->userId = user_id;
-                emit UserProfile::userIdChanged();
-        }
+        return cache::displayName(roomid_, userid_);
+}
+
+QString
+UserProfile::avatarUrl()
+{
+        return cache::avatarUrl(roomid_, userid_);
 }
 
 void
@@ -74,27 +99,27 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
                 auto device = d.second;
 
                 // TODO: Verify signatures and ignore those that don't pass.
-                UserProfile::Status verified = UserProfile::Status::UNVERIFIED;
+                verification::Status verified = verification::Status::UNVERIFIED;
                 if (cross_verified.has_value()) {
                         if (std::find(cross_verified->begin(), cross_verified->end(), d.first) !=
                             cross_verified->end())
-                                verified = UserProfile::Status::VERIFIED;
+                                verified = verification::Status::VERIFIED;
                 } else if (device_verified.has_value()) {
                         if (std::find(device_verified->device_verified.begin(),
                                       device_verified->device_verified.end(),
                                       d.first) != device_verified->device_verified.end())
-                                verified = UserProfile::Status::VERIFIED;
+                                verified = verification::Status::VERIFIED;
                 } else if (device_verified.has_value()) {
                         if (std::find(device_verified->device_blocked.begin(),
                                       device_verified->device_blocked.end(),
                                       d.first) != device_verified->device_blocked.end())
-                                verified = UserProfile::Status::BLOCKED;
+                                verified = verification::Status::BLOCKED;
                 }
 
-                emit UserProfile::appendDeviceList(
-                  QString::fromStdString(d.first),
-                  QString::fromStdString(device.unsigned_info.device_display_name),
-                  verified);
+                deviceInfo.push_back(
+                  {QString::fromStdString(d.first),
+                   QString::fromStdString(device.unsigned_info.device_display_name),
+                   verified});
         }
 
         // std::sort(
@@ -102,8 +127,7 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
         //           return a.device_id > b.device_id;
         //   });
 
-        this->deviceList = std::move(deviceInfo);
-        emit UserProfile::deviceListUpdated();
+        this->deviceList_.queueReset(std::move(deviceInfo));
 }
 
 void
@@ -130,7 +154,7 @@ UserProfile::fetchDeviceList(const QString &userID)
 void
 UserProfile::banUser()
 {
-        ChatPage::instance()->banUser(this->userId, "");
+        ChatPage::instance()->banUser(this->userid_, "");
 }
 
 // void ignoreUser(){
@@ -140,7 +164,7 @@ UserProfile::banUser()
 void
 UserProfile::kickUser()
 {
-        ChatPage::instance()->kickUser(this->userId, "");
+        ChatPage::instance()->kickUser(this->userid_, "");
 }
 
 void
@@ -149,7 +173,7 @@ UserProfile::startChat()
         mtx::requests::CreateRoom req;
         req.preset     = mtx::requests::Preset::PrivateChat;
         req.visibility = mtx::requests::Visibility::Private;
-        if (utils::localUser() != this->userId)
-                req.invite = {this->userId.toStdString()};
+        if (utils::localUser() != this->userid_)
+                req.invite = {this->userid_.toStdString()};
         emit ChatPage::instance()->createRoom(req);
 }
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index 1725b961b..38002fffc 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -1,34 +1,92 @@
 #pragma once
 
+#include 
 #include 
 #include 
 #include 
 
 #include "MatrixClient.h"
 
-class DeviceInfo;
+namespace verification {
+Q_NAMESPACE
 
-class UserProfile : public QObject
+enum Status
+{
+        VERIFIED,
+        UNVERIFIED,
+        BLOCKED
+};
+Q_ENUM_NS(Status)
+}
+
+class DeviceInfo
+{
+public:
+        DeviceInfo(const QString deviceID,
+                   const QString displayName,
+                   verification::Status verification_status_)
+          : device_id(deviceID)
+          , display_name(displayName)
+          , verification_status(verification_status_)
+        {}
+        DeviceInfo()
+          : verification_status(verification::UNVERIFIED)
+        {}
+
+        QString device_id;
+        QString display_name;
+
+        verification::Status verification_status;
+};
+
+class DeviceInfoModel : public QAbstractListModel
 {
         Q_OBJECT
-        Q_PROPERTY(QString userId READ getUserId WRITE setUserId NOTIFY userIdChanged)
-        Q_PROPERTY(std::vector deviceList READ getDeviceList NOTIFY deviceListUpdated)
 public:
-        // constructor
-        explicit UserProfile(QObject *parent = 0);
-        // getters
-        std::vector getDeviceList();
-        QString getUserId();
-        // setters
-        void setUserId(const QString &userId);
-
-        enum Status
+        enum Roles
         {
-                VERIFIED,
-                UNVERIFIED,
-                BLOCKED
+                DeviceId,
+                DeviceName,
+                VerificationStatus,
         };
-        Q_ENUM(Status)
+
+        explicit DeviceInfoModel(QObject *parent = nullptr)
+        {
+                (void)parent;
+                connect(this, &DeviceInfoModel::queueReset, this, &DeviceInfoModel::reset);
+        };
+        QHash roleNames() const override;
+        int rowCount(const QModelIndex &parent = QModelIndex()) const
+        {
+                (void)parent;
+                return (int)deviceList_.size();
+        }
+        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+signals:
+        void queueReset(const std::vector &deviceList);
+public slots:
+        void reset(const std::vector &deviceList);
+
+private:
+        std::vector deviceList_;
+};
+
+class UserProfile : public QObject
+{
+        Q_OBJECT
+        Q_PROPERTY(QString displayName READ displayName CONSTANT)
+        Q_PROPERTY(QString userid READ userid CONSTANT)
+        Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT)
+        Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
+public:
+        UserProfile(QString roomid, QString userid, QObject *parent = 0);
+
+        DeviceInfoModel *deviceList();
+
+        QString userid();
+        QString displayName();
+        QString avatarUrl();
 
         void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void banUser();
@@ -36,37 +94,13 @@ class UserProfile : public QObject
         Q_INVOKABLE void kickUser();
         Q_INVOKABLE void startChat();
 
-signals:
-        void userIdChanged();
-        void deviceListUpdated();
-        void updateDeviceList();
-        void appendDeviceList(const QString device_id,
-                              const QString device_naem,
-                              const UserProfile::Status verification_status);
-
 private:
-        std::vector deviceList;
-        QString userId;
+        QString roomid_, userid_;
         std::optional cross_verified;
+        DeviceInfoModel deviceList_;
 
         void callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,
                          std::string user_id,
                          std::optional> cross_verified);
 };
-
-class DeviceInfo
-{
-public:
-        DeviceInfo(const QString deviceID,
-                   const QString displayName,
-                   UserProfile::Status verification_status_)
-          : device_id(deviceID)
-          , display_name(displayName)
-          , verification_status(verification_status_)
-        {}
-
-        QString device_id;
-        QString display_name;
-        UserProfile::Status verification_status;
-};
\ No newline at end of file
diff --git a/src/ui/UserProfileModel.cpp b/src/ui/UserProfileModel.cpp
deleted file mode 100644
index 3fa8fe2d8..000000000
--- a/src/ui/UserProfileModel.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "UserProfileModel.h"
-#include 
-
-UserProfileModel::UserProfileModel(QObject *parent)
-  : QAbstractListModel(parent)
-  , deviceList(nullptr)
-{
-        this->deviceList = new UserProfile(this);
-
-        connect(this->deviceList, &UserProfile::userIdChanged, this, [this]() {
-                emit this->deviceList->updateDeviceList();
-        });
-        connect(this->deviceList, &UserProfile::deviceListUpdated, this, [this]() {
-                beginResetModel();
-                this->beginInsertRows(
-                  QModelIndex(), 0, this->deviceList->getDeviceList().size() - 1);
-                this->endInsertRows();
-                endResetModel();
-        });
-}
-
-int
-UserProfileModel::rowCount(const QModelIndex &parent) const
-{
-        if (parent.isValid() || !this->deviceList)
-                return 0;
-        return this->deviceList->getDeviceList().size();
-}
-
-QVariant
-UserProfileModel::data(const QModelIndex &index, int role) const
-{
-        if (!index.isValid() &&
-            static_cast(this->deviceList->getDeviceList().size()) <= index.row())
-                return QVariant();
-
-        const DeviceInfo device = this->deviceList->getDeviceList().at(index.row());
-        switch (role) {
-        case DEVICEID:
-                return QVariant(device.device_id);
-        case DISPLAYNAME:
-                return QVariant(device.display_name);
-        case VERIFIED_STATUS:
-                return device.verification_status;
-        }
-        return QVariant();
-}
-
-QHash
-UserProfileModel::roleNames() const
-{
-        QHash names;
-        names[DEVICEID]        = "deviceID";
-        names[DISPLAYNAME]     = "displayName";
-        names[VERIFIED_STATUS] = "verified_status";
-        return names;
-}
-
-UserProfile *
-UserProfileModel::getList() const
-{
-        return (this->deviceList);
-}
diff --git a/src/ui/UserProfileModel.h b/src/ui/UserProfileModel.h
deleted file mode 100644
index ba7a25255..000000000
--- a/src/ui/UserProfileModel.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include "UserProfile.h"
-#include 
-
-class UserProfile; // forward declaration of the class UserProfile
-
-class UserProfileModel : public QAbstractListModel
-{
-        Q_OBJECT
-        Q_PROPERTY(UserProfile *deviceList READ getList)
-
-public:
-        explicit UserProfileModel(QObject *parent = nullptr);
-
-        enum
-        {
-                DEVICEID,
-                DISPLAYNAME,
-                VERIFIED_STATUS
-        };
-        UserProfile *getList() const;
-
-        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
-        QVariant data(const QModelIndex &index, int role) const override;
-        virtual QHash roleNames() const override;
-
-private:
-        UserProfile *deviceList;
-};
\ No newline at end of file

From fcbff88587dbb85721c801371259ee34d7be5200 Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Sun, 5 Jul 2020 21:33:27 +0530
Subject: [PATCH 26/28] Adding icons to UserProfile

---
 CMakeLists.txt                                |   3 -
 resources/qml/TimelineView.qml                |   2 +-
 resources/qml/UserProfile.qml                 |  81 ++++++-----
 .../DeviceVerification.qml                    |   4 +
 src/DeviceVerificationFlow.cpp                | 130 +++++++++++++-----
 src/DeviceVerificationFlow.h                  |   6 +
 src/timeline/TimelineViewManager.h            |   2 +
 src/ui/UserProfile.cpp                        |  11 +-
 src/ui/UserProfile.h                          |   4 +-
 9 files changed, 163 insertions(+), 80 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7e7ee96f5..56bc7c88c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,9 +14,6 @@ set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard")
 set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require C++ standard to be supported")
 set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "compile as PIC by default")
 
-# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
-# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
-
 option(HUNTER_ENABLED "Enable Hunter package manager" OFF)
 include("cmake/HunterGate.cmake")
 HunterGate(
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 046312247..2622f69cc 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -101,7 +101,7 @@ Page {
 		}
 		Connections {
 			target: TimelineManager
-			function onNewDeviceVerificationRequest(flow) {
+			function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId) {
 				flow.userId = userId;
 				flow.sender = false;
 				flow.deviceId = deviceId;
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index df54367b3..5bdccb4d1 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -17,6 +17,13 @@ ApplicationWindow{
 	Layout.alignment: Qt.AlignHCenter
 	palette: colors
 
+	Connections{
+		target: deviceVerificationList
+		onUpdateProfile: {
+			profile.fetchDeviceList(profile.userid)
+		}
+	}
+
 	Component {
 		id: deviceVerificationDialog
 		DeviceVerification {}
@@ -139,20 +146,12 @@ ApplicationWindow{
 							top : 50
 						}
 						ColumnLayout{
-							RowLayout{
-								Text{
-									Layout.fillWidth: true
-									color: colors.text
-									font.bold: true
-									Layout.alignment: Qt.AlignLeft
-									text: model.deviceId
-								}
-								Text{
-									Layout.fillWidth: true
-									color:colors.text
-									Layout.alignment: Qt.AlignLeft
-									text: (model.verificationStatus ==  VerificationStatus.VERIFIED?"V":(model.verificationStatus ==  VerificationStatus.UNVERIFIED?"NV":"B"))
-								}
+							Text{
+								Layout.fillWidth: true
+								color: colors.text
+								font.bold: true
+								Layout.alignment: Qt.AlignLeft
+								text: model.deviceId
 							}
 							Text{
 								Layout.fillWidth: true
@@ -161,27 +160,41 @@ ApplicationWindow{
 								text: model.deviceName
 							}
 						}
-						Button{
-							id: verifyButton
-							text:"Verify"
-							onClicked: {
-								var newFlow = deviceVerificationFlow.createObject(userProfileDialog,
-								{userId : profile.userid, sender: true, deviceId : model.deviceID});
-								deviceVerificationList.add(newFlow.tranId);
-								var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow});
-								dialog.show();
+						RowLayout{
+							Image{
+								Layout.preferredWidth: 20
+								Layout.preferredHeight: 20
+								source: ((model.verificationStatus == VerificationStatus.VERIFIED)?"image://colorimage/:/icons/icons/ui/lock.png?green":
+								((model.verificationStatus == VerificationStatus.UNVERIFIED)?"image://colorimage/:/icons/icons/ui/unlock.png?yellow":
+								"image://colorimage/:/icons/icons/ui/unlock.png?red"))
 							}
-							Layout.margins:{
-								right: 10
-							}
-							palette {
-								button: "white"
-							}
-							contentItem: Text {
-								text: verifyButton.text
-								color: "black"
-								horizontalAlignment: Text.AlignHCenter
-								verticalAlignment: Text.AlignVCenter
+							Button{
+								id: verifyButton
+								text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
+								onClicked: {
+									var newFlow = deviceVerificationFlow.createObject(userProfileDialog,
+									{userId : profile.userid, sender: true, deviceId : model.deviceId});
+									if(model.verificationStatus == VerificationStatus.VERIFIED){
+										newFlow.unverify();
+										deviceVerificationList.updateProfile(newFlow.userId);
+									}else{
+										deviceVerificationList.add(newFlow.tranId);
+										var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow});
+										dialog.show();
+									}
+								}
+								Layout.margins:{
+									right: 10
+								}
+								palette {
+									button: "white"
+								}
+								contentItem: Text {
+									text: verifyButton.text
+									color: "black"
+									horizontalAlignment: Text.AlignHCenter
+									verticalAlignment: Text.AlignVCenter
+								}
 							}
 						}
 					}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 15c2d7a25..4d734a687 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -33,6 +33,10 @@ ApplicationWindow {
 			case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break;
 			case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break;
 		}
+
+		onRefreshProfile: {
+			deviceVerificationList.updateProfile(flow.userId);
+		}
 	}
 
 	Component {
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index b5134a3b1..7829c41d1 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -1,4 +1,5 @@
 #include "DeviceVerificationFlow.h"
+#include "Cache.h"
 #include "ChatPage.h"
 #include "Logging.h"
 
@@ -181,9 +182,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                   // uncomment this in future to be compatible with the
                                   // MSC2366 this->sendVerificationDone(); and remove the
                                   // below line
-                                  if (this->isMacVerified == true)
-                                          emit this->deviceVerified();
-                                  else
+                                  if (this->isMacVerified == true) {
+                                          this->acceptDevice();
+                                  } else
                                           this->isMacVerified = true;
                           } else {
                                   this->cancelVerification(
@@ -208,7 +209,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                         auto msg =
                           std::get>(message);
                         if (msg.content.transaction_id == this->transaction_id) {
-                                emit this->deviceVerified();
+                                this->acceptDevice();
                         }
                 });
         timeout->start(TIMEOUT);
@@ -259,36 +260,22 @@ DeviceVerificationFlow::setTransactionId(QString transaction_id_)
 void
 DeviceVerificationFlow::setUserId(QString userID)
 {
-        this->userId   = userID;
-        this->toClient = mtx::identifiers::parse(userID.toStdString());
-
-        mtx::responses::QueryKeys res;
-        mtx::requests::QueryKeys req;
-        req.device_keys[userID.toStdString()] = {};
-        http::client()->query_keys(
-          req,
-          [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
-                                                 mtx::http::RequestErr err) {
-                  if (err) {
-                          nhlog::net()->warn("failed to query device keys: {},{}",
-                                             err->matrix_error.errcode,
-                                             static_cast(err->status_code));
-                          return;
-                  }
-
-                  for (auto x : res.device_keys) {
-                          for (auto y : x.second) {
-                                  auto z = y.second;
-                                  if (z.user_id == user_id &&
-                                      z.device_id == this->deviceId.toStdString()) {
-                                          for (auto a : z.keys) {
-                                                  // TODO: Verify Signatures
-                                                  this->device_keys[a.first] = a.second;
-                                          }
-                                  }
-                          }
-                  }
-          });
+        this->userId    = userID;
+        this->toClient  = mtx::identifiers::parse(userID.toStdString());
+        auto user_cache = cache::getUserCache(userID.toStdString());
+
+        if (user_cache.has_value()) {
+                this->callback_fn(user_cache->keys, {}, userID.toStdString());
+        } else {
+                mtx::requests::QueryKeys req;
+                req.device_keys[userID.toStdString()] = {};
+                http::client()->query_keys(
+                  req,
+                  [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
+                                                         mtx::http::RequestErr err) {
+                          this->callback_fn(res, err, user_id);
+                  });
+        }
 }
 
 void
@@ -482,6 +469,16 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
                             nhlog::net()->warn("failed to cancel verification request: {} {}",
                                                err->matrix_error.error,
                                                static_cast(err->status_code));
+                    auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
+                    if (verified_cache.has_value()) {
+                            verified_cache->device_blocked.push_back(this->deviceId.toStdString());
+                            cache::setVerifiedCache(this->userId.toStdString(),
+                                                    verified_cache.value());
+                    } else {
+                            cache::setVerifiedCache(
+                              this->userId.toStdString(),
+                              DeviceVerifiedCache{{}, {this->deviceId.toStdString()}});
+                    }
                     this->deleteLater();
             });
 }
@@ -546,7 +543,7 @@ DeviceVerificationFlow::sendVerificationMac()
                                                static_cast(err->status_code));
 
                     if (this->isMacVerified == true)
-                            emit this->deviceVerified();
+                            this->acceptDevice();
                     else
                             this->isMacVerified = true;
             });
@@ -555,8 +552,69 @@ DeviceVerificationFlow::sendVerificationMac()
 void
 DeviceVerificationFlow::acceptDevice()
 {
+        auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
+        if (verified_cache.has_value()) {
+                verified_cache->device_verified.push_back(this->deviceId.toStdString());
+                for (auto it = verified_cache->device_blocked.begin();
+                     it != verified_cache->device_blocked.end();
+                     it++) {
+                        if (*it == this->deviceId.toStdString()) {
+                                verified_cache->device_blocked.erase(it);
+                        }
+                }
+                cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value());
+        } else {
+                cache::setVerifiedCache(this->userId.toStdString(),
+                                        DeviceVerifiedCache{{this->deviceId.toStdString()}, {}});
+        }
+
         emit deviceVerified();
+        emit refreshProfile();
         this->deleteLater();
+}
+//! callback function to keep track of devices
+void
+DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res,
+                                    mtx::http::RequestErr err,
+                                    std::string user_id)
+{
+        if (err) {
+                nhlog::net()->warn("failed to query device keys: {},{}",
+                                   err->matrix_error.errcode,
+                                   static_cast(err->status_code));
+                return;
+        }
+
+        if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) {
+                nhlog::net()->warn("no devices retrieved {}", user_id);
+                return;
+        }
 
-        // Yet to add send to_device message
+        for (auto x : res.device_keys) {
+                for (auto y : x.second) {
+                        auto z = y.second;
+                        if (z.user_id == user_id && z.device_id == this->deviceId.toStdString()) {
+                                for (auto a : z.keys) {
+                                        // TODO: Verify Signatures
+                                        this->device_keys[a.first] = a.second;
+                                }
+                        }
+                }
+        }
+}
+
+void
+DeviceVerificationFlow::unverify()
+{
+        auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
+        if (verified_cache.has_value()) {
+                auto it = std::remove(verified_cache->device_verified.begin(),
+                                      verified_cache->device_verified.end(),
+                                      this->deviceId.toStdString());
+                verified_cache->device_verified.erase(it);
+                cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value());
+        }
+
+        emit refreshProfile();
+        this->deleteLater();
 }
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 891c6aea9..edff7c8e4 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -51,6 +51,9 @@ class DeviceVerificationFlow : public QObject
         void setDeviceId(QString deviceID);
         void setMethod(Method method_);
         void setSender(bool sender_);
+        void callback_fn(const mtx::responses::QueryKeys &res,
+                         mtx::http::RequestErr err,
+                         std::string user_id);
 
         nlohmann::json canonical_json;
 
@@ -73,12 +76,15 @@ public slots:
         void sendVerificationMac();
         //! Completes the verification flow
         void acceptDevice();
+        //! unverifies a device
+        void unverify();
 
 signals:
         void verificationRequestAccepted(Method method);
         void deviceVerified();
         void timedout();
         void verificationCanceled();
+        void refreshProfile();
 
 private:
         QString userId;
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 92c540fd6..cc3df662b 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -29,6 +29,8 @@ class DeviceVerificationList : public QObject
         Q_INVOKABLE void add(QString tran_id);
         Q_INVOKABLE void remove(QString tran_id);
         Q_INVOKABLE bool exist(QString tran_id);
+signals:
+        void updateProfile(QString userId);
 
 private:
         QVector deviceVerificationList;
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index fde0044bc..b4938e8d9 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -1,6 +1,7 @@
 #include "UserProfile.h"
 #include "Cache.h"
 #include "ChatPage.h"
+#include "DeviceVerificationFlow.h"
 #include "Logging.h"
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
@@ -122,10 +123,10 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
                    verified});
         }
 
-        // std::sort(
-        //   deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) {
-        //           return a.device_id > b.device_id;
-        //   });
+        std::sort(
+          deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) {
+                  return a.device_id > b.device_id;
+          });
 
         this->deviceList_.queueReset(std::move(deviceInfo));
 }
@@ -176,4 +177,4 @@ UserProfile::startChat()
         if (utils::localUser() != this->userid_)
                 req.invite = {this->userid_.toStdString()};
         emit ChatPage::instance()->createRoom(req);
-}
+}
\ No newline at end of file
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index 38002fffc..99c6a7553 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -19,6 +19,8 @@ enum Status
 Q_ENUM_NS(Status)
 }
 
+class DeviceVerificationFlow;
+
 class DeviceInfo
 {
 public:
@@ -88,7 +90,7 @@ class UserProfile : public QObject
         QString displayName();
         QString avatarUrl();
 
-        void fetchDeviceList(const QString &userID);
+        Q_INVOKABLE void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void banUser();
         // Q_INVOKABLE void ignoreUser();
         Q_INVOKABLE void kickUser();

From eb803606e3e60fc9bcd8417a6dab31d175579ce6 Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Mon, 6 Jul 2020 21:32:21 +0530
Subject: [PATCH 27/28] Updating keys of outdated encrypted users

---
 src/Cache.cpp                  | 56 ++++++++++++++++++++++++++++------
 src/Cache.h                    |  3 ++
 src/CacheCryptoStructs.h       | 29 +++++++++++++++---
 src/Cache_p.h                  |  5 +--
 src/DeviceVerificationFlow.cpp |  8 ++---
 src/ui/UserProfile.cpp         | 17 +++++------
 src/ui/UserProfile.h           |  3 +-
 7 files changed, 89 insertions(+), 32 deletions(-)

diff --git a/src/Cache.cpp b/src/Cache.cpp
index 5761cfe12..230105277 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -31,8 +31,10 @@
 
 #include "Cache.h"
 #include "Cache_p.h"
+#include "ChatPage.h"
 #include "EventAccessors.h"
 #include "Logging.h"
+#include "MatrixClient.h"
 #include "Utils.h"
 
 //! Should be changed when a breaking change occurs in the cache format.
@@ -954,6 +956,8 @@ Cache::saveState(const mtx::responses::Sync &res)
 
         savePresence(txn, res.presence);
 
+        updateUserCache(res.device_lists);
+
         removeLeftRooms(txn, res.rooms.leave);
 
         txn.commit();
@@ -2318,17 +2322,13 @@ Cache::statusMessage(const std::string &user_id)
 void
 to_json(json &j, const UserCache &info)
 {
-        j["is_user_verified"] = info.is_user_verified;
-        j["cross_verified"]   = info.cross_verified;
-        j["keys"]             = info.keys;
+        j["keys"] = info.keys;
 }
 
 void
 from_json(const json &j, UserCache &info)
 {
-        info.is_user_verified = j.at("is_user_verified");
-        info.cross_verified   = j.at("cross_verified").get>();
-        info.keys             = j.at("keys").get();
+        info.keys = j.at("keys").get();
 }
 
 std::optional
@@ -2365,6 +2365,32 @@ Cache::setUserCache(const std::string &user_id, const UserCache &body)
         return res;
 }
 
+void
+Cache::updateUserCache(const mtx::responses::DeviceLists body)
+{
+        for (auto user_id : body.changed) {
+                mtx::requests::QueryKeys req;
+                req.device_keys[user_id] = {};
+
+                http::client()->query_keys(
+                  req,
+                  [user_id, this](const mtx::responses::QueryKeys res, mtx::http::RequestErr err) {
+                          if (err) {
+                                  nhlog::net()->warn("failed to query device keys: {},{}",
+                                                     err->matrix_error.errcode,
+                                                     static_cast(err->status_code));
+                                  return;
+                          }
+
+                          setUserCache(user_id, UserCache{std::move(res)});
+                  });
+        }
+
+        for (std::string user_id : body.left) {
+                deleteUserCache(user_id);
+        }
+}
+
 int
 Cache::deleteUserCache(const std::string &user_id)
 {
@@ -2380,15 +2406,19 @@ Cache::deleteUserCache(const std::string &user_id)
 void
 to_json(json &j, const DeviceVerifiedCache &info)
 {
-        j["device_verified"] = info.device_verified;
-        j["device_blocked"]  = info.device_blocked;
+        j["is_user_verified"] = info.is_user_verified;
+        j["cross_verified"]   = info.cross_verified;
+        j["device_verified"]  = info.device_verified;
+        j["device_blocked"]   = info.device_blocked;
 }
 
 void
 from_json(const json &j, DeviceVerifiedCache &info)
 {
-        info.device_verified = j.at("device_verified").get>();
-        info.device_blocked  = j.at("device_blocked").get>();
+        info.is_user_verified = j.at("is_user_verified");
+        info.cross_verified   = j.at("cross_verified").get>();
+        info.device_verified  = j.at("device_verified").get>();
+        info.device_blocked   = j.at("device_blocked").get>();
 }
 
 std::optional
@@ -2611,6 +2641,12 @@ getUserCache(const std::string &user_id)
         return instance_->getUserCache(user_id);
 }
 
+void
+updateUserCache(const mtx::responses::DeviceLists body)
+{
+        instance_->updateUserCache(body);
+}
+
 int
 setUserCache(const std::string &user_id, const UserCache &body)
 {
diff --git a/src/Cache.h b/src/Cache.h
index 0c955c926..82d909ae7 100644
--- a/src/Cache.h
+++ b/src/Cache.h
@@ -64,6 +64,9 @@ statusMessage(const std::string &user_id);
 std::optional
 getUserCache(const std::string &user_id);
 
+void
+updateUserCache(const mtx::responses::DeviceLists body);
+
 int
 setUserCache(const std::string &user_id, const UserCache &body);
 
diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index 241cac764..ba746f590 100644
--- a/src/CacheCryptoStructs.h
+++ b/src/CacheCryptoStructs.h
@@ -66,14 +66,16 @@ struct OlmSessionStorage
         std::mutex group_inbound_mtx;
 };
 
+// this will store the keys of the user with whom a encrypted room is shared with
 struct UserCache
 {
-        //! this stores if the user is verified (with cross-signing)
-        bool is_user_verified = false;
-        //! list of verified device_ids with cross-signing
-        std::vector cross_verified;
         //! map of public key key_ids and their public_key
         mtx::responses::QueryKeys keys;
+
+        UserCache(mtx::responses::QueryKeys res)
+          : keys(res)
+        {}
+        UserCache() {}
 };
 
 void
@@ -81,11 +83,30 @@ to_json(nlohmann::json &j, const UserCache &info);
 void
 from_json(const nlohmann::json &j, UserCache &info);
 
+// the reason these are stored in a seperate cache rather than storing it in the user cache is
+// UserCache stores only keys of users with which encrypted room is shared
 struct DeviceVerifiedCache
 {
         //! list of verified device_ids with device-verification
         std::vector device_verified;
+        //! list of verified device_ids with cross-signing
+        std::vector cross_verified;
+        //! list of devices the user blocks
         std::vector device_blocked;
+        //! this stores if the user is verified (with cross-signing)
+        bool is_user_verified = false;
+
+        DeviceVerifiedCache(std::vector device_verified_,
+                            std::vector cross_verified_,
+                            std::vector device_blocked_,
+                            bool is_user_verified_ = false)
+          : device_verified(device_verified_)
+          , cross_verified(cross_verified_)
+          , device_blocked(device_blocked_)
+          , is_user_verified(is_user_verified_)
+        {}
+
+        DeviceVerifiedCache() {}
 };
 
 void
diff --git a/src/Cache_p.h b/src/Cache_p.h
index 4af332200..98efa74c5 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -58,6 +58,7 @@ class Cache : public QObject
 
         // user cache stores user keys
         std::optional getUserCache(const std::string &user_id);
+        void updateUserCache(const mtx::responses::DeviceLists body);
         int setUserCache(const std::string &user_id, const UserCache &body);
         int deleteUserCache(const std::string &user_id);
 
@@ -454,12 +455,12 @@ class Cache : public QObject
 
         lmdb::dbi getUserCacheDb(lmdb::txn &txn)
         {
-                return lmdb::dbi::open(txn, std::string("user_cache").c_str(), MDB_CREATE);
+                return lmdb::dbi::open(txn, "user_cache", MDB_CREATE);
         }
 
         lmdb::dbi getDeviceVerifiedDb(lmdb::txn &txn)
         {
-                return lmdb::dbi::open(txn, std::string("verified").c_str(), MDB_CREATE);
+                return lmdb::dbi::open(txn, "verified", MDB_CREATE);
         }
 
         //! Retrieves or creates the database that stores the open OLM sessions between our device
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 7829c41d1..0122e691d 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -477,7 +477,7 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
                     } else {
                             cache::setVerifiedCache(
                               this->userId.toStdString(),
-                              DeviceVerifiedCache{{}, {this->deviceId.toStdString()}});
+                              DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}});
                     }
                     this->deleteLater();
             });
@@ -564,8 +564,9 @@ DeviceVerificationFlow::acceptDevice()
                 }
                 cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value());
         } else {
-                cache::setVerifiedCache(this->userId.toStdString(),
-                                        DeviceVerifiedCache{{this->deviceId.toStdString()}, {}});
+                cache::setVerifiedCache(
+                  this->userId.toStdString(),
+                  DeviceVerifiedCache{{this->deviceId.toStdString()}, {}, {}});
         }
 
         emit deviceVerified();
@@ -616,5 +617,4 @@ DeviceVerificationFlow::unverify()
         }
 
         emit refreshProfile();
-        this->deleteLater();
 }
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index b4938e8d9..6ae04d0b5 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -77,8 +77,7 @@ UserProfile::avatarUrl()
 void
 UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,
-                         std::string user_id,
-                         std::optional> cross_verified)
+                         std::string user_id)
 {
         if (err) {
                 nhlog::net()->warn("failed to query device keys: {},{}",
@@ -101,16 +100,15 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
 
                 // TODO: Verify signatures and ignore those that don't pass.
                 verification::Status verified = verification::Status::UNVERIFIED;
-                if (cross_verified.has_value()) {
-                        if (std::find(cross_verified->begin(), cross_verified->end(), d.first) !=
-                            cross_verified->end())
+                if (device_verified.has_value()) {
+                        if (std::find(device_verified->cross_verified.begin(),
+                                      device_verified->cross_verified.end(),
+                                      d.first) != device_verified->cross_verified.end())
                                 verified = verification::Status::VERIFIED;
-                } else if (device_verified.has_value()) {
                         if (std::find(device_verified->device_verified.begin(),
                                       device_verified->device_verified.end(),
                                       d.first) != device_verified->device_verified.end())
                                 verified = verification::Status::VERIFIED;
-                } else if (device_verified.has_value()) {
                         if (std::find(device_verified->device_blocked.begin(),
                                       device_verified->device_blocked.end(),
                                       d.first) != device_verified->device_blocked.end())
@@ -138,8 +136,7 @@ UserProfile::fetchDeviceList(const QString &userID)
         auto user_cache = cache::getUserCache(userID.toStdString());
 
         if (user_cache.has_value()) {
-                this->callback_fn(
-                  user_cache->keys, {}, userID.toStdString(), user_cache->cross_verified);
+                this->callback_fn(user_cache->keys, {}, userID.toStdString());
         } else {
                 mtx::requests::QueryKeys req;
                 req.device_keys[userID.toStdString()] = {};
@@ -147,7 +144,7 @@ UserProfile::fetchDeviceList(const QString &userID)
                   req,
                   [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
                                                          mtx::http::RequestErr err) {
-                          this->callback_fn(res, err, user_id, {});
+                          this->callback_fn(res, err, user_id);
                   });
         }
 }
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index 99c6a7553..4e0484000 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -103,6 +103,5 @@ class UserProfile : public QObject
 
         void callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,
-                         std::string user_id,
-                         std::optional> cross_verified);
+                         std::string user_id);
 };

From ce2acbd50754b55325334f2f397cf94fe552b3ee Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Sat, 18 Jul 2020 01:46:30 +0530
Subject: [PATCH 28/28] Adding Room Key Verification Stuff

---
 resources/qml/UserProfile.qml               |  22 +-
 resources/qml/delegates/MessageDelegate.qml |  54 ++
 src/Cache.cpp                               |   2 +-
 src/ChatPage.h                              |  18 +-
 src/DeviceVerificationFlow.cpp              | 665 +++++++++++---------
 src/DeviceVerificationFlow.h                |  17 +-
 src/EventAccessors.cpp                      |  11 +-
 src/Olm.cpp                                 |  39 +-
 src/timeline/TimelineModel.cpp              |  62 +-
 src/timeline/TimelineModel.h                |   8 +
 src/timeline/TimelineViewManager.cpp        |  76 +--
 src/timeline/TimelineViewManager.h          |   9 +-
 src/ui/UserProfile.cpp                      |  34 +
 src/ui/UserProfile.h                        |   4 +
 14 files changed, 661 insertions(+), 360 deletions(-)

diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index 5bdccb4d1..c7dbc9aa3 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -74,6 +74,26 @@ ApplicationWindow{
 				Layout.alignment: Qt.AlignHCenter
 			}
 
+			Button {
+				id: verifyUserButton
+				text: "Verify"
+				Layout.alignment: Qt.AlignHCenter
+				enabled: profile.isUserVerified?false:true
+				visible: profile.isUserVerified?false:true
+				palette {
+					button: "white"
+				}
+				contentItem: Text {
+					text: verifyUserButton.text
+					color: "black"
+					horizontalAlignment: Text.AlignHCenter
+					verticalAlignment: Text.AlignVCenter
+				}
+				onClicked: {
+					profile.verifyUser();
+				}
+			}
+
 			RowLayout {
 				Layout.alignment: Qt.AlignHCenter
 				ImageButton {
@@ -127,7 +147,7 @@ ApplicationWindow{
 			}
 
 			ScrollView {
-				implicitHeight: userProfileDialog.height/2 + 20
+				implicitHeight: userProfileDialog.height/2-13
 				implicitWidth: userProfileDialog.width-20
 				clip: true
 				Layout.alignment: Qt.AlignHCenter
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index 3ce752836..80253deff 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -121,6 +121,60 @@ Item {
 				text: TimelineManager.timeline.formatMemberEvent(model.data.id);
 			}
 		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationRequest
+			NoticeMessage {
+				text: "KeyVerificationRequest";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationStart
+			NoticeMessage {
+				text: "KeyVerificationStart";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationReady
+			NoticeMessage {
+				text: "KeyVerificationReady";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationCancel
+			NoticeMessage {
+				text: "KeyVerificationCancel";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationKey
+			NoticeMessage {
+				text: "KeyVerificationKey";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationMac
+			NoticeMessage {
+				text: "KeyVerificationMac";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationDone
+			NoticeMessage {
+				text: "KeyVerificationDone";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationDone
+			NoticeMessage {
+				text: "KeyVerificationDone";
+			}
+		}
+		DelegateChoice {
+			roleValue: MtxEvent.KeyVerificationAccept
+			NoticeMessage {
+				text: "KeyVerificationAccept";
+			}
+		}
 		DelegateChoice {
 			Placeholder {}
 		}
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 230105277..44ca86d74 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -956,7 +956,7 @@ Cache::saveState(const mtx::responses::Sync &res)
 
         savePresence(txn, res.presence);
 
-        updateUserCache(res.device_lists);
+        // updateUserCache(res.device_lists);
 
         removeLeftRooms(txn, res.rooms.leave);
 
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 0b395033e..67acf9e08 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -166,16 +166,18 @@ public slots:
 
         //! Signals for device verificaiton
         void recievedDeviceVerificationAccept(
-          const mtx::events::collections::DeviceEvents &message);
+          const mtx::events::msg::KeyVerificationAccept &message);
         void recievedDeviceVerificationRequest(
-          const mtx::events::collections::DeviceEvents &message);
+          const mtx::events::msg::KeyVerificationRequest &message,
+          std::string sender);
         void recievedDeviceVerificationCancel(
-          const mtx::events::collections::DeviceEvents &message);
-        void recievedDeviceVerificationKey(const mtx::events::collections::DeviceEvents &message);
-        void recievedDeviceVerificationMac(const mtx::events::collections::DeviceEvents &message);
-        void recievedDeviceVerificationStart(const mtx::events::collections::DeviceEvents &message);
-        void recievedDeviceVerificationReady(const mtx::events::collections::DeviceEvents &message);
-        void recievedDeviceVerificationDone(const mtx::events::collections::DeviceEvents &message);
+          const mtx::events::msg::KeyVerificationCancel &message);
+        void recievedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message);
+        void recievedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message);
+        void recievedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message,
+                                             std::string sender);
+        void recievedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message);
+        void recievedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message);
 
 private slots:
         void showUnreadMessageNotification(int count);
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 0122e691d..69de4937b 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -6,11 +6,13 @@
 #include 
 #include 
 
+#include 
+
 static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes
 
 namespace msgs = mtx::events::msg;
 
-DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
+DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Type)
 {
         timeout = new QTimer(this);
         timeout->setSingleShot(true);
@@ -26,192 +28,218 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
           ChatPage::instance(),
           &ChatPage::recievedDeviceVerificationStart,
           this,
-          [this](const mtx::events::collections::DeviceEvents &message) {
-                  auto msg =
-                    std::get>(message);
-                  if (msg.content.transaction_id == this->transaction_id) {
-                          if ((std::find(msg.content.key_agreement_protocols.begin(),
-                                         msg.content.key_agreement_protocols.end(),
-                                         "curve25519-hkdf-sha256") !=
-                               msg.content.key_agreement_protocols.end()) &&
-                              (std::find(msg.content.hashes.begin(),
-                                         msg.content.hashes.end(),
-                                         "sha256") != msg.content.hashes.end()) &&
-                              (std::find(msg.content.message_authentication_codes.begin(),
-                                         msg.content.message_authentication_codes.end(),
-                                         "hmac-sha256") !=
-                               msg.content.message_authentication_codes.end())) {
-                                  if (std::find(msg.content.short_authentication_string.begin(),
-                                                msg.content.short_authentication_string.end(),
-                                                mtx::events::msg::SASMethods::Decimal) !=
-                                      msg.content.short_authentication_string.end()) {
-                                          this->method = DeviceVerificationFlow::Method::Emoji;
-                                  } else if (std::find(
-                                               msg.content.short_authentication_string.begin(),
-                                               msg.content.short_authentication_string.end(),
-                                               mtx::events::msg::SASMethods::Emoji) !=
-                                             msg.content.short_authentication_string.end()) {
-                                          this->method = DeviceVerificationFlow::Method::Decimal;
-                                  } else {
-                                          this->cancelVerification(
-                                            DeviceVerificationFlow::Error::UnknownMethod);
-                                          return;
-                                  }
-                                  this->acceptVerificationRequest();
-                                  this->canonical_json = nlohmann::json(msg.content);
-                          } else {
-                                  this->cancelVerification(
-                                    DeviceVerificationFlow::Error::UnknownMethod);
-                          }
+          [this](const mtx::events::msg::KeyVerificationStart &msg, std::string) {
+                  if (msg.transaction_id.has_value()) {
+                          if (msg.transaction_id.value() != this->transaction_id)
+                                  return;
+                  } else if (msg.relates_to.has_value()) {
+                          if (msg.relates_to.value().in_reply_to.event_id !=
+                              this->relation.in_reply_to.event_id)
+                                  return;
                   }
-          });
-        connect(
-          ChatPage::instance(),
-          &ChatPage::recievedDeviceVerificationAccept,
-          this,
-          [this](const mtx::events::collections::DeviceEvents &message) {
-                  auto msg =
-                    std::get>(message);
-                  if (msg.content.transaction_id == this->transaction_id) {
-                          if ((msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") &&
-                              (msg.content.hash == "sha256") &&
-                              (msg.content.message_authentication_code == "hkdf-hmac-sha256")) {
-                                  this->commitment = msg.content.commitment;
-                                  if (std::find(msg.content.short_authentication_string.begin(),
-                                                msg.content.short_authentication_string.end(),
-                                                mtx::events::msg::SASMethods::Emoji) !=
-                                      msg.content.short_authentication_string.end()) {
-                                          this->method = DeviceVerificationFlow::Method::Emoji;
-                                  } else {
-                                          this->method = DeviceVerificationFlow::Method::Decimal;
-                                  }
-                                  this->mac_method = msg.content.message_authentication_code;
-                                  this->sendVerificationKey();
+                  if ((std::find(msg.key_agreement_protocols.begin(),
+                                 msg.key_agreement_protocols.end(),
+                                 "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) &&
+                      (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") !=
+                       msg.hashes.end()) &&
+                      (std::find(msg.message_authentication_codes.begin(),
+                                 msg.message_authentication_codes.end(),
+                                 "hmac-sha256") != msg.message_authentication_codes.end())) {
+                          if (std::find(msg.short_authentication_string.begin(),
+                                        msg.short_authentication_string.end(),
+                                        mtx::events::msg::SASMethods::Decimal) !=
+                              msg.short_authentication_string.end()) {
+                                  this->method = DeviceVerificationFlow::Method::Emoji;
+                          } else if (std::find(msg.short_authentication_string.begin(),
+                                               msg.short_authentication_string.end(),
+                                               mtx::events::msg::SASMethods::Emoji) !=
+                                     msg.short_authentication_string.end()) {
+                                  this->method = DeviceVerificationFlow::Method::Decimal;
                           } else {
                                   this->cancelVerification(
                                     DeviceVerificationFlow::Error::UnknownMethod);
+                                  return;
                           }
+                          this->acceptVerificationRequest();
+                          this->canonical_json = nlohmann::json(msg);
+                  } else {
+                          this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod);
                   }
           });
+
+        connect(ChatPage::instance(),
+                &ChatPage::recievedDeviceVerificationAccept,
+                this,
+                [this](const mtx::events::msg::KeyVerificationAccept &msg) {
+                        if (msg.transaction_id.has_value()) {
+                                if (msg.transaction_id.value() != this->transaction_id)
+                                        return;
+                        } else if (msg.relates_to.has_value()) {
+                                if (msg.relates_to.value().in_reply_to.event_id !=
+                                    this->relation.in_reply_to.event_id)
+                                        return;
+                        }
+                        if ((msg.key_agreement_protocol == "curve25519-hkdf-sha256") &&
+                            (msg.hash == "sha256") &&
+                            (msg.message_authentication_code == "hkdf-hmac-sha256")) {
+                                this->commitment = msg.commitment;
+                                if (std::find(msg.short_authentication_string.begin(),
+                                              msg.short_authentication_string.end(),
+                                              mtx::events::msg::SASMethods::Emoji) !=
+                                    msg.short_authentication_string.end()) {
+                                        this->method = DeviceVerificationFlow::Method::Emoji;
+                                } else {
+                                        this->method = DeviceVerificationFlow::Method::Decimal;
+                                }
+                                this->mac_method = msg.message_authentication_code;
+                                this->sendVerificationKey();
+                        } else {
+                                this->cancelVerification(
+                                  DeviceVerificationFlow::Error::UnknownMethod);
+                        }
+                });
+
         connect(ChatPage::instance(),
                 &ChatPage::recievedDeviceVerificationCancel,
                 this,
-                [this](const mtx::events::collections::DeviceEvents &message) {
-                        auto msg =
-                          std::get>(message);
-                        if (msg.content.transaction_id == this->transaction_id) {
-                                emit verificationCanceled();
+                [this](const mtx::events::msg::KeyVerificationCancel &msg) {
+                        if (msg.transaction_id.has_value()) {
+                                if (msg.transaction_id.value() != this->transaction_id)
+                                        return;
+                        } else if (msg.relates_to.has_value()) {
+                                if (msg.relates_to.value().in_reply_to.event_id !=
+                                    this->relation.in_reply_to.event_id)
+                                        return;
+                        }
+                        emit verificationCanceled();
+                });
+
+        connect(ChatPage::instance(),
+                &ChatPage::recievedDeviceVerificationKey,
+                this,
+                [this](const mtx::events::msg::KeyVerificationKey &msg) {
+                        if (msg.transaction_id.has_value()) {
+                                if (msg.transaction_id.value() != this->transaction_id)
+                                        return;
+                        } else if (msg.relates_to.has_value()) {
+                                if (msg.relates_to.value().in_reply_to.event_id !=
+                                    this->relation.in_reply_to.event_id)
+                                        return;
+                        }
+                        this->sas->set_their_key(msg.key);
+                        std::string info;
+                        if (this->sender == true) {
+                                info = "MATRIX_KEY_VERIFICATION_SAS|" +
+                                       http::client()->user_id().to_string() + "|" +
+                                       http::client()->device_id() + "|" + this->sas->public_key() +
+                                       "|" + this->toClient.to_string() + "|" +
+                                       this->deviceId.toStdString() + "|" + msg.key + "|" +
+                                       this->transaction_id;
+                        } else {
+                                info = "MATRIX_KEY_VERIFICATION_SAS|" + this->toClient.to_string() +
+                                       "|" + this->deviceId.toStdString() + "|" + msg.key + "|" +
+                                       http::client()->user_id().to_string() + "|" +
+                                       http::client()->device_id() + "|" + this->sas->public_key() +
+                                       "|" + this->transaction_id;
+                        }
+
+                        if (this->method == DeviceVerificationFlow::Method::Emoji) {
+                                this->sasList = this->sas->generate_bytes_emoji(info);
+                        } else if (this->method == DeviceVerificationFlow::Method::Decimal) {
+                                this->sasList = this->sas->generate_bytes_decimal(info);
+                        }
+                        if (this->sender == false) {
+                                emit this->verificationRequestAccepted(this->method);
+                                this->sendVerificationKey();
+                        } else {
+                                if (this->commitment ==
+                                    mtx::crypto::bin2base64_unpadded(
+                                      mtx::crypto::sha256(msg.key + this->canonical_json.dump()))) {
+                                        emit this->verificationRequestAccepted(this->method);
+                                } else {
+                                        this->cancelVerification(
+                                          DeviceVerificationFlow::Error::MismatchedCommitment);
+                                }
                         }
                 });
+
         connect(
           ChatPage::instance(),
-          &ChatPage::recievedDeviceVerificationKey,
+          &ChatPage::recievedDeviceVerificationMac,
           this,
-          [this](const mtx::events::collections::DeviceEvents &message) {
-                  auto msg = std::get>(message);
-                  if (msg.content.transaction_id == this->transaction_id) {
-                          this->sas->set_their_key(msg.content.key);
-                          std::string info;
-                          if (this->sender == true) {
-                                  info = "MATRIX_KEY_VERIFICATION_SAS|" +
-                                         http::client()->user_id().to_string() + "|" +
-                                         http::client()->device_id() + "|" +
-                                         this->sas->public_key() + "|" +
-                                         this->toClient.to_string() + "|" +
-                                         this->deviceId.toStdString() + "|" + msg.content.key +
-                                         "|" + this->transaction_id;
-                          } else {
-                                  info = "MATRIX_KEY_VERIFICATION_SAS|" +
-                                         this->toClient.to_string() + "|" +
-                                         this->deviceId.toStdString() + "|" + msg.content.key +
-                                         "|" + http::client()->user_id().to_string() + "|" +
-                                         http::client()->device_id() + "|" +
-                                         this->sas->public_key() + "|" + this->transaction_id;
-                          }
-
-                          if (this->method == DeviceVerificationFlow::Method::Emoji) {
-                                  this->sasList = this->sas->generate_bytes_emoji(info);
-                          } else if (this->method == DeviceVerificationFlow::Method::Decimal) {
-                                  this->sasList = this->sas->generate_bytes_decimal(info);
-                          }
-                          if (this->sender == false) {
-                                  emit this->verificationRequestAccepted(this->method);
-                                  this->sendVerificationKey();
-                          } else {
-                                  if (this->commitment ==
-                                      mtx::crypto::bin2base64_unpadded(mtx::crypto::sha256(
-                                        msg.content.key + this->canonical_json.dump()))) {
-                                          emit this->verificationRequestAccepted(this->method);
+          [this](const mtx::events::msg::KeyVerificationMac &msg) {
+                  if (msg.transaction_id.has_value()) {
+                          if (msg.transaction_id.value() != this->transaction_id)
+                                  return;
+                  } else if (msg.relates_to.has_value()) {
+                          if (msg.relates_to.value().in_reply_to.event_id !=
+                              this->relation.in_reply_to.event_id)
+                                  return;
+                  }
+                  std::string info = "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() +
+                                     this->deviceId.toStdString() +
+                                     http::client()->user_id().to_string() +
+                                     http::client()->device_id() + this->transaction_id;
+
+                  std::vector key_list;
+                  std::string key_string;
+                  for (auto mac : msg.mac) {
+                          key_string += mac.first + ",";
+                          if (device_keys[mac.first] != "") {
+                                  if (mac.second ==
+                                      this->sas->calculate_mac(this->device_keys[mac.first],
+                                                               info + mac.first)) {
                                   } else {
                                           this->cancelVerification(
-                                            DeviceVerificationFlow::Error::MismatchedCommitment);
+                                            DeviceVerificationFlow::Error::KeyMismatch);
+                                          return;
                                   }
                           }
                   }
-          });
-        connect(
-          ChatPage::instance(),
-          &ChatPage::recievedDeviceVerificationMac,
-          this,
-          [this](const mtx::events::collections::DeviceEvents &message) {
-                  auto msg = std::get>(message);
-                  if (msg.content.transaction_id == this->transaction_id) {
-                          std::string info =
-                            "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() +
-                            this->deviceId.toStdString() + http::client()->user_id().to_string() +
-                            http::client()->device_id() + this->transaction_id;
-
-                          std::vector key_list;
-                          std::string key_string;
-                          for (auto mac : msg.content.mac) {
-                                  key_string += mac.first + ",";
-                                  if (device_keys[mac.first] != "") {
-                                          if (mac.second ==
-                                              this->sas->calculate_mac(this->device_keys[mac.first],
-                                                                       info + mac.first)) {
-                                          } else {
-                                                  this->cancelVerification(
-                                                    DeviceVerificationFlow::Error::KeyMismatch);
-                                                  return;
-                                          }
-                                  }
-                          }
-                          key_string = key_string.substr(0, key_string.length() - 1);
-                          if (msg.content.keys ==
-                              this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
-                                  // uncomment this in future to be compatible with the
-                                  // MSC2366 this->sendVerificationDone(); and remove the
-                                  // below line
-                                  if (this->isMacVerified == true) {
-                                          this->acceptDevice();
-                                  } else
-                                          this->isMacVerified = true;
-                          } else {
-                                  this->cancelVerification(
-                                    DeviceVerificationFlow::Error::KeyMismatch);
-                          }
+                  key_string = key_string.substr(0, key_string.length() - 1);
+                  if (msg.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
+                          // uncomment this in future to be compatible with the
+                          // MSC2366 this->sendVerificationDone(); and remove the
+                          // below line
+                          if (this->isMacVerified == true) {
+                                  this->acceptDevice();
+                          } else
+                                  this->isMacVerified = true;
+                  } else {
+                          this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch);
                   }
           });
+
         connect(ChatPage::instance(),
                 &ChatPage::recievedDeviceVerificationReady,
                 this,
-                [this](const mtx::events::collections::DeviceEvents &message) {
-                        auto msg =
-                          std::get>(message);
-                        if (msg.content.transaction_id == this->transaction_id) {
-                                this->startVerificationRequest();
+                [this](const mtx::events::msg::KeyVerificationReady &msg) {
+                        if (msg.transaction_id.has_value()) {
+                                if (msg.transaction_id.value() != this->transaction_id)
+                                        return;
+                        } else if (msg.relates_to.has_value()) {
+                                if (msg.relates_to.value().in_reply_to.event_id !=
+                                    this->relation.in_reply_to.event_id)
+                                        return;
                         }
+                        this->startVerificationRequest();
                 });
+
         connect(ChatPage::instance(),
                 &ChatPage::recievedDeviceVerificationDone,
                 this,
-                [this](const mtx::events::collections::DeviceEvents &message) {
-                        auto msg =
-                          std::get>(message);
-                        if (msg.content.transaction_id == this->transaction_id) {
-                                this->acceptDevice();
+                [this](const mtx::events::msg::KeyVerificationDone &msg) {
+                        if (msg.transaction_id.has_value()) {
+                                if (msg.transaction_id.value() != this->transaction_id)
+                                        return;
+                        } else if (msg.relates_to.has_value()) {
+                                if (msg.relates_to.value().in_reply_to.event_id !=
+                                    this->relation.in_reply_to.event_id)
+                                        return;
                         }
+                        this->acceptDevice();
                 });
+
         timeout->start(TIMEOUT);
 }
 
@@ -294,18 +322,18 @@ void
 DeviceVerificationFlow::setSender(bool sender_)
 {
         this->sender = sender_;
-        if (this->sender == true)
+        if (this->sender == true && this->type == DeviceVerificationFlow::Type::ToDevice)
                 this->transaction_id = http::client()->generate_txn_id();
+        else if (this->sender == true && this->type == DeviceVerificationFlow::Type::RoomMsg)
+                this->relation.in_reply_to.event_id = http::client()->generate_txn_id();
 }
 
 //! accepts a verification
 void
 DeviceVerificationFlow::acceptVerificationRequest()
 {
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationAccept req;
 
-        req.transaction_id              = this->transaction_id;
         req.method                      = mtx::events::msg::VerificationMethods::SASv1;
         req.key_agreement_protocol      = "curve25519-hkdf-sha256";
         req.hash                        = "sha256";
@@ -317,126 +345,152 @@ DeviceVerificationFlow::acceptVerificationRequest()
         req.commitment = mtx::crypto::bin2base64_unpadded(
           mtx::crypto::sha256(this->sas->public_key() + this->canonical_json.dump()));
 
-        body[this->toClient][this->deviceId.toStdString()] = req;
-
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to accept verification request: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-            });
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                mtx::requests::ToDeviceMessages body;
+                req.transaction_id = this->transaction_id;
+
+                body[this->toClient][this->deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn(
+                                      "failed to accept verification request: {} {}",
+                                      err->matrix_error.error,
+                                      static_cast(err->status_code));
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                req.relates_to = this->relation;
+        }
 }
 //! responds verification request
 void
 DeviceVerificationFlow::sendVerificationReady()
 {
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationReady req;
 
-        req.from_device    = http::client()->device_id();
-        req.transaction_id = this->transaction_id;
-        req.methods        = {mtx::events::msg::VerificationMethods::SASv1};
-
-        body[this->toClient][this->deviceId.toStdString()] = req;
-
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to send verification ready: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-            });
+        req.from_device = http::client()->device_id();
+        req.methods     = {mtx::events::msg::VerificationMethods::SASv1};
+
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                req.transaction_id = this->transaction_id;
+                mtx::requests::ToDeviceMessages body;
+
+                body[this->toClient][this->deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn("failed to send verification ready: {} {}",
+                                                       err->matrix_error.error,
+                                                       static_cast(err->status_code));
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                req.relates_to = this->relation;
+        }
 }
 //! accepts a verification
 void
 DeviceVerificationFlow::sendVerificationDone()
 {
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationDone req;
 
-        req.transaction_id = this->transaction_id;
-
-        body[this->toClient][this->deviceId.toStdString()] = req;
-
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to send verification done: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-            });
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                mtx::requests::ToDeviceMessages body;
+                req.transaction_id = this->transaction_id;
+
+                body[this->toClient][this->deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn("failed to send verification done: {} {}",
+                                                       err->matrix_error.error,
+                                                       static_cast(err->status_code));
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                req.relates_to = this->relation;
+        }
 }
 //! starts the verification flow
 void
 DeviceVerificationFlow::startVerificationRequest()
 {
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationStart req;
 
         req.from_device                  = http::client()->device_id();
-        req.transaction_id               = this->transaction_id;
         req.method                       = mtx::events::msg::VerificationMethods::SASv1;
         req.key_agreement_protocols      = {"curve25519-hkdf-sha256"};
         req.hashes                       = {"sha256"};
-        req.message_authentication_codes = {"hkdf-hmac-sha256", "hmac-sha256"};
+        req.message_authentication_codes = {"hkdf-hmac-sha256"};
         req.short_authentication_string  = {mtx::events::msg::SASMethods::Decimal,
                                            mtx::events::msg::SASMethods::Emoji};
 
-        body[this->toClient][this->deviceId.toStdString()] = req;
-        this->canonical_json                               = nlohmann::json(req);
-
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [body](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to start verification request: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-            });
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                mtx::requests::ToDeviceMessages body;
+                req.transaction_id                                 = this->transaction_id;
+                this->canonical_json                               = nlohmann::json(req);
+                body[this->toClient][this->deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [body](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn(
+                                      "failed to start verification request: {} {}",
+                                      err->matrix_error.error,
+                                      static_cast(err->status_code));
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                req.relates_to = this->relation;
+        }
 }
 //! sends a verification request
 void
 DeviceVerificationFlow::sendVerificationRequest()
 {
-        QDateTime CurrentTime = QDateTime::currentDateTimeUtc();
-
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationRequest req;
 
-        req.from_device    = http::client()->device_id();
-        req.transaction_id = this->transaction_id;
+        req.from_device = http::client()->device_id();
         req.methods.resize(1);
         req.methods[0] = mtx::events::msg::VerificationMethods::SASv1;
-        req.timestamp  = (uint64_t)CurrentTime.toTime_t();
-
-        body[this->toClient][this->deviceId.toStdString()] = req;
-
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to send verification request: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-            });
+
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                QDateTime CurrentTime = QDateTime::currentDateTimeUtc();
+
+                req.transaction_id = this->transaction_id;
+                req.timestamp      = (uint64_t)CurrentTime.toTime_t();
+
+                mtx::requests::ToDeviceMessages body;
+
+                body[this->toClient][this->deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn("failed to send verification request: {} {}",
+                                                       err->matrix_error.error,
+                                                       static_cast(err->status_code));
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                std::cout << "lulz" << std::endl;
+        }
 }
 //! cancels a verification flow
 void
 DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_code)
 {
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationCancel req;
 
-        req.transaction_id = this->transaction_id;
         if (error_code == DeviceVerificationFlow::Error::UnknownMethod) {
                 req.code   = "m.unknown_method";
                 req.reason = "unknown method recieved";
@@ -457,65 +511,79 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
                 req.reason = "user cancelled the verification";
         }
 
-        body[this->toClient][deviceId.toStdString()] = req;
-
         emit this->verificationCanceled();
 
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [this](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to cancel verification request: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-                    auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
-                    if (verified_cache.has_value()) {
-                            verified_cache->device_blocked.push_back(this->deviceId.toStdString());
-                            cache::setVerifiedCache(this->userId.toStdString(),
-                                                    verified_cache.value());
-                    } else {
-                            cache::setVerifiedCache(
-                              this->userId.toStdString(),
-                              DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}});
-                    }
-                    this->deleteLater();
-            });
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                req.transaction_id = this->transaction_id;
+                mtx::requests::ToDeviceMessages body;
+
+                body[this->toClient][deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [this](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn(
+                                      "failed to cancel verification request: {} {}",
+                                      err->matrix_error.error,
+                                      static_cast(err->status_code));
+
+                            this->deleteLater();
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                req.relates_to = this->relation;
+        }
+
+        // TODO : Handle Blocking user better
+        // auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
+        //     if (verified_cache.has_value()) {
+        //             verified_cache->device_blocked.push_back(this->deviceId.toStdString());
+        //             cache::setVerifiedCache(this->userId.toStdString(),
+        //                                     verified_cache.value());
+        //     } else {
+        //             cache::setVerifiedCache(
+        //               this->userId.toStdString(),
+        //               DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}});
+        //     }
 }
 //! sends the verification key
 void
 DeviceVerificationFlow::sendVerificationKey()
 {
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationKey req;
 
-        req.key            = this->sas->public_key();
-        req.transaction_id = this->transaction_id;
-
-        body[this->toClient][deviceId.toStdString()] = req;
-
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to send verification key: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-            });
+        req.key = this->sas->public_key();
+
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                mtx::requests::ToDeviceMessages body;
+                req.transaction_id = this->transaction_id;
+
+                body[this->toClient][deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn("failed to send verification key: {} {}",
+                                                       err->matrix_error.error,
+                                                       static_cast(err->status_code));
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                req.relates_to = this->relation;
+        }
 }
 //! sends the mac of the keys
 void
 DeviceVerificationFlow::sendVerificationMac()
 {
-        mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationMac req;
 
         std::string info = "MATRIX_KEY_VERIFICATION_MAC" + http::client()->user_id().to_string() +
                            http::client()->device_id() + this->toClient.to_string() +
                            this->deviceId.toStdString() + this->transaction_id;
 
-        req.transaction_id = this->transaction_id;
         //! this vector stores the type of the key and the key
         std::vector> key_list;
         key_list.push_back(make_pair("ed25519", olm::client()->identity_keys().ed25519));
@@ -531,22 +599,28 @@ DeviceVerificationFlow::sendVerificationMac()
         req.keys =
           this->sas->calculate_mac(req.keys.substr(0, req.keys.size() - 1), info + "KEY_IDS");
 
-        body[this->toClient][deviceId.toStdString()] = req;
-
-        http::client()
-          ->send_to_device(
-            this->transaction_id, body, [this](mtx::http::RequestErr err) {
-                    if (err)
-                            nhlog::net()->warn("failed to send verification MAC: {} {}",
-                                               err->matrix_error.error,
-                                               static_cast(err->status_code));
-
-                    if (this->isMacVerified == true)
-                            this->acceptDevice();
-                    else
-                            this->isMacVerified = true;
-            });
+        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                mtx::requests::ToDeviceMessages body;
+                req.transaction_id                           = this->transaction_id;
+                body[this->toClient][deviceId.toStdString()] = req;
+
+                http::client()
+                  ->send_to_device(
+                    this->transaction_id, body, [this](mtx::http::RequestErr err) {
+                            if (err)
+                                    nhlog::net()->warn("failed to send verification MAC: {} {}",
+                                                       err->matrix_error.error,
+                                                       static_cast(err->status_code));
+
+                            if (this->isMacVerified == true)
+                                    this->acceptDevice();
+                            else
+                                    this->isMacVerified = true;
+                    });
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+                req.relates_to = this->relation;
+        }
 }
 //! Completes the verification flow
 void
@@ -555,14 +629,11 @@ DeviceVerificationFlow::acceptDevice()
         auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
         if (verified_cache.has_value()) {
                 verified_cache->device_verified.push_back(this->deviceId.toStdString());
-                for (auto it = verified_cache->device_blocked.begin();
-                     it != verified_cache->device_blocked.end();
-                     it++) {
-                        if (*it == this->deviceId.toStdString()) {
-                                verified_cache->device_blocked.erase(it);
-                        }
-                }
-                cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value());
+                verified_cache->device_blocked.erase(
+                  std::remove(verified_cache->device_blocked.begin(),
+                              verified_cache->device_blocked.end(),
+                              this->deviceId.toStdString()),
+                  verified_cache->device_blocked.end());
         } else {
                 cache::setVerifiedCache(
                   this->userId.toStdString(),
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index edff7c8e4..3f999e801 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -2,8 +2,8 @@
 
 #include "Olm.h"
 
+#include "MatrixClient.h"
 #include "mtx/responses/crypto.hpp"
-#include 
 #include 
 
 class QTimer;
@@ -19,15 +19,22 @@ class DeviceVerificationFlow : public QObject
         Q_PROPERTY(QString userId READ getUserId WRITE setUserId)
         Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId)
         Q_PROPERTY(Method method READ getMethod WRITE setMethod)
-        Q_PROPERTY(std::vector sasList READ getSasList)
+        Q_PROPERTY(std::vector sasList READ getSasList CONSTANT)
 
 public:
+        enum Type
+        {
+                ToDevice,
+                RoomMsg
+        };
+
         enum Method
         {
                 Decimal,
                 Emoji
         };
         Q_ENUM(Method)
+
         enum Error
         {
                 UnknownMethod,
@@ -39,7 +46,9 @@ class DeviceVerificationFlow : public QObject
         };
         Q_ENUM(Error)
 
-        DeviceVerificationFlow(QObject *parent = nullptr);
+        DeviceVerificationFlow(
+          QObject *parent              = nullptr,
+          DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice);
         QString getTransactionId();
         QString getUserId();
         QString getDeviceId();
@@ -90,6 +99,7 @@ public slots:
         QString userId;
         QString deviceId;
         Method method;
+        Type type;
         bool sender;
 
         QTimer *timeout = nullptr;
@@ -101,4 +111,5 @@ public slots:
         mtx::identifiers::User toClient;
         std::vector sasList;
         std::map device_keys;
+        mtx::common::ReplyRelatesTo relation;
 };
diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp
index a2d8adbb2..bde34cfd4 100644
--- a/src/EventAccessors.cpp
+++ b/src/EventAccessors.cpp
@@ -72,8 +72,15 @@ struct EventBody
         template
         std::string operator()(const mtx::events::Event &e)
         {
-                if constexpr (is_detected::value)
-                        return e.content.body;
+                if constexpr (is_detected::value) {
+                        if constexpr (std::is_same_v,
+                                                     std::remove_cv_t>)
+                                return e.content.body ? e.content.body.value() : "";
+                        else if constexpr (std::is_same_v<
+                                             std::string,
+                                             std::remove_cv_t>)
+                                return e.content.body;
+                }
                 return "";
         }
 };
diff --git a/src/Olm.cpp b/src/Olm.cpp
index 0364b959f..1854579df 100644
--- a/src/Olm.cpp
+++ b/src/Olm.cpp
@@ -5,10 +5,10 @@
 
 #include "Cache.h"
 #include "ChatPage.h"
+#include "DeviceVerificationFlow.h"
 #include "Logging.h"
 #include "MatrixClient.h"
 #include "Utils.h"
-#include 
 
 static const std::string STORAGE_SECRET_KEY("secret");
 constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2";
@@ -77,21 +77,42 @@ handle_to_device_messages(const std::vectorrecievedDeviceVerificationAccept(msg);
+                        auto message = std::get<
+                          mtx::events::DeviceEvent>(msg);
+                        ChatPage::instance()->recievedDeviceVerificationAccept(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) {
-                        ChatPage::instance()->recievedDeviceVerificationRequest(msg);
+                        auto message = std::get<
+                          mtx::events::DeviceEvent>(msg);
+                        ChatPage::instance()->recievedDeviceVerificationRequest(message.content,
+                                                                                message.sender);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) {
-                        ChatPage::instance()->recievedDeviceVerificationCancel(msg);
+                        auto message = std::get<
+                          mtx::events::DeviceEvent>(msg);
+                        ChatPage::instance()->recievedDeviceVerificationCancel(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) {
-                        ChatPage::instance()->recievedDeviceVerificationKey(msg);
+                        auto message =
+                          std::get>(
+                            msg);
+                        ChatPage::instance()->recievedDeviceVerificationKey(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) {
-                        ChatPage::instance()->recievedDeviceVerificationMac(msg);
+                        auto message =
+                          std::get>(
+                            msg);
+                        ChatPage::instance()->recievedDeviceVerificationMac(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) {
-                        ChatPage::instance()->recievedDeviceVerificationStart(msg);
+                        auto message = std::get<
+                          mtx::events::DeviceEvent>(msg);
+                        ChatPage::instance()->recievedDeviceVerificationStart(message.content,
+                                                                              message.sender);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) {
-                        ChatPage::instance()->recievedDeviceVerificationReady(msg);
+                        auto message = std::get<
+                          mtx::events::DeviceEvent>(msg);
+                        ChatPage::instance()->recievedDeviceVerificationReady(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) {
-                        ChatPage::instance()->recievedDeviceVerificationDone(msg);
+                        auto message =
+                          std::get>(
+                            msg);
+                        ChatPage::instance()->recievedDeviceVerificationDone(message.content);
                 } else {
                         nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2));
                 }
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 7d6e0e911..e756595b8 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -22,6 +22,8 @@
 #include "Utils.h"
 #include "dialogs/RawMessage.h"
 
+#include 
+
 Q_DECLARE_METATYPE(QModelIndex)
 
 namespace std {
@@ -116,7 +118,41 @@ struct RoomEventType
         {
                 return qml_mtx_events::EventType::VideoMessage;
         }
-
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationRequest;
+        }
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationStart;
+        }
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationMac;
+        }
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationAccept;
+        }
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationCancel;
+        }
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationKey;
+        }
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationDone;
+        }
         qml_mtx_events::EventType operator()(const mtx::events::Event &)
         {
                 return qml_mtx_events::EventType::Redacted;
@@ -650,6 +686,30 @@ TimelineModel::internalAddEvents(
                         continue;
                 }
 
+                if (std::get_if>(
+                      &e)) {
+                        std::cout << "got a request" << std::endl;
+                }
+
+                if (auto cancelVerification =
+                      std::get_if>(
+                        &e)) {
+                        std::cout<<"it is happening"<content.relates_to.has_value()) {
+                                QString event_id = QString::fromStdString(
+                                  cancelVerification->content.relates_to.value()
+                                    .in_reply_to.event_id);
+                                auto request =
+                                  std::find(eventOrder.begin(), eventOrder.end(), event_id);
+                                if (request != eventOrder.end()) {
+                                        auto event = events.value(event_id);
+                                        auto e     = std::get_if>(&event);
+                                        std::cout<>(&e)) {
                         QString redacts = QString::fromStdString(redaction->redacts);
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index edf33c7d9..eaa6cd286 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -80,6 +80,14 @@ enum EventType
         VideoMessage,
         Redacted,
         UnknownMessage,
+        KeyVerificationRequest,
+        KeyVerificationStart,
+        KeyVerificationMac,
+        KeyVerificationAccept,
+        KeyVerificationCancel,
+        KeyVerificationKey,
+        KeyVerificationDone,
+        KeyVerificationReady
 };
 Q_ENUM_NS(EventType)
 
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index b7c734ee9..7f1d07d7d 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -101,7 +101,15 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
   , blurhashProvider(new BlurhashProvider())
   , settings(userSettings)
 {
-        qRegisterMetaType();
+        qRegisterMetaType();
+        qRegisterMetaType();
+        qRegisterMetaType();
+        qRegisterMetaType();
+        qRegisterMetaType();
+        qRegisterMetaType();
+        qRegisterMetaType();
+        qRegisterMetaType();
+
         qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
                                          "im.nheko",
                                          1,
@@ -177,21 +185,19 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
           dynamic_cast(parent),
           &ChatPage::recievedDeviceVerificationRequest,
           this,
-          [this](const mtx::events::collections::DeviceEvents &message) {
-                  auto msg =
-                    std::get>(message);
+          [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) {
                   auto flow = new DeviceVerificationFlow(this);
-                  if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) {
-                          if (std::find(msg.content.methods.begin(),
-                                        msg.content.methods.end(),
+                  if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) {
+                          if (std::find(msg.methods.begin(),
+                                        msg.methods.end(),
                                         mtx::events::msg::VerificationMethods::SASv1) !=
-                              msg.content.methods.end()) {
+                              msg.methods.end()) {
                                   //   flow->sendVerificationReady();
                                   emit newDeviceVerificationRequest(
                                     std::move(flow),
-                                    QString::fromStdString(msg.content.transaction_id),
-                                    QString::fromStdString(msg.sender),
-                                    QString::fromStdString(msg.content.from_device));
+                                    QString::fromStdString(msg.transaction_id.value()),
+                                    QString::fromStdString(sender),
+                                    QString::fromStdString(msg.from_device));
                           } else {
                                   flow->cancelVerification(
                                     DeviceVerificationFlow::Error::UnknownMethod);
@@ -202,33 +208,29 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
           dynamic_cast(parent),
           &ChatPage::recievedDeviceVerificationStart,
           this,
-          [this](const mtx::events::collections::DeviceEvents &message) {
-                  auto msg =
-                    std::get>(message);
+          [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) {
                   auto flow            = new DeviceVerificationFlow(this);
-                  flow->canonical_json = nlohmann::json(msg.content);
-                  if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) {
-                          if ((std::find(msg.content.key_agreement_protocols.begin(),
-                                         msg.content.key_agreement_protocols.end(),
+                  flow->canonical_json = nlohmann::json(msg);
+                  if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) {
+                          if ((std::find(msg.key_agreement_protocols.begin(),
+                                         msg.key_agreement_protocols.end(),
                                          "curve25519-hkdf-sha256") !=
-                               msg.content.key_agreement_protocols.end()) &&
-                              (std::find(msg.content.hashes.begin(),
-                                         msg.content.hashes.end(),
-                                         "sha256") != msg.content.hashes.end()) &&
-                              (std::find(msg.content.message_authentication_codes.begin(),
-                                         msg.content.message_authentication_codes.end(),
+                               msg.key_agreement_protocols.end()) &&
+                              (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") !=
+                               msg.hashes.end()) &&
+                              (std::find(msg.message_authentication_codes.begin(),
+                                         msg.message_authentication_codes.end(),
                                          "hmac-sha256") !=
-                               msg.content.message_authentication_codes.end())) {
-                                  if (std::find(msg.content.short_authentication_string.begin(),
-                                                msg.content.short_authentication_string.end(),
+                               msg.message_authentication_codes.end())) {
+                                  if (std::find(msg.short_authentication_string.begin(),
+                                                msg.short_authentication_string.end(),
                                                 mtx::events::msg::SASMethods::Emoji) !=
-                                      msg.content.short_authentication_string.end()) {
+                                      msg.short_authentication_string.end()) {
                                           flow->setMethod(DeviceVerificationFlow::Method::Emoji);
-                                  } else if (std::find(
-                                               msg.content.short_authentication_string.begin(),
-                                               msg.content.short_authentication_string.end(),
-                                               mtx::events::msg::SASMethods::Decimal) !=
-                                             msg.content.short_authentication_string.end()) {
+                                  } else if (std::find(msg.short_authentication_string.begin(),
+                                                       msg.short_authentication_string.end(),
+                                                       mtx::events::msg::SASMethods::Decimal) !=
+                                             msg.short_authentication_string.end()) {
                                           flow->setMethod(DeviceVerificationFlow::Method::Decimal);
                                   } else {
                                           flow->cancelVerification(
@@ -237,10 +239,10 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                                   }
                                   emit newDeviceVerificationRequest(
                                     std::move(flow),
-                                    QString::fromStdString(msg.content.transaction_id),
-                                    QString::fromStdString(msg.sender),
-                                    QString::fromStdString(msg.content.from_device));
-                                  flow->canonical_json = nlohmann::json(msg.content);
+                                    QString::fromStdString(msg.transaction_id.value()),
+                                    QString::fromStdString(sender),
+                                    QString::fromStdString(msg.from_device));
+                                  flow->canonical_json = nlohmann::json(msg);
                           } else {
                                   flow->cancelVerification(
                                     DeviceVerificationFlow::Error::UnknownMethod);
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index cc3df662b..d78d887fc 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -139,4 +139,11 @@ public slots:
 
         DeviceVerificationList *dvList;
 };
-Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationAccept)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationCancel)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationDone)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationKey)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationMac)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationReady)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationRequest)
+Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationStart)
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 6ae04d0b5..3499384cc 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -6,6 +6,8 @@
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
 
+#include  // only for debugging
+
 UserProfile::UserProfile(QString roomid, QString userid, QObject *parent)
   : QObject(parent)
   , roomid_(roomid)
@@ -74,6 +76,12 @@ UserProfile::avatarUrl()
         return cache::avatarUrl(roomid_, userid_);
 }
 
+bool
+UserProfile::getUserStatus()
+{
+        return isUserVerified;
+}
+
 void
 UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,
@@ -100,6 +108,7 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
 
                 // TODO: Verify signatures and ignore those that don't pass.
                 verification::Status verified = verification::Status::UNVERIFIED;
+                isUserVerified                = device_verified->is_user_verified;
                 if (device_verified.has_value()) {
                         if (std::find(device_verified->cross_verified.begin(),
                                       device_verified->cross_verified.end(),
@@ -174,4 +183,29 @@ UserProfile::startChat()
         if (utils::localUser() != this->userid_)
                 req.invite = {this->userid_.toStdString()};
         emit ChatPage::instance()->createRoom(req);
+}
+
+void
+UserProfile::verifyUser()
+{
+        std::cout << "Checking if to start to device verification or room message verification"
+                  << std::endl;
+        auto joined_rooms = cache::joinedRooms();
+        auto room_infos   = cache::getRoomInfo(joined_rooms);
+
+        for (std::string room_id : joined_rooms) {
+                if ((room_infos[QString::fromStdString(room_id)].member_count == 2) &&
+                    cache::isRoomEncrypted(room_id)) {
+                        auto room_members = cache::roomMembers(room_id);
+                        if (std::find(room_members.begin(),
+                                      room_members.end(),
+                                      (this->userid()).toStdString()) != room_members.end()) {
+                                std::cout << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id
+                                          << std::endl;
+                                return;
+                        }
+                }
+        }
+
+        std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl;
 }
\ No newline at end of file
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index 4e0484000..3f9cbe6f9 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -81,6 +81,7 @@ class UserProfile : public QObject
         Q_PROPERTY(QString userid READ userid CONSTANT)
         Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT)
         Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
+        Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT)
 public:
         UserProfile(QString roomid, QString userid, QObject *parent = 0);
 
@@ -89,17 +90,20 @@ class UserProfile : public QObject
         QString userid();
         QString displayName();
         QString avatarUrl();
+        bool getUserStatus();
 
         Q_INVOKABLE void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void banUser();
         // Q_INVOKABLE void ignoreUser();
         Q_INVOKABLE void kickUser();
         Q_INVOKABLE void startChat();
+        Q_INVOKABLE void verifyUser();
 
 private:
         QString roomid_, userid_;
         std::optional cross_verified;
         DeviceInfoModel deviceList_;
+        bool isUserVerified = false;
 
         void callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,