223 lines
6.9 KiB
TypeScript
223 lines
6.9 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useRef, useEffect } from "react"
|
|
import Link from "next/link"
|
|
import { toast } from "sonner"
|
|
import { Icon, addCollection } from "@iconify/react"
|
|
import { icons as mdiIcons } from "@iconify-json/mdi"
|
|
import { Pencil } from "lucide-react"
|
|
import { AccountAvatar } from "@/components/gmail/account-avatar"
|
|
import { AccountSwitcherDropdown } from "@/components/gmail/account-switcher-dropdown"
|
|
import { Button } from "@/components/ui/button"
|
|
import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity"
|
|
import { useMailSettingsStore } from "@/lib/stores/mail-settings-store"
|
|
import { MAIL_HEADER_DROPDOWN_CLASS, MAIL_ICON_BTN } from "@/lib/mail-chrome-classes"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
const HEADER_ICON_BTN_CLASS = cn(
|
|
"rounded-full",
|
|
MAIL_ICON_BTN,
|
|
"hover:text-accent-foreground",
|
|
)
|
|
|
|
addCollection(mdiIcons)
|
|
|
|
type FavoriteApp = {
|
|
name: string
|
|
icon: string
|
|
href?: string
|
|
/** Logos sombres : blanc en dark via invert + hue-rotate. */
|
|
whiteLogoInDark?: boolean
|
|
}
|
|
|
|
const googleApps: FavoriteApp[] = [
|
|
{ name: "Compte", icon: "/compte-mark.svg" },
|
|
{ name: "Agenda", icon: "/agenda-mark.svg" },
|
|
{ name: "Photos", icon: "/photos-mark.svg" },
|
|
{ name: "Ultimail", icon: "/brand/ultimail-header-icon.png", href: "/mail" },
|
|
{ name: "UltiDrive", icon: "/ultidrive-mark.svg" },
|
|
{ name: "UltiMeet", icon: "/ultimeet-mark.svg" },
|
|
{ name: "Administration", icon: "/admin-mark.svg" },
|
|
{ name: "OpenMaps", icon: "/openstreetmap-mark.svg" },
|
|
{ name: "Mistral", icon: "/mistral-mark.svg" },
|
|
{ name: "Qwant", icon: "/qwant-mark.svg", whiteLogoInDark: true },
|
|
{ name: "Ground News", icon: "/ground-news-mark.svg", whiteLogoInDark: true },
|
|
]
|
|
|
|
const FAVORITE_TILE_CLASS =
|
|
"flex flex-col items-center gap-2 rounded-lg p-3 transition-colors hover:bg-accent"
|
|
|
|
function FavoriteAppTile({ app }: { app: FavoriteApp }) {
|
|
const content = (
|
|
<>
|
|
<div className="flex h-10 w-10 items-center justify-center">
|
|
<img
|
|
src={app.icon}
|
|
alt={app.name}
|
|
className={cn(
|
|
"h-10 w-10 object-contain",
|
|
app.whiteLogoInDark && "dark:invert dark:hue-rotate-180",
|
|
)}
|
|
onError={(e) => {
|
|
const target = e.target as HTMLImageElement
|
|
target.style.display = "none"
|
|
target.parentElement!.innerHTML = `<div class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-500 font-bold text-white">${app.name[0]}</div>`
|
|
}}
|
|
/>
|
|
</div>
|
|
<span className="w-full text-center text-xs text-muted-foreground">{app.name}</span>
|
|
</>
|
|
)
|
|
|
|
if (app.href) {
|
|
return (
|
|
<Link href={app.href} className={FAVORITE_TILE_CLASS}>
|
|
{content}
|
|
</Link>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<button type="button" className={FAVORITE_TILE_CLASS}>
|
|
{content}
|
|
</button>
|
|
)
|
|
}
|
|
|
|
interface HeaderAccountActionsProps {
|
|
className?: string
|
|
}
|
|
|
|
export function HeaderAccountActions({ className }: HeaderAccountActionsProps) {
|
|
const [appsMenuOpen, setAppsMenuOpen] = useState(false)
|
|
const [accountMenuOpen, setAccountMenuOpen] = useState(false)
|
|
const appsMenuRef = useRef<HTMLDivElement>(null)
|
|
const accountMenuRef = useRef<HTMLDivElement>(null)
|
|
const identity = useChromeIdentity()
|
|
const openQuickSettings = useMailSettingsStore((s) => s.setQuickSettingsOpen)
|
|
|
|
useEffect(() => {
|
|
const notice = sessionStorage.getItem("ulti_account_notice")
|
|
if (notice === "same") {
|
|
sessionStorage.removeItem("ulti_account_notice")
|
|
toast.message("Vous utilisez déjà ce compte Ulti.")
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
function handleClickOutside(event: MouseEvent) {
|
|
if (
|
|
appsMenuRef.current &&
|
|
!appsMenuRef.current.contains(event.target as Node)
|
|
) {
|
|
setAppsMenuOpen(false)
|
|
}
|
|
}
|
|
document.addEventListener("mousedown", handleClickOutside)
|
|
return () => document.removeEventListener("mousedown", handleClickOutside)
|
|
}, [])
|
|
|
|
return (
|
|
<div className={cn("flex shrink-0 items-center gap-1", className)}>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className={cn("hidden sm:inline-flex", HEADER_ICON_BTN_CLASS)}
|
|
aria-label="Aide"
|
|
>
|
|
<Icon
|
|
icon="mdi:help-circle-outline"
|
|
className="size-6 shrink-0"
|
|
aria-hidden
|
|
/>
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className={HEADER_ICON_BTN_CLASS}
|
|
aria-label="Réglages"
|
|
onClick={() => openQuickSettings(true)}
|
|
>
|
|
<Icon icon="mdi:cog-outline" className="size-6 shrink-0" aria-hidden />
|
|
</Button>
|
|
|
|
<div className="relative hidden sm:block" ref={appsMenuRef}>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className={HEADER_ICON_BTN_CLASS}
|
|
aria-label="Applications"
|
|
onClick={() => {
|
|
setAppsMenuOpen(!appsMenuOpen)
|
|
setAccountMenuOpen(false)
|
|
}}
|
|
>
|
|
<Icon
|
|
icon="mdi:view-grid-outline"
|
|
className="size-6 shrink-0"
|
|
aria-hidden
|
|
/>
|
|
</Button>
|
|
|
|
{appsMenuOpen && (
|
|
<div
|
|
className={cn(
|
|
"absolute right-0 top-12 z-50 w-96 rounded-2xl",
|
|
MAIL_HEADER_DROPDOWN_CLASS,
|
|
)}
|
|
>
|
|
<div className="flex items-center justify-between border-b border-border p-4">
|
|
<span className="text-lg font-normal text-foreground">
|
|
Vos favoris
|
|
</span>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className={cn("h-8 w-8", HEADER_ICON_BTN_CLASS)}
|
|
>
|
|
<Pencil className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-1 p-3">
|
|
{googleApps.map((app) => (
|
|
<FavoriteAppTile key={app.name} app={app} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="relative ml-2" ref={accountMenuRef}>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon-lg"
|
|
className="size-11 overflow-hidden rounded-full p-0"
|
|
aria-label={`Compte : ${identity?.email ?? "Utilisateur"}`}
|
|
aria-expanded={accountMenuOpen}
|
|
aria-haspopup="dialog"
|
|
onClick={() => {
|
|
setAccountMenuOpen(!accountMenuOpen)
|
|
setAppsMenuOpen(false)
|
|
}}
|
|
>
|
|
{identity ? (
|
|
<AccountAvatar
|
|
account={{ name: identity.name, email: identity.email }}
|
|
size="md"
|
|
/>
|
|
) : (
|
|
<span className="flex size-10 items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground">
|
|
?
|
|
</span>
|
|
)}
|
|
</Button>
|
|
<AccountSwitcherDropdown
|
|
open={accountMenuOpen}
|
|
onOpenChange={setAccountMenuOpen}
|
|
containerRef={accountMenuRef}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|