Skip to content

Commit bee650a

Browse files
committed
Improve the realtime hooks approach in the waitpoint reference project
1 parent 427a741 commit bee650a

File tree

7 files changed

+236
-331
lines changed

7 files changed

+236
-331
lines changed

references/waitpoint-tokens/src/app/actions.ts

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@
22

33
import type { articleWorkflow } from "@/trigger/articleWorkflow";
44
import type { ReviewPayload } from "@/trigger/reviewSummary";
5-
import { tasks, wait } from "@trigger.dev/sdk/v3";
6-
7-
// A user identifier that could be fetched from your auth mechanism.
8-
// This is out of scope for this example, so we just hardcode it.
9-
const user = "reactflowtest";
10-
const userTag = `user_${user}`;
5+
import { auth, tasks, wait } from "@trigger.dev/sdk/v3";
116

127
const randomStr = (length: number) =>
138
[...Array(length)]
@@ -21,30 +16,41 @@ const randomStr = (length: number) =>
2116

2217
export async function triggerArticleWorkflow(prevState: any, formData: FormData) {
2318
const articleUrl = formData.get("articleUrl") as string;
24-
const uniqueTag = `reactflow_${randomStr(20)}`;
19+
const workflowTag = `reactflow_${randomStr(20)}`;
2520

2621
const reviewWaitpointToken = await wait.createToken({
27-
tags: [uniqueTag, userTag],
22+
tags: [workflowTag],
2823
timeout: "1h",
29-
idempotencyKey: `review-summary-${uniqueTag}`,
24+
idempotencyKey: `review-summary-${workflowTag}`,
3025
});
3126

32-
const handle = await tasks.trigger<typeof articleWorkflow>(
33-
"article-workflow",
34-
{
35-
articleUrl,
36-
approvalWaitpointTokenId: reviewWaitpointToken.id,
37-
},
38-
{
39-
tags: [uniqueTag, userTag],
40-
}
41-
);
27+
const [workflowPublicAccessToken] = await Promise.all([
28+
// We generate a public access token to use the Trigger.dev realtime API and listen to changes in task runs using react hooks.
29+
// This token has access to all runs tagged with the unique workflow tag.
30+
auth.createPublicToken({
31+
scopes: {
32+
read: {
33+
tags: [workflowTag],
34+
},
35+
},
36+
}),
37+
,
38+
tasks.trigger<typeof articleWorkflow>(
39+
"article-workflow",
40+
{
41+
articleUrl,
42+
approvalWaitpointTokenId: reviewWaitpointToken.id,
43+
},
44+
{
45+
tags: [workflowTag],
46+
}
47+
),
48+
]);
4249

4350
return {
4451
articleUrl,
45-
runId: handle.id,
46-
runTag: uniqueTag,
47-
reviewWaitpointTokenId: reviewWaitpointToken.id,
52+
workflowTag,
53+
workflowPublicAccessToken,
4854
};
4955
}
5056

@@ -54,7 +60,7 @@ export async function approveArticleSummary(tokenId: string) {
5460
{
5561
approved: true,
5662
approvedAt: new Date(),
57-
approvedBy: user,
63+
approvedBy: "Alice",
5864
}
5965
);
6066
}
@@ -65,7 +71,7 @@ export async function rejectArticleSummary(tokenId: string) {
6571
{
6672
approved: false,
6773
rejectedAt: new Date(),
68-
rejectedBy: user,
74+
rejectedBy: "Alice",
6975
reason: "It's no good",
7076
}
7177
);

references/waitpoint-tokens/src/app/page.tsx

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,8 @@ import React from "react";
22
import Flow from "@/components/Flow";
33
import Image from "next/image";
44
import logo from "./logo.svg";
5-
import { auth } from "@trigger.dev/sdk";
65

76
export default async function Home() {
8-
// A user identifier that could be fetched from your auth mechanism.
9-
// This is out of scope for this example, so we just hardcode it.
10-
const user = "reactflowtest";
11-
const userTag = `user_${user}`;
12-
13-
// We generate a public access token to use the Trigger.dev realtime API and listen to changes in task runs.
14-
// Depending on your setup, you might want to be more granular in the scopes you grant.
15-
// Check the frontend usage docs for a comprehensive list of the approaches to authenticate:
16-
// https://trigger.dev/docs/frontend/overview#authentication
17-
const publicAccessToken = await auth.createPublicToken({
18-
scopes: {
19-
read: {
20-
tags: [userTag],
21-
},
22-
},
23-
});
24-
257
return (
268
<div className="flex flex-col items-center justify-center h-screen p-5 gap-5">
279
<div className="flex flex-col items-center justify-center gap-2">
@@ -33,7 +15,7 @@ export default async function Home() {
3315
</p>
3416
</div>
3517
<div className="grow w-full max-w-[1500px]">
36-
<Flow triggerPublicAccessToken={publicAccessToken} triggerUserTag={userTag} />
18+
<Flow />
3719
</div>
3820
</div>
3921
);

references/waitpoint-tokens/src/components/ActionNode.tsx

Lines changed: 14 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"use client";
22

3-
import React, { useEffect } from "react";
4-
import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react";
3+
import React from "react";
4+
import { Handle, Position, NodeProps, Node } from "@xyflow/react";
55
import type { LucideIcon } from "lucide-react";
66
import { Loader2, Check, X, Layers, Asterisk, RefreshCcw } from "lucide-react";
77
import { Tooltip } from "react-tippy";
8-
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
8+
import type { RealtimeRun, AnyTask } from "@trigger.dev/sdk";
99
import { cn } from "@/lib/cn";
1010

1111
export type ActionNodeData = Node<
@@ -15,24 +15,7 @@ export type ActionNodeData = Node<
1515
isTerminalAction?: boolean;
1616
trigger: {
1717
taskIdentifier: string;
18-
userTag: string;
19-
currentRunTag?: string;
20-
currentRunStatus?:
21-
| "WAITING_FOR_DEPLOY"
22-
| "PENDING_VERSION"
23-
| "QUEUED"
24-
| "EXECUTING"
25-
| "REATTEMPTING"
26-
| "FROZEN"
27-
| "COMPLETED"
28-
| "CANCELED"
29-
| "FAILED"
30-
| "CRASHED"
31-
| "INTERRUPTED"
32-
| "SYSTEM_FAILURE"
33-
| "DELAYED"
34-
| "EXPIRED"
35-
| "TIMED_OUT";
18+
currentRun?: RealtimeRun<AnyTask>;
3619
};
3720
},
3821
"action"
@@ -50,60 +33,33 @@ const triggerStatusToIcon: Record<string, React.ElementType> = {
5033
FAILED: X,
5134
};
5235

53-
function ActionNode({ id, data }: NodeProps<ActionNodeData>) {
54-
const { runs } = useRealtimeRunsWithTag(data.trigger.userTag);
55-
const { updateNodeData } = useReactFlow<ActionNodeData>();
56-
57-
useEffect(() => {
58-
if (!data.trigger.currentRunTag && data.trigger.currentRunStatus !== undefined) {
59-
updateNodeData(id, {
60-
trigger: { ...data.trigger, currentRunStatus: undefined },
61-
});
62-
return;
63-
}
64-
65-
const run = runs.find(
66-
(run) =>
67-
run.tags.includes(data.trigger.currentRunTag as string) &&
68-
run.taskIdentifier === data.trigger.taskIdentifier
69-
);
70-
if (!run) {
71-
if (data.trigger.currentRunStatus !== undefined) {
72-
updateNodeData(id, {
73-
trigger: { ...data.trigger, currentRunStatus: undefined },
74-
});
75-
}
76-
return;
77-
}
78-
updateNodeData(id, {
79-
trigger: { ...data.trigger, currentRunStatus: run.status },
80-
});
81-
}, [runs, id, updateNodeData]);
36+
function ActionNode({ data }: NodeProps<ActionNodeData>) {
37+
const { currentRun } = data.trigger;
8238

8339
return (
8440
<div className="px-4 py-2 shadow-md rounded-lg bg-white border-1 border-zinc-200 text-sm relative">
85-
{data.trigger.currentRunStatus && (
41+
{currentRun && (
8642
<div
8743
className={cn(
8844
"absolute -top-1.5 -right-1.5 flex items-center justify-center w-4 h-4 rounded-full bg-gray-400",
8945
{
90-
"bg-blue-400": data.trigger.currentRunStatus === "EXECUTING",
91-
"bg-yellow-400": data.trigger.currentRunStatus === "REATTEMPTING",
92-
"bg-emerald-400": data.trigger.currentRunStatus === "COMPLETED",
93-
"bg-red-400": data.trigger.currentRunStatus === "FAILED",
46+
"bg-blue-400": currentRun.status === "EXECUTING",
47+
"bg-yellow-400": currentRun.status === "REATTEMPTING",
48+
"bg-emerald-400": currentRun.status === "COMPLETED",
49+
"bg-red-400": currentRun.status === "FAILED",
9450
}
9551
)}
9652
>
9753
{/* @ts-ignore - there is some weird type issue with react-tippy */}
9854
<Tooltip
99-
title={data.trigger.currentRunStatus.toLowerCase()}
55+
title={currentRun.status.toLowerCase()}
10056
position="right"
10157
trigger="mouseenter"
10258
size="small"
10359
>
104-
{React.createElement(triggerStatusToIcon[data.trigger.currentRunStatus] ?? Asterisk, {
60+
{React.createElement(triggerStatusToIcon[currentRun.status] ?? Asterisk, {
10561
className: cn("size-3 text-white", {
106-
"animate-spin": data.trigger.currentRunStatus === "EXECUTING",
62+
"animate-spin": currentRun.status === "EXECUTING",
10763
}),
10864
})}
10965
</Tooltip>

0 commit comments

Comments
 (0)