diff --git a/components/mail/__tests__/mail-app-source-routing.test.tsx b/components/mail/__tests__/mail-app-source-routing.test.tsx
index 890635547..9348baa37 100644
--- a/components/mail/__tests__/mail-app-source-routing.test.tsx
+++ b/components/mail/__tests__/mail-app-source-routing.test.tsx
@@ -195,3 +195,63 @@ describe('the open message of a group account in a tag view', () => {
expect(moveToMailbox).toHaveBeenCalledWith(client, 'eaaaaab', 'ce:b', 'login-a/ce:eaaaaab');
});
});
+
+describe('archiving a group message by month', () => {
+ // The group's Archive `k` already holds 2026/10, as the list names them:
+ // store ids and parents namespaced `ce:...`, the server's ids bare.
+ const year = { ...mailbox('y1', undefined, 'ce', true), name: '2026', parentId: 'ce:k' };
+ const month = { ...mailbox('m1', undefined, 'ce', true), name: '10', parentId: 'ce:y1' };
+ const withMonth = [...folders, year, month] as Mailbox[];
+
+ beforeEach(() => {
+ useSettingsStore.setState({ archiveMode: 'month' } as never);
+ client.createMailbox = vi.fn();
+ });
+
+ it('reuses the year and month folders in a tag view', async () => {
+ useEmailStore.setState({ mailboxes: withMonth } as never);
+ render();
+ fireEvent.click(await screen.findByText('stub archive', {}, { timeout: 5000 }));
+ await waitFor(() => expect(moveThreadToMailbox).toHaveBeenCalled());
+ expect(client.createMailbox).not.toHaveBeenCalled();
+ expect(moveThreadToMailbox).toHaveBeenCalledWith(client, 'eaaaaab', 'ce:m1', 'login-a/ce:eaaaaab');
+ });
+
+ it('reuses them with the group\'s Inbox open', async () => {
+ const unstamped = { ...groupMessage, sourceClientAccountId: undefined, sourceAccountId: undefined } as Email;
+ useEmailStore.setState({ mailboxes: withMonth, selectedMailbox: 'ce:a', selectedKeyword: null, emails: [unstamped], selectedEmail: unstamped } as never);
+ render();
+ fireEvent.click(await screen.findByText('stub archive', {}, { timeout: 5000 }));
+ await waitFor(() => expect(moveThreadToMailbox).toHaveBeenCalled());
+ expect(client.createMailbox).not.toHaveBeenCalled();
+ expect(moveThreadToMailbox).toHaveBeenCalledWith(client, 'eaaaaab', 'ce:m1', 'eaaaaab');
+ });
+
+ // The own account has a folder with the server id of the group's Archive,
+ // with 2026/10 below it (live on 0.16: a's "Shared" folder is `h`, and so
+ // was the group's Archive).
+ it('does not take the own account\'s namesake folders', async () => {
+ const unstamped = { ...groupMessage, sourceClientAccountId: undefined, sourceAccountId: undefined } as Email;
+ const ownNamesakes = [
+ { ...mailbox('k', undefined, 'cf'), name: 'Shared' },
+ { ...mailbox('oy', undefined, 'cf'), name: '2026', parentId: 'k' },
+ { ...mailbox('om', undefined, 'cf'), name: '10', parentId: 'oy' },
+ ];
+ useEmailStore.setState({ mailboxes: [...ownNamesakes, ...withMonth], selectedMailbox: 'ce:a', selectedKeyword: null, emails: [unstamped], selectedEmail: unstamped } as never);
+ render();
+ fireEvent.click(await screen.findByText('stub archive', {}, { timeout: 5000 }));
+ await waitFor(() => expect(moveThreadToMailbox).toHaveBeenCalled());
+ expect(moveThreadToMailbox).toHaveBeenCalledWith(client, 'eaaaaab', 'ce:m1', 'eaaaaab');
+ });
+
+ it('creates a missing year folder in the group account with the group\'s Inbox open', async () => {
+ const unstamped = { ...groupMessage, sourceClientAccountId: undefined, sourceAccountId: undefined } as Email;
+ useSettingsStore.setState({ archiveMode: 'year' } as never);
+ (client.createMailbox as ReturnType).mockResolvedValue({ ...mailbox('y2', undefined, 'ce', true), id: 'y2', name: '2026' });
+ useEmailStore.setState({ selectedMailbox: 'ce:a', selectedKeyword: null, emails: [unstamped], selectedEmail: unstamped } as never);
+ render();
+ fireEvent.click(await screen.findByText('stub archive', {}, { timeout: 5000 }));
+ await waitFor(() => expect(client.createMailbox).toHaveBeenCalled());
+ expect(client.createMailbox).toHaveBeenCalledWith('2026', 'k', 'ce', undefined);
+ });
+});
diff --git a/components/mail/mail-app.tsx b/components/mail/mail-app.tsx
index c366a6cea..2a8d777be 100644
--- a/components/mail/mail-app.tsx
+++ b/components/mail/mail-app.tsx
@@ -2948,12 +2948,20 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
const year = emailDate.getFullYear().toString();
const month = (emailDate.getMonth() + 1).toString().padStart(2, '0');
const archiveId = archiveMailbox.originalId || archiveMailbox.id;
+ // By the server's ids: the list names a shared account's folders and
+ // their parents `account:id`, so a group's year folder was never
+ // found again. The folders go in the Archive's own account, and only
+ // that account's are looked at: the own folders have server ids too.
+ const parentOf = (m: Mailbox) => (m.parentId && m.accountId && m.parentId.startsWith(`${m.accountId}:`)
+ ? m.parentId.slice(m.accountId.length + 1)
+ : m.parentId);
+ const folderAccountId = archiveMailbox.accountId ?? archiveAccountId;
+ const isFolder = (m: Mailbox, name: string, parentId: string) =>
+ m.name === name && parentOf(m) === parentId && (!folderAccountId || m.accountId === folderAccountId);
- let yearMailbox = currentMailboxes.find(
- m => m.name === year && m.parentId === archiveId
- ) ?? null;
+ let yearMailbox = currentMailboxes.find(m => isFolder(m, year, archiveId)) ?? null;
if (!yearMailbox) {
- yearMailbox = await createMailboxWithHooks(archiveClient, year, archiveId, archiveAccountId);
+ yearMailbox = await createMailboxWithHooks(archiveClient, year, archiveId, folderAccountId);
if (!yearMailbox) return; // a plugin refused the folder
await refreshMailboxes();
}
@@ -2963,11 +2971,9 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
} else {
const yearId = yearMailbox.originalId || yearMailbox.id;
const afterYear = readMailboxes();
- let monthMailbox = afterYear.find(
- m => m.name === month && m.parentId === yearId
- ) ?? null;
+ let monthMailbox = afterYear.find(m => isFolder(m, month, yearId)) ?? null;
if (!monthMailbox) {
- monthMailbox = await createMailboxWithHooks(archiveClient, month, yearId, archiveAccountId);
+ monthMailbox = await createMailboxWithHooks(archiveClient, month, yearId, folderAccountId);
if (!monthMailbox) return; // a plugin refused the folder
await refreshMailboxes();
}