import { useNavigate } from "@tanstack/react-router"; import { useAtomValue } from "@effect/atom-react"; import type { ServerProvider } from "@t3tools/contracts"; import { CircleCheckIcon, DownloadIcon, LoaderIcon, TriangleAlertIcon, XIcon } from "lucide-react"; import { useCallback, useEffect, useState, type CSSProperties } from "react"; import { primaryServerProvidersAtom } from "../../state/server"; import { getProviderUpdateSidebarPillView, type ProviderUpdateSidebarPillView, } from "../ProviderUpdateLaunchNotification.logic"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; const PROVIDER_UPDATE_PILL_STYLES = { loading: "bg-primary/15 text-primary group-has-[button.provider-update-main:hover]/provider-update:bg-primary/22", success: "bg-success/12 text-success group-has-[button.provider-update-main:hover]/provider-update:bg-success/18", warning: "bg-warning/12 text-warning group-has-[button.provider-update-main:hover]/provider-update:bg-warning/18", error: "bg-destructive/12 text-destructive group-has-[button.provider-update-main:hover]/provider-update:bg-destructive/18", } as const; const PROVIDER_UPDATE_PILL_PROGRESS_STYLES = { success: "bg-success/18", warning: "bg-warning/14", error: "bg-destructive/14", } as const; function providerUpdatePillProgressStyle( tone: ProviderUpdateSidebarPillView["tone"], ): string | null { return tone === "loading" ? null : PROVIDER_UPDATE_PILL_PROGRESS_STYLES[tone]; } interface SidebarProviderUpdatePillContentProps { providers: ReadonlyArray; onOpenProviderSettings: () => void; } function useProviderUpdateSidebarVisibleAfterIso(): string { const [visibleAfterIso] = useState(() => new Date().toISOString()); return visibleAfterIso; } function useSidebarProviderUpdatePillTransition( view: ProviderUpdateSidebarPillView | null, onDismissViewKey: (viewKey: string) => void, ) { const [renderedView, setRenderedView] = useState(view); const [pendingView, setPendingView] = useState(null); const [exitingKey, setExitingKey] = useState(null); const [dismissAfterExitKey, setDismissAfterExitKey] = useState(null); const displayedView = renderedView ?? view; const dismissAfterVisibleMs = displayedView?.dismissAfterVisibleMs; const viewKey = displayedView?.key ?? null; const dismissProgressStyle = displayedView ? providerUpdatePillProgressStyle(displayedView.tone) : null; const showDismissProgress = dismissAfterVisibleMs !== undefined && dismissProgressStyle !== null && exitingKey !== viewKey; const startExit = useCallback( (key: string, nextView: ProviderUpdateSidebarPillView | null, dismissKey?: string) => { if (exitingKey === key) { return; } setPendingView(nextView); setExitingKey(key); setDismissAfterExitKey(dismissKey ?? null); }, [exitingKey], ); useEffect(() => { if (exitingKey !== null) { return; } if (!renderedView) { if (view) { setRenderedView(view); } return; } if (!view) { startExit(renderedView.key, null); return; } if (view.key !== renderedView.key) { startExit(renderedView.key, view); return; } }, [exitingKey, renderedView, startExit, view]); useEffect(() => { if (!dismissAfterVisibleMs || !viewKey) { return; } if (exitingKey === viewKey) { return; } const timeoutId = window.setTimeout(() => { startExit(viewKey, null, viewKey); }, dismissAfterVisibleMs); return () => window.clearTimeout(timeoutId); }, [dismissAfterVisibleMs, exitingKey, startExit, viewKey]); const finishExit = useCallback( (viewKey: string) => { if (dismissAfterExitKey === viewKey) { onDismissViewKey(viewKey); } setRenderedView(pendingView); setPendingView(null); setExitingKey(null); setDismissAfterExitKey(null); }, [dismissAfterExitKey, onDismissViewKey, pendingView], ); return { displayedView, dismissAfterVisibleMs, dismissProgressStyle, exitingKey, finishExit, showDismissProgress, startExit, }; } export function SidebarProviderUpdatePillContent({ providers, onOpenProviderSettings, }: SidebarProviderUpdatePillContentProps) { const [dismissedKeys, setDismissedKeys] = useState>(() => new Set()); const visibleAfterIso = useProviderUpdateSidebarVisibleAfterIso(); const view = getProviderUpdateSidebarPillView(providers, { visibleAfterIso, dismissedKeys, }); const dismissViewKey = useCallback((viewKey: string) => { setDismissedKeys((previous) => new Set(previous).add(viewKey)); }, []); const { displayedView, dismissAfterVisibleMs, dismissProgressStyle, exitingKey, finishExit, showDismissProgress, startExit, } = useSidebarProviderUpdatePillTransition(view, dismissViewKey); if (!displayedView) { return null; } return (
{ if (event.target !== event.currentTarget) { return; } if (!displayedView || exitingKey !== displayedView.key) { return; } finishExit(displayedView.key); }} > {showDismissProgress && dismissProgressStyle ? (