mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 08:06:36 +00:00
fix: only drop exact role-folder name duplicates from the folder tree (#771)
This commit is contained in:
@@ -140,3 +140,42 @@ describe('account scoping helpers', () => {
|
||||
expect(mailboxesForSiblingOf(all, null).map((m) => m.id)).toEqual(['inbox', 'trash']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('role-folder deduplication (#771)', () => {
|
||||
it('keeps user folders whose name merely contains a role folder name', () => {
|
||||
const tree = buildMailboxTree([
|
||||
own('inbox', 'Inbox', { role: 'inbox' }),
|
||||
own('sent', 'Sent', { role: 'sent' }),
|
||||
own('archive', 'Archive', { role: 'archive' }),
|
||||
own('old-inbox', 'Old Inbox'),
|
||||
own('arch-2025', '2025 Archive'),
|
||||
own('sent-acc', 'Sent to Accounting'),
|
||||
own('in', 'In'),
|
||||
]);
|
||||
expect(tree.map((n) => n.id)).toEqual([
|
||||
'inbox', 'sent', 'archive', 'arch-2025', 'in', 'old-inbox', 'sent-acc',
|
||||
]);
|
||||
});
|
||||
|
||||
it('drops only an exact (trimmed, case-insensitive) duplicate of a role folder', () => {
|
||||
const tree = buildMailboxTree([
|
||||
own('sent', 'Sent', { role: 'sent' }),
|
||||
own('sent-dup', ' sent '),
|
||||
own('sent-mail', 'Sent Mail'),
|
||||
]);
|
||||
expect(tree.map((n) => n.id)).toEqual(['sent', 'sent-mail']);
|
||||
});
|
||||
|
||||
it('never drops a nested folder or a duplicate that has children', () => {
|
||||
const tree = buildMailboxTree([
|
||||
own('inbox', 'Inbox', { role: 'inbox' }),
|
||||
own('sent', 'Sent', { role: 'sent' }),
|
||||
own('proj', 'Projects'),
|
||||
own('proj-sent', 'Sent', { parentId: 'proj' }),
|
||||
own('inbox-dup', 'Inbox'),
|
||||
own('inbox-dup-child', 'Child', { parentId: 'inbox-dup' }),
|
||||
]);
|
||||
expect(tree.map((n) => n.id)).toEqual(['inbox', 'sent', 'inbox-dup', 'proj']);
|
||||
expect(tree.find((n) => n.id === 'proj')?.children.map((n) => n.id)).toEqual(['proj-sent']);
|
||||
});
|
||||
});
|
||||
|
||||
+10
-7
@@ -44,8 +44,12 @@ const ROLE_PRIORITY: Record<string, number> = {
|
||||
};
|
||||
|
||||
// Drop root-level folders whose name collides with an existing role mailbox
|
||||
// (e.g. "Sent Mail" when a role=sent mailbox already exists). Mirrors the
|
||||
// webmail's `deduplicateMailboxes`; kept minimal (single account only).
|
||||
// (e.g. a plain "Sent" folder when a role=sent mailbox already exists).
|
||||
// Mirrors the webmail's `deduplicateMailboxes`; kept minimal (single account
|
||||
// only). Only an *exact* (trimmed, case-insensitive) name collision counts:
|
||||
// substring matching silently hid legitimate user folders whose name merely
|
||||
// contained a role folder's name — "Old Inbox", "2025 Archive",
|
||||
// "Sent to Accounting" (GitHub #771).
|
||||
function deduplicate(mailboxes: Mailbox[]): Mailbox[] {
|
||||
const roles = mailboxes.filter((m) => m.role);
|
||||
const referencedParentIds = new Set<string>();
|
||||
@@ -56,12 +60,11 @@ function deduplicate(mailboxes: Mailbox[]): Mailbox[] {
|
||||
const result: Mailbox[] = [];
|
||||
for (const m of mailboxes) {
|
||||
if (m.role) { result.push(m); continue; }
|
||||
// Never deduplicate nested folders — removing one would orphan its
|
||||
// children to the root (GitHub #118).
|
||||
if (m.parentId) { result.push(m); continue; }
|
||||
const lower = m.name.toLowerCase();
|
||||
const dup = roles.find((r) => {
|
||||
const rn = r.name.toLowerCase();
|
||||
return lower.includes(rn) || rn.includes(lower);
|
||||
});
|
||||
const lower = m.name.trim().toLowerCase();
|
||||
const dup = roles.find((r) => r.name.trim().toLowerCase() === lower);
|
||||
if (!dup || referencedParentIds.has(m.id)) result.push(m);
|
||||
}
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user