optimized rule page performance

This commit is contained in:
pompurin404 2024-09-01 16:00:31 +08:00
parent 704a322c25
commit 449597f941
No known key found for this signature in database
11 changed files with 115 additions and 26 deletions

View File

@ -2,10 +2,6 @@
- 1.2.x YAML覆写语法有所变动请更新后参考文档进行修改 - 1.2.x YAML覆写语法有所变动请更新后参考文档进行修改
### New Features ### Performance Improvements
- 支持使用外部编辑器打开文件 - 优化规则/代理页面性能
- 支持新建订阅/覆写
- 添加窗口置顶按钮
- 允许禁用系统标题栏
- 重写连接页面

View File

@ -8,17 +8,22 @@ import {
} from '../config' } from '../config'
import { mihomoWorkConfigPath, overridePath } from '../utils/dirs' import { mihomoWorkConfigPath, overridePath } from '../utils/dirs'
import yaml from 'yaml' import yaml from 'yaml'
import { readFile, writeFile } from 'fs/promises' import { writeFile } from 'fs/promises'
import { deepMerge } from '../utils/merge' import { deepMerge } from '../utils/merge'
import vm from 'vm' import vm from 'vm'
import { writeFileSync } from 'fs' import { writeFileSync } from 'fs'
let runtimeConfigStr: string
let runtimeConfig: IMihomoConfig
export async function generateProfile(): Promise<void> { export async function generateProfile(): Promise<void> {
const { current } = await getProfileConfig() const { current } = await getProfileConfig()
const currentProfile = await overrideProfile(current, await getProfile(current)) const currentProfile = await overrideProfile(current, await getProfile(current))
const controledMihomoConfig = await getControledMihomoConfig() const controledMihomoConfig = await getControledMihomoConfig()
const profile = deepMerge(currentProfile, controledMihomoConfig) const profile = deepMerge(currentProfile, controledMihomoConfig)
await writeFile(mihomoWorkConfigPath(), yaml.stringify(profile)) runtimeConfig = profile
runtimeConfigStr = yaml.stringify(profile)
await writeFile(mihomoWorkConfigPath(), runtimeConfigStr)
} }
async function overrideProfile( async function overrideProfile(
@ -87,9 +92,9 @@ function runOverrideScript(
} }
export async function getRuntimeConfigStr(): Promise<string> { export async function getRuntimeConfigStr(): Promise<string> {
return await readFile(mihomoWorkConfigPath(), 'utf8') return runtimeConfigStr
} }
export async function getRuntimeConfig(): Promise<IMihomoConfig> { export async function getRuntimeConfig(): Promise<IMihomoConfig> {
return yaml.parse(await getRuntimeConfigStr()) return runtimeConfig
} }

View File

@ -60,6 +60,7 @@ export async function startCore(): Promise<void> {
} }
if (data.toString().includes('RESTful API listening at')) { if (data.toString().includes('RESTful API listening at')) {
await startMihomoTraffic() await startMihomoTraffic()
mainWindow?.webContents.send('coreRestart')
retry = 10 retry = 10
resolve() resolve()
} }

View File

@ -71,8 +71,11 @@ export const mihomoRules = async (): Promise<IMihomoRulesInfo> => {
export const mihomoProxies = async (): Promise<IMihomoProxies> => { export const mihomoProxies = async (): Promise<IMihomoProxies> => {
const instance = await getAxios() const instance = await getAxios()
const proxies = (await instance.get('/proxies')) as IMihomoProxies
return await instance.get('/proxies') if (!proxies.proxies['GLOBAL']) {
throw new Error('GLOBAL proxy not found')
}
return proxies
} }
export const mihomoGroups = async (): Promise<IMihomoMixedGroup[]> => { export const mihomoGroups = async (): Promise<IMihomoMixedGroup[]> => {

View File

@ -3,14 +3,13 @@ import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { LuGroup } from 'react-icons/lu' import { LuGroup } from 'react-icons/lu'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import useSWR from 'swr' import { useGroups } from '@renderer/hooks/use-groups'
import { mihomoGroups } from '@renderer/utils/ipc'
const ProxyCard: React.FC = () => { const ProxyCard: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/proxies') const match = location.pathname.includes('/proxies')
const { data: groups = [] } = useSWR('mihomoGroups', mihomoGroups) const { groups = [] } = useGroups()
const { const {
attributes, attributes,
listeners, listeners,

View File

@ -1,16 +1,15 @@
import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react'
import { mihomoRules } from '@renderer/utils/ipc'
import { MdOutlineAltRoute } from 'react-icons/md' import { MdOutlineAltRoute } from 'react-icons/md'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import useSWR from 'swr' import { useRules } from '@renderer/hooks/use-rules'
const RuleCard: React.FC = () => { const RuleCard: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/rules') const match = location.pathname.includes('/rules')
const { data: rules } = useSWR<IMihomoRulesInfo>('mihomoRules', mihomoRules) const { rules } = useRules()
const { const {
attributes, attributes,
listeners, listeners,

View File

@ -0,0 +1,36 @@
import React, { createContext, useContext, ReactNode } from 'react'
import useSWR from 'swr'
import { mihomoGroups } from '@renderer/utils/ipc'
interface GroupsContextType {
groups: IMihomoMixedGroup[] | undefined
mutate: () => void
}
const GroupsContext = createContext<GroupsContextType | undefined>(undefined)
export const GroupsProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const { data: groups, mutate } = useSWR<IMihomoMixedGroup[]>('mihomoGroups', mihomoGroups, {
errorRetryInterval: 200,
errorRetryCount: 10
})
React.useEffect(() => {
window.electron.ipcRenderer.on('coreRestart', () => {
mutate()
})
return (): void => {
window.electron.ipcRenderer.removeAllListeners('coreRestart')
}
}, [])
return <GroupsContext.Provider value={{ groups, mutate }}>{children}</GroupsContext.Provider>
}
export const useGroups = (): GroupsContextType => {
const context = useContext(GroupsContext)
if (context === undefined) {
throw new Error('useGroups must be used within an GroupsProvider')
}
return context
}

View File

@ -0,0 +1,47 @@
import React, { createContext, useContext, ReactNode } from 'react'
import useSWR from 'swr'
import { mihomoRules } from '@renderer/utils/ipc'
interface RulesContextType {
rules: IMihomoRulesInfo | undefined
mutate: () => void
}
const RulesContext = createContext<RulesContextType | undefined>(undefined)
let emptyRetry = 10
export const RulesProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const { data: rules, mutate } = useSWR<IMihomoRulesInfo>('mihomoRules', mihomoRules, {
errorRetryInterval: 200,
errorRetryCount: 10,
onSuccess: (data) => {
if (data.rules.length === 0 && emptyRetry) {
emptyRetry--
setTimeout(() => {
mutate()
}, 200)
} else {
emptyRetry = 10
}
}
})
React.useEffect(() => {
window.electron.ipcRenderer.on('coreRestart', () => {
mutate()
})
return (): void => {
window.electron.ipcRenderer.removeAllListeners('coreRestart')
}
}, [])
return <RulesContext.Provider value={{ rules, mutate }}>{children}</RulesContext.Provider>
}
export const useRules = (): RulesContextType => {
const context = useContext(RulesContext)
if (context === undefined) {
throw new Error('useRules must be used within an RulesProvider')
}
return context
}

View File

@ -12,6 +12,8 @@ import { AppConfigProvider } from './hooks/use-app-config'
import { ControledMihomoConfigProvider } from './hooks/use-controled-mihomo-config' import { ControledMihomoConfigProvider } from './hooks/use-controled-mihomo-config'
import { OverrideConfigProvider } from './hooks/use-override-config' import { OverrideConfigProvider } from './hooks/use-override-config'
import { ProfileConfigProvider } from './hooks/use-profile-config' import { ProfileConfigProvider } from './hooks/use-profile-config'
import { RulesProvider } from './hooks/use-rules'
import { GroupsProvider } from './hooks/use-groups'
init().then(() => { init().then(() => {
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
@ -53,7 +55,11 @@ init().then(() => {
<ControledMihomoConfigProvider> <ControledMihomoConfigProvider>
<ProfileConfigProvider> <ProfileConfigProvider>
<OverrideConfigProvider> <OverrideConfigProvider>
<GroupsProvider>
<RulesProvider>
<App /> <App />
</RulesProvider>
</GroupsProvider>
</OverrideConfigProvider> </OverrideConfigProvider>
</ProfileConfigProvider> </ProfileConfigProvider>
</ControledMihomoConfigProvider> </ControledMihomoConfigProvider>

View File

@ -5,7 +5,6 @@ import {
mihomoChangeProxy, mihomoChangeProxy,
mihomoCloseAllConnections, mihomoCloseAllConnections,
mihomoGroupDelay, mihomoGroupDelay,
mihomoGroups,
mihomoProxyDelay mihomoProxyDelay
} from '@renderer/utils/ipc' } from '@renderer/utils/ipc'
import { CgDetailsLess, CgDetailsMore } from 'react-icons/cg' import { CgDetailsLess, CgDetailsMore } from 'react-icons/cg'
@ -14,14 +13,14 @@ import { TbCircleLetterD } from 'react-icons/tb'
import { FaLocationCrosshairs } from 'react-icons/fa6' import { FaLocationCrosshairs } from 'react-icons/fa6'
import { RxLetterCaseCapitalize } from 'react-icons/rx' import { RxLetterCaseCapitalize } from 'react-icons/rx'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import useSWR from 'swr'
import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso' import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso'
import ProxyItem from '@renderer/components/proxies/proxy-item' import ProxyItem from '@renderer/components/proxies/proxy-item'
import { IoIosArrowBack } from 'react-icons/io' import { IoIosArrowBack } from 'react-icons/io'
import { MdOutlineSpeed } from 'react-icons/md' import { MdOutlineSpeed } from 'react-icons/md'
import { useGroups } from '@renderer/hooks/use-groups'
const Proxies: React.FC = () => { const Proxies: React.FC = () => {
const { data: groups = [], mutate } = useSWR('mihomoGroups', mihomoGroups) const { groups = [], mutate } = useGroups()
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { const {
proxyDisplayMode = 'simple', proxyDisplayMode = 'simple',
@ -190,7 +189,6 @@ const Proxies: React.FC = () => {
canvas.height = img.height canvas.height = img.height
ctx?.drawImage(img, 0, 0) ctx?.drawImage(img, 0, 0)
const data = canvas.toDataURL('image/png') const data = canvas.toDataURL('image/png')
console.log('set')
localStorage.setItem(groups[index].icon, data) localStorage.setItem(groups[index].icon, data)
} }
img.src = groups[index].icon img.src = groups[index].icon

View File

@ -3,11 +3,10 @@ import RuleItem from '@renderer/components/rules/rule-item'
import { Virtuoso } from 'react-virtuoso' import { Virtuoso } from 'react-virtuoso'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { Divider, Input } from '@nextui-org/react' import { Divider, Input } from '@nextui-org/react'
import useSWR from 'swr' import { useRules } from '@renderer/hooks/use-rules'
import { mihomoRules } from '@renderer/utils/ipc'
const Rules: React.FC = () => { const Rules: React.FC = () => {
const { data: rules } = useSWR<IMihomoRulesInfo>('mihomoRules', mihomoRules) const { rules } = useRules()
const [filter, setFilter] = useState('') const [filter, setFilter] = useState('')
const filteredRules = useMemo(() => { const filteredRules = useMemo(() => {