Commit5e97b62eRecorded13 Mar 2026Repositorysigil-jmap

Fix JMAP send compatibility and add batch email move

Message

Drop onSuccessUpdateEmail from EmailSubmission/set (Fastmail rejects the #draft back-reference key) and move email to Sent in a separate Email/set call after successful submission. Add jmap-email-move-many! for batch mailbox moves in a single JMAP request.

Changed
 src/sigil/jmap.sgl       |  1 +
 src/sigil/jmap/email.sgl | 18 ++++++++++++++++++
 src/sigil/jmap/send.sgl  | 37 +++++++++++++++++++++++--------------
 3 files changed, 42 insertions(+), 14 deletions(-)
Diff
src/sigil/jmap.sglmodified
@@ -68,6 +68,7 @@
68
jmap-email-get-many
69
jmap-email-update!
70
jmap-email-move!
+71
jmap-email-move-many!
72
jmap-email-destroy!
73
jmap-email-set-keyword!
74
jmap-email-remove-keyword!
src/sigil/jmap/email.sglmodified
@@ -15,6 +15,7 @@
15
jmap-email-get-many
16
jmap-email-update!
17
jmap-email-move!
+18
jmap-email-move-many!
19
jmap-email-destroy!
20
jmap-email-set-keyword!
21
jmap-email-remove-keyword!)
@@ -169,6 +170,23 @@
170
(jmap-email-update! client email-id
171
(dict mailboxIds: (dict-set #{} (string->keyword target-mailbox-id) #t))))
172
+173
;;; Move multiple emails to a mailbox in a single request.
+174
;;;
+175
;;; ```scheme
+176
;;; (jmap-email-move-many! client (list id1 id2 id3) trash-id)
+177
;;; ```
+178
(define (jmap-email-move-many! client email-ids target-mailbox-id)
+179
(: jmap-client? list? string? -> dict?)
+180
(let ((updates (fold (lambda (acc id)
+181
(dict-set acc (string->keyword id)
+182
(dict mailboxIds: #{ target-mailbox-id #t })))
+183
#{}
+184
email-ids)))
+185
(jmap-result-ref client
+186
(jmap-call "Email/set"
+187
(dict accountId: (jmap-client-account-id client)
+188
update: updates)))))
+189
190
;;; Delete an email permanently.
191
;;;
192
;;; ```scheme
src/sigil/jmap/send.sglmodified
@@ -116,8 +116,13 @@
116
(error "jmap-send!: to: is required"))
117
118
(let* ((account-id (jmap-client-account-id client))
+119
;; Find drafts mailbox to place the email initially
+120
(drafts-mailbox (jmap-mailbox-by-role client "drafts"))
+121
(drafts-id (if drafts-mailbox (dict-ref drafts-mailbox id: #f) #f))
122
;; Build the email object
120
(email #{ mailboxIds: #{}
+123
(email #{ mailboxIds: (if drafts-id
+124
#{ drafts-id #t }
+125
#{})
126
keywords: #{ "$draft" #t } })
127
(email (if from
128
(dict-set email from: (list->array (list (make-address from))))
@@ -163,26 +168,16 @@
168
(email (if (pair? html-parts)
169
(dict-set email htmlBody: (list->array html-parts))
170
email))
166
;; Find Sent mailbox for onSuccessUpdateEmail
167
(sent-mailbox (jmap-mailbox-by-role client "sent"))
168
(sent-id (if sent-mailbox (dict-ref sent-mailbox id: #f) #f))
171
;; Build the request: Email/set create + EmailSubmission/set
172
(create-call (jmap-call "Email/set"
173
(dict accountId: account-id
174
create: (dict draft: email))
175
"c0"))
176
;; Submission with back-reference to created email
175
(submission (dict identityId: identity-id
176
emailId: "#draft"))
177
;; Move to Sent on success
178
(on-success (if sent-id
179
#{ "#draft" #{ mailboxIds: #{ sent-id #t }
180
keywords: 'null } }
181
#{}))
177
(submit-call (jmap-call "EmailSubmission/set"
178
(dict accountId: account-id
184
create: (dict s0: submission)
185
onSuccessUpdateEmail: on-success)
+179
create: #{ s0: #{ identityId: identity-id
+180
emailId: "#draft" } })
181
"s0"))
182
(response (jmap-request client create-call submit-call
183
using: %submission-capabilities)))
@@ -195,6 +190,20 @@
190
(error "jmap-send!: submission failed"
191
(jmap-error-type response "s0")))
192
198
(jmap-response-get response "s0")))))
+193
;; Move email from Drafts to Sent
+194
(let* ((c0-result (jmap-response-get response "c0"))
+195
(created (dict-ref c0-result created: #f))
+196
(created-email (and created (dict-ref created draft: #f)))
+197
(email-id (and created-email (dict-ref created-email id: #f)))
+198
(sent-mailbox (jmap-mailbox-by-role client "sent"))
+199
(sent-id (and sent-mailbox (dict-ref sent-mailbox id: #f))))
+200
(when (and email-id sent-id)
+201
(jmap-request client
+202
(jmap-call "Email/set"
+203
(dict accountId: account-id
+204
update: #{ email-id #{ mailboxIds: #{ sent-id #t }
+205
keywords: 'null } }))
+206
using: %submission-capabilities))
+207
(jmap-response-get response "s0"))))))
208
209
))