184 lines
5.3 KiB
TypeScript
184 lines
5.3 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useRef, type CSSProperties } from "react"
|
|
import { Printer, ExternalLink } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip"
|
|
import { cn } from "@/lib/utils"
|
|
import type { Email } from "@/lib/email-data"
|
|
import type { FolderTreeNode, LabelRowItem } from "@/lib/sidebar-nav-data"
|
|
import { openConversationPrint } from "@/lib/print-conversation"
|
|
import { MailLabelPillStrip } from "@/components/gmail/mail-label-pills"
|
|
import {
|
|
MAIL_ICON_BTN,
|
|
MAIL_PREVIEW_SUBJECT_HEADER_CLASS,
|
|
MAIL_TOOLTIP_CONTENT_CLASS,
|
|
} from "@/lib/mail-chrome-classes"
|
|
import { useTheme } from "next-themes"
|
|
import {
|
|
emailPreviewSubjectCss,
|
|
} from "@/lib/email-preview-dark-styles"
|
|
|
|
const EMAIL_PREVIEW_IFRAME_STYLE: CSSProperties = {
|
|
display: "block",
|
|
background: "transparent",
|
|
}
|
|
|
|
function documentIsDark(): boolean {
|
|
return document.documentElement.classList.contains("dark")
|
|
}
|
|
|
|
function SandboxedSubject({ text }: { text: string }) {
|
|
const iframeRef = useRef<HTMLIFrameElement>(null)
|
|
const { resolvedTheme } = useTheme()
|
|
|
|
useEffect(() => {
|
|
const iframe = iframeRef.current
|
|
if (!iframe) return
|
|
const doc = iframe.contentDocument
|
|
if (!doc) return
|
|
|
|
const isDark = documentIsDark()
|
|
|
|
doc.open()
|
|
doc.write(`<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline';">
|
|
<style>${emailPreviewSubjectCss(isDark)}</style>
|
|
</head>
|
|
<body>${text.replace(/</g, "<").replace(/>/g, ">")}</body>
|
|
</html>`)
|
|
doc.close()
|
|
}, [text, resolvedTheme])
|
|
|
|
return (
|
|
<iframe
|
|
ref={iframeRef}
|
|
sandbox="allow-same-origin"
|
|
title="Sujet du message"
|
|
className="pointer-events-none w-full border-0 bg-transparent"
|
|
style={{ ...EMAIL_PREVIEW_IFRAME_STYLE, height: "32px" }}
|
|
tabIndex={-1}
|
|
/>
|
|
)
|
|
}
|
|
|
|
const LABEL_DISPLAY_NAMES: Record<string, string> = {
|
|
inbox: "Boîte de réception",
|
|
starred: "Suivis",
|
|
snoozed: "En attente",
|
|
important: "Important",
|
|
sent: "Messages envoyés",
|
|
drafts: "Brouillons",
|
|
spam: "Spam",
|
|
trash: "Corbeille",
|
|
}
|
|
|
|
export interface EmailViewSubjectHeaderProps {
|
|
email: Email
|
|
isSpamMessage: boolean
|
|
onNotSpam?: () => void
|
|
onNavigateToLabel?: (label: string) => void
|
|
showLabelChip?: (label: string) => boolean
|
|
labelBgByText?: Map<string, string>
|
|
emailLabelToSidebarFolderId?: Record<string, string>
|
|
getNavItemPrefs?: (id: string) => { messages: string }
|
|
folderTree?: FolderTreeNode[]
|
|
labelRows?: readonly LabelRowItem[]
|
|
currentFolderId?: string
|
|
}
|
|
|
|
export function EmailViewSubjectHeader({
|
|
email,
|
|
isSpamMessage,
|
|
onNotSpam,
|
|
onNavigateToLabel,
|
|
showLabelChip,
|
|
labelBgByText,
|
|
emailLabelToSidebarFolderId = {},
|
|
getNavItemPrefs = () => ({ messages: "show" }),
|
|
folderTree,
|
|
labelRows,
|
|
currentFolderId,
|
|
}: EmailViewSubjectHeaderProps) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex items-start gap-3 px-6 py-4 max-sm:px-4",
|
|
MAIL_PREVIEW_SUBJECT_HEADER_CLASS
|
|
)}
|
|
>
|
|
<div className="min-w-0 flex-1">
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<SandboxedSubject text={email.subject} />
|
|
{labelBgByText && onNavigateToLabel ? (
|
|
<MailLabelPillStrip
|
|
variant="header"
|
|
labels={email.labels ?? ["inbox"]}
|
|
labelBgByText={labelBgByText}
|
|
emailLabelToSidebarFolderId={emailLabelToSidebarFolderId}
|
|
getNavItemPrefs={getNavItemPrefs}
|
|
labelRows={labelRows}
|
|
folderTree={folderTree}
|
|
currentFolderId={currentFolderId}
|
|
onLabelNavigate={onNavigateToLabel}
|
|
showLabel={showLabelChip}
|
|
resolveDisplayName={(lab) => LABEL_DISPLAY_NAMES[lab] ?? lab}
|
|
showRemoveOnPills
|
|
spamChip={
|
|
isSpamMessage && onNotSpam ? { onNotSpam } : undefined
|
|
}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
<div className="flex shrink-0 items-center gap-1">
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className={cn("h-8 w-8", MAIL_ICON_BTN)}
|
|
aria-label="Imprimer"
|
|
onClick={() => openConversationPrint(email)}
|
|
>
|
|
<Printer className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent
|
|
side="bottom"
|
|
className={cn(MAIL_TOOLTIP_CONTENT_CLASS, "text-xs")}
|
|
>
|
|
Imprimer tout
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className={cn("h-8 w-8", MAIL_ICON_BTN)}
|
|
aria-label="Ouvrir dans une nouvelle fenêtre"
|
|
>
|
|
<ExternalLink className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent
|
|
side="bottom"
|
|
className={cn(MAIL_TOOLTIP_CONTENT_CLASS, "text-xs")}
|
|
>
|
|
Dans une nouvelle fenêtre
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|