diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index f934e2f6..e687e170 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,8 +54,8 @@ Rectangle {
height: avatar.height / 6
width: height
- radius: settings.avatarCircles ? height / 2 : height / 4
- color: switch (timelineManager.userPresence(userid)) {
+ 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
diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index 1da223d5..bbbb80cf 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
@@ -11,10 +13,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 timelineManager.openLink(link)
diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml
index c1091756..9fc30f61 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 " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent)
- timelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key)
+ TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key)
}
@@ -46,7 +48,7 @@ Flow {
TextMetrics {
id: textMetrics
- font.family: settings.emojiFont
+ font.family: Settings.emojiFont
elide: Text.ElideRight
elideWidth: 150
text: modelData.key
@@ -55,8 +57,8 @@ Flow {
Text {
anchors.baseline: reactionCounter.baseline
id: reactionText
- text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…")
- font.family: settings.emojiFont
+ text: textMetrics.elidedText + (textMetrics.elidedText == model.key ? "" : "…")
+ 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 a38a4d34..2979908e 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -29,7 +29,7 @@ Item {
}
}
Rectangle {
- color: (settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent"
+ color: (Settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent"
anchors.fill: row
}
RowLayout {
@@ -48,8 +48,8 @@ Item {
// fancy reply, if this is a reply
Reply {
visible: model.replyTo
- modelData: chat.model.getDump(model.replyTo, model.id)
- userColor: timelineManager.userColor(modelData.userId, colors.window)
+ modelData: chat.model.getDump(model.replyTo,model.id)
+ userColor: TimelineManager.userColor(modelData.userId, colors.window)
}
// actual message content
@@ -84,7 +84,7 @@ Item {
width: 16
}
EmojiButton {
- visible: settings.buttonsInTimeline
+ visible: Settings.buttonsInTimeline
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
@@ -96,7 +96,7 @@ Item {
event_id: model.id
}
ImageButton {
- visible: settings.buttonsInTimeline
+ visible: Settings.buttonsInTimeline
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
@@ -112,7 +112,7 @@ Item {
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 dd9c4029..f2390b18 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 {
id: timelineRoot
@@ -90,7 +91,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)
}
}
@@ -98,8 +99,39 @@ Page {
anchors.fill: parent
color: colors.window
+ Component {
+ id: deviceVerificationDialog
+ DeviceVerification {}
+ }
+ Connections {
+ target: TimelineManager
+ function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId,isRequest) {
+ flow.userId = userId;
+ flow.sender = false;
+ flow.deviceId = deviceId;
+ switch(flow.type){
+ case DeviceVerificationFlow.ToDevice:
+ flow.tranId = transactionId;
+ deviceVerificationList.add(flow.tranId);
+ break;
+ case DeviceVerificationFlow.RoomMsg:
+ deviceVerificationList.add(flow.tranId);
+ break;
+ }
+ var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,isRequest = isRequest});
+ dialog.show();
+ }
+ }
+ Connections {
+ target: TimelineManager.timeline
+ function onOpenProfile(profile) {
+ var userProfile = userProfileComponent.createObject(timelineRoot,{profile: profile});
+ userProfile.show();
+ }
+ }
+
Label {
- visible: !timelineManager.timeline && !timelineManager.isInitialSync
+ visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
anchors.centerIn: parent
text: qsTr("No room open")
font.pointSize: 24
@@ -109,7 +141,7 @@ Page {
BusyIndicator {
visible: running
anchors.centerIn: parent
- running: timelineManager.isInitialSync
+ running: TimelineManager.isInitialSync
height: 200
width: 200
z: 3
@@ -118,7 +150,7 @@ Page {
ListView {
id: chat
- visible: !!timelineManager.timeline
+ visible: TimelineManager.timeline != null
cacheBuffer: 400
@@ -130,7 +162,7 @@ Page {
anchors.leftMargin: 4
anchors.rightMargin: scrollbar.width
- model: timelineManager.timeline
+ model: TimelineManager.timeline
boundsBehavior: Flickable.StopAtBounds
@@ -178,7 +210,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.
@@ -219,6 +251,11 @@ Page {
}
}
+ Component{
+ id: userProfileComponent
+ UserProfile{}
+ }
+
section {
property: "section"
}
@@ -255,6 +292,7 @@ Page {
color: colors.base
}
}
+
Row {
height: userName.height
spacing: 8
@@ -277,25 +315,17 @@ 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 {
anchors.fill: parent
- onClicked: chat.model.openUserProfile(section.split(" ")[0])
+ Layout.alignment: Qt.AlignHCenter
+ onClicked: chat.model.openUserProfile(modelData.userId)
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
- }
}
}
}
@@ -359,7 +389,7 @@ Page {
anchors.bottom: parent.bottom
modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {}
- 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
new file mode 100644
index 00000000..115a73c4
--- /dev/null
+++ b/resources/qml/UserProfile.qml
@@ -0,0 +1,258 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.3
+
+import im.nheko 1.0
+
+import "./device-verification"
+
+ApplicationWindow{
+ property var profile
+
+ id: userProfileDialog
+ height: 650
+ width: 420
+ modality: Qt.WindowModal
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
+ palette: colors
+
+ Connections{
+ target: deviceVerificationList
+ onUpdateProfile: {
+ profile.fetchDeviceList(profile.userid)
+ }
+ }
+
+ 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 {
+ 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
+ }
+
+ 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: {
+ var newFlow = profile.createFlow(true);
+ newFlow.userId = profile.userid;
+ newFlow.sender = true;
+ deviceVerificationList.add(newFlow.tranId);
+ var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true});
+ dialog.show();
+ }
+ }
+
+ 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-13
+ 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{
+ 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.AlignRight
+ text: model.deviceName
+ }
+ }
+ 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"))
+ }
+ Button{
+ id: verifyButton
+ text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
+ onClicked: {
+ var newFlow = profile.createFlow(false);
+ newFlow.userId = profile.userid;
+ newFlow.sender = true;
+ newFlow.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,isRequest:false});
+ 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/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml
index d8e4215f..158daf45 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 3885ddae..b5c51c2c 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 56b8040e..ff025730 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 {
@@ -124,31 +124,85 @@ 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 {
+ 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 {
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 8d2fa8a8..893325b6 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 f9fd3f11..d4fffb06 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 cc2d2da0..99ff9329 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: "<style type=\"text/css\">a { color:"+colors.link+";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap'>")
width: parent ? parent.width : undefined
height: isReply ? Math.round(Math.min(timelineRoot.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
new file mode 100644
index 00000000..94cb1e33
--- /dev/null
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -0,0 +1,633 @@
+import QtQuick 2.3
+import QtQuick.Controls 2.10
+import QtQuick.Window 2.2
+import QtQuick.Layouts 1.10
+
+import im.nheko 1.0
+
+ApplicationWindow {
+ title: stack.currentItem.title
+ id: dialog
+
+ flags: Qt.Dialog
+
+ palette: colors
+
+ height: stack.implicitHeight
+ width: stack.implicitWidth
+ StackView {
+ id: stack
+ initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest
+ implicitWidth: currentItem.implicitWidth
+ implicitHeight: currentItem.implicitHeight
+ }
+
+ property var flow
+ property bool isRequest
+
+ 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;
+ }
+
+ onRefreshProfile: {
+ deviceVerificationList.updateProfile(flow.userId);
+ }
+ }
+
+ Component {
+ id: newVerificationRequest
+ Pane {
+ property string title: qsTr("Sending Device Verification Request")
+ ColumnLayout {
+ spacing: 16
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ text: qsTr("A new device was added.")
+ color:colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ 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: qsTr("Cancel")
+ palette {
+ button: "white"
+ }
+ contentItem: Text {
+ text: parent.text
+ color: "black"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ onClicked: {
+ dialog.close();
+ delete flow;
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ Button {
+ Layout.alignment: Qt.AlignRight
+ text: qsTr("Start verification")
+ palette {
+ button: "white"
+ }
+ contentItem: Text {
+ text: parent.text
+ color: "black"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ onClicked: {
+ stack.replace(awaitingVerificationRequestAccept);
+ isRequest?flow.sendVerificationRequest():flow.startVerificationRequest(); }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: acceptNewVerificationRequest
+ Pane {
+ property string title: qsTr("Recieving Device Verification Request")
+ ColumnLayout {
+ spacing: 16
+
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ text: qsTr("The device was requested to be verified")
+ color:colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+ RowLayout {
+ Button {
+ Layout.alignment: Qt.AlignLeft
+ text: qsTr("Deny")
+ palette {
+ button: "white"
+ }
+ contentItem: Text {
+ text: parent.text
+ color: "black"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ onClicked: {
+ dialog.close();
+ flow.cancelVerification(DeviceVerificationFlow.User);
+ deviceVerificationList.remove(flow.tranId);
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ Button {
+ Layout.alignment: Qt.AlignRight
+ text: qsTr("Accept")
+ palette {
+ button: "white"
+ }
+ contentItem: Text {
+ text: parent.text
+ color: "black"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ onClicked: {
+ stack.replace(awaitingVerificationRequestAccept);
+ isRequest?flow.sendVerificationReady():flow.acceptVerificationRequest();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: awaitingVerificationRequestAccept
+ Pane {
+ property string title: qsTr("Waiting for other party")
+ ColumnLayout {
+ spacing: 16
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ id: content
+ text: qsTr("Waiting for other side to accept the verification request.")
+ color:colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ BusyIndicator {
+ Layout.alignment: Qt.AlignHCenter
+ }
+ RowLayout {
+ Button {
+ Layout.alignment: Qt.AlignLeft
+ text: qsTr("Cancel")
+ palette {
+ button: "white"
+ }
+ contentItem: Text {
+ text: parent.text
+ color: "black"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ onClicked: {
+ dialog.close();
+ flow.cancelVerification(DeviceVerificationFlow.User);
+ deviceVerificationList.remove(flow.tranId);
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: digitVerification
+ Pane {
+ property string title: qsTr("Verification Code")
+ ColumnLayout {
+ spacing: 16
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ 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
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ 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
+ }
+ }
+
+ RowLayout {
+ Button {
+ Layout.alignment: Qt.AlignLeft
+ 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(DeviceVerificationFlow.MismatchedSAS);
+ deviceVerificationList.remove(flow.tranId);
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ Button {
+ Layout.alignment: Qt.AlignRight
+ 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(); }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: emojiVerification
+ Pane {
+ property string title: qsTr("Verification Code")
+ ColumnLayout {
+ spacing: 16
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ 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
+ }
+
+ 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: "transparent"
+ implicitHeight: Qt.application.font.pixelSize * 8
+ implicitWidth: col.width
+ ColumnLayout {
+ id: col
+ Layout.fillWidth: true
+ anchors.bottom: parent.bottom
+ property var emoji: emojis.mapping[flow.sasList[index]]
+ Label {
+ //height: font.pixelSize * 2
+ Layout.alignment: Qt.AlignHCenter
+ 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
+ text: col.emoji.description
+ color:colors.text
+ }
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ Button {
+ Layout.alignment: Qt.AlignLeft
+ 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(DeviceVerificationFlow.MismatchedSAS);
+ deviceVerificationList.remove(flow.tranId);
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ Button {
+ Layout.alignment: Qt.AlignRight
+ 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(); }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: awaitingVerificationConfirmation
+ Pane {
+ property string title: qsTr("Awaiting Confirmation")
+ ColumnLayout {
+ spacing: 16
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ id: content
+ text: qsTr("Waiting for other side to complete verification.")
+ color:colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ BusyIndicator {
+ Layout.alignment: Qt.AlignHCenter
+ }
+ RowLayout {
+ Button {
+ Layout.alignment: Qt.AlignLeft
+ text: qsTr("Cancel")
+ palette {
+ button: "white"
+ }
+ contentItem: Text {
+ text: parent.text
+ color: "black"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ onClicked: {
+ dialog.close();
+ flow.cancelVerification(DeviceVerificationFlow.User);
+ deviceVerificationList.remove(flow.tranId);
+ delete flow;
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: verificationSuccess
+ Pane {
+ property string title: qsTr("Successful Verification")
+ ColumnLayout {
+ spacing: 16
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ id: content
+ text: qsTr("Verification successful! Both sides verified their devices!")
+ color:colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RowLayout {
+ Item {
+ Layout.fillWidth: true
+ }
+ Button {
+ Layout.alignment: Qt.AlignRight
+ 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);
+ delete flow;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: partnerAborted
+ Pane {
+ property string title: qsTr("Verification aborted!")
+ ColumnLayout {
+ spacing: 16
+ Label {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ id: content
+ text: qsTr("Verification canceled by the other party!")
+ color:colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RowLayout {
+ Item {
+ Layout.fillWidth: true
+ }
+ Button {
+ Layout.alignment: Qt.AlignRight
+ 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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: timedout
+ Pane {
+ property string title: qsTr("Verification timed out")
+ ColumnLayout {
+ spacing: 16
+ Text {
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ id: content
+ text: qsTr("Device verification timed out.")
+ color:colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RowLayout {
+ Item {
+ Layout.fillWidth: true
+ }
+ Button {
+ id: timedOutCancel
+ Layout.alignment: Qt.AlignRight
+ 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);
+ delete flow;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/resources/qml/device-verification/EmojiElement.qml b/resources/qml/device-verification/EmojiElement.qml
new file mode 100644
index 00000000..22f9e414
--- /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)]
+ Label {
+ height: font.pixelSize * 2
+ Layout.alignment: Qt.AlignHCenter
+ text: col.emoji.emoji
+ font.pixelSize: Qt.application.font.pixelSize * 2
+ }
+ Label {
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
+ text: col.emoji.description
+ }
+ }
+}
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"}
+]
diff --git a/resources/qml/emoji/EmojiPicker.qml b/resources/qml/emoji/EmojiPicker.qml
index f75221d5..d2ca4da3 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)
emojiPopup.close()
- timelineManager.queueReactionMessage(emojiPopup.event_id, model.unicode)
+ TimelineManager.queueReactionMessage(emojiPopup.room_id, emojiPopup.event_id, model.unicode)
}
}
|