"use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useLocalStorage } from "@app/hooks/useLocalStorage"; import { cn } from "@app/lib/cn"; import { type LatestVersionResponse, type ProductUpdate, productUpdatesQueries } from "@app/lib/queries"; import { useQueries } from "@tanstack/react-query"; import { ArrowRight, BellIcon, ChevronRightIcon, ExternalLinkIcon, RocketIcon, XIcon } from "lucide-react"; import { useTranslations } from "next-intl"; import { Transition } from "@headlessui/react"; import * as React from "react"; import { gt, valid } from "semver"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Button } from "./ui/button"; import { Badge } from "./ui/badge"; import { timeAgoFormatter } from "@app/lib/timeAgoFormatter"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip"; export default function ProductUpdates({ isCollapsed }: { isCollapsed?: boolean; }) { const { env } = useEnvContext(); const data = useQueries({ queries: [ productUpdatesQueries.list(env.app.notifications.product_updates), productUpdatesQueries.latestVersion( env.app.notifications.new_releases ) ], combine(result) { if (result[0].isLoading || result[1].isLoading) return null; return { updates: result[0].data?.data ?? [], latestVersion: result[1].data }; } }); const t = useTranslations(); const [showMoreUpdatesText, setShowMoreUpdatesText] = React.useState(false); // we delay the small text animation so that the user can notice it React.useEffect(() => { const timeout = setTimeout(() => setShowMoreUpdatesText(true), 600); return () => clearTimeout(timeout); }, []); const [ignoredVersionUpdate, setIgnoredVersionUpdate] = useLocalStorage< string | null >("product-updates:skip-version", null); const [productUpdatesRead, setProductUpdatesRead] = useLocalStorage< number[] >("product-updates:read", []); if (!data) return null; const latestVersion = data?.latestVersion?.data?.pangolin.latestVersion; const currentVersion = env.app.version; const showNewVersionPopup = Boolean( latestVersion && valid(latestVersion) && valid(currentVersion) && ignoredVersionUpdate !== latestVersion && gt(latestVersion, currentVersion) ); const filteredUpdates = data.updates.filter( (update) => !productUpdatesRead.includes(update.id) ); return (
{t("productUpdateWhatsNew")}
{t("pangolinUpdateAvailable")}