summary refs log tree commit diff
path: root/resources/qml
diff options
context:
space:
mode:
Diffstat (limited to 'resources/qml')
-rw-r--r--resources/qml/Avatar.qml10
-rw-r--r--resources/qml/MatrixText.qml6
-rw-r--r--resources/qml/Reactions.qml10
-rw-r--r--resources/qml/TimelineRow.qml12
-rw-r--r--resources/qml/TimelineView.qml66
-rw-r--r--resources/qml/UserProfile.qml258
-rw-r--r--resources/qml/delegates/FileMessage.qml4
-rw-r--r--resources/qml/delegates/ImageMessage.qml2
-rw-r--r--resources/qml/delegates/MessageDelegate.qml66
-rw-r--r--resources/qml/delegates/PlayableMediaMessage.qml4
-rw-r--r--resources/qml/delegates/Reply.qml4
-rw-r--r--resources/qml/delegates/TextMessage.qml4
-rw-r--r--resources/qml/device-verification/DeviceVerification.qml633
-rw-r--r--resources/qml/device-verification/EmojiElement.qml25
-rw-r--r--resources/qml/device-verification/sas-emoji.json66
-rw-r--r--resources/qml/emoji/EmojiPicker.qml4
16 files changed, 1126 insertions, 48 deletions
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) } }