Merge branch 'main' of https://github.com/bulwarkmail/webmail
This commit is contained in:
@@ -78,7 +78,9 @@ export interface IJMAPClient {
|
||||
deleteMailbox(mailboxId: string): Promise<void>;
|
||||
|
||||
// ── Emails ────────────────────────────────────────────────────
|
||||
getEmails(mailboxId?: string, accountId?: string, limit?: number, position?: number, hasKeyword?: string): Promise<{ emails: Email[]; hasMore: boolean; total: number }>;
|
||||
// `pinnedFirst` sorts emails carrying the $pinned keyword to the top
|
||||
// (server-side hasKeyword sort comparator, RFC 8621), then receivedAt desc.
|
||||
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>;
|
||||
getTagCounts(tagIds: string[]): Promise<Record<string, { total: number; unread: number }>>;
|
||||
|
||||
+13
-2
@@ -1062,7 +1062,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
}
|
||||
}
|
||||
|
||||
async getEmails(mailboxId?: string, accountId?: string, limit: number = 50, position: number = 0, hasKeyword?: string): Promise<{ emails: Email[], hasMore: boolean, total: number }> {
|
||||
async getEmails(mailboxId?: string, accountId?: string, limit: number = 50, position: number = 0, hasKeyword?: string, pinnedFirst?: boolean): Promise<{ emails: Email[], hasMore: boolean, total: number }> {
|
||||
try {
|
||||
const targetAccountId = accountId || this.accountId;
|
||||
const filter: { inMailbox?: string; hasKeyword?: string } = {};
|
||||
@@ -1072,12 +1072,20 @@ export class JMAPClient implements IJMAPClient {
|
||||
if (hasKeyword) {
|
||||
filter.hasKeyword = hasKeyword;
|
||||
}
|
||||
// Pinned-first uses the hasKeyword sort comparator (RFC 8621 §4.4.2);
|
||||
// every page of a view must use the same sort or pagination tears.
|
||||
const sort = pinnedFirst
|
||||
? [
|
||||
{ property: "hasKeyword", keyword: "$pinned", isAscending: false },
|
||||
{ property: "receivedAt", isAscending: false },
|
||||
]
|
||||
: [{ property: "receivedAt", isAscending: false }];
|
||||
|
||||
const response = await this.request([
|
||||
["Email/query", {
|
||||
accountId: targetAccountId,
|
||||
filter,
|
||||
sort: [{ property: "receivedAt", isAscending: false }],
|
||||
sort,
|
||||
limit,
|
||||
position,
|
||||
calculateTotal: true,
|
||||
@@ -1096,7 +1104,10 @@ export class JMAPClient implements IJMAPClient {
|
||||
const emails = (getResponse.list || []) as Email[];
|
||||
// Sort client-side as safety net - some servers may not honour
|
||||
// the query sort for large mailboxes without additional filters.
|
||||
// Must mirror the query sort, or it would undo the pinned-first order.
|
||||
const pinRank = (e: Email) => (pinnedFirst && e.keywords?.['$pinned'] ? 1 : 0);
|
||||
emails.sort((a: Email, b: Email) =>
|
||||
pinRank(b) - pinRank(a) ||
|
||||
new Date(b.receivedAt).getTime() - new Date(a.receivedAt).getTime()
|
||||
);
|
||||
const total = queryResponse?.total || 0;
|
||||
|
||||
@@ -217,6 +217,7 @@ export interface ThreadGroup {
|
||||
participantNames: string[];// Unique participant names
|
||||
hasUnread: boolean; // Any unread emails in thread
|
||||
hasStarred: boolean; // Any starred emails in thread
|
||||
hasPinned: boolean; // Any pinned emails in thread ($pinned keyword)
|
||||
hasAttachment: boolean; // Any email has attachment
|
||||
hasAnswered: boolean; // Any email has been replied to
|
||||
hasForwarded: boolean; // Any email has been forwarded
|
||||
|
||||
Reference in New Issue
Block a user