Skip to content
Snippets Groups Projects
TimelineView.qml 7.05 KiB
Newer Older
import QtQuick 2.6
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.5
import QtGraphicalEffects 1.0
import QtQuick.Window 2.2
import com.github.nheko 1.0

Rectangle {
	anchors.fill: parent

	SystemPalette { id: colors; colorGroup: SystemPalette.Active }
	SystemPalette { id: inactiveColors; colorGroup: SystemPalette.Disabled }
	property int avatarSize: 32

	color: colors.window

Nicolas Werner's avatar
Nicolas Werner committed
		visible: !timelineManager.timeline
		anchors.centerIn: parent
		text: qsTr("No room open")
		font.pointSize: 24
		color: colors.windowText
		cacheBuffer: parent.height
Nicolas Werner's avatar
Nicolas Werner committed
		visible: timelineManager.timeline != null
		anchors.fill: parent

		model: timelineManager.timeline

		onModelChanged: {
			if (model) {
				currentIndex = model.currentIndex
				if (model.currentIndex == count - 1) {
					positionViewAtEnd()
				} else {
					positionViewAtIndex(model.currentIndex, ListView.End)
				}
			}
		}

		ScrollBar.vertical: ScrollBar {
			id: scrollbar
			anchors.top: parent.top
			anchors.right: parent.right
			anchors.bottom: parent.bottom
			onPressedChanged: if (!pressed) chat.updatePosition()
		property bool atBottom: false
		onCountChanged: {
			if (atBottom && Window.active) {
				var newIndex = count - 1 // last index
				positionViewAtEnd()
				currentIndex = newIndex
				model.currentIndex = newIndex
			}
		}

		function updatePosition() {
			for (var y = chat.contentY + chat.height; y > chat.height; y -= 5) {
				var i = chat.itemAt(100, y);
				if (!i) continue;
				if (!i.isFullyVisible()) continue;
				chat.model.currentIndex = i.getIndex();
				chat.currentIndex = i.getIndex()
				atBottom = i.getIndex() == count - 1;
				console.log("bottom:" + atBottom)
				break;
			}
		}
		onMovementEnded: updatePosition()

		spacing: 4
			anchors.leftMargin: avatarSize + 4
			anchors.left: parent.left
			anchors.right: parent.right
			anchors.rightMargin: scrollbar.width

			function isFullyVisible() {
				return (y - chat.contentY - 1) + height < chat.height
			}
			function getIndex() {
				return index;
			}

			Loader {
				id: loader
				Layout.alignment: Qt.AlignTop
				height: item.height

				source: switch(model.type) {
					//case MtxEvent.Aliases: return "delegates/Aliases.qml"
					//case MtxEvent.Avatar: return "delegates/Avatar.qml"
					//case MtxEvent.CanonicalAlias: return "delegates/CanonicalAlias.qml"
					//case MtxEvent.Create: return "delegates/Create.qml"
					//case MtxEvent.GuestAccess: return "delegates/GuestAccess.qml"
					//case MtxEvent.HistoryVisibility: return "delegates/HistoryVisibility.qml"
					//case MtxEvent.JoinRules: return "delegates/JoinRules.qml"
					//case MtxEvent.Member: return "delegates/Member.qml"
					//case MtxEvent.Name: return "delegates/Name.qml"
					//case MtxEvent.PowerLevels: return "delegates/PowerLevels.qml"
					//case MtxEvent.Topic: return "delegates/Topic.qml"
					case MtxEvent.NoticeMessage: return "delegates/NoticeMessage.qml"
					case MtxEvent.TextMessage: return "delegates/TextMessage.qml"
					case MtxEvent.ImageMessage: return "delegates/ImageMessage.qml"
					//case MtxEvent.VideoMessage: return "delegates/VideoMessage.qml"
					case MtxEvent.Redacted: return "delegates/Redacted.qml"
					default: return "delegates/placeholder.qml"
				}
				property variant eventData: model
			StatusIndicator {
				state: model.state
				Layout.alignment: Qt.AlignRight | Qt.AlignTop
				Layout.preferredHeight: 16
			}
			EncryptionIndicator {
				visible: model.isEncrypted
				Layout.alignment: Qt.AlignRight | Qt.AlignTop
				Layout.preferredHeight: 16
			}

				Layout.alignment: Qt.AlignRight | Qt.AlignTop
				Layout.preferredHeight: 16
				ToolTip.visible: hovered
				ToolTip.text: qsTr("Reply")
				// disable background, because we don't want a border on hover
				background: Item {
				}

				Image {
					id: replyButtonImg
					// Workaround, can't get icon.source working for now...
					anchors.fill: parent
					source: "qrc:/icons/icons/ui/mail-reply.png"
				}
				ColorOverlay {
					anchors.fill: replyButtonImg
					source: replyButtonImg
					color: replyButton.hovered ? colors.highlight : colors.buttonText

				onClicked: chat.model.replyAction(model.id)
				Layout.alignment: Qt.AlignRight | Qt.AlignTop
				Layout.preferredHeight: 16
				ToolTip.visible: hovered
				ToolTip.text: qsTr("Options")

				// disable background, because we don't want a border on hover
				background: Item {
				}

				Image {
					id: optionsButtonImg
					// Workaround, can't get icon.source working for now...
					anchors.fill: parent
					source: "qrc:/icons/icons/ui/vertical-ellipsis.png"
				}
				ColorOverlay {
					anchors.fill: optionsButtonImg
					source: optionsButtonImg
					color: optionsButton.hovered ? colors.highlight : colors.buttonText

				onClicked: contextMenu.open()

				Menu {
					y: optionsButton.height
					id: contextMenu

					MenuItem {
Nicolas Werner's avatar
Nicolas Werner committed
						text: qsTr("Read receipts")
						onTriggered: chat.model.readReceiptsAction(model.id)
Nicolas Werner's avatar
Nicolas Werner committed
						text: qsTr("Mark as read")
Nicolas Werner's avatar
Nicolas Werner committed
						text: qsTr("View raw message")
						onTriggered: chat.model.viewRawMessage(model.id)
Nicolas Werner's avatar
Nicolas Werner committed
						text: qsTr("Redact message")
					}
					MenuItem {
						visible: model.type == MtxEvent.ImageMessage || model.type == MtxEvent.VideoMessage || model.type == MtxEvent.AudioMessage || model.type == MtxEvent.FileMessage
						text: qsTr("Save as")
						onTriggered: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type)
				Layout.alignment: Qt.AlignRight | Qt.AlignTop
				text: model.timestamp.toLocaleTimeString("HH:mm")
				color: inactiveColors.text

				ToolTip.visible: ma.containsMouse
				ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)

				MouseArea{
					id: ma
					anchors.fill: parent
					hoverEnabled: true
				}
			}
		}

		section {
			property: "section"
			delegate: Column {
				topPadding: 4
				bottomPadding: 4
				spacing: 8

					anchors.horizontalCenter: parent.horizontalCenter
					visible: section.includes(" ")
					text: chat.model.formatDateSeparator(new Date(Number(section.split(" ")[1])))
					color: colors.windowText
					height: contentHeight * 1.2
					width: contentWidth * 1.2
					horizontalAlignment: Text.AlignHCenter
					background: Rectangle {
						radius: parent.height / 2
						color: colors.dark
					height: userName.height
						width: avatarSize
						height: avatarSize
						url: chat.model.avatarUrl(section.split(" ")[0]).replace("mxc://", "image://MxcImage/")
						displayName: chat.model.displayName(section.split(" ")[0])
Nicolas Werner's avatar
Nicolas Werner committed
						text: chat.model.escapeEmoji(chat.model.displayName(section.split(" ")[0]))
						color: chat.model.userColor(section.split(" ")[0], colors.window)
Nicolas Werner's avatar
Nicolas Werner committed
						textFormat: Text.RichText