mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-28 05:30:29 +08:00
feat: optimize and improve subscription switching
This commit is contained in:
parent
2e4090460d
commit
866cdb4661
@ -37,15 +37,21 @@ export async function getProfileItem(id: string | undefined): Promise<IProfileIt
|
|||||||
export async function changeCurrentProfile(id: string): Promise<void> {
|
export async function changeCurrentProfile(id: string): Promise<void> {
|
||||||
const config = await getProfileConfig()
|
const config = await getProfileConfig()
|
||||||
const current = config.current
|
const current = config.current
|
||||||
|
|
||||||
|
if (current === id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
config.current = id
|
config.current = id
|
||||||
await setProfileConfig(config)
|
await setProfileConfig(config)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// 如果重启失败,恢复原来的配置
|
||||||
config.current = current
|
config.current = current
|
||||||
throw e
|
|
||||||
} finally {
|
|
||||||
await setProfileConfig(config)
|
await setProfileConfig(config)
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -212,7 +212,12 @@ export async function restartCore(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
await startCore()
|
await startCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dialog.showErrorBox(i18next.t('mihomo.error.coreStartFailed'), `${e}`)
|
// 记录错误到日志而不是显示阻塞对话框
|
||||||
|
await writeFile(logPath(), `[Manager]: restart core failed, ${e}\n`, {
|
||||||
|
flag: 'a'
|
||||||
|
})
|
||||||
|
// 重新抛出错误,让调用者处理
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
import { calcPercent, calcTraffic } from '@renderer/utils/calc'
|
import { calcPercent, calcTraffic } from '@renderer/utils/calc'
|
||||||
import { IoMdMore, IoMdRefresh } from 'react-icons/io'
|
import { IoMdMore, IoMdRefresh } from 'react-icons/io'
|
||||||
import dayjs from '@renderer/utils/dayjs'
|
import dayjs from '@renderer/utils/dayjs'
|
||||||
import React, { Key, useEffect, useMemo, useState } from 'react'
|
import React, { Key, useMemo, useState } from 'react'
|
||||||
import EditFileModal from './edit-file-modal'
|
import EditFileModal from './edit-file-modal'
|
||||||
import EditInfoModal from './edit-info-modal'
|
import EditInfoModal from './edit-info-modal'
|
||||||
import { useSortable } from '@dnd-kit/sortable'
|
import { useSortable } from '@dnd-kit/sortable'
|
||||||
@ -72,7 +72,8 @@ const ProfileItem: React.FC<Props> = (props) => {
|
|||||||
id: info.id
|
id: info.id
|
||||||
})
|
})
|
||||||
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
|
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
|
||||||
const [disableSelect, setDisableSelect] = useState(false)
|
const [isActuallyDragging, setIsActuallyDragging] = useState(false)
|
||||||
|
const [clickStartPos, setClickStartPos] = useState<{ x: number; y: number } | null>(null)
|
||||||
|
|
||||||
const menuItems: MenuItem[] = useMemo(() => {
|
const menuItems: MenuItem[] = useMemo(() => {
|
||||||
const list = [
|
const list = [
|
||||||
@ -150,19 +151,35 @@ const ProfileItem: React.FC<Props> = (props) => {
|
|||||||
setDropdownOpen(true)
|
setDropdownOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
// 智能区分点击和拖拽的事件处理
|
||||||
if (isDragging) {
|
const handleMouseDown = (e: React.MouseEvent) => {
|
||||||
setTimeout(() => {
|
setClickStartPos({ x: e.clientX, y: e.clientY })
|
||||||
setDisableSelect(true)
|
setIsActuallyDragging(false)
|
||||||
}, 200)
|
}
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
const handleMouseMove = (e: React.MouseEvent) => {
|
||||||
setDisableSelect(false)
|
if (clickStartPos) {
|
||||||
}, 200)
|
const dx = e.clientX - clickStartPos.x
|
||||||
|
const dy = e.clientY - clickStartPos.y
|
||||||
|
// 移动距离超过 5px 认为是拖拽
|
||||||
|
if (dx * dx + dy * dy > 25) {
|
||||||
|
setIsActuallyDragging(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [isDragging])
|
}
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
// 如果没有拖拽,则处理为点击事件
|
||||||
|
if (!isActuallyDragging && !isDragging && clickStartPos) {
|
||||||
|
setSelecting(true)
|
||||||
|
onPress().finally(() => {
|
||||||
|
setSelecting(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setClickStartPos(null)
|
||||||
|
setTimeout(() => setIsActuallyDragging(false), 100)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -186,18 +203,19 @@ const ProfileItem: React.FC<Props> = (props) => {
|
|||||||
<Card
|
<Card
|
||||||
as="div"
|
as="div"
|
||||||
fullWidth
|
fullWidth
|
||||||
isPressable
|
isPressable={false}
|
||||||
onPress={() => {
|
|
||||||
if (disableSelect) return
|
|
||||||
setSelecting(true)
|
|
||||||
onPress().finally(() => {
|
|
||||||
setSelecting(false)
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
className={`${isCurrent ? 'bg-primary' : ''} ${selecting ? 'blur-sm' : ''}`}
|
className={`${isCurrent ? 'bg-primary' : ''} ${selecting ? 'blur-sm' : ''} cursor-pointer`}
|
||||||
>
|
>
|
||||||
<div ref={setNodeRef} {...attributes} {...listeners} className="w-full h-full">
|
<div
|
||||||
|
ref={setNodeRef}
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}
|
||||||
|
className="w-full h-full"
|
||||||
|
onMouseDownCapture={handleMouseDown}
|
||||||
|
onMouseMoveCapture={handleMouseMove}
|
||||||
|
onMouseUpCapture={handleMouseUp}
|
||||||
|
>
|
||||||
<CardBody className="pb-1">
|
<CardBody className="pb-1">
|
||||||
<div className="flex justify-between h-[32px]">
|
<div className="flex justify-between h-[32px]">
|
||||||
<h3
|
<h3
|
||||||
|
|||||||
@ -71,13 +71,34 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
}
|
}
|
||||||
|
|
||||||
const changeCurrentProfile = async (id: string): Promise<void> => {
|
const changeCurrentProfile = async (id: string): Promise<void> => {
|
||||||
|
if (profileConfig?.current === id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 乐观更新:立即更新 UI 状态,提供即时反馈
|
||||||
|
if (profileConfig) {
|
||||||
|
const optimisticUpdate = { ...profileConfig, current: id }
|
||||||
|
mutateProfileConfig(optimisticUpdate, false)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await change(id)
|
// 异步执行后台切换,不阻塞 UI
|
||||||
|
change(id).then(() => {
|
||||||
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
|
mutateProfileConfig()
|
||||||
|
}).catch((e) => {
|
||||||
|
const errorMsg = e?.message || String(e)
|
||||||
|
// 处理 IPC 超时错误
|
||||||
|
if (errorMsg.includes('reply was never sent')) {
|
||||||
|
setTimeout(() => mutateProfileConfig(), 1000)
|
||||||
|
} else {
|
||||||
|
alert(`切换 Profile 失败: ${errorMsg}`)
|
||||||
|
mutateProfileConfig()
|
||||||
|
}
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
alert(`切换 Profile 失败: ${e}`)
|
||||||
} finally {
|
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user