Skip to content

Commit 1cc68b5

Browse files
author
pompurin404
committed
update profile
1 parent 8d7ae19 commit 1cc68b5

File tree

10 files changed

+343
-61
lines changed

10 files changed

+343
-61
lines changed

src/main/config/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@ export {
77
getProfileConfig,
88
addProfileItem,
99
removeProfileItem,
10-
createProfile
10+
createProfile,
11+
getProfileStr,
12+
setProfileStr,
13+
changeCurrentProfile,
14+
updateProfileItem
1115
} from './profile'

src/main/config/profile.ts

+29-5
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,21 @@ export async function changeCurrentProfile(id: string): Promise<void> {
3838
}
3939
}
4040

41+
export async function updateProfileItem(item: IProfileItem): Promise<void> {
42+
const index = profileConfig.items.findIndex((i) => i.id === item.id)
43+
profileConfig.items[index] = item
44+
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
45+
window?.webContents.send('profileConfigUpdated')
46+
}
47+
4148
export async function addProfileItem(item: Partial<IProfileItem>): Promise<void> {
4249
const newItem = await createProfile(item)
43-
profileConfig.items.push(newItem)
50+
if (profileConfig.items.find((i) => i.id === newItem.id)) {
51+
updateProfileItem(newItem)
52+
} else {
53+
profileConfig.items.push(newItem)
54+
}
55+
4456
if (!getProfileConfig().current) {
4557
changeCurrentProfile(newItem.id)
4658
}
@@ -134,7 +146,7 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
134146
if (headers['subscription-userinfo']) {
135147
newItem.extra = parseSubinfo(headers['subscription-userinfo'])
136148
}
137-
fs.writeFileSync(profilePath(id), data, 'utf-8')
149+
setProfileStr(id, data)
138150
} catch (e) {
139151
dialog.showErrorBox('Failed to fetch remote profile', `${e}\nurl: ${item.url}`)
140152
throw new Error(`Failed to fetch remote profile ${e}`)
@@ -150,21 +162,33 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
150162
throw new Error('File is required for local profile')
151163
}
152164
const data = item.file
153-
fs.writeFileSync(profilePath(id), yaml.stringify(data))
165+
setProfileStr(id, data)
154166
break
155167
}
156168
}
157169

158170
return newItem
159171
}
160172

173+
export function getProfileStr(id: string): string {
174+
return fs.readFileSync(profilePath(id), 'utf-8')
175+
}
176+
177+
export function setProfileStr(id: string, content: string): void {
178+
fs.writeFileSync(profilePath(id), content, 'utf-8')
179+
if (id === getProfileConfig().current) {
180+
getCurrentProfile(true)
181+
restartCore()
182+
}
183+
}
184+
161185
export function getCurrentProfile(force = false): Partial<IMihomoConfig> {
162186
if (force || !currentProfile) {
163187
const current = getProfileConfig().current
164188
if (current) {
165-
currentProfile = yaml.parse(fs.readFileSync(profilePath(current), 'utf-8'))
189+
currentProfile = yaml.parse(getProfileStr(current))
166190
} else {
167-
currentProfile = yaml.parse(fs.readFileSync(profilePath('default'), 'utf-8'))
191+
currentProfile = yaml.parse(getProfileStr('default'))
168192
}
169193
}
170194
return currentProfile

src/main/utils/ipc.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ import {
2323
getCurrentProfileItem,
2424
getProfileItem,
2525
addProfileItem,
26-
removeProfileItem
26+
removeProfileItem,
27+
changeCurrentProfile,
28+
getProfileStr,
29+
setProfileStr,
30+
updateProfileItem
2731
} from '../config'
2832
import { isEncryptionAvailable, restartCore } from '../core/manager'
2933
import { triggerSysProxy } from '../resolve/sysproxy'
30-
import { changeCurrentProfile } from '../config/profile'
3134

3235
export function registerIpcMainHandlers(): void {
3336
ipcMain.handle('mihomoVersion', mihomoVersion)
@@ -52,6 +55,9 @@ export function registerIpcMainHandlers(): void {
5255
ipcMain.handle('getProfileConfig', (_e, force) => getProfileConfig(force))
5356
ipcMain.handle('getCurrentProfileItem', getCurrentProfileItem)
5457
ipcMain.handle('getProfileItem', (_e, id) => getProfileItem(id))
58+
ipcMain.handle('getProfileStr', (_e, id) => getProfileStr(id))
59+
ipcMain.handle('setProfileStr', (_e, id, str) => setProfileStr(id, str))
60+
ipcMain.handle('updateProfileItem', (_e, item) => updateProfileItem(item))
5561
ipcMain.handle('changeCurrentProfile', (_e, id) => changeCurrentProfile(id))
5662
ipcMain.handle('addProfileItem', (_e, item) => addProfileItem(item))
5763
ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react'
2+
import React, { useEffect, useState } from 'react'
3+
import MonacoEditor, { monaco } from 'react-monaco-editor'
4+
import { useTheme } from 'next-themes'
5+
import { getProfileStr, setProfileStr } from '@renderer/utils/ipc'
6+
interface Props {
7+
id: string
8+
onClose: () => void
9+
}
10+
const EditFileModal: React.FC<Props> = (props) => {
11+
const { id, onClose } = props
12+
const [currData, setCurrData] = useState('')
13+
const { theme } = useTheme()
14+
15+
const editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor): void => {
16+
window.electron.ipcRenderer.on('resize', () => {
17+
editor.layout()
18+
})
19+
}
20+
21+
const editorWillUnmount = (editor: monaco.editor.IStandaloneCodeEditor): void => {
22+
window.electron.ipcRenderer.removeAllListeners('resize')
23+
editor.dispose()
24+
}
25+
26+
const getContent = async (): Promise<void> => {
27+
setCurrData(await getProfileStr(id))
28+
}
29+
30+
useEffect(() => {
31+
getContent()
32+
}, [])
33+
34+
return (
35+
<Modal size="5xl" hideCloseButton isOpen={true} scrollBehavior="inside">
36+
<ModalContent className="h-full w-[calc(100%-100px)]">
37+
<ModalHeader className="flex">编辑订阅</ModalHeader>
38+
<ModalBody className="h-full">
39+
<MonacoEditor
40+
height="100%"
41+
language="yaml"
42+
value={currData}
43+
theme={theme === 'dark' ? 'vs-dark' : 'vs'}
44+
options={{
45+
minimap: {
46+
enabled: false
47+
},
48+
mouseWheelZoom: true,
49+
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"`,
50+
fontLigatures: true, // 连字符
51+
smoothScrolling: true // 平滑滚动
52+
}}
53+
editorDidMount={editorDidMount}
54+
editorWillUnmount={editorWillUnmount}
55+
onChange={(value) => setCurrData(value)}
56+
/>
57+
</ModalBody>
58+
<ModalFooter>
59+
<Button variant="light" onPress={onClose}>
60+
取消
61+
</Button>
62+
<Button
63+
color="primary"
64+
onPress={async () => {
65+
await setProfileStr(id, currData)
66+
onClose()
67+
}}
68+
>
69+
确认
70+
</Button>
71+
</ModalFooter>
72+
</ModalContent>
73+
</Modal>
74+
)
75+
}
76+
77+
export default EditFileModal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
Modal,
3+
ModalContent,
4+
ModalHeader,
5+
ModalBody,
6+
ModalFooter,
7+
Button,
8+
Input
9+
} from '@nextui-org/react'
10+
import React, { useState } from 'react'
11+
import SettingItem from '../base/base-setting-item'
12+
interface Props {
13+
item: IProfileItem
14+
updateProfileItem: (item: IProfileItem) => Promise<void>
15+
onClose: () => void
16+
}
17+
const EditInfoModal: React.FC<Props> = (props) => {
18+
const { item, updateProfileItem, onClose } = props
19+
const [values, setValues] = useState(item)
20+
21+
const onSave = async (): Promise<void> => {
22+
await updateProfileItem(values)
23+
onClose()
24+
}
25+
26+
return (
27+
<Modal hideCloseButton isOpen={true} scrollBehavior="inside">
28+
<ModalContent>
29+
<ModalHeader className="flex">编辑信息</ModalHeader>
30+
<ModalBody>
31+
<SettingItem title="名称">
32+
<Input
33+
size="sm"
34+
className="w-[200px]"
35+
value={values.name}
36+
onValueChange={(v) => {
37+
setValues({ ...values, name: v })
38+
}}
39+
/>
40+
</SettingItem>
41+
{values.url && (
42+
<SettingItem title="订阅地址">
43+
<Input
44+
size="sm"
45+
className="w-[200px]"
46+
value={values.url}
47+
onValueChange={(v) => {
48+
setValues({ ...values, url: v })
49+
}}
50+
/>
51+
</SettingItem>
52+
)}
53+
54+
<SettingItem title="更新间隔(分钟)">
55+
<Input
56+
size="sm"
57+
type="number"
58+
className="w-[200px]"
59+
value={values.interval?.toString() ?? ''}
60+
onValueChange={(v) => {
61+
setValues({ ...values, interval: parseInt(v) })
62+
}}
63+
/>
64+
</SettingItem>
65+
</ModalBody>
66+
<ModalFooter>
67+
<Button variant="light" onPress={onClose}>
68+
取消
69+
</Button>
70+
<Button color="primary" onPress={onSave}>
71+
保存
72+
</Button>
73+
</ModalFooter>
74+
</ModalContent>
75+
</Modal>
76+
)
77+
}
78+
79+
export default EditInfoModal

0 commit comments

Comments
 (0)