feat: add onEmailsFetched and onSearchResults hook + new JMAP method getSomeEmails

This commit is contained in:
Paulhenry Saux
2026-07-13 18:18:57 +02:00
committed by Linus Rath
parent c1acf58c5f
commit 622adc34de
5 changed files with 154 additions and 81 deletions
+13
View File
@@ -166,6 +166,19 @@ export class DemoJMAPClient implements IJMAPClient {
return { emails, hasMore: position + limit < total, total };
}
async getSomeEmails(emailsId: string[], _accountId?: string): Promise<Email[]> {
if (!emailsId || emailsId.length === 0) {
return [];
}
const filtered = this.data.emails.filter(e => emailsId.includes(e.id));
filtered.sort((a, b) =>
new Date(b.receivedAt).getTime() - new Date(a.receivedAt).getTime()
);
return filtered;
}
async getEmailsInMailbox(mailboxId: string): Promise<Email[]> {
return this.data.emails.filter(e => e.mailboxIds[mailboxId]);
}
+1
View File
@@ -83,6 +83,7 @@ export interface IJMAPClient {
getEmails(mailboxId?: string, accountId?: string, limit?: number, position?: number, hasKeyword?: string, pinnedFirst?: boolean): Promise<{ emails: Email[]; hasMore: boolean; total: number }>;
getEmailsInMailbox(mailboxId: string): Promise<Email[]>;
getEmail(emailId: string, accountId?: string): Promise<Email | null>;
getSomeEmails(emailsId: string[], accountId?: string): Promise<Email[]>
getTagCounts(tagIds: string[]): Promise<Record<string, { total: number; unread: number }>>;
searchEmails(query: string, mailboxId?: string, accountId?: string, limit?: number, position?: number): Promise<{ emails: Email[]; hasMore: boolean; total: number }>;
advancedSearchEmails(
+37
View File
@@ -550,6 +550,43 @@ export class JMAPClient implements IJMAPClient {
this.authHeader = `Bearer ${token}`;
}
async getSomeEmails(emailsId: string[], accountId?: string): Promise<Email[]> {
try {
const targetAccountId = accountId || this.accountId;
if (!emailsId || emailsId.length === 0) {
return [];
}
const response = await this.request([
["Email/get", {
accountId: targetAccountId,
ids: emailsId,
properties: [...EMAIL_LIST_PROPERTIES],
}, "0"],
]);
const getResponse = response.methodResponses?.[0]?.[1];
if (response.methodResponses?.[0]?.[0] === "Email/get" && getResponse) {
const emails = (getResponse.list || []) as Email[];
emails.sort((a: Email, b: Email) =>
new Date(b.receivedAt).getTime() - new Date(a.receivedAt).getTime()
);
if (accountId && accountId !== this.accountId) {
namespaceMailboxIds(emails, accountId);
}
return emails;
}
return [];
} catch (error) {
console.error('Failed to get specific emails:', error);
return [];
}
}
/** Upgrade an existing basic-auth client to bearer-token auth (e.g. after TOTP token exchange). */
upgradeToBearer(accessToken: string, onRefresh?: () => Promise<string | null>): void {
this.authMode = 'bearer';
+4
View File
@@ -272,6 +272,10 @@ export const emailHooks = {
// normally. This is the send-takeover hook used by the S/MIME plugin to
// replace the former native sign+encrypt+sendRaw pipeline.
onComposeSend: new HookBus(),
// Transform hook - receive Email[] or ScheduledEmail[] just after there are fetched to
// lets plugin edit emails before they are shown in row. Used to populate preview
// field for encryption plugins.
onEmailsFetched: new HookBus(),
};
// §7.2 Calendar Hooks