diff --git a/src/main/cmds.ts b/src/main/cmds.ts index 7c55e5e..0efe21f 100644 --- a/src/main/cmds.ts +++ b/src/main/cmds.ts @@ -10,9 +10,13 @@ import { checkAutoRun, disableAutoRun, enableAutoRun } from './autoRun' import { getAppConfig, setAppConfig, - getProfileConfig, getControledMihomoConfig, - setControledMihomoConfig + setControledMihomoConfig, + getProfileConfig, + getCurrentProfileItem, + getProfileItem, + addProfileItem, + removeProfileItem } from './config' import { restartCore } from './manager' @@ -30,5 +34,9 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('getControledMihomoConfig', (_e, force) => getControledMihomoConfig(force)) ipcMain.handle('setControledMihomoConfig', (_e, config) => setControledMihomoConfig(config)) ipcMain.handle('getProfileConfig', (_e, force) => getProfileConfig(force)) + ipcMain.handle('getCurrentProfileItem', getCurrentProfileItem) + ipcMain.handle('getProfileItem', (_e, id) => getProfileItem(id)) + ipcMain.handle('addProfileItem', (_e, item) => addProfileItem(item)) + ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id)) ipcMain.handle('restartCore', () => restartCore()) } diff --git a/src/main/config.ts b/src/main/config.ts index 01b2e32..96f5519 100644 --- a/src/main/config.ts +++ b/src/main/config.ts @@ -8,10 +8,10 @@ import { } from './template' import { appConfigPath, controledMihomoConfigPath, profileConfigPath, profilePath } from './dirs' -export let appConfig: IAppConfig -export let profileConfig: IProfileConfig -export let currentProfile: Partial -export let controledMihomoConfig: Partial +export let appConfig: IAppConfig // config.yaml +export let profileConfig: IProfileConfig // profile.yaml +export let currentProfile: Partial // profiles/xxx.yaml +export let controledMihomoConfig: Partial // mihomo.yaml export function initConfig(): void { if (!fs.existsSync(appConfigPath())) { @@ -63,11 +63,30 @@ export function getProfileConfig(force = false): IProfileConfig { return profileConfig } -export function setProfileConfig(patch: Partial): void { - profileConfig = Object.assign(profileConfig, patch) +export function getProfileItem(id: string | undefined): IProfileItem { + const items = profileConfig.items + return items?.find((item) => item.id === id) || { id: 'default', type: 'local', name: '空白订阅' } +} + +export function addProfileItem(item: IProfileItem): void { + profileConfig.items.push(item) + if (!profileConfig.current) { + profileConfig.current = item.id + } fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig)) } +export function removeProfileItem(id: string): void { + profileConfig.items = profileConfig.items?.filter((item) => item.id !== id) + if (profileConfig.current === id) { + profileConfig.current = profileConfig.items[0]?.id + } + fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig)) +} + +export function getCurrentProfileItem(): IProfileItem { + return getProfileItem(profileConfig.current) +} export function getCurrentProfile(force = false): Partial { if (force || !currentProfile) { if (profileConfig.current) { diff --git a/src/main/template.ts b/src/main/template.ts index d9f388e..76937f2 100644 --- a/src/main/template.ts +++ b/src/main/template.ts @@ -13,14 +13,7 @@ export const defaultControledMihomoConfig: Partial = { } export const defaultProfileConfig: IProfileConfig = { - current: 'default', - profiles: [ - { - id: 'default', - type: 'local', - name: '默认' - } - ] + items: [] } export const defaultProfile: Partial = { diff --git a/src/renderer/src/components/sider/conn-card.tsx b/src/renderer/src/components/sider/conn-card.tsx index 7e5cfea..982f49b 100644 --- a/src/renderer/src/components/sider/conn-card.tsx +++ b/src/renderer/src/components/sider/conn-card.tsx @@ -8,6 +8,7 @@ import { mihomoConnections } from '@renderer/utils/ipc' const ConnCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() + const match = location.pathname.includes('/connections') const { data: connections } = useSWR('/connections', mihomoConnections, { refreshInterval: 5000 @@ -24,7 +25,7 @@ const ConnCard: React.FC = () => { return ( navigate('/connections')} > @@ -38,7 +39,22 @@ const ConnCard: React.FC = () => { > - + {connections?.connections?.length ?? 0} diff --git a/src/renderer/src/components/sider/log-card.tsx b/src/renderer/src/components/sider/log-card.tsx index 8fce847..be57bcf 100644 --- a/src/renderer/src/components/sider/log-card.tsx +++ b/src/renderer/src/components/sider/log-card.tsx @@ -5,10 +5,10 @@ import { useLocation, useNavigate } from 'react-router-dom' const LogCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() - + const match = location.pathname.includes('/logs') return ( navigate('/logs')} > diff --git a/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx b/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx index 5399510..069b7f9 100644 --- a/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx +++ b/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx @@ -25,13 +25,13 @@ const MihomoCoreCard: React.FC = () => { const { core } = appConfig || {} const navigate = useNavigate() const location = useLocation() - + const match = location.pathname.includes('/mihomo') return ( navigate('/mihomo')} - className={`mb-2 ${location.pathname.includes('/mihomo') ? 'bg-primary' : ''}`} + className={`mb-2 ${match ? 'bg-primary' : ''}`} >
diff --git a/src/renderer/src/components/sider/override-card.tsx b/src/renderer/src/components/sider/override-card.tsx index 829e2ee..948fd59 100644 --- a/src/renderer/src/components/sider/override-card.tsx +++ b/src/renderer/src/components/sider/override-card.tsx @@ -1,14 +1,17 @@ import { Button, Card, CardBody, CardFooter, Switch } from '@nextui-org/react' +import React, { useState } from 'react' import { MdFormatOverline } from 'react-icons/md' import { useLocation, useNavigate } from 'react-router-dom' const OverrideCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() + const match = location.pathname.includes('/override') + const [enable, setEnable] = useState(false) return ( navigate('/override')} > @@ -22,7 +25,14 @@ const OverrideCard: React.FC = () => { > - +
diff --git a/src/renderer/src/components/sider/profile-card.tsx b/src/renderer/src/components/sider/profile-card.tsx index d819516..739c865 100644 --- a/src/renderer/src/components/sider/profile-card.tsx +++ b/src/renderer/src/components/sider/profile-card.tsx @@ -1,31 +1,51 @@ -import { Button, Card, CardBody, CardFooter, Slider } from '@nextui-org/react' +import { Button, Card, CardBody, CardFooter, Progress } from '@nextui-org/react' +import { getCurrentProfileItem } from '@renderer/utils/ipc' import { IoMdRefresh } from 'react-icons/io' import { useLocation, useNavigate } from 'react-router-dom' - +import useSWR from 'swr' const ProfileCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() + const match = location.pathname.includes('/profiles') + + const { data: info } = useSWR('getCurrentProfileItem', getCurrentProfileItem) + const extra = info?.extra return ( navigate('/profiles')} >
-

订阅名称

+

{info?.name}

- +
) } export default ProfileCard + +function calcPercent( + upload: number | undefined, + download: number | undefined, + total: number | undefined +): number { + if (upload === undefined || download === undefined || total === undefined) { + return 100 + } + return Math.round(((upload + download) / total) * 100) +} diff --git a/src/renderer/src/components/sider/proxy-card.tsx b/src/renderer/src/components/sider/proxy-card.tsx index 8beea7d..be6a0f7 100644 --- a/src/renderer/src/components/sider/proxy-card.tsx +++ b/src/renderer/src/components/sider/proxy-card.tsx @@ -5,11 +5,11 @@ import { useLocation, useNavigate } from 'react-router-dom' const ProxyCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() - + const match = location.pathname.includes('/proxies') return ( navigate('/proxies')} > diff --git a/src/renderer/src/components/sider/rule-card.tsx b/src/renderer/src/components/sider/rule-card.tsx index 983eb31..33098e6 100644 --- a/src/renderer/src/components/sider/rule-card.tsx +++ b/src/renderer/src/components/sider/rule-card.tsx @@ -7,14 +7,14 @@ import useSWR from 'swr' const RuleCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() - + const match = location.pathname.includes('/rules') const { data: rules } = useSWR('/connections', mihomoRules, { refreshInterval: 5000 }) return ( navigate('/rules')} > @@ -28,7 +28,22 @@ const RuleCard: React.FC = () => { > - + {rules?.rules?.length ?? 0} diff --git a/src/renderer/src/components/sider/sysproxy-switcher.tsx b/src/renderer/src/components/sider/sysproxy-switcher.tsx index 2f6d097..63d6e49 100644 --- a/src/renderer/src/components/sider/sysproxy-switcher.tsx +++ b/src/renderer/src/components/sider/sysproxy-switcher.tsx @@ -1,14 +1,17 @@ import { Button, Card, CardBody, CardFooter, Switch } from '@nextui-org/react' +import React, { useState } from 'react' import { AiOutlineGlobal } from 'react-icons/ai' import { useLocation, useNavigate } from 'react-router-dom' const SysproxySwitcher: React.FC = () => { const navigate = useNavigate() const location = useLocation() + const match = location.pathname.includes('/sysproxy') + const [enable, setEnable] = useState(false) return ( navigate('/sysproxy')} > @@ -22,7 +25,12 @@ const SysproxySwitcher: React.FC = () => { > - + diff --git a/src/renderer/src/components/sider/tun-switcher.tsx b/src/renderer/src/components/sider/tun-switcher.tsx index b8c9f9e..9c21e06 100644 --- a/src/renderer/src/components/sider/tun-switcher.tsx +++ b/src/renderer/src/components/sider/tun-switcher.tsx @@ -1,23 +1,36 @@ import { Button, Card, CardBody, CardFooter, Switch } from '@nextui-org/react' +import React, { useState } from 'react' import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb' import { useLocation, useNavigate } from 'react-router-dom' const TunSwitcher: React.FC = () => { const navigate = useNavigate() const location = useLocation() + const match = location.pathname.includes('/tun') + const [enable, setEnable] = useState(false) return ( navigate('/tun')} >
- - +
diff --git a/src/renderer/src/hooks/use-profile.tsx b/src/renderer/src/hooks/use-profile.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/renderer/src/pages/profiles.tsx b/src/renderer/src/pages/profiles.tsx index a56cf58..01f653e 100644 --- a/src/renderer/src/pages/profiles.tsx +++ b/src/renderer/src/pages/profiles.tsx @@ -1,5 +1,44 @@ +import { Button, Input } from '@nextui-org/react' +import BasePage from '@renderer/components/base/base-page' +import { useState } from 'react' +import { MdContentPaste } from 'react-icons/md' + const Profiles: React.FC = () => { - return
Profiles
+ const [url, setUrl] = useState('') + + const handleImport = async (): Promise => { + console.log('import', url) + } + return ( + +
+ { + navigator.clipboard.readText().then((text) => { + setUrl(text) + }) + }} + > + + + } + /> + +
+
+ ) } export default Profiles diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index 0202c5b..be7548f 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -46,6 +46,26 @@ export async function setControledMihomoConfig(patch: Partial): P await window.electron.ipcRenderer.invoke('setControledMihomoConfig', patch) } +export async function getProfileConfig(force = false): Promise { + return await window.electron.ipcRenderer.invoke('getProfileConfig', force) +} + +export async function getCurrentProfileItem(): Promise { + return await window.electron.ipcRenderer.invoke('getCurrentProfileItem') +} + +export async function getProfileItem(id: string | undefined): Promise { + return await window.electron.ipcRenderer.invoke('getProfileItem', id) +} + +export async function addProfileItem(item: IProfileItem): Promise { + await window.electron.ipcRenderer.invoke('addProfileItem', item) +} + +export async function removeProfileItem(id: string): Promise { + await window.electron.ipcRenderer.invoke('removeProfileItem', id) +} + export async function restartCore(): Promise { await window.electron.ipcRenderer.invoke('restartCore') } diff --git a/src/shared/types.d.ts b/src/shared/types.d.ts index fa684e0..fb56908 100644 --- a/src/shared/types.d.ts +++ b/src/shared/types.d.ts @@ -85,11 +85,19 @@ interface IMihomoConfig { interface IProfileConfig { current?: string - profiles?: IProfileItem[] + items: IProfileItem[] } interface IProfileItem { id: string type: 'remote' | 'local' name: string + url?: string + updated?: number + extra?: { + upload: number + download: number + total: number + expire: number + } }