mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 00:14:23 +00:00
fix: open search hits from shared folders in the right account #923
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user