diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-01-01 13:37:19 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-01-02 00:31:41 +0100 |
commit | ba7128b30850594c7efb258d1794e377eede364a (patch) | |
tree | f805d511f6d12b0799c05b414054db8b8635bfc9 /framework/domain/composercontroller.cpp | |
parent | 431c257dd29e2e3d8878db200f0de4d452bffe92 (diff) | |
download | kube-ba7128b30850594c7efb258d1794e377eede364a.tar.gz kube-ba7128b30850594c7efb258d1794e377eede364a.zip |
Instead of using the action system we use controllers only.
It's simpler, and the action system was just too complex to use in a
typesafe way.
Diffstat (limited to 'framework/domain/composercontroller.cpp')
-rw-r--r-- | framework/domain/composercontroller.cpp | 268 |
1 files changed, 164 insertions, 104 deletions
diff --git a/framework/domain/composercontroller.cpp b/framework/domain/composercontroller.cpp index 57d386c6..4ce356a9 100644 --- a/framework/domain/composercontroller.cpp +++ b/framework/domain/composercontroller.cpp | |||
@@ -19,9 +19,6 @@ | |||
19 | 19 | ||
20 | 20 | ||
21 | #include "composercontroller.h" | 21 | #include "composercontroller.h" |
22 | #include <actions/context.h> | ||
23 | #include <actions/action.h> | ||
24 | #include <actions/actionhandler.h> | ||
25 | #include <settings/settings.h> | 22 | #include <settings/settings.h> |
26 | #include <KMime/Message> | 23 | #include <KMime/Message> |
27 | #include <KCodecs/KEmailAddress> | 24 | #include <KCodecs/KEmailAddress> |
@@ -40,41 +37,9 @@ | |||
40 | 37 | ||
41 | SINK_DEBUG_AREA("composercontroller"); | 38 | SINK_DEBUG_AREA("composercontroller"); |
42 | 39 | ||
43 | Q_DECLARE_METATYPE(KMime::Types::Mailbox) | ||
44 | |||
45 | ComposerController::ComposerController(QObject *parent) : QObject(parent) | ||
46 | { | ||
47 | QQmlEngine::setObjectOwnership(&mContext, QQmlEngine::CppOwnership); | ||
48 | } | ||
49 | |||
50 | |||
51 | Kube::Context* ComposerController::mailContext() | ||
52 | { | ||
53 | return &mContext; | ||
54 | } | ||
55 | |||
56 | class RecipientCompleter : public Completer { | ||
57 | public: | ||
58 | RecipientCompleter() : Completer(new RecipientAutocompletionModel) | ||
59 | { | ||
60 | } | ||
61 | |||
62 | void setSearchString(const QString &s) { | ||
63 | static_cast<RecipientAutocompletionModel*>(model())->setFilter(s); | ||
64 | Completer::setSearchString(s); | ||
65 | } | ||
66 | }; | ||
67 | |||
68 | Completer *ComposerController::recipientCompleter() const | ||
69 | { | ||
70 | static auto selector = new RecipientCompleter(); | ||
71 | QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership); | ||
72 | return selector; | ||
73 | } | ||
74 | |||
75 | class IdentitySelector : public Selector { | 40 | class IdentitySelector : public Selector { |
76 | public: | 41 | public: |
77 | IdentitySelector(ComposerContext &context) : Selector(new IdentitiesModel), mContext(context) | 42 | IdentitySelector(ComposerController &controller) : Selector(new IdentitiesModel), mController(controller) |
78 | { | 43 | { |
79 | } | 44 | } |
80 | 45 | ||
@@ -88,33 +53,72 @@ public: | |||
88 | mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); | 53 | mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); |
89 | SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; | 54 | SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; |
90 | 55 | ||
91 | mContext.setProperty("identity", QVariant::fromValue(mb)); | 56 | mController.setIdentity(mb); |
92 | mContext.setProperty("accountId", QVariant::fromValue(currentAccountId)); | 57 | mController.setAccountId(currentAccountId); |
93 | } else { | 58 | } else { |
94 | SinkWarning() << "No valid identity for index: " << index; | 59 | SinkWarning() << "No valid identity for index: " << index; |
95 | mContext.setProperty("identity", QVariant{}); | 60 | mController.clearIdentity(); |
96 | mContext.setProperty("accountId", QVariant{}); | 61 | mController.clearAccountId(); |
97 | } | 62 | } |
98 | 63 | ||
99 | } | 64 | } |
100 | private: | 65 | private: |
101 | ComposerContext &mContext; | 66 | ComposerController &mController; |
102 | }; | 67 | }; |
103 | 68 | ||
69 | class RecipientCompleter : public Completer { | ||
70 | public: | ||
71 | RecipientCompleter() : Completer(new RecipientAutocompletionModel) | ||
72 | { | ||
73 | } | ||
74 | |||
75 | void setSearchString(const QString &s) { | ||
76 | static_cast<RecipientAutocompletionModel*>(model())->setFilter(s); | ||
77 | Completer::setSearchString(s); | ||
78 | } | ||
79 | }; | ||
80 | |||
81 | |||
82 | ComposerController::ComposerController() | ||
83 | : Kube::Controller(), | ||
84 | mSendAction{new Kube::ControllerAction}, | ||
85 | mSaveAsDraftAction{new Kube::ControllerAction}, | ||
86 | mRecipientCompleter{new RecipientCompleter}, | ||
87 | mIdentitySelector{new IdentitySelector{*this}} | ||
88 | { | ||
89 | QObject::connect(mSaveAsDraftAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::saveAsDraft); | ||
90 | updateSaveAsDraftAction(); | ||
91 | // mSendAction->monitorProperty<To>(); | ||
92 | // mSendAction->monitorProperty<Send>([] (const QString &) -> bool{ | ||
93 | // //validate | ||
94 | // }); | ||
95 | // registerAction<ControllerAction>(&ComposerController::send); | ||
96 | // actionDepends<ControllerAction, To, Subject>(); | ||
97 | // TODO do in constructor | ||
98 | QObject::connect(mSendAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::send); | ||
99 | |||
100 | QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction); | ||
101 | QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); | ||
102 | updateSendAction(); | ||
103 | } | ||
104 | |||
105 | Completer *ComposerController::recipientCompleter() const | ||
106 | { | ||
107 | return mRecipientCompleter.data(); | ||
108 | } | ||
109 | |||
104 | Selector *ComposerController::identitySelector() const | 110 | Selector *ComposerController::identitySelector() const |
105 | { | 111 | { |
106 | static auto selector = new IdentitySelector(*const_cast<ComposerContext*>(&mContext)); | 112 | return mIdentitySelector.data(); |
107 | QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership); | ||
108 | return selector; | ||
109 | } | 113 | } |
110 | 114 | ||
111 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) | 115 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) |
112 | { | 116 | { |
113 | mContext.setTo(msg->to(true)->asUnicodeString()); | 117 | setTo(msg->to(true)->asUnicodeString()); |
114 | mContext.setCc(msg->cc(true)->asUnicodeString()); | 118 | setCc(msg->cc(true)->asUnicodeString()); |
115 | mContext.setSubject(msg->subject(true)->asUnicodeString()); | 119 | setSubject(msg->subject(true)->asUnicodeString()); |
116 | mContext.setBody(msg->body()); | 120 | setBody(msg->body()); |
117 | mContext.setProperty("existingMessage", QVariant::fromValue(msg)); | 121 | setExistingMessage(msg); |
118 | } | 122 | } |
119 | 123 | ||
120 | void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | 124 | void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) |
@@ -122,18 +126,20 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
122 | Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>()); | 126 | Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>()); |
123 | query.request<Sink::ApplicationDomain::Mail::MimeMessage>(); | 127 | query.request<Sink::ApplicationDomain::Mail::MimeMessage>(); |
124 | Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).syncThen<void, Sink::ApplicationDomain::Mail>([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) { | 128 | Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).syncThen<void, Sink::ApplicationDomain::Mail>([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) { |
125 | mContext.setProperty("existingMail", QVariant::fromValue(mail)); | 129 | setExistingMail(mail); |
130 | |||
131 | //TODO this should probably happen as reaction to the property being set. | ||
126 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); | 132 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); |
127 | if (!mailData.isEmpty()) { | 133 | if (!mailData.isEmpty()) { |
128 | KMime::Message::Ptr mail(new KMime::Message); | 134 | KMime::Message::Ptr mail(new KMime::Message); |
129 | mail->setContent(mailData); | 135 | mail->setContent(mailData); |
130 | mail->parse(); | 136 | mail->parse(); |
131 | if (loadAsDraft) { | 137 | if (loadAsDraft) { |
138 | setMessage(mail); | ||
139 | } else { | ||
132 | auto reply = MailTemplates::reply(mail); | 140 | auto reply = MailTemplates::reply(mail); |
133 | //We assume reply | 141 | //We assume reply |
134 | setMessage(reply); | 142 | setMessage(reply); |
135 | } else { | ||
136 | setMessage(mail); | ||
137 | } | 143 | } |
138 | } else { | 144 | } else { |
139 | qWarning() << "Retrieved empty message"; | 145 | qWarning() << "Retrieved empty message"; |
@@ -159,68 +165,122 @@ void applyAddresses(const QString &list, std::function<void(const QByteArray &, | |||
159 | } | 165 | } |
160 | } | 166 | } |
161 | 167 | ||
162 | void ComposerController::clear() | 168 | Kube::ControllerAction* ComposerController::saveAsDraftAction() |
169 | { | ||
170 | return mSaveAsDraftAction.data(); | ||
171 | } | ||
172 | |||
173 | Kube::ControllerAction* ComposerController::sendAction() | ||
163 | { | 174 | { |
164 | mContext.clear(); | 175 | return mSendAction.data(); |
165 | } | 176 | } |
166 | 177 | ||
178 | KMime::Message::Ptr ComposerController::assembleMessage() | ||
179 | { | ||
180 | auto mail = mExistingMessage; | ||
181 | if (!mail) { | ||
182 | mail = KMime::Message::Ptr::create(); | ||
183 | } | ||
184 | applyAddresses(getTo(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
185 | mail->to(true)->addAddress(addrSpec, displayName); | ||
186 | recordForAutocompletion(addrSpec, displayName); | ||
187 | }); | ||
188 | applyAddresses(getCc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
189 | mail->cc(true)->addAddress(addrSpec, displayName); | ||
190 | recordForAutocompletion(addrSpec, displayName); | ||
191 | }); | ||
192 | applyAddresses(getBcc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
193 | mail->bcc(true)->addAddress(addrSpec, displayName); | ||
194 | recordForAutocompletion(addrSpec, displayName); | ||
195 | }); | ||
196 | |||
197 | mail->from(true)->addAddress(getIdentity()); | ||
198 | |||
199 | mail->subject(true)->fromUnicodeString(getSubject(), "utf-8"); | ||
200 | mail->setBody(getBody().toUtf8()); | ||
201 | mail->assemble(); | ||
202 | return mail; | ||
203 | } | ||
167 | 204 | ||
168 | Kube::ActionHandler *ComposerController::messageHandler() | 205 | void ComposerController::updateSendAction() |
169 | { | 206 | { |
170 | return new Kube::ActionHandlerHelper( | 207 | auto enabled = !getTo().isEmpty() && !getSubject().isEmpty(); |
171 | [](Kube::Context *context) { | 208 | mSendAction->setEnabled(enabled); |
172 | auto identity = context->property("identity"); | 209 | } |
173 | return identity.isValid(); | 210 | |
174 | }, | 211 | void ComposerController::send() |
175 | [this](Kube::Context *context) { | 212 | { |
176 | auto mail = context->property("existingMessage").value<KMime::Message::Ptr>(); | 213 | // verify<To, Subject>() |
177 | if (!mail) { | 214 | // && verify<Subject>(); |
178 | mail = KMime::Message::Ptr::create(); | 215 | auto message = assembleMessage(); |
216 | |||
217 | auto accountId = getAccountId(); | ||
218 | //SinkLog() << "Sending a mail: " << *this; | ||
219 | using namespace Sink; | ||
220 | using namespace Sink::ApplicationDomain; | ||
221 | |||
222 | Query query; | ||
223 | query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::transport); | ||
224 | query.filter<SinkResource::Account>(accountId); | ||
225 | auto job = Store::fetchAll<ApplicationDomain::SinkResource>(query) | ||
226 | .then<void, QList<ApplicationDomain::SinkResource::Ptr>>([=](const QList<ApplicationDomain::SinkResource::Ptr> &resources) -> KAsync::Job<void> { | ||
227 | if (!resources.isEmpty()) { | ||
228 | auto resourceId = resources[0]->identifier(); | ||
229 | SinkTrace() << "Sending message via resource: " << resourceId; | ||
230 | Mail mail(resourceId); | ||
231 | mail.setBlobProperty("mimeMessage", message->encodedContent()); | ||
232 | return Store::create(mail); | ||
179 | } | 233 | } |
180 | applyAddresses(context->property(ComposerContext::To::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | 234 | return KAsync::error<void>(0, "Failed to find a MailTransport resource."); |
181 | mail->to(true)->addAddress(addrSpec, displayName); | 235 | }); |
182 | recordForAutocompletion(addrSpec, displayName); | 236 | run(job); |
183 | }); | 237 | job = job.syncThen<void>([&] { |
184 | applyAddresses(context->property(ComposerContext::Cc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | 238 | emit done(); |
185 | mail->cc(true)->addAddress(addrSpec, displayName); | 239 | }); |
186 | recordForAutocompletion(addrSpec, displayName); | ||
187 | }); | ||
188 | applyAddresses(context->property(ComposerContext::Bcc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
189 | mail->bcc(true)->addAddress(addrSpec, displayName); | ||
190 | recordForAutocompletion(addrSpec, displayName); | ||
191 | }); | ||
192 | |||
193 | mail->from(true)->addAddress(context->property("identity").value<KMime::Types::Mailbox>()); | ||
194 | |||
195 | mail->subject(true)->fromUnicodeString(context->property(ComposerContext::Subject::name).toString(), "utf-8"); | ||
196 | mail->setBody(context->property(ComposerContext::Body::name).toString().toUtf8()); | ||
197 | mail->assemble(); | ||
198 | |||
199 | context->setProperty("message", QVariant::fromValue(mail)); | ||
200 | } | ||
201 | ); | ||
202 | } | 240 | } |
203 | 241 | ||
204 | Kube::Action* ComposerController::saveAsDraftAction() | 242 | void ComposerController::updateSaveAsDraftAction() |
205 | { | 243 | { |
206 | auto action = new Kube::Action("org.kde.kube.actions.save-as-draft", mContext); | 244 | mSendAction->setEnabled(true); |
207 | action->addPreHandler(messageHandler()); | ||
208 | action->addPostHandler(new Kube::ActionHandlerHelper( | ||
209 | [this](Kube::Context *context) { | ||
210 | emit done(); | ||
211 | })); | ||
212 | return action; | ||
213 | } | 245 | } |
214 | 246 | ||
215 | Kube::Action* ComposerController::sendAction() | 247 | void ComposerController::saveAsDraft() |
216 | { | 248 | { |
217 | auto action = new Kube::Action("org.kde.kube.actions.sendmail", mContext); | 249 | const auto accountId = getAccountId(); |
218 | // action->addPreHandler(identityHandler()); | 250 | auto existingMail = getExistingMail(); |
219 | action->addPreHandler(messageHandler()); | 251 | |
220 | // action->addPreHandler(encryptionHandler()); | 252 | auto message = assembleMessage(); |
221 | action->addPostHandler(new Kube::ActionHandlerHelper( | 253 | //FIXME this is something for the validation |
222 | [this](Kube::Context *context) { | 254 | if (!message) { |
223 | emit done(); | 255 | SinkWarning() << "Failed to get the mail: "; |
224 | })); | 256 | return; |
225 | return action; | 257 | // return KAsync::error<void>(1, "Failed to get the mail."); |
258 | } | ||
259 | |||
260 | using namespace Sink; | ||
261 | using namespace Sink::ApplicationDomain; | ||
262 | |||
263 | auto job = [&] { | ||
264 | if (existingMail.identifier().isEmpty()) { | ||
265 | Query query; | ||
266 | query.containsFilter<SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::drafts); | ||
267 | query.filter<SinkResource::Account>(accountId); | ||
268 | return Store::fetchOne<SinkResource>(query) | ||
269 | .then<void, SinkResource>([=](const SinkResource &resource) -> KAsync::Job<void> { | ||
270 | Mail mail(resource.identifier()); | ||
271 | mail.setDraft(true); | ||
272 | mail.setMimeMessage(message->encodedContent()); | ||
273 | return Store::create(mail); | ||
274 | }); | ||
275 | } else { | ||
276 | SinkWarning() << "Modifying an existing mail" << existingMail.identifier(); | ||
277 | existingMail.setDraft(true); | ||
278 | existingMail.setMimeMessage(message->encodedContent()); | ||
279 | return Store::modify(existingMail); | ||
280 | } | ||
281 | }(); | ||
282 | job = job.syncThen<void>([&] { | ||
283 | emit done(); | ||
284 | }); | ||
285 | run(job); | ||
226 | } | 286 | } |