summary refs log tree commit diff
path: root/resources/qml/TimelineDefaultMessageStyle.qml
diff options
context:
space:
mode:
Diffstat (limited to 'resources/qml/TimelineDefaultMessageStyle.qml')
-rw-r--r--resources/qml/TimelineDefaultMessageStyle.qml327
1 files changed, 327 insertions, 0 deletions
diff --git a/resources/qml/TimelineDefaultMessageStyle.qml b/resources/qml/TimelineDefaultMessageStyle.qml
new file mode 100644

index 00000000..e9a0712d --- /dev/null +++ b/resources/qml/TimelineDefaultMessageStyle.qml
@@ -0,0 +1,327 @@ +// 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 +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window +import im.nheko + +TimelineEvent { + id: wrapper + ListView.delayRemove: true + width: chat.delegateMaxWidth + height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 10) + anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter + //room: chatRoot.roommodel + + required property var day + required property bool isSender + required property int index + property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day) + property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent) + property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId) + + required property date timestamp + required property string userId + required property string userName + required property string threadId + required property int userPowerlevel + required property bool isEdited + required property bool isEncrypted + required property var reactions + required property int status + required property int trustlevel + required property int notificationlevel + required property int type + required property bool isEditable + + required property QtObject messageContextMenu + required property QtObject replyContextMenu + required property Item messageActions + + property int avatarMargin: (wrapper.isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) // align bubble with section header + + property alias hovered: messageHover.hovered + property bool scrolledToThis: false + + mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0) + replyInset: mainInset + 4 + Nheko.paddingSmall + + maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width + + data: [ + Loader { + id: section + + active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent + //asynchronous: true + sourceComponent: TimelineSectionHeader { + day: wrapper.day + isSender: wrapper.isSender + isStateEvent: wrapper.isStateEvent + parentWidth: wrapper.width + previousMessageDay: wrapper.previousMessageDay + previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent + previousMessageUserId: wrapper.previousMessageUserId + timestamp: wrapper.timestamp + userId: wrapper.userId + userName: wrapper.userName + userPowerlevel: wrapper.userPowerlevel + } + visible: status == Loader.Ready + z: 4 + }, + Rectangle { + anchors.fill: gridContainer + color: (Settings.messageHoverHighlight && messageHover.hovered) ? palette.alternateBase : "transparent" + + // this looks better without margins + TapHandler { + acceptedButtons: Qt.RightButton + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad + gesturePolicy: TapHandler.ReleaseWithinBounds + + onSingleTapped: messageContextMenu.show(wrapper.eventId, wrapper.threadId, wrapper.type, wrapper.isSender, wrapper.isEncrypted, wrapper.isEditable, wrapper.main.hoveredLink, wrapper.main.copyText) + } + }, + Rectangle { + id: scrollHighlight + anchors.fill: gridContainer + + color: palette.highlight + enabled: false + opacity: 0 + visible: true + z: 1 + + states: State { + name: "revealed" + when: wrapper.scrolledToThis + } + transitions: Transition { + from: "" + to: "revealed" + + SequentialAnimation { + PropertyAnimation { + duration: 500 + easing.type: Easing.InOutQuad + from: 0 + properties: "opacity" + target: scrollHighlight + to: 1 + } + PropertyAnimation { + duration: 500 + easing.type: Easing.InOutQuad + from: 1 + properties: "opacity" + target: scrollHighlight + to: 0 + } + ScriptAction { + script: wrapper.room.eventShown() + } + } + } + }, + Rectangle { + anchors.top: gridContainer.top + anchors.left: gridContainer.left + anchors.topMargin: -2 + anchors.leftMargin: -2 + color: "transparent" + border.color: Nheko.theme.red + border.width: wrapper.notificationlevel == MtxEvent.Highlight ? 1 : 0 + radius: 4 + height: contentColumn.implicitHeight + 4 + width: contentColumn.implicitWidth + 4 + }, + Row { + id: gridContainer + + width: wrapper.width - wrapper.avatarMargin + x: wrapper.avatarMargin + y: section.visible && section.active ? section.y + section.height : 0 + spacing: Nheko.paddingSmall + + HoverHandler { + id: messageHover + blocking: false + onHoveredChanged: () => { + if (!Settings.mobileMode && hovered) { + if (!messageActions.hovered) { + messageActions.model = wrapper; + messageActions.attached = wrapper; + messageActions.anchors.bottomMargin = -gridContainer.y + messageActions.anchors.rightMargin = metadata.width + } + } + } + + } + + AbstractButton { + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Part of a thread") + ToolTip.visible: hovered + height: contentColumn.height + visible: wrapper.threadId + width: 4 + + onClicked: wrapper.room.thread = wrapper.threadId + + Rectangle { + id: threadLine + + anchors.fill: parent + color: TimelineManager.userColor(wrapper.threadId, palette.base) + } + } + + Item { + visible: wrapper.isStateEvent + width: (wrapper.maxWidth - (wrapper.main?.width ?? 0)) / 2 + height: 1 + } + + Column { + id: contentColumn + + AbstractButton { + id: replyRow + visible: wrapper.reply + + height: replyLine.height + + property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base) + + clip: true + + NhekoCursorShape { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + } + + contentItem: Row { + id: replyRowLay + + spacing: Nheko.paddingSmall + + Rectangle { + id: replyLine + height: Math.min( wrapper.reply?.height, timelineView.height / 10) + Nheko.paddingSmall + replyUserButton.height + color: replyRow.userColor + width: 4 + } + + Column { + spacing: 0 + + id: replyCol + + AbstractButton { + id: replyUserButton + + contentItem: Label { + id: userName_ + text: wrapper.reply?.userName ?? '' + color: replyRow.userColor + textFormat: Text.RichText + width: wrapper.maxWidth + //elideWidth: wrapper.maxWidth + } + onClicked: wrapper.room.openUserProfile(wrapper.reply?.userId) + } + data: [ + replyUserButton, + wrapper.reply, + ] + } + } + + background: Rectangle { + //width: replyRow.implicitContentWidth + color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1)) + } + + onClicked: { + let link = wrapper.reply.hoveredLink + if (link) { + Nheko.openLink(link) + } else { + console.log("Scrolling to "+wrapper.replyTo); + wrapper.room.showEvent(wrapper.replyTo) + } + } + onPressAndHold: wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(pressX-replyLine.width - Nheko.paddingSmall, pressY - replyUserButton.implicitHeight) : "", wrapper.replyTo) + TapHandler { + acceptedButtons: Qt.RightButton + onSingleTapped: (eventPoint) => wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(eventPoint.position.x-replyLine.width - Nheko.paddingSmall, eventPoint.position.y - replyUserButton.implicitHeight) : "", wrapper.replyTo) + gesturePolicy: TapHandler.ReleaseWithinBounds + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad + } + } + + data: [ + replyRow, wrapper.main, + ] + } + + }, + TimelineMetadata { + id: metadata + + scaling: 1 + + anchors.right: parent.right + y: section.visible && section.active ? section.y + section.height : 0 + + visible: !wrapper.isStateEvent + + eventId: wrapper.eventId + status: wrapper.status + trustlevel: wrapper.trustlevel + isEdited: wrapper.isEdited + isEncrypted: wrapper.isEncrypted + threadId: wrapper.threadId + timestamp: wrapper.timestamp + room: wrapper.room + }, + Reactions { + id: reactionRow + + eventId: wrapper.eventId + reactions: wrapper.reactions + width: wrapper.width - wrapper.avatarMargin + x: wrapper.avatarMargin + + anchors { + top: gridContainer.bottom + topMargin: -4 + } + }, + Rectangle { + id: unreadRow + + color: palette.highlight + height: visible ? 3 : 0 + visible: (wrapper.index > 0 && (wrapper.room.fullyReadEventId == wrapper.eventId)) + + anchors { + left: parent.left + right: parent.right + top: reactionRow.bottom + topMargin: 5 + } + } + ] +}