mirror of
https://github.com/remnawave/frontend.git
synced 2026-05-13 04:09:03 +00:00
feat: add version check and alert for updates in MainLayout and BuildInfoModal
- Integrated semver to compare current and latest versions. - Added an alert in BuildInfoModal to notify users of available updates. - Updated MainLayout to manage version state and display indicators for new versions.
This commit is contained in:
parent
7a7cb03e7e
commit
79fa16176f
6 changed files with 155 additions and 28 deletions
20
.github/workflows/release-frontend.yml
vendored
20
.github/workflows/release-frontend.yml
vendored
|
|
@ -65,6 +65,14 @@ jobs:
|
|||
}
|
||||
EOF
|
||||
|
||||
- name: Generate changelog
|
||||
id: changelog
|
||||
run: |
|
||||
CHANGELOG=$(npx changelogen --from=${{ steps.tag.outputs.previousTag }} --to=${{ steps.tag.outputs.latestTag }})
|
||||
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
|
||||
echo "$CHANGELOG" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies and build
|
||||
run: |
|
||||
npm install
|
||||
|
|
@ -88,13 +96,17 @@ jobs:
|
|||
prerelease: false
|
||||
name: ${{ github.ref_name }}
|
||||
body: |
|
||||
🎉 Automatic release of Remnawave Frontend, v${{ github.ref_name }}
|
||||
🌊 Remnawave Frontend v${{ github.ref_name }}
|
||||
|
||||
This release was automatically created through GitHub Actions.
|
||||
<p align="center">
|
||||
<a href="https://t.me/remnawave" target="_blank" rel="noopener noreferrer">
|
||||
<img src="https://img.shields.io/badge/Join%20community-Telegram-26A5E4?style=for-the-badge&logo=telegram&logoColor=white" alt="Join community on Telegram" width="220" height="auto">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
### 📝 Changes
|
||||
📝 Compare changes: [${{ steps.tag.outputs.previousTag }}...${{ steps.tag.outputs.latestTag }}](https://github.com/${{ github.repository }}/compare/${{ steps.tag.outputs.previousTag }}...${{ steps.tag.outputs.latestTag }})
|
||||
|
||||
✏️ Compare: [${{ steps.tag.outputs.previousTag }}...${{ steps.tag.outputs.latestTag }}](https://github.com/${{ github.repository }}/compare/${{ steps.tag.outputs.previousTag }}...${{ steps.tag.outputs.latestTag }})
|
||||
${{ env.CHANGELOG }}
|
||||
|
||||
### 📦 Artifacts
|
||||
- remnawave-frontend.zip - archive with built frontend
|
||||
|
|
|
|||
4
changelog.config.json
Normal file
4
changelog.config.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"hideAuthorEmail": true,
|
||||
"noAuthors": true
|
||||
}
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@remnawave/frontend",
|
||||
"version": "1.5.4",
|
||||
"version": "1.5.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@remnawave/frontend",
|
||||
"version": "1.5.4",
|
||||
"version": "1.5.5",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@gfazioli/mantine-text-animate": "^1.0.2",
|
||||
|
|
@ -59,6 +59,7 @@
|
|||
"react-imask": "^7.6.1",
|
||||
"react-router-dom": "6.27.0",
|
||||
"recharts": "^2.15.1",
|
||||
"semver": "^7.7.1",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"uqr": "^0.1.2",
|
||||
"vite-plugin-deadfile": "^1.4.0",
|
||||
|
|
@ -79,6 +80,7 @@
|
|||
"@types/node": "^22.13.17",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
||||
"@typescript-eslint/parser": "^8.29.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
|
|
@ -2996,6 +2998,13 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
|
|
@ -9814,7 +9823,6 @@
|
|||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@remnawave/frontend",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"version": "1.5.4",
|
||||
"version": "1.5.5",
|
||||
"license": "AGPL-3.0-only",
|
||||
"author": "REMNAWAVE <github.com/remnawave>",
|
||||
"homepage": "https://github.com/remnawave",
|
||||
|
|
@ -80,6 +80,7 @@
|
|||
"react-imask": "^7.6.1",
|
||||
"react-router-dom": "6.27.0",
|
||||
"recharts": "^2.15.1",
|
||||
"semver": "^7.7.1",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"uqr": "^0.1.2",
|
||||
"vite-plugin-deadfile": "^1.4.0",
|
||||
|
|
@ -100,6 +101,7 @@
|
|||
"@types/node": "^22.13.17",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
||||
"@typescript-eslint/parser": "^8.29.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
import { AppShell, Badge, Burger, Code, Container, Group, ScrollArea, Text } from '@mantine/core'
|
||||
import {
|
||||
AppShell,
|
||||
Badge,
|
||||
Burger,
|
||||
Code,
|
||||
Container,
|
||||
Group,
|
||||
Indicator,
|
||||
ScrollArea,
|
||||
Text
|
||||
} from '@mantine/core'
|
||||
import { useClickOutside, useDisclosure, useMediaQuery } from '@mantine/hooks'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import semver from 'semver'
|
||||
import axios from 'axios'
|
||||
|
||||
import { getBuildInfo } from '@shared/utils/get-build-info/get-build-info.util'
|
||||
|
|
@ -21,6 +32,15 @@ export function MainLayout() {
|
|||
const [buildInfoModalOpened, setBuildInfoModalOpened] = useState(false)
|
||||
const [isMediaQueryReady, setIsMediaQueryReady] = useState(false)
|
||||
|
||||
const [versions, setVersions] = useState<{
|
||||
currentVersion: string
|
||||
latestVersion: string
|
||||
}>({
|
||||
currentVersion: '0.0.0',
|
||||
latestVersion: '0.0.0'
|
||||
})
|
||||
const [isNewVersionAvailable, setIsNewVersionAvailable] = useState(false)
|
||||
|
||||
const buildInfo = getBuildInfo()
|
||||
|
||||
const isMobile = useMediaQuery(`(max-width: 64rem)`, undefined, {
|
||||
|
|
@ -52,6 +72,31 @@ export function MainLayout() {
|
|||
}
|
||||
})
|
||||
|
||||
const { data: latestVersion } = useQuery({
|
||||
queryKey: ['github-latest-version'],
|
||||
staleTime: sToMs(3600),
|
||||
refetchInterval: sToMs(3600),
|
||||
queryFn: async () => {
|
||||
const response = await axios.get<{
|
||||
release: {
|
||||
tag: string
|
||||
}
|
||||
}>('https://ungh.cc/repos/remnawave/panel/releases/latest')
|
||||
return response.data.release.tag
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setVersions({
|
||||
currentVersion: buildInfo.tag ?? '0.0.0',
|
||||
latestVersion: latestVersion ?? '0.0.0'
|
||||
})
|
||||
}, [latestVersion, buildInfo.tag])
|
||||
|
||||
useEffect(() => {
|
||||
setIsNewVersionAvailable(semver.gt(versions.latestVersion, versions.currentVersion))
|
||||
}, [versions])
|
||||
|
||||
return isMediaQueryReady ? (
|
||||
<AppShell
|
||||
header={{ height: 64 }}
|
||||
|
|
@ -116,6 +161,12 @@ export function MainLayout() {
|
|||
</Text>
|
||||
</Group>
|
||||
{buildInfo.branch === 'dev' && (
|
||||
<Indicator
|
||||
color="cyan"
|
||||
disabled={!isNewVersionAvailable}
|
||||
processing
|
||||
size={11}
|
||||
>
|
||||
<Badge
|
||||
color="red"
|
||||
onClick={() => setBuildInfoModalOpened(true)}
|
||||
|
|
@ -126,9 +177,16 @@ export function MainLayout() {
|
|||
>
|
||||
dev
|
||||
</Badge>
|
||||
</Indicator>
|
||||
)}
|
||||
|
||||
{buildInfo.branch !== 'dev' && (
|
||||
<Indicator
|
||||
color="cyan"
|
||||
disabled={!isNewVersionAvailable}
|
||||
processing
|
||||
size={11}
|
||||
>
|
||||
<Code
|
||||
c="cyan"
|
||||
fw={700}
|
||||
|
|
@ -137,6 +195,7 @@ export function MainLayout() {
|
|||
>
|
||||
{`v${packageJson.version}`}
|
||||
</Code>
|
||||
</Indicator>
|
||||
)}
|
||||
|
||||
{isSocialButton && (
|
||||
|
|
@ -175,6 +234,7 @@ export function MainLayout() {
|
|||
|
||||
<BuildInfoModal
|
||||
buildInfo={buildInfo}
|
||||
isNewVersionAvailable={isNewVersionAvailable}
|
||||
onClose={() => setBuildInfoModalOpened(false)}
|
||||
opened={buildInfoModalOpened}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,20 +5,39 @@ import {
|
|||
TbCheck as IconCheck,
|
||||
TbCopy as IconCopy,
|
||||
TbGitBranch as IconGitBranch,
|
||||
TbHash as IconHash
|
||||
TbHash as IconHash,
|
||||
TbRipple
|
||||
} from 'react-icons/tb'
|
||||
import { Badge, Box, Button, Code, Group, Modal, Stack, Text, Title, Tooltip } from '@mantine/core'
|
||||
import {
|
||||
Alert,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Code,
|
||||
Group,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
Tooltip
|
||||
} from '@mantine/core'
|
||||
import { useClipboard } from '@mantine/hooks'
|
||||
|
||||
import { IBuildInfo } from '@shared/utils/get-build-info/interfaces/build-info.interface'
|
||||
|
||||
interface BuildInfoModalProps {
|
||||
buildInfo: IBuildInfo
|
||||
isNewVersionAvailable: boolean
|
||||
onClose: () => void
|
||||
opened: boolean
|
||||
}
|
||||
|
||||
export function BuildInfoModal({ opened, onClose, buildInfo }: BuildInfoModalProps) {
|
||||
export function BuildInfoModal({
|
||||
opened,
|
||||
onClose,
|
||||
buildInfo,
|
||||
isNewVersionAvailable
|
||||
}: BuildInfoModalProps) {
|
||||
const buildDate = new Date(buildInfo.buildTime).toLocaleString()
|
||||
const clipboard = useClipboard({ timeout: 1000 })
|
||||
|
||||
|
|
@ -54,6 +73,28 @@ export function BuildInfoModal({ opened, onClose, buildInfo }: BuildInfoModalPro
|
|||
withCloseButton
|
||||
>
|
||||
<Stack gap="md">
|
||||
{isNewVersionAvailable && (
|
||||
<Alert
|
||||
color="teal"
|
||||
icon={<TbRipple size={22} />}
|
||||
title="Update available"
|
||||
variant="outline"
|
||||
>
|
||||
<Text size="sm">A new version of Remnawave is available.</Text>
|
||||
<Button
|
||||
color="cyan"
|
||||
component="a"
|
||||
href={'https://github.com/remnawave/panel/releases/latest'}
|
||||
mt="xs"
|
||||
size="xs"
|
||||
target="_blank"
|
||||
variant="outline"
|
||||
>
|
||||
Check out
|
||||
</Button>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Group align="flex-start">
|
||||
<IconCalendar size={20} />
|
||||
<Box>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue