123 lines
3.3 KiB
TypeScript
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
|
|
}
|