diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index 6c12952a..9685dde1 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -11,10 +11,11 @@ import im.nheko 1.0
Rectangle {
id: avatar
- property alias url: img.source
+ property string url
property string userid
property string displayName
property alias textColor: label.color
+ property bool crop: true
signal clicked(var mouse)
@@ -44,12 +45,13 @@ Rectangle {
anchors.fill: parent
asynchronous: true
- fillMode: Image.PreserveAspectCrop
+ fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
mipmap: true
smooth: true
sourceSize.width: avatar.width
sourceSize.height: avatar.height
layer.enabled: true
+ source: avatar.url + ((avatar.crop || !avatar.url) ? "" : "?scale")
MouseArea {
id: mouseArea
diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml
index 50287ad5..2c0e15a7 100644
--- a/resources/qml/InviteDialog.qml
+++ b/resources/qml/InviteDialog.qml
@@ -30,12 +30,12 @@ ApplicationWindow {
}
title: qsTr("Invite users to %1").arg(plainRoomName)
- x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
- y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
height: 380
width: 340
palette: Nheko.colors
color: Nheko.colors.window
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(inviteDialogRoot)
Shortcut {
sequence: "Ctrl+Enter"
diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index 8bc8ac62..7fb09684 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -7,7 +7,7 @@ import "./voip"
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
import im.nheko 1.0
Rectangle {
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 07feec8c..79cbd700 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -10,7 +10,7 @@ import QtGraphicalEffects 1.0
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
import im.nheko 1.0
ScrollView {
@@ -212,9 +212,9 @@ ScrollView {
// force current read index to update
onTriggered: {
- if (chat.model) {
+ if (chat.model)
chat.model.setCurrentIndex(chat.model.currentIndex);
- }
+
}
interval: 1000
}
@@ -349,6 +349,7 @@ ScrollView {
required property string callType
required property var reactions
required property int trustlevel
+ required property int encryptionError
required property var timestamp
required property int status
required property int index
@@ -456,6 +457,7 @@ ScrollView {
callType: wrapper.callType
reactions: wrapper.reactions
trustlevel: wrapper.trustlevel
+ encryptionError: wrapper.encryptionError
timestamp: wrapper.timestamp
status: wrapper.status
relatedEventCacheBuster: wrapper.relatedEventCacheBuster
@@ -580,7 +582,7 @@ ScrollView {
Platform.MenuItem {
text: qsTr("Read receip&ts")
- onTriggered: room.readReceiptsAction(messageContextMenu.eventId)
+ onTriggered: room.showReadReceipts(messageContextMenu.eventId)
}
Platform.MenuItem {
diff --git a/resources/qml/RawMessageDialog.qml b/resources/qml/RawMessageDialog.qml
new file mode 100644
index 00000000..e2a476cd
--- /dev/null
+++ b/resources/qml/RawMessageDialog.qml
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import im.nheko 1.0
+
+ApplicationWindow {
+ id: rawMessageRoot
+
+ property alias rawMessage: rawMessageView.text
+
+ height: 420
+ width: 420
+ palette: Nheko.colors
+ color: Nheko.colors.window
+ flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(rawMessageRoot)
+
+ Shortcut {
+ sequence: StandardKey.Cancel
+ onActivated: rawMessageRoot.close()
+ }
+
+ ScrollView {
+ anchors.margins: Nheko.paddingMedium
+ anchors.fill: parent
+ palette: Nheko.colors
+ padding: Nheko.paddingMedium
+
+ TextArea {
+ id: rawMessageView
+
+ font: Nheko.monospaceFont()
+ color: Nheko.colors.text
+ readOnly: true
+
+ background: Rectangle {
+ color: Nheko.colors.base
+ }
+
+ }
+
+ }
+
+ footer: DialogButtonBox {
+ standardButtons: DialogButtonBox.Ok
+ onAccepted: rawMessageRoot.close()
+ }
+
+}
diff --git a/resources/qml/ReadReceipts.qml b/resources/qml/ReadReceipts.qml
new file mode 100644
index 00000000..9adbfd5c
--- /dev/null
+++ b/resources/qml/ReadReceipts.qml
@@ -0,0 +1,130 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+import im.nheko 1.0
+
+ApplicationWindow {
+ id: readReceiptsRoot
+
+ property ReadReceiptsProxy readReceipts
+ property Room room
+
+ height: 380
+ width: 340
+ minimumHeight: 380
+ minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
+ palette: Nheko.colors
+ color: Nheko.colors.window
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(readReceiptsRoot)
+
+ Shortcut {
+ sequence: StandardKey.Cancel
+ onActivated: readReceiptsRoot.close()
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingMedium
+ spacing: Nheko.paddingMedium
+
+ Label {
+ id: headerTitle
+
+ color: Nheko.colors.text
+ Layout.alignment: Qt.AlignCenter
+ text: qsTr("Read receipts")
+ font.pointSize: fontMetrics.font.pointSize * 1.5
+ }
+
+ ScrollView {
+ palette: Nheko.colors
+ padding: Nheko.paddingMedium
+ ScrollBar.horizontal.visible: false
+ Layout.fillHeight: true
+ Layout.minimumHeight: 200
+ Layout.fillWidth: true
+
+ ListView {
+ id: readReceiptsList
+
+ clip: true
+ spacing: Nheko.paddingMedium
+ boundsBehavior: Flickable.StopAtBounds
+ model: readReceipts
+
+ delegate: RowLayout {
+ spacing: Nheko.paddingMedium
+
+ Avatar {
+ width: Nheko.avatarSize
+ height: Nheko.avatarSize
+ userid: model.mxid
+ url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
+ displayName: model.displayName
+ onClicked: room.openUserProfile(model.mxid)
+ ToolTip.visible: avatarHover.hovered
+ ToolTip.text: model.mxid
+
+ HoverHandler {
+ id: avatarHover
+ }
+
+ }
+
+ ColumnLayout {
+ spacing: Nheko.paddingSmall
+
+ Label {
+ text: model.displayName
+ color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window)
+ font.pointSize: fontMetrics.font.pointSize
+ ToolTip.visible: displayNameHover.hovered
+ ToolTip.text: model.mxid
+
+ TapHandler {
+ onSingleTapped: room.openUserProfile(userId)
+ }
+
+ CursorShape {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ }
+
+ HoverHandler {
+ id: displayNameHover
+ }
+
+ }
+
+ Label {
+ text: model.timestamp
+ color: Nheko.colors.buttonText
+ font.pointSize: fontMetrics.font.pointSize * 0.9
+ }
+
+ Item {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ footer: DialogButtonBox {
+ standardButtons: DialogButtonBox.Ok
+ onAccepted: readReceiptsRoot.close()
+ }
+
+}
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index 31c9d3cf..e8aacf75 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -179,31 +179,38 @@ Component {
}
]
- TapHandler {
- margin: -Nheko.paddingSmall
- acceptedButtons: Qt.RightButton
- onSingleTapped: {
- if (!TimelineManager.isInvite)
- roomContextMenu.show(roomId, tags);
+ // NOTE(Nico): We want to prevent the touch areas from overlapping. For some reason we need to add 1px of padding for that...
+ Item {
+ anchors.fill: parent
+ anchors.margins: 1
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ onSingleTapped: {
+ if (!TimelineManager.isInvite)
+ roomContextMenu.show(roomId, tags);
+
+ }
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
}
- gesturePolicy: TapHandler.ReleaseWithinBounds
- }
- TapHandler {
- margin: -Nheko.paddingSmall
- onSingleTapped: Rooms.setCurrentRoom(roomId)
- onLongPressed: {
- if (!isInvite)
- roomContextMenu.show(roomId, tags);
+ TapHandler {
+ margin: -Nheko.paddingSmall
+ onSingleTapped: Rooms.setCurrentRoom(roomId)
+ onLongPressed: {
+ if (!isInvite)
+ roomContextMenu.show(roomId, tags);
+ }
}
- }
- HoverHandler {
- id: hovered
+ HoverHandler {
+ id: hovered
+
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
+ }
- margin: -Nheko.paddingSmall
}
RowLayout {
@@ -439,6 +446,7 @@ Component {
url: (userInfoGrid.profile ? userInfoGrid.profile.avatarUrl : "").replace("mxc://", "image://MxcImage/")
displayName: userInfoGrid.profile ? userInfoGrid.profile.displayName : ""
userid: userInfoGrid.profile ? userInfoGrid.profile.userid : ""
+ enabled: false
}
ColumnLayout {
diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml
index 641a08be..447e6fd1 100644
--- a/resources/qml/RoomMembers.qml
+++ b/resources/qml/RoomMembers.qml
@@ -6,7 +6,7 @@ import "./ui"
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
-import QtQuick.Window 2.12
+import QtQuick.Window 2.13
import im.nheko 1.0
ApplicationWindow {
@@ -15,13 +15,13 @@ ApplicationWindow {
property MemberList members
title: qsTr("Members of %1").arg(members.roomName)
- x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
- y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
height: 650
width: 420
minimumHeight: 420
palette: Nheko.colors
color: Nheko.colors.window
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(roomMembersRoot)
Shortcut {
sequence: StandardKey.Cancel
diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml
index b8e527a5..69cf427c 100644
--- a/resources/qml/RoomSettings.qml
+++ b/resources/qml/RoomSettings.qml
@@ -7,7 +7,7 @@ import Qt.labs.platform 1.1 as Platform
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
-import QtQuick.Window 2.3
+import QtQuick.Window 2.13
import im.nheko 1.0
ApplicationWindow {
@@ -15,14 +15,13 @@ ApplicationWindow {
property var roomSettings
- x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
- y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
minimumWidth: 420
minimumHeight: 650
palette: Nheko.colors
color: Nheko.colors.window
modality: Qt.NonModal
- flags: Qt.Dialog
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(roomSettingsDialog)
title: qsTr("Room Settings")
Shortcut {
@@ -155,7 +154,7 @@ ApplicationWindow {
GridLayout {
columns: 2
- rowSpacing: 10
+ rowSpacing: Nheko.paddingLarge
MatrixText {
text: qsTr("SETTINGS")
@@ -181,7 +180,7 @@ ApplicationWindow {
}
MatrixText {
- text: "Room access"
+ text: qsTr("Room access")
Layout.fillWidth: true
}
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index e80ff764..b229acda 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -9,10 +9,10 @@ import "./emoji"
import "./voip"
import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.0
-import QtQuick 2.9
-import QtQuick.Controls 2.5
+import QtQuick 2.15
+import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
-import QtQuick.Window 2.2
+import QtQuick.Window 2.15
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
@@ -96,6 +96,22 @@ Page {
}
+ Component {
+ id: readReceiptsDialog
+
+ ReadReceipts {
+ }
+
+ }
+
+ Component {
+ id: rawMessageDialog
+
+ RawMessageDialog {
+ }
+
+ }
+
Shortcut {
sequence: "Ctrl+K"
onActivated: {
diff --git a/resources/qml/ScrollHelper.qml b/resources/qml/ScrollHelper.qml
index 2dd56f27..e84e67fd 100644
--- a/resources/qml/ScrollHelper.qml
+++ b/resources/qml/ScrollHelper.qml
@@ -23,6 +23,9 @@ MouseArea {
// console.warn("Delta: ", wheel.pixelDelta.y);
// console.warn("Old position: ", flickable.contentY);
// console.warn("New position: ", newPos);
+ // breaks ListView's with headers...
+ //if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
+ // minYExtent += flickableItem.headerItem.height;
id: root
@@ -55,9 +58,6 @@ MouseArea {
var minYExtent = flickableItem.originY + flickableItem.topMargin;
var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height;
- if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
- minYExtent += flickableItem.headerItem.height;
-
//Avoid overscrolling
return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta));
}
diff --git a/resources/qml/StatusIndicator.qml b/resources/qml/StatusIndicator.qml
index 7e471d69..0af02b3c 100644
--- a/resources/qml/StatusIndicator.qml
+++ b/resources/qml/StatusIndicator.qml
@@ -34,7 +34,7 @@ ImageButton {
}
onClicked: {
if (status == MtxEvent.Read)
- room.readReceiptsAction(eventId);
+ room.showReadReceipts(eventId);
}
image: {
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 755ab503..c612479a 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -7,7 +7,7 @@ import "./emoji"
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
import im.nheko 1.0
Item {
@@ -38,6 +38,7 @@ Item {
required property string callType
required property var reactions
required property int trustlevel
+ required property int encryptionError
required property var timestamp
required property int status
required property int relatedEventCacheBuster
@@ -110,6 +111,7 @@ Item {
roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? ""
+ encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? ""
relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
}
@@ -136,6 +138,7 @@ Item {
roomTopic: r.roomTopic
roomName: r.roomName
callType: r.callType
+ encryptionError: r.encryptionError
relatedEventCacheBuster: r.relatedEventCacheBuster
isReply: false
}
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index c5cc69a6..6fc9d51b 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -13,7 +13,7 @@ import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
@@ -249,4 +249,23 @@ Item {
roomid: room ? room.roomId : ""
}
+ Connections {
+ function onOpenReadReceiptsDialog(rr) {
+ var dialog = readReceiptsDialog.createObject(timelineRoot, {
+ "readReceipts": rr,
+ "room": room
+ });
+ dialog.show();
+ }
+
+ function onShowRawMessageDialog(rawMessage) {
+ var dialog = rawMessageDialog.createObject(timelineRoot, {
+ "rawMessage": rawMessage
+ });
+ dialog.show();
+ }
+
+ target: room
+ }
+
}
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index d138060b..767d2317 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -4,19 +4,20 @@
import "./device-verification"
import "./ui"
-import QtQuick 2.9
-import QtQuick.Controls 2.3
+import QtQuick 2.15
+import QtQuick.Controls 2.15
import QtQuick.Layouts 1.2
-import QtQuick.Window 2.3
+import QtQuick.Window 2.13
import im.nheko 1.0
ApplicationWindow {
+ // this does not work in ApplicationWindow, just in Window
+ //transientParent: Nheko.mainwindow()
+
id: userProfileDialog
property var profile
- x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
- y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
height: 650
width: 420
minimumHeight: 420
@@ -24,7 +25,8 @@ ApplicationWindow {
color: Nheko.colors.window
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
modality: Qt.NonModal
- flags: Qt.Dialog
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(userProfileDialog)
Shortcut {
sequence: StandardKey.Cancel
diff --git a/resources/qml/components/AvatarListTile.qml b/resources/qml/components/AvatarListTile.qml
new file mode 100644
index 00000000..36c26a97
--- /dev/null
+++ b/resources/qml/components/AvatarListTile.qml
@@ -0,0 +1,133 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+import im.nheko 1.0
+
+Rectangle {
+ id: tile
+
+ property color background: Nheko.colors.window
+ property color importantText: Nheko.colors.text
+ property color unimportantText: Nheko.colors.buttonText
+ property color bubbleBackground: Nheko.colors.highlight
+ property color bubbleText: Nheko.colors.highlightedText
+ property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
+ required property string avatarUrl
+ required property string title
+ required property string subtitle
+ required property int index
+ required property int selectedIndex
+ property bool crop: true
+
+ color: background
+ height: avatarSize + 2 * Nheko.paddingMedium
+ width: ListView.view.width
+ state: "normal"
+ states: [
+ State {
+ name: "highlight"
+ when: hovered.hovered && !(index == selectedIndex)
+
+ PropertyChanges {
+ target: tile
+ background: Nheko.colors.dark
+ importantText: Nheko.colors.brightText
+ unimportantText: Nheko.colors.brightText
+ bubbleBackground: Nheko.colors.highlight
+ bubbleText: Nheko.colors.highlightedText
+ }
+
+ },
+ State {
+ name: "selected"
+ when: index == selectedIndex
+
+ PropertyChanges {
+ target: tile
+ background: Nheko.colors.highlight
+ importantText: Nheko.colors.highlightedText
+ unimportantText: Nheko.colors.highlightedText
+ bubbleBackground: Nheko.colors.highlightedText
+ bubbleText: Nheko.colors.highlight
+ }
+
+ }
+ ]
+
+ HoverHandler {
+ id: hovered
+ }
+
+ RowLayout {
+ spacing: Nheko.paddingMedium
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingMedium
+
+ Avatar {
+ id: avatar
+
+ enabled: false
+ Layout.alignment: Qt.AlignVCenter
+ height: avatarSize
+ width: avatarSize
+ url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
+ displayName: title
+ crop: tile.crop
+ }
+
+ ColumnLayout {
+ id: textContent
+
+ Layout.alignment: Qt.AlignLeft
+ Layout.fillWidth: true
+ Layout.minimumWidth: 100
+ width: parent.width - avatar.width
+ Layout.preferredWidth: parent.width - avatar.width
+ spacing: Nheko.paddingSmall
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 0
+
+ ElidedLabel {
+ Layout.alignment: Qt.AlignBottom
+ color: tile.importantText
+ elideWidth: textContent.width - Nheko.paddingMedium
+ fullText: title
+ textFormat: Text.PlainText
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 0
+
+ ElidedLabel {
+ color: tile.unimportantText
+ font.pixelSize: fontMetrics.font.pixelSize * 0.9
+ elideWidth: textContent.width - Nheko.paddingSmall
+ fullText: subtitle
+ textFormat: Text.PlainText
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ }
+
+ }
+
+ }
+
+}
diff --git a/resources/qml/delegates/Encrypted.qml b/resources/qml/delegates/Encrypted.qml
new file mode 100644
index 00000000..cd00a9d4
--- /dev/null
+++ b/resources/qml/delegates/Encrypted.qml
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.2
+import im.nheko 1.0
+
+ColumnLayout {
+ id: r
+
+ required property int encryptionError
+ required property string eventId
+
+ width: parent ? parent.width : undefined
+
+ MatrixText {
+ text: {
+ switch (encryptionError) {
+ case Olm.MissingSession:
+ return qsTr("There is no key to unlock this message. We requested the key automatically, but you can try requesting it again if you are impatient.");
+ case Olm.MissingSessionIndex:
+ return qsTr("This message couldn't be decrypted, because we only have a key for newer messages. You can try requesting access to this message.");
+ case Olm.DbError:
+ return qsTr("There was an internal error reading the decryption key from the database.");
+ case Olm.DecryptionFailed:
+ return qsTr("There was an error decrypting this message.");
+ case Olm.ParsingFailed:
+ return qsTr("The message couldn't be parsed.");
+ case Olm.ReplayAttack:
+ return qsTr("The encryption key was reused! Someone is possibly trying to insert false messages into this chat!");
+ default:
+ return qsTr("Unknown decryption error");
+ }
+ }
+ color: Nheko.colors.buttonText
+ width: r ? r.width : undefined
+ }
+
+ Button {
+ palette: Nheko.colors
+ visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
+ text: qsTr("Request key")
+ onClicked: room.requestKeyForEvent(eventId)
+ }
+
+}
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index a98c2a8b..a8bdf183 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -29,6 +29,7 @@ Item {
required property string roomTopic
required property string roomName
required property string callType
+ required property int encryptionError
required property int relatedEventCacheBuster
height: chooser.childrenRect.height
@@ -190,6 +191,16 @@ Item {
}
DelegateChoice {
+ roleValue: MtxEvent.Encrypted
+
+ Encrypted {
+ encryptionError: d.encryptionError
+ eventId: d.eventId
+ }
+
+ }
+
+ DelegateChoice {
roleValue: MtxEvent.Name
NoticeMessage {
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index 75e3d617..8bbce10e 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -5,7 +5,7 @@
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
import im.nheko 1.0
Item {
@@ -30,6 +30,7 @@ Item {
property string roomTopic
property string roomName
property string callType
+ property int encryptionError
property int relatedEventCacheBuster
width: parent.width
@@ -97,6 +98,7 @@ Item {
roomName: r.roomName
callType: r.callType
relatedEventCacheBuster: r.relatedEventCacheBuster
+ encryptionError: r.encryptionError
enabled: false
width: parent.width
isReply: true
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index e2c66c5a..8e0271d6 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -4,7 +4,7 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
-import QtQuick.Window 2.10
+import QtQuick.Window 2.13
import im.nheko 1.0
ApplicationWindow {
@@ -14,13 +14,12 @@ ApplicationWindow {
onClosing: TimelineManager.removeVerificationFlow(flow)
title: stack.currentItem.title
- flags: Qt.Dialog
modality: Qt.NonModal
palette: Nheko.colors
height: stack.implicitHeight
width: stack.implicitWidth
- x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
- y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(dialog)
StackView {
id: stack
diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml
new file mode 100644
index 00000000..b839c9e3
--- /dev/null
+++ b/resources/qml/dialogs/ImagePackEditorDialog.qml
@@ -0,0 +1,301 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import "../components"
+import Qt.labs.platform 1.1
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import im.nheko 1.0
+
+ApplicationWindow {
+ //Component.onCompleted: Nheko.reparent(win)
+
+ id: win
+
+ property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
+ property SingleImagePackModel imagePack
+ property int currentImageIndex: -1
+ readonly property int stickerDim: 128
+ readonly property int stickerDimPad: 128 + Nheko.paddingSmall
+
+ title: qsTr("Editing image pack")
+ height: 600
+ width: 600
+ palette: Nheko.colors
+ color: Nheko.colors.base
+ modality: Qt.WindowModal
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+
+ AdaptiveLayout {
+ id: adaptiveView
+
+ anchors.fill: parent
+ singlePageMode: false
+ pageIndex: 0
+
+ AdaptiveLayoutElement {
+ id: packlistC
+
+ visible: Settings.groupView
+ minimumWidth: 200
+ collapsedWidth: 200
+ preferredWidth: 300
+ maximumWidth: 300
+ clip: true
+
+ ListView {
+ //required property bool isEmote
+ //required property bool isSticker
+
+ model: imagePack
+
+ ScrollHelper {
+ flickable: parent
+ anchors.fill: parent
+ enabled: !Settings.mobileMode
+ }
+
+ header: AvatarListTile {
+ title: imagePack.packname
+ avatarUrl: imagePack.avatarUrl
+ subtitle: imagePack.statekey
+ index: -1
+ selectedIndex: currentImageIndex
+
+ TapHandler {
+ onSingleTapped: currentImageIndex = -1
+ }
+
+ Rectangle {
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ height: parent.height - Nheko.paddingSmall * 2
+ width: 3
+ color: Nheko.colors.highlight
+ }
+
+ }
+
+ footer: Button {
+ palette: Nheko.colors
+ onClicked: addFilesDialog.open()
+ width: ListView.view.width
+ text: qsTr("Add images")
+
+ FileDialog {
+ id: addFilesDialog
+
+ folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
+ fileMode: FileDialog.OpenFiles
+ nameFilters: [qsTr("Stickers (*.png *.webp)")]
+ onAccepted: imagePack.addStickers(files)
+ }
+
+ }
+
+ delegate: AvatarListTile {
+ id: packItem
+
+ property color background: Nheko.colors.window
+ property color importantText: Nheko.colors.text
+ property color unimportantText: Nheko.colors.buttonText
+ property color bubbleBackground: Nheko.colors.highlight
+ property color bubbleText: Nheko.colors.highlightedText
+ required property string shortCode
+ required property string url
+ required property string body
+
+ title: shortCode
+ subtitle: body
+ avatarUrl: url
+ selectedIndex: currentImageIndex
+ crop: false
+
+ TapHandler {
+ onSingleTapped: currentImageIndex = index
+ }
+
+ }
+
+ }
+
+ }
+
+ AdaptiveLayoutElement {
+ id: packinfoC
+
+ Rectangle {
+ color: Nheko.colors.window
+
+ GridLayout {
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingMedium
+ visible: currentImageIndex == -1
+ enabled: visible
+ columns: 2
+ rowSpacing: Nheko.paddingLarge
+
+ Avatar {
+ Layout.columnSpan: 2
+ url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/")
+ displayName: imagePack.packname
+ height: 130
+ width: 130
+ crop: false
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ MatrixText {
+ visible: imagePack.roomid
+ text: qsTr("State key")
+ }
+
+ MatrixTextField {
+ visible: imagePack.roomid
+ Layout.fillWidth: true
+ text: imagePack.statekey
+ onTextEdited: imagePack.statekey = text
+ }
+
+ MatrixText {
+ text: qsTr("Packname")
+ }
+
+ MatrixTextField {
+ Layout.fillWidth: true
+ text: imagePack.packname
+ onTextEdited: imagePack.packname = text
+ }
+
+ MatrixText {
+ text: qsTr("Attrbution")
+ }
+
+ MatrixTextField {
+ Layout.fillWidth: true
+ text: imagePack.attribution
+ onTextEdited: imagePack.attribution = text
+ }
+
+ MatrixText {
+ text: qsTr("Use as Emoji")
+ }
+
+ ToggleButton {
+ checked: imagePack.isEmotePack
+ onClicked: imagePack.isEmotePack = checked
+ Layout.alignment: Qt.AlignRight
+ }
+
+ MatrixText {
+ text: qsTr("Use as Sticker")
+ }
+
+ ToggleButton {
+ checked: imagePack.isStickerPack
+ onClicked: imagePack.isStickerPack = checked
+ Layout.alignment: Qt.AlignRight
+ }
+
+ Item {
+ Layout.columnSpan: 2
+ Layout.fillHeight: true
+ }
+
+ }
+
+ GridLayout {
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingMedium
+ visible: currentImageIndex >= 0
+ enabled: visible
+ columns: 2
+ rowSpacing: Nheko.paddingLarge
+
+ Avatar {
+ Layout.columnSpan: 2
+ url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/")
+ displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
+ height: 130
+ width: 130
+ crop: false
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ MatrixText {
+ text: qsTr("Shortcode")
+ }
+
+ MatrixTextField {
+ Layout.fillWidth: true
+ text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
+ onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.ShortCode)
+ }
+
+ MatrixText {
+ text: qsTr("Body")
+ }
+
+ MatrixTextField {
+ Layout.fillWidth: true
+ text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Body)
+ onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.Body)
+ }
+
+ MatrixText {
+ text: qsTr("Use as Emoji")
+ }
+
+ ToggleButton {
+ checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
+ onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
+ Layout.alignment: Qt.AlignRight
+ }
+
+ MatrixText {
+ text: qsTr("Use as Sticker")
+ }
+
+ ToggleButton {
+ checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
+ onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
+ Layout.alignment: Qt.AlignRight
+ }
+
+ Item {
+ Layout.columnSpan: 2
+ Layout.fillHeight: true
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ footer: DialogButtonBox {
+ id: buttons
+
+ Button {
+ text: qsTr("Cancel")
+ DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
+ onClicked: win.close()
+ }
+
+ Button {
+ text: qsTr("Save")
+ DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole
+ onClicked: {
+ imagePack.save();
+ win.close();
+ }
+ }
+
+ }
+
+}
diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml
index c4b4a885..5181619c 100644
--- a/resources/qml/dialogs/ImagePackSettingsDialog.qml
+++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml
@@ -20,14 +20,21 @@ ApplicationWindow {
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
title: qsTr("Image pack settings")
- x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
- y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
- height: 400
- width: 600
+ height: 600
+ width: 800
palette: Nheko.colors
color: Nheko.colors.base
modality: Qt.NonModal
- flags: Qt.Dialog
+ flags: Qt.Dialog | Qt.WindowCloseButtonHint
+ Component.onCompleted: Nheko.reparent(win)
+
+ Component {
+ id: packEditor
+
+ ImagePackEditorDialog {
+ }
+
+ }
AdaptiveLayout {
id: adaptiveView
@@ -55,7 +62,35 @@ ApplicationWindow {
enabled: !Settings.mobileMode
}
- delegate: Rectangle {
+ footer: ColumnLayout {
+ Button {
+ palette: Nheko.colors
+ onClicked: {
+ var dialog = packEditor.createObject(timelineRoot, {
+ "imagePack": packlist.newPack(false)
+ });
+ dialog.show();
+ }
+ width: packlist.width
+ visible: !packlist.containsAccountPack
+ text: qsTr("Create account pack")
+ }
+
+ Button {
+ palette: Nheko.colors
+ onClicked: {
+ var dialog = packEditor.createObject(timelineRoot, {
+ "imagePack": packlist.newPack(true)
+ });
+ dialog.show();
+ }
+ width: packlist.width
+ text: qsTr("New room pack")
+ }
+
+ }
+
+ delegate: AvatarListTile {
id: packItem
property color background: Nheko.colors.window
@@ -64,131 +99,24 @@ ApplicationWindow {
property color bubbleBackground: Nheko.colors.highlight
property color bubbleText: Nheko.colors.highlightedText
required property string displayName
- required property string avatarUrl
required property bool fromAccountData
required property bool fromCurrentRoom
- required property int index
- color: background
- height: avatarSize + 2 * Nheko.paddingMedium
- width: ListView.view.width
- state: "normal"
- states: [
- State {
- name: "highlight"
- when: hovered.hovered && !(index == currentPackIndex)
-
- PropertyChanges {
- target: packItem
- background: Nheko.colors.dark
- importantText: Nheko.colors.brightText
- unimportantText: Nheko.colors.brightText
- bubbleBackground: Nheko.colors.highlight
- bubbleText: Nheko.colors.highlightedText
- }
-
- },
- State {
- name: "selected"
- when: index == currentPackIndex
-
- PropertyChanges {
- target: packItem
- background: Nheko.colors.highlight
- importantText: Nheko.colors.highlightedText
- unimportantText: Nheko.colors.highlightedText
- bubbleBackground: Nheko.colors.highlightedText
- bubbleText: Nheko.colors.highlight
- }
-
- }
- ]
+ title: displayName
+ subtitle: {
+ if (fromAccountData)
+ return qsTr("Private pack");
+ else if (fromCurrentRoom)
+ return qsTr("Pack from this room");
+ else
+ return qsTr("Globally enabled pack");
+ }
+ selectedIndex: currentPackIndex
TapHandler {
- margin: -Nheko.paddingSmall
onSingleTapped: currentPackIndex = index
}
- HoverHandler {
- id: hovered
- }
-
- RowLayout {
- spacing: Nheko.paddingMedium
- anchors.fill: parent
- anchors.margins: Nheko.paddingMedium
-
- Avatar {
- // In the future we could show an online indicator by setting the userid for the avatar
- //userid: Nheko.currentUser.userid
-
- id: avatar
-
- enabled: false
- Layout.alignment: Qt.AlignVCenter
- height: avatarSize
- width: avatarSize
- url: avatarUrl.replace("mxc://", "image://MxcImage/")
- displayName: packItem.displayName
- }
-
- ColumnLayout {
- id: textContent
-
- Layout.alignment: Qt.AlignLeft
- Layout.fillWidth: true
- Layout.minimumWidth: 100
- width: parent.width - avatar.width
- Layout.preferredWidth: parent.width - avatar.width
- spacing: Nheko.paddingSmall
-
- RowLayout {
- Layout.fillWidth: true
- spacing: 0
-
- ElidedLabel {
- Layout.alignment: Qt.AlignBottom
- color: packItem.importantText
- elideWidth: textContent.width - Nheko.paddingMedium
- fullText: displayName
- textFormat: Text.PlainText
- }
-
- Item {
- Layout.fillWidth: true
- }
-
- }
-
- RowLayout {
- Layout.fillWidth: true
- spacing: 0
-
- ElidedLabel {
- color: packItem.unimportantText
- font.pixelSize: fontMetrics.font.pixelSize * 0.9
- elideWidth: textContent.width - Nheko.paddingSmall
- fullText: {
- if (fromAccountData)
- return qsTr("Private pack");
- else if (fromCurrentRoom)
- return qsTr("Pack from this room");
- else
- return qsTr("Globally enabled pack");
- }
- textFormat: Text.PlainText
- }
-
- Item {
- Layout.fillWidth: true
- }
-
- }
-
- }
-
- }
-
}
}
@@ -205,6 +133,7 @@ ApplicationWindow {
id: packinfo
property string packName: currentPack ? currentPack.packname : ""
+ property string attribution: currentPack ? currentPack.attribution : ""
property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
anchors.fill: parent
@@ -222,8 +151,18 @@ ApplicationWindow {
MatrixText {
text: packinfo.packName
- font.pixelSize: 24
+ font.pixelSize: Math.ceil(fontMetrics.pixelSize * 1.1)
+ horizontalAlignment: TextEdit.AlignHCenter
Layout.alignment: Qt.AlignHCenter
+ Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
+ }
+
+ MatrixText {
+ text: packinfo.attribution
+ wrapMode: TextEdit.Wrap
+ horizontalAlignment: TextEdit.AlignHCenter
+ Layout.alignment: Qt.AlignHCenter
+ Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
}
GridLayout {
@@ -245,6 +184,18 @@ ApplicationWindow {
}
+ Button {
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("Edit")
+ enabled: currentPack.canEdit
+ onClicked: {
+ var dialog = packEditor.createObject(timelineRoot, {
+ "imagePack": currentPack
+ });
+ dialog.show();
+ }
+ }
+
GridView {
Layout.fillHeight: true
Layout.fillWidth: true
@@ -267,7 +218,7 @@ ApplicationWindow {
width: stickerDim
height: stickerDim
hoverEnabled: true
- ToolTip.text: ":" + model.shortcode + ": - " + model.body
+ ToolTip.text: ":" + model.shortCode + ": - " + model.body
ToolTip.visible: hovered
contentItem: Image {
diff --git a/resources/qml/dialogs/InputDialog.qml b/resources/qml/dialogs/InputDialog.qml
index 134b78a3..e0f17851 100644
--- a/resources/qml/dialogs/InputDialog.qml
+++ b/resources/qml/dialogs/InputDialog.qml
@@ -16,6 +16,7 @@ ApplicationWindow {
modality: Qt.NonModal
flags: Qt.Dialog
+ Component.onCompleted: Nheko.reparent(inputDialog)
width: 350
height: fontMetrics.lineSpacing * 7
diff --git a/resources/res.qrc b/resources/res.qrc
index 407a0a98..3e417d4c 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -112,7 +112,6 @@
</qresource>
<qresource prefix="/">
<file>qtquickcontrols2.conf</file>
-
<file>qml/Root.qml</file>
<file>qml/ChatPage.qml</file>
<file>qml/CommunitiesList.qml</file>
@@ -144,15 +143,21 @@
<file>qml/emoji/StickerPicker.qml</file>
<file>qml/UserProfile.qml</file>
<file>qml/RoomDirectory.qml</file>
- <file>qml/delegates/MessageDelegate.qml</file>
+ <file>qml/delegates/MessageDelegate.qml</file>
<file>qml/delegates/TextMessage.qml</file>
<file>qml/delegates/NoticeMessage.qml</file>
<file>qml/delegates/ImageMessage.qml</file>
<file>qml/delegates/PlayableMediaMessage.qml</file>
+ <file>qml/delegates/MessageDelegate.qml</file>
+ <file>qml/delegates/Encrypted.qml</file>
<file>qml/delegates/FileMessage.qml</file>
+ <file>qml/delegates/ImageMessage.qml</file>
+ <file>qml/delegates/NoticeMessage.qml</file>
<file>qml/delegates/Pill.qml</file>
<file>qml/delegates/Placeholder.qml</file>
+ <file>qml/delegates/PlayableMediaMessage.qml</file>
<file>qml/delegates/Reply.qml</file>
+ <file>qml/delegates/TextMessage.qml</file>
<file>qml/device-verification/Waiting.qml</file>
<file>qml/device-verification/DeviceVerification.qml</file>
<file>qml/device-verification/DigitVerification.qml</file>
@@ -162,6 +167,7 @@
<file>qml/device-verification/Success.qml</file>
<file>qml/dialogs/InputDialog.qml</file>
<file>qml/dialogs/ImagePackSettingsDialog.qml</file>
+ <file>qml/dialogs/ImagePackEditorDialog.qml</file>
<file>qml/ui/Ripple.qml</file>
<file>qml/ui/Spinner.qml</file>
<file>qml/ui/animations/BlinkAnimation.qml</file>
@@ -175,9 +181,12 @@
<file>qml/voip/VideoCall.qml</file>
<file>qml/components/AdaptiveLayout.qml</file>
<file>qml/components/AdaptiveLayoutElement.qml</file>
+ <file>qml/components/AvatarListTile.qml</file>
<file>qml/components/FlatButton.qml</file>
<file>qml/RoomMembers.qml</file>
<file>qml/InviteDialog.qml</file>
+ <file>qml/ReadReceipts.qml</file>
+ <file>qml/RawMessageDialog.qml</file>
</qresource>
<qresource prefix="/media">
<file>media/ring.ogg</file>
|