diff options
author | Rémi Nicole <nicole@kolabsystems.com> | 2018-02-26 18:19:43 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-02-26 18:20:13 +0100 |
commit | 331f75d4da056ddb235be45a3784d2c19e545211 (patch) | |
tree | 64adc5432725e6d484012acfcea64b95b5d1711f | |
parent | 2dc4d380f8c0cc90aa0dec67a826703c3ceb34af (diff) | |
download | kube-331f75d4da056ddb235be45a3784d2c19e545211.tar.gz kube-331f75d4da056ddb235be45a3784d2c19e545211.zip |
Improvements of the log view
Summary:
- Add a test view for the log view
- Allow passing the `entities` part of Sink messages
- That allowed getting information about which mail could not be sent in sink transmission errors
Reviewers: cmollekopf
Reviewed By: cmollekopf
Differential Revision: https://phabricator.kde.org/D10861
-rw-r--r-- | framework/src/domain/maillistmodel.cpp | 33 | ||||
-rw-r--r-- | framework/src/domain/maillistmodel.h | 4 | ||||
-rw-r--r-- | framework/src/sinkfabric.cpp | 7 | ||||
-rw-r--r-- | tests/teststore.cpp | 17 | ||||
-rw-r--r-- | views/log/main.qml | 153 | ||||
-rw-r--r-- | views/log/qml/View.qml | 67 |
6 files changed, 269 insertions, 12 deletions
diff --git a/framework/src/domain/maillistmodel.cpp b/framework/src/domain/maillistmodel.cpp index e2f52d13..143c8a60 100644 --- a/framework/src/domain/maillistmodel.cpp +++ b/framework/src/domain/maillistmodel.cpp | |||
@@ -400,3 +400,36 @@ bool MailListModel::showInbox() const | |||
400 | { | 400 | { |
401 | return false; | 401 | return false; |
402 | } | 402 | } |
403 | |||
404 | void MailListModel::setEntityId(const QString &id) | ||
405 | { | ||
406 | qDebug() << "Running mail query for mail with ID:" << id; | ||
407 | using namespace Sink::ApplicationDomain; | ||
408 | Sink::Query query; | ||
409 | query.setFlags(Sink::Query::LiveQuery); | ||
410 | query.filter(id.toUtf8()); | ||
411 | query.request<Mail::Subject>(); | ||
412 | query.request<Mail::Sender>(); | ||
413 | query.request<Mail::To>(); | ||
414 | query.request<Mail::Cc>(); | ||
415 | query.request<Mail::Bcc>(); | ||
416 | query.request<Mail::Date>(); | ||
417 | query.request<Mail::Unread>(); | ||
418 | query.request<Mail::Important>(); | ||
419 | query.request<Mail::Draft>(); | ||
420 | query.request<Mail::Folder>(); | ||
421 | query.request<Mail::Sent>(); | ||
422 | query.request<Mail::Trash>(); | ||
423 | query.request<Mail::MimeMessage>(); | ||
424 | query.request<Mail::FullPayloadAvailable>(); | ||
425 | mFetchMails = true; | ||
426 | mFetchedMails.clear(); | ||
427 | // Latest mail at the top | ||
428 | sort(0, Qt::DescendingOrder); | ||
429 | runQuery(query); | ||
430 | } | ||
431 | |||
432 | QString MailListModel::entityId() const | ||
433 | { | ||
434 | return {}; | ||
435 | } | ||
diff --git a/framework/src/domain/maillistmodel.h b/framework/src/domain/maillistmodel.h index f83656b9..a6965915 100644 --- a/framework/src/domain/maillistmodel.h +++ b/framework/src/domain/maillistmodel.h | |||
@@ -33,6 +33,7 @@ class MailListModel : public QSortFilterProxyModel | |||
33 | Q_PROPERTY (QVariant mail READ mail WRITE setMail) | 33 | Q_PROPERTY (QVariant mail READ mail WRITE setMail) |
34 | Q_PROPERTY (bool showDrafts READ showDrafts WRITE setShowDrafts) | 34 | Q_PROPERTY (bool showDrafts READ showDrafts WRITE setShowDrafts) |
35 | Q_PROPERTY (bool showInbox READ showInbox WRITE setShowInbox) | 35 | Q_PROPERTY (bool showInbox READ showInbox WRITE setShowInbox) |
36 | Q_PROPERTY (QString entityId READ entityId WRITE setEntityId) | ||
36 | 37 | ||
37 | Q_PROPERTY (QString filter READ filter WRITE setFilter) | 38 | Q_PROPERTY (QString filter READ filter WRITE setFilter) |
38 | 39 | ||
@@ -93,6 +94,9 @@ public: | |||
93 | void setShowInbox(bool); | 94 | void setShowInbox(bool); |
94 | bool showInbox() const; | 95 | bool showInbox() const; |
95 | 96 | ||
97 | void setEntityId(const QString &id); | ||
98 | QString entityId() const; | ||
99 | |||
96 | private: | 100 | private: |
97 | void fetchMail(Sink::ApplicationDomain::Mail::Ptr mail); | 101 | void fetchMail(Sink::ApplicationDomain::Mail::Ptr mail); |
98 | 102 | ||
diff --git a/framework/src/sinkfabric.cpp b/framework/src/sinkfabric.cpp index 7d780ce3..8492f272 100644 --- a/framework/src/sinkfabric.cpp +++ b/framework/src/sinkfabric.cpp | |||
@@ -152,6 +152,13 @@ public: | |||
152 | QVariantMap message; | 152 | QVariantMap message; |
153 | if (notification.type == Sink::Notification::Warning) { | 153 | if (notification.type == Sink::Notification::Warning) { |
154 | message["type"] = "warning"; | 154 | message["type"] = "warning"; |
155 | |||
156 | QVariantList entities; | ||
157 | for(const auto &entity : notification.entities) { | ||
158 | entities << entity; | ||
159 | } | ||
160 | message["entities"] = entities; | ||
161 | |||
155 | message["resource"] = QString{notification.resource}; | 162 | message["resource"] = QString{notification.resource}; |
156 | if (notification.code == Sink::ApplicationDomain::TransmissionError) { | 163 | if (notification.code == Sink::ApplicationDomain::TransmissionError) { |
157 | message["message"] = QObject::tr("Failed to send message."); | 164 | message["message"] = QObject::tr("Failed to send message."); |
diff --git a/tests/teststore.cpp b/tests/teststore.cpp index e5a3ea88..9d56dd33 100644 --- a/tests/teststore.cpp +++ b/tests/teststore.cpp | |||
@@ -160,11 +160,14 @@ QVariant TestStore::load(const QByteArray &type, const QVariantMap &filter) | |||
160 | using namespace Sink::ApplicationDomain; | 160 | using namespace Sink::ApplicationDomain; |
161 | const auto list = loadList(type, filter); | 161 | const auto list = loadList(type, filter); |
162 | if (!list.isEmpty()) { | 162 | if (!list.isEmpty()) { |
163 | if (list.size() > 1) { | ||
164 | qWarning() << "While loading" << type << "with filter" << filter | ||
165 | << "; got multiple elements, but returning the first one."; | ||
166 | } | ||
163 | return list.first(); | 167 | return list.first(); |
164 | } | 168 | } |
165 | return {}; | 169 | return {}; |
166 | } | 170 | } |
167 | |||
168 | template <typename T> | 171 | template <typename T> |
169 | QVariantList toVariantList(const QList<T> &list) | 172 | QVariantList toVariantList(const QList<T> &list) |
170 | { | 173 | { |
@@ -183,6 +186,17 @@ QVariantList TestStore::loadList(const QByteArray &type, const QVariantMap &filt | |||
183 | if (filter.contains("resource")) { | 186 | if (filter.contains("resource")) { |
184 | query.resourceFilter(filter.value("resource").toByteArray()); | 187 | query.resourceFilter(filter.value("resource").toByteArray()); |
185 | } | 188 | } |
189 | |||
190 | for (QVariantMap::const_iterator it = filter.begin(); it != filter.end(); ++it) { | ||
191 | if (it.key() == "messageId") { | ||
192 | query.filter<Mail::MessageId>(it.value()); | ||
193 | } else if (it.key() == "draft") { | ||
194 | query.filter<Mail::Draft>(it.value()); | ||
195 | } else if (it.key() == "subject") { | ||
196 | query.filter<Mail::Subject>(it.value()); | ||
197 | } | ||
198 | } | ||
199 | |||
186 | if (type == "mail") { | 200 | if (type == "mail") { |
187 | return toVariantList(Sink::Store::read<Mail>(query)); | 201 | return toVariantList(Sink::Store::read<Mail>(query)); |
188 | } | 202 | } |
@@ -205,6 +219,7 @@ QVariantMap TestStore::read(const QVariant &object) | |||
205 | using namespace Sink::ApplicationDomain; | 219 | using namespace Sink::ApplicationDomain; |
206 | QVariantMap map; | 220 | QVariantMap map; |
207 | if (auto mail = object.value<Mail::Ptr>()) { | 221 | if (auto mail = object.value<Mail::Ptr>()) { |
222 | map.insert("uid", mail->identifier()); | ||
208 | map.insert("subject", mail->getSubject()); | 223 | map.insert("subject", mail->getSubject()); |
209 | map.insert("draft", mail->getDraft()); | 224 | map.insert("draft", mail->getDraft()); |
210 | return map; | 225 | return map; |
diff --git a/views/log/main.qml b/views/log/main.qml new file mode 100644 index 00000000..f4f20582 --- /dev/null +++ b/views/log/main.qml | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2018 Christian Mollekopf, <mollekopf@kolabsys.com> | ||
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 | |||
19 | import QtQuick 2.7 | ||
20 | import QtQuick.Controls 2.0 | ||
21 | import QtQuick.Window 2.0 | ||
22 | |||
23 | import org.kube.framework 1.0 as Kube | ||
24 | import org.kube.test 1.0 | ||
25 | import "qml" | ||
26 | |||
27 | ApplicationWindow { | ||
28 | id: app | ||
29 | height: Screen.desktopAvailableHeight * 0.8 | ||
30 | width: Screen.desktopAvailableWidth * 0.8 | ||
31 | |||
32 | Component.onCompleted: { | ||
33 | var initialState = { | ||
34 | accounts: [{ | ||
35 | id: "account1", | ||
36 | name: "Test Account" | ||
37 | }], | ||
38 | identities: [{ | ||
39 | account: "account1", | ||
40 | name: "Test Identity", | ||
41 | address: "identity@example.org" | ||
42 | }], | ||
43 | resources: [{ | ||
44 | id: "resource1", | ||
45 | account: "account1", | ||
46 | type: "dummy" | ||
47 | }, | ||
48 | { | ||
49 | id: "resource2", | ||
50 | account: "account1", | ||
51 | type: "mailtransport" | ||
52 | }], | ||
53 | folders: [{ | ||
54 | id: "folder1", | ||
55 | resource: "resource1", | ||
56 | name: "Folder 1", | ||
57 | specialpurpose: ["drafts"], | ||
58 | mails: [{ | ||
59 | resource: "resource1", | ||
60 | messageId: "<msg1@test.com>", | ||
61 | date: "2017-07-24T15:46:29", | ||
62 | subject: "subject1", | ||
63 | body: "body", | ||
64 | to: ["to@example.org"], | ||
65 | cc: ["cc@example.org"], | ||
66 | bcc: ["bcc@example.org"], | ||
67 | draft: true | ||
68 | }, | ||
69 | { | ||
70 | resource: "resource1", | ||
71 | messageId: "<msg2@test.com>", | ||
72 | date: "2017-07-23T15:46:29", | ||
73 | subject: "LooooooooooooooooooooooooooooooooooooooooooooooooooooooooonggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggEnd", | ||
74 | body: "LooooooooooooooooooooooooooooooooooooooooooooooooooooooooonggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggEnd\nbody\nbody\n\n\nbody\n\n\nbody\n\n\nbody\n\n\nbody\nbody\n\n\nbody\n\n\nbody\n\n\nbody\n\n\nbody\n\n\n\n\n\n\n\n\nbody\nbody\n\n\nbody\n\n\nbody\n\n\nbody\n\n\nbody\n\n\nbody", | ||
75 | to: ["toLoooooooooooooooooooooooooooooooooooooooooooooooooong@example.org"], | ||
76 | cc: ["ccLoooooooooooooooooooooooooooooooooooooooooooooooooong@example.org"], | ||
77 | bcc: ["bccLoooooooooooooooooooooooooooooooooooooooooooooooooong@example.org"], | ||
78 | draft: true | ||
79 | } | ||
80 | ] | ||
81 | }], | ||
82 | } | ||
83 | TestStore.setup(initialState) | ||
84 | Kube.Fabric.postMessage( | ||
85 | Kube.Messages.notification, | ||
86 | { | ||
87 | "type": Kube.Notifications.error, | ||
88 | "subtype": Kube.Notifications.loginError, | ||
89 | message: "merge1", | ||
90 | resource: "resource1", | ||
91 | entities: [] | ||
92 | } | ||
93 | ); | ||
94 | Kube.Fabric.postMessage( | ||
95 | Kube.Messages.notification, | ||
96 | { | ||
97 | "type": Kube.Notifications.error, | ||
98 | "subtype": Kube.Notifications.hostNotFoundError, | ||
99 | message: "merge1", | ||
100 | resource: "resource1", | ||
101 | entities: [] | ||
102 | } | ||
103 | ) | ||
104 | Kube.Fabric.postMessage( | ||
105 | Kube.Messages.notification, | ||
106 | { | ||
107 | "type": Kube.Notifications.error, | ||
108 | "subtype": Kube.Notifications.connectionError, | ||
109 | message: "merge1", | ||
110 | resource: "resource1", | ||
111 | entities: [] | ||
112 | } | ||
113 | ) | ||
114 | var mail = TestStore.load("mail", {messageId: "msg1@test.com"}) | ||
115 | var mail_uid = TestStore.read(mail).uid | ||
116 | Kube.Fabric.postMessage( | ||
117 | Kube.Messages.notification, | ||
118 | { | ||
119 | "type": Kube.Notifications.error, | ||
120 | "subtype": Kube.Notifications.transmissionError, | ||
121 | message: "merge1", | ||
122 | resource: "resource1", | ||
123 | entities: [mail_uid] | ||
124 | } | ||
125 | ) | ||
126 | var mail2 = TestStore.load("mail", {messageId: "msg2@test.com"}) | ||
127 | var mail2_uid = TestStore.read(mail2).uid | ||
128 | Kube.Fabric.postMessage( | ||
129 | Kube.Messages.notification, | ||
130 | { | ||
131 | "type": Kube.Notifications.error, | ||
132 | "subtype": Kube.Notifications.transmissionError, | ||
133 | message: "merge1", | ||
134 | resource: "resource1", | ||
135 | entities: [mail2_uid] | ||
136 | } | ||
137 | ) | ||
138 | Kube.Fabric.postMessage( | ||
139 | Kube.Messages.notification, | ||
140 | { | ||
141 | "type": Kube.Notifications.error, | ||
142 | "subtype": "customSubType", | ||
143 | message: "merge1", | ||
144 | resource: "resource1", | ||
145 | entities: [] | ||
146 | } | ||
147 | ) | ||
148 | } | ||
149 | |||
150 | View { | ||
151 | anchors.fill: parent | ||
152 | } | ||
153 | } | ||
diff --git a/views/log/qml/View.qml b/views/log/qml/View.qml index 14e2d543..7e95e20f 100644 --- a/views/log/qml/View.qml +++ b/views/log/qml/View.qml | |||
@@ -45,7 +45,18 @@ Controls.SplitView { | |||
45 | if (message.type == Kube.Notifications.error) { | 45 | if (message.type == Kube.Notifications.error) { |
46 | root.pendingError = true | 46 | root.pendingError = true |
47 | } | 47 | } |
48 | var error = {timestamp: new Date(), message: message.message, details: message.details, resource: message.resource} | 48 | |
49 | var error = { | ||
50 | timestamp: new Date(), | ||
51 | message: message.message, | ||
52 | details: message.details, | ||
53 | resource: message.resource, | ||
54 | // TODO: if we passed entities as a list, it would get | ||
55 | // converted to a ListModel, in all likelihood because of | ||
56 | // ListDelegate, which we should rewrite in C++ | ||
57 | entities: {elements: message.entities} | ||
58 | } | ||
59 | |||
49 | if (logModel.count > 0) { | 60 | if (logModel.count > 0) { |
50 | var lastEntry = logModel.get(0) | 61 | var lastEntry = logModel.get(0) |
51 | //Merge if we get an entry of the same subtype | 62 | //Merge if we get an entry of the same subtype |
@@ -89,6 +100,8 @@ Controls.SplitView { | |||
89 | } else { | 100 | } else { |
90 | details.subtype = "" | 101 | details.subtype = "" |
91 | } | 102 | } |
103 | |||
104 | details.entities = error.entities | ||
92 | } | 105 | } |
93 | 106 | ||
94 | delegate: Kube.ListDelegate { | 107 | delegate: Kube.ListDelegate { |
@@ -145,6 +158,7 @@ Controls.SplitView { | |||
145 | property date timestamp | 158 | property date timestamp |
146 | property string message: "" | 159 | property string message: "" |
147 | property string resourceId: "" | 160 | property string resourceId: "" |
161 | property var entities: [] | ||
148 | 162 | ||
149 | Kube.ModelIndexRetriever { | 163 | Kube.ModelIndexRetriever { |
150 | id: retriever | 164 | id: retriever |
@@ -166,6 +180,7 @@ Controls.SplitView { | |||
166 | property string resourceId: details.resourceId | 180 | property string resourceId: details.resourceId |
167 | property string accountId: retriever.currentData ? retriever.currentData.accountId : "" | 181 | property string accountId: retriever.currentData ? retriever.currentData.accountId : "" |
168 | property string accountName: retriever.currentData ? retriever.currentData.name : "" | 182 | property string accountName: retriever.currentData ? retriever.currentData.name : "" |
183 | property var entities: details.entities | ||
169 | 184 | ||
170 | function getComponent(subtype) { | 185 | function getComponent(subtype) { |
171 | if (subtype == Kube.Notifications.loginError) { | 186 | if (subtype == Kube.Notifications.loginError) { |
@@ -340,19 +355,49 @@ Controls.SplitView { | |||
340 | right: parent.right | 355 | right: parent.right |
341 | } | 356 | } |
342 | spacing: Kube.Units.largeSpacing | 357 | spacing: Kube.Units.largeSpacing |
358 | |||
359 | Kube.Heading { | ||
360 | id: heading | ||
361 | text: qsTr("Failed to send the message.") | ||
362 | color: Kube.Colors.warningColor | ||
363 | } | ||
364 | |||
343 | Column { | 365 | Column { |
344 | Kube.Heading { | 366 | spacing: Kube.Units.largeSpacing |
345 | id: heading | 367 | |
346 | text: qsTr("Failed to send the message.") | 368 | Repeater { |
347 | color: Kube.Colors.warningColor | 369 | model: Kube.MailListModel { |
348 | } | 370 | entityId: entities.elements[0] |
349 | Kube.Label { | 371 | } |
350 | id: subHeadline | 372 | delegate: Column { |
351 | text: accountName | 373 | id: subHeadline |
352 | color: Kube.Colors.disabledTextColor | 374 | |
353 | wrapMode: Text.Wrap | 375 | Kube.Label { |
376 | text: qsTr("Account") + ": " + accountName | ||
377 | color: Kube.Colors.disabledTextColor | ||
378 | wrapMode: Text.Wrap | ||
379 | } | ||
380 | Kube.Label { | ||
381 | text: qsTr("Subject") + ": " + model.subject | ||
382 | color: Kube.Colors.disabledTextColor | ||
383 | wrapMode: Text.Wrap | ||
384 | } | ||
385 | Kube.Label { | ||
386 | text: qsTr("To") + ": " + model.to | ||
387 | color: Kube.Colors.disabledTextColor | ||
388 | wrapMode: Text.Wrap | ||
389 | } | ||
390 | Kube.Label { | ||
391 | visible: !!model.cc | ||
392 | text: qsTr("Cc") + ": " + model.cc; | ||
393 | color: Kube.Colors.disabledTextColor | ||
394 | wrapMode: Text.Wrap | ||
395 | } | ||
396 | |||
397 | } | ||
354 | } | 398 | } |
355 | } | 399 | } |
400 | |||
356 | Kube.Button { | 401 | Kube.Button { |
357 | text: qsTr("Try again") | 402 | text: qsTr("Try again") |
358 | onClicked: { | 403 | onClicked: { |