feat: chart history on browser

This commit is contained in:
hamster1963 2024-12-25 11:03:11 +08:00
parent 1a1ceb4665
commit 112607740a
5 changed files with 399 additions and 132 deletions

View File

@ -4,7 +4,7 @@ import { useWebSocketContext } from "@/hooks/use-websocket-context"
import { formatBytes } from "@/lib/format" import { formatBytes } from "@/lib/format"
import { cn, formatNezhaInfo, formatRelativeTime } from "@/lib/utils" import { cn, formatNezhaInfo, formatRelativeTime } from "@/lib/utils"
import { NezhaServer, NezhaWebsocketResponse } from "@/types/nezha-api" import { NezhaServer, NezhaWebsocketResponse } from "@/types/nezha-api"
import { useEffect, useState } from "react" import { useEffect, useRef, useState } from "react"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { Area, AreaChart, CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts" import { Area, AreaChart, CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"
@ -50,7 +50,7 @@ type connectChartData = {
} }
export default function ServerDetailChart({ server_id }: { server_id: string }) { export default function ServerDetailChart({ server_id }: { server_id: string }) {
const { lastMessage, connected } = useWebSocketContext() const { lastMessage, connected, messageHistory } = useWebSocketContext()
if (!connected && !lastMessage) { if (!connected && !lastMessage) {
return <ServerDetailChartLoading /> return <ServerDetailChartLoading />
@ -73,48 +73,108 @@ export default function ServerDetailChart({ server_id }: { server_id: string })
return ( return (
<section className="grid md:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-3 server-charts"> <section className="grid md:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-3 server-charts">
<CpuChart now={nezhaWsData.now} data={server} /> <CpuChart now={nezhaWsData.now} data={server} messageHistory={messageHistory} />
{gpuStats.length >= 1 && gpuList.length === gpuStats.length ? ( {gpuStats.length >= 1 && gpuList.length === gpuStats.length ? (
gpuList.map((gpu, index) => <GpuChart now={nezhaWsData.now} gpuStat={gpuStats[index]} gpuName={gpu} key={index} />) gpuList.map((gpu, index) => (
<GpuChart
index={index}
id={server.id}
now={nezhaWsData.now}
gpuStat={gpuStats[index]}
gpuName={gpu}
messageHistory={messageHistory}
key={index}
/>
))
) : gpuStats.length > 0 ? ( ) : gpuStats.length > 0 ? (
gpuStats.map((gpu, index) => <GpuChart now={nezhaWsData.now} gpuStat={gpu} gpuName={`#${index + 1}`} key={index} />) gpuStats.map((gpu, index) => (
<GpuChart
index={index}
id={server.id}
now={nezhaWsData.now}
gpuStat={gpu}
gpuName={`#${index + 1}`}
messageHistory={messageHistory}
key={index}
/>
))
) : ( ) : (
<></> <></>
)} )}
<ProcessChart now={nezhaWsData.now} data={server} /> <ProcessChart now={nezhaWsData.now} data={server} messageHistory={messageHistory} />
<DiskChart now={nezhaWsData.now} data={server} /> <DiskChart now={nezhaWsData.now} data={server} messageHistory={messageHistory} />
<MemChart now={nezhaWsData.now} data={server} /> <MemChart now={nezhaWsData.now} data={server} messageHistory={messageHistory} />
<NetworkChart now={nezhaWsData.now} data={server} /> <NetworkChart now={nezhaWsData.now} data={server} messageHistory={messageHistory} />
<ConnectChart now={nezhaWsData.now} data={server} /> <ConnectChart now={nezhaWsData.now} data={server} messageHistory={messageHistory} />
</section> </section>
) )
} }
function GpuChart({ now, gpuStat, gpuName }: { now: number; gpuStat: number; gpuName?: string }) { function GpuChart({
const [gpuChartData, setGpuChartData] = useState([] as gpuChartData[]) id,
index,
gpuStat,
gpuName,
messageHistory,
}: {
now: number
id: number
index: number
gpuStat: number
gpuName?: string
messageHistory: { data: string }[]
}) {
const [gpuChartData, setGpuChartData] = useState<gpuChartData[]>([])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
const customBackgroundImage = const customBackgroundImage =
// @ts-expect-error CustomBackgroundImage is a global variable // @ts-expect-error CustomBackgroundImage is a global variable
(window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined
// 初始化历史数据
useEffect(() => { useEffect(() => {
if (gpuStat) { if (!hasInitialized.current && messageHistory.length > 0) {
const timestamp = Date.now().toString() const historyData = messageHistory
let newData = [] as gpuChartData[] .map((msg) => {
if (gpuChartData.length === 0) { const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
newData = [ const server = wsData.servers.find((s) => s.id === id)
{ timeStamp: timestamp, gpu: gpuStat }, if (!server) return null
{ timeStamp: timestamp, gpu: gpuStat }, const { gpu } = formatNezhaInfo(wsData.now, server)
] return {
} else { timeStamp: wsData.now.toString(),
newData = [...gpuChartData, { timeStamp: timestamp, gpu: gpuStat }] gpu: gpu[index],
} }
if (newData.length > 30) { })
newData.shift() .filter((item): item is gpuChartData => item !== null)
} .reverse()
setGpuChartData(newData)
setGpuChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
} }
}, [now, gpuStat]) }, [messageHistory])
useEffect(() => {
if (gpuStat && historyLoaded) {
const timestamp = Date.now().toString()
setGpuChartData((prevData) => {
let newData = [] as gpuChartData[]
if (prevData.length === 0) {
newData = [
{ timeStamp: timestamp, gpu: gpuStat },
{ timeStamp: timestamp, gpu: gpuStat },
]
} else {
newData = [...prevData, { timeStamp: timestamp, gpu: gpuStat }]
if (newData.length > 30) {
newData.shift()
}
}
return newData
})
}
}, [gpuStat, historyLoaded])
const chartConfig = { const chartConfig = {
gpu: { gpu: {
@ -170,8 +230,10 @@ function GpuChart({ now, gpuStat, gpuName }: { now: number; gpuStat: number; gpu
) )
} }
function CpuChart({ now, data }: { now: number; data: NezhaServer }) { function CpuChart({ now, data, messageHistory }: { now: number; data: NezhaServer; messageHistory: { data: string }[] }) {
const [cpuChartData, setCpuChartData] = useState([] as cpuChartData[]) const [cpuChartData, setCpuChartData] = useState<cpuChartData[]>([])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
const { cpu } = formatNezhaInfo(now, data) const { cpu } = formatNezhaInfo(now, data)
@ -179,24 +241,50 @@ function CpuChart({ now, data }: { now: number; data: NezhaServer }) {
// @ts-expect-error CustomBackgroundImage is a global variable // @ts-expect-error CustomBackgroundImage is a global variable
(window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined
// 初始化历史数据
useEffect(() => { useEffect(() => {
if (data) { if (!hasInitialized.current && messageHistory.length > 0) {
const timestamp = Date.now().toString() const historyData = messageHistory
let newData = [] as cpuChartData[] .map((msg) => {
if (cpuChartData.length === 0) { const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
newData = [ const server = wsData.servers.find((s) => s.id === data.id)
{ timeStamp: timestamp, cpu: cpu }, if (!server) return null
{ timeStamp: timestamp, cpu: cpu }, const { cpu } = formatNezhaInfo(wsData.now, server)
] return {
} else { timeStamp: wsData.now.toString(),
newData = [...cpuChartData, { timeStamp: timestamp, cpu: cpu }] cpu: cpu,
} }
if (newData.length > 30) { })
newData.shift() .filter((item): item is cpuChartData => item !== null)
} .reverse() // 保持时间顺序
setCpuChartData(newData)
setCpuChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
} }
}, [data]) }, [messageHistory])
// 更新实时数据
useEffect(() => {
if (data && historyLoaded) {
const timestamp = Date.now().toString()
setCpuChartData((prevData) => {
let newData = [] as cpuChartData[]
if (prevData.length === 0) {
newData = [
{ timeStamp: timestamp, cpu: cpu },
{ timeStamp: timestamp, cpu: cpu },
]
} else {
newData = [...prevData, { timeStamp: timestamp, cpu: cpu }]
if (newData.length > 30) {
newData.shift()
}
}
return newData
})
}
}, [data, historyLoaded])
const chartConfig = { const chartConfig = {
cpu: { cpu: {
@ -249,9 +337,11 @@ function CpuChart({ now, data }: { now: number; data: NezhaServer }) {
) )
} }
function ProcessChart({ now, data }: { now: number; data: NezhaServer }) { function ProcessChart({ now, data, messageHistory }: { now: number; data: NezhaServer; messageHistory: { data: string }[] }) {
const { t } = useTranslation() const { t } = useTranslation()
const [processChartData, setProcessChartData] = useState([] as processChartData[]) const [processChartData, setProcessChartData] = useState([] as processChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
const customBackgroundImage = const customBackgroundImage =
// @ts-expect-error CustomBackgroundImage is a global variable // @ts-expect-error CustomBackgroundImage is a global variable
@ -259,24 +349,50 @@ function ProcessChart({ now, data }: { now: number; data: NezhaServer }) {
const { process } = formatNezhaInfo(now, data) const { process } = formatNezhaInfo(now, data)
// 初始化历史数据
useEffect(() => { useEffect(() => {
if (data) { if (!hasInitialized.current && messageHistory.length > 0) {
const timestamp = Date.now().toString() const historyData = messageHistory
let newData = [] as processChartData[] .map((msg) => {
if (processChartData.length === 0) { const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
newData = [ const server = wsData.servers.find((s) => s.id === data.id)
{ timeStamp: timestamp, process: process }, if (!server) return null
{ timeStamp: timestamp, process: process }, const { process } = formatNezhaInfo(wsData.now, server)
] return {
} else { timeStamp: wsData.now.toString(),
newData = [...processChartData, { timeStamp: timestamp, process: process }] process,
} }
if (newData.length > 30) { })
newData.shift() .filter((item): item is processChartData => item !== null)
} .reverse()
setProcessChartData(newData)
setProcessChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
} }
}, [data]) }, [messageHistory])
// 修改实时数据更新逻辑
useEffect(() => {
if (data && historyLoaded) {
const timestamp = Date.now().toString()
setProcessChartData((prevData) => {
let newData = [] as processChartData[]
if (prevData.length === 0) {
newData = [
{ timeStamp: timestamp, process },
{ timeStamp: timestamp, process },
]
} else {
newData = [...prevData, { timeStamp: timestamp, process }]
if (newData.length > 30) {
newData.shift()
}
}
return newData
})
}
}, [data, historyLoaded])
const chartConfig = { const chartConfig = {
process: { process: {
@ -335,9 +451,11 @@ function ProcessChart({ now, data }: { now: number; data: NezhaServer }) {
) )
} }
function MemChart({ now, data }: { now: number; data: NezhaServer }) { function MemChart({ now, data, messageHistory }: { now: number; data: NezhaServer; messageHistory: { data: string }[] }) {
const { t } = useTranslation() const { t } = useTranslation()
const [memChartData, setMemChartData] = useState([] as memChartData[]) const [memChartData, setMemChartData] = useState([] as memChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
const customBackgroundImage = const customBackgroundImage =
// @ts-expect-error CustomBackgroundImage is a global variable // @ts-expect-error CustomBackgroundImage is a global variable
@ -345,24 +463,51 @@ function MemChart({ now, data }: { now: number; data: NezhaServer }) {
const { mem, swap } = formatNezhaInfo(now, data) const { mem, swap } = formatNezhaInfo(now, data)
// 初始化历史数据
useEffect(() => { useEffect(() => {
if (data) { if (!hasInitialized.current && messageHistory.length > 0) {
const timestamp = Date.now().toString() const historyData = messageHistory
let newData = [] as memChartData[] .map((msg) => {
if (memChartData.length === 0) { const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
newData = [ const server = wsData.servers.find((s) => s.id === data.id)
{ timeStamp: timestamp, mem: mem, swap: swap }, if (!server) return null
{ timeStamp: timestamp, mem: mem, swap: swap }, const { mem, swap } = formatNezhaInfo(wsData.now, server)
] return {
} else { timeStamp: wsData.now.toString(),
newData = [...memChartData, { timeStamp: timestamp, mem: mem, swap: swap }] mem,
} swap,
if (newData.length > 30) { }
newData.shift() })
} .filter((item): item is memChartData => item !== null)
setMemChartData(newData) .reverse()
setMemChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
} }
}, [data]) }, [messageHistory])
// 修改实时数据更新逻辑
useEffect(() => {
if (data && historyLoaded) {
const timestamp = Date.now().toString()
setMemChartData((prevData) => {
let newData = [] as memChartData[]
if (prevData.length === 0) {
newData = [
{ timeStamp: timestamp, mem, swap },
{ timeStamp: timestamp, mem, swap },
]
} else {
newData = [...prevData, { timeStamp: timestamp, mem, swap }]
if (newData.length > 30) {
newData.shift()
}
}
return newData
})
}
}, [data, historyLoaded])
const chartConfig = { const chartConfig = {
mem: { mem: {
@ -451,9 +596,11 @@ function MemChart({ now, data }: { now: number; data: NezhaServer }) {
) )
} }
function DiskChart({ now, data }: { now: number; data: NezhaServer }) { function DiskChart({ now, data, messageHistory }: { now: number; data: NezhaServer; messageHistory: { data: string }[] }) {
const { t } = useTranslation() const { t } = useTranslation()
const [diskChartData, setDiskChartData] = useState([] as diskChartData[]) const [diskChartData, setDiskChartData] = useState([] as diskChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
const customBackgroundImage = const customBackgroundImage =
// @ts-expect-error CustomBackgroundImage is a global variable // @ts-expect-error CustomBackgroundImage is a global variable
@ -461,24 +608,50 @@ function DiskChart({ now, data }: { now: number; data: NezhaServer }) {
const { disk } = formatNezhaInfo(now, data) const { disk } = formatNezhaInfo(now, data)
// 初始化历史数据
useEffect(() => { useEffect(() => {
if (data) { if (!hasInitialized.current && messageHistory.length > 0) {
const timestamp = Date.now().toString() const historyData = messageHistory
let newData = [] as diskChartData[] .map((msg) => {
if (diskChartData.length === 0) { const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
newData = [ const server = wsData.servers.find((s) => s.id === data.id)
{ timeStamp: timestamp, disk: disk }, if (!server) return null
{ timeStamp: timestamp, disk: disk }, const { disk } = formatNezhaInfo(wsData.now, server)
] return {
} else { timeStamp: wsData.now.toString(),
newData = [...diskChartData, { timeStamp: timestamp, disk: disk }] disk,
} }
if (newData.length > 30) { })
newData.shift() .filter((item): item is diskChartData => item !== null)
} .reverse()
setDiskChartData(newData)
setDiskChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
} }
}, [data]) }, [messageHistory])
// 修改实时数据更新逻辑
useEffect(() => {
if (data && historyLoaded) {
const timestamp = Date.now().toString()
setDiskChartData((prevData) => {
let newData = [] as diskChartData[]
if (prevData.length === 0) {
newData = [
{ timeStamp: timestamp, disk },
{ timeStamp: timestamp, disk },
]
} else {
newData = [...prevData, { timeStamp: timestamp, disk }]
if (newData.length > 30) {
newData.shift()
}
}
return newData
})
}
}, [data, historyLoaded])
const chartConfig = { const chartConfig = {
disk: { disk: {
@ -536,9 +709,11 @@ function DiskChart({ now, data }: { now: number; data: NezhaServer }) {
) )
} }
function NetworkChart({ now, data }: { now: number; data: NezhaServer }) { function NetworkChart({ now, data, messageHistory }: { now: number; data: NezhaServer; messageHistory: { data: string }[] }) {
const { t } = useTranslation() const { t } = useTranslation()
const [networkChartData, setNetworkChartData] = useState([] as networkChartData[]) const [networkChartData, setNetworkChartData] = useState([] as networkChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
const customBackgroundImage = const customBackgroundImage =
// @ts-expect-error CustomBackgroundImage is a global variable // @ts-expect-error CustomBackgroundImage is a global variable
@ -546,24 +721,51 @@ function NetworkChart({ now, data }: { now: number; data: NezhaServer }) {
const { up, down } = formatNezhaInfo(now, data) const { up, down } = formatNezhaInfo(now, data)
// 初始化历史数据
useEffect(() => { useEffect(() => {
if (data) { if (!hasInitialized.current && messageHistory.length > 0) {
const timestamp = Date.now().toString() const historyData = messageHistory
let newData = [] as networkChartData[] .map((msg) => {
if (networkChartData.length === 0) { const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
newData = [ const server = wsData.servers.find((s) => s.id === data.id)
{ timeStamp: timestamp, upload: up, download: down }, if (!server) return null
{ timeStamp: timestamp, upload: up, download: down }, const { up, down } = formatNezhaInfo(wsData.now, server)
] return {
} else { timeStamp: wsData.now.toString(),
newData = [...networkChartData, { timeStamp: timestamp, upload: up, download: down }] upload: up,
} download: down,
if (newData.length > 30) { }
newData.shift() })
} .filter((item): item is networkChartData => item !== null)
setNetworkChartData(newData) .reverse()
setNetworkChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
} }
}, [data]) }, [messageHistory])
// 修改实时数据更新逻辑
useEffect(() => {
if (data && historyLoaded) {
const timestamp = Date.now().toString()
setNetworkChartData((prevData) => {
let newData = [] as networkChartData[]
if (prevData.length === 0) {
newData = [
{ timeStamp: timestamp, upload: up, download: down },
{ timeStamp: timestamp, upload: up, download: down },
]
} else {
newData = [...prevData, { timeStamp: timestamp, upload: up, download: down }]
if (newData.length > 30) {
newData.shift()
}
}
return newData
})
}
}, [data, historyLoaded])
let maxDownload = Math.max(...networkChartData.map((item) => item.download)) let maxDownload = Math.max(...networkChartData.map((item) => item.download))
maxDownload = Math.ceil(maxDownload) maxDownload = Math.ceil(maxDownload)
@ -651,8 +853,10 @@ function NetworkChart({ now, data }: { now: number; data: NezhaServer }) {
) )
} }
function ConnectChart({ now, data }: { now: number; data: NezhaServer }) { function ConnectChart({ now, data, messageHistory }: { now: number; data: NezhaServer; messageHistory: { data: string }[] }) {
const [connectChartData, setConnectChartData] = useState([] as connectChartData[]) const [connectChartData, setConnectChartData] = useState([] as connectChartData[])
const hasInitialized = useRef(false)
const [historyLoaded, setHistoryLoaded] = useState(false)
const customBackgroundImage = const customBackgroundImage =
// @ts-expect-error CustomBackgroundImage is a global variable // @ts-expect-error CustomBackgroundImage is a global variable
@ -660,24 +864,51 @@ function ConnectChart({ now, data }: { now: number; data: NezhaServer }) {
const { tcp, udp } = formatNezhaInfo(now, data) const { tcp, udp } = formatNezhaInfo(now, data)
// 初始化历史数据
useEffect(() => { useEffect(() => {
if (data) { if (!hasInitialized.current && messageHistory.length > 0) {
const timestamp = Date.now().toString() const historyData = messageHistory
let newData = [] as connectChartData[] .map((msg) => {
if (connectChartData.length === 0) { const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
newData = [ const server = wsData.servers.find((s) => s.id === data.id)
{ timeStamp: timestamp, tcp: tcp, udp: udp }, if (!server) return null
{ timeStamp: timestamp, tcp: tcp, udp: udp }, const { tcp, udp } = formatNezhaInfo(wsData.now, server)
] return {
} else { timeStamp: wsData.now.toString(),
newData = [...connectChartData, { timeStamp: timestamp, tcp: tcp, udp: udp }] tcp,
} udp,
if (newData.length > 30) { }
newData.shift() })
} .filter((item): item is connectChartData => item !== null)
setConnectChartData(newData) .reverse()
setConnectChartData(historyData)
hasInitialized.current = true
setHistoryLoaded(true)
} }
}, [data]) }, [messageHistory])
// 修改实时数据更新逻辑
useEffect(() => {
if (data && historyLoaded) {
const timestamp = Date.now().toString()
setConnectChartData((prevData) => {
let newData = [] as connectChartData[]
if (prevData.length === 0) {
newData = [
{ timeStamp: timestamp, tcp, udp },
{ timeStamp: timestamp, tcp, udp },
]
} else {
newData = [...prevData, { timeStamp: timestamp, tcp, udp }]
if (newData.length > 30) {
newData.shift()
}
}
return newData
})
}
}, [data, historyLoaded])
const chartConfig = { const chartConfig = {
tcp: { tcp: {

View File

@ -3,9 +3,11 @@ import { createContext } from "react"
export interface WebSocketContextType { export interface WebSocketContextType {
lastMessage: { data: string } | null lastMessage: { data: string } | null
connected: boolean connected: boolean
messageHistory: { data: string }[]
} }
export const WebSocketContext = createContext<WebSocketContextType>({ export const WebSocketContext = createContext<WebSocketContextType>({
lastMessage: null, lastMessage: null,
connected: false, connected: false,
messageHistory: [],
}) })

View File

View File

@ -9,6 +9,7 @@ interface WebSocketProviderProps {
export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, children }) => { export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, children }) => {
const [lastMessage, setLastMessage] = useState<{ data: string } | null>(null) const [lastMessage, setLastMessage] = useState<{ data: string } | null>(null)
const [messageHistory, setMessageHistory] = useState<{ data: string }[]>([]) // 新增历史消息状态
const [connected, setConnected] = useState(false) const [connected, setConnected] = useState(false)
const ws = useRef<WebSocket | null>(null) const ws = useRef<WebSocket | null>(null)
const reconnectTimeout = useRef<NodeJS.Timeout>(null) const reconnectTimeout = useRef<NodeJS.Timeout>(null)
@ -42,7 +43,13 @@ export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, child
} }
ws.current.onmessage = (event) => { ws.current.onmessage = (event) => {
setLastMessage({ data: event.data }) const newMessage = { data: event.data }
setLastMessage(newMessage)
// 更新历史消息保持最新的30条记录
setMessageHistory((prev) => {
const updated = [newMessage, ...prev]
return updated.slice(0, 30)
})
} }
ws.current.onerror = (error) => { ws.current.onerror = (error) => {
@ -69,6 +76,7 @@ export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, child
const contextValue: WebSocketContextType = { const contextValue: WebSocketContextType = {
lastMessage, lastMessage,
connected, connected,
messageHistory, // 添加到 context value
} }
return <WebSocketContext.Provider value={contextValue}>{children}</WebSocketContext.Provider> return <WebSocketContext.Provider value={contextValue}>{children}</WebSocketContext.Provider>

View File

@ -0,0 +1,26 @@
import { NezhaWebsocketResponse } from "@/types/nezha-api"
import { useEffect, useState } from "react"
export function useChartHistory<T>(
messageHistory: { data: string }[],
serverId: number,
formatFn: (wsData: NezhaWebsocketResponse, serverId: number) => T | null,
) {
const [data, setData] = useState<T[]>([])
useEffect(() => {
if (messageHistory.length > 0 && data.length === 0) {
const historyData = messageHistory
.map((msg) => {
const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse
return formatFn(wsData, serverId)
})
.filter((item): item is T => item !== null)
.reverse()
setData(historyData)
}
}, [messageHistory])
return data
}