mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Reimplement reply delegate by moving out the timeline event without layout
This commit is contained in:
parent
aef0cb9884
commit
b187440e68
13 changed files with 352 additions and 460 deletions
|
@ -713,6 +713,7 @@ set(QML_SOURCES
|
||||||
resources/qml/UploadBox.qml
|
resources/qml/UploadBox.qml
|
||||||
resources/qml/MessageInput.qml
|
resources/qml/MessageInput.qml
|
||||||
resources/qml/MessageView.qml
|
resources/qml/MessageView.qml
|
||||||
|
resources/qml/TimelineEvent.qml
|
||||||
resources/qml/PrivacyScreen.qml
|
resources/qml/PrivacyScreen.qml
|
||||||
resources/qml/Reactions.qml
|
resources/qml/Reactions.qml
|
||||||
resources/qml/ReplyPopup.qml
|
resources/qml/ReplyPopup.qml
|
||||||
|
|
|
@ -145,7 +145,6 @@ Control {
|
||||||
roleValue: "user"
|
roleValue: "user"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
||||||
anchors.centerIn: centerRowContent ? parent : undefined
|
anchors.centerIn: centerRowContent ? parent : undefined
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
|
@ -171,7 +170,6 @@ Control {
|
||||||
roleValue: "emoji"
|
roleValue: "emoji"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
|
@ -207,7 +205,6 @@ Control {
|
||||||
roleValue: "command"
|
roleValue: "command"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
|
@ -226,7 +223,6 @@ Control {
|
||||||
roleValue: "room"
|
roleValue: "room"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
||||||
anchors.centerIn: centerRowContent ? parent : undefined
|
anchors.centerIn: centerRowContent ? parent : undefined
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
|
@ -251,7 +247,6 @@ Control {
|
||||||
roleValue: "roomAliases"
|
roleValue: "roomAliases"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
|
|
|
@ -54,24 +54,8 @@ Popup {
|
||||||
Reply {
|
Reply {
|
||||||
id: replyPreview
|
id: replyPreview
|
||||||
|
|
||||||
property var modelData: room ? room.getDump(mid, "") : {}
|
eventId: 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 ?? ""
|
|
||||||
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
||||||
userId: modelData.userId ?? ""
|
|
||||||
userName: modelData.userName ?? ""
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
|
|
|
@ -11,25 +11,24 @@ TextArea {
|
||||||
|
|
||||||
property alias cursorShape: cs.cursorShape
|
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.text: hoveredLink
|
||||||
ToolTip.visible: hoveredLink || false
|
ToolTip.visible: hoveredLink || false
|
||||||
|
background: null
|
||||||
|
bottomInset: 0
|
||||||
|
bottomPadding: 0
|
||||||
// this always has to be enabled, otherwise you can't click links anymore!
|
// this always has to be enabled, otherwise you can't click links anymore!
|
||||||
//enabled: selectByMouse
|
//enabled: selectByMouse
|
||||||
color: palette.text
|
color: palette.text
|
||||||
focus: false
|
focus: false
|
||||||
|
leftInset: 0
|
||||||
|
leftPadding: 0
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
rightInset: 0
|
||||||
|
rightPadding: 0
|
||||||
selectByMouse: !Settings.mobileMode
|
selectByMouse: !Settings.mobileMode
|
||||||
textFormat: TextEdit.RichText
|
textFormat: TextEdit.RichText
|
||||||
|
topInset: 0
|
||||||
|
topPadding: 0
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
// Setting a tooltip delay makes the hover text empty .-.
|
// Setting a tooltip delay makes the hover text empty .-.
|
||||||
|
@ -40,8 +39,8 @@ TextArea {
|
||||||
onLinkActivated: Nheko.openLink(link)
|
onLinkActivated: Nheko.openLink(link)
|
||||||
|
|
||||||
// propagate events up
|
// propagate events up
|
||||||
onPressAndHold: (event) => event.accepted = false
|
onPressAndHold: event => event.accepted = false
|
||||||
onPressed: (event) => event.accepted = (event.button == Qt.LeftButton)
|
onPressed: event => event.accepted = (event.button == Qt.LeftButton)
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
id: cs
|
id: cs
|
||||||
|
|
|
@ -59,7 +59,7 @@ Item {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
verticalLayoutDirection: ListView.BottomToTop
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
|
||||||
delegate: EventDelegateChooser {
|
delegate: TimelineEvent {
|
||||||
id: wrapper
|
id: wrapper
|
||||||
ListView.delayRemove: true
|
ListView.delayRemove: true
|
||||||
width: chat.delegateMaxWidth
|
width: chat.delegateMaxWidth
|
||||||
|
@ -69,7 +69,6 @@ Item {
|
||||||
|
|
||||||
required property var day
|
required property var day
|
||||||
required property bool isSender
|
required property bool isSender
|
||||||
required property bool isStateEvent
|
|
||||||
//required property var previousMessageDay
|
//required property var previousMessageDay
|
||||||
//required property bool previousMessageIsStateEvent
|
//required property bool previousMessageIsStateEvent
|
||||||
//required property string previousMessageUserId
|
//required property string previousMessageUserId
|
||||||
|
@ -145,6 +144,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: replyUserButton
|
id: replyUserButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -222,314 +223,6 @@ Item {
|
||||||
opacity: 0.2
|
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 {
|
footer: Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
|
@ -74,10 +74,10 @@ Flow {
|
||||||
anchors.verticalCenter: divider.verticalCenter
|
anchors.verticalCenter: divider.verticalCenter
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
height: textMetrics.height
|
height: textMetrics.height
|
||||||
|
mipmap: true
|
||||||
source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
||||||
visible: modelData.key.startsWith("mxc://")
|
visible: modelData.key.startsWith("mxc://")
|
||||||
width: textMetrics.height
|
width: textMetrics.height
|
||||||
mipmap: true
|
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: divider
|
id: divider
|
||||||
|
|
|
@ -29,22 +29,8 @@ Rectangle {
|
||||||
anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
|
anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Nheko.paddingSmall
|
anchors.topMargin: Nheko.paddingSmall
|
||||||
blurhash: modelData.blurhash ?? ""
|
eventId: room.reply ?? ""
|
||||||
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 ?? ""
|
|
||||||
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
||||||
userId: modelData.userId ?? ""
|
|
||||||
userName: modelData.userName ?? ""
|
|
||||||
visible: room && room.reply
|
visible: room && room.reply
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,9 +728,9 @@ Page {
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Mark as read")
|
text: qsTr("Mark as read")
|
||||||
|
|
||||||
onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
|
onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Room settings")
|
text: qsTr("Room settings")
|
||||||
|
|
||||||
|
|
|
@ -355,7 +355,6 @@ Pane {
|
||||||
|
|
||||||
onAccepted: UIA.continue3pidReceived()
|
onAccepted: UIA.continue3pidReceived()
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onConfirm3pidToken() {
|
function onConfirm3pidToken() {
|
||||||
uiaConfirmationLinkDialog.open();
|
uiaConfirmationLinkDialog.open();
|
||||||
|
@ -363,6 +362,18 @@ Pane {
|
||||||
function onEmail() {
|
function onEmail() {
|
||||||
uiaEmailPrompt.show();
|
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() {
|
function onPassword() {
|
||||||
console.log("UIA: password needed");
|
console.log("UIA: password needed");
|
||||||
uiaPassPrompt.show();
|
uiaPassPrompt.show();
|
||||||
|
@ -385,18 +396,6 @@ Pane {
|
||||||
console.error("Failed to create component: " + component.errorString());
|
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
|
target: UIA
|
||||||
}
|
}
|
||||||
|
|
255
resources/qml/TimelineEvent.qml
Normal file
255
resources/qml/TimelineEvent.qml
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,6 +147,7 @@ AbstractButton {
|
||||||
columns: Settings.bubbles ? 1 : 2
|
columns: Settings.bubbles ? 1 : 2
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
rows: Settings.bubbles ? 3 : 2
|
rows: Settings.bubbles ? 3 : 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
|
|
@ -286,24 +286,9 @@ Pane {
|
||||||
property var e: room ? room.getDump(modelData, "pins") : {}
|
property var e: room ? room.getDump(modelData, "pins") : {}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: height
|
//Layout.preferredHeight: height
|
||||||
blurhash: e.blurhash ?? ""
|
|
||||||
body: e.body ?? ""
|
|
||||||
encryptionError: e.encryptionError ?? 0
|
|
||||||
eventId: e.eventId ?? ""
|
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)
|
userColor: TimelineManager.userColor(e.userId, palette.window)
|
||||||
userId: e.userId ?? ""
|
|
||||||
userName: e.userName ?? ""
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onPinnedMessagesChanged() {
|
function onPinnedMessagesChanged() {
|
||||||
|
|
|
@ -14,102 +14,96 @@ AbstractButton {
|
||||||
id: r
|
id: r
|
||||||
|
|
||||||
property color userColor: "red"
|
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
|
property bool keepFullText: false
|
||||||
|
|
||||||
height: replyContainer.height
|
required property string eventId
|
||||||
implicitHeight: replyContainer.height
|
|
||||||
implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues
|
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 {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: colorLine
|
|
||||||
|
|
||||||
anchors.top: replyContainer.top
|
|
||||||
anchors.bottom: replyContainer.bottom
|
|
||||||
width: 4
|
|
||||||
color: TimelineManager.userColor(userId, palette.base)
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
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) {
|
if (link) {
|
||||||
Nheko.openLink(link)
|
Nheko.openLink(link)
|
||||||
} else {
|
} else {
|
||||||
room.showEvent(r.eventId)
|
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 {
|
contentItem: TimelineEvent {
|
||||||
|
id: timelineEvent
|
||||||
|
|
||||||
|
isStateEvent: false
|
||||||
|
room: room_
|
||||||
|
eventId: r.eventId
|
||||||
|
replyTo: ""
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: replyContainer.implicitHeight
|
||||||
|
|
||||||
|
//height: replyContainer.implicitHeight
|
||||||
|
data: GridLayout {
|
||||||
id: replyContainer
|
id: replyContainer
|
||||||
|
|
||||||
anchors.left: colorLine.right
|
width: parent.width
|
||||||
width: parent.width - 4
|
columns: 2
|
||||||
spacing: 0
|
rows: 2
|
||||||
|
columnSpacing: Nheko.paddingMedium
|
||||||
|
rowSpacing: Nheko.paddingSmall
|
||||||
|
|
||||||
TapHandler {
|
Rectangle {
|
||||||
acceptedButtons: Qt.RightButton
|
id: colorline
|
||||||
onSingleTapped: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight), r.eventId)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
Layout.preferredWidth: 4
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
Layout.rowSpan: 2
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Layout.row: 0
|
||||||
|
Layout.column: 0
|
||||||
|
|
||||||
|
color: TimelineManager.userColor(r.userId, palette.base)
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
Layout.leftMargin: 4
|
id: usernameBtn
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Layout.row: 0
|
||||||
|
Layout.column: 1
|
||||||
|
|
||||||
contentItem: ElidedLabel {
|
contentItem: ElidedLabel {
|
||||||
id: userName_
|
id: userName_
|
||||||
fullText: userName
|
fullText: r.userName
|
||||||
color: r.userColor
|
color: r.userColor
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
elideWidth: width
|
elideWidth: width
|
||||||
}
|
}
|
||||||
onClicked: room.openUserProfile(userId)
|
onClicked: room.openUserProfile(r.userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
data: [
|
||||||
Layout.leftMargin: 4
|
colorline, usernameBtn, timelineEvent.main,
|
||||||
Layout.preferredHeight: 20
|
]
|
||||||
Layout.fillWidth: true
|
|
||||||
color: "green"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
background: Rectangle {
|
||||||
id: backgroundItem
|
id: backgroundItem
|
||||||
|
|
||||||
z: -1
|
z: -1
|
||||||
anchors.fill: replyContainer
|
property color userColor: TimelineManager.userColor(r.userId, palette.base)
|
||||||
property color userColor: TimelineManager.userColor(userId, palette.base)
|
|
||||||
property color bgColor: palette.base
|
property color bgColor: palette.base
|
||||||
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue