ultisuite-client/lib/mail-folder-filter.ts
2026-05-15 23:51:57 +02:00

151 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
subtreeIdsCache?: Map<string, string[] | null>
): boolean {
let subtreeIds: string[] | null
if (subtreeIdsCache) {
if (!subtreeIdsCache.has(folderId)) {
subtreeIdsCache.set(
folderId,
collectSubtreeFolderIds(maps.folderTree, folderId)
)
}
subtreeIds = subtreeIdsCache.get(folderId) ?? null
} else {
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,
/** Réutiliser entre appels (ex. `computeFolderUnreadCounts`) pour éviter de rescanner larbre à chaque mail. */
subtreeIdsCache?: Map<string, string[] | 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, subtreeIdsCache)
}
if (email.labels?.includes(folderId)) return true
return false
}