fix(email): make the "Move to" context menu work across accounts
Moving a message to a folder in another account (own ↔ delegated/shared) via the "Move to" context menu was a no-op — the handlers always issued a single-account Email/set, which can't move between JMAP accounts. Drag-and-drop already routed these correctly; the context menu never did. Add moveToMailboxCrossAware: it detects a cross-account destination (own and shared mailboxes both carry accountId) and routes through the drag-and-drop crossAccountMoveEmails pipeline, else falls back to the single-account move. Fix the pipeline for delegated folders too: a client can't stage a blob in a delegated account (Blob/upload → blobNotFound), so importing into a shared folder failed. When one client reaches both accounts, use a server-side JMAP Email/copy (+ destroy original) instead of blob copy+import; the blob path is kept only for separate cross-server login accounts. Adds client.copyEmailAcrossAccounts. Unit tests for the dispatch; the two 08-shared-moves specs are un-pinned. Full docker integration suite green (37 passed).
This commit is contained in:
@@ -1094,6 +1094,7 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
// ── S/MIME raw-email helpers ──────────────────────────────────
|
||||
|
||||
async importRawEmail(): Promise<string> { return generateDemoId('email'); }
|
||||
async copyEmailAcrossAccounts(): Promise<string> { return generateDemoId('email'); }
|
||||
async submitEmail(): Promise<void> { /* no-op */ }
|
||||
async submitRawEmail(blob: Blob,
|
||||
identityId: string,
|
||||
|
||||
@@ -345,4 +345,12 @@ export interface IJMAPClient {
|
||||
// ── S/MIME raw-email helpers ──────────────────────────────────
|
||||
importRawEmail(blob: Blob, mailboxIds: Record<string, boolean>, keywords?: Record<string, boolean>, accountId?: string): Promise<string>;
|
||||
submitEmail(emailId: string, identityId: string): Promise<void>;
|
||||
/**
|
||||
* Server-side move of one email across accounts reachable through THIS client
|
||||
* (JMAP `Email/copy` + destroy-original). Used for delegated/shared folders,
|
||||
* where the two accounts share a client but a client can't stage a blob in a
|
||||
* delegated account (so the blob copy+import path doesn't work). Returns the
|
||||
* new email id in the destination account.
|
||||
*/
|
||||
copyEmailAcrossAccounts(emailId: string, fromAccountId: string, toAccountId: string, destMailboxId: string): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -6394,6 +6394,32 @@ export class JMAPClient implements IJMAPClient {
|
||||
* a shared mailbox owned by another user). When omitted, falls back to the
|
||||
* client's own primary account.
|
||||
*/
|
||||
async copyEmailAcrossAccounts(
|
||||
emailId: string,
|
||||
fromAccountId: string,
|
||||
toAccountId: string,
|
||||
destMailboxId: string,
|
||||
): Promise<string> {
|
||||
const response = await this.request([
|
||||
["Email/copy", {
|
||||
fromAccountId,
|
||||
accountId: toAccountId,
|
||||
create: { c: { id: emailId, mailboxIds: { [destMailboxId]: true } } },
|
||||
onSuccessDestroyOriginal: true,
|
||||
}, "0"],
|
||||
]);
|
||||
const res = response.methodResponses?.[0]?.[1];
|
||||
const err = res?.notCreated?.c;
|
||||
if (err) {
|
||||
throw new Error(err.description || err.type || "Failed to copy email across accounts");
|
||||
}
|
||||
const id = res?.created?.c?.id;
|
||||
if (!id) {
|
||||
throw new Error("Email/copy succeeded but no ID returned");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
async importRawEmail(
|
||||
blob: Blob,
|
||||
mailboxIds: Record<string, boolean>,
|
||||
|
||||
Reference in New Issue
Block a user