From 179a87d4bf518ce29fcde162c5eafa74474e4867 Mon Sep 17 00:00:00 2001 From: pompurin404 Date: Mon, 5 Aug 2024 15:28:39 +0800 Subject: [PATCH] update profile --- src/main/config/index.ts | 6 +- src/main/config/profile.ts | 34 +++- src/main/utils/ipc.ts | 10 +- .../components/profiles/edit-file-modal.tsx | 77 +++++++++ .../components/profiles/edit-info-modal.tsx | 79 +++++++++ .../src/components/profiles/profile-item.tsx | 151 ++++++++++++------ .../src/components/sider/profile-card.tsx | 24 ++- src/renderer/src/hooks/use-profile-config.tsx | 8 + src/renderer/src/pages/profiles.tsx | 3 + src/renderer/src/utils/ipc.ts | 12 ++ 10 files changed, 343 insertions(+), 61 deletions(-) create mode 100644 src/renderer/src/components/profiles/edit-file-modal.tsx create mode 100644 src/renderer/src/components/profiles/edit-info-modal.tsx diff --git a/src/main/config/index.ts b/src/main/config/index.ts index 29c91b1..89223e2 100644 --- a/src/main/config/index.ts +++ b/src/main/config/index.ts @@ -7,5 +7,9 @@ export { getProfileConfig, addProfileItem, removeProfileItem, - createProfile + createProfile, + getProfileStr, + setProfileStr, + changeCurrentProfile, + updateProfileItem } from './profile' diff --git a/src/main/config/profile.ts b/src/main/config/profile.ts index a6f3b81..d10738d 100644 --- a/src/main/config/profile.ts +++ b/src/main/config/profile.ts @@ -38,9 +38,21 @@ export async function changeCurrentProfile(id: string): Promise { } } +export async function updateProfileItem(item: IProfileItem): Promise { + const index = profileConfig.items.findIndex((i) => i.id === item.id) + profileConfig.items[index] = item + fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig)) + window?.webContents.send('profileConfigUpdated') +} + export async function addProfileItem(item: Partial): Promise { const newItem = await createProfile(item) - profileConfig.items.push(newItem) + if (profileConfig.items.find((i) => i.id === newItem.id)) { + updateProfileItem(newItem) + } else { + profileConfig.items.push(newItem) + } + if (!getProfileConfig().current) { changeCurrentProfile(newItem.id) } @@ -134,7 +146,7 @@ export async function createProfile(item: Partial): Promise): Promise): Promise { if (force || !currentProfile) { const current = getProfileConfig().current if (current) { - currentProfile = yaml.parse(fs.readFileSync(profilePath(current), 'utf-8')) + currentProfile = yaml.parse(getProfileStr(current)) } else { - currentProfile = yaml.parse(fs.readFileSync(profilePath('default'), 'utf-8')) + currentProfile = yaml.parse(getProfileStr('default')) } } return currentProfile diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index ad6479a..6311823 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -23,11 +23,14 @@ import { getCurrentProfileItem, getProfileItem, addProfileItem, - removeProfileItem + removeProfileItem, + changeCurrentProfile, + getProfileStr, + setProfileStr, + updateProfileItem } from '../config' import { isEncryptionAvailable, restartCore } from '../core/manager' import { triggerSysProxy } from '../resolve/sysproxy' -import { changeCurrentProfile } from '../config/profile' export function registerIpcMainHandlers(): void { ipcMain.handle('mihomoVersion', mihomoVersion) @@ -52,6 +55,9 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('getProfileConfig', (_e, force) => getProfileConfig(force)) ipcMain.handle('getCurrentProfileItem', getCurrentProfileItem) ipcMain.handle('getProfileItem', (_e, id) => getProfileItem(id)) + ipcMain.handle('getProfileStr', (_e, id) => getProfileStr(id)) + ipcMain.handle('setProfileStr', (_e, id, str) => setProfileStr(id, str)) + ipcMain.handle('updateProfileItem', (_e, item) => updateProfileItem(item)) ipcMain.handle('changeCurrentProfile', (_e, id) => changeCurrentProfile(id)) ipcMain.handle('addProfileItem', (_e, item) => addProfileItem(item)) ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id)) diff --git a/src/renderer/src/components/profiles/edit-file-modal.tsx b/src/renderer/src/components/profiles/edit-file-modal.tsx new file mode 100644 index 0000000..703b7bd --- /dev/null +++ b/src/renderer/src/components/profiles/edit-file-modal.tsx @@ -0,0 +1,77 @@ +import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react' +import React, { useEffect, useState } from 'react' +import MonacoEditor, { monaco } from 'react-monaco-editor' +import { useTheme } from 'next-themes' +import { getProfileStr, setProfileStr } from '@renderer/utils/ipc' +interface Props { + id: string + onClose: () => void +} +const EditFileModal: React.FC = (props) => { + const { id, onClose } = props + const [currData, setCurrData] = useState('') + const { theme } = useTheme() + + const editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor): void => { + window.electron.ipcRenderer.on('resize', () => { + editor.layout() + }) + } + + const editorWillUnmount = (editor: monaco.editor.IStandaloneCodeEditor): void => { + window.electron.ipcRenderer.removeAllListeners('resize') + editor.dispose() + } + + const getContent = async (): Promise => { + setCurrData(await getProfileStr(id)) + } + + useEffect(() => { + getContent() + }, []) + + return ( + + + 编辑订阅 + + setCurrData(value)} + /> + + + + + + + + ) +} + +export default EditFileModal diff --git a/src/renderer/src/components/profiles/edit-info-modal.tsx b/src/renderer/src/components/profiles/edit-info-modal.tsx new file mode 100644 index 0000000..c9e1f72 --- /dev/null +++ b/src/renderer/src/components/profiles/edit-info-modal.tsx @@ -0,0 +1,79 @@ +import { + Modal, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, + Button, + Input +} from '@nextui-org/react' +import React, { useState } from 'react' +import SettingItem from '../base/base-setting-item' +interface Props { + item: IProfileItem + updateProfileItem: (item: IProfileItem) => Promise + onClose: () => void +} +const EditInfoModal: React.FC = (props) => { + const { item, updateProfileItem, onClose } = props + const [values, setValues] = useState(item) + + const onSave = async (): Promise => { + await updateProfileItem(values) + onClose() + } + + return ( + + + 编辑信息 + + + { + setValues({ ...values, name: v }) + }} + /> + + {values.url && ( + + { + setValues({ ...values, url: v }) + }} + /> + + )} + + + { + setValues({ ...values, interval: parseInt(v) }) + }} + /> + + + + + + + + + ) +} + +export default EditInfoModal diff --git a/src/renderer/src/components/profiles/profile-item.tsx b/src/renderer/src/components/profiles/profile-item.tsx index 8044c5e..abd0d6e 100644 --- a/src/renderer/src/components/profiles/profile-item.tsx +++ b/src/renderer/src/components/profiles/profile-item.tsx @@ -12,11 +12,15 @@ import { import { calcPercent, calcTraffic } from '@renderer/utils/calc' import { IoMdMore, IoMdRefresh } from 'react-icons/io' import dayjs from 'dayjs' -import React, { Key, useMemo } from 'react' +import React, { Key, useMemo, useState } from 'react' +import EditFileModal from './edit-file-modal' +import EditInfoModal from './edit-info-modal' interface Props { info: IProfileItem isCurrent: boolean + addProfileItem: (item: Partial) => Promise + updateProfileItem: (item: IProfileItem) => Promise removeProfileItem: (id: string) => Promise mutateProfileConfig: () => void onClick: () => Promise @@ -30,15 +34,33 @@ interface MenuItem { className: string } const ProfileItem: React.FC = (props) => { - const { info, removeProfileItem, mutateProfileConfig, onClick, isCurrent } = props + const { + info, + addProfileItem, + removeProfileItem, + mutateProfileConfig, + updateProfileItem, + onClick, + isCurrent + } = props const extra = info?.extra const usage = (extra?.upload ?? 0) + (extra?.download ?? 0) const total = extra?.total ?? 0 + const [updating, setUpdating] = useState(false) + const [openInfo, setOpenInfo] = useState(false) + const [openFile, setOpenFile] = useState(false) const menuItems: MenuItem[] = useMemo(() => { const list = [ { - key: 'edit', + key: 'edit-info', + label: '编辑信息', + showDivider: false, + color: 'default', + className: '' + } as MenuItem, + { + key: 'edit-file', label: '编辑文件', showDivider: true, color: 'default', @@ -66,8 +88,14 @@ const ProfileItem: React.FC = (props) => { const onMenuAction = (key: Key): void => { switch (key) { - case 'edit': + case 'edit-info': { + setOpenInfo(true) break + } + case 'edit-file': { + setOpenFile(true) + break + } case 'delete': { removeProfileItem(info.id) mutateProfileConfig() @@ -82,52 +110,77 @@ const ProfileItem: React.FC = (props) => { } return ( - - -
-

- {info?.name} -

-
- - - - - - - {menuItems.map((item) => ( - - {item.label} - - ))} - - + <> + {openFile && setOpenFile(false)} />} + {openInfo && ( + setOpenInfo(false)} + updateProfileItem={updateProfileItem} + /> + )} + + +
+

+ {info?.name} +

+
+ + + + + + + {menuItems.map((item) => ( + + {item.label} + + ))} + + +
-
-
- {extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined} - {dayjs(info.updated).fromNow()} -
- - - {extra && ( - - )} - - +
+ {extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined} + {dayjs(info.updated).fromNow()} +
+ + + {extra && ( + + )} + + + ) } diff --git a/src/renderer/src/components/sider/profile-card.tsx b/src/renderer/src/components/sider/profile-card.tsx index 6c528b3..e3393e6 100644 --- a/src/renderer/src/components/sider/profile-card.tsx +++ b/src/renderer/src/components/sider/profile-card.tsx @@ -6,6 +6,7 @@ import { IoMdRefresh } from 'react-icons/io' import relativeTime from 'dayjs/plugin/relativeTime' import 'dayjs/locale/zh-cn' import dayjs from 'dayjs' +import { useState } from 'react' dayjs.extend(relativeTime) dayjs.locale('zh-cn') @@ -14,8 +15,8 @@ const ProfileCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() const match = location.pathname.includes('/profiles') - - const { profileConfig } = useProfileConfig() + const [updating, setUpdating] = useState(false) + const { profileConfig, addProfileItem } = useProfileConfig() const { current, items } = profileConfig ?? {} const info = items?.find((item) => item.id === current) ?? { id: 'default', @@ -39,8 +40,23 @@ const ProfileCard: React.FC = () => {

{info?.name}

-
diff --git a/src/renderer/src/hooks/use-profile-config.tsx b/src/renderer/src/hooks/use-profile-config.tsx index 3bdadc3..ce47f54 100644 --- a/src/renderer/src/hooks/use-profile-config.tsx +++ b/src/renderer/src/hooks/use-profile-config.tsx @@ -3,6 +3,7 @@ import { getProfileConfig, addProfileItem as add, removeProfileItem as remove, + updateProfileItem as update, changeCurrentProfile as change } from '@renderer/utils/ipc' import { useEffect } from 'react' @@ -11,6 +12,7 @@ interface RetuenType { profileConfig: IProfileConfig | undefined mutateProfileConfig: () => void addProfileItem: (item: Partial) => Promise + updateProfileItem: (item: IProfileItem) => Promise removeProfileItem: (id: string) => Promise changeCurrentProfile: (id: string) => Promise } @@ -30,6 +32,11 @@ export const useProfileConfig = (): RetuenType => { mutateProfileConfig() } + const updateProfileItem = async (item: IProfileItem): Promise => { + await update(item) + mutateProfileConfig() + } + const changeCurrentProfile = async (id: string): Promise => { await change(id) mutateProfileConfig() @@ -49,6 +56,7 @@ export const useProfileConfig = (): RetuenType => { mutateProfileConfig, addProfileItem, removeProfileItem, + updateProfileItem, changeCurrentProfile } } diff --git a/src/renderer/src/pages/profiles.tsx b/src/renderer/src/pages/profiles.tsx index 750057c..2bfd545 100644 --- a/src/renderer/src/pages/profiles.tsx +++ b/src/renderer/src/pages/profiles.tsx @@ -9,6 +9,7 @@ const Profiles: React.FC = () => { const { profileConfig, addProfileItem, + updateProfileItem, removeProfileItem, changeCurrentProfile, mutateProfileConfig @@ -61,8 +62,10 @@ const Profiles: React.FC = () => { { await changeCurrentProfile(item.id) diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index 3e027bb..d4a065c 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -99,6 +99,18 @@ export async function removeProfileItem(id: string): Promise { return await window.electron.ipcRenderer.invoke('removeProfileItem', id) } +export async function updateProfileItem(item: IProfileItem): Promise { + return await window.electron.ipcRenderer.invoke('updateProfileItem', item) +} + +export async function getProfileStr(id: string): Promise { + return await window.electron.ipcRenderer.invoke('getProfileStr', id) +} + +export async function setProfileStr(id: string, str: string): Promise { + return await window.electron.ipcRenderer.invoke('setProfileStr', id, str) +} + export async function restartCore(): Promise { return await window.electron.ipcRenderer.invoke('restartCore') }