ChatMessage.qml 9.01 KB
Newer Older
1 2 3
/*
 *  Kaidan - A user-friendly XMPP client for every device!
 *
LNJ's avatar
LNJ committed
4
 *  Copyright (C) 2016-2019 Kaidan developers and contributors
LNJ's avatar
LNJ committed
5
 *  (see the LICENSE file for a full list of copyright authors)
6 7 8 9 10 11
 *
 *  Kaidan is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
LNJ's avatar
LNJ committed
12 13 14 15 16 17 18 19 20 21
 *  In addition, as a special exception, the author of Kaidan gives
 *  permission to link the code of its release with the OpenSSL
 *  project's "OpenSSL" library (or with modified versions of it that
 *  use the same license as the "OpenSSL" library), and distribute the
 *  linked executables. You must obey the GNU General Public License in
 *  all respects for all of the code used other than "OpenSSL". If you
 *  modify this file, you may extend this exception to your version of
 *  the file, but you are not obligated to do so.  If you do not wish to
 *  do so, delete this exception statement from your version.
 *
22 23 24 25 26 27
 *  Kaidan is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
LNJ's avatar
LNJ committed
28
 *  along with Kaidan.  If not, see <http://www.gnu.org/licenses/>.
29 30
 */

31
import QtQuick 2.6
32 33
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
34
import QtQuick.Controls 2.2 as Controls
35
import org.kde.kirigami 2.0 as Kirigami
36
import im.kaidan.kaidan 1.0
37

Ilya Bizyaev's avatar
Ilya Bizyaev committed
38
RowLayout {
39
	id: root
40

41
	property string msgId
LNJ's avatar
LNJ committed
42
	property string sender
43
	property bool sentByMe: true
44 45
	property string messageBody
	property date dateTime
46
	property bool isRead: false
47
	property int mediaType
48
	property string mediaGetUrl
49
	property string mediaLocation
50
	property bool edited
51
	property bool isLoading: kaidan.transferCache.hasUpload(msgId)
52
	property string name
53 54 55
	property TransferJob upload: {
		if (mediaType !== Enums.MessageType.MessageText && isLoading) {
			return kaidan.transferCache.jobByMessageId(model.id)
56
		}
57 58

		return null
59
	}
60 61
	property bool isSpoiler
	property string spoilerHint
62
	property bool isShowingSpoiler: false
LNJ's avatar
LNJ committed
63
	property string avatarUrl: kaidan.avatarStorage.getAvatarUrl(sender)
64

LNJ's avatar
LNJ committed
65 66
	signal messageEditRequested(string id, string body)

67
	// own messages are on the right, others on the left
68
	layoutDirection: sentByMe ? Qt.RightToLeft : Qt.LeftToRight
69
	spacing: 8
70
	width: ListView.view.width
71 72 73

	// placeholder
	Item {
74
		Layout.preferredWidth: root.layoutDirection === Qt.LeftToRight ? 5 : 10
75
	}
Ilya Bizyaev's avatar
Ilya Bizyaev committed
76

77
	RoundImage {
78
		id: avatar
LNJ's avatar
LNJ committed
79 80
		visible: !sentByMe && avatarUrl
		source: avatarUrl
Ilya Bizyaev's avatar
Ilya Bizyaev committed
81
		fillMode: Image.PreserveAspectFit
82 83
		mipmap: true
		height: width
84
		Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Ilya Bizyaev's avatar
Ilya Bizyaev committed
85
		Layout.preferredHeight: Kirigami.Units.gridUnit * 2.2
86 87 88
		Layout.preferredWidth: Kirigami.Units.gridUnit * 2.2
		sourceSize.height: Kirigami.Units.gridUnit * 2.2
		sourceSize.width: Kirigami.Units.gridUnit * 2.2
89 90
	}

91 92
	TextAvatar {
		id: textAvatar
LNJ's avatar
LNJ committed
93
		visible: !sentByMe && !avatar.visible
94 95 96 97 98 99
		name: root.name
		Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
		Layout.preferredHeight: Kirigami.Units.gridUnit * 2.2
		Layout.preferredWidth: Kirigami.Units.gridUnit * 2.2
	}

100 101 102 103
	// message bubble/box
	Item {
		Layout.preferredWidth: content.width + 13
		Layout.preferredHeight: content.height + 8
104

105 106
		Rectangle {
			id: box
107
			anchors.fill: parent
108
			color: sentByMe ? Kirigami.Theme.complementaryTextColor
109
							: Kirigami.Theme.highlightColor
110
			radius: Kirigami.Units.smallSpacing * 2
111 112 113 114 115 116 117 118
			layer.enabled: box.visible
			layer.effect: DropShadow {
				verticalOffset: Kirigami.Units.gridUnit * 0.08
				horizontalOffset: Kirigami.Units.gridUnit * 0.08
				color: Kirigami.Theme.disabledTextColor
				samples: 10
				spread: 0.1
			}
119 120 121

			MouseArea {
				anchors.fill: parent
122
				acceptedButtons: Qt.LeftButton | Qt.RightButton
123
				onClicked: {
124 125 126 127
					if (mouse.button === Qt.RightButton)
						contextMenu.popup()
				}
				onPressAndHold: {
128
					contextMenu.popup()
129 130 131 132 133 134 135
				}
			}

			Controls.Menu {
				id: contextMenu
				Controls.MenuItem {
					text: qsTr("Copy Message")
136 137 138 139 140 141 142
					enabled: bodyLabel.visible
					onTriggered: {
						if (!isSpoiler || isShowingSpoiler)
							kaidan.utils.copyToClipboard(messageBody);
						else
							kaidan.utils.copyToClipboard(spoilerHint);
					}
143 144 145 146
				}

				Controls.MenuItem {
					text: qsTr("Edit Message")
147
					enabled: kaidan.messageModel.canCorrectMessage(msgId)
LNJ's avatar
LNJ committed
148
					onTriggered: root.messageEditRequested(msgId, messageBody)
149
				}
150 151 152 153 154 155

				Controls.MenuItem {
					text: qsTr("Copy download URL")
					enabled: mediaGetUrl
					onTriggered: kaidan.utils.copyToClipboard(mediaGetUrl)
				}
156
			}
157
		}
158

159 160 161
		ColumnLayout {
			id: content
			spacing: 0
162 163
			anchors.centerIn: parent
			anchors.margins: 4
164 165 166 167 168 169 170
			RowLayout {
				id: spoilerHintRow
				visible: isSpoiler
				MouseArea {
					anchors.fill: parent
					acceptedButtons: Qt.LeftButton | Qt.RightButton
					onClicked: {
LNJ's avatar
LNJ committed
171
						if (mouse.button === Qt.LeftButton) {
172 173 174 175 176 177 178
							isShowingSpoiler = !isShowingSpoiler
						}
					}
				}
				Controls.Label {
					id: dateLabeltest
					text: spoilerHint == "" ? qsTr("Spoiler") : spoilerHint
179
					color: sentByMe ? Kirigami.Theme.textColor
180 181 182
								: Kirigami.Theme.complementaryTextColor
					font.pixelSize: Kirigami.Units.gridUnit * 0.8
				}
183

184 185 186
				Item {
					Layout.fillWidth: true
					height: 1
187
				}
188 189 190 191 192

				Kirigami.Icon {
					height: 28
					width: 28
					source: isShowingSpoiler ? "password-show-off" : "password-show-on"
193
					color: sentByMe ? Kirigami.Theme.textColor : Kirigami.Theme.complementaryTextColor
194 195
				}
			}
196 197 198 199 200
			Kirigami.Separator {
				visible: isSpoiler
				Layout.fillWidth: true
				color: {
					var bgColor = sentByMe ? Kirigami.Theme.backgroundColor : Kirigami.Theme.highlightColor
201
					var textColor = sentByMe ? Kirigami.Theme.textColor : Kirigami.Theme.highlightedTextColor
202
					return Qt.tint(textColor, Qt.rgba(bgColor.r, bgColor.g, bgColor.b, 0.7))
203 204
				}
			}
205

206 207 208 209 210 211
			ColumnLayout {
				visible: isSpoiler && isShowingSpoiler || !isSpoiler


				Controls.ToolButton {
					visible: {
212 213 214 215
						mediaType !== Enums.MessageText &&
								!isLoading &&
								mediaLocation === "" &&
								mediaGetUrl !== ""
216 217 218
					}
					text: qsTr("Download")
					onClicked: {
219
						print("Downloading " + mediaGetUrl + "...")
220 221 222
						kaidan.downloadMedia(msgId, mediaGetUrl)
					}
				}
Ilya Bizyaev's avatar
Ilya Bizyaev committed
223

224 225 226 227
				// media loader
				Loader {
					id: media
					source: {
228
						if (mediaType === Enums.MessageImage &&
229 230 231 232 233 234 235 236 237 238 239 240 241
							mediaLocation !== "")
							"ChatMessageImage.qml"
						else
							""
					}
					property string sourceUrl: "file://" + mediaLocation
					Layout.maximumWidth: root.width - Kirigami.Units.gridUnit * 6
					Layout.preferredHeight: loaded ? item.paintedHeight : 0
				}


				// message body
				Controls.Label {
242
					id: bodyLabel
243
					visible: messageBody !== "" && messageBody !== mediaGetUrl
244
					text: kaidan.utils.formatMessage(messageBody)
245 246
					textFormat: Text.StyledText
					wrapMode: Text.Wrap
247
					color: sentByMe ? Kirigami.Theme.textColor
248
					                : Kirigami.Theme.complementaryTextColor
249 250 251 252 253 254 255 256 257 258 259
					onLinkActivated: Qt.openUrlExternally(link)

					Layout.maximumWidth: mediaType === Enums.MessageImage && media.width !== 0
										? media.width
										: root.width - Kirigami.Units.gridUnit * 6
				}
				Kirigami.Separator {
					visible: isSpoiler && isShowingSpoiler
					Layout.fillWidth: true
					color: {
						var bgColor = sentByMe ? Kirigami.Theme.backgroundColor : Kirigami.Theme.highlightColor
260
						var textColor = sentByMe ? Kirigami.Theme.textColor : Kirigami.Theme.highlightedTextColor
261 262 263 264
						return Qt.tint(textColor, Qt.rgba(bgColor.r, bgColor.g, bgColor.b, 0.7))
					}
				}
			}
265
			// message meta: date, isRead
266
			RowLayout {
267
				// progress bar for upload/download status
268
				Controls.ProgressBar {
269
					visible: isLoading
270
					value: upload ? upload.progress : 0
271 272
				}

273 274 275
				Controls.Label {
					id: dateLabel
					text: Qt.formatDateTime(dateTime, "dd. MMM yyyy, hh:mm")
276 277
					color: sentByMe ? Kirigami.Theme.disabledTextColor
					                : Qt.darker(Kirigami.Theme.disabledTextColor, 1.3)
278 279 280 281 282 283
					font.pixelSize: Kirigami.Units.gridUnit * 0.8
				}

				Image {
					id: checkmark
					visible: (sentByMe && isRead)
284
					source: kaidan.utils.getResourcePath("images/message_checkmark.svg")
285 286 287 288 289
					Layout.preferredHeight: Kirigami.Units.gridUnit * 0.65
					Layout.preferredWidth: Kirigami.Units.gridUnit * 0.65
					sourceSize.height: Kirigami.Units.gridUnit * 0.65
					sourceSize.width: Kirigami.Units.gridUnit * 0.65
				}
290 291 292 293 294
				Kirigami.Icon {
					source: "edit-symbolic"
					visible: edited
					Layout.preferredHeight: Kirigami.Units.gridUnit * 0.65
					Layout.preferredWidth: Kirigami.Units.gridUnit * 0.65
LNJ's avatar
LNJ committed
295
				}
296 297
			}
		}
298
	}
Ilya Bizyaev's avatar
Ilya Bizyaev committed
299

300
	// placeholder
Ilya Bizyaev's avatar
Ilya Bizyaev committed
301 302 303
	Item {
		Layout.fillWidth: true
	}
304 305 306 307 308 309 310 311 312 313 314

	function updateIsLoading() {
		isLoading = kaidan.transferCache.hasUpload(msgId)
	}

	Component.onCompleted: {
		kaidan.transferCache.jobsChanged.connect(updateIsLoading)
	}
	Component.onDestruction: {
		kaidan.transferCache.jobsChanged.disconnect(updateIsLoading)
	}
315
}