Skip to content

Commit 2aee427

Browse files
committed
feat(nodes): show update indicators in list view
1 parent 740ddcc commit 2aee427

1 file changed

Lines changed: 89 additions & 16 deletions

File tree

dashboard/src/features/nodes/components/use-node-list-columns.tsx

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import NodeActionsMenu from '@/features/nodes/components/node-actions-menu'
66
import { CoresSimpleResponse, NodeResponse, NodeStatus } from '@/service/api'
77
import { cn } from '@/lib/utils'
88
import { Package, Server } from 'lucide-react'
9+
import { useXrayReleases } from '@/hooks/use-xray-releases'
10+
import { useNodeReleases } from '@/hooks/use-node-releases'
11+
import { Separator } from '@/components/ui/separator'
12+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
913

1014
interface UseNodeListColumnsProps {
1115
onEdit: (node: NodeResponse) => void
@@ -35,6 +39,8 @@ const getNodeStatusDotColor = (status: NodeStatus) => {
3539

3640
export const useNodeListColumns = ({ onEdit, onToggleStatus, coresData, canUpdate = true, canDelete = true, canReconnect = true, canUpdateCore = true, canReadStats = true }: UseNodeListColumnsProps) => {
3741
const { t } = useTranslation()
42+
const { latestVersion: latestXrayVersion, hasUpdate: hasXrayUpdate } = useXrayReleases()
43+
const { latestVersion: latestNodeVersion, hasUpdate: hasNodeUpdate } = useNodeReleases()
3844

3945
return useMemo<ListColumn<NodeResponse>[]>(
4046
() => [
@@ -65,22 +71,89 @@ export const useNodeListColumns = ({ onEdit, onToggleStatus, coresData, canUpdat
6571
header: t('version.title', { defaultValue: 'Version' }),
6672
width: '2fr',
6773
cell: node => {
68-
if (!node.xray_version && !node.node_version) return null
74+
const coreVersion = node.core_version ?? node.xray_version
75+
const resolvedCoreType = coresData?.cores?.find(c => c.id === node.core_config_id)?.type ?? null
76+
const isWireGuardCore = resolvedCoreType === 'wg'
77+
const isXrayBackend = resolvedCoreType !== 'wg'
78+
const coreUpdateVersion = node.xray_version ?? coreVersion
79+
const hasCoreUpdate = !!(isXrayBackend && coreUpdateVersion && latestXrayVersion && hasXrayUpdate(coreUpdateVersion))
80+
const hasNodeVersionUpdate = !isWireGuardCore && !!latestNodeVersion && !!node.node_version && hasNodeUpdate(node.node_version)
81+
82+
if (!coreVersion && !node.node_version) return null
83+
6984
return (
70-
<div className="text-muted-foreground flex flex-col gap-1 text-xs">
71-
{node.xray_version && (
72-
<div className="flex items-center gap-1.5">
73-
<Server className="h-3.5 w-3.5 shrink-0" />
74-
<span className="truncate">{node.xray_version}</span>
75-
</div>
76-
)}
77-
{node.node_version && (
78-
<div className="flex items-center gap-1.5">
79-
<Package className="h-3.5 w-3.5 shrink-0" />
80-
<span className="truncate">{node.node_version}</span>
81-
</div>
82-
)}
83-
</div>
85+
<TooltipProvider>
86+
<div className="flex flex-col gap-1 text-xs">
87+
{coreVersion && (
88+
<Tooltip>
89+
<TooltipTrigger asChild>
90+
<div className="inline-flex min-w-0 items-center gap-1.5">
91+
<Package className={cn('h-3.5 w-3.5 shrink-0 transition-colors', hasCoreUpdate ? 'text-amber-600 dark:text-amber-400' : 'text-muted-foreground')} />
92+
<span className={cn('truncate font-mono font-medium', hasCoreUpdate ? 'text-amber-700 dark:text-amber-300' : 'text-muted-foreground')}>{coreVersion}</span>
93+
{hasCoreUpdate && <span className="h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500" />}
94+
</div>
95+
</TooltipTrigger>
96+
<TooltipContent side="top" className="max-w-xs">
97+
<div className="space-y-2 text-xs">
98+
<div className="font-semibold">{t('node.xrayVersion', { defaultValue: 'Core' })}</div>
99+
<div className="space-y-1.5">
100+
<div className="flex items-center justify-between gap-4">
101+
<span>{t('version.currentVersion', { defaultValue: 'Current' })}</span>
102+
<span className="font-mono font-medium">{coreVersion}</span>
103+
</div>
104+
{isXrayBackend && latestXrayVersion && (
105+
<div className="flex items-center justify-between gap-4">
106+
<span>{t('version.latestVersion', { defaultValue: 'Latest' })}</span>
107+
<span className="font-mono font-medium">{latestXrayVersion}</span>
108+
</div>
109+
)}
110+
{hasCoreUpdate && (
111+
<>
112+
<Separator className="my-1.5" />
113+
<span>{t('nodeModal.updateAvailable', { defaultValue: 'Update available' })}</span>
114+
</>
115+
)}
116+
</div>
117+
</div>
118+
</TooltipContent>
119+
</Tooltip>
120+
)}
121+
{node.node_version && (
122+
<Tooltip>
123+
<TooltipTrigger asChild>
124+
<div className="inline-flex min-w-0 items-center gap-1.5">
125+
<Server className={cn('h-3.5 w-3.5 shrink-0 transition-colors', hasNodeVersionUpdate ? 'text-amber-600 dark:text-amber-400' : 'text-muted-foreground')} />
126+
<span className={cn('truncate font-mono font-medium', hasNodeVersionUpdate ? 'text-amber-700 dark:text-amber-300' : 'text-muted-foreground')}>{node.node_version}</span>
127+
{hasNodeVersionUpdate && <span className="h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500" />}
128+
</div>
129+
</TooltipTrigger>
130+
<TooltipContent side="top" className="max-w-xs">
131+
<div className="space-y-2 text-xs">
132+
<div className="font-semibold">{t('node.coreVersion', { defaultValue: 'Node Core' })}</div>
133+
<div className="space-y-1.5">
134+
<div className="flex items-center justify-between gap-4">
135+
<span>{t('version.currentVersion', { defaultValue: 'Current' })}</span>
136+
<span className="font-mono font-medium">{node.node_version}</span>
137+
</div>
138+
{!isWireGuardCore && latestNodeVersion && (
139+
<div className="flex items-center justify-between gap-4">
140+
<span>{t('version.latestVersion', { defaultValue: 'Latest' })}</span>
141+
<span className="font-mono font-medium">{latestNodeVersion}</span>
142+
</div>
143+
)}
144+
{hasNodeVersionUpdate && (
145+
<>
146+
<Separator className="my-1.5" />
147+
<span>{t('nodeModal.updateAvailable', { defaultValue: 'Update available' })}</span>
148+
</>
149+
)}
150+
</div>
151+
</div>
152+
</TooltipContent>
153+
</Tooltip>
154+
)}
155+
</div>
156+
</TooltipProvider>
84157
)
85158
},
86159
hideOnMobile: true,
@@ -118,6 +191,6 @@ export const useNodeListColumns = ({ onEdit, onToggleStatus, coresData, canUpdat
118191
]
119192
: []),
120193
],
121-
[t, onEdit, onToggleStatus, coresData, canUpdate, canDelete, canReconnect, canUpdateCore, canReadStats],
194+
[t, onEdit, onToggleStatus, coresData, canUpdate, canDelete, canReconnect, canUpdateCore, canReadStats, latestXrayVersion, hasXrayUpdate, latestNodeVersion, hasNodeUpdate],
122195
)
123196
}

0 commit comments

Comments
 (0)