Skip to content

Commit 9c4f9e6

Browse files
committed
WIP on disconnected banner on v3 and v4
1 parent 2e22f0c commit 9c4f9e6

File tree

4 files changed

+127
-125
lines changed
  • apps/webapp/app
    • components
    • routes
      • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam
      • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index

4 files changed

+127
-125
lines changed

apps/webapp/app/components/DevPresence.tsx

Lines changed: 87 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,38 @@ export function useDevPresence() {
9393
return context;
9494
}
9595

96+
/**
97+
* We need this for the legacy v1 engine, where we show the banner after a delay if there are no events.
98+
*/
99+
export function useCrossEngineIsConnected({ logCount }: { logCount: number }) {
100+
const project = useProject();
101+
const environment = useEnvironment();
102+
const { isConnected } = useDevPresence();
103+
const [crossEngineIsConnected, setCrossEngineIsConnected] = useState<boolean | undefined>(
104+
undefined
105+
);
106+
107+
useEffect(() => {
108+
if (project.engine === "V2") {
109+
setCrossEngineIsConnected(isConnected);
110+
return;
111+
}
112+
113+
if (project.engine === "V1") {
114+
if (logCount <= 1) {
115+
const timer = setTimeout(() => {
116+
setCrossEngineIsConnected(false);
117+
}, 5000);
118+
return () => clearTimeout(timer);
119+
} else {
120+
setCrossEngineIsConnected(true);
121+
}
122+
}
123+
}, [environment.type, project.engine, logCount, isConnected]);
124+
125+
return crossEngineIsConnected;
126+
}
127+
96128
export function ConnectionIcon({ isConnected }: { isConnected: boolean | undefined }) {
97129
if (isConnected === undefined) {
98130
return <CheckingConnectionIcon className="size-5" />;
@@ -104,88 +136,75 @@ export function ConnectionIcon({ isConnected }: { isConnected: boolean | undefin
104136
);
105137
}
106138

107-
export function DevConnection({
108-
children,
109-
}: {
110-
children: (props: { isConnected: boolean | undefined }) => React.ReactNode;
111-
}) {
112-
const { isConnected } = useDevPresence();
113-
139+
export function DevPresencePanel({ isConnected }: { isConnected: boolean | undefined }) {
114140
return (
115-
<Dialog>
116-
{children({ isConnected })}
117-
<DialogContent>
118-
<DialogHeader>
119-
{isConnected === undefined
120-
? "Checking connection..."
121-
: isConnected
122-
? "Your dev server is connected"
123-
: "Your dev server is not connected"}
124-
</DialogHeader>
125-
<div className="mt-2 flex flex-col gap-3 px-2">
126-
<div className="flex flex-col items-center justify-center gap-6 px-6 py-10">
127-
<img
128-
src={isConnected === true ? connectedImage : disconnectedImage}
129-
alt={isConnected === true ? "Connected" : "Disconnected"}
130-
width={282}
131-
height={45}
132-
/>
133-
<Paragraph variant="small" className={isConnected ? "text-success" : "text-error"}>
134-
{isConnected === undefined
135-
? "Checking connection..."
136-
: isConnected
137-
? "Your local dev server is connected to Trigger.dev"
138-
: "Your local dev server is not connected to Trigger.dev"}
141+
<DialogContent>
142+
<DialogHeader>
143+
{isConnected === undefined
144+
? "Checking connection..."
145+
: isConnected
146+
? "Your dev server is connected"
147+
: "Your dev server is not connected"}
148+
</DialogHeader>
149+
<div className="mt-2 flex flex-col gap-3 px-2">
150+
<div className="flex flex-col items-center justify-center gap-6 px-6 py-10">
151+
<img
152+
src={isConnected === true ? connectedImage : disconnectedImage}
153+
alt={isConnected === true ? "Connected" : "Disconnected"}
154+
width={282}
155+
height={45}
156+
/>
157+
<Paragraph variant="small" className={isConnected ? "text-success" : "text-error"}>
158+
{isConnected === undefined
159+
? "Checking connection..."
160+
: isConnected
161+
? "Your local dev server is connected to Trigger.dev"
162+
: "Your local dev server is not connected to Trigger.dev"}
163+
</Paragraph>
164+
</div>
165+
{isConnected ? null : (
166+
<div className="space-y-3">
167+
<PackageManagerProvider>
168+
<TriggerDevStepV3 title="Run this command to connect" />
169+
</PackageManagerProvider>
170+
<Paragraph variant="small">
171+
Run this CLI <InlineCode variant="extra-small">dev</InlineCode> command to connect to
172+
the Trigger.dev servers to start developing locally. Keep it running while you develop
173+
to stay connected. Learn more in the{" "}
174+
<TextLink to={docsPath("cli-dev")}>CLI docs</TextLink>.
139175
</Paragraph>
140176
</div>
141-
{isConnected ? null : (
142-
<div className="space-y-3">
143-
<PackageManagerProvider>
144-
<TriggerDevStepV3 title="Run this command to connect" />
145-
</PackageManagerProvider>
146-
<Paragraph variant="small">
147-
Run this CLI <InlineCode variant="extra-small">dev</InlineCode> command to connect
148-
to the Trigger.dev servers to start developing locally. Keep it running while you
149-
develop to stay connected. Learn more in the{" "}
150-
<TextLink to={docsPath("cli-dev")}>CLI docs</TextLink>.
151-
</Paragraph>
152-
</div>
153-
)}
154-
</div>
155-
</DialogContent>
156-
</Dialog>
177+
)}
178+
</div>
179+
</DialogContent>
157180
);
158181
}
159182

160-
export function DevPresenceBanner() {
161-
const environment = useEnvironment();
162-
const { isConnected } = useDevPresence();
163-
183+
export function DevDisconnectedBanner({ isConnected }: { isConnected: boolean | undefined }) {
164184
return (
165-
<AnimatePresence>
166-
{environment.type === "DEVELOPMENT" && !isConnected && (
185+
<Dialog>
186+
<AnimatePresence>
167187
<motion.div
168188
initial={{ opacity: 0 }}
169189
animate={{ opacity: 1 }}
170190
exit={{ opacity: 0 }}
171191
transition={{ duration: 0.3 }}
172192
className="flex"
173193
>
174-
<DevConnection>
175-
{({ isConnected }) => (
176-
<DialogTrigger asChild>
177-
<Button
178-
variant="minimal/small"
179-
className="py-1 pl-1 pr-2 text-error"
180-
LeadingIcon={<ConnectionIcon isConnected={isConnected} />}
181-
>
182-
Your local dev server is not connected to Trigger.dev
183-
</Button>
184-
</DialogTrigger>
185-
)}
186-
</DevConnection>
194+
{isConnected === false && (
195+
<DialogTrigger asChild>
196+
<Button
197+
variant="minimal/small"
198+
className="py-1 pl-1 pr-2 text-error"
199+
LeadingIcon={<ConnectionIcon isConnected={false} />}
200+
>
201+
Your local dev server is not connected to Trigger.dev
202+
</Button>
203+
</DialogTrigger>
204+
)}
187205
</motion.div>
188-
)}
189-
</AnimatePresence>
206+
</AnimatePresence>
207+
<DevPresencePanel isConnected={isConnected} />
208+
</Dialog>
190209
);
191210
}

apps/webapp/app/components/navigation/SideMenu.tsx

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,11 @@ import {
5555
v3UsagePath,
5656
v3WaitpointTokensPath,
5757
} from "~/utils/pathBuilder";
58-
5958
import { FreePlanUsage } from "../billing/FreePlanUsage";
60-
import { ConnectionIcon, DevConnection } from "../DevPresence";
59+
import { ConnectionIcon, DevPresencePanel, useDevPresence } from "../DevPresence";
6160
import { ImpersonationBanner } from "../ImpersonationBanner";
6261
import { Button, ButtonContent, LinkButton } from "../primitives/Buttons";
63-
import { DialogTrigger } from "../primitives/Dialog";
62+
import { Dialog, DialogTrigger } from "../primitives/Dialog";
6463
import { Paragraph } from "../primitives/Paragraph";
6564
import {
6665
Popover,
@@ -105,6 +104,7 @@ export function SideMenu({
105104
const borderRef = useRef<HTMLDivElement>(null);
106105
const [showHeaderDivider, setShowHeaderDivider] = useState(false);
107106
const currentPlan = useCurrentPlan();
107+
const { isConnected } = useDevPresence();
108108
const isFreeUser = currentPlan?.v3Subscription?.isPaying === false;
109109

110110
useEffect(() => {
@@ -154,32 +154,31 @@ export function SideMenu({
154154
environment={environment}
155155
/>
156156
{environment.type === "DEVELOPMENT" && project.engine === "V2" && (
157-
<DevConnection>
158-
{({ isConnected }) => (
159-
<TooltipProvider disableHoverableContent={true}>
160-
<Tooltip>
161-
<TooltipTrigger asChild>
162-
<div className="inline-flex">
163-
<DialogTrigger asChild>
164-
<Button
165-
variant="minimal/small"
166-
className="aspect-square h-7 p-1"
167-
LeadingIcon={<ConnectionIcon isConnected={isConnected} />}
168-
/>
169-
</DialogTrigger>
170-
</div>
171-
</TooltipTrigger>
172-
<TooltipContent side="right" className={"text-xs"}>
173-
{isConnected === undefined
174-
? "Checking connection..."
175-
: isConnected
176-
? "Your dev server is connected"
177-
: "Your dev server is not connected"}
178-
</TooltipContent>
179-
</Tooltip>
180-
</TooltipProvider>
181-
)}
182-
</DevConnection>
157+
<Dialog>
158+
<TooltipProvider disableHoverableContent={true}>
159+
<Tooltip>
160+
<TooltipTrigger asChild>
161+
<div className="inline-flex">
162+
<DialogTrigger asChild>
163+
<Button
164+
variant="minimal/small"
165+
className="aspect-square h-7 p-1"
166+
LeadingIcon={<ConnectionIcon isConnected={isConnected} />}
167+
/>
168+
</DialogTrigger>
169+
</div>
170+
</TooltipTrigger>
171+
<TooltipContent side="right" className={"text-xs"}>
172+
{isConnected === undefined
173+
? "Checking connection..."
174+
: isConnected
175+
? "Your dev server is connected"
176+
: "Your dev server is not connected"}
177+
</TooltipContent>
178+
</Tooltip>
179+
</TooltipProvider>
180+
<DevPresencePanel isConnected={isConnected} />
181+
</Dialog>
183182
)}
184183
</div>
185184
</div>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ import { useHotkeys } from "react-hotkeys-hook";
2727
import { DisconnectedIcon } from "~/assets/icons/ConnectionIcons";
2828
import { ShowParentIcon, ShowParentIconSelected } from "~/assets/icons/ShowParentIcon";
2929
import tileBgPath from "~/assets/images/[email protected]";
30-
import { DevPresenceBanner, useDevPresence } from "~/components/DevPresence";
30+
import {
31+
DevDisconnectedBanner,
32+
useCrossEngineIsConnected,
33+
useDevPresence,
34+
} from "~/components/DevPresence";
3135
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
3236
import { PageBody } from "~/components/layout/AppLayout";
3337
import { Badge } from "~/components/primitives/Badge";
@@ -184,6 +188,7 @@ export default function Page() {
184188
const organization = useOrganization();
185189
const project = useProject();
186190
const environment = useEnvironment();
191+
const isConnected = useCrossEngineIsConnected({ logCount: trace?.events.length ?? 0 });
187192

188193
return (
189194
<>
@@ -195,19 +200,7 @@ export default function Page() {
195200
}}
196201
title={`Run #${run.number}`}
197202
/>
198-
<AnimatePresence>
199-
{environment.type === "DEVELOPMENT" && (
200-
<motion.div
201-
initial={{ opacity: 0 }}
202-
animate={{ opacity: 1 }}
203-
exit={{ opacity: 0 }}
204-
transition={{ duration: 0.3 }}
205-
className="flex"
206-
>
207-
<DevPresenceBanner />
208-
</motion.div>
209-
)}
210-
</AnimatePresence>
203+
{environment.type === "DEVELOPMENT" && <DevDisconnectedBanner isConnected={isConnected} />}
211204
<PageAccessories>
212205
<AdminDebugTooltip>
213206
<Property.Table>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ListChecks, ListX } from "lucide-react";
88
import { Suspense, useState } from "react";
99
import { TypedAwait, typeddefer, useTypedLoaderData } from "remix-typedjson";
1010
import { TaskIcon } from "~/assets/icons/TaskIcon";
11-
import { DevPresenceBanner, useDevPresence } from "~/components/DevPresence";
11+
import { DevDisconnectedBanner, useDevPresence } from "~/components/DevPresence";
1212
import { StepContentContainer } from "~/components/StepContentContainer";
1313
import { MainCenteredContainer, PageBody } from "~/components/layout/AppLayout";
1414
import { Button, LinkButton } from "~/components/primitives/Buttons";
@@ -163,25 +163,16 @@ export default function Page() {
163163
const navigation = useNavigation();
164164
const isLoading = navigation.state !== "idle";
165165
const { isConnected } = useDevPresence();
166+
const project = useProject();
166167
const environment = useEnvironment();
167168

168169
return (
169170
<>
170171
<NavBar>
171172
<PageTitle title="Runs" />
172-
<AnimatePresence>
173-
{environment.type === "DEVELOPMENT" && !isConnected && (
174-
<motion.div
175-
initial={{ opacity: 0 }}
176-
animate={{ opacity: 1 }}
177-
exit={{ opacity: 0 }}
178-
transition={{ duration: 0.3 }}
179-
className="flex"
180-
>
181-
<DevPresenceBanner />
182-
</motion.div>
183-
)}
184-
</AnimatePresence>
173+
{environment.type === "DEVELOPMENT" && project.engine === "V2" && (
174+
<DevDisconnectedBanner isConnected={isConnected} />
175+
)}
185176
<PageAccessories>
186177
<LinkButton
187178
variant={"docs/small"}

0 commit comments

Comments
 (0)