From 488cc5e73be3c0aba2caf4df6ae38225c6a3d641 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 9 Mar 2020 00:25:00 +0100 Subject: [PATCH] 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 00000000..c32e3414 --- /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 00000000..6682e7ec --- /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 00000000..060fbd49 --- /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"} +]