Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions apps/webapp/app/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1028,8 +1028,9 @@ const EnvironmentSchema = z
TASK_EVENT_PARTITIONING_ENABLED: z.string().default("0"),
TASK_EVENT_PARTITIONED_WINDOW_IN_SECONDS: z.coerce.number().int().default(60), // 1 minute

QUEUE_SSE_AUTORELOAD_INTERVAL_MS: z.coerce.number().int().default(5_000),
QUEUE_SSE_AUTORELOAD_TIMEOUT_MS: z.coerce.number().int().default(60_000),
DEPLOYMENTS_AUTORELOAD_POLL_INTERVAL_MS: z.coerce.number().int().default(5_000),
BULK_ACTION_AUTORELOAD_POLL_INTERVAL_MS: z.coerce.number().int().default(1_000),
QUEUES_AUTORELOAD_POLL_INTERVAL_MS: z.coerce.number().int().default(5_000),

SLACK_BOT_TOKEN: z.string().optional(),
SLACK_SIGNUP_REASON_CHANNEL_ID: z.string().optional(),
Expand Down
48 changes: 48 additions & 0 deletions apps/webapp/app/hooks/useAutoRevalidate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useRevalidator } from "@remix-run/react";
import { useEffect } from "react";

type UseAutoRevalidateOptions = {
interval?: number; // in milliseconds
onFocus?: boolean;
disabled?: boolean;
};

export function useAutoRevalidate(options: UseAutoRevalidateOptions = {}) {
const { interval = 5000, onFocus = true, disabled = false } = options;
const revalidator = useRevalidator();

useEffect(() => {
if (!interval || interval <= 0 || disabled) return;

const intervalId = setInterval(() => {
if (revalidator.state === "loading") {
return;
}
revalidator.revalidate();
}, interval);

return () => clearInterval(intervalId);
}, [interval, disabled]);

useEffect(() => {
if (!onFocus || disabled) return;

const handleFocus = () => {
if (document.visibilityState === "visible" && revalidator.state !== "loading") {
revalidator.revalidate();
}
};

// Revalidate when the page becomes visible
document.addEventListener("visibilitychange", handleFocus);
// Revalidate when the window gains focus
window.addEventListener("focus", handleFocus);

return () => {
document.removeEventListener("visibilitychange", handleFocus);
window.removeEventListener("focus", handleFocus);
};
}, [onFocus, disabled]);

return revalidator;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ArrowPathIcon } from "@heroicons/react/20/solid";
import { Form, useRevalidator } from "@remix-run/react";
import { Form } from "@remix-run/react";
import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { tryCatch } from "@trigger.dev/core";
import type { BulkActionType } from "@trigger.dev/database";
import { motion } from "framer-motion";
import { useEffect } from "react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { ExitIcon } from "~/assets/icons/ExitIcon";
Expand All @@ -18,8 +17,9 @@ import { Paragraph } from "~/components/primitives/Paragraph";
import * as Property from "~/components/primitives/PropertyTable";
import { BulkActionStatusCombo, BulkActionTypeCombo } from "~/components/runs/v3/BulkAction";
import { UserAvatar } from "~/components/UserProfilePhoto";
import { env } from "~/env.server";
import { useAutoRevalidate } from "~/hooks/useAutoRevalidate";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useEventSource } from "~/hooks/useEventSource";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
Expand Down Expand Up @@ -72,7 +72,9 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
throw new Error(error.message);
}

return typedjson({ bulkAction: data });
const autoReloadPollIntervalMs = env.BULK_ACTION_AUTORELOAD_POLL_INTERVAL_MS;

return typedjson({ bulkAction: data, autoReloadPollIntervalMs });
} catch (error) {
console.error(error);
throw new Response(undefined, {
Expand Down Expand Up @@ -130,30 +132,16 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
};

export default function Page() {
const { bulkAction } = useTypedLoaderData<typeof loader>();
const { bulkAction, autoReloadPollIntervalMs } = useTypedLoaderData<typeof loader>();
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();

const disabled = bulkAction.status !== "PENDING";

const streamedEvents = useEventSource(
`/resources/orgs/${organization.slug}/projects/${project.slug}/env/${environment.id}/runs/bulkaction/${bulkAction.friendlyId}/stream`,
{
event: "progress",
disabled,
}
);

const revalidation = useRevalidator();

useEffect(() => {
if (disabled || streamedEvents === null) {
return;
}

revalidation.revalidate();
}, [streamedEvents, disabled]);
useAutoRevalidate({
interval: autoReloadPollIntervalMs,
onFocus: true,
disabled: bulkAction.status !== "PENDING",
});

return (
<div className="grid h-full max-h-full grid-rows-[2.5rem_2.5rem_1fr_3.25rem] overflow-hidden bg-background-bright">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { ExitIcon } from "~/assets/icons/ExitIcon";
import { GitMetadata } from "~/components/GitMetadata";
import { RuntimeIcon } from "~/components/RuntimeIcon";
import { UserAvatar } from "~/components/UserProfilePhoto";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel";
import { Badge } from "~/components/primitives/Badge";
Expand Down Expand Up @@ -132,7 +131,11 @@ export default function Page() {
<Property.Label>Deploy</Property.Label>
<Property.Value className="flex items-center gap-2">
<span>{deployment.shortCode}</span>
{deployment.label && <Badge variant="outline-rounded">{deployment.label}</Badge>}
{deployment.label && (
<Badge variant="extra-small" className="capitalize">
{deployment.label}
</Badge>
)}
</Property.Value>
</Property.Item>
<Property.Item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ import {
} from "~/utils/pathBuilder";
import { createSearchParams } from "~/utils/searchParams";
import { compareDeploymentVersions } from "~/v3/utils/deploymentVersions";
import { useAutoRevalidate } from "~/hooks/useAutoRevalidate";
import { env } from "~/env.server";

export const meta: MetaFunction = () => {
return [
Expand Down Expand Up @@ -116,7 +118,9 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
? result.deployments.find((d) => d.version === version)
: undefined;

return typedjson({ ...result, selectedDeployment });
const autoReloadPollIntervalMs = env.DEPLOYMENTS_AUTORELOAD_POLL_INTERVAL_MS;

return typedjson({ ...result, selectedDeployment, autoReloadPollIntervalMs });
} catch (error) {
console.error(error);
throw new Response(undefined, {
Expand All @@ -137,13 +141,16 @@ export default function Page() {
selectedDeployment,
connectedGithubRepository,
environmentGitHubBranch,
autoReloadPollIntervalMs,
} = useTypedLoaderData<typeof loader>();
const hasDeployments = totalPages > 0;

const { deploymentParam } = useParams();
const location = useLocation();
const navigate = useNavigate();

useAutoRevalidate({ interval: autoReloadPollIntervalMs, onFocus: true });

// If we have a selected deployment from the version param, show it
useEffect(() => {
if (selectedDeployment && !deploymentParam) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,7 @@ import {
RectangleStackIcon,
} from "@heroicons/react/20/solid";
import { DialogClose } from "@radix-ui/react-dialog";
import {
Form,
useNavigate,
useNavigation,
useRevalidator,
useSearchParams,
type MetaFunction,
} from "@remix-run/react";
import { Form, useNavigation, useSearchParams, type MetaFunction } from "@remix-run/react";
import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/server-runtime";
import type { RuntimeEnvironmentType } from "@trigger.dev/database";
import { useEffect, useState } from "react";
Expand All @@ -30,7 +23,7 @@ import { Feedback } from "~/components/Feedback";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import { BigNumber } from "~/components/metrics/BigNumber";
import { Badge } from "~/components/primitives/Badge";
import { Button, ButtonVariant, LinkButton } from "~/components/primitives/Buttons";
import { Button, type ButtonVariant, LinkButton } from "~/components/primitives/Buttons";
import { Callout } from "~/components/primitives/Callout";
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components/primitives/Dialog";
import { FormButtons } from "~/components/primitives/FormButtons";
Expand All @@ -56,7 +49,6 @@ import {
TooltipTrigger,
} from "~/components/primitives/Tooltip";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useEventSource } from "~/hooks/useEventSource";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
Expand All @@ -74,6 +66,8 @@ import { Header3 } from "~/components/primitives/Headers";
import { Input } from "~/components/primitives/Input";
import { useThrottle } from "~/hooks/useThrottle";
import { RunsIcon } from "~/assets/icons/RunsIcon";
import { useAutoRevalidate } from "~/hooks/useAutoRevalidate";
import { env } from "~/env.server";

const SearchParamsSchema = z.object({
query: z.string().optional(),
Expand Down Expand Up @@ -121,9 +115,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {

const environmentQueuePresenter = new EnvironmentQueuePresenter();

const autoReloadPollIntervalMs = env.QUEUES_AUTORELOAD_POLL_INTERVAL_MS;

return typedjson({
...queues,
environment: await environmentQueuePresenter.call(environment),
autoReloadPollIntervalMs,
});
} catch (error) {
console.error(error);
Expand Down Expand Up @@ -217,28 +214,23 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
};

export default function Page() {
const { environment, queues, success, pagination, code, totalQueues, hasFilters } =
useTypedLoaderData<typeof loader>();
const {
environment,
queues,
success,
pagination,
code,
totalQueues,
hasFilters,
autoReloadPollIntervalMs,
} = useTypedLoaderData<typeof loader>();

const organization = useOrganization();
const project = useProject();
const env = useEnvironment();
const plan = useCurrentPlan();

// Reload the page periodically
const streamedEvents = useEventSource(
`/resources/orgs/${organization.slug}/projects/${project.slug}/env/${env.slug}/queues/stream`,
{
event: "update",
}
);

const revalidation = useRevalidator();
useEffect(() => {
if (streamedEvents) {
revalidation.revalidate();
}
}, [streamedEvents]);
useAutoRevalidate({ interval: autoReloadPollIntervalMs, onFocus: true });

const limitStatus =
environment.running === environment.concurrencyLimit * environment.burstFactor
Expand Down

This file was deleted.

Loading
Loading