308 lines
10 KiB
TypeScript
308 lines
10 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import {
|
|
Star,
|
|
Reply,
|
|
Forward,
|
|
MoreVertical,
|
|
Printer,
|
|
Trash2,
|
|
Mail,
|
|
Ban,
|
|
ShieldAlert,
|
|
Fish,
|
|
Flag,
|
|
SlidersHorizontal,
|
|
Languages,
|
|
Download,
|
|
Code2,
|
|
MessageCircleWarning,
|
|
ChevronDown,
|
|
TriangleAlert,
|
|
} from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip"
|
|
import { cn } from "@/lib/utils"
|
|
import { avatarColor, cleanSenderName, senderInitial } from "@/lib/sender-display"
|
|
import { MailDateText } from "@/components/gmail/mail-date-text"
|
|
import { ContactHoverCard } from "@/components/gmail/contact-hover-card"
|
|
import { EmailViewDetailsPopover } from "@/components/gmail/email-view/email-view-details-popover"
|
|
import type { MessageHeaderDetails } from "@/lib/mail-message-header-details"
|
|
import {
|
|
MAIL_ICON_BTN,
|
|
MAIL_MENU_SURFACE_WIDE_CLASS,
|
|
MAIL_TOOLTIP_CONTENT_CLASS,
|
|
} from "@/lib/mail-chrome-classes"
|
|
|
|
const MESSAGE_MORE_MENU_CLASS = MAIL_MENU_SURFACE_WIDE_CLASS
|
|
|
|
const MENU_ICON_CLASS = "size-[18px] shrink-0 text-muted-foreground"
|
|
|
|
export interface EmailViewMessageToolbarProps {
|
|
sender: string
|
|
senderEmail: string
|
|
headerDetails: MessageHeaderDetails
|
|
dateIso: string
|
|
isSpam: boolean
|
|
isLast: boolean
|
|
starred: boolean
|
|
onToggleStar?: () => void
|
|
onCollapse?: () => void
|
|
onPrintConversation?: () => void
|
|
onReply?: () => void
|
|
onForward?: () => void
|
|
detailsOpen?: boolean
|
|
onDetailsOpenChange?: (open: boolean) => void
|
|
messageId: string
|
|
}
|
|
|
|
export function EmailViewMessageToolbar({
|
|
sender,
|
|
senderEmail,
|
|
headerDetails,
|
|
dateIso,
|
|
isSpam,
|
|
isLast,
|
|
starred,
|
|
onToggleStar,
|
|
onCollapse,
|
|
onPrintConversation,
|
|
onReply,
|
|
onForward,
|
|
detailsOpen,
|
|
onDetailsOpenChange,
|
|
messageId,
|
|
}: EmailViewMessageToolbarProps) {
|
|
const name = cleanSenderName(sender)
|
|
const [internalDetailsOpen, setInternalDetailsOpen] = useState(false)
|
|
const detailsIsOpen = detailsOpen ?? internalDetailsOpen
|
|
const setDetailsIsOpen = onDetailsOpenChange ?? setInternalDetailsOpen
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className={cn("flex items-start gap-3 px-4 py-3", !isLast && "cursor-pointer")}
|
|
onClick={
|
|
!isLast
|
|
? () => {
|
|
setDetailsIsOpen(false)
|
|
onCollapse?.()
|
|
}
|
|
: undefined
|
|
}
|
|
>
|
|
{isSpam ? (
|
|
<div
|
|
className="flex h-10 w-10 shrink-0 self-start items-center justify-center rounded-full bg-muted text-amber-600"
|
|
aria-label="Expéditeur ou message suspect (spam)"
|
|
>
|
|
<TriangleAlert className="size-[22px]" strokeWidth={2} aria-hidden />
|
|
</div>
|
|
) : (
|
|
<div
|
|
className="flex h-10 w-10 shrink-0 self-start items-center justify-center rounded-full text-sm font-bold text-white"
|
|
style={{ backgroundColor: avatarColor(name) }}
|
|
>
|
|
{senderInitial(name)}
|
|
</div>
|
|
)}
|
|
|
|
<div className="min-w-0 flex-1 flex flex-col gap-1" data-selectable-text>
|
|
<div className="min-w-0 truncate text-sm leading-snug">
|
|
<ContactHoverCard
|
|
displayName={sender}
|
|
email={senderEmail}
|
|
onTriggerClick={!isLast ? (e) => e.stopPropagation() : undefined}
|
|
className="inline min-w-0 max-w-full align-baseline"
|
|
>
|
|
<span className="font-semibold text-foreground">{name}</span>
|
|
{senderEmail ? (
|
|
<span className="text-muted-foreground"> <{senderEmail}></span>
|
|
) : null}
|
|
</ContactHoverCard>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1">
|
|
<EmailViewDetailsPopover
|
|
summary={headerDetails.recipientSummary}
|
|
details={headerDetails}
|
|
open={detailsIsOpen}
|
|
onOpenChange={setDetailsIsOpen}
|
|
isSpam={isSpam}
|
|
messageId={messageId}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex shrink-0 flex-col items-end gap-1 self-start pt-0.5">
|
|
<div className="flex items-center gap-1">
|
|
<MailDateText
|
|
iso={dateIso}
|
|
variant="preview"
|
|
className="hidden text-xs text-muted-foreground sm:inline"
|
|
/>
|
|
|
|
{onToggleStar ? (
|
|
<button
|
|
type="button"
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
onToggleStar()
|
|
}}
|
|
className="flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-full text-muted-foreground/60 hover:bg-black/4 hover:text-muted-foreground"
|
|
aria-label={starred ? "Retirer des favoris" : "Marquer comme favori"}
|
|
>
|
|
<Star
|
|
strokeWidth={starred ? 0 : 1.25}
|
|
className={cn(
|
|
"size-4",
|
|
starred
|
|
? "fill-amber-300 stroke-none text-amber-300"
|
|
: "fill-transparent stroke-muted-foreground/60"
|
|
)}
|
|
/>
|
|
</button>
|
|
) : (
|
|
<Star
|
|
strokeWidth={1.25}
|
|
className="ml-1 size-4 fill-transparent stroke-muted-foreground/60"
|
|
/>
|
|
)}
|
|
|
|
<Tooltip delayDuration={400}>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className={cn("h-8 w-8", MAIL_ICON_BTN)}
|
|
aria-label="Répondre"
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
onReply?.()
|
|
}}
|
|
>
|
|
<Reply className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent
|
|
side="bottom"
|
|
className={cn(MAIL_TOOLTIP_CONTENT_CLASS, "text-xs")}
|
|
>
|
|
Répondre
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className={cn("h-8 w-8", MAIL_ICON_BTN)}
|
|
aria-label="Plus d'actions"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<MoreVertical className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent
|
|
align="end"
|
|
sideOffset={4}
|
|
className={MESSAGE_MORE_MENU_CLASS}
|
|
>
|
|
<DropdownMenuItem
|
|
onSelect={() => {
|
|
onReply?.()
|
|
}}
|
|
>
|
|
<Reply className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Répondre
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onSelect={() => {
|
|
onForward?.()
|
|
}}
|
|
>
|
|
<Forward className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Transférer
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem>
|
|
<Trash2 className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Supprimer
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Mail className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Marquer comme non lus à partir d'ici
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem>
|
|
<Ban className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Bloquer « {name} »
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<ShieldAlert className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Signaler comme spam
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Fish className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Signaler comme hameçonnage
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Flag className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Signaler un contenu illégal
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem>
|
|
<SlidersHorizontal className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Filtrer les messages similaires
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Languages className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Traduire
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onSelect={() => {
|
|
onPrintConversation?.()
|
|
}}
|
|
>
|
|
<Printer className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Imprimer
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Download className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Télécharger le message
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Code2 className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Afficher l'original
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<MessageCircleWarning className={MENU_ICON_CLASS} strokeWidth={1.5} />
|
|
Partager pour aider à améliorer Google
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
<MailDateText
|
|
iso={dateIso}
|
|
variant="previewShort"
|
|
className="text-xs text-muted-foreground sm:hidden"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|