fix: open search hits from shared folders in the right account #923

This commit is contained in:
Linus Rath
2026-09-07 22:34:47 +02:00
parent 8477acf217
commit 80dadfbbd4
3 changed files with 83 additions and 5 deletions
+9 -5
View File
@@ -31,7 +31,7 @@ import { usePolicyStore } from "@/stores/policy-store";
import type { UnifiedAccountClient } from "@/lib/unified-mailbox";
import { connectedAccountsGrew } from "@/lib/unified-mailbox";
import { KeyboardShortcutsModal } from "@/components/keyboard-shortcuts-modal";
import { useEmailStore, buildUnifiedAccountClients, ArchiveMailboxNotFoundError, findArchiveMailbox } from "@/stores/email-store";
import { useEmailStore, buildUnifiedAccountClients, ArchiveMailboxNotFoundError, findArchiveMailbox, resolveUnstampedEmailAccountId } from "@/stores/email-store";
import { toast } from "@/stores/toast-store";
import { MailboxShareDialog } from "@/components/layout/mailbox-share-dialog";
import { ShareNotificationToaster } from "@/components/layout/share-notification-toaster";
@@ -3289,11 +3289,15 @@ export function MailApp({ linkSegments }: MailAppProps = {}) {
?? (viewingAccountId ? useAuthStore.getState().getClientForAccount(viewingAccountId) : undefined)
?? client;
// During an unscoped search the hit is the primary account's, not the
// selected shared folder's owner - see resolveUnstampedEmailAccountId. (#923)
const accountId = listEmail?.sourceAccountId
?? (() => {
const mailbox = viewMailboxes.find(mb => mb.id === selectedMailbox);
return mailbox?.isShared ? mailbox.accountId : undefined;
})();
?? resolveUnstampedEmailAccountId({
mailboxes: viewMailboxes,
selectedMailbox,
searchActive: !!searchQuery || !isFilterEmpty(searchFilters),
searchMailboxId,
});
const fullEmail = await fetchClient.getEmail(email.id, accountId);
if (fullEmail) {
@@ -0,0 +1,53 @@
import { describe, expect, it } from 'vitest';
import { resolveUnstampedEmailAccountId } from '../email-store';
import type { Mailbox } from '@/lib/jmap/types';
// #923: with an unscoped ("All folders") search active while a shared folder
// is selected, the hit belongs to the primary account. Deriving the owner
// from the selected shared folder made getEmail() look in the wrong account.
const mailboxes = [
{ id: 'inbox', name: 'Inbox', role: 'inbox', isShared: false },
{ id: 'owner-x:x-inbox', name: 'Shared Inbox', role: 'inbox', isShared: true, accountId: 'owner-x' },
] as unknown as Mailbox[];
describe('resolveUnstampedEmailAccountId (#923)', () => {
it('uses the primary account for an unscoped search even with a shared folder selected', () => {
expect(
resolveUnstampedEmailAccountId({
mailboxes,
selectedMailbox: 'owner-x:x-inbox',
searchActive: true,
searchMailboxId: '',
}),
).toBeUndefined();
});
it('keeps the shared owner when browsing the shared folder without a search', () => {
expect(
resolveUnstampedEmailAccountId({
mailboxes,
selectedMailbox: 'owner-x:x-inbox',
searchActive: false,
searchMailboxId: '',
}),
).toBe('owner-x');
});
it('keeps the shared owner for a search scoped to the shared folder', () => {
expect(
resolveUnstampedEmailAccountId({
mailboxes,
selectedMailbox: 'owner-x:x-inbox',
searchActive: true,
searchMailboxId: 'owner-x:x-inbox',
}),
).toBe('owner-x');
});
it('is undefined for an own folder', () => {
expect(
resolveUnstampedEmailAccountId({ mailboxes, selectedMailbox: 'inbox', searchActive: false, searchMailboxId: '' }),
).toBeUndefined();
});
});
+21
View File
@@ -601,6 +601,27 @@ export function findArchiveMailbox(
);
}
/**
* JMAP accountId for opening an email that carries no source stamps.
*
* Normally the selected folder decides: a shared/group folder's owner, else
* the account's own (undefined). During an unscoped ("All folders") search
* the hits come from the primary account even while a shared folder is
* selected, so deriving the owner from that folder asks the wrong account and
* `getEmail` returns nothing. In that case the caller's own account is used
* (searching the shared owners too is a separate issue). (#923)
*/
export function resolveUnstampedEmailAccountId(opts: {
mailboxes: Mailbox[];
selectedMailbox: string | null | undefined;
searchActive: boolean;
searchMailboxId: string;
}): string | undefined {
if (opts.searchActive && opts.searchMailboxId === '') return undefined;
const mailbox = opts.mailboxes.find(mb => mb.id === opts.selectedMailbox);
return mailbox?.isShared ? mailbox.accountId : undefined;
}
function resolveActionMailboxes(): Mailbox[] {
const state = useEmailStore.getState();
if (state.viewingAccountId) {