feat: Search pagination and UX improvements

- Add pagination support to search (previously limited to 50 results)
- Search now scoped to current mailbox/folder
- Support shared mailbox folders in search
- Display total results count (e.g., "50 of 1400 conversations")
- Add clear (X) button to search input
- Re-run search when changing folders during active search
- Remove unused sidebar screenshot
This commit is contained in:
Matthieu MALVACHE
2025-12-17 18:39:49 +01:00
committed by Matthieu MALVACHE
parent ec9b322726
commit 93834511c0
7 changed files with 117 additions and 34 deletions
+26 -11
View File
@@ -753,19 +753,27 @@ export class JMAPClient {
]);
}
async searchEmails(query: string, limit: number = 50): Promise<Email[]> {
async searchEmails(query: string, mailboxId?: string, accountId?: string, limit: number = 50, position: number = 0): Promise<{ emails: Email[], hasMore: boolean, total: number }> {
try {
// Use provided accountId or fallback to primary account
const targetAccountId = accountId || this.accountId;
// Build filter with text search, optionally scoped to a mailbox
const filter: Record<string, unknown> = { text: query };
if (mailboxId) {
filter.inMailbox = mailboxId;
}
const response = await this.request([
["Email/query", {
accountId: this.accountId,
filter: {
text: query,
},
accountId: targetAccountId,
filter: filter,
sort: [{ property: "receivedAt", isAscending: false }],
limit: limit,
position: position,
}, "0"],
["Email/get", {
accountId: this.accountId,
accountId: targetAccountId,
"#ids": {
resultOf: "0",
name: "Email/query",
@@ -788,14 +796,21 @@ export class JMAPClient {
}, "1"],
]);
if (response.methodResponses?.[1]?.[0] === "Email/get") {
return response.methodResponses[1][1].list || [];
}
const queryResponse = response.methodResponses?.[0]?.[1];
const emails = response.methodResponses?.[1]?.[1]?.list || [];
return [];
// Stalwart doesn't always return 'total', so we use a different strategy:
// If we got exactly 'limit' emails, there might be more
// If we got fewer, we've reached the end
const total = queryResponse?.total || 0;
const hasMore = total > 0
? (position + emails.length) < total // Use total if available
: emails.length === limit; // Otherwise, check if we got a full page
return { emails, hasMore, total };
} catch (error) {
console.error('Search failed:', error);
return [];
return { emails: [], hasMore: false, total: 0 };
}
}