Skip to content
Snippets Groups Projects
TimelineView.qml 7.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • import QtQuick 2.9
    
    import QtQuick.Controls 2.3
    
    import QtQuick.Layouts 1.2
    
    import QtGraphicalEffects 1.0
    
    import QtQuick.Window 2.2
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    import "./delegates"
    
    
    	property var colors: currentActivePalette
    	property var systemInactive: SystemPalette { colorGroup: SystemPalette.Disabled }
    	property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive
    
    	property int avatarSize: 40
    
    	palette: colors
    
    
    	Settings {
    		id: settings
    		category: "user"
    		property bool avatar_circles: true
    	}
    
    	Settings {
    		id: timelineSettings
    		category: "user/timeline"
    		property bool buttons: true
    	}
    
    
    	Menu {
    		id: messageContextMenu
    
    		modal: true
    
    
    		function show(eventId_, eventType_, showAt) {
    			eventId = eventId_
    			eventType = eventType_
    			popup(showAt)
    		}
    
    		property string eventId
    		property int eventType
    
    
    		MenuItem {
    			text: qsTr("Reply")
    			onClicked: chat.model.replyAction(messageContextMenu.eventId)
    		}
    
    		MenuItem {
    			text: qsTr("Read receipts")
    			onTriggered: chat.model.readReceiptsAction(messageContextMenu.eventId)
    		}
    		MenuItem {
    			text: qsTr("Mark as read")
    		}
    		MenuItem {
    			text: qsTr("View raw message")
    			onTriggered: chat.model.viewRawMessage(messageContextMenu.eventId)
    		}
    		MenuItem {
    			text: qsTr("Redact message")
    			onTriggered: chat.model.redactEvent(messageContextMenu.eventId)
    		}
    		MenuItem {
    			visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
    			text: qsTr("Save as")
    			onTriggered: timelineManager.timeline.saveMedia(messageContextMenu.eventId)
    		}
    	}
    
    
    	Rectangle {
    		anchors.fill: parent
    		color: colors.window
    
    
    			visible: !timelineManager.timeline && !timelineManager.isInitialSync
    
    			anchors.centerIn: parent
    			text: qsTr("No room open")
    			font.pointSize: 24
    		}
    
    		BusyIndicator {
    			anchors.centerIn: parent
    			running: timelineManager.isInitialSync
    			height: 200
    			width: 200
    
    			visible: timelineManager.timeline != null
    
    
    			anchors.left: parent.left
    			anchors.right: parent.right
    			anchors.top: parent.top
    			anchors.bottom: chatFooter.top
    
    			anchors.leftMargin: 4
    			anchors.rightMargin: scrollbar.width
    
    			boundsBehavior: Flickable.StopAtBounds
    
    			pixelAligned: true
    
    
    			MouseArea {
    				anchors.fill: parent
    				acceptedButtons: Qt.NoButton
    				propagateComposedEvents: true
    				z: -1
    				onWheel: {
    					if (wheel.angleDelta != 0) {
    						chat.contentY = chat.contentY - wheel.angleDelta.y
    						wheel.accepted = true
    
    			Shortcut {
    				sequence: StandardKey.MoveToPreviousPage
    
    				onActivated: { chat.contentY = chat.contentY - chat.height / 2; chat.returnToBounds(); }
    
    			}
    			Shortcut {
    				sequence: StandardKey.MoveToNextPage
    
    				onActivated: { chat.contentY = chat.contentY + chat.height / 2; chat.returnToBounds(); }
    
    			ScrollBar.vertical: ScrollBar {
    				id: scrollbar
    
    				parent: chat.parent
    				anchors.top: chat.top
    				anchors.left: chat.right
    				anchors.bottom: chat.bottom
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    			verticalLayoutDirection: ListView.BottomToTop
    
    
    			onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom
    
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    			delegate: Rectangle {
    				// This would normally be previousSection, but our model's order is inverted.
    				property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
    
    				id: wrapper
    				property Item section
    				width: chat.width
    				height: section ? section.height + timelinerow.height : timelinerow.height
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    
    				TimelineRow {
    					id: timelinerow
    					y: section ? section.y + section.height : 0
    				}
    
    				onSectionBoundaryChanged: {
    					if (sectionBoundary) {
    						var properties = {
    							'modelData': model.dump,
    							'section': ListView.section,
    							'nextSection': ListView.nextSection
    						}
    						section = sectionHeader.createObject(wrapper, properties)
    					} else {
    						section.destroy()
    						section = null
    					}
    				}
    
    
    				Binding {
    					target: chat.model
    					property: "currentIndex"
    
    					when: y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    			}
    			Component {
    				id: sectionHeader
    				Column {
    					property var modelData
    					property string section
    					property string nextSection
    
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    					visible: !!modelData
    
    
    					width: parent.width
    					height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8
    
    					Label {
    						id: dateBubble
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    						anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    						text: chat.model.formatDateSeparator(modelData.timestamp)
    
    						color: colors.brightText
    
    
    						height: contentHeight * 1.2
    						width: contentWidth * 1.2
    						horizontalAlignment: Text.AlignHCenter
    						background: Rectangle {
    							radius: parent.height / 2
    
    							color: colors.dark
    
    					Row {
    						height: userName.height
    						spacing: 4
    						Avatar {
    							width: avatarSize
    							height: avatarSize
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    							url: chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/")
    							displayName: modelData.userName
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    								onClicked: chat.model.openUserProfile(modelData.userId)
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    							text: chat.model.escapeEmoji(modelData.userName)
    
    							color: timelineManager.userColor(modelData.userId, colors.window)
    
    							textFormat: Text.RichText
    
    							MouseArea {
    								anchors.fill: parent
    								onClicked: chat.model.openUserProfile(section.split(" ")[0])
    								cursorShape: Qt.PointingHandCursor
    
    
    		}
    
    		Rectangle {
    			id: chatFooter
    
    
    			height: Math.max(16, footerContent.height)
    
    			anchors.left: parent.left
    			anchors.right: parent.right
    			anchors.bottom: parent.bottom
    			z: 3
    
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    			color: "transparent"
    
    			Column {
    
    				id: footerContent
    
    				anchors.left: parent.left
    				anchors.right: parent.right
    
    					id: typingDisplay
    					anchors.left: parent.left
    					anchors.right: parent.right
    					anchors.leftMargin: 10
    					anchors.rightMargin: 10
    
    					text: chat.model ? chat.model.formatTypingUsers(chat.model.typingUsers, colors.window) : ""
    					textFormat: Text.RichText
    				}
    
    				Rectangle {
    
    					anchors.left: parent.left
    					anchors.right: parent.right
    
    
    					id: replyPopup
    
    					visible: timelineManager.replyingEvent && chat.model
    					// Height of child, plus margins, plus border
    					height: replyPreview.height + 10
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    					color: colors.base
    
    					Reply {
    						id: replyPreview
    
    						anchors.left: parent.left
    						anchors.leftMargin: 10
    						anchors.right: closeReplyButton.left
    
    						anchors.rightMargin: 20
    
    						anchors.bottom: parent.bottom
    
    						modelData: chat.model ? chat.model.getDump(timelineManager.replyingEvent) : {}
    
    						userColor: timelineManager.userColor(modelData.userId, colors.window)
    
    					ImageButton {
    						id: closeReplyButton
    
    
    						anchors.right: parent.right
    
    						anchors.rightMargin: 15
    
    						anchors.top: replyPreview.top
    
    						hoverEnabled: true
    
    						width: 16
    						height: 16
    
    						image: ":/icons/icons/ui/remove-symbol.png"
    
    						ToolTip.visible: closeReplyButton.hovered
    						ToolTip.text: qsTr("Close")
    
    						onClicked: timelineManager.closeReply()