diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index 2c2ed02a..86780413 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -14,23 +14,70 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right - height: Math.max(contentItem.height, 16) + //height: Math.max(model.replyTo ? reply.height + contentItem.height + 4 : contentItem.height, 16) Column { Layout.fillWidth: true Layout.alignment: Qt.AlignTop + spacing: 4 - //property var replyTo: model.replyTo + // fancy reply, if this is a reply + Rectangle { + visible: model.replyTo + width: parent.width + height: replyContainer.height - //Text { - // property int idx: timelineManager.timeline.idToIndex(replyTo) - // text: "" + (idx != -1 ? timelineManager.timeline.data(timelineManager.timeline.index(idx, 0), 2) : "nothing") - //} + Rectangle { + id: colorLine + height: replyContainer.height + width: 4 + color: chat.model.userColor(reply.modelData.userId, colors.window) + } + + Column { + id: replyContainer + anchors.left: colorLine.right + anchors.leftMargin: 4 + width: parent.width - 8 + + + Text { + id: userName + text: chat.model.escapeEmoji(reply.modelData.userName) + color: chat.model.userColor(reply.modelData.userId, colors.window) + textFormat: Text.RichText + + MouseArea { + anchors.fill: parent + onClicked: chat.model.openUserProfile(reply.modelData.userId) + cursorShape: Qt.PointingHandCursor + } + } + + MessageDelegate { + id: reply + width: parent.width + + modelData: chat.model.getDump(model.replyTo) + } + } + + color: { var col = chat.model.userColor(reply.modelData.userId, colors.window); col.a = 0.2; return col } + + MouseArea { + anchors.fill: parent + onClicked: chat.positionViewAtIndex(chat.model.idToIndex(model.replyTo), ListView.Contain) + cursorShape: Qt.PointingHandCursor + } + } + + // actual message content MessageDelegate { id: contentItem width: parent.width - height: childrenRect.height + + modelData: model } } diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml index 2c911c5e..9a5300bb 100644 --- a/resources/qml/delegates/FileMessage.qml +++ b/resources/qml/delegates/FileMessage.qml @@ -31,7 +31,7 @@ Rectangle { } MouseArea { anchors.fill: parent - onClicked: timelineManager.timeline.saveMedia(model.id) + onClicked: timelineManager.timeline.saveMedia(model.data.id) cursorShape: Qt.PointingHandCursor } } @@ -40,14 +40,14 @@ Rectangle { Text { Layout.fillWidth: true - text: model.body + text: model.data.body textFormat: Text.PlainText elide: Text.ElideRight color: colors.text } Text { Layout.fillWidth: true - text: model.filesize + text: model.data.filesize textFormat: Text.PlainText elide: Text.ElideRight color: colors.text diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml index 15ce29b7..3393f043 100644 --- a/resources/qml/delegates/ImageMessage.qml +++ b/resources/qml/delegates/ImageMessage.qml @@ -3,26 +3,26 @@ import QtQuick 2.6 import im.nheko 1.0 Item { - property double tempWidth: Math.min(parent ? parent.width : undefined, model.width) - property double tempHeight: tempWidth * model.proportionalHeight + property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width) + property double tempHeight: tempWidth * model.data.proportionalHeight property bool tooHigh: tempHeight > chat.height - 40 height: tooHigh ? chat.height - 40 : tempHeight - width: tooHigh ? (chat.height - 40) / model.proportionalHeight : tempWidth + width: tooHigh ? (chat.height - 40) / model.data.proportionalHeight : tempWidth Image { id: img anchors.fill: parent - source: model.url.replace("mxc://", "image://MxcImage/") + source: model.data.url.replace("mxc://", "image://MxcImage/") asynchronous: true fillMode: Image.PreserveAspectFit MouseArea { - enabled: model.type == MtxEvent.ImageMessage + enabled: model.data.type == MtxEvent.ImageMessage anchors.fill: parent - onClicked: timelineManager.openImageOverlay(model.url, model.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 20ec71e5..1716d2d4 100644 --- a/resources/qml/delegates/MessageDelegate.qml +++ b/resources/qml/delegates/MessageDelegate.qml @@ -1,67 +1,81 @@ import QtQuick 2.6 import im.nheko 1.0 -DelegateChooser { - //role: "type" //< not supported in our custom implementation, have to use roleValue - roleValue: model.type +Item { + // Workaround to have an assignable global property + Item { + id: model + property var data; + } + + property alias modelData: model.data - DelegateChoice { - roleValue: MtxEvent.TextMessage - TextMessage {} - } - DelegateChoice { - roleValue: MtxEvent.NoticeMessage - NoticeMessage {} - } - DelegateChoice { - roleValue: MtxEvent.EmoteMessage - TextMessage {} - } - DelegateChoice { - roleValue: MtxEvent.ImageMessage - ImageMessage {} - } - DelegateChoice { - roleValue: MtxEvent.Sticker - ImageMessage {} - } - DelegateChoice { - roleValue: MtxEvent.FileMessage - FileMessage {} - } - DelegateChoice { - roleValue: MtxEvent.VideoMessage - PlayableMediaMessage {} - } - DelegateChoice { - roleValue: MtxEvent.AudioMessage - PlayableMediaMessage {} - } - DelegateChoice { - roleValue: MtxEvent.Redacted - Pill { - text: qsTr("redacted") + height: chooser.childrenRect.height + + DelegateChooser { + id: chooser + //role: "type" //< not supported in our custom implementation, have to use roleValue + roleValue: model.data.type + anchors.fill: parent + + DelegateChoice { + roleValue: MtxEvent.TextMessage + TextMessage {} } - } - DelegateChoice { - roleValue: MtxEvent.Encryption - Pill { - text: qsTr("Encryption enabled") + DelegateChoice { + roleValue: MtxEvent.NoticeMessage + NoticeMessage {} } - } - DelegateChoice { - roleValue: MtxEvent.Name - NoticeMessage { - notice: model.roomName ? qsTr("room name changed to: %1").arg(model.roomName) : qsTr("removed room name") + DelegateChoice { + roleValue: MtxEvent.EmoteMessage + TextMessage {} } - } - DelegateChoice { - roleValue: MtxEvent.Topic - NoticeMessage { - notice: model.roomTopic ? qsTr("topic changed to: %1").arg(model.roomTopic) : qsTr("removed topic") + DelegateChoice { + roleValue: MtxEvent.ImageMessage + ImageMessage {} + } + DelegateChoice { + roleValue: MtxEvent.Sticker + ImageMessage {} + } + DelegateChoice { + roleValue: MtxEvent.FileMessage + FileMessage {} + } + DelegateChoice { + roleValue: MtxEvent.VideoMessage + PlayableMediaMessage {} + } + DelegateChoice { + roleValue: MtxEvent.AudioMessage + PlayableMediaMessage {} + } + DelegateChoice { + roleValue: MtxEvent.Redacted + Pill { + text: qsTr("redacted") + } + } + DelegateChoice { + roleValue: MtxEvent.Encryption + Pill { + text: qsTr("Encryption enabled") + } + } + DelegateChoice { + roleValue: MtxEvent.Name + NoticeMessage { + notice: model.data.roomName ? qsTr("room name changed to: %1").arg(model.data.roomName) : qsTr("removed room name") + } + } + DelegateChoice { + roleValue: MtxEvent.Topic + NoticeMessage { + notice: model.data.roomTopic ? qsTr("topic changed to: %1").arg(model.data.roomTopic) : qsTr("removed topic") + } + } + DelegateChoice { + Placeholder {} } - } - DelegateChoice { - Placeholder {} } } diff --git a/resources/qml/delegates/NoticeMessage.qml b/resources/qml/delegates/NoticeMessage.qml index f7467eca..34132bcf 100644 --- a/resources/qml/delegates/NoticeMessage.qml +++ b/resources/qml/delegates/NoticeMessage.qml @@ -1,7 +1,7 @@ import ".." MatrixText { - property string notice: model.formattedBody.replace("
", "")
+ property string notice: model.data.formattedBody.replace("", "")
text: notice
width: parent ? parent.width : undefined
font.italic: true
diff --git a/resources/qml/delegates/Placeholder.qml b/resources/qml/delegates/Placeholder.qml
index 4c0e68c3..36d7b2bc 100644
--- a/resources/qml/delegates/Placeholder.qml
+++ b/resources/qml/delegates/Placeholder.qml
@@ -1,7 +1,7 @@
import ".."
MatrixText {
- text: qsTr("unimplemented event: ") + model.type
+ text: qsTr("unimplemented event: ") + model.data.type
width: parent ? parent.width : undefined
color: inactiveColors.text
}
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index b3275462..ebf7487c 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -19,12 +19,12 @@ Rectangle {
Rectangle {
id: videoContainer
- visible: model.type == MtxEvent.VideoMessage
- width: Math.min(parent.width, model.width ? model.width : 400) // some media has 0 as size...
- height: width*model.proportionalHeight
+ visible: model.data.type == MtxEvent.VideoMessage
+ width: Math.min(parent.width, model.data.width ? model.data.width : 400) // some media has 0 as size...
+ height: width*model.data.proportionalHeight
Image {
anchors.fill: parent
- source: model.thumbnailUrl.replace("mxc://", "image://MxcImage/")
+ source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
asynchronous: true
fillMode: Image.PreserveAspectFit
@@ -97,7 +97,7 @@ Rectangle {
anchors.fill: parent
onClicked: {
switch (button.state) {
- case "": timelineManager.timeline.cacheMedia(model.id); break;
+ case "": timelineManager.timeline.cacheMedia(model.data.id); break;
case "stopped":
media.play(); console.log("play");
button.state = "playing"
@@ -120,7 +120,7 @@ Rectangle {
Connections {
target: timelineManager.timeline
onMediaCached: {
- if (mxcUrl == model.url) {
+ if (mxcUrl == model.data.url) {
media.source = "file://" + cacheUrl
button.state = "stopped"
console.log("media loaded: " + mxcUrl + " at " + cacheUrl)
@@ -145,14 +145,14 @@ Rectangle {
Text {
Layout.fillWidth: true
- text: model.body
+ text: model.data.body
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
Text {
Layout.fillWidth: true
- text: model.filesize
+ text: model.data.filesize
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index f984b32f..92ba560b 100644
--- a/resources/qml/delegates/TextMessage.qml
+++ b/resources/qml/delegates/TextMessage.qml
@@ -1,6 +1,6 @@
import ".."
MatrixText {
- text: model.formattedBody.replace("", "")
+ text: model.data.formattedBody.replace("", "")
width: parent ? parent.width : undefined
}
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 0e7f5259..41d864bd 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -212,6 +212,14 @@ TimelineModel::rowCount(const QModelIndex &parent) const
return (int)this->eventOrder.size();
}
+QVariantMap
+TimelineModel::getDump(QString eventId) const
+{
+ if (events.contains(eventId))
+ return data(index(idToIndex(eventId), 0), Dump).toMap();
+ return {};
+}
+
QVariant
TimelineModel::data(const QModelIndex &index, int role) const
{
@@ -263,11 +271,13 @@ TimelineModel::data(const QModelIndex &index, int role) const
return QVariant(toRoomEventType(event));
case Body:
return QVariant(utils::replaceEmoji(QString::fromStdString(body(event))));
- case FormattedBody:
+ case FormattedBody: {
+ const static QRegularExpression replyFallback(
+ ".* ", QRegularExpression::DotMatchesEverythingOption);
return QVariant(
utils::replaceEmoji(utils::linkifyMessage(formattedBodyWithFallback(event)))
- .remove("")
- .remove(" "));
+ .remove(replyFallback));
+ }
case Url:
return QVariant(QString::fromStdString(url(event)));
case ThumbnailUrl:
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 0f18f7ef..61dd6b69 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -181,6 +181,7 @@ public slots:
void setCurrentIndex(int index);
int currentIndex() const { return idToIndex(currentId); }
void markEventsAsRead(const std::vector &event_ids);
+ QVariantMap getDump(QString eventId) const;
private slots:
// Add old events at the top of the timeline.