summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accounts/imap/tests/settingstest.cpp4
-rw-r--r--accounts/maildir/maildircontroller.cpp2
-rw-r--r--accounts/maildir/tests/settingstest.cpp4
-rw-r--r--components/package/contents/ui/SingleMailView.qml2
-rw-r--r--framework/accounts/accountfactory.cpp2
-rw-r--r--framework/accounts/gmailcontroller.cpp2
-rw-r--r--framework/accounts/kolabnowcontroller.cpp2
-rw-r--r--framework/accounts/maildircontroller.cpp4
-rw-r--r--framework/actions/actionhandler.cpp2
-rw-r--r--framework/actions/actionhandler.h2
-rw-r--r--framework/domain/composercontroller.cpp51
-rw-r--r--framework/domain/composercontroller.h3
-rw-r--r--framework/domain/controller.cpp4
-rw-r--r--framework/domain/controller.h2
-rw-r--r--framework/domain/foldercontroller.cpp18
-rw-r--r--framework/domain/foldercontroller.h2
-rw-r--r--framework/domain/identitiesmodel.cpp2
-rw-r--r--framework/domain/mailcontroller.cpp78
-rw-r--r--framework/domain/mailcontroller.h4
-rw-r--r--framework/domain/maillistmodel.cpp17
-rw-r--r--framework/domain/maillistmodel.h4
-rw-r--r--framework/domain/selector.cpp5
-rw-r--r--framework/domain/selector.h2
-rw-r--r--framework/domain/settings/accountsettings.cpp10
24 files changed, 164 insertions, 64 deletions
diff --git a/accounts/imap/tests/settingstest.cpp b/accounts/imap/tests/settingstest.cpp
index 2fbc9b7b..69d93079 100644
--- a/accounts/imap/tests/settingstest.cpp
+++ b/accounts/imap/tests/settingstest.cpp
@@ -43,7 +43,7 @@ private slots:
43 settings.setProperty("emailAddress", emailAddress); 43 settings.setProperty("emailAddress", emailAddress);
44 settings.save(); 44 settings.save();
45 45
46 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).syncThen<void, QList<Sink::ApplicationDomain::SinkResource::Ptr>>([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) { 46 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).then([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) {
47 QCOMPARE(resources.size(), 2); 47 QCOMPARE(resources.size(), 2);
48 }) 48 })
49 .exec().waitForFinished(); 49 .exec().waitForFinished();
@@ -76,7 +76,7 @@ private slots:
76 settings.remove(); 76 settings.remove();
77 } 77 }
78 78
79 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).syncThen<void, QList<Sink::ApplicationDomain::SinkResource::Ptr>>([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) { 79 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).then([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) {
80 QCOMPARE(resources.size(), 0); 80 QCOMPARE(resources.size(), 0);
81 }) 81 })
82 .exec().waitForFinished(); 82 .exec().waitForFinished();
diff --git a/accounts/maildir/maildircontroller.cpp b/accounts/maildir/maildircontroller.cpp
index dc34d59a..fa211284 100644
--- a/accounts/maildir/maildircontroller.cpp
+++ b/accounts/maildir/maildircontroller.cpp
@@ -85,7 +85,7 @@ void MaildirController::loadAccount(const QByteArray &id)
85{ 85{
86 Q_ASSERT(!mAccountIdentifier.isEmpty()); 86 Q_ASSERT(!mAccountIdentifier.isEmpty());
87 Store::fetchOne<SinkAccount>(Query().filter(mAccountIdentifier)) 87 Store::fetchOne<SinkAccount>(Query().filter(mAccountIdentifier))
88 .syncThen<void, SinkAccount>([this](const SinkAccount &account) { 88 .then([this](const SinkAccount &account) {
89 mIcon = account.getIcon(); 89 mIcon = account.getIcon();
90 mName = account.getName(); 90 mName = account.getName();
91 emit nameChanged(); 91 emit nameChanged();
diff --git a/accounts/maildir/tests/settingstest.cpp b/accounts/maildir/tests/settingstest.cpp
index 47cb3b35..bf041e3c 100644
--- a/accounts/maildir/tests/settingstest.cpp
+++ b/accounts/maildir/tests/settingstest.cpp
@@ -39,7 +39,7 @@ private slots:
39 settings.setProperty("emailAddress", emailAddress); 39 settings.setProperty("emailAddress", emailAddress);
40 settings.save(); 40 settings.save();
41 41
42 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).syncThen<void, QList<Sink::ApplicationDomain::SinkResource::Ptr>>([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) { 42 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).then([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) {
43 QCOMPARE(resources.size(), 2); 43 QCOMPARE(resources.size(), 2);
44 }) 44 })
45 .exec().waitForFinished(); 45 .exec().waitForFinished();
@@ -70,7 +70,7 @@ private slots:
70 settings.remove(); 70 settings.remove();
71 } 71 }
72 72
73 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).syncThen<void, QList<Sink::ApplicationDomain::SinkResource::Ptr>>([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) { 73 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkResource>(Sink::Query()).then([](const QList<Sink::ApplicationDomain::SinkResource::Ptr> &resources) {
74 QCOMPARE(resources.size(), 0); 74 QCOMPARE(resources.size(), 0);
75 }) 75 })
76 .exec().waitForFinished(); 76 .exec().waitForFinished();
diff --git a/components/package/contents/ui/SingleMailView.qml b/components/package/contents/ui/SingleMailView.qml
index 0b70ddfe..ab203ef5 100644
--- a/components/package/contents/ui/SingleMailView.qml
+++ b/components/package/contents/ui/SingleMailView.qml
@@ -365,7 +365,7 @@ Rectangle {
365 365
366 KubeFramework.MailController { 366 KubeFramework.MailController {
367 id: mailController 367 id: mailController
368 mail: model.mail 368 threadLeader: model.mail
369 } 369 }
370 370
371 text: model.trash ? qsTr("Delete Mail") : qsTr("Move to trash") 371 text: model.trash ? qsTr("Delete Mail") : qsTr("Move to trash")
diff --git a/framework/accounts/accountfactory.cpp b/framework/accounts/accountfactory.cpp
index 182a0a1d..c590e4b6 100644
--- a/framework/accounts/accountfactory.cpp
+++ b/framework/accounts/accountfactory.cpp
@@ -44,7 +44,7 @@ void AccountFactory::setAccountId(const QString &accountId)
44{ 44{
45 mAccountId = accountId; 45 mAccountId = accountId;
46 Sink::Store::fetchOne<Sink::ApplicationDomain::SinkAccount>(Sink::Query().filter(accountId.toUtf8())) 46 Sink::Store::fetchOne<Sink::ApplicationDomain::SinkAccount>(Sink::Query().filter(accountId.toUtf8()))
47 .syncThen<void, Sink::ApplicationDomain::SinkAccount>([this](const Sink::ApplicationDomain::SinkAccount &account) { 47 .then([this](const Sink::ApplicationDomain::SinkAccount &account) {
48 mAccountType = account.getProperty("type").toByteArray(); 48 mAccountType = account.getProperty("type").toByteArray();
49 loadPackage(); 49 loadPackage();
50 }).exec(); 50 }).exec();
diff --git a/framework/accounts/gmailcontroller.cpp b/framework/accounts/gmailcontroller.cpp
index bc644ae9..89877117 100644
--- a/framework/accounts/gmailcontroller.cpp
+++ b/framework/accounts/gmailcontroller.cpp
@@ -80,7 +80,7 @@ void GmailController::load(const QByteArray &id) {
80 m_accountId = id; 80 m_accountId = id;
81 81
82 Store::fetchOne<SinkAccount>(Query().filter(m_accountId)) 82 Store::fetchOne<SinkAccount>(Query().filter(m_accountId))
83 .syncThen<void, SinkAccount>([this](const SinkAccount &account) { 83 .then([this](const SinkAccount &account) {
84 setName(account.getName()); 84 setName(account.getName());
85 }).exec(); 85 }).exec();
86 86
diff --git a/framework/accounts/kolabnowcontroller.cpp b/framework/accounts/kolabnowcontroller.cpp
index 1cbe2f5b..05577754 100644
--- a/framework/accounts/kolabnowcontroller.cpp
+++ b/framework/accounts/kolabnowcontroller.cpp
@@ -80,7 +80,7 @@ void KolabNowController::load(const QByteArray &id) {
80 m_accountId = id; 80 m_accountId = id;
81 81
82 Store::fetchOne<SinkAccount>(Query().filter(m_accountId)) 82 Store::fetchOne<SinkAccount>(Query().filter(m_accountId))
83 .syncThen<void, SinkAccount>([this](const SinkAccount &account) { 83 .then([this](const SinkAccount &account) {
84 setName(account.getName()); 84 setName(account.getName());
85 }).exec(); 85 }).exec();
86 86
diff --git a/framework/accounts/maildircontroller.cpp b/framework/accounts/maildircontroller.cpp
index 481d43d8..c2e15eb8 100644
--- a/framework/accounts/maildircontroller.cpp
+++ b/framework/accounts/maildircontroller.cpp
@@ -65,13 +65,13 @@ void MaildirController::load(const QByteArray &id) {
65 clear(); 65 clear();
66 66
67 Store::fetchOne<SinkAccount>(Query().filter(m_accountId)) 67 Store::fetchOne<SinkAccount>(Query().filter(m_accountId))
68 .syncThen<void, SinkAccount>([this](const SinkAccount &account) { 68 .then([this](const SinkAccount &account) {
69 setIcon(account.getIcon()); 69 setIcon(account.getIcon());
70 setName(account.getName()); 70 setName(account.getName());
71 }).exec(); 71 }).exec();
72 72
73 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(m_accountId).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::storage)) 73 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(m_accountId).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::storage))
74 .syncThen<void, SinkResource>([this](const SinkResource &resource) { 74 .then([this](const SinkResource &resource) {
75 m_resourceId = resource.identifier(); 75 m_resourceId = resource.identifier();
76 setPath(resource.getProperty("path").toString()); 76 setPath(resource.getProperty("path").toString());
77 }).exec(); 77 }).exec();
diff --git a/framework/actions/actionhandler.cpp b/framework/actions/actionhandler.cpp
index eb7b3224..99fdf66a 100644
--- a/framework/actions/actionhandler.cpp
+++ b/framework/actions/actionhandler.cpp
@@ -138,7 +138,7 @@ ActionResult ActionHandlerHelper::execute(Context *context)
138 handlerFunction(context); 138 handlerFunction(context);
139 result.setDone(); 139 result.setDone();
140 } else { 140 } else {
141 jobHandlerFunction(context).syncThen<void>([=](const KAsync::Error &error) { 141 jobHandlerFunction(context).then([=](const KAsync::Error &error) {
142 auto modifyableResult = result; 142 auto modifyableResult = result;
143 if (error) { 143 if (error) {
144 qWarning() << "Job failed: " << error.errorCode << error.errorMessage; 144 qWarning() << "Job failed: " << error.errorCode << error.errorMessage;
diff --git a/framework/actions/actionhandler.h b/framework/actions/actionhandler.h
index 5ccf0ac7..c0e1b318 100644
--- a/framework/actions/actionhandler.h
+++ b/framework/actions/actionhandler.h
@@ -73,7 +73,7 @@ public:
73 ActionResult result; 73 ActionResult result;
74 auto wrapper = ContextType{*c}; 74 auto wrapper = ContextType{*c};
75 execute(wrapper) 75 execute(wrapper)
76 .template syncThen<void>([=](const KAsync::Error &error) { 76 .template then([=](const KAsync::Error &error) {
77 auto modifyableResult = result; 77 auto modifyableResult = result;
78 if (error) { 78 if (error) {
79 qWarning() << "Job failed: " << error.errorCode << error.errorMessage; 79 qWarning() << "Job failed: " << error.errorCode << error.errorMessage;
diff --git a/framework/domain/composercontroller.cpp b/framework/domain/composercontroller.cpp
index a4a8857f..207c5047 100644
--- a/framework/domain/composercontroller.cpp
+++ b/framework/domain/composercontroller.cpp
@@ -97,9 +97,20 @@ ComposerController::ComposerController()
97 97
98 QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction); 98 QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction);
99 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); 99 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction);
100 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction);
101 QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSaveAsDraftAction);
102 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction);
103 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction);
100 updateSendAction(); 104 updateSendAction();
101} 105}
102 106
107void ComposerController::clear()
108{
109 Controller::clear();
110 //Reapply account and identity from selection
111 mIdentitySelector->reapplyCurrentIndex();
112}
113
103Completer *ComposerController::recipientCompleter() const 114Completer *ComposerController::recipientCompleter() const
104{ 115{
105 return mRecipientCompleter.data(); 116 return mRecipientCompleter.data();
@@ -123,7 +134,7 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft)
123{ 134{
124 Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>()); 135 Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>());
125 query.request<Sink::ApplicationDomain::Mail::MimeMessage>(); 136 query.request<Sink::ApplicationDomain::Mail::MimeMessage>();
126 Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).syncThen<void, Sink::ApplicationDomain::Mail>([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) { 137 Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).then([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) {
127 setExistingMail(mail); 138 setExistingMail(mail);
128 139
129 //TODO this should probably happen as reaction to the property being set. 140 //TODO this should probably happen as reaction to the property being set.
@@ -199,7 +210,7 @@ KMime::Message::Ptr ComposerController::assembleMessage()
199 210
200void ComposerController::updateSendAction() 211void ComposerController::updateSendAction()
201{ 212{
202 auto enabled = !getTo().isEmpty() && !getSubject().isEmpty(); 213 auto enabled = !getTo().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty();
203 sendAction()->setEnabled(enabled); 214 sendAction()->setEnabled(enabled);
204} 215}
205 216
@@ -214,33 +225,42 @@ void ComposerController::send()
214 using namespace Sink; 225 using namespace Sink;
215 using namespace Sink::ApplicationDomain; 226 using namespace Sink::ApplicationDomain;
216 227
228 Q_ASSERT(!accountId.isEmpty());
217 Query query; 229 Query query;
218 query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::transport); 230 query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::transport);
219 query.filter<SinkResource::Account>(accountId); 231 query.filter<SinkResource::Account>(accountId);
220 auto job = Store::fetchAll<ApplicationDomain::SinkResource>(query) 232 auto job = Store::fetchAll<ApplicationDomain::SinkResource>(query)
221 .then<void, QList<ApplicationDomain::SinkResource::Ptr>>([=](const QList<ApplicationDomain::SinkResource::Ptr> &resources) -> KAsync::Job<void> { 233 .then([=](const QList<ApplicationDomain::SinkResource::Ptr> &resources) {
222 if (!resources.isEmpty()) { 234 if (!resources.isEmpty()) {
223 auto resourceId = resources[0]->identifier(); 235 auto resourceId = resources[0]->identifier();
224 SinkTrace() << "Sending message via resource: " << resourceId; 236 SinkLog() << "Sending message via resource: " << resourceId;
225 Mail mail(resourceId); 237 Mail mail(resourceId);
226 mail.setBlobProperty("mimeMessage", message->encodedContent()); 238 mail.setMimeMessage(message->encodedContent());
227 return Store::create(mail); 239 return Store::create(mail)
240 .then<void>([=] {
241 //Trigger a sync, but don't wait for it.
242 Store::synchronize(Sink::SyncScope{}.resourceFilter(resourceId)).exec();
243 });
228 } 244 }
245 SinkWarning() << "Failed to find a mailtransport resource";
229 return KAsync::error<void>(0, "Failed to find a MailTransport resource."); 246 return KAsync::error<void>(0, "Failed to find a MailTransport resource.");
247 })
248 .then([&] (const KAsync::Error &error) {
249 SinkLog() << "Message was sent: ";
250 emit done();
230 }); 251 });
231 run(job); 252 run(job);
232 job = job.syncThen<void>([&] {
233 emit done();
234 });
235} 253}
236 254
237void ComposerController::updateSaveAsDraftAction() 255void ComposerController::updateSaveAsDraftAction()
238{ 256{
239 sendAction()->setEnabled(true); 257 bool enabled = !getAccountId().isEmpty();
258 sendAction()->setEnabled(enabled);
240} 259}
241 260
242void ComposerController::saveAsDraft() 261void ComposerController::saveAsDraft()
243{ 262{
263 SinkLog() << "Save as draft";
244 const auto accountId = getAccountId(); 264 const auto accountId = getAccountId();
245 auto existingMail = getExistingMail(); 265 auto existingMail = getExistingMail();
246 266
@@ -249,7 +269,6 @@ void ComposerController::saveAsDraft()
249 if (!message) { 269 if (!message) {
250 SinkWarning() << "Failed to get the mail: "; 270 SinkWarning() << "Failed to get the mail: ";
251 return; 271 return;
252 // return KAsync::error<void>(1, "Failed to get the mail.");
253 } 272 }
254 273
255 using namespace Sink; 274 using namespace Sink;
@@ -257,24 +276,28 @@ void ComposerController::saveAsDraft()
257 276
258 auto job = [&] { 277 auto job = [&] {
259 if (existingMail.identifier().isEmpty()) { 278 if (existingMail.identifier().isEmpty()) {
279 SinkLog() << "Creating a new draft" << existingMail.identifier();
260 Query query; 280 Query query;
261 query.containsFilter<SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::drafts); 281 query.containsFilter<SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::drafts);
262 query.filter<SinkResource::Account>(accountId); 282 query.filter<SinkResource::Account>(accountId);
263 return Store::fetchOne<SinkResource>(query) 283 return Store::fetchOne<SinkResource>(query)
264 .then<void, SinkResource>([=](const SinkResource &resource) -> KAsync::Job<void> { 284 .then([=](const SinkResource &resource) {
265 Mail mail(resource.identifier()); 285 Mail mail(resource.identifier());
266 mail.setDraft(true); 286 mail.setDraft(true);
267 mail.setMimeMessage(message->encodedContent()); 287 mail.setMimeMessage(message->encodedContent());
268 return Store::create(mail); 288 return Store::create(mail);
289 })
290 .onError([] (const KAsync::Error &error) {
291 SinkWarning() << "Error while creating draft: " << error.errorMessage;
269 }); 292 });
270 } else { 293 } else {
271 SinkWarning() << "Modifying an existing mail" << existingMail.identifier(); 294 SinkLog() << "Modifying an existing mail" << existingMail.identifier();
272 existingMail.setDraft(true); 295 existingMail.setDraft(true);
273 existingMail.setMimeMessage(message->encodedContent()); 296 existingMail.setMimeMessage(message->encodedContent());
274 return Store::modify(existingMail); 297 return Store::modify(existingMail);
275 } 298 }
276 }(); 299 }();
277 job = job.syncThen<void>([&] { 300 job = job.then([&] (const KAsync::Error &) {
278 emit done(); 301 emit done();
279 }); 302 });
280 run(job); 303 run(job);
diff --git a/framework/domain/composercontroller.h b/framework/domain/composercontroller.h
index 161bcebc..92467d05 100644
--- a/framework/domain/composercontroller.h
+++ b/framework/domain/composercontroller.h
@@ -76,6 +76,9 @@ public:
76 76
77 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); 77 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft);
78 78
79public slots:
80 virtual void clear() Q_DECL_OVERRIDE;
81
79private slots: 82private slots:
80 void updateSendAction(); 83 void updateSendAction();
81 void updateSaveAsDraftAction(); 84 void updateSaveAsDraftAction();
diff --git a/framework/domain/controller.cpp b/framework/domain/controller.cpp
index fb971136..52f4cd1f 100644
--- a/framework/domain/controller.cpp
+++ b/framework/domain/controller.cpp
@@ -20,6 +20,7 @@
20 20
21#include <QQmlEngine> 21#include <QQmlEngine>
22#include <QMetaProperty> 22#include <QMetaProperty>
23#include <sink/log.h>
23 24
24using namespace Kube; 25using namespace Kube;
25 26
@@ -49,6 +50,9 @@ void Controller::clear()
49void Controller::run(const KAsync::Job<void> &job) 50void Controller::run(const KAsync::Job<void> &job)
50{ 51{
51 auto jobToExec = job; 52 auto jobToExec = job;
53 jobToExec.onError([] (const KAsync::Error &error) {
54 SinkWarningCtx(Sink::Log::Context{"controller"}) << "Error while executing job: " << error.errorMessage;
55 });
52 //TODO handle error 56 //TODO handle error
53 //TODO attach a log context to the execution that we can gather from the job? 57 //TODO attach a log context to the execution that we can gather from the job?
54 jobToExec.exec(); 58 jobToExec.exec();
diff --git a/framework/domain/controller.h b/framework/domain/controller.h
index 9370fdc7..8e6d7155 100644
--- a/framework/domain/controller.h
+++ b/framework/domain/controller.h
@@ -77,7 +77,7 @@ public:
77 virtual ~Controller() = default; 77 virtual ~Controller() = default;
78 78
79public slots: 79public slots:
80 void clear(); 80 virtual void clear();
81 81
82signals: 82signals:
83 void done(); 83 void done();
diff --git a/framework/domain/foldercontroller.cpp b/framework/domain/foldercontroller.cpp
index d06ccb52..65c34dba 100644
--- a/framework/domain/foldercontroller.cpp
+++ b/framework/domain/foldercontroller.cpp
@@ -23,20 +23,22 @@
23 23
24SINK_DEBUG_AREA("foldercontroller"); 24SINK_DEBUG_AREA("foldercontroller");
25 25
26using namespace Sink;
27using namespace Sink::ApplicationDomain;
28
26FolderController::FolderController() 29FolderController::FolderController()
27 : Kube::Controller(), 30 : Kube::Controller(),
28 action_synchronize{new Kube::ControllerAction{this, &FolderController::synchronize}} 31 action_synchronize{new Kube::ControllerAction{this, &FolderController::synchronize}},
32 action_moveToFolder{new Kube::ControllerAction{this, &FolderController::moveToFolder}}
29{ 33{
30} 34}
31 35
32void FolderController::synchronize() 36void FolderController::synchronize()
33{ 37{
34 using namespace Sink;
35 using namespace Sink::ApplicationDomain;
36 auto job = [&] { 38 auto job = [&] {
37 if (auto folder = getFolder()) { 39 if (auto folder = getFolder()) {
38 SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier(); 40 SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier();
39 auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter<Mail::Folder>(QVariant::fromValue(folder->identifier())); 41 auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter<ApplicationDomain::Mail::Folder>(QVariant::fromValue(folder->identifier()));
40 scope.setType<ApplicationDomain::Mail>(); 42 scope.setType<ApplicationDomain::Mail>();
41 return Store::synchronize(scope); 43 return Store::synchronize(scope);
42 } else { 44 } else {
@@ -47,3 +49,11 @@ void FolderController::synchronize()
47 run(job); 49 run(job);
48} 50}
49 51
52void FolderController::moveToFolder()
53{
54 auto mail = getMail();
55 auto targetFolder = getFolder();
56 mail->setFolder(*targetFolder);
57 SinkLog() << "Moving to folder " << mail->identifier() << targetFolder->identifier();
58 run(Store::modify(*mail));
59}
diff --git a/framework/domain/foldercontroller.h b/framework/domain/foldercontroller.h
index 24d6929c..b56e08e8 100644
--- a/framework/domain/foldercontroller.h
+++ b/framework/domain/foldercontroller.h
@@ -26,7 +26,9 @@ class FolderController : public Kube::Controller
26{ 26{
27 Q_OBJECT 27 Q_OBJECT
28 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, Folder, folder) 28 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, Folder, folder)
29 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail::Ptr, Mail, mail)
29 KUBE_CONTROLLER_ACTION(synchronize) 30 KUBE_CONTROLLER_ACTION(synchronize)
31 KUBE_CONTROLLER_ACTION(moveToFolder)
30 32
31public: 33public:
32 explicit FolderController(); 34 explicit FolderController();
diff --git a/framework/domain/identitiesmodel.cpp b/framework/domain/identitiesmodel.cpp
index 33cc191c..49c08005 100644
--- a/framework/domain/identitiesmodel.cpp
+++ b/framework/domain/identitiesmodel.cpp
@@ -88,7 +88,7 @@ void IdentitiesModel::runQuery(const Sink::Query &query)
88 setSourceModel(mModel.data()); 88 setSourceModel(mModel.data());
89 89
90 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkAccount>(Sink::Query()) 90 Sink::Store::fetchAll<Sink::ApplicationDomain::SinkAccount>(Sink::Query())
91 .syncThen<void, QList<Sink::ApplicationDomain::SinkAccount::Ptr> >([this](const QList<Sink::ApplicationDomain::SinkAccount::Ptr> &accounts) { 91 .then([this](const QList<Sink::ApplicationDomain::SinkAccount::Ptr> &accounts) {
92 for (const auto &account : accounts) { 92 for (const auto &account : accounts) {
93 mAccountNames.insert(account->identifier(), account->getName()); 93 mAccountNames.insert(account->identifier(), account->getName());
94 mAccountIcons.insert(account->identifier(), account->getIcon()); 94 mAccountIcons.insert(account->identifier(), account->getIcon());
diff --git a/framework/domain/mailcontroller.cpp b/framework/domain/mailcontroller.cpp
index 3e5e6ed4..962b785f 100644
--- a/framework/domain/mailcontroller.cpp
+++ b/framework/domain/mailcontroller.cpp
@@ -20,6 +20,7 @@
20 20
21#include <sink/store.h> 21#include <sink/store.h>
22#include <sink/log.h> 22#include <sink/log.h>
23#include <sink/standardqueries.h>
23 24
24SINK_DEBUG_AREA("mailcontroller"); 25SINK_DEBUG_AREA("mailcontroller");
25 26
@@ -33,15 +34,31 @@ MailController::MailController()
33 action_markAsImportant{new Kube::ControllerAction{this, &MailController::markAsImportant}}, 34 action_markAsImportant{new Kube::ControllerAction{this, &MailController::markAsImportant}},
34 action_moveToTrash{new Kube::ControllerAction{this, &MailController::moveToTrash}}, 35 action_moveToTrash{new Kube::ControllerAction{this, &MailController::moveToTrash}},
35 action_restoreFromTrash{new Kube::ControllerAction{this, &MailController::restoreFromTrash}}, 36 action_restoreFromTrash{new Kube::ControllerAction{this, &MailController::restoreFromTrash}},
36 action_remove{new Kube::ControllerAction{this, &MailController::remove}} 37 action_remove{new Kube::ControllerAction{this, &MailController::remove}},
38 action_moveToFolder{new Kube::ControllerAction{this, &MailController::moveToFolder}}
37{ 39{
38 QObject::connect(this, &MailController::mailChanged, &MailController::updateActions); 40 QObject::connect(this, &MailController::mailChanged, &MailController::updateActions);
39 updateActions(); 41 updateActions();
40} 42}
41 43
42void MailController::updateActions() 44void MailController::runModification(const std::function<void(ApplicationDomain::Mail &)> &f)
43{ 45{
44 if (auto mail = getMail()) { 46 if (auto mail = getMail()) {
47 f(*mail);
48 run(Store::modify(*mail));
49 } else if (auto mail = getThreadLeader()) {
50 f(*mail);
51 run(Store::modify(Sink::StandardQueries::completeThread(*mail), *mail));
52 }
53}
54
55void MailController::updateActions()
56{
57 auto mail = getMail();
58 if (!mail) {
59 mail= getThreadLeader();
60 }
61 if (mail) {
45 action_moveToTrash->setEnabled(!mail->getTrash()); 62 action_moveToTrash->setEnabled(!mail->getTrash());
46 action_restoreFromTrash->setEnabled(mail->getTrash()); 63 action_restoreFromTrash->setEnabled(mail->getTrash());
47 } 64 }
@@ -49,49 +66,58 @@ void MailController::updateActions()
49 66
50void MailController::markAsRead() 67void MailController::markAsRead()
51{ 68{
52 auto mail = getMail(); 69 runModification([] (ApplicationDomain::Mail &mail) {
53 mail->setUnread(false); 70 mail.setUnread(false);
54 SinkLog() << "Mark as read " << mail->identifier(); 71 SinkLog() << "Mark as read " << mail.identifier();
55 run(Store::modify(*mail)); 72 });
56} 73}
57 74
58void MailController::markAsUnread() 75void MailController::markAsUnread()
59{ 76{
60 auto mail = getMail(); 77 runModification([] (ApplicationDomain::Mail &mail) {
61 mail->setUnread(true); 78 mail.setUnread(true);
62 SinkLog() << "Mark as unread " << mail->identifier(); 79 SinkLog() << "Mark as unread " << mail.identifier();
63 run(Store::modify(*mail)); 80 });
64} 81}
65 82
66void MailController::markAsImportant() 83void MailController::markAsImportant()
67{ 84{
68 auto mail = getMail(); 85 runModification([] (ApplicationDomain::Mail &mail) {
69 mail->setImportant(true); 86 mail.setImportant(true);
70 SinkLog() << "Mark as important " << mail->identifier(); 87 SinkLog() << "Mark as important " << mail.identifier();
71 run(Store::modify(*mail)); 88 });
72} 89}
73 90
74void MailController::moveToTrash() 91void MailController::moveToTrash()
75{ 92{
76 auto mail = getMail(); 93 runModification([] (ApplicationDomain::Mail &mail) {
77 mail->setTrash(true); 94 mail.setTrash(true);
78 SinkLog() << "Move to trash " << mail->identifier(); 95 SinkLog() << "Move to trash " << mail.identifier();
79 run(Store::modify(*mail)); 96 });
80} 97}
81 98
82void MailController::restoreFromTrash() 99void MailController::restoreFromTrash()
83{ 100{
84 auto mail = getMail(); 101 runModification([] (ApplicationDomain::Mail &mail) {
85 mail->setTrash(false); 102 mail.setTrash(false);
86 SinkLog() << "Restore from trash " << mail->identifier(); 103 SinkLog() << "Restore from trash " << mail.identifier();
87 run(Store::modify(*mail)); 104 });
88} 105}
89 106
90void MailController::remove() 107void MailController::remove()
91{ 108{
92 auto mail = getMail(); 109 runModification([] (ApplicationDomain::Mail &mail) {
93 mail->setTrash(true); 110 mail.setTrash(true);
94 SinkLog() << "Remove " << mail->identifier(); 111 SinkLog() << "Remove " << mail.identifier();
95 run(Store::remove(*mail)); 112 });
113}
114
115void MailController::moveToFolder()
116{
117 runModification([&] (ApplicationDomain::Mail &mail) {
118 auto targetFolder = getTargetFolder();
119 mail.setFolder(*targetFolder);
120 SinkLog() << "Moving to folder " << mail.identifier() << targetFolder->identifier();
121 });
96} 122}
97 123
diff --git a/framework/domain/mailcontroller.h b/framework/domain/mailcontroller.h
index b92fd566..ae0f32d5 100644
--- a/framework/domain/mailcontroller.h
+++ b/framework/domain/mailcontroller.h
@@ -26,15 +26,19 @@ class MailController : public Kube::Controller
26{ 26{
27 Q_OBJECT 27 Q_OBJECT
28 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail::Ptr, Mail, mail) 28 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail::Ptr, Mail, mail)
29 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail::Ptr, ThreadLeader, threadLeader)
30 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, TargetFolder, targetFolder)
29 KUBE_CONTROLLER_ACTION(markAsRead) 31 KUBE_CONTROLLER_ACTION(markAsRead)
30 KUBE_CONTROLLER_ACTION(markAsUnread) 32 KUBE_CONTROLLER_ACTION(markAsUnread)
31 KUBE_CONTROLLER_ACTION(markAsImportant) 33 KUBE_CONTROLLER_ACTION(markAsImportant)
32 KUBE_CONTROLLER_ACTION(moveToTrash) 34 KUBE_CONTROLLER_ACTION(moveToTrash)
33 KUBE_CONTROLLER_ACTION(restoreFromTrash) 35 KUBE_CONTROLLER_ACTION(restoreFromTrash)
34 KUBE_CONTROLLER_ACTION(remove) 36 KUBE_CONTROLLER_ACTION(remove)
37 KUBE_CONTROLLER_ACTION(moveToFolder)
35 38
36public: 39public:
37 explicit MailController(); 40 explicit MailController();
38private slots: 41private slots:
39 void updateActions(); 42 void updateActions();
43 void runModification(const std::function<void(Sink::ApplicationDomain::Mail &)> &f);
40}; 44};
diff --git a/framework/domain/maillistmodel.cpp b/framework/domain/maillistmodel.cpp
index 0e76e0a8..207128b7 100644
--- a/framework/domain/maillistmodel.cpp
+++ b/framework/domain/maillistmodel.cpp
@@ -125,9 +125,23 @@ bool MailListModel::lessThan(const QModelIndex &left, const QModelIndex &right)
125void MailListModel::runQuery(const Sink::Query &query) 125void MailListModel::runQuery(const Sink::Query &query)
126{ 126{
127 m_model = Sink::Store::loadModel<Sink::ApplicationDomain::Mail>(query); 127 m_model = Sink::Store::loadModel<Sink::ApplicationDomain::Mail>(query);
128 QObject::connect(m_model.data(), &QAbstractItemModel::rowsInserted, this, &MailListModel::onRowsInserted);
128 setSourceModel(m_model.data()); 129 setSourceModel(m_model.data());
129} 130}
130 131
132void MailListModel::onRowsInserted(const QModelIndex &parent, int begin, int end)
133{
134 if (mFetchMails) {
135 for (int row = begin; row <= end; row++) {
136 auto mail = sourceModel()->index(row, 0, parent).data(Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::Mail::Ptr>();
137 if (mail && !mail->getFullPayloadAvailable()) {
138 qWarning() << "Fetching mail: " << mail->identifier();
139 Sink::Store::synchronize(Sink::SyncScope{*mail}).exec();
140 }
141 }
142 }
143}
144
131void MailListModel::setParentFolder(const QVariant &parentFolder) 145void MailListModel::setParentFolder(const QVariant &parentFolder)
132{ 146{
133 using namespace Sink::ApplicationDomain; 147 using namespace Sink::ApplicationDomain;
@@ -150,6 +164,7 @@ void MailListModel::setParentFolder(const QVariant &parentFolder)
150 query.request<Mail::Draft>(); 164 query.request<Mail::Draft>();
151 query.request<Mail::Trash>(); 165 query.request<Mail::Trash>();
152 query.request<Mail::Folder>(); 166 query.request<Mail::Folder>();
167 mFetchMails = false;
153 qWarning() << "Running folder query: " << folder->resourceInstanceIdentifier() << folder->identifier(); 168 qWarning() << "Running folder query: " << folder->resourceInstanceIdentifier() << folder->identifier();
154 runQuery(query); 169 runQuery(query);
155} 170}
@@ -179,6 +194,8 @@ void MailListModel::setMail(const QVariant &variant)
179 query.request<Mail::Draft>(); 194 query.request<Mail::Draft>();
180 query.request<Mail::Trash>(); 195 query.request<Mail::Trash>();
181 query.request<Mail::MimeMessage>(); 196 query.request<Mail::MimeMessage>();
197 query.request<Mail::FullPayloadAvailable>();
198 mFetchMails = true;
182 qWarning() << "Running mail query: " << mail->resourceInstanceIdentifier() << mail->identifier(); 199 qWarning() << "Running mail query: " << mail->resourceInstanceIdentifier() << mail->identifier();
183 runQuery(query); 200 runQuery(query);
184} 201}
diff --git a/framework/domain/maillistmodel.h b/framework/domain/maillistmodel.h
index 71a0a6df..faed9edf 100644
--- a/framework/domain/maillistmodel.h
+++ b/framework/domain/maillistmodel.h
@@ -68,6 +68,10 @@ public:
68 68
69 void setMail(const QVariant &mail); 69 void setMail(const QVariant &mail);
70 QVariant mail() const; 70 QVariant mail() const;
71private slots:
72 void onRowsInserted(const QModelIndex &idx, int, int);
73
71private: 74private:
72 QSharedPointer<QAbstractItemModel> m_model; 75 QSharedPointer<QAbstractItemModel> m_model;
76 bool mFetchMails = false;
73}; 77};
diff --git a/framework/domain/selector.cpp b/framework/domain/selector.cpp
index ddb23744..d021095b 100644
--- a/framework/domain/selector.cpp
+++ b/framework/domain/selector.cpp
@@ -24,3 +24,8 @@ Selector::Selector(QAbstractItemModel *model) : mModel{model}
24{ 24{
25 QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); 25 QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership);
26} 26}
27
28void Selector::reapplyCurrentIndex()
29{
30 setCurrentIndex(currentIndex());
31}
diff --git a/framework/domain/selector.h b/framework/domain/selector.h
index 77c47ba7..fedb91d2 100644
--- a/framework/domain/selector.h
+++ b/framework/domain/selector.h
@@ -40,6 +40,8 @@ public:
40 setCurrent(mModel->index(mCurrentIndex, 0)); 40 setCurrent(mModel->index(mCurrentIndex, 0));
41 } 41 }
42 42
43 void reapplyCurrentIndex();
44
43 int currentIndex() { return mCurrentIndex; } 45 int currentIndex() { return mCurrentIndex; }
44 46
45 virtual void setCurrent(const QModelIndex &) = 0; 47 virtual void setCurrent(const QModelIndex &) = 0;
diff --git a/framework/domain/settings/accountsettings.cpp b/framework/domain/settings/accountsettings.cpp
index 067d1d79..d7c8c1c0 100644
--- a/framework/domain/settings/accountsettings.cpp
+++ b/framework/domain/settings/accountsettings.cpp
@@ -152,7 +152,7 @@ void AccountSettings::loadAccount()
152{ 152{
153 Q_ASSERT(!mAccountIdentifier.isEmpty()); 153 Q_ASSERT(!mAccountIdentifier.isEmpty());
154 Store::fetchOne<SinkAccount>(Query().filter(mAccountIdentifier)) 154 Store::fetchOne<SinkAccount>(Query().filter(mAccountIdentifier))
155 .syncThen<void, SinkAccount>([this](const SinkAccount &account) { 155 .then([this](const SinkAccount &account) {
156 mIcon = account.getIcon(); 156 mIcon = account.getIcon();
157 mName = account.getName(); 157 mName = account.getName();
158 emit changed(); 158 emit changed();
@@ -162,7 +162,7 @@ void AccountSettings::loadAccount()
162void AccountSettings::loadImapResource() 162void AccountSettings::loadImapResource()
163{ 163{
164 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::storage)) 164 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::storage))
165 .syncThen<void, SinkResource>([this](const SinkResource &resource) { 165 .then([this](const SinkResource &resource) {
166 mImapIdentifier = resource.identifier(); 166 mImapIdentifier = resource.identifier();
167 mImapServer = resource.getProperty("server").toString(); 167 mImapServer = resource.getProperty("server").toString();
168 mImapUsername = resource.getProperty("username").toString(); 168 mImapUsername = resource.getProperty("username").toString();
@@ -176,7 +176,7 @@ void AccountSettings::loadImapResource()
176void AccountSettings::loadMaildirResource() 176void AccountSettings::loadMaildirResource()
177{ 177{
178 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::storage)) 178 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::storage))
179 .syncThen<void, SinkResource>([this](const SinkResource &resource) { 179 .then([this](const SinkResource &resource) {
180 mMaildirIdentifier = resource.identifier(); 180 mMaildirIdentifier = resource.identifier();
181 auto path = resource.getProperty("path").toString(); 181 auto path = resource.getProperty("path").toString();
182 if (mPath != path) { 182 if (mPath != path) {
@@ -191,7 +191,7 @@ void AccountSettings::loadMaildirResource()
191void AccountSettings::loadMailtransportResource() 191void AccountSettings::loadMailtransportResource()
192{ 192{
193 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::transport)) 193 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::transport))
194 .syncThen<void, SinkResource>([this](const SinkResource &resource) { 194 .then([this](const SinkResource &resource) {
195 mMailtransportIdentifier = resource.identifier(); 195 mMailtransportIdentifier = resource.identifier();
196 mSmtpServer = resource.getProperty("server").toString(); 196 mSmtpServer = resource.getProperty("server").toString();
197 mSmtpUsername = resource.getProperty("username").toString(); 197 mSmtpUsername = resource.getProperty("username").toString();
@@ -206,7 +206,7 @@ void AccountSettings::loadIdentity()
206{ 206{
207 //FIXME this assumes that we only ever have one identity per account 207 //FIXME this assumes that we only ever have one identity per account
208 Store::fetchOne<Identity>(Query().filter<Identity::Account>(mAccountIdentifier)) 208 Store::fetchOne<Identity>(Query().filter<Identity::Account>(mAccountIdentifier))
209 .syncThen<void, Identity>([this](const Identity &identity) { 209 .then([this](const Identity &identity) {
210 mIdentityIdentifier = identity.identifier(); 210 mIdentityIdentifier = identity.identifier();
211 mUsername = identity.getName(); 211 mUsername = identity.getName();
212 mEmailAddress = identity.getAddress(); 212 mEmailAddress = identity.getAddress();