fix(logs): preserve log data and eliminate blank flash on page navigation

Preserve SWR cache in onConnected to avoid replacing accumulated logs
with kernel buffer on reconnect. Add KeepAlive for the logs page so
its DOM stays mounted across route changes, removing the visible blank
window when navigating back.
This commit is contained in:
Tunglies 2026-04-03 13:13:57 +08:00
parent 51578c03b0
commit 36624aff49
No known key found for this signature in database
GPG Key ID: B9B01B389469B3E8
3 changed files with 27 additions and 4 deletions

View File

@ -101,7 +101,12 @@ export const useLogData = () => {
async onConnected() { async onConnected() {
const logs = await getClashLogs() const logs = await getClashLogs()
if (isMounted()) { if (isMounted()) {
next(null, clampLogs(filterLogsByLevel(logs, allowedTypes))) next(null, (current) => {
if (!current || current.length === 0) {
return clampLogs(filterLogsByLevel(logs, allowedTypes))
}
return current
})
} }
}, },
cleanup: clearFlushTimer, cleanup: clearFlushTimer,

View File

@ -26,7 +26,7 @@ import relativeTime from 'dayjs/plugin/relativeTime'
import type { CSSProperties } from 'react' import type { CSSProperties } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Outlet, useNavigate } from 'react-router' import { Outlet, useLocation, useNavigate } from 'react-router'
import { SWRConfig } from 'swr' import { SWRConfig } from 'swr'
import iconDark from '@/assets/image/icon_dark.svg?react' import iconDark from '@/assets/image/icon_dark.svg?react'
@ -53,6 +53,7 @@ import {
} from './_layout/hooks' } from './_layout/hooks'
import { handleNoticeMessage } from './_layout/utils' import { handleNoticeMessage } from './_layout/utils'
import { navItems } from './_routers' import { navItems } from './_routers'
import LogsPage from './logs'
import 'dayjs/locale/ru' import 'dayjs/locale/ru'
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn'
@ -120,6 +121,10 @@ const Layout = () => {
const navCollapsed = verge?.collapse_navbar ?? false const navCollapsed = verge?.collapse_navbar ?? false
const { switchLanguage } = useI18n() const { switchLanguage } = useI18n()
const navigate = useNavigate() const navigate = useNavigate()
const { pathname } = useLocation()
const isLogsPage = pathname === '/logs'
const logsPageMountedRef = useRef(false)
if (isLogsPage) logsPageMountedRef.current = true
const themeReady = useMemo(() => Boolean(theme), [theme]) const themeReady = useMemo(() => Boolean(theme), [theme])
const [menuUnlocked, setMenuUnlocked] = useState(false) const [menuUnlocked, setMenuUnlocked] = useState(false)
@ -477,6 +482,20 @@ const Layout = () => {
<BaseErrorBoundary> <BaseErrorBoundary>
<Outlet /> <Outlet />
</BaseErrorBoundary> </BaseErrorBoundary>
{logsPageMountedRef.current && (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
display: isLogsPage ? undefined : 'none',
}}
>
<LogsPage />
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -20,7 +20,6 @@ import UnlockSvg from '@/assets/image/itemicon/unlock.svg?react'
import Layout from './_layout' import Layout from './_layout'
import ConnectionsPage from './connections' import ConnectionsPage from './connections'
import HomePage from './home' import HomePage from './home'
import LogsPage from './logs'
import ProfilesPage from './profiles' import ProfilesPage from './profiles'
import ProxiesPage from './proxies' import ProxiesPage from './proxies'
import RulesPage from './rules' import RulesPage from './rules'
@ -62,7 +61,7 @@ export const navItems = [
label: 'layout.components.navigation.tabs.logs', label: 'layout.components.navigation.tabs.logs',
path: '/logs', path: '/logs',
icon: [<SubjectRoundedIcon key="mui" />, <LogsSvg key="svg" />], icon: [<SubjectRoundedIcon key="mui" />, <LogsSvg key="svg" />],
Component: LogsPage, Component: () => null /* KeepAlive: real LogsPage rendered in Layout */,
}, },
{ {
label: 'layout.components.navigation.tabs.unlock', label: 'layout.components.navigation.tabs.unlock',