@@ -4,9 +4,57 @@ import { cn } from "@common/lib/cn";
44import { HealthCard } from "@common/elements/HealthCard" ;
55import { TimestampDistance } from "@common/elements/TimestampDistance" ;
66import { Loading } from "@common/elements/Loading" ;
7+ import { useEffect , useState } from "react" ;
8+ import semver from "semver" ;
9+ import { Button } from "@common/elements/Button" ;
10+
11+ function useLatestConvexVersion ( currentVersion : string | undefined ) {
12+ const [ hasUpdate , setHasUpdate ] = useState ( false ) ;
13+ const [ latestVersion , setLatestVersion ] = useState < string > ( ) ;
14+
15+ useEffect ( ( ) => {
16+ let isMounted = true ;
17+
18+ async function checkVersion ( ) {
19+ try {
20+ const response = await fetch (
21+ "https://registry.npmjs.org/convex/latest" ,
22+ ) ;
23+ if ( ! response . ok ) return ;
24+
25+ const data = await response . json ( ) ;
26+ if ( ! isMounted ) return ;
27+
28+ setLatestVersion ( data . version ) ;
29+
30+ if ( currentVersion && data . version ) {
31+ const hasNewVersion = semver . gt ( data . version , currentVersion ) ;
32+ setHasUpdate ( hasNewVersion ) ;
33+ }
34+ } catch ( e ) {
35+ // Swallow any errors and don't show update notice
36+ }
37+ }
38+
39+ if ( currentVersion ) {
40+ void checkVersion ( ) ;
41+ }
42+
43+ return ( ) => {
44+ isMounted = false ;
45+ } ;
46+ } , [ currentVersion ] ) ;
47+
48+ return { hasUpdate, latestVersion } ;
49+ }
750
851export function LastDeployed ( ) {
952 const lastPushEvent = useQuery ( udfs . deploymentEvents . lastPushEvent , { } ) ;
53+ const serverVersion = useQuery ( udfs . getVersion . default ) ;
54+ const { hasUpdate, latestVersion } = useLatestConvexVersion (
55+ serverVersion || undefined ,
56+ ) ;
57+
1058 const content =
1159 lastPushEvent === undefined ? (
1260 < Loading className = "h-5 w-24" />
@@ -31,7 +79,33 @@ export function LastDeployed() {
3179 size = "sm"
3280 tip = "The last time functions were deployed."
3381 >
34- < div className = "h-full w-full grow px-2 pb-2" > { content } </ div >
82+ < div className = "flex h-full w-full grow flex-wrap justify-between px-2 pb-2" >
83+ { content }
84+ < div className = "flex items-center gap-2" >
85+ < span className = "animate-fadeInFromLoading text-sm text-content-secondary" >
86+ Convex v{ serverVersion }
87+ </ span >
88+ { hasUpdate && (
89+ < Button
90+ tip = { `A ${
91+ serverVersion && latestVersion
92+ ? latestVersion . split ( "." ) [ 0 ] !== serverVersion . split ( "." ) [ 0 ]
93+ ? "major"
94+ : latestVersion . split ( "." ) [ 1 ] !==
95+ serverVersion . split ( "." ) [ 1 ]
96+ ? "minor"
97+ : "patch"
98+ : ""
99+ } update is available for Convex (${ serverVersion } → ${ latestVersion } )`}
100+ className = "bg-util-accent p-0.5 px-1 text-white"
101+ href = "https://www.npmjs.com/package/convex?activeTab=versions"
102+ target = "_blank"
103+ >
104+ Update Available
105+ </ Button >
106+ ) }
107+ </ div >
108+ </ div >
35109 </ HealthCard >
36110 ) ;
37111}
0 commit comments