diff --git a/frontend/src/components/ui/AboutDialog.tsx b/frontend/src/components/ui/AboutDialog.tsx index 4d74073..9a63f4a 100644 --- a/frontend/src/components/ui/AboutDialog.tsx +++ b/frontend/src/components/ui/AboutDialog.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { X } from 'lucide-react'; +import { X, RefreshCw } from 'lucide-react'; +import { checkLatestVersion, compareVersions } from '@/utils/version'; interface AboutDialogProps { isOpen: boolean; @@ -12,30 +13,28 @@ const AboutDialog: React.FC = ({ isOpen, onClose, version }) = const { t } = useTranslation(); const [hasNewVersion, setHasNewVersion] = useState(false); const [latestVersion, setLatestVersion] = useState(""); + const [isChecking, setIsChecking] = useState(false); + + const checkForUpdates = async () => { + setIsChecking(true); + try { + const latest = await checkLatestVersion(); + if (latest) { + setLatestVersion(latest); + setHasNewVersion(compareVersions(version, latest) > 0); + } + } catch (error) { + console.error('Failed to check for updates:', error); + } finally { + setIsChecking(false); + } + }; - // Check if there's a new version available - // In a real application, this would make an API call to check for updates useEffect(() => { if (isOpen) { - // Mock implementation - in real implementation, you would fetch from an API - const checkForUpdates = async () => { - try { - // This is a placeholder - in a real app you would call an API - // const response = await fetch('/api/check-updates'); - // const data = await response.json(); - - // For demo purposes, we'll just pretend there's a new version - // TODO: Replace this placeholder logic with a real API call to check for updates - setHasNewVersion(false); - setLatestVersion("0.0.28"); // Just a higher version for demo - } catch (error) { - console.error('Failed to check for updates:', error); - } - }; - checkForUpdates(); } - }, [isOpen]); + }, [isOpen, version]); if (!isOpen) return null; @@ -66,7 +65,7 @@ const AboutDialog: React.FC = ({ isOpen, onClose, version }) = - {hasNewVersion && ( + {hasNewVersion && latestVersion && (
@@ -74,14 +73,35 @@ const AboutDialog: React.FC = ({ isOpen, onClose, version }) =
-
-

- {t('about.newVersion')} (v{latestVersion}) +

+

{t('about.newVersionAvailable', { version: latestVersion })}

+

+ + {t('about.viewOnGitHub')} +

)} + +
diff --git a/frontend/src/components/ui/UserProfileMenu.tsx b/frontend/src/components/ui/UserProfileMenu.tsx index 275f2b4..c4ed3e6 100644 --- a/frontend/src/components/ui/UserProfileMenu.tsx +++ b/frontend/src/components/ui/UserProfileMenu.tsx @@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom'; import { useAuth } from '@/contexts/AuthContext'; import { User, Settings, LogOut, Info } from 'lucide-react'; import AboutDialog from './AboutDialog'; +import { checkLatestVersion, compareVersions } from '@/utils/version'; interface UserProfileMenuProps { collapsed: boolean; @@ -19,17 +20,21 @@ const UserProfileMenu: React.FC = ({ collapsed, version }) const [showAboutDialog, setShowAboutDialog] = useState(false); const menuRef = useRef(null); - // Check for new version - this could be replaced with a real API call + // Check for new version on login and component mount useEffect(() => { - // Mock implementation - in a real app, you would check against an API const checkForNewVersion = async () => { - // For demo purposes, we're just showing the notification - // In real implementation, you would compare current version with latest version - setShowNewVersionInfo(false); + try { + const latestVersion = await checkLatestVersion(); + if (latestVersion) { + setShowNewVersionInfo(compareVersions(version, latestVersion) > 0); + } + } catch (error) { + console.error('Error checking for new version:', error); + } }; checkForNewVersion(); - }, []); + }, [version]); // Close the menu when clicking outside useEffect(() => { diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 443aacf..c6818eb 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -16,7 +16,11 @@ "title": "About", "versionInfo": "MCP Hub Version: {{version}}", "newVersion": "New version available!", - "currentVersion": "Current version" + "currentVersion": "Current version", + "newVersionAvailable": "New version {{version}} is available", + "viewOnGitHub": "View on GitHub", + "checkForUpdates": "Check for Updates", + "checking": "Checking for updates..." }, "profile": { "viewProfile": "View profile", diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index ce7e081..e6b3f4a 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -16,7 +16,11 @@ "title": "关于", "versionInfo": "MCP Hub 版本: {{version}}", "newVersion": "有新版本可用!", - "currentVersion": "当前版本" + "currentVersion": "当前版本", + "newVersionAvailable": "新版本 {{version}} 已发布", + "viewOnGitHub": "在 GitHub 上查看", + "checkForUpdates": "检查更新", + "checking": "检查更新中..." }, "profile": { "viewProfile": "查看个人中心", diff --git a/frontend/src/utils/version.ts b/frontend/src/utils/version.ts new file mode 100644 index 0000000..9cbb818 --- /dev/null +++ b/frontend/src/utils/version.ts @@ -0,0 +1,31 @@ +const NPM_REGISTRY = 'https://registry.npmjs.org'; +const PACKAGE_NAME = '@samanhappy/mcphub'; + +export const checkLatestVersion = async (): Promise => { + try { + const response = await fetch(`${NPM_REGISTRY}/${PACKAGE_NAME}/latest`); + if (!response.ok) { + throw new Error(`Failed to fetch latest version: ${response.status}`); + } + const data = await response.json(); + return data.version || null; + } catch (error) { + console.error('Error checking for latest version:', error); + return null; + } +}; + +export const compareVersions = (current: string, latest: string): number => { + const currentParts = current.split('.').map(Number); + const latestParts = latest.split('.').map(Number); + + for (let i = 0; i < 3; i++) { + const currentPart = currentParts[i] || 0; + const latestPart = latestParts[i] || 0; + + if (currentPart > latestPart) return -1; + if (currentPart < latestPart) return 1; + } + + return 0; +};