Menu
Public repository / 275d4fa11a39ecc30e2caf604d5a0d499910e8db
AkurAI-Mail / src/lib/webmail/folders.ts
Latest change 22202d1bde69be4ace8a299dffd91f8ff24378df - Recover deployed webmail frontend (frontend parity with EC2 a6afffc) by Ólafur Búi Ólafsson
/** Turns flat IMAP folder names ("Projects/2026") into a collapsible tree. */
export interface FolderSummary {
name: string;
unread?: number;
total?: number;
}
export interface FolderNode {
name: string;
path: string;
depth: number;
unread: number;
total: number;
children: FolderNode[];
}
export function buildFolderTree(folders: FolderSummary[]): FolderNode[] {
const roots: FolderNode[] = [];
const byPath = new Map<string, FolderNode>();
for (const folder of folders) {
const segments = String(folder.name).split("/");
let path = "";
let siblings = roots;
segments.forEach((segment, depth) => {
path = path ? `${path}/${segment}` : segment;
let node = byPath.get(path);
if (!node) {
node = { name: segment, path, depth, unread: 0, total: 0, children: [] };
byPath.set(path, node);
siblings.push(node);
}
if (depth === segments.length - 1) {
node.unread = folder.unread ?? 0;
node.total = folder.total ?? 0;
}
siblings = node.children;
});
}
return roots;
}
/** Depth-first list of visible nodes; folders are expanded unless `expanded[path]` is false. */
export function visibleFolders(nodes: FolderNode[], expanded: Record<string, boolean>): FolderNode[] {
const rows: FolderNode[] = [];
for (const node of nodes) {
rows.push(node);
if (node.children.length && (expanded[node.path] ?? true)) rows.push(...visibleFolders(node.children, expanded));
}
return rows;
}