ultisuite-client/lib/mail-nav-metrics.ts
2026-05-15 23:22:24 +02:00

123 lines
3.3 KiB
TypeScript

import type { Email } from "@/lib/email-data"
import { applyLabelEditsToEmails, mergeEmailNotSpam } from "@/lib/label-edits"
import {
emailMatchesFolder,
type MailFolderFilterCtx,
type MailNavFolderMaps,
} from "@/lib/mail-folder-filter"
import type { LabelEditState } from "@/lib/stores/mail-store"
import {
folderTree as defaultFolderTree,
sidebarNavFolderIdToLabel,
type FolderTreeNode,
} from "@/lib/sidebar-nav-data"
export const MAIN_NAV_FOLDER_IDS = [
"inbox",
"starred",
"snoozed",
"important",
"sent",
"drafts",
"scheduled",
"spam",
] as const
export const CATEGORY_NAV_IDS = [
"purchases",
"travel",
"social",
"notifications",
"updates",
"forums",
"finance",
"promotions",
] as const
function collectTreeIds(nodes: FolderTreeNode[]): string[] {
const out: string[] = []
for (const n of nodes) {
out.push(n.id)
if (n.children?.length) out.push(...collectTreeIds(n.children))
}
return out
}
const MAIN_SET = new Set<string>(MAIN_NAV_FOLDER_IDS)
const CATEGORY_SET = new Set<string>(CATEGORY_NAV_IDS)
/** Tous les ids de lignes sidebar pour lesquelles on calcule un décompte « non lus ». */
export function allSidebarNavFolderIds(maps?: MailNavFolderMaps | null): string[] {
const tree = maps?.folderTree ?? defaultFolderTree
const idToLabel =
maps?.folderIdToLabel ?? (sidebarNavFolderIdToLabel as Record<string, string>)
const treeIds = collectTreeIds(tree)
const labelRowIds = Object.keys(idToLabel).filter((id) => {
if (MAIN_SET.has(id) || CATEGORY_SET.has(id)) return false
if (id === "scheduled") return false
if (treeIds.includes(id)) return false
return true
})
return [...MAIN_NAV_FOLDER_IDS, ...CATEGORY_NAV_IDS, ...treeIds, ...labelRowIds]
}
function effectiveRead(
email: Email,
readOverrides: Record<string, boolean>
): boolean {
return readOverrides[email.id] !== undefined
? readOverrides[email.id]!
: email.read
}
export function countUnreadInFolder(
allEmails: Email[],
folderId: string,
ctx: MailFolderFilterCtx,
hiddenIds: Set<string>,
readOverrides: Record<string, boolean>,
maps?: MailNavFolderMaps | null
): number {
if (folderId === "scheduled" || folderId === "snoozed") {
let n = 0
for (const e of allEmails) {
if (hiddenIds.has(e.id)) continue
if (!emailMatchesFolder(e, folderId, ctx, maps)) continue
n++
}
return n
}
let n = 0
for (const e of allEmails) {
if (hiddenIds.has(e.id)) continue
if (!emailMatchesFolder(e, folderId, ctx, maps)) continue
if (!effectiveRead(e, readOverrides)) n++
}
return n
}
export function computeFolderUnreadCounts(
allEmails: Email[],
ctx: MailFolderFilterCtx,
hiddenEmailIds: string[],
readOverrides: Record<string, boolean>,
maps?: MailNavFolderMaps | null,
labelEdits?: LabelEditState,
notSpamEmailIds?: readonly string[]
): Record<string, number> {
let pool =
labelEdits &&
(Object.keys(labelEdits.additions).length > 0 ||
Object.keys(labelEdits.removals).length > 0)
? applyLabelEditsToEmails(allEmails, labelEdits)
: allEmails
pool = pool.map((e) => mergeEmailNotSpam(e, notSpamEmailIds ?? []))
const hidden = new Set(hiddenEmailIds)
const out: Record<string, number> = {}
for (const id of allSidebarNavFolderIds(maps)) {
out[id] = countUnreadInFolder(pool, id, ctx, hidden, readOverrides, maps)
}
return out
}