fix: use sync state to prevent tray icon flickering on macOS with traffic display

This commit is contained in:
xmk23333 2026-01-15 23:14:17 +08:00
parent 228e2cbffc
commit e21558ac37
2 changed files with 14 additions and 9 deletions

View File

@ -39,6 +39,8 @@ import { trayLogger } from '../utils/logger'
import { floatingWindow, triggerFloatingWindow } from './floatingWindow' import { floatingWindow, triggerFloatingWindow } from './floatingWindow'
export let tray: Tray | null = null export let tray: Tray | null = null
// macOS 流量显示状态,避免异步读取配置导致的时序问题
let macTrafficIconEnabled = false
export const buildContextMenu = async (): Promise<Menu> => { export const buildContextMenu = async (): Promise<Menu> => {
// 添加调试日志 // 添加调试日志
@ -231,6 +233,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
if (process.platform === 'win32') { if (process.platform === 'win32') {
try { try {
await restartAsAdmin() await restartAsAdmin()
return
} catch (error) { } catch (error) {
await trayLogger.error('Failed to restart as admin from tray', error) await trayLogger.error('Failed to restart as admin from tray', error)
item.checked = false item.checked = false
@ -392,7 +395,8 @@ export async function createTray(): Promise<void> {
} }
// 移除旧监听器防止累积 // 移除旧监听器防止累积
ipcMain.removeAllListeners('trayIconUpdate') ipcMain.removeAllListeners('trayIconUpdate')
ipcMain.on('trayIconUpdate', async (_, png: string) => { ipcMain.on('trayIconUpdate', async (_, png: string, enabled: boolean) => {
macTrafficIconEnabled = enabled
const image = nativeImage.createFromDataURL(png).resize({ height: 16 }) const image = nativeImage.createFromDataURL(png).resize({ height: 16 })
image.setTemplateImage(true) image.setTemplateImage(true)
tray?.setImage(image) tray?.setImage(image)
@ -524,14 +528,15 @@ const getIconPaths = () => {
export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void { export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void {
if (!tray) return if (!tray) return
// macOS 流量显示开启时,由 trayIconUpdate 负责图标更新
if (process.platform === 'darwin' && macTrafficIconEnabled) return
const status = calculateTrayIconStatus(sysProxyEnabled, tunEnabled) const status = calculateTrayIconStatus(sysProxyEnabled, tunEnabled)
const iconPaths = getIconPaths() const iconPaths = getIconPaths()
getAppConfig().then(({ disableTrayIconColor = false, showTraffic = false }) => { getAppConfig().then(({ disableTrayIconColor = false }) => {
if (!tray) return if (!tray) return
// macOS 开启流量显示时,由 trayIconUpdate 负责图标更新 if (process.platform === 'darwin' && macTrafficIconEnabled) return
if (process.platform === 'darwin' && showTraffic) return
const iconPath = disableTrayIconColor ? iconPaths.white : iconPaths[status] const iconPath = disableTrayIconColor ? iconPaths.white : iconPaths[status]
try { try {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@ -550,10 +555,10 @@ export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: bo
export async function updateTrayIcon(): Promise<void> { export async function updateTrayIcon(): Promise<void> {
if (!tray) return if (!tray) return
// macOS 流量显示开启时,由 trayIconUpdate 负责图标更新
if (process.platform === 'darwin' && macTrafficIconEnabled) return
const { disableTrayIconColor = false, showTraffic = false } = await getAppConfig() const { disableTrayIconColor = false } = await getAppConfig()
// macOS 开启流量显示时,由 trayIconUpdate 负责图标更新
if (process.platform === 'darwin' && showTraffic) return
const status = await getTrayIconStatus() const status = await getTrayIconStatus()
const iconPaths = getIconPaths() const iconPaths = getIconPaths()
const iconPath = disableTrayIconColor ? iconPaths.white : iconPaths[status] const iconPath = disableTrayIconColor ? iconPaths.white : iconPaths[status]

View File

@ -161,7 +161,7 @@ const ConnCard: React.FC<Props> = (props) => {
} }
} else if (hasShowTrafficRef.current) { } else if (hasShowTrafficRef.current) {
// 只在从 showTraffic=true 切换到 false 时恢复一次原始图标 // 只在从 showTraffic=true 切换到 false 时恢复一次原始图标
window.electron.ipcRenderer.send('trayIconUpdate', trayIconBase64) window.electron.ipcRenderer.send('trayIconUpdate', trayIconBase64, false)
hasShowTrafficRef.current = false hasShowTrafficRef.current = false
} }
} }
@ -305,7 +305,7 @@ const drawSvg = async (
currentDownloadRef.current = download currentDownloadRef.current = download
const svg = `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 36"><image height="36" width="36" href="${trayIconBase64}"/><text x="140" y="15" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end">${calcTraffic(upload)}/s</text><text x="140" y="34" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end">${calcTraffic(download)}/s</text></svg>` const svg = `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 36"><image height="36" width="36" href="${trayIconBase64}"/><text x="140" y="15" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end">${calcTraffic(upload)}/s</text><text x="140" y="34" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end">${calcTraffic(download)}/s</text></svg>`
const image = await loadImage(svg) const image = await loadImage(svg)
window.electron.ipcRenderer.send('trayIconUpdate', image) window.electron.ipcRenderer.send('trayIconUpdate', image, true)
} }
const loadImage = (url: string): Promise<string> => { const loadImage = (url: string): Promise<string> => {