Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced turbopack alias for canvas in next.config.mjs. - Updated package.json scripts for development and branding tasks. - Added new dependencies for Tiptap extensions. - Implemented new demo layouts for agenda, contacts, drive, and mail applications. - Enhanced globals.css for improved theming and splash screen animations. - Added OAuth callback handling for drive mounts. - Updated layout components to integrate new demo shells and improve structure.
134 lines
4.1 KiB
TypeScript
134 lines
4.1 KiB
TypeScript
import type { Identity } from "@/lib/compose-context"
|
|
import type {
|
|
AgendaAutoImportSource,
|
|
AgendaInvitationExclusion,
|
|
} from "@/lib/agenda/agenda-settings-types"
|
|
|
|
export function normalizeDestinationEmail(email: string): string {
|
|
return email.trim().toLowerCase()
|
|
}
|
|
|
|
/** Extrait l'email d'une clé stockée (legacy `accountId:email` ou email seul). */
|
|
export function destinationEmailFromStoredKey(key: string): string {
|
|
const trimmed = key.trim()
|
|
if (!trimmed) return trimmed
|
|
const at = trimmed.lastIndexOf("@")
|
|
if (at <= 0) return normalizeDestinationEmail(trimmed)
|
|
const colon = trimmed.lastIndexOf(":", at)
|
|
if (colon >= 0) return normalizeDestinationEmail(trimmed.slice(colon + 1))
|
|
return normalizeDestinationEmail(trimmed)
|
|
}
|
|
|
|
export type AgendaDestinationMailbox = {
|
|
/** Clé stable (email normalisé). */
|
|
key: string
|
|
/** Libellé affiché (casse d'origine). */
|
|
label: string
|
|
}
|
|
|
|
function addDestinationEmail(map: Map<string, string>, raw: string) {
|
|
const trimmed = raw.trim()
|
|
if (!trimmed || !trimmed.includes("@")) return
|
|
const key = normalizeDestinationEmail(trimmed)
|
|
if (!map.has(key)) map.set(key, trimmed)
|
|
}
|
|
|
|
/** Boîtes mail de destination uniques (comptes + identités), dédupliquées par email. */
|
|
export function buildDestinationMailboxes(
|
|
identities: Identity[],
|
|
accountEmails: string[] = [],
|
|
): AgendaDestinationMailbox[] {
|
|
const map = new Map<string, string>()
|
|
for (const email of accountEmails) addDestinationEmail(map, email)
|
|
for (const identity of identities) addDestinationEmail(map, identity.email)
|
|
return [...map.entries()]
|
|
.map(([key, label]) => ({ key, label }))
|
|
.sort((a, b) => a.label.localeCompare(b.label, "fr", { sensitivity: "base" }))
|
|
}
|
|
|
|
export function destinationMailboxLabel(
|
|
storedKey: string,
|
|
mailboxes: AgendaDestinationMailbox[],
|
|
): string {
|
|
const key = destinationEmailFromStoredKey(storedKey)
|
|
return mailboxes.find((m) => m.key === key)?.label ?? key
|
|
}
|
|
|
|
export function normalizeAutoImportDestinationKeys(keys: string[]): string[] {
|
|
return [...new Set(keys.map(destinationEmailFromStoredKey).filter(Boolean))]
|
|
}
|
|
|
|
export function normalizeAutoImportInvitationSources(
|
|
items: AgendaInvitationExclusion[],
|
|
mailboxes: AgendaDestinationMailbox[] = [],
|
|
): AgendaAutoImportSource[] {
|
|
const seen = new Set<string>()
|
|
const out: AgendaAutoImportSource[] = []
|
|
for (const item of items) {
|
|
if (item.type !== "identity" && item.type !== "contact") continue
|
|
let normalized: AgendaAutoImportSource
|
|
if (item.type === "identity") {
|
|
const value = destinationEmailFromStoredKey(item.value)
|
|
normalized = {
|
|
type: "identity",
|
|
value,
|
|
label: destinationMailboxLabel(value, mailboxes) || value,
|
|
}
|
|
} else {
|
|
const value = normalizeDestinationEmail(item.value)
|
|
if (!value) continue
|
|
normalized = {
|
|
type: "contact",
|
|
value,
|
|
label: item.label.trim() || value,
|
|
}
|
|
}
|
|
const id =
|
|
normalized.type === "identity"
|
|
? `identity:${normalized.value}`
|
|
: `contact:${normalized.value}`
|
|
if (seen.has(id)) continue
|
|
seen.add(id)
|
|
out.push(normalized)
|
|
}
|
|
return out
|
|
}
|
|
|
|
export function migrateAutoImportIdentityKeys(
|
|
keys: string[],
|
|
mailboxes: AgendaDestinationMailbox[] = [],
|
|
): AgendaAutoImportSource[] {
|
|
return normalizeAutoImportInvitationSources(
|
|
keys.map((key) => ({
|
|
type: "identity" as const,
|
|
value: destinationEmailFromStoredKey(key),
|
|
label: destinationEmailFromStoredKey(key),
|
|
})),
|
|
mailboxes,
|
|
)
|
|
}
|
|
|
|
export function normalizeInvitationImportExclusions(
|
|
items: AgendaInvitationExclusion[],
|
|
mailboxes: AgendaDestinationMailbox[] = [],
|
|
): AgendaInvitationExclusion[] {
|
|
const seen = new Set<string>()
|
|
const out: AgendaInvitationExclusion[] = []
|
|
for (const item of items) {
|
|
if (item.type !== "identity") {
|
|
out.push(item)
|
|
continue
|
|
}
|
|
const value = destinationEmailFromStoredKey(item.value)
|
|
const id = `identity:${value}`
|
|
if (seen.has(id)) continue
|
|
seen.add(id)
|
|
out.push({
|
|
type: "identity",
|
|
value,
|
|
label: destinationMailboxLabel(value, mailboxes) || value,
|
|
})
|
|
}
|
|
return out
|
|
}
|