import { AnimatePresence, motion } from "framer-motion"; import { createContext, type ReactNode, useContext, useEffect, useMemo, useState } from "react"; import { CheckingConnectionIcon, ConnectedIcon, DisconnectedIcon, } from "~/assets/icons/ConnectionIcons"; import { useEnvironment } from "~/hooks/useEnvironment"; import { useEventSource } from "~/hooks/useEventSource"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { docsPath } from "~/utils/pathBuilder"; import connectedImage from "../assets/images/cli-connected.png"; import disconnectedImage from "../assets/images/cli-disconnected.png"; import { InlineCode } from "./code/InlineCode"; import { Button } from "./primitives/Buttons"; import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "./primitives/Dialog"; import { Paragraph } from "./primitives/Paragraph"; import { TextLink } from "./primitives/TextLink"; import { PackageManagerProvider, TriggerDevStepV3 } from "./SetupCommands"; // Define Context types type DevPresenceContextType = { isConnected: boolean | undefined; }; // Create Context with default values const DevPresenceContext = createContext({ isConnected: undefined, }); // Provider component with enabled prop interface DevPresenceProviderProps { children: ReactNode; enabled?: boolean; } export function DevPresenceProvider({ children, enabled = true }: DevPresenceProviderProps) { const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); // Only subscribe to event source if enabled is true const streamedEvents = useEventSource( `/resources/orgs/${organization.slug}/projects/${project.slug}/env/${environment.slug}/presence`, { event: "presence", disabled: !enabled, } ); const [isConnected, setIsConnected] = useState(undefined); useEffect(() => { // If disabled or no events if (!enabled || streamedEvents === null) { setIsConnected(undefined); return; } try { const data = JSON.parse(streamedEvents) as any; if ("isConnected" in data && data.isConnected) { try { setIsConnected(true); } catch (error) { console.log("DevPresence: Failed to parse lastSeen timestamp", { error }); setIsConnected(false); } } else { setIsConnected(false); } } catch (error) { console.log("DevPresence: Failed to parse presence message", { error }); setIsConnected(false); } }, [streamedEvents, enabled]); // Calculate isConnected and memoize the context value const contextValue = useMemo(() => { return { isConnected }; }, [isConnected, enabled]); return {children}; } // Custom hook to use the context export function useDevPresence() { const context = useContext(DevPresenceContext); if (context === undefined) { throw new Error("useDevPresence must be used within a DevPresenceProvider"); } return context; } /** * We need this for the legacy v1 engine, where we show the banner after a delay if there are no events. */ export function useCrossEngineIsConnected({ isCompleted, logCount, }: { isCompleted: boolean; logCount: number; }) { const project = useProject(); const environment = useEnvironment(); const { isConnected } = useDevPresence(); const [crossEngineIsConnected, setCrossEngineIsConnected] = useState( undefined ); useEffect(() => { if (project.engine === "V2") { setCrossEngineIsConnected(isConnected); return; } if (project.engine === "V1") { if (isCompleted) { setCrossEngineIsConnected(true); return; } if (logCount <= 1) { const timer = setTimeout(() => { setCrossEngineIsConnected(false); }, 5000); return () => clearTimeout(timer); } else { setCrossEngineIsConnected(true); } } }, [environment.type, project.engine, logCount, isConnected, isCompleted]); return crossEngineIsConnected; } export function ConnectionIcon({ isConnected }: { isConnected: boolean | undefined }) { if (isConnected === undefined) { return ; } return isConnected ? ( ) : ( ); } export function DevPresencePanel({ isConnected }: { isConnected: boolean | undefined }) { return ( {isConnected === undefined ? "Checking connection..." : isConnected ? "Your dev server is connected" : "Your dev server is not connected"}
{isConnected {isConnected === undefined ? "Checking connection..." : isConnected ? "Your local dev server is connected to Trigger.dev" : "Your local dev server is not connected to Trigger.dev"}
{isConnected ? null : (
Run this CLI dev command to connect to the Trigger.dev servers to start developing locally. Keep it running while you develop to stay connected. Learn more in the{" "} CLI docs.
)}
); } export function DevDisconnectedBanner({ isConnected }: { isConnected: boolean | undefined }) { return ( {isConnected === false && ( )} ); }