Skip to content

Commit 0b24f38

Browse files
committed
fix(webapp): preserve refresh behavior after dependency cleanup
1 parent 43cca67 commit 0b24f38

5 files changed

Lines changed: 31 additions & 31 deletions

File tree

apps/webapp/app/assets/icons/AnimatedHourglassIcon.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useAnimate } from "framer-motion";
22
import { HourglassIcon } from "lucide-react";
3-
import { useEffect } from "react";
3+
import { useEffect, useRef } from "react";
44

55
export function AnimatedHourglassIcon({
66
className,
@@ -10,6 +10,7 @@ export function AnimatedHourglassIcon({
1010
delay?: number;
1111
}) {
1212
const [scope, animate] = useAnimate();
13+
const initialDelay = useRef(delay);
1314

1415
useEffect(() => {
1516
const controls = animate(
@@ -19,11 +20,11 @@ export function AnimatedHourglassIcon({
1920
[scope.current, { rotate: 180 }, { duration: 0.7 }],
2021
[scope.current, { rotate: 360 }, { duration: 0.3 }],
2122
],
22-
{ repeat: Infinity, delay }
23+
{ repeat: Infinity, delay: initialDelay.current }
2324
);
2425

2526
return () => controls.stop();
26-
}, [animate, delay, scope]);
27+
}, [animate, scope]);
2728

2829
return <HourglassIcon ref={scope} className={className} />;
2930
}

apps/webapp/app/routes/resources.incidents.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { json } from "@remix-run/node";
33
import { useFetcher, type ShouldRevalidateFunction } from "@remix-run/react";
44
import { motion } from "framer-motion";
55
import { useEffect, useRef } from "react";
6+
import { useLatest } from "react-use";
67
import { LinkButton } from "~/components/primitives/Buttons";
78
import { Paragraph } from "~/components/primitives/Paragraph";
89
import { Popover, PopoverContent, PopoverTrigger } from "~/components/primitives/Popover";
@@ -42,6 +43,7 @@ export function useIncidentStatus() {
4243
const { isManagedCloud } = useFeatures();
4344
const fetcher = useFetcher<typeof loader>();
4445
const { load, state } = fetcher;
46+
const stateRef = useLatest(state);
4547
const hasInitiallyFetched = useRef(false);
4648

4749
useEffect(() => {
@@ -52,16 +54,20 @@ export function useIncidentStatus() {
5254
hasInitiallyFetched.current = true;
5355
load("/resources/incidents");
5456
}
57+
}, [isManagedCloud, load, state]);
58+
59+
useEffect(() => {
60+
if (!isManagedCloud) return;
5561

5662
// Poll every 60 seconds
5763
const interval = setInterval(() => {
58-
if (state === "idle") {
64+
if (stateRef.current === "idle") {
5965
load("/resources/incidents");
6066
}
6167
}, POLL_INTERVAL_MS);
6268

6369
return () => clearInterval(interval);
64-
}, [isManagedCloud, load, state]);
70+
}, [isManagedCloud, load, stateRef]);
6571

6672
return {
6773
status: fetcher.data?.status ?? "operational",

apps/webapp/app/routes/resources.metric.tsx

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -275,20 +275,7 @@ export function MetricWidget({
275275
// Reload periodically and on focus (onLoad: false — the useEffect below handles initial load)
276276
useInterval({ interval: refreshIntervalMs, callback: submit, onLoad: false });
277277

278-
const reloadKey = JSON.stringify({
279-
query: props.query,
280-
from: props.from,
281-
to: props.to,
282-
period: props.period,
283-
scope: props.scope,
284-
taskIdentifiers: props.taskIdentifiers,
285-
queues: props.queues,
286-
responseModels: props.responseModels,
287-
promptSlugs: props.promptSlugs,
288-
promptVersions: props.promptVersions,
289-
operations: props.operations,
290-
providers: props.providers,
291-
});
278+
const reloadKey = JSON.stringify(props);
292279

293280
// Reload on mount and when query, time period, or filters change
294281
useEffect(() => {

apps/webapp/app/routes/resources.platform-changelogs.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { json } from "@remix-run/node";
22
import type { LoaderFunctionArgs } from "@remix-run/node";
33
import { useFetcher, type ShouldRevalidateFunction } from "@remix-run/react";
44
import { useEffect, useRef } from "react";
5+
import { useLatest } from "react-use";
56
import { logger } from "~/services/logger.server";
67
import { requireUserId } from "~/services/session.server";
78
import { getRecentChangelogs, verifyOrgMembership } from "~/services/platformNotifications.server";
@@ -43,28 +44,30 @@ const POLL_INTERVAL_MS = 60_000;
4344
export function useRecentChangelogs(organizationId?: string, projectId?: string) {
4445
const fetcher = useFetcher<typeof loader>();
4546
const { load, state } = fetcher;
47+
const stateRef = useLatest(state);
4648
const lastLoadedUrl = useRef<string | null>(null);
49+
const params = new URLSearchParams();
50+
if (organizationId) params.set("organizationId", organizationId);
51+
if (projectId) params.set("projectId", projectId);
52+
const qs = params.toString();
53+
const url = `/resources/platform-changelogs${qs ? `?${qs}` : ""}`;
4754

4855
useEffect(() => {
49-
const params = new URLSearchParams();
50-
if (organizationId) params.set("organizationId", organizationId);
51-
if (projectId) params.set("projectId", projectId);
52-
const qs = params.toString();
53-
const url = `/resources/platform-changelogs${qs ? `?${qs}` : ""}`;
54-
5556
if (lastLoadedUrl.current !== url && state === "idle") {
5657
lastLoadedUrl.current = url;
5758
load(url);
5859
}
60+
}, [load, state, url]);
5961

62+
useEffect(() => {
6063
const interval = setInterval(() => {
61-
if (state === "idle") {
64+
if (stateRef.current === "idle") {
6265
load(url);
6366
}
6467
}, POLL_INTERVAL_MS);
6568

6669
return () => clearInterval(interval);
67-
}, [organizationId, projectId, load, state]);
70+
}, [load, stateRef, url]);
6871

6972
return {
7073
changelogs: fetcher.data?.changelogs ?? [],

apps/webapp/app/routes/resources.platform-notifications.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { json } from "@remix-run/node";
22
import type { LoaderFunctionArgs } from "@remix-run/node";
33
import { useFetcher, type ShouldRevalidateFunction } from "@remix-run/react";
44
import { useEffect, useRef } from "react";
5+
import { useLatest } from "react-use";
56
import { requireUserId } from "~/services/session.server";
67
import {
78
getActivePlatformNotifications,
@@ -42,24 +43,26 @@ const POLL_INTERVAL_MS = 60000; // 1 minute
4243
export function usePlatformNotifications(organizationId: string, projectId: string) {
4344
const fetcher = useFetcher<typeof loader>();
4445
const { load, state } = fetcher;
46+
const stateRef = useLatest(state);
4547
const lastLoadedUrl = useRef<string | null>(null);
48+
const url = `/resources/platform-notifications?organizationId=${encodeURIComponent(organizationId)}&projectId=${encodeURIComponent(projectId)}`;
4649

4750
useEffect(() => {
48-
const url = `/resources/platform-notifications?organizationId=${encodeURIComponent(organizationId)}&projectId=${encodeURIComponent(projectId)}`;
49-
5051
if (lastLoadedUrl.current !== url && state === "idle") {
5152
lastLoadedUrl.current = url;
5253
load(url);
5354
}
55+
}, [load, state, url]);
5456

57+
useEffect(() => {
5558
const interval = setInterval(() => {
56-
if (state === "idle") {
59+
if (stateRef.current === "idle") {
5760
load(url);
5861
}
5962
}, POLL_INTERVAL_MS);
6063

6164
return () => clearInterval(interval);
62-
}, [organizationId, projectId, load, state]);
65+
}, [load, stateRef, url]);
6366

6467
return {
6568
notifications: fetcher.data?.notifications ?? [],

0 commit comments

Comments
 (0)