Merge branch 'bulwarkmail:main' into feature/scheduled-send

This commit is contained in:
Lucas Gaitzsch
2026-05-23 06:39:35 +02:00
committed by GitHub
54 changed files with 1891 additions and 152 deletions
+3 -3
View File
@@ -98,9 +98,9 @@ export interface IJMAPClient {
setKeyword(emailId: string, keyword: string): Promise<void>;
migrateKeyword(oldKeyword: string, newKeyword: string): Promise<number>;
deleteEmail(emailId: string): Promise<void>;
moveToTrash(emailId: string, trashMailboxId: string, accountId?: string): Promise<void>;
moveToTrash(emailId: string, trashMailboxId: string, accountId?: string, markAsRead?: boolean): Promise<void>;
batchDeleteEmails(emailIds: string[]): Promise<void>;
batchMoveEmails(emailIds: string[], toMailboxId: string, accountId?: string): Promise<void>;
batchMoveEmails(emailIds: string[], toMailboxId: string, accountId?: string, markAsRead?: boolean): Promise<void>;
batchArchiveEmails(
emails: Array<{ id: string; receivedAt: string }>,
archiveMailboxId: string,
@@ -112,7 +112,7 @@ export interface IJMAPClient {
emptyMailbox(mailboxId: string): Promise<number>;
markMailboxAsRead(mailboxId: string, accountId?: string): Promise<number>;
markAllAsRead(excludeMailboxIds?: string[], accountId?: string): Promise<number>;
markAsSpam(emailId: string, accountId?: string): Promise<void>;
markAsSpam(emailId: string, accountId?: string, markAsRead?: boolean): Promise<void>;
undoSpam(emailId: string, originalMailboxId: string, accountId?: string): Promise<void>;
// ── Threads ───────────────────────────────────────────────────
+19 -14
View File
@@ -99,6 +99,8 @@ const EMAIL_LIST_PROPERTIES = [
"subject",
"preview",
"hasAttachment",
// Needed so list rows can serve drag-out to the file system as .eml.
"blobId",
] as const;
// Stalwart's default property list for Calendar/get omits shareWith, isVisible,
@@ -1278,16 +1280,14 @@ export class JMAPClient implements IJMAPClient {
]);
}
async moveToTrash(emailId: string, trashMailboxId: string, accountId?: string): Promise<void> {
async moveToTrash(emailId: string, trashMailboxId: string, accountId?: string, markAsRead?: boolean): Promise<void> {
const targetAccountId = accountId || this.accountId;
const patch: Record<string, unknown> = { mailboxIds: { [trashMailboxId]: true } };
if (markAsRead) patch["keywords/$seen"] = true;
await this.request([
["Email/set", {
accountId: targetAccountId,
update: {
[emailId]: {
mailboxIds: { [trashMailboxId]: true },
},
},
update: { [emailId]: patch },
}, "0"],
]);
}
@@ -1303,10 +1303,15 @@ export class JMAPClient implements IJMAPClient {
]);
}
async batchMoveEmails(emailIds: string[], toMailboxId: string, accountId?: string): Promise<void> {
async batchMoveEmails(emailIds: string[], toMailboxId: string, accountId?: string, markAsRead?: boolean): Promise<void> {
if (emailIds.length === 0) return;
const updates = Object.fromEntries(emailIds.map(id => [id, { mailboxIds: { [toMailboxId]: true } }]));
const buildPatch = () => {
const patch: Record<string, unknown> = { mailboxIds: { [toMailboxId]: true } };
if (markAsRead) patch["keywords/$seen"] = true;
return patch;
};
const updates = Object.fromEntries(emailIds.map(id => [id, buildPatch()]));
await this.request([
["Email/set", { accountId: accountId || this.accountId, update: updates }, "0"],
]);
@@ -1559,7 +1564,7 @@ export class JMAPClient implements IJMAPClient {
return totalMarked;
}
async markAsSpam(emailId: string, accountId?: string): Promise<void> {
async markAsSpam(emailId: string, accountId?: string, markAsRead?: boolean): Promise<void> {
const targetAccountId = accountId || this.accountId;
const mailboxes = await this.getMailboxes();
@@ -1578,14 +1583,13 @@ export class JMAPClient implements IJMAPClient {
? junkMailbox.originalId
: junkMailbox.id;
const patch: Record<string, unknown> = { mailboxIds: { [mailboxId]: true } };
if (markAsRead) patch["keywords/$seen"] = true;
await this.request([
["Email/set", {
accountId: targetAccountId,
update: {
[emailId]: {
mailboxIds: { [mailboxId]: true },
},
},
update: { [emailId]: patch },
}, "0"],
]);
}
@@ -4279,6 +4283,7 @@ export class JMAPClient implements IJMAPClient {
const createMap: Record<string, Partial<CalendarEvent>> = {};
for (let i = 0; i < events.length; i++) {
const { originalId: _oi, originalCalendarIds: _oc, accountId: _ai, accountName: _an, isShared: _is, ...clean } = events[i] as CalendarEvent;
cleanRecurrenceRules(clean as unknown as Record<string, unknown>);
createMap[`new-${i}`] = clean;
}