Skip to content

Commit 8d7ae19

Browse files
author
pompurin404
committed
Initialization constant
1 parent 1aad740 commit 8d7ae19

File tree

9 files changed

+169
-46
lines changed

9 files changed

+169
-46
lines changed

src/main/config/profile.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ export async function addProfileItem(item: Partial<IProfileItem>): Promise<void>
5151
export function removeProfileItem(id: string): void {
5252
profileConfig.items = profileConfig.items?.filter((item) => item.id !== id)
5353
if (profileConfig.current === id) {
54-
profileConfig.current = profileConfig.items[0]?.id
54+
if (profileConfig.items.length > 0) {
55+
profileConfig.current = profileConfig.items[0]?.id
56+
} else {
57+
profileConfig.current = undefined
58+
}
5559
}
5660
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
61+
fs.rmSync(profilePath(id))
5762
window?.webContents.send('profileConfigUpdated')
5863
}
5964

src/renderer/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!doctype html>
22
<html>
33
<head>
4-
<meta charset="UTF-8" />
4+
<meta charset="UTF-8" lang="zh" />
55
<title>Mihomo Party</title>
66
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
77
<meta

src/renderer/src/components/profiles/profile-item.tsx

+107-15
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,131 @@
1-
import { Button, Card, CardBody, CardFooter, Progress } from '@nextui-org/react'
1+
import {
2+
Button,
3+
Card,
4+
CardBody,
5+
CardFooter,
6+
Dropdown,
7+
DropdownItem,
8+
DropdownMenu,
9+
DropdownTrigger,
10+
Progress
11+
} from '@nextui-org/react'
212
import { calcPercent, calcTraffic } from '@renderer/utils/calc'
3-
import React from 'react'
4-
import { IoMdRefresh } from 'react-icons/io'
13+
import { IoMdMore, IoMdRefresh } from 'react-icons/io'
14+
import dayjs from 'dayjs'
15+
import React, { Key, useMemo } from 'react'
516

617
interface Props {
718
info: IProfileItem
819
isCurrent: boolean
20+
removeProfileItem: (id: string) => Promise<void>
21+
mutateProfileConfig: () => void
922
onClick: () => Promise<void>
1023
}
1124

25+
interface MenuItem {
26+
key: string
27+
label: string
28+
showDivider: boolean
29+
color: 'default' | 'danger'
30+
className: string
31+
}
1232
const ProfileItem: React.FC<Props> = (props) => {
13-
const { info, onClick, isCurrent } = props
33+
const { info, removeProfileItem, mutateProfileConfig, onClick, isCurrent } = props
1434
const extra = info?.extra
1535
const usage = (extra?.upload ?? 0) + (extra?.download ?? 0)
1636
const total = extra?.total ?? 0
1737

38+
const menuItems: MenuItem[] = useMemo(() => {
39+
const list = [
40+
{
41+
key: 'edit',
42+
label: '编辑文件',
43+
showDivider: true,
44+
color: 'default',
45+
className: ''
46+
} as MenuItem,
47+
{
48+
key: 'delete',
49+
label: '删除',
50+
showDivider: false,
51+
color: 'danger',
52+
className: 'text-danger'
53+
} as MenuItem
54+
]
55+
if (info.home) {
56+
list.unshift({
57+
key: 'home',
58+
label: '主页',
59+
showDivider: false,
60+
color: 'default',
61+
className: ''
62+
} as MenuItem)
63+
}
64+
return list
65+
}, [info])
66+
67+
const onMenuAction = (key: Key): void => {
68+
switch (key) {
69+
case 'edit':
70+
break
71+
case 'delete': {
72+
removeProfileItem(info.id)
73+
mutateProfileConfig()
74+
break
75+
}
76+
77+
case 'home': {
78+
open(info.home)
79+
break
80+
}
81+
}
82+
}
83+
1884
return (
1985
<Card fullWidth isPressable onPress={onClick} className={isCurrent ? 'bg-primary' : ''}>
20-
<CardBody>
86+
<CardBody className="pb-1">
2187
<div className="flex justify-between h-[32px]">
2288
<h3 className="select-none text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px]">
2389
{info?.name}
2490
</h3>
25-
<Button isIconOnly size="sm" variant="light" color="default">
26-
<IoMdRefresh color="default" className="text-[24px]" />
27-
</Button>
91+
<div className="flex">
92+
<Button isIconOnly size="sm" variant="light" color="default">
93+
<IoMdRefresh color="default" className="text-[24px]" />
94+
</Button>
95+
<Dropdown>
96+
<DropdownTrigger>
97+
<Button isIconOnly size="sm" variant="light" color="default">
98+
<IoMdMore color="default" className="text-[24px]" />
99+
</Button>
100+
</DropdownTrigger>
101+
<DropdownMenu onAction={onMenuAction}>
102+
{menuItems.map((item) => (
103+
<DropdownItem
104+
showDivider={item.showDivider}
105+
key={item.key}
106+
color={item.color}
107+
className={item.className}
108+
>
109+
{item.label}
110+
</DropdownItem>
111+
))}
112+
</DropdownMenu>
113+
</Dropdown>
114+
</div>
115+
</div>
116+
<div className="mt-2 flex justify-between">
117+
<small>{extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}</small>
118+
<small>{dayjs(info.updated).fromNow()}</small>
28119
</div>
29120
</CardBody>
30-
<CardFooter className="pt-1">
31-
<Progress
32-
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
33-
label={extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}
34-
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
35-
className="max-w-md"
36-
/>
121+
<CardFooter className="pt-0">
122+
{extra && (
123+
<Progress
124+
className="w-full"
125+
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
126+
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
127+
/>
128+
)}
37129
</CardFooter>
38130
</Card>
39131
)

src/renderer/src/components/sider/profile-card.tsx

+20-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { useProfileConfig } from '@renderer/hooks/use-profile-config'
33
import { useLocation, useNavigate } from 'react-router-dom'
44
import { calcTraffic, calcPercent } from '@renderer/utils/calc'
55
import { IoMdRefresh } from 'react-icons/io'
6+
import relativeTime from 'dayjs/plugin/relativeTime'
7+
import 'dayjs/locale/zh-cn'
8+
import dayjs from 'dayjs'
9+
10+
dayjs.extend(relativeTime)
11+
dayjs.locale('zh-cn')
612

713
const ProfileCard: React.FC = () => {
814
const navigate = useNavigate()
@@ -16,6 +22,7 @@ const ProfileCard: React.FC = () => {
1622
type: 'local',
1723
name: '空白订阅'
1824
}
25+
1926
const extra = info?.extra
2027
const usage = (extra?.upload ?? 0) + (extra?.download ?? 0)
2128
const total = extra?.total ?? 0
@@ -27,7 +34,7 @@ const ProfileCard: React.FC = () => {
2734
isPressable
2835
onPress={() => navigate('/profiles')}
2936
>
30-
<CardBody>
37+
<CardBody className="pb-1">
3138
<div className="flex justify-between h-[32px]">
3239
<h3 className="select-none text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px]">
3340
{info?.name}
@@ -36,14 +43,19 @@ const ProfileCard: React.FC = () => {
3643
<IoMdRefresh color="default" className="text-[24px]" />
3744
</Button>
3845
</div>
46+
<div className="mt-2 flex justify-between">
47+
<small>{extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}</small>
48+
<small>{dayjs(info.updated).fromNow()}</small>
49+
</div>
3950
</CardBody>
40-
<CardFooter className="pt-1">
41-
<Progress
42-
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
43-
label={extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}
44-
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
45-
className="max-w-md"
46-
/>
51+
<CardFooter className="pt-0">
52+
{extra && (
53+
<Progress
54+
className="w-full"
55+
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
56+
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
57+
/>
58+
)}
4759
</CardFooter>
4860
</Card>
4961
)

src/renderer/src/components/sider/tun-switcher.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@ import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-c
33
import BorderSwitch from '@renderer/components/base/border-swtich'
44
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
55
import { useLocation, useNavigate } from 'react-router-dom'
6-
import {
7-
platform,
8-
encryptString,
9-
patchMihomoConfig,
10-
isEncryptionAvailable
11-
} from '@renderer/utils/ipc'
6+
import { encryptString, patchMihomoConfig, isEncryptionAvailable } from '@renderer/utils/ipc'
7+
import { platform } from '@renderer/utils/init'
128
import React, { useState } from 'react'
139
import { useAppConfig } from '@renderer/hooks/use-app-config'
1410
import BasePasswordModal from '../base/base-password-modal'
@@ -24,7 +20,7 @@ const TunSwitcher: React.FC = () => {
2420
const { enable } = tun || {}
2521

2622
const onChange = async (enable: boolean): Promise<void> => {
27-
if (enable && (await platform()) !== 'win32') {
23+
if (enable && platform !== 'win32') {
2824
const encryptionAvailable = await isEncryptionAvailable()
2925
if (!appConfig?.encryptedPassword && encryptionAvailable) {
3026
setOpenPasswordModal(true)

src/renderer/src/main.tsx

+14-12
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ import ReactDOM from 'react-dom/client'
33
import { HashRouter } from 'react-router-dom'
44
import { ThemeProvider as NextThemesProvider } from 'next-themes'
55
import { NextUIProvider } from '@nextui-org/react'
6-
import '@renderer/utils/init'
6+
import { init } from '@renderer/utils/init'
77
import '@renderer/assets/main.css'
88
import App from '@renderer/App'
99

10-
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
11-
<React.StrictMode>
12-
<NextUIProvider>
13-
<NextThemesProvider attribute="class" defaultTheme="dark">
14-
<HashRouter>
15-
<App />
16-
</HashRouter>
17-
</NextThemesProvider>
18-
</NextUIProvider>
19-
</React.StrictMode>
20-
)
10+
init().then(() => {
11+
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
12+
<React.StrictMode>
13+
<NextUIProvider>
14+
<NextThemesProvider attribute="class" defaultTheme="dark">
15+
<HashRouter>
16+
<App />
17+
</HashRouter>
18+
</NextThemesProvider>
19+
</NextUIProvider>
20+
</React.StrictMode>
21+
)
22+
})

src/renderer/src/pages/profiles.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ import { useState } from 'react'
66
import { MdContentPaste } from 'react-icons/md'
77

88
const Profiles: React.FC = () => {
9-
const { profileConfig, addProfileItem, changeCurrentProfile } = useProfileConfig()
9+
const {
10+
profileConfig,
11+
addProfileItem,
12+
removeProfileItem,
13+
changeCurrentProfile,
14+
mutateProfileConfig
15+
} = useProfileConfig()
1016
const { current, items } = profileConfig || {}
1117
const [importing, setImporting] = useState(false)
1218
const [url, setUrl] = useState('')
@@ -55,6 +61,8 @@ const Profiles: React.FC = () => {
5561
<ProfileItem
5662
key={item.id}
5763
isCurrent={item.id === current}
64+
removeProfileItem={removeProfileItem}
65+
mutateProfileConfig={mutateProfileConfig}
5866
info={item}
5967
onClick={async () => {
6068
await changeCurrentProfile(item.id)

src/renderer/src/utils/init.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
3+
import { getPlatform } from './ipc'
24
const originError = console.error
35
const originWarn = console.warn
46
console.error = function (...args: any[]): void {
@@ -13,3 +15,9 @@ console.warn = function (...args): void {
1315
}
1416
originWarn.call(console, args)
1517
}
18+
19+
export let platform: NodeJS.Platform
20+
21+
export async function init(): Promise<void> {
22+
platform = await getPlatform()
23+
}

src/renderer/src/utils/ipc.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export async function encryptString(str: string): Promise<Buffer> {
115115
return await window.electron.ipcRenderer.invoke('encryptString', str)
116116
}
117117

118-
export async function platform(): Promise<NodeJS.Platform> {
118+
export async function getPlatform(): Promise<NodeJS.Platform> {
119119
return await window.electron.ipcRenderer.invoke('platform')
120120
}
121121

0 commit comments

Comments
 (0)