1
- import { AppShell , Badge , Burger , Code , Container , Group , ScrollArea , Text } from '@mantine/core'
1
+ import {
2
+ AppShell ,
3
+ Badge ,
4
+ Burger ,
5
+ Code ,
6
+ Container ,
7
+ Group ,
8
+ Indicator ,
9
+ ScrollArea ,
10
+ Text
11
+ } from '@mantine/core'
2
12
import { useClickOutside , useDisclosure , useMediaQuery } from '@mantine/hooks'
3
13
import { useQuery } from '@tanstack/react-query'
4
14
import { useEffect , useState } from 'react'
5
15
import { Outlet } from 'react-router-dom'
16
+ import semver from 'semver'
6
17
import axios from 'axios'
7
18
8
19
import { getBuildInfo } from '@shared/utils/get-build-info/get-build-info.util'
@@ -21,6 +32,15 @@ export function MainLayout() {
21
32
const [ buildInfoModalOpened , setBuildInfoModalOpened ] = useState ( false )
22
33
const [ isMediaQueryReady , setIsMediaQueryReady ] = useState ( false )
23
34
35
+ const [ versions , setVersions ] = useState < {
36
+ currentVersion : string
37
+ latestVersion : string
38
+ } > ( {
39
+ currentVersion : '0.0.0' ,
40
+ latestVersion : '0.0.0'
41
+ } )
42
+ const [ isNewVersionAvailable , setIsNewVersionAvailable ] = useState ( false )
43
+
24
44
const buildInfo = getBuildInfo ( )
25
45
26
46
const isMobile = useMediaQuery ( `(max-width: 64rem)` , undefined , {
@@ -52,6 +72,31 @@ export function MainLayout() {
52
72
}
53
73
} )
54
74
75
+ const { data : latestVersion } = useQuery ( {
76
+ queryKey : [ 'github-latest-version' ] ,
77
+ staleTime : sToMs ( 3600 ) ,
78
+ refetchInterval : sToMs ( 3600 ) ,
79
+ queryFn : async ( ) => {
80
+ const response = await axios . get < {
81
+ release : {
82
+ tag : string
83
+ }
84
+ } > ( 'https://ungh.cc/repos/remnawave/panel/releases/latest' )
85
+ return response . data . release . tag
86
+ }
87
+ } )
88
+
89
+ useEffect ( ( ) => {
90
+ setVersions ( {
91
+ currentVersion : buildInfo . tag ?? '0.0.0' ,
92
+ latestVersion : latestVersion ?? '0.0.0'
93
+ } )
94
+ } , [ latestVersion , buildInfo . tag ] )
95
+
96
+ useEffect ( ( ) => {
97
+ setIsNewVersionAvailable ( semver . gt ( versions . latestVersion , versions . currentVersion ) )
98
+ } , [ versions ] )
99
+
55
100
return isMediaQueryReady ? (
56
101
< AppShell
57
102
header = { { height : 64 } }
@@ -116,27 +161,41 @@ export function MainLayout() {
116
161
</ Text >
117
162
</ Group >
118
163
{ buildInfo . branch === 'dev' && (
119
- < Badge
120
- color = "red"
121
- onClick = { ( ) => setBuildInfoModalOpened ( true ) }
122
- radius = "sm"
123
- size = "lg"
124
- style = { { cursor : 'help' , marginLeft : 'auto' } }
125
- variant = "light"
164
+ < Indicator
165
+ color = "cyan"
166
+ disabled = { ! isNewVersionAvailable }
167
+ processing
168
+ size = { 11 }
126
169
>
127
- dev
128
- </ Badge >
170
+ < Badge
171
+ color = "red"
172
+ onClick = { ( ) => setBuildInfoModalOpened ( true ) }
173
+ radius = "sm"
174
+ size = "lg"
175
+ style = { { cursor : 'help' , marginLeft : 'auto' } }
176
+ variant = "light"
177
+ >
178
+ dev
179
+ </ Badge >
180
+ </ Indicator >
129
181
) }
130
182
131
183
{ buildInfo . branch !== 'dev' && (
132
- < Code
133
- c = "cyan"
134
- fw = { 700 }
135
- onClick = { ( ) => setBuildInfoModalOpened ( true ) }
136
- style = { { cursor : 'pointer' , marginLeft : 'auto' } }
184
+ < Indicator
185
+ color = "cyan"
186
+ disabled = { ! isNewVersionAvailable }
187
+ processing
188
+ size = { 11 }
137
189
>
138
- { `v${ packageJson . version } ` }
139
- </ Code >
190
+ < Code
191
+ c = "cyan"
192
+ fw = { 700 }
193
+ onClick = { ( ) => setBuildInfoModalOpened ( true ) }
194
+ style = { { cursor : 'pointer' , marginLeft : 'auto' } }
195
+ >
196
+ { `v${ packageJson . version } ` }
197
+ </ Code >
198
+ </ Indicator >
140
199
) }
141
200
142
201
{ isSocialButton && (
@@ -175,6 +234,7 @@ export function MainLayout() {
175
234
176
235
< BuildInfoModal
177
236
buildInfo = { buildInfo }
237
+ isNewVersionAvailable = { isNewVersionAvailable }
178
238
onClose = { ( ) => setBuildInfoModalOpened ( false ) }
179
239
opened = { buildInfoModalOpened }
180
240
/>
0 commit comments