summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2023-08-25 20:43:04 +0200
committerNicolas Werner <nicolas.werner@hotmail.de>2023-08-25 20:43:04 +0200
commitb187440e68334b828d7271ac3d51dfd4fd24b18d (patch)
treeaa9199968eb18245357128c1e51aac598d058257
parentShow reactions again (diff)
downloadnheko-b187440e68334b828d7271ac3d51dfd4fd24b18d.tar.xz
Reimplement reply delegate by moving out the timeline event without layout
-rw-r--r--CMakeLists.txt1
-rw-r--r--resources/qml/Completer.qml5
-rw-r--r--resources/qml/ForwardCompleter.qml18
-rw-r--r--resources/qml/MatrixText.qml23
-rw-r--r--resources/qml/MessageView.qml313
-rw-r--r--resources/qml/Reactions.qml2
-rw-r--r--resources/qml/ReplyPopup.qml16
-rw-r--r--resources/qml/RoomList.qml2
-rw-r--r--resources/qml/Root.qml25
-rw-r--r--resources/qml/TimelineEvent.qml255
-rw-r--r--resources/qml/TimelineRow.qml3
-rw-r--r--resources/qml/TopBar.qml17
-rw-r--r--resources/qml/delegates/Reply.qml132
13 files changed, 352 insertions, 460 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 01070a82..548d5303 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -713,6 +713,7 @@ set(QML_SOURCES
         resources/qml/UploadBox.qml
         resources/qml/MessageInput.qml
         resources/qml/MessageView.qml
+				resources/qml/TimelineEvent.qml
         resources/qml/PrivacyScreen.qml
         resources/qml/Reactions.qml
         resources/qml/ReplyPopup.qml
diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml
index 590d5bb8..9ebe0a40 100644
--- a/resources/qml/Completer.qml
+++ b/resources/qml/Completer.qml
@@ -145,7 +145,6 @@ Control {
                     roleValue: "user"
 
                     RowLayout {
-
                         anchors.centerIn: centerRowContent ? parent : undefined
                         spacing: rowSpacing
 
@@ -171,7 +170,6 @@ Control {
                     roleValue: "emoji"
 
                     RowLayout {
-
                         anchors.centerIn: parent
                         spacing: rowSpacing
 
@@ -207,7 +205,6 @@ Control {
                     roleValue: "command"
 
                     RowLayout {
-
                         anchors.centerIn: parent
                         spacing: rowSpacing
 
@@ -226,7 +223,6 @@ Control {
                     roleValue: "room"
 
                     RowLayout {
-
                         anchors.centerIn: centerRowContent ? parent : undefined
                         spacing: rowSpacing
 
@@ -251,7 +247,6 @@ Control {
                     roleValue: "roomAliases"
 
                     RowLayout {
-
                         anchors.centerIn: parent
                         spacing: rowSpacing
 
diff --git a/resources/qml/ForwardCompleter.qml b/resources/qml/ForwardCompleter.qml
index 0174e0f6..6f95c663 100644
--- a/resources/qml/ForwardCompleter.qml
+++ b/resources/qml/ForwardCompleter.qml
@@ -54,24 +54,8 @@ Popup {
         Reply {
             id: replyPreview
 
-            property var modelData: room ? room.getDump(mid, "") : {}
-
-            blurhash: modelData.blurhash ?? ""
-            body: modelData.body ?? ""
-            encryptionError: modelData.encryptionError ?? ""
-            eventId: modelData.eventId ?? ""
-            filename: modelData.filename ?? ""
-            filesize: modelData.filesize ?? ""
-            formattedBody: modelData.formattedBody ?? ""
-            isOnlyEmoji: modelData.isOnlyEmoji ?? false
-            originalWidth: modelData.originalWidth ?? 0
-            proportionalHeight: modelData.proportionalHeight ?? 1
-            type: modelData.type ?? MtxEvent.UnknownMessage
-            typeString: modelData.typeString ?? ""
-            url: modelData.url ?? ""
+            eventId: mid
             userColor: TimelineManager.userColor(modelData.userId, palette.window)
-            userId: modelData.userId ?? ""
-            userName: modelData.userName ?? ""
             width: parent.width
         }
         MatrixTextField {
diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index de15e078..bf953d56 100644
--- a/resources/qml/MatrixText.qml
+++ b/resources/qml/MatrixText.qml
@@ -11,25 +11,24 @@ TextArea {
 
     property alias cursorShape: cs.cursorShape
 
-    leftInset: 0
-    bottomInset: 0
-    rightInset: 0
-    topInset: 0
-    leftPadding: 0
-    bottomPadding: 0
-    rightPadding: 0
-    topPadding: 0
-    background: null
-
     ToolTip.text: hoveredLink
     ToolTip.visible: hoveredLink || false
+    background: null
+    bottomInset: 0
+    bottomPadding: 0
     // this always has to be enabled, otherwise you can't click links anymore!
     //enabled: selectByMouse
     color: palette.text
     focus: false
+    leftInset: 0
+    leftPadding: 0
     readOnly: true
+    rightInset: 0
+    rightPadding: 0
     selectByMouse: !Settings.mobileMode
     textFormat: TextEdit.RichText
+    topInset: 0
+    topPadding: 0
     wrapMode: Text.Wrap
 
     // Setting a tooltip delay makes the hover text empty .-.
@@ -40,8 +39,8 @@ TextArea {
     onLinkActivated: Nheko.openLink(link)
 
     // propagate events up
-    onPressAndHold: (event) => event.accepted = false
-    onPressed: (event) => event.accepted = (event.button == Qt.LeftButton)
+    onPressAndHold: event => event.accepted = false
+    onPressed: event => event.accepted = (event.button == Qt.LeftButton)
 
     NhekoCursorShape {
         id: cs
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 976312f2..2df9b731 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -59,7 +59,7 @@ Item {
         spacing: 2
         verticalLayoutDirection: ListView.BottomToTop
 
-        delegate: EventDelegateChooser {
+        delegate: TimelineEvent {
             id: wrapper
             ListView.delayRemove: true
             width: chat.delegateMaxWidth
@@ -69,7 +69,6 @@ Item {
 
             required property var day
             required property bool isSender
-            required property bool isStateEvent
             //required property var previousMessageDay
             //required property bool previousMessageIsStateEvent
             //required property string previousMessageUserId
@@ -145,6 +144,8 @@ Item {
                                 }
 
                                 ColumnLayout {
+                                    spacing: 0
+
                                     AbstractButton {
                                         id: replyUserButton
                                         Layout.fillWidth: true
@@ -222,314 +223,6 @@ Item {
                     opacity: 0.2
                 }
             ]
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.TextMessage,
-                    MtxEvent.NoticeMessage,
-                    MtxEvent.ElementEffectMessage,
-                    MtxEvent.UnknownMessage,
-                ]
-                TextMessage {
-                    keepFullText: true
-                    required property string userId
-                    required property string userName
-                    required property string formattedBody
-                    required property int type
-
-                    color: type == MtxEvent.NoticeMessage ? palette.buttonText : palette.text
-                    font.italic: type == MtxEvent.NoticeMessage
-                    formatted: formattedBody
-
-                    Layout.fillWidth: true
-                    //Layout.maximumWidth: implicitWidth
-
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.EmoteMessage,
-                ]
-                TextMessage {
-                    keepFullText: true
-                    required property string userId
-                    required property string userName
-                    required property string formattedBody
-
-                    formatted: TimelineManager.escapeEmoji(userName) + " " + formattedBody
-
-                    color: TimelineManager.userColor(userId, palette.base)
-                    font.italic: true
-
-                    Layout.fillWidth: true
-                    //Layout.maximumWidth: implicitWidth
-
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.CanonicalAlias,
-                    MtxEvent.ServerAcl,
-                    MtxEvent.Name,
-                    MtxEvent.Topic,
-                    MtxEvent.Avatar,
-                    MtxEvent.PinnedEvents,
-                    MtxEvent.ImagePackInRoom,
-                    MtxEvent.SpaceParent,
-                    MtxEvent.RoomCreate,
-                    MtxEvent.PowerLevels,
-                    MtxEvent.PolicyRuleUser,
-                    MtxEvent.PolicyRuleRoom,
-                    MtxEvent.PolicyRuleServer,
-                    MtxEvent.RoomJoinRules,
-                    MtxEvent.RoomHistoryVisibility,
-                    MtxEvent.RoomGuestAccess,
-                ]
-                TextMessage {
-                    keepFullText: true
-
-                    required property string userId
-                    required property string userName
-                    required property string formattedStateEvent
-
-                    isOnlyEmoji: false
-                    text: formattedStateEvent
-                    formatted: ''
-                    body: ''
-                    horizontalAlignment: Text.AlignHCenter
-
-                    color: palette.buttonText
-                    font.italic: true
-
-                    Layout.fillWidth: true
-                    //Layout.maximumWidth: implicitWidth
-
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.CallInvite,
-                ]
-                TextMessage {
-                    keepFullText: true
-
-                    required property string userId
-                    required property string userName
-                    required property string callType
-
-                    isOnlyEmoji: false
-                    body: formatted
-                    formatted: {
-                        switch (callType) {
-                            case "voice":
-                            return qsTr("%1 placed a voice call.").arg(TimelineManager.escapeEmoji(userName));
-                            case "video":
-                            return qsTr("%1 placed a video call.").arg(TimelineManager.escapeEmoji(userName));
-                            default:
-                            return qsTr("%1 placed a call.").arg(TimelineManager.escapeEmoji(userName));
-                        }
-                    }
-
-                    color: palette.buttonText
-                    font.italic: true
-
-                    Layout.fillWidth: true
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.CallAnswer,
-                    MtxEvent.CallReject,
-                    MtxEvent.CallSelectAnswer,
-                    MtxEvent.CallHangUp,
-                    MtxEvent.CallCandidates,
-                    MtxEvent.CallNegotiate,
-                ]
-                TextMessage {
-                    keepFullText: true
-
-                    required property string userId
-                    required property string userName
-                    required property int type
-
-                    isOnlyEmoji: false
-                    body: formatted
-                    formatted: {
-                        switch (type) {
-                            case MtxEvent.CallAnswer:
-                            return qsTr("%1 answered the call.").arg(TimelineManager.escapeEmoji(userName));
-                            case MtxEvent.CallReject:
-                            return qsTr("%1 rejected the call.").arg(TimelineManager.escapeEmoji(userName));
-                            case MtxEvent.CallSelectAnswer:
-                            return qsTr("%1 selected answer.").arg(TimelineManager.escapeEmoji(userName));
-                            case MtxEvent.CallHangUp:
-                            return qsTr("%1 ended the call.").arg(TimelineManager.escapeEmoji(userName));
-                            case MtxEvent.CallCandidates:
-                            return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
-                            case MtxEvent.CallNegotiate:
-                            return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
-                        }
-                    }
-
-                    color: palette.buttonText
-                    font.italic: true
-
-                    Layout.fillWidth: true
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.ImageMessage,
-                    MtxEvent.Sticker,
-                ]
-                ImageMessage {
-                    Layout.fillWidth: true
-
-                    containerHeight: timelineView.height
-                    Layout.maximumWidth: tempWidth
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.FileMessage,
-                ]
-                FileMessage {
-                    Layout.fillWidth: true
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.VideoMessage,
-                    MtxEvent.AudioMessage,
-                ]
-                PlayableMediaMessage {
-                    Layout.fillWidth: true
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.Encrypted,
-                ]
-                Encrypted {
-                    Layout.fillWidth: true
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.Encryption,
-                ]
-                EncryptionEnabled {
-                    Layout.fillWidth: true
-                }
-            }
-
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.Redacted
-                ]
-
-                Redacted {
-                    Layout.fillWidth: true
-
-                    required property string userId
-                    required property string userName
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.Member
-                ]
-
-                ColumnLayout {
-                    id: member
-
-                    required property string userId
-                    required property string userName
-
-                    required property bool isReply
-                    required property Room room
-                    required property string formattedStateEvent
-
-                    NoticeMessage {
-                        body: formatted
-                        isOnlyEmoji: false
-                        isReply: tombstone.isReply
-                        keepFullText: true
-                        isStateEvent: true
-                        Layout.fillWidth: true
-                        formatted: member.formattedStateEvent
-                    }
-
-                    Button {
-                        visible: room.showAcceptKnockButton(eventId)
-                        Layout.alignment: Qt.AlignHCenter
-                        text: qsTr("Allow them in")
-                        onClicked: room.acceptKnock(member.eventId)
-                    }
-
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                    MtxEvent.Tombstone
-                ]
-
-                ColumnLayout {
-                    id: tombstone
-
-                    required property string userId
-                    required property string userName
-
-                    required property string body
-                    required property bool isReply
-                    required property Room room
-                    required property string eventId
-
-                    NoticeMessage {
-                        body: formatted
-                        isOnlyEmoji: false
-                        isReply: tombstone.isReply
-                        keepFullText: true
-                        isStateEvent: true
-                        Layout.fillWidth: true
-                        formatted: qsTr("This room was replaced for the following reason: %1").arg(tombstone.body)
-                    }
-
-                    Button {
-                        Layout.alignment: Qt.AlignHCenter
-                        text: qsTr("Go to replacement room")
-                        onClicked: tombstone.room.joinReplacementRoom(tombstone.eventId)
-                    }
-
-                }
-            }
-
-            EventDelegateChoice {
-                roleValues: [
-                ]
-                MatrixText {
-                    Layout.fillWidth: true
-
-                    required property string typeString
-
-                    text: "Unsupported: " + typeString
-
-                    required property string userId
-                    required property string userName
-                }
-            }
         }
         footer: Item {
             anchors.horizontalCenter: parent.horizontalCenter
diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml
index eff62fc1..5b994145 100644
--- a/resources/qml/Reactions.qml
+++ b/resources/qml/Reactions.qml
@@ -74,10 +74,10 @@ Flow {
                     anchors.verticalCenter: divider.verticalCenter
                     fillMode: Image.PreserveAspectFit
                     height: textMetrics.height
+                    mipmap: true
                     source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : ""
                     visible: modelData.key.startsWith("mxc://")
                     width: textMetrics.height
-                    mipmap: true
                 }
                 Rectangle {
                     id: divider
diff --git a/resources/qml/ReplyPopup.qml b/resources/qml/ReplyPopup.qml
index ce24297c..64c58e56 100644
--- a/resources/qml/ReplyPopup.qml
+++ b/resources/qml/ReplyPopup.qml
@@ -29,22 +29,8 @@ Rectangle {
         anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
         anchors.top: parent.top
         anchors.topMargin: Nheko.paddingSmall
-        blurhash: modelData.blurhash ?? ""
-        body: modelData.body ?? ""
-        encryptionError: modelData.encryptionError ?? 0
-        eventId: modelData.eventId ?? ""
-        filename: modelData.filename ?? ""
-        filesize: modelData.filesize ?? ""
-        formattedBody: modelData.formattedBody ?? ""
-        isOnlyEmoji: modelData.isOnlyEmoji ?? false
-        originalWidth: modelData.originalWidth ?? 0
-        proportionalHeight: modelData.proportionalHeight ?? 1
-        type: modelData.type ?? MtxEvent.UnknownMessage
-        typeString: modelData.typeString ?? ""
-        url: modelData.url ?? ""
+        eventId: room.reply ?? ""
         userColor: TimelineManager.userColor(modelData.userId, palette.window)
-        userId: modelData.userId ?? ""
-        userName: modelData.userName ?? ""
         visible: room && room.reply
         width: parent.width
     }
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index 20e5b95b..0c432189 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -728,9 +728,9 @@ Page {
             }
             Platform.MenuItem {
                 text: qsTr("Mark as read")
+
                 onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
             }
-
             Platform.MenuItem {
                 text: qsTr("Room settings")
 
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index 1e8a6a27..09a8f442 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -355,7 +355,6 @@ Pane {
 
         onAccepted: UIA.continue3pidReceived()
     }
-
     Connections {
         function onConfirm3pidToken() {
             uiaConfirmationLinkDialog.open();
@@ -363,6 +362,18 @@ Pane {
         function onEmail() {
             uiaEmailPrompt.show();
         }
+        function onFallbackAuth(fallback) {
+            var component = Qt.createComponent("qrc:/resources/qml/dialogs/FallbackAuthDialog.qml");
+            if (component.status == Component.Ready) {
+                var dialog = component.createObject(timelineRoot, {
+                        "fallback": fallback
+                    });
+                dialog.show();
+                destroyOnClose(dialog);
+            } else {
+                console.error("Failed to create component: " + component.errorString());
+            }
+        }
         function onPassword() {
             console.log("UIA: password needed");
             uiaPassPrompt.show();
@@ -385,18 +396,6 @@ Pane {
                 console.error("Failed to create component: " + component.errorString());
             }
         }
-        function onFallbackAuth(fallback) {
-            var component = Qt.createComponent("qrc:/resources/qml/dialogs/FallbackAuthDialog.qml");
-            if (component.status == Component.Ready) {
-                var dialog = component.createObject(timelineRoot, {
-                        "fallback": fallback
-                    });
-                dialog.show();
-                destroyOnClose(dialog);
-            } else {
-                console.error("Failed to create component: " + component.errorString());
-            }
-        }
 
         target: UIA
     }
diff --git a/resources/qml/TimelineEvent.qml b/resources/qml/TimelineEvent.qml
new file mode 100644
index 00000000..787fb7dc
--- /dev/null
+++ b/resources/qml/TimelineEvent.qml
@@ -0,0 +1,255 @@
+// SPDX-FileCopyrightText: Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import "./components"
+import "./delegates"
+import "./emoji"
+import "./ui"
+import "./dialogs"
+import Qt.labs.platform 1.1 as Platform
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.13
+import im.nheko 1.0
+
+EventDelegateChooser {
+    id: wrapper
+
+    required property bool isStateEvent
+
+    EventDelegateChoice {
+        roleValues: [MtxEvent.TextMessage, MtxEvent.NoticeMessage, MtxEvent.ElementEffectMessage, MtxEvent.UnknownMessage,]
+
+        TextMessage {
+            required property string formattedBody
+            required property int type
+            required property string userId
+            required property string userName
+
+            Layout.fillWidth: true
+            //Layout.maximumWidth: implicitWidth
+
+            color: type == MtxEvent.NoticeMessage ? palette.buttonText : palette.text
+            font.italic: type == MtxEvent.NoticeMessage
+            formatted: formattedBody
+            keepFullText: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.EmoteMessage,]
+
+        TextMessage {
+            required property string formattedBody
+            required property string userId
+            required property string userName
+
+            Layout.fillWidth: true
+            //Layout.maximumWidth: implicitWidth
+
+            color: TimelineManager.userColor(userId, palette.base)
+            font.italic: true
+            formatted: TimelineManager.escapeEmoji(userName) + " " + formattedBody
+            keepFullText: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.CanonicalAlias, MtxEvent.ServerAcl, MtxEvent.Name, MtxEvent.Topic, MtxEvent.Avatar, MtxEvent.PinnedEvents, MtxEvent.ImagePackInRoom, MtxEvent.SpaceParent, MtxEvent.RoomCreate, MtxEvent.PowerLevels, MtxEvent.PolicyRuleUser, MtxEvent.PolicyRuleRoom, MtxEvent.PolicyRuleServer, MtxEvent.RoomJoinRules, MtxEvent.RoomHistoryVisibility, MtxEvent.RoomGuestAccess,]
+
+        TextMessage {
+            required property string formattedStateEvent
+            required property string userId
+            required property string userName
+
+            Layout.fillWidth: true
+            //Layout.maximumWidth: implicitWidth
+
+            body: ''
+            color: palette.buttonText
+            font.italic: true
+            formatted: ''
+            horizontalAlignment: Text.AlignHCenter
+            isOnlyEmoji: false
+            keepFullText: true
+            text: formattedStateEvent
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.CallInvite,]
+
+        TextMessage {
+            required property string callType
+            required property string userId
+            required property string userName
+
+            Layout.fillWidth: true
+            body: formatted
+            color: palette.buttonText
+            font.italic: true
+            formatted: {
+                switch (callType) {
+                case "voice":
+                    return qsTr("%1 placed a voice call.").arg(TimelineManager.escapeEmoji(userName));
+                case "video":
+                    return qsTr("%1 placed a video call.").arg(TimelineManager.escapeEmoji(userName));
+                default:
+                    return qsTr("%1 placed a call.").arg(TimelineManager.escapeEmoji(userName));
+                }
+            }
+            isOnlyEmoji: false
+            keepFullText: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.CallAnswer, MtxEvent.CallReject, MtxEvent.CallSelectAnswer, MtxEvent.CallHangUp, MtxEvent.CallCandidates, MtxEvent.CallNegotiate,]
+
+        TextMessage {
+            required property int type
+            required property string userId
+            required property string userName
+
+            Layout.fillWidth: true
+            body: formatted
+            color: palette.buttonText
+            font.italic: true
+            formatted: {
+                switch (type) {
+                case MtxEvent.CallAnswer:
+                    return qsTr("%1 answered the call.").arg(TimelineManager.escapeEmoji(userName));
+                case MtxEvent.CallReject:
+                    return qsTr("%1 rejected the call.").arg(TimelineManager.escapeEmoji(userName));
+                case MtxEvent.CallSelectAnswer:
+                    return qsTr("%1 selected answer.").arg(TimelineManager.escapeEmoji(userName));
+                case MtxEvent.CallHangUp:
+                    return qsTr("%1 ended the call.").arg(TimelineManager.escapeEmoji(userName));
+                case MtxEvent.CallCandidates:
+                    return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
+                case MtxEvent.CallNegotiate:
+                    return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
+                }
+            }
+            isOnlyEmoji: false
+            keepFullText: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.ImageMessage, MtxEvent.Sticker,]
+
+        ImageMessage {
+            Layout.fillWidth: true
+            Layout.maximumWidth: tempWidth
+            containerHeight: timelineView.height
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.FileMessage,]
+
+        FileMessage {
+            Layout.fillWidth: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.VideoMessage, MtxEvent.AudioMessage,]
+
+        PlayableMediaMessage {
+            Layout.fillWidth: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.Encrypted,]
+
+        Encrypted {
+            Layout.fillWidth: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.Encryption,]
+
+        EncryptionEnabled {
+            Layout.fillWidth: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.Redacted]
+
+        Redacted {
+            required property string userId
+            required property string userName
+
+            Layout.fillWidth: true
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.Member]
+
+        ColumnLayout {
+            id: member
+
+            required property string formattedStateEvent
+            required property bool isReply
+            required property Room room
+            required property string userId
+            required property string userName
+
+            NoticeMessage {
+                Layout.fillWidth: true
+                body: formatted
+                formatted: member.formattedStateEvent
+                isOnlyEmoji: false
+                isReply: member.isReply
+                isStateEvent: true
+                keepFullText: true
+            }
+            Button {
+                Layout.alignment: Qt.AlignHCenter
+                text: qsTr("Allow them in")
+                visible: room.showAcceptKnockButton(eventId)
+
+                onClicked: room.acceptKnock(member.eventId)
+            }
+        }
+    }
+    EventDelegateChoice {
+        roleValues: [MtxEvent.Tombstone]
+
+        ColumnLayout {
+            id: tombstone
+
+            required property string body
+            required property string eventId
+            required property bool isReply
+            required property Room room
+            required property string userId
+            required property string userName
+
+            NoticeMessage {
+                Layout.fillWidth: true
+                body: formatted
+                formatted: qsTr("This room was replaced for the following reason: %1").arg(tombstone.body)
+                isOnlyEmoji: false
+                isReply: tombstone.isReply
+                isStateEvent: true
+                keepFullText: true
+            }
+            Button {
+                Layout.alignment: Qt.AlignHCenter
+                text: qsTr("Go to replacement room")
+
+                onClicked: tombstone.room.joinReplacementRoom(tombstone.eventId)
+            }
+        }
+    }
+    EventDelegateChoice {
+        roleValues: []
+
+        MatrixText {
+            required property string typeString
+            required property string userId
+            required property string userName
+
+            Layout.fillWidth: true
+            text: "Unsupported: " + typeString
+        }
+    }
+}
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 64fa80b1..539882ed 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -147,7 +147,8 @@ AbstractButton {
             columns: Settings.bubbles ? 1 : 2
             rowSpacing: 0
             rows: Settings.bubbles ? 3 : 2
-/*
+
+            /*
             anchors {
                 left: parent.left
                 leftMargin: 4
diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml
index 699595e6..4c70348b 100644
--- a/resources/qml/TopBar.qml
+++ b/resources/qml/TopBar.qml
@@ -286,24 +286,9 @@ Pane {
                             property var e: room ? room.getDump(modelData, "pins") : {}
 
                             Layout.fillWidth: true
-                            Layout.preferredHeight: height
-                            blurhash: e.blurhash ?? ""
-                            body: e.body ?? ""
-                            encryptionError: e.encryptionError ?? 0
+                            //Layout.preferredHeight: height
                             eventId: e.eventId ?? ""
-                            filename: e.filename ?? ""
-                            filesize: e.filesize ?? ""
-                            formattedBody: e.formattedBody ?? ""
-                            isOnlyEmoji: e.isOnlyEmoji ?? false
-                            keepFullText: true
-                            originalWidth: e.originalWidth ?? 0
-                            proportionalHeight: e.proportionalHeight ?? 1
-                            type: e.type ?? MtxEvent.UnknownMessage
-                            typeString: e.typeString ?? ""
-                            url: e.url ?? ""
                             userColor: TimelineManager.userColor(e.userId, palette.window)
-                            userId: e.userId ?? ""
-                            userName: e.userName ?? ""
 
                             Connections {
                                 function onPinnedMessagesChanged() {
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index 64eb65a3..55a376f7 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -14,102 +14,96 @@ AbstractButton {
     id: r
 
     property color userColor: "red"
-    property double proportionalHeight
-    property int type
-    property string typeString
-    property int originalWidth
-    property string blurhash
-    property string body
-    property string formattedBody
-    property string eventId
-    property string filename
-    property string filesize
-    property string url
-    property bool isOnlyEmoji
-    property bool isStateEvent
-    property string userId
-    property string userName
-    property string thumbnailUrl
-    property string roomTopic
-    property string roomName
-    property string callType
-    property int duration
-    property int encryptionError
-    property int relatedEventCacheBuster
-    property int maxWidth
     property bool keepFullText: false
 
-    height: replyContainer.height
-    implicitHeight: replyContainer.height
-    implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues
+    required property string eventId
+
+    property var room_: room
+
+    property string userId: eventId ? room.dataById(eventId, Room.UserId, "") : ""
+    property string userName: eventId ? room.dataById(eventId, Room.UserName, "") : ""
+    implicitHeight: replyContainer.implicitHeight
+    implicitWidth: replyContainer.implicitWidth
 
     NhekoCursorShape {
         anchors.fill: parent
         cursorShape: Qt.PointingHandCursor
     }
 
-    Rectangle {
-        id: colorLine
-
-        anchors.top: replyContainer.top
-        anchors.bottom: replyContainer.bottom
-        width: 4
-        color: TimelineManager.userColor(userId, palette.base)
-    }
-
     onClicked: {
-        let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX-colorLine.width, pressY - userName_.implicitHeight);
+        let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX-colorline.width, pressY - userName_.implicitHeight);
         if (link) {
             Nheko.openLink(link)
         } else {
             room.showEvent(r.eventId)
         }
     }
-    onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX-colorLine.width, pressY - userName_.implicitHeight), r.eventId)
+    onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX-colorline.width, pressY - userName_.implicitHeight), r.eventId)
 
-    ColumnLayout {
-        id: replyContainer
+    contentItem: TimelineEvent {
+        id: timelineEvent
 
-        anchors.left: colorLine.right
-        width: parent.width - 4
-        spacing: 0
+        isStateEvent: false
+        room: room_
+        eventId: r.eventId
+        replyTo: ""
 
-        TapHandler {
-            acceptedButtons: Qt.RightButton
-            onSingleTapped: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight), r.eventId)
-            gesturePolicy: TapHandler.ReleaseWithinBounds
-            acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
-        }
+        width: parent.width
+        height: replyContainer.implicitHeight
+
+        //height: replyContainer.implicitHeight
+        data: GridLayout {
+            id: replyContainer
+
+            width: parent.width
+            columns: 2
+            rows: 2
+            columnSpacing: Nheko.paddingMedium
+            rowSpacing: Nheko.paddingSmall
 
-        AbstractButton {
-            Layout.leftMargin: 4
-            Layout.fillWidth: true
-            contentItem: ElidedLabel {
-                id: userName_
-                fullText: userName
-                color: r.userColor
-                textFormat: Text.RichText
-                width: parent.width
-                elideWidth: width
+            Rectangle {
+                id: colorline
+
+                Layout.preferredWidth: 4
+                Layout.rowSpan: 2
+                Layout.fillHeight: true
+
+                Layout.row: 0
+                Layout.column: 0
+
+                color: TimelineManager.userColor(r.userId, palette.base)
             }
-            onClicked: room.openUserProfile(userId)
-        }
 
-        Rectangle {
-            Layout.leftMargin: 4
-            Layout.preferredHeight: 20
-            Layout.fillWidth: true
-            color: "green"
-        }
+            AbstractButton {
+                id: usernameBtn
+                Layout.fillWidth: true
+
+                Layout.row: 0
+                Layout.column: 1
+
+                contentItem: ElidedLabel {
+                    id: userName_
+                    fullText: r.userName
+                    color: r.userColor
+                    textFormat: Text.RichText
+                    width: parent.width
+                    elideWidth: width
+                }
+                onClicked: room.openUserProfile(r.userId)
+            }
 
+            data: [
+                colorline, usernameBtn, timelineEvent.main,
+            ]
+
+        }
     }
 
-    Rectangle {
+    background: Rectangle {
         id: backgroundItem
 
         z: -1
-        anchors.fill: replyContainer
-        property color userColor: TimelineManager.userColor(userId, palette.base)
+        property color userColor: TimelineManager.userColor(r.userId, palette.base)
         property color bgColor: palette.base
         color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
     }