|
1 | 1 | "use client"; |
2 | 2 |
|
3 | 3 | import { useQuery } from "convex/react"; |
4 | | -import { useEffect, useRef, useState } from "react"; |
| 4 | +import { useState, useMemo } from "react"; |
5 | 5 | import type { FunctionReference } from "convex/server"; |
6 | 6 |
|
7 | 7 | type DeploymentInfo = { |
@@ -46,34 +46,36 @@ export function useDeploymentUpdates( |
46 | 46 | getCurrentDeployment: FunctionReference<"query", "public", Record<string, never>, DeploymentInfo>, |
47 | 47 | ) { |
48 | 48 | const deployment = useQuery(getCurrentDeployment, {}); |
49 | | - const initialDeploymentId = useRef<string | null>(null); |
50 | | - const [updateAvailable, setUpdateAvailable] = useState(false); |
| 49 | + const [initialDeploymentId, setInitialDeploymentId] = useState<string | null>(null); |
| 50 | + const [dismissedDeploymentId, setDismissedDeploymentId] = useState<string | null>(null); |
51 | 51 |
|
52 | | - useEffect(() => { |
53 | | - if (!deployment) return; |
| 52 | + // Capture the initial deployment ID on first load |
| 53 | + // Using useState with functional update to avoid stale closure issues |
| 54 | + if (deployment && initialDeploymentId === null) { |
| 55 | + // This is safe - we're setting initial state based on first data load |
| 56 | + // It only runs once when deployment first becomes available |
| 57 | + setInitialDeploymentId(deployment.currentDeploymentId); |
| 58 | + } |
54 | 59 |
|
55 | | - // Store the initial deployment ID on first load |
56 | | - if (initialDeploymentId.current === null) { |
57 | | - initialDeploymentId.current = deployment.currentDeploymentId; |
58 | | - return; |
| 60 | + // Derive updateAvailable from current state |
| 61 | + const updateAvailable = useMemo(() => { |
| 62 | + if (!deployment || initialDeploymentId === null) { |
| 63 | + return false; |
59 | 64 | } |
60 | | - |
61 | | - // Check if deployment changed |
62 | | - if (deployment.currentDeploymentId !== initialDeploymentId.current) { |
63 | | - setUpdateAvailable(true); |
64 | | - } |
65 | | - }, [deployment]); |
| 65 | + // Show update if deployment changed from initial AND user hasn't dismissed this one |
| 66 | + const hasNewDeployment = deployment.currentDeploymentId !== initialDeploymentId; |
| 67 | + const isDismissed = deployment.currentDeploymentId === dismissedDeploymentId; |
| 68 | + return hasNewDeployment && !isDismissed; |
| 69 | + }, [deployment, initialDeploymentId, dismissedDeploymentId]); |
66 | 70 |
|
67 | 71 | const reload = () => { |
68 | 72 | window.location.reload(); |
69 | 73 | }; |
70 | 74 |
|
71 | 75 | const dismiss = () => { |
72 | | - // Update the stored deployment ID so we don't show the banner again |
73 | 76 | if (deployment) { |
74 | | - initialDeploymentId.current = deployment.currentDeploymentId; |
| 77 | + setDismissedDeploymentId(deployment.currentDeploymentId); |
75 | 78 | } |
76 | | - setUpdateAvailable(false); |
77 | 79 | }; |
78 | 80 |
|
79 | 81 | return { |
|
0 commit comments