summaryrefslogtreecommitdiffstats
path: root/framework/qml/ConversationView.qml
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-04-04 19:19:41 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-04-04 19:19:41 +0200
commitd9295fc8f19e4005f8454e7f193f80316550ac0c (patch)
treef27c370d54bced09212b9c4a12b827d1cebb6110 /framework/qml/ConversationView.qml
parentd002eae7f8b443dd1bad914444c296088c2b6e85 (diff)
downloadkube-d9295fc8f19e4005f8454e7f193f80316550ac0c.tar.gz
kube-d9295fc8f19e4005f8454e7f193f80316550ac0c.zip
One framework plugin to rule them all
Diffstat (limited to 'framework/qml/ConversationView.qml')
-rw-r--r--framework/qml/ConversationView.qml574
1 files changed, 574 insertions, 0 deletions
diff --git a/framework/qml/ConversationView.qml b/framework/qml/ConversationView.qml
new file mode 100644
index 00000000..4196ebbd
--- /dev/null
+++ b/framework/qml/ConversationView.qml
@@ -0,0 +1,574 @@
1/*
2 * Copyright (C) 2016 Michael Bohlender, <michael.bohlender@kdemail.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19import QtQuick 2.7
20import QtQuick.Controls 1.3 as Controls1
21import QtQuick.Controls 2
22import QtQuick.Layouts 1.1
23import org.kde.kirigami 1.0 as Kirigami
24
25import QtQml 2.2 as QtQml
26
27import org.kube.framework.domain 1.0 as KubeFramework
28import org.kube.framework.actions 1.0 as KubeAction
29
30import org.kube.components.theme 1.0 as KubeTheme
31
32Rectangle {
33 id: root
34
35 property variant mail;
36 property int currentIndex: 0;
37 property bool scrollToEnd: true;
38 property variant currentMail: null;
39 onCurrentIndexChanged: {
40 markAsReadTimer.restart();
41 }
42 onMailChanged: {
43 scrollToEnd = true;
44 currentMail = null;
45 }
46
47 color: KubeTheme.Colors.backgroundColor
48
49 ListView {
50 id: listView
51 function setCurrentIndex()
52 {
53 /**
54 * This will detect the index at the "scrollbar-position" (visibleArea.yPosition).
55 * This ensures that the first and last entry can become the currentIndex,
56 * but in the middle of the list the item in the middle is set as the current item.
57 */
58 var yPos = 0.5;
59 if (listView.visibleArea.yPosition < 0.4) {
60 yPos = 0.2 + (0.2 * listView.visibleArea.yPosition);
61 }
62 if (listView.visibleArea.yPosition > 0.6) {
63 yPos = 0.6 + (0.2 * listView.visibleArea.yPosition)
64 }
65 var indexAtCenter = listView.indexAt(root.width / 2, contentY + root.height * yPos);
66 if (indexAtCenter >= 0) {
67 root.currentIndex = indexAtCenter;
68 } else {
69 root.currentIndex = count - 1;
70 }
71 }
72
73 anchors {
74 top: parent.top
75 left: parent.left
76 right: parent.right
77 bottom: parent.bottom
78 }
79
80 clip: true
81
82 model: KubeFramework.MailListModel {
83 mail: root.mail
84 }
85
86 header: Item {
87 height: KubeTheme.Units.gridUnit * 0.5
88 width: parent.width
89
90 }
91
92 footer: Item {
93 height: KubeTheme.Units.gridUnit
94 width: parent.width
95 }
96
97 delegate: mailDelegate
98
99 //Setting the currentIndex results in further lags. So we don't do that either.
100 // currentIndex: root.currentIndex
101
102 boundsBehavior: Flickable.StopAtBounds
103
104 //default is 1500, which is not usable with a mouse
105 flickDeceleration: 10000
106
107 //Optimize for view quality
108 pixelAligned: true
109
110 Timer {
111 id: scrollToEndTimer
112 interval: 10
113 running: false
114 repeat: false
115 onTriggered: {
116 //Only do this once per conversation
117 root.scrollToEnd = false;
118 root.currentIndex = listView.count - 1
119 //positionViewAtEnd/Index don't work
120 listView.contentY = Math.max(listView.contentHeight - listView.height, 0)
121 }
122 }
123
124 onCountChanged: {
125 if (root.scrollToEnd) {
126 scrollToEndTimer.restart()
127 }
128 }
129
130 onContentHeightChanged: {
131 //Initially it will resize a lot, so we keep waiting
132 if (root.scrollToEnd) {
133 scrollToEndTimer.restart()
134 }
135 }
136
137 onContentYChanged: {
138 //We have to track our current mail manually
139 setCurrentIndex();
140 }
141
142 //The cacheBuffer needs to be large enough to fit the whole thread.
143 //Otherwise the contentHeight will constantly increase and decrease,
144 //which will break lot's of things.
145 cacheBuffer: 100000
146
147 KubeFramework.MailController {
148 id: mailController
149 Binding on mail {
150 //!! checks for the availability of the type
151 when: !!root.currentMail
152 value: root.currentMail
153 }
154 }
155
156 Timer {
157 id: markAsReadTimer
158 interval: 2000
159 running: false
160 repeat: false
161 onTriggered: {
162 if (mailController.markAsReadAction.enabled) {
163 mailController.markAsReadAction.execute();
164 }
165 }
166 }
167
168 //Intercept all scroll events,
169 //necessary due to the webengineview
170 KubeFramework.MouseProxy {
171 anchors.fill: parent
172 target: listView
173 forwardWheelEvents: true
174 }
175 }
176 Component {
177 id: mailDelegate
178
179 Item {
180 id: wrapper
181 property bool isCurrent: root.currentIndex === index;
182 onIsCurrentChanged: {
183 if (isCurrent) {
184 root.currentMail = model.mail
185 }
186 }
187
188 height: sheet.height + KubeTheme.Units.gridUnit
189 width: parent.width
190
191 Rectangle {
192 id: sheet
193 anchors.centerIn: parent
194 implicitHeight: header.height + attachments.height + body.height + incompleteBody.height + footer.height + KubeTheme.Units.largeSpacing
195 width: parent.width - KubeTheme.Units.gridUnit * 2
196
197 //Overlay for non-active mails
198 Rectangle {
199 anchors.fill: parent
200 visible: !wrapper.isCurrent
201 color: "lightGrey"
202 z: 1
203 opacity: 0.2
204 }
205
206 color: KubeTheme.Colors.viewBackgroundColor
207
208 //BEGIN header
209 Item {
210 id: header
211
212 anchors {
213 top: parent.top
214 left: parent.left
215 right: parent.right
216 margins: KubeTheme.Units.largeSpacing
217 }
218
219 height: headerContent.height + KubeTheme.Units.smallSpacing
220
221 states: [
222 State {
223 name: "small"
224 PropertyChanges { target: subject; wrapMode: Text.NoWrap}
225 PropertyChanges { target: recipients; visible: true}
226 PropertyChanges { target: to; visible: false}
227 PropertyChanges { target: cc; visible: false}
228 PropertyChanges { target: bcc; visible: false}
229 },
230 State {
231 name: "details"
232 PropertyChanges { target: subject; wrapMode: Text.WrapAnywhere}
233 PropertyChanges { target: recipients; visible: false}
234 PropertyChanges { target: to; visible: true}
235 PropertyChanges { target: cc; visible: true}
236 PropertyChanges { target: bcc; visible: true}
237 }
238 ]
239
240 state: "small"
241
242 Text {
243 id: date_label
244
245 anchors {
246 right: seperator.right
247 top: parent.top
248 }
249
250 text: Qt.formatDateTime(model.date, "dd MMM yyyy hh:mm")
251
252 font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.7
253 color: KubeTheme.Colors.textColor
254 opacity: 0.75
255 }
256
257 Column {
258 id: headerContent
259
260 anchors {
261 //left: to_l.right
262 horizontalCenter: parent.horizontalCenter
263 }
264
265 //spacing: KubeTheme.Units.smallSpacing
266
267 width: parent.width
268
269 Row{
270 id: from
271
272 width: parent.width
273
274 spacing: KubeTheme.Units.smallSpacing
275 clip: true
276
277 Text {
278 id: senderName
279
280 text: model.senderName
281
282 font.weight: Font.DemiBold
283 color: KubeTheme.Colors.textColor
284 opacity: 0.75
285 }
286
287 Text {
288
289 text: model.sender
290
291 width: parent.width - senderName.width - date_label.width - KubeTheme.Units.largeSpacing
292 elide: Text.ElideRight
293
294 color: KubeTheme.Colors.textColor
295 opacity: 0.75
296
297 clip: true
298 }
299 }
300
301 Text {
302 id: subject
303
304 width: to.width
305
306 text: model.subject
307
308 elide: Text.ElideRight
309
310 color: KubeTheme.Colors.textColor
311 opacity: 0.75
312 font.italic: true
313 }
314
315 Text {
316 id: recipients
317
318 width: parent.width - goDown.width - KubeTheme.Units.smallSpacing
319
320 text:"to: "+ model.to + " " + model.cc + " " + model.bcc
321
322 elide: Text.ElideRight
323
324 color: KubeTheme.Colors.textColor
325 opacity: 0.75
326 }
327
328 Text {
329 id: to
330
331 width: parent.width - goDown.width - KubeTheme.Units.smallSpacing
332
333 text:"to: " + model.to
334
335 wrapMode: Text.WordWrap
336 color: KubeTheme.Colors.textColor
337 opacity: 0.75
338 }
339
340 Text {
341 id: cc
342
343 width: parent.width - goDown.width - KubeTheme.Units.smallSpacing
344
345 text:"cc: " + model.cc
346
347 wrapMode: Text.WordWrap
348 color: KubeTheme.Colors.textColor
349 opacity: 0.75
350 }
351
352 Text {
353 id: bcc
354
355 width: parent.width - goDown.width - KubeTheme.Units.smallSpacing
356
357 text:"bcc: " + model.bcc
358
359 wrapMode: Text.WordWrap
360 color: KubeTheme.Colors.textColor
361 opacity: 0.75
362 }
363
364 }
365 Rectangle {
366 id: goDown
367 anchors {
368 bottom: seperator.top
369 right: seperator.right
370 }
371
372 height: KubeTheme.Units.gridUnit
373 width: height
374
375 color: KubeTheme.Colors.backgroundColor
376
377 Controls1.ToolButton {
378 anchors.fill: parent
379
380 iconName: KubeTheme.Icons.goDown
381 }
382 }
383
384 Rectangle {
385 anchors {
386 bottom: seperator.top
387 right: seperator.right
388 }
389
390 height: KubeTheme.Units.gridUnit
391 width: height
392
393 color: KubeTheme.Colors.backgroundColor
394
395 Controls1.ToolButton {
396 anchors.fill: parent
397
398 iconName: header.state === "details" ? KubeTheme.Icons.goUp : KubeTheme.Icons.goDown
399
400 onClicked: {
401 header.state === "details" ? header.state = "small" : header.state = "details"
402 }
403 }
404 }
405
406 Rectangle {
407 id: seperator
408
409 anchors {
410 left: parent.left
411 right: parent.right
412 bottom: parent.bottom
413 }
414
415 height: 1
416
417 color: KubeTheme.Colors.textColor
418 opacity: 0.5
419 }
420 }
421 //END header
422
423 Flow {
424 id: attachments
425
426 anchors {
427 top: header.bottom
428 topMargin: KubeTheme.Units.smallSpacing
429 right: header.right
430 }
431
432 width: header.width - KubeTheme.Units.largeSpacing
433
434 layoutDirection: Qt.RightToLeft
435 spacing: KubeTheme.Units.smallSpacing
436 clip: true
437
438 Repeater {
439 model: body.attachments
440
441 delegate: AttachmentDelegate {
442 name: model.name
443 icon: "mail-attachment"
444
445 clip: true
446
447 //TODO size encrypted signed type
448 }
449 }
450 }
451
452 MailViewer {
453 id: body
454
455 anchors {
456 top: header.bottom
457 left: header.left
458 right: header.right
459 leftMargin: KubeTheme.Units.largeSpacing
460 rightMargin: KubeTheme.Units.largeSpacing
461 topMargin: Math.max(attachments.height, KubeTheme.Units.largeSpacing)
462 }
463
464 width: header.width - KubeTheme.Units.largeSpacing * 2
465 height: desiredHeight
466
467 message: model.mimeMessage
468 visible: !model.incomplete
469 }
470
471 Label {
472 id: incompleteBody
473 anchors {
474 top: header.bottom
475 left: header.left
476 right: header.right
477 leftMargin: KubeTheme.Units.largeSpacing
478 rightMargin: KubeTheme.Units.largeSpacing
479 topMargin: Math.max(attachments.height, KubeTheme.Units.largeSpacing)
480 }
481 visible: model.incomplete
482 text: "Incomplete body..."
483 color: KubeTheme.Colors.textColor
484 enabled: false
485 states: [
486 State {
487 name: "inprogress"; when: model.status == KubeFramework.MailListModel.InProgressStatus
488 PropertyChanges { target: incompleteBody; text: "Downloading message..." }
489 },
490 State {
491 name: "error"; when: model.status == KubeFramework.MailListModel.ErrorStatus
492 PropertyChanges { target: incompleteBody; text: "Failed to download message..." }
493 }
494 ]
495 }
496 Item {
497 id: footer
498
499 anchors.bottom: parent.bottom
500
501 height: KubeTheme.Units.gridUnit * 2
502 width: parent.width
503
504 Text {
505 anchors{
506 verticalCenter: parent.verticalCenter
507 left: parent.left
508 leftMargin: KubeTheme.Units.largeSpacing
509 }
510
511 KubeFramework.MailController {
512 id: mailController
513 mail: model.mail
514 }
515
516 text: model.trash ? qsTr("Delete Mail") : qsTr("Move to trash")
517 color: KubeTheme.Colors.textColor
518 opacity: 0.5
519 enabled: model.trash ? mailController.removeAction.enabled : mailController.moveToTrashAction.enabled
520 MouseArea {
521 anchors.fill: parent
522 enabled: parent.enabled
523 onClicked: {
524 if (model.trash) {
525 mailController.removeAction.execute();
526 } else {
527 mailController.moveToTrashAction.execute();
528 }
529 }
530 }
531 }
532
533 Controls1.ToolButton {
534 visible: !model.trash
535 anchors{
536 verticalCenter: parent.verticalCenter
537 right: parent.right
538 rightMargin: KubeTheme.Units.largeSpacing
539 }
540
541 KubeAction.Context {
542 id: mailcontext
543 property variant mail
544 property bool isDraft
545 mail: model.mail
546 isDraft: model.draft
547 }
548
549 KubeAction.Action {
550 id: replyAction
551 actionId: "org.kde.kube.actions.reply"
552 context: maillistcontext
553 }
554
555 KubeAction.Action {
556 id: editAction
557 actionId: "org.kde.kube.actions.edit"
558 context: maillistcontext
559 }
560
561 iconName: model.draft ? KubeTheme.Icons.edit : KubeTheme.Icons.replyToSender
562 onClicked: {
563 if (model.draft) {
564 editAction.execute()
565 } else {
566 replyAction.execute()
567 }
568 }
569 }
570 }
571 }
572 }
573 }
574}