Merge branch 'message-bubbles' of https://github.com/maltee1/nheko into maltee1-message-bubbles

This commit is contained in:
Nicolas Werner 2022-02-14 15:43:17 +01:00
commit ddcd4850f1
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
18 changed files with 303 additions and 128 deletions

View File

@ -33,7 +33,7 @@ ScrollView {
//reuseItems: true //reuseItems: true
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
pixelAligned: true pixelAligned: true
spacing: 4 spacing: 2
verticalLayoutDirection: ListView.BottomToTop verticalLayoutDirection: ListView.BottomToTop
onCountChanged: { onCountChanged: {
// Mark timeline as read // Mark timeline as read
@ -249,12 +249,12 @@ ScrollView {
id: sectionHeader id: sectionHeader
Column { Column {
topPadding: 4 topPadding: userName_.visible? 4: 0
bottomPadding: 4 bottomPadding: Settings.bubbles? (isSender? 0 : 2) : 3
spacing: 8 spacing: 8
visible: (previousMessageUserId !== userId || previousMessageDay !== day) visible: (previousMessageUserId !== userId || previousMessageDay !== day || isStateEvent !== previousMessageIsStateEvent)
width: parentWidth width: parentWidth
height: ((previousMessageDay !== day) ? dateBubble.height + 8 + userName.height : userName.height) + 8 height: ((previousMessageDay !== day) ? dateBubble.height : 0) + (isStateEvent? 0 : userName.height +8 )
Label { Label {
id: dateBubble id: dateBubble
@ -278,18 +278,19 @@ ScrollView {
Row { Row {
height: userName_.height height: userName_.height
spacing: 8 spacing: 8
visible: !isStateEvent && (!isSender || !Settings.bubbles)
Avatar { Avatar {
id: messageUserAvatar id: messageUserAvatar
width: Nheko.avatarSize width: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1)
height: Nheko.avatarSize height: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1)
url: !room ? "" : room.avatarUrl(userId).replace("mxc://", "image://MxcImage/") url: !room ? "" : room.avatarUrl(userId).replace("mxc://", "image://MxcImage/")
displayName: userName displayName: userName
userid: userId userid: userId
onClicked: room.openUserProfile(userId) onClicked: room.openUserProfile(userId)
ToolTip.visible: avatarHover.hovered ToolTip.visible: avatarHover.hovered
ToolTip.delay: Nheko.tooltipDelay ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: userid ToolTip.text: userid
HoverHandler { HoverHandler {
@ -317,7 +318,7 @@ ScrollView {
color: TimelineManager.userColor(userId, Nheko.colors.base) color: TimelineManager.userColor(userId, Nheko.colors.base)
textFormat: Text.RichText textFormat: Text.RichText
ToolTip.visible: displayNameHover.hovered ToolTip.visible: displayNameHover.hovered
ToolTip.delay: Nheko.tooltipDelay ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: userId ToolTip.text: userId
TapHandler { TapHandler {
@ -379,6 +380,8 @@ ScrollView {
required property bool isEncrypted required property bool isEncrypted
required property bool isEditable required property bool isEditable
required property bool isEdited required property bool isEdited
required property bool isStateEvent
required property bool previousMessageIsStateEvent
required property string replyTo required property string replyTo
required property string userId required property string userId
required property string roomTopic required property string roomTopic
@ -455,11 +458,14 @@ ScrollView {
property string previousMessageUserId: wrapper.previousMessageUserId property string previousMessageUserId: wrapper.previousMessageUserId
property string day: wrapper.day property string day: wrapper.day
property string previousMessageDay: wrapper.previousMessageDay property string previousMessageDay: wrapper.previousMessageDay
property bool previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent
property bool isStateEvent: wrapper.isStateEvent
property bool isSender: wrapper.isSender
property string userName: wrapper.userName property string userName: wrapper.userName
property date timestamp: wrapper.timestamp property date timestamp: wrapper.timestamp
z: 4 z: 4
active: previousMessageUserId !== undefined && previousMessageUserId !== userId || previousMessageDay !== day active: previousMessageUserId !== undefined && previousMessageUserId !== userId || previousMessageDay !== day || previousMessageIsStateEvent !== isStateEvent
//asynchronous: true //asynchronous: true
sourceComponent: sectionHeader sourceComponent: sectionHeader
visible: status == Loader.Ready visible: status == Loader.Ready
@ -487,6 +493,7 @@ ScrollView {
isEncrypted: wrapper.isEncrypted isEncrypted: wrapper.isEncrypted
isEditable: wrapper.isEditable isEditable: wrapper.isEditable
isEdited: wrapper.isEdited isEdited: wrapper.isEdited
isStateEvent: wrapper.isStateEvent
replyTo: wrapper.replyTo replyTo: wrapper.replyTo
userId: wrapper.userId userId: wrapper.userId
userName: wrapper.userName userName: wrapper.userName

View File

@ -47,6 +47,7 @@ Rectangle {
userId: modelData.userId ?? "" userId: modelData.userId ?? ""
userName: modelData.userName ?? "" userName: modelData.userName ?? ""
encryptionError: modelData.encryptionError ?? "" encryptionError: modelData.encryptionError ?? ""
width: parent.width
} }
ImageButton { ImageButton {

View File

@ -31,6 +31,7 @@ Item {
required property bool isEncrypted required property bool isEncrypted
required property bool isEditable required property bool isEditable
required property bool isEdited required property bool isEdited
required property bool isStateEvent
required property string replyTo required property string replyTo
required property string userId required property string userId
required property string userName required property string userName
@ -44,9 +45,8 @@ Item {
required property int status required property int status
required property int relatedEventCacheBuster required property int relatedEventCacheBuster
anchors.left: parent.left width: parent.width
anchors.right: parent.right height: childrenRect.height
height: row.height
Rectangle { Rectangle {
color: (Settings.messageHoverHighlight && hoverHandler.hovered) ? Nheko.colors.alternateBase : "transparent" color: (Settings.messageHoverHighlight && hoverHandler.hovered) ? Nheko.colors.alternateBase : "transparent"
@ -71,27 +71,48 @@ Item {
gesturePolicy: TapHandler.ReleaseWithinBounds gesturePolicy: TapHandler.ReleaseWithinBounds
} }
RowLayout { Control {
id: row id: row
property bool bubbleOnRight : isSender && Settings.bubbles
property int bubblePadding: (parent.width-(Settings.smallAvatars? 0 : Nheko.avatarSize+8))/10
anchors.rightMargin: isSender || !Settings.bubbles? 0 : bubblePadding
anchors.leftMargin: (Settings.smallAvatars? 0 : Nheko.avatarSize+8) + (bubbleOnRight? bubblePadding : 0) // align bubble with section header
anchors.left: bubbleOnRight? undefined : parent.left
anchors.right: bubbleOnRight? parent.right : undefined
property int maxWidth: parent.width-anchors.leftMargin-anchors.rightMargin
width: Settings.bubbles? Math.min(maxWidth,implicitWidth+4) : maxWidth
leftPadding: 4
rightPadding: (Settings.bubbles && !isStateEvent)? 4: 2
topPadding: (Settings.bubbles && !isStateEvent)? 4: 2
bottomPadding: topPadding
background: Rectangle {
property color userColor: TimelineManager.userColor(userId, Nheko.colors.base)
property color bgColor: Nheko.colors.base
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2))
radius: 4
visible: Settings.bubbles && !isStateEvent
}
anchors.rightMargin: 1 contentItem: GridLayout {
anchors.leftMargin: Nheko.avatarSize + 16 id: msg
anchors.left: parent.left rowSpacing: 0
anchors.right: parent.right columnSpacing: 2
columns: Settings.bubbles? 1 : 2
Column { rows: Settings.bubbles? 3 : 2
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
spacing: 4
Layout.topMargin: 1
Layout.bottomMargin: 1
// fancy reply, if this is a reply // fancy reply, if this is a reply
Reply { Reply {
Layout.row: 0
Layout.column: 0
Layout.fillWidth: true
Layout.bottomMargin: visible? 2 : 0
Layout.preferredHeight: height
Layout.maximumWidth: implicitWidth
id: reply
function fromModel(role) { function fromModel(role) {
return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null; return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null;
} }
visible: replyTo visible: replyTo
userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, Nheko.colors.base) userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, Nheko.colors.base)
blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? "" blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
@ -106,6 +127,7 @@ Item {
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? "" url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0 originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? "" userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? "" userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? "" thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
@ -118,9 +140,13 @@ Item {
// actual message content // actual message content
MessageDelegate { MessageDelegate {
Layout.row: 1
Layout.column: 0
Layout.fillWidth: true
Layout.preferredHeight: height
Layout.maximumWidth: implicitWidth
id: contentItem id: contentItem
width: parent.width
blurhash: r.blurhash blurhash: r.blurhash
body: r.body body: r.body
formattedBody: r.formattedBody formattedBody: r.formattedBody
@ -134,6 +160,7 @@ Item {
thumbnailUrl: r.thumbnailUrl thumbnailUrl: r.thumbnailUrl
originalWidth: r.originalWidth originalWidth: r.originalWidth
isOnlyEmoji: r.isOnlyEmoji isOnlyEmoji: r.isOnlyEmoji
isStateEvent: r.isStateEvent
userId: r.userId userId: r.userId
userName: r.userName userName: r.userName
roomTopic: r.roomTopic roomTopic: r.roomTopic
@ -144,67 +171,82 @@ Item {
isReply: false isReply: false
} }
Reactions { RowLayout {
id: reactionRow id: metadata
Layout.column: Settings.bubbles? 0 : 1
Layout.row: Settings.bubbles? 2 : 0
Layout.rowSpan: Settings.bubbles? 1 : 2
Layout.bottomMargin: -2
Layout.alignment: Qt.AlignTop | Qt.AlignRight
Layout.preferredWidth: implicitWidth
visible: !isStateEvent
reactions: r.reactions property double scaling: Settings.bubbles? 0.75 : 1
eventId: r.eventId
StatusIndicator {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16*parent.scaling
Layout.preferredWidth: 16*parent.scaling
status: r.status
eventId: r.eventId
}
Image {
visible: isEdited || eventId == chat.model.edit
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16*parent.scaling
Layout.preferredWidth: 16*parent.scaling
sourceSize.width: 16 * Screen.devicePixelRatio*parent.scaling
sourceSize.height: 16 * Screen.devicePixelRatio*parent.scaling
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == chat.model.edit) ? Nheko.colors.highlight : Nheko.colors.buttonText)
ToolTip.visible: editHovered.hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Edited")
HoverHandler {
id: editHovered
}
}
EncryptionIndicator {
visible: room.isEncrypted
encrypted: isEncrypted
trust: trustlevel
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16*parent.scaling
Layout.preferredWidth: 16*parent.scaling
sourceSize.width: 16 * Screen.devicePixelRatio*parent.scaling
sourceSize.height: 16 * Screen.devicePixelRatio*parent.scaling
}
Label {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredWidth: implicitWidth
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
color: Nheko.inactiveColors.text
ToolTip.visible: ma.hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
font.pointSize: 10*parent.scaling
HoverHandler {
id: ma
}
}
} }
} }
StatusIndicator {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
status: r.status
eventId: r.eventId
}
Image {
visible: isEdited || eventId == chat.model.edit
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
Layout.preferredWidth: 16
height: 16
width: 16
sourceSize.width: 16 * Screen.devicePixelRatio
sourceSize.height: 16 * Screen.devicePixelRatio
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == chat.model.edit) ? Nheko.colors.highlight : Nheko.colors.buttonText)
ToolTip.visible: editHovered.hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Edited")
HoverHandler {
id: editHovered
}
}
EncryptionIndicator {
visible: room.isEncrypted
encrypted: isEncrypted
trust: trustlevel
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
Layout.preferredWidth: 16
}
Label {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
color: Nheko.inactiveColors.text
ToolTip.visible: ma.hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
HoverHandler {
id: ma
}
}
} }
Reactions {
anchors {
top: row.bottom
topMargin: -2
left: row.left
}
id: reactionRow
reactions: r.reactions
eventId: r.eventId
}
} }

View File

@ -16,7 +16,8 @@ Rectangle {
required property string eventId required property string eventId
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
width: parent.width width: parent.width? parent.width : 0
implicitWidth: encryptedText.implicitWidth+24+Nheko.paddingMedium*3 // Column doesn't provide a useful implicitWidth, should be replaced by ColumnLayout
height: contents.implicitHeight + Nheko.paddingMedium * 2 height: contents.implicitHeight + Nheko.paddingMedium * 2
color: Nheko.colors.alternateBase color: Nheko.colors.alternateBase
@ -39,6 +40,7 @@ Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
MatrixText { MatrixText {
id: encryptedText
text: { text: {
switch (encryptionError) { switch (encryptionError) {
case Olm.MissingSession: case Olm.MissingSession:

View File

@ -14,6 +14,7 @@ Item {
height: row.height + 24 height: row.height + 24
width: parent.width width: parent.width
implicitWidth: row.implicitWidth
RowLayout { RowLayout {
id: row id: row
@ -86,8 +87,7 @@ Item {
color: Nheko.colors.alternateBase color: Nheko.colors.alternateBase
z: -1 z: -1
radius: 10 radius: 10
height: row.height + 24 anchors.fill: parent
width: 44 + 24 + 24 + Math.max(Math.min(filesize_.width, filesize_.implicitWidth), Math.min(filename_.width, filename_.implicitWidth))
} }
} }

View File

@ -17,13 +17,11 @@ Item {
required property string filename required property string filename
required property bool isReply required property bool isReply
required property string eventId required property string eventId
property double tempWidth: Math.min(parent.width, originalWidth < 1 ? 200 : originalWidth)
property double tempHeight: tempWidth * proportionalHeight
property double divisor: isReply ? 5 : 3 property double divisor: isReply ? 5 : 3
property bool tooHigh: tempHeight > timelineView.height / divisor
height: Math.round(tooHigh ? timelineView.height / divisor : tempHeight) implicitWidth: Math.round(originalWidth*Math.min((timelineView.height/divisor)/(originalWidth*proportionalHeight), 1))
width: Math.round(tooHigh ? (timelineView.height / divisor) / proportionalHeight : tempWidth) width: parent.width
height: width*proportionalHeight
Image { Image {
id: blurhash_ id: blurhash_

View File

@ -13,7 +13,7 @@ Item {
required property bool isReply required property bool isReply
property alias child: chooser.child property alias child: chooser.child
property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : 0
required property double proportionalHeight required property double proportionalHeight
required property int type required property int type
required property string typeString required property string typeString
@ -27,6 +27,7 @@ Item {
required property string url required property string url
required property string thumbnailUrl required property string thumbnailUrl
required property bool isOnlyEmoji required property bool isOnlyEmoji
required property bool isStateEvent
required property string userId required property string userId
required property string userName required property string userName
required property string roomTopic required property string roomTopic
@ -42,7 +43,9 @@ Item {
//role: "type" //< not supported in our custom implementation, have to use roleValue //role: "type" //< not supported in our custom implementation, have to use roleValue
roleValue: type roleValue: type
anchors.fill: parent //anchors.fill: parent
width: parent.width? parent.width: 0 // this should get rid of "cannot read property 'width' of null"
DelegateChoice { DelegateChoice {
roleValue: MtxEvent.UnknownMessage roleValue: MtxEvent.UnknownMessage
@ -74,6 +77,7 @@ Item {
body: d.body body: d.body
isOnlyEmoji: d.isOnlyEmoji isOnlyEmoji: d.isOnlyEmoji
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
} }
} }
@ -87,6 +91,7 @@ Item {
body: d.body body: d.body
isOnlyEmoji: d.isOnlyEmoji isOnlyEmoji: d.isOnlyEmoji
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
} }
} }
@ -172,7 +177,7 @@ Item {
roleValue: MtxEvent.Redacted roleValue: MtxEvent.Redacted
Redacted { Redacted {
delegateWidth: d.width //delegateWidth: d.width
} }
} }
@ -180,7 +185,8 @@ Item {
roleValue: MtxEvent.Redaction roleValue: MtxEvent.Redaction
Pill { Pill {
text: qsTr("removed") text: qsTr("%1 removed a message").arg(d.userName)
isStateEvent: d.isStateEvent
} }
} }
@ -189,7 +195,8 @@ Item {
roleValue: MtxEvent.Encryption roleValue: MtxEvent.Encryption
Pill { Pill {
text: qsTr("Encryption enabled") text: qsTr("%1 enabled encryption").arg(d.userName)
isStateEvent: d.isStateEvent
} }
} }
@ -211,7 +218,8 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
formatted: d.roomName ? qsTr("room name changed to: %1").arg(d.roomName) : qsTr("removed room name") isStateEvent: d.isStateEvent
formatted: d.roomName ? qsTr("%2 changed the room name to: %1").arg(d.roomName).arg(d.userName) : qsTr("%1 removed the room name").arg(d.userName)
} }
} }
@ -223,7 +231,8 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
formatted: d.roomTopic ? qsTr("topic changed to: %1").arg(d.roomTopic) : qsTr("removed topic") isStateEvent: d.isStateEvent
formatted: d.roomTopic ? qsTr("%2 changed the topic to: %1").arg(d.roomTopic).arg(d.userName): qsTr("%1 removed the topic").arg(d.userName)
} }
} }
@ -235,6 +244,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the room avatar").arg(d.userName) formatted: qsTr("%1 changed the room avatar").arg(d.userName)
} }
@ -247,6 +257,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the pinned messages.").arg(d.userName) formatted: qsTr("%1 changed the pinned messages.").arg(d.userName)
} }
@ -259,6 +270,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the stickers and emotes in this room.").arg(d.userName) formatted: qsTr("%1 changed the stickers and emotes in this room.").arg(d.userName)
} }
@ -271,6 +283,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the addresses for this room.").arg(d.userName) formatted: qsTr("%1 changed the addresses for this room.").arg(d.userName)
} }
@ -283,6 +296,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the parent spaces for this room.").arg(d.userName) formatted: qsTr("%1 changed the parent spaces for this room.").arg(d.userName)
} }
@ -295,6 +309,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId) formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId)
} }
@ -307,6 +322,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: { formatted: {
switch (d.callType) { switch (d.callType) {
case "voice": case "voice":
@ -328,6 +344,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 answered the call.").arg(d.userName) formatted: qsTr("%1 answered the call.").arg(d.userName)
} }
@ -340,6 +357,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: qsTr("%1 ended the call.").arg(d.userName) formatted: qsTr("%1 ended the call.").arg(d.userName)
} }
@ -352,7 +370,8 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
formatted: qsTr("Negotiating call...") isStateEvent: d.isStateEvent
formatted: qsTr("%1 is negotiating the call...").arg(d.userName)
} }
} }
@ -365,6 +384,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId) formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId)
} }
@ -377,6 +397,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId) formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId)
} }
@ -389,6 +410,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId) formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId)
} }
@ -401,6 +423,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId) formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId)
} }
@ -416,6 +439,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
Layout.fillWidth: true Layout.fillWidth: true
formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId) formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
} }
@ -438,6 +462,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationRequest" formatted: "KeyVerificationRequest"
} }
@ -450,6 +475,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationStart" formatted: "KeyVerificationStart"
} }
@ -462,6 +488,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationReady" formatted: "KeyVerificationReady"
} }
@ -474,6 +501,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationCancel" formatted: "KeyVerificationCancel"
} }
@ -486,6 +514,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationKey" formatted: "KeyVerificationKey"
} }
@ -498,6 +527,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationMac" formatted: "KeyVerificationMac"
} }
@ -510,6 +540,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationDone" formatted: "KeyVerificationDone"
} }
@ -522,6 +553,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationDone" formatted: "KeyVerificationDone"
} }
@ -534,6 +566,7 @@ Item {
body: formatted body: formatted
isOnlyEmoji: false isOnlyEmoji: false
isReply: d.isReply isReply: d.isReply
isStateEvent: d.isStateEvent
formatted: "KeyVerificationAccept" formatted: "KeyVerificationAccept"
} }

View File

@ -5,7 +5,10 @@
import im.nheko 1.0 import im.nheko 1.0
TextMessage { TextMessage {
property bool isStateEvent
font.italic: true font.italic: true
color: Nheko.colors.buttonText color: Nheko.colors.buttonText
font.pointSize: isStateEvent? 0.75*fontMetrics.font.pointSize : 1*fontMetrics.font.pointSize
} }

View File

@ -8,10 +8,12 @@ import QtQuick.Controls 2.1
import im.nheko 1.0 import im.nheko 1.0
Label { Label {
property bool isStateEvent
color: Nheko.colors.text color: Nheko.colors.text
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
height: contentHeight * 1.2 //height: contentHeight * 1.2
width: contentWidth * 1.2 //width: contentWidth * 1.2
font.pointSize: isStateEvent? 0.75*fontMetrics.font.pointSize : 1*fontMetrics.font.pointSize
background: Rectangle { background: Rectangle {
radius: parent.height / 2 radius: parent.height / 2

View File

@ -10,6 +10,6 @@ MatrixText {
required property string typeString required property string typeString
text: qsTr("unimplemented event: ") + typeString text: qsTr("unimplemented event: ") + typeString
width: parent.width // width: parent.width
color: Nheko.inactiveColors.text color: Nheko.inactiveColors.text
} }

View File

@ -22,13 +22,12 @@ Item {
required property string url required property string url
required property string body required property string body
required property string filesize required property string filesize
property double tempWidth: Math.min(parent.width, originalWidth < 1 ? 400 : originalWidth)
property double tempHeight: tempWidth * proportionalHeight
property double divisor: isReply ? 4 : 2 property double divisor: isReply ? 4 : 2
property bool tooHigh: tempHeight > timelineRoot.height / divisor property int tempWidth: originalWidth < 1? 400: originalWidth
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
height: (type == MtxEvent.VideoMessage ? tooHigh ? timelineRoot.height / divisor : tempHeight : 80) + fileInfoLabel.height width: parent.width
width: type == MtxEvent.VideoMessage ? tooHigh ? (timelineRoot.height / divisor) / proportionalHeight : tempWidth : 250 height: (type == MtxEvent.VideoMessage ? width*proportionalHeight : 80) + fileInfoLabel.height
implicitHeight: height
MxcMedia { MxcMedia {
id: mxcmedia id: mxcmedia

View File

@ -10,15 +10,16 @@ import im.nheko 1.0
Rectangle{ Rectangle{
required property real delegateWidth
height: redactedLayout.implicitHeight + Nheko.paddingSmall height: redactedLayout.implicitHeight + Nheko.paddingSmall
width: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium implicitWidth: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium
width: parent.width
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
color: Nheko.colors.alternateBase color: Nheko.colors.alternateBase
RowLayout { RowLayout {
id: redactedLayout id: redactedLayout
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width
spacing: Nheko.paddingSmall spacing: Nheko.paddingSmall
Image { Image {
@ -32,8 +33,8 @@ Rectangle{
id: redactedLabel id: redactedLabel
Layout.margins: 0 Layout.margins: 0
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.preferredWidth: implicitWidth
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: delegateWidth - 4 * Nheko.paddingSmall - trashImg.width - 2 * Nheko.paddingMedium
property var redactedPair: room.formatRedactedEvent(eventId) property var redactedPair: room.formatRedactedEvent(eventId)
text: redactedPair["first"] text: redactedPair["first"]
wrapMode: Label.WordWrap wrapMode: Label.WordWrap

View File

@ -26,6 +26,7 @@ Item {
property string filesize property string filesize
property string url property string url
property bool isOnlyEmoji property bool isOnlyEmoji
property bool isStateEvent
property string userId property string userId
property string userName property string userName
property string thumbnailUrl property string thumbnailUrl
@ -34,9 +35,11 @@ Item {
property string callType property string callType
property int encryptionError property int encryptionError
property int relatedEventCacheBuster property int relatedEventCacheBuster
property int maxWidth
width: parent.width
height: replyContainer.height height: replyContainer.height
implicitHeight: replyContainer.height
implicitWidth: visible? colorLine.width+replyContainer.implicitWidth : 0
CursorShape { CursorShape {
anchors.fill: parent anchors.fill: parent
@ -52,12 +55,12 @@ Item {
color: TimelineManager.userColor(userId, Nheko.colors.base) color: TimelineManager.userColor(userId, Nheko.colors.base)
} }
Column { ColumnLayout {
id: replyContainer id: replyContainer
anchors.left: colorLine.right anchors.left: colorLine.right
anchors.leftMargin: 4 width: parent.width - 4
width: parent.width - 8 spacing: 0
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
@ -80,6 +83,7 @@ Item {
} }
Text { Text {
Layout.leftMargin: 4
id: userName_ id: userName_
text: TimelineManager.escapeEmoji(userName) text: TimelineManager.escapeEmoji(userName)
@ -94,8 +98,9 @@ Item {
} }
MessageDelegate { MessageDelegate {
Layout.leftMargin: 4
Layout.preferredHeight: height
id: reply id: reply
blurhash: r.blurhash blurhash: r.blurhash
body: r.body body: r.body
formattedBody: r.formattedBody formattedBody: r.formattedBody
@ -109,6 +114,7 @@ Item {
thumbnailUrl: r.thumbnailUrl thumbnailUrl: r.thumbnailUrl
originalWidth: r.originalWidth originalWidth: r.originalWidth
isOnlyEmoji: r.isOnlyEmoji isOnlyEmoji: r.isOnlyEmoji
isStateEvent: r.isStateEvent
userId: r.userId userId: r.userId
userName: r.userName userName: r.userName
roomTopic: r.roomTopic roomTopic: r.roomTopic
@ -118,7 +124,7 @@ Item {
encryptionError: r.encryptionError encryptionError: r.encryptionError
// This is disabled so that left clicking the reply goes to its location // This is disabled so that left clicking the reply goes to its location
enabled: false enabled: false
width: parent.width Layout.fillWidth: true
isReply: true isReply: true
} }
@ -128,9 +134,10 @@ Item {
id: backgroundItem id: backgroundItem
z: -1 z: -1
height: replyContainer.height anchors.fill: replyContainer
width: Math.min(Math.max(reply.implicitWidth, userName_.implicitWidth) + 8 + 4, parent.width) property color userColor: TimelineManager.userColor(userId, Nheko.colors.base)
color: Qt.rgba(userColor.r, userColor.g, userColor.b, 0.1) property color bgColor: Nheko.colors.base
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
} }
} }

View File

@ -34,7 +34,7 @@ MatrixText {
</style> </style>
" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>").replace("<del>", "<s>").replace("</del>", "</s>").replace("<strike>", "<s>").replace("</strike>", "</s>") " + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>").replace("<del>", "<s>").replace("</del>", "</s>").replace("<strike>", "<s>").replace("</strike>", "</s>")
width: parent.width width: parent.width
height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : undefined height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
clip: isReply clip: isReply
selectByMouse: !Settings.mobileMode && !isReply selectByMouse: !Settings.mobileMode && !isReply
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize

View File

@ -69,7 +69,9 @@ UserSettings::load(std::optional<QString> profile)
settings.value(QStringLiteral("user/timeline/message_hover_highlight"), false).toBool(); settings.value(QStringLiteral("user/timeline/message_hover_highlight"), false).toBool();
enlargeEmojiOnlyMessages_ = enlargeEmojiOnlyMessages_ =
settings.value(QStringLiteral("user/timeline/enlarge_emoji_only_msg"), false).toBool(); settings.value(QStringLiteral("user/timeline/enlarge_emoji_only_msg"), false).toBool();
markdown_ = settings.value(QStringLiteral("user/markdown_enabled"), true).toBool(); markdown_ = settings.value(QStringLiteral("user/markdown_enabled"), true).toBool();
bubbles_ = settings.value(QStringLiteral("user/bubbles_enabled"), false).toBool();
smallAvatars_ = settings.value(QStringLiteral("user/small_avatars_enabled"), false).toBool();
animateImagesOnHover_ = animateImagesOnHover_ =
settings.value(QStringLiteral("user/animate_images_on_hover"), false).toBool(); settings.value(QStringLiteral("user/animate_images_on_hover"), false).toBool();
typingNotifications_ = typingNotifications_ =
@ -251,6 +253,26 @@ UserSettings::setMarkdown(bool state)
save(); save();
} }
void
UserSettings::setBubbles(bool state)
{
if (state == bubbles_)
return;
bubbles_ = state;
emit bubblesChanged(state);
save();
}
void
UserSettings::setSmallAvatars(bool state)
{
if (state == smallAvatars_)
return;
smallAvatars_ = state;
emit smallAvatarsChanged(state);
save();
}
void void
UserSettings::setAnimateImagesOnHover(bool state) UserSettings::setAnimateImagesOnHover(bool state)
{ {
@ -705,6 +727,8 @@ UserSettings::save()
settings.setValue(QStringLiteral("read_receipts"), readReceipts_); settings.setValue(QStringLiteral("read_receipts"), readReceipts_);
settings.setValue(QStringLiteral("group_view"), groupView_); settings.setValue(QStringLiteral("group_view"), groupView_);
settings.setValue(QStringLiteral("markdown_enabled"), markdown_); settings.setValue(QStringLiteral("markdown_enabled"), markdown_);
settings.setValue(QStringLiteral("bubbles_enabled"), bubbles_);
settings.setValue(QStringLiteral("small_avatars_enabled"), smallAvatars_);
settings.setValue(QStringLiteral("animate_images_on_hover"), animateImagesOnHover_); settings.setValue(QStringLiteral("animate_images_on_hover"), animateImagesOnHover_);
settings.setValue(QStringLiteral("desktop_notifications"), hasDesktopNotifications_); settings.setValue(QStringLiteral("desktop_notifications"), hasDesktopNotifications_);
settings.setValue(QStringLiteral("alert_on_notification"), hasAlertOnNotification_); settings.setValue(QStringLiteral("alert_on_notification"), hasAlertOnNotification_);
@ -806,6 +830,10 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return tr("Group's sidebar"); return tr("Group's sidebar");
case Markdown: case Markdown:
return tr("Send messages as Markdown"); return tr("Send messages as Markdown");
case Bubbles:
return tr("Enable message bubbles");
case SmallAvatars:
return tr("Enable small Avatars");
case AnimateImagesOnHover: case AnimateImagesOnHover:
return tr("Play animated images only on hover"); return tr("Play animated images only on hover");
case TypingNotifications: case TypingNotifications:
@ -926,6 +954,10 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return i->groupView(); return i->groupView();
case Markdown: case Markdown:
return i->markdown(); return i->markdown();
case Bubbles:
return i->bubbles();
case SmallAvatars:
return i->smallAvatars();
case AnimateImagesOnHover: case AnimateImagesOnHover:
return i->animateImagesOnHover(); return i->animateImagesOnHover();
case TypingNotifications: case TypingNotifications:
@ -1052,6 +1084,11 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return tr( return tr(
"Allow using markdown in messages.\nWhen disabled, all messages are sent as a plain " "Allow using markdown in messages.\nWhen disabled, all messages are sent as a plain "
"text."); "text.");
case Bubbles:
return tr(
"Messages get a bubble background. This also triggers some layout changes (WIP).");
case SmallAvatars:
return tr("Avatars are resized to fit above the message.");
case AnimateImagesOnHover: case AnimateImagesOnHover:
return tr("Plays media like GIFs or WEBPs only when explicitly hovering over them."); return tr("Plays media like GIFs or WEBPs only when explicitly hovering over them.");
case TypingNotifications: case TypingNotifications:
@ -1168,6 +1205,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
case StartInTray: case StartInTray:
case GroupView: case GroupView:
case Markdown: case Markdown:
case Bubbles:
case SmallAvatars:
case AnimateImagesOnHover: case AnimateImagesOnHover:
case TypingNotifications: case TypingNotifications:
case SortByImportance: case SortByImportance:
@ -1385,6 +1424,20 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int
} else } else
return false; return false;
} }
case Bubbles: {
if (value.userType() == QMetaType::Bool) {
i->setBubbles(value.toBool());
return true;
} else
return false;
}
case SmallAvatars: {
if (value.userType() == QMetaType::Bool) {
i->setSmallAvatars(value.toBool());
return true;
} else
return false;
}
case AnimateImagesOnHover: { case AnimateImagesOnHover: {
if (value.userType() == QMetaType::Bool) { if (value.userType() == QMetaType::Bool) {
i->setAnimateImagesOnHover(value.toBool()); i->setAnimateImagesOnHover(value.toBool());
@ -1747,7 +1800,12 @@ UserSettingsModel::UserSettingsModel(QObject *p)
connect(s.get(), &UserSettings::markdownChanged, this, [this]() { connect(s.get(), &UserSettings::markdownChanged, this, [this]() {
emit dataChanged(index(Markdown), index(Markdown), {Value}); emit dataChanged(index(Markdown), index(Markdown), {Value});
}); });
connect(s.get(), &UserSettings::bubblesChanged, this, [this]() {
emit dataChanged(index(Bubbles), index(Bubbles), {Value});
});
connect(s.get(), &UserSettings::smallAvatarsChanged, this, [this]() {
emit dataChanged(index(SmallAvatars), index(SmallAvatars), {Value});
});
connect(s.get(), &UserSettings::groupViewStateChanged, this, [this]() { connect(s.get(), &UserSettings::groupViewStateChanged, this, [this]() {
emit dataChanged(index(GroupView), index(GroupView), {Value}); emit dataChanged(index(GroupView), index(GroupView), {Value});
}); });

View File

@ -40,6 +40,8 @@ class UserSettings : public QObject
Q_PROPERTY(bool startInTray READ startInTray WRITE setStartInTray NOTIFY startInTrayChanged) Q_PROPERTY(bool startInTray READ startInTray WRITE setStartInTray NOTIFY startInTrayChanged)
Q_PROPERTY(bool groupView READ groupView WRITE setGroupView NOTIFY groupViewStateChanged) Q_PROPERTY(bool groupView READ groupView WRITE setGroupView NOTIFY groupViewStateChanged)
Q_PROPERTY(bool markdown READ markdown WRITE setMarkdown NOTIFY markdownChanged) Q_PROPERTY(bool markdown READ markdown WRITE setMarkdown NOTIFY markdownChanged)
Q_PROPERTY(bool bubbles READ bubbles WRITE setBubbles NOTIFY bubblesChanged)
Q_PROPERTY(bool smallAvatars READ smallAvatars WRITE setSmallAvatars NOTIFY smallAvatarsChanged)
Q_PROPERTY(bool animateImagesOnHover READ animateImagesOnHover WRITE setAnimateImagesOnHover Q_PROPERTY(bool animateImagesOnHover READ animateImagesOnHover WRITE setAnimateImagesOnHover
NOTIFY animateImagesOnHoverChanged) NOTIFY animateImagesOnHoverChanged)
Q_PROPERTY(bool typingNotifications READ typingNotifications WRITE setTypingNotifications NOTIFY Q_PROPERTY(bool typingNotifications READ typingNotifications WRITE setTypingNotifications NOTIFY
@ -141,6 +143,8 @@ public:
void setEmojiFontFamily(QString family); void setEmojiFontFamily(QString family);
void setGroupView(bool state); void setGroupView(bool state);
void setMarkdown(bool state); void setMarkdown(bool state);
void setBubbles(bool state);
void setSmallAvatars(bool state);
void setAnimateImagesOnHover(bool state); void setAnimateImagesOnHover(bool state);
void setReadReceipts(bool state); void setReadReceipts(bool state);
void setTypingNotifications(bool state); void setTypingNotifications(bool state);
@ -193,6 +197,8 @@ public:
bool privacyScreen() const { return privacyScreen_; } bool privacyScreen() const { return privacyScreen_; }
int privacyScreenTimeout() const { return privacyScreenTimeout_; } int privacyScreenTimeout() const { return privacyScreenTimeout_; }
bool markdown() const { return markdown_; } bool markdown() const { return markdown_; }
bool bubbles() const { return bubbles_; }
bool smallAvatars() const { return smallAvatars_; }
bool animateImagesOnHover() const { return animateImagesOnHover_; } bool animateImagesOnHover() const { return animateImagesOnHover_; }
bool typingNotifications() const { return typingNotifications_; } bool typingNotifications() const { return typingNotifications_; }
bool sortByImportance() const { return sortByImportance_; } bool sortByImportance() const { return sortByImportance_; }
@ -251,6 +257,8 @@ signals:
void trayChanged(bool state); void trayChanged(bool state);
void startInTrayChanged(bool state); void startInTrayChanged(bool state);
void markdownChanged(bool state); void markdownChanged(bool state);
void bubblesChanged(bool state);
void smallAvatarsChanged(bool state);
void animateImagesOnHoverChanged(bool state); void animateImagesOnHoverChanged(bool state);
void typingNotificationsChanged(bool state); void typingNotificationsChanged(bool state);
void buttonInTimelineChanged(bool state); void buttonInTimelineChanged(bool state);
@ -307,6 +315,8 @@ private:
bool startInTray_; bool startInTray_;
bool groupView_; bool groupView_;
bool markdown_; bool markdown_;
bool bubbles_;
bool smallAvatars_;
bool animateImagesOnHover_; bool animateImagesOnHover_;
bool typingNotifications_; bool typingNotifications_;
bool sortByImportance_; bool sortByImportance_;
@ -386,7 +396,8 @@ class UserSettingsModel : public QAbstractListModel
ReadReceipts, ReadReceipts,
ButtonsInTimeline, ButtonsInTimeline,
Markdown, Markdown,
Bubbles,
SmallAvatars,
SidebarSection, SidebarSection,
GroupView, GroupView,
SortByImportance, SortByImportance,

View File

@ -467,6 +467,7 @@ TimelineModel::roleNames() const
{UserId, "userId"}, {UserId, "userId"},
{UserName, "userName"}, {UserName, "userName"},
{PreviousMessageDay, "previousMessageDay"}, {PreviousMessageDay, "previousMessageDay"},
{PreviousMessageIsStateEvent, "previousMessageIsStateEvent"},
{Day, "day"}, {Day, "day"},
{Timestamp, "timestamp"}, {Timestamp, "timestamp"},
{Url, "url"}, {Url, "url"},
@ -483,6 +484,7 @@ TimelineModel::roleNames() const
{IsEdited, "isEdited"}, {IsEdited, "isEdited"},
{IsEditable, "isEditable"}, {IsEditable, "isEditable"},
{IsEncrypted, "isEncrypted"}, {IsEncrypted, "isEncrypted"},
{IsStateEvent, "isStateEvent"},
{Trustlevel, "trustlevel"}, {Trustlevel, "trustlevel"},
{EncryptionError, "encryptionError"}, {EncryptionError, "encryptionError"},
{ReplyTo, "replyTo"}, {ReplyTo, "replyTo"},
@ -680,6 +682,9 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
std::holds_alternative<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>( std::holds_alternative<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
*encrypted_event); *encrypted_event);
} }
case IsStateEvent: {
return is_state_event(event);
}
case Trustlevel: { case Trustlevel: {
auto encrypted_event = events.get(event_id(event), "", false); auto encrypted_event = events.get(event_id(event), "", false);
@ -744,6 +749,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
m.insert(names[IsEdited], data(event, static_cast<int>(IsEdited))); m.insert(names[IsEdited], data(event, static_cast<int>(IsEdited)));
m.insert(names[IsEditable], data(event, static_cast<int>(IsEditable))); m.insert(names[IsEditable], data(event, static_cast<int>(IsEditable)));
m.insert(names[IsEncrypted], data(event, static_cast<int>(IsEncrypted))); m.insert(names[IsEncrypted], data(event, static_cast<int>(IsEncrypted)));
m.insert(names[IsStateEvent], data(event, static_cast<int>(IsStateEvent)));
m.insert(names[ReplyTo], data(event, static_cast<int>(ReplyTo))); m.insert(names[ReplyTo], data(event, static_cast<int>(ReplyTo)));
m.insert(names[RoomName], data(event, static_cast<int>(RoomName))); m.insert(names[RoomName], data(event, static_cast<int>(RoomName)));
m.insert(names[RoomTopic], data(event, static_cast<int>(RoomTopic))); m.insert(names[RoomTopic], data(event, static_cast<int>(RoomTopic)));
@ -776,7 +782,8 @@ TimelineModel::data(const QModelIndex &index, int role) const
if (!event) if (!event)
return ""; return "";
if (role == PreviousMessageDay || role == PreviousMessageUserId) { if (role == PreviousMessageDay || role == PreviousMessageUserId ||
role == PreviousMessageIsStateEvent) {
int prevIdx = rowCount() - index.row() - 2; int prevIdx = rowCount() - index.row() - 2;
if (prevIdx < 0) if (prevIdx < 0)
return {}; return {};
@ -785,8 +792,10 @@ TimelineModel::data(const QModelIndex &index, int role) const
return {}; return {};
if (role == PreviousMessageUserId) if (role == PreviousMessageUserId)
return data(*tempEv, UserId); return data(*tempEv, UserId);
else else if (role == PreviousMessageDay)
return data(*tempEv, Day); return data(*tempEv, Day);
else
return data(*tempEv, IsStateEvent);
} }
return data(*event, role); return data(*event, role);

View File

@ -210,6 +210,7 @@ public:
UserId, UserId,
UserName, UserName,
PreviousMessageDay, PreviousMessageDay,
PreviousMessageIsStateEvent,
Day, Day,
Timestamp, Timestamp,
Url, Url,
@ -226,6 +227,7 @@ public:
IsEdited, IsEdited,
IsEditable, IsEditable,
IsEncrypted, IsEncrypted,
IsStateEvent,
Trustlevel, Trustlevel,
EncryptionError, EncryptionError,
ReplyTo, ReplyTo,