mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 05:51:56 +08:00
91 lines
3.3 KiB
TypeScript
91 lines
3.3 KiB
TypeScript
import dayjs from 'dayjs'
|
|
import { lowerCase } from 'lodash-es'
|
|
import { useLayoutEffect, useEffect, useRef, useState } from 'react'
|
|
|
|
import { Select, Card, Header } from '@components'
|
|
import { type Log } from '@models/Log'
|
|
import { useConfig, useGeneral, useI18n, useLogsStreamReader } from '@stores'
|
|
|
|
import './style.scss'
|
|
|
|
const logLevelOptions = [
|
|
{ label: 'Default', value: '' },
|
|
{ label: 'Debug', value: 'debug' },
|
|
{ label: 'Info', value: 'info' },
|
|
{ label: 'Warn', value: 'warning' },
|
|
{ label: 'Error', value: 'error' },
|
|
{ label: 'Silent', value: 'silent' },
|
|
]
|
|
const logMap = new Map([
|
|
['debug', 'text-teal-500'],
|
|
['info', 'text-sky-500'],
|
|
['warning', 'text-pink-500'],
|
|
['error', 'text-rose-500'],
|
|
])
|
|
|
|
export default function Logs () {
|
|
const listRef = useRef<HTMLUListElement>(null)
|
|
const logsRef = useRef<Log[]>([])
|
|
const [logs, setLogs] = useState<Log[]>([])
|
|
const { translation } = useI18n()
|
|
const { data: { logLevel }, set: setConfig } = useConfig()
|
|
const { general: { logLevel: configLevel } } = useGeneral()
|
|
const { t } = translation('Logs')
|
|
const logsStreamReader = useLogsStreamReader()
|
|
const scrollHeightRef = useRef(listRef.current?.scrollHeight ?? 0)
|
|
|
|
const isConfigSilent = lowerCase(configLevel) === 'silent'
|
|
|
|
useLayoutEffect(() => {
|
|
const ul = listRef.current
|
|
if (ul != null && scrollHeightRef.current === (ul.scrollTop + ul.clientHeight)) {
|
|
ul.scrollTop = ul.scrollHeight - ul.clientHeight
|
|
}
|
|
scrollHeightRef.current = ul?.scrollHeight ?? 0
|
|
})
|
|
|
|
useEffect(() => {
|
|
function handleLog (newLogs: Log[]) {
|
|
logsRef.current = logsRef.current.slice().concat(newLogs.map(d => ({ ...d, time: new Date() })))
|
|
setLogs(logsRef.current)
|
|
}
|
|
|
|
if (logsStreamReader != null) {
|
|
logsStreamReader.subscribe('data', handleLog)
|
|
logsRef.current = logsStreamReader.buffer()
|
|
setLogs(logsRef.current)
|
|
}
|
|
return () => logsStreamReader?.unsubscribe('data', handleLog)
|
|
}, [logsStreamReader])
|
|
|
|
return (
|
|
<div className="page">
|
|
<Header title={ t('title') } >
|
|
<span className="mr-2 text-sm text-primary-darken">{t('levelLabel')}:</span>
|
|
<Select
|
|
disabled={isConfigSilent}
|
|
options={logLevelOptions}
|
|
value={isConfigSilent ? 'silent' : logLevel}
|
|
onSelect={level => setConfig(c => { c.logLevel = level })}
|
|
/>
|
|
</Header>
|
|
|
|
<Card className="mt-2.5 flex flex-1 flex-col md:mt-4">
|
|
<ul className="logs-panel" ref={listRef}>
|
|
{
|
|
logs.map(
|
|
(log, index) => (
|
|
<li className="inline-block text-[11px] leading-5" key={index}>
|
|
<span className="mr-2 text-orange-400">[{ dayjs(log.time).format('YYYY-MM-DD HH:mm:ss') }]</span>
|
|
<span className={logMap.get(log.type)}>[{ log.type.toUpperCase() }]</span>
|
|
<span> { log.payload }</span>
|
|
</li>
|
|
),
|
|
)
|
|
}
|
|
</ul>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|