ultisuite-client/lib/mail-folder-filter.ts
2026-05-15 17:40:17 +02:00

137 lines
3.7 KiB
TypeScript

import type { Email } from "@/lib/email-data"
import {
folderTree as defaultFolderTree,
sidebarNavFolderIdToLabel as defaultSidebarNavFolderIdToLabel,
type FolderTreeNode,
} from "@/lib/sidebar-nav-data"
import { collectSubtreeFolderIds } from "@/lib/sidebar-nav-maps"
export type MailFolderFilterCtx = {
starredEmailIds: string[]
importantEmailIds: string[]
}
/** Carte navigation dynamique (sidebar). Si absent, valeurs par défaut du module. */
export type MailNavFolderMaps = {
folderIdToLabel: Record<string, string>
folderTree: FolderTreeNode[]
}
const CATEGORY_EMAIL_TAB_IDS = new Set([
"social",
"promotions",
"updates",
"forums",
])
/** Catégories latérales (sidebar) → libellé optionnel sur `email.labels`. */
const SIDEBAR_CATEGORY_EXTRA_LABEL: Partial<Record<string, string>> = {
purchases: "Achats",
travel: "Déplacements",
finance: "Finance",
}
function effectiveStarred(email: Email, ctx: MailFolderFilterCtx): boolean {
return ctx.starredEmailIds.includes(email.id) || email.starred
}
function effectiveImportant(email: Email, ctx: MailFolderFilterCtx): boolean {
return ctx.importantEmailIds.includes(email.id) || email.important
}
function isInInbox(email: Email): boolean {
if (email.spam) return false
const ls = email.labels
if (!ls?.length) return true
return ls.includes("inbox")
}
function resolveNavMaps(maps?: MailNavFolderMaps | null): MailNavFolderMaps {
if (maps) return maps
return {
folderIdToLabel: defaultSidebarNavFolderIdToLabel as Record<string, string>,
folderTree: defaultFolderTree,
}
}
function emailHasAnyLabel(email: Email, labels: string[]): boolean {
const ls = email.labels
if (!ls?.length) return false
const lower = new Set(ls.map((x) => x.toLowerCase()))
return labels.some((lab) => lower.has(lab.toLowerCase()))
}
function matchesFolderLabelRow(
email: Email,
folderId: string,
maps: MailNavFolderMaps
): boolean {
const label = maps.folderIdToLabel[folderId]
if (!label) return false
return emailHasAnyLabel(email, [label])
}
/** Dossier hiérarchique : ce nœud ou n'importe quel sous-dossier (décompte unique par mail). */
function matchesLabelNav(
email: Email,
folderId: string,
maps: MailNavFolderMaps
): boolean {
const subtreeIds = collectSubtreeFolderIds(maps.folderTree, folderId)
if (subtreeIds) {
return subtreeIds.some((id) => matchesFolderLabelRow(email, id, maps))
}
return matchesFolderLabelRow(email, folderId, maps)
}
export function emailMatchesFolder(
email: Email,
folderId: string,
ctx: MailFolderFilterCtx,
maps?: MailNavFolderMaps | null
): boolean {
const nav = resolveNavMaps(maps)
switch (folderId) {
case "inbox":
return isInInbox(email)
case "starred":
return effectiveStarred(email, ctx)
case "snoozed":
return email.labels?.includes("snoozed") ?? false
case "important":
return effectiveImportant(email, ctx)
case "sent":
return email.labels?.includes("sent") ?? false
case "drafts":
return email.labels?.includes("drafts") ?? false
case "scheduled":
return email.labels?.includes("scheduled") ?? false
case "spam":
return email.spam === true || (email.labels?.includes("spam") ?? false)
case "notifications":
return email.category === "updates"
case "purchases":
case "travel":
case "finance": {
const extra = SIDEBAR_CATEGORY_EXTRA_LABEL[folderId]
if (!extra) return false
return email.labels?.includes(extra) ?? false
}
default:
break
}
if (CATEGORY_EMAIL_TAB_IDS.has(folderId)) {
return email.category === folderId
}
if (nav.folderIdToLabel[folderId]) {
return matchesLabelNav(email, folderId, nav)
}
if (email.labels?.includes(folderId)) return true
return false
}