mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2026-02-10 19:50:28 +08:00
Compare commits
2 Commits
98c8280d48
...
2467306903
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2467306903 | ||
|
|
573be5501e |
@ -13,6 +13,8 @@ import { join } from 'path'
|
|||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
import { mihomoUpgradeConfig } from '../core/mihomoApi'
|
import { mihomoUpgradeConfig } from '../core/mihomoApi'
|
||||||
|
|
||||||
|
import i18next from 'i18next'
|
||||||
|
|
||||||
let profileConfig: IProfileConfig // profile.yaml
|
let profileConfig: IProfileConfig // profile.yaml
|
||||||
// 最终选中订阅ID
|
// 最终选中订阅ID
|
||||||
let targetProfileId: string | null = null
|
let targetProfileId: string | null = null
|
||||||
@ -33,7 +35,8 @@ export async function setProfileConfig(config: IProfileConfig): Promise<void> {
|
|||||||
|
|
||||||
export async function getProfileItem(id: string | undefined): Promise<IProfileItem | undefined> {
|
export async function getProfileItem(id: string | undefined): Promise<IProfileItem | undefined> {
|
||||||
const { items } = await getProfileConfig()
|
const { items } = await getProfileConfig()
|
||||||
if (!id || id === 'default') return { id: 'default', type: 'local', name: '空白订阅' }
|
if (!id || id === 'default')
|
||||||
|
return { id: 'default', type: 'local', name: i18next.t('profiles.emptyProfile') }
|
||||||
return items.find((item) => item.id === id)
|
return items.find((item) => item.id === id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +129,13 @@ export async function removeProfileItem(id: string): Promise<void> {
|
|||||||
|
|
||||||
export async function getCurrentProfileItem(): Promise<IProfileItem> {
|
export async function getCurrentProfileItem(): Promise<IProfileItem> {
|
||||||
const { current } = await getProfileConfig()
|
const { current } = await getProfileConfig()
|
||||||
return (await getProfileItem(current)) || { id: 'default', type: 'local', name: '空白订阅' }
|
return (
|
||||||
|
(await getProfileItem(current)) || {
|
||||||
|
id: 'default',
|
||||||
|
type: 'local',
|
||||||
|
name: i18next.t('profiles.emptyProfile')
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FetchOptions {
|
interface FetchOptions {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { exec, execSync, spawn } from 'child_process'
|
|||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
import { appLogger } from '../utils/logger'
|
import { appLogger } from '../utils/logger'
|
||||||
import { checkAdminPrivileges } from '../core/manager'
|
import { checkAdminPrivileges } from '../core/manager'
|
||||||
|
import i18next from 'i18next'
|
||||||
|
|
||||||
export async function checkUpdate(): Promise<IAppVersion | undefined> {
|
export async function checkUpdate(): Promise<IAppVersion | undefined> {
|
||||||
const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig()
|
const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig()
|
||||||
@ -68,7 +69,7 @@ export async function downloadAndInstallUpdate(version: string): Promise<void> {
|
|||||||
file = file.replace('-setup.exe', '-portable.7z')
|
file = file.replace('-setup.exe', '-portable.7z')
|
||||||
}
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
throw new Error('不支持自动更新,请手动下载更新')
|
throw new Error(i18next.t('common.error.autoUpdateNotSupported'))
|
||||||
}
|
}
|
||||||
if (process.platform === 'win32' && parseInt(os.release()) < 10) {
|
if (process.platform === 'win32' && parseInt(os.release()) < 10) {
|
||||||
file = file.replace('windows', 'win7')
|
file = file.replace('windows', 'win7')
|
||||||
|
|||||||
@ -11,10 +11,11 @@ import {
|
|||||||
profilePath,
|
profilePath,
|
||||||
resourcesDir
|
resourcesDir
|
||||||
} from '../utils/dirs'
|
} from '../utils/dirs'
|
||||||
|
import i18next from 'i18next'
|
||||||
|
|
||||||
export function getFilePath(ext: string[]): string[] | undefined {
|
export function getFilePath(ext: string[]): string[] | undefined {
|
||||||
return dialog.showOpenDialogSync({
|
return dialog.showOpenDialogSync({
|
||||||
title: '选择订阅文件',
|
title: i18next.t('common.dialog.selectSubscriptionFile'),
|
||||||
filters: [{ name: `${ext} file`, extensions: ext }],
|
filters: [{ name: `${ext} file`, extensions: ext }],
|
||||||
properties: ['openFile']
|
properties: ['openFile']
|
||||||
})
|
})
|
||||||
|
|||||||
@ -126,23 +126,31 @@ import { addProfileUpdater, removeProfileUpdater } from '../core/profileUpdater'
|
|||||||
import { readFile, writeFile } from 'fs/promises'
|
import { readFile, writeFile } from 'fs/promises'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function wrapAsync<T extends (...args: any[]) => Promise<any>>(
|
type AsyncFn = (...args: any[]) => Promise<any>
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
type SyncFn = (...args: any[]) => any
|
||||||
|
|
||||||
|
function wrapAsync<T extends AsyncFn>(
|
||||||
fn: T
|
fn: T
|
||||||
): (...args: Parameters<T>) => Promise<ReturnType<T> | { invokeError: unknown }> {
|
): (...args: Parameters<T>) => Promise<ReturnType<T> | { invokeError: unknown }> {
|
||||||
return async (...args) => {
|
return async (...args) => {
|
||||||
try {
|
try {
|
||||||
return await fn(...args)
|
return await fn(...args)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && typeof e === 'object') {
|
if (e && typeof e === 'object' && 'message' in e) {
|
||||||
if ('message' in e) {
|
return { invokeError: e.message }
|
||||||
return { invokeError: e.message }
|
|
||||||
}
|
|
||||||
return { invokeError: JSON.stringify(e) }
|
|
||||||
}
|
}
|
||||||
if (typeof e === 'string') {
|
return { invokeError: typeof e === 'string' ? e : 'Unknown Error' }
|
||||||
return { invokeError: e }
|
}
|
||||||
}
|
}
|
||||||
return { invokeError: 'Unknown Error' }
|
}
|
||||||
|
|
||||||
|
function registerHandlers(handlers: Record<string, AsyncFn | SyncFn>, async = true): void {
|
||||||
|
for (const [channel, handler] of Object.entries(handlers)) {
|
||||||
|
if (async) {
|
||||||
|
ipcMain.handle(channel, (_e, ...args) => wrapAsync(handler as AsyncFn)(...args))
|
||||||
|
} else {
|
||||||
|
ipcMain.handle(channel, (_e, ...args) => (handler as SyncFn)(...args))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,232 +198,160 @@ async function setTitleBarOverlay(overlay: Electron.TitleBarOverlayOptions): Pro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerIpcMainHandlers(): void {
|
const asyncHandlers: Record<string, AsyncFn> = {
|
||||||
// Mihomo API
|
// Mihomo API
|
||||||
ipcMain.handle('mihomoVersion', wrapAsync(mihomoVersion))
|
mihomoVersion,
|
||||||
ipcMain.handle('mihomoCloseConnection', (_e, id: string) => wrapAsync(mihomoCloseConnection)(id))
|
mihomoCloseConnection,
|
||||||
ipcMain.handle('mihomoCloseAllConnections', wrapAsync(mihomoCloseAllConnections))
|
mihomoCloseAllConnections,
|
||||||
ipcMain.handle('mihomoRules', wrapAsync(mihomoRules))
|
mihomoRules,
|
||||||
ipcMain.handle('mihomoProxies', wrapAsync(mihomoProxies))
|
mihomoProxies,
|
||||||
ipcMain.handle('mihomoGroups', wrapAsync(mihomoGroups))
|
mihomoGroups,
|
||||||
ipcMain.handle('mihomoProxyProviders', wrapAsync(mihomoProxyProviders))
|
mihomoProxyProviders,
|
||||||
ipcMain.handle('mihomoUpdateProxyProviders', (_e, name: string) =>
|
mihomoUpdateProxyProviders,
|
||||||
wrapAsync(mihomoUpdateProxyProviders)(name)
|
mihomoRuleProviders,
|
||||||
)
|
mihomoUpdateRuleProviders,
|
||||||
ipcMain.handle('mihomoRuleProviders', wrapAsync(mihomoRuleProviders))
|
mihomoChangeProxy,
|
||||||
ipcMain.handle('mihomoUpdateRuleProviders', (_e, name: string) =>
|
mihomoUnfixedProxy,
|
||||||
wrapAsync(mihomoUpdateRuleProviders)(name)
|
mihomoUpgradeGeo,
|
||||||
)
|
mihomoUpgrade,
|
||||||
ipcMain.handle('mihomoChangeProxy', (_e, group: string, proxy: string) =>
|
mihomoUpgradeUI,
|
||||||
wrapAsync(mihomoChangeProxy)(group, proxy)
|
mihomoUpgradeConfig,
|
||||||
)
|
mihomoProxyDelay,
|
||||||
ipcMain.handle('mihomoUnfixedProxy', (_e, group: string) => wrapAsync(mihomoUnfixedProxy)(group))
|
mihomoGroupDelay,
|
||||||
ipcMain.handle('mihomoUpgradeGeo', wrapAsync(mihomoUpgradeGeo))
|
patchMihomoConfig,
|
||||||
ipcMain.handle('mihomoUpgrade', wrapAsync(mihomoUpgrade))
|
mihomoSmartGroupWeights,
|
||||||
ipcMain.handle('mihomoUpgradeUI', wrapAsync(mihomoUpgradeUI))
|
mihomoSmartFlushCache,
|
||||||
ipcMain.handle('mihomoUpgradeConfig', wrapAsync(mihomoUpgradeConfig))
|
|
||||||
ipcMain.handle('mihomoProxyDelay', (_e, proxy: string, url?: string) =>
|
|
||||||
wrapAsync(mihomoProxyDelay)(proxy, url)
|
|
||||||
)
|
|
||||||
ipcMain.handle('mihomoGroupDelay', (_e, group: string, url?: string) =>
|
|
||||||
wrapAsync(mihomoGroupDelay)(group, url)
|
|
||||||
)
|
|
||||||
ipcMain.handle('patchMihomoConfig', (_e, patch: Partial<IMihomoConfig>) =>
|
|
||||||
wrapAsync(patchMihomoConfig)(patch)
|
|
||||||
)
|
|
||||||
ipcMain.handle('mihomoSmartGroupWeights', (_e, groupName: string) =>
|
|
||||||
wrapAsync(mihomoSmartGroupWeights)(groupName)
|
|
||||||
)
|
|
||||||
ipcMain.handle('mihomoSmartFlushCache', (_e, configName?: string) =>
|
|
||||||
wrapAsync(mihomoSmartFlushCache)(configName)
|
|
||||||
)
|
|
||||||
|
|
||||||
// AutoRun
|
// AutoRun
|
||||||
ipcMain.handle('checkAutoRun', wrapAsync(checkAutoRun))
|
checkAutoRun,
|
||||||
ipcMain.handle('enableAutoRun', wrapAsync(enableAutoRun))
|
enableAutoRun,
|
||||||
ipcMain.handle('disableAutoRun', wrapAsync(disableAutoRun))
|
disableAutoRun,
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
ipcMain.handle('getAppConfig', (_e, force?: boolean) => wrapAsync(getAppConfig)(force))
|
getAppConfig,
|
||||||
ipcMain.handle('patchAppConfig', (_e, config: Partial<IAppConfig>) =>
|
patchAppConfig,
|
||||||
wrapAsync(patchAppConfig)(config)
|
getControledMihomoConfig,
|
||||||
)
|
patchControledMihomoConfig,
|
||||||
ipcMain.handle('getControledMihomoConfig', (_e, force?: boolean) =>
|
|
||||||
wrapAsync(getControledMihomoConfig)(force)
|
|
||||||
)
|
|
||||||
ipcMain.handle('patchControledMihomoConfig', (_e, config: Partial<IMihomoConfig>) =>
|
|
||||||
wrapAsync(patchControledMihomoConfig)(config)
|
|
||||||
)
|
|
||||||
ipcMain.handle('resetAppConfig', () => resetAppConfig())
|
|
||||||
|
|
||||||
// Profile
|
// Profile
|
||||||
ipcMain.handle('getProfileConfig', (_e, force?: boolean) => wrapAsync(getProfileConfig)(force))
|
getProfileConfig,
|
||||||
ipcMain.handle('setProfileConfig', (_e, config: IProfileConfig) =>
|
setProfileConfig,
|
||||||
wrapAsync(setProfileConfig)(config)
|
getCurrentProfileItem,
|
||||||
)
|
getProfileItem,
|
||||||
ipcMain.handle('getCurrentProfileItem', wrapAsync(getCurrentProfileItem))
|
getProfileStr,
|
||||||
ipcMain.handle('getProfileItem', (_e, id?: string) => wrapAsync(getProfileItem)(id))
|
setProfileStr,
|
||||||
ipcMain.handle('getProfileStr', (_e, id: string) => wrapAsync(getProfileStr)(id))
|
addProfileItem,
|
||||||
ipcMain.handle('setProfileStr', (_e, id: string, str: string) =>
|
removeProfileItem,
|
||||||
wrapAsync(setProfileStr)(id, str)
|
updateProfileItem,
|
||||||
)
|
changeCurrentProfile,
|
||||||
ipcMain.handle('addProfileItem', (_e, item: Partial<IProfileItem>) =>
|
addProfileUpdater,
|
||||||
wrapAsync(addProfileItem)(item)
|
removeProfileUpdater,
|
||||||
)
|
|
||||||
ipcMain.handle('removeProfileItem', (_e, id: string) => wrapAsync(removeProfileItem)(id))
|
|
||||||
ipcMain.handle('updateProfileItem', (_e, item: IProfileItem) => wrapAsync(updateProfileItem)(item))
|
|
||||||
ipcMain.handle('changeCurrentProfile', (_e, id: string) => wrapAsync(changeCurrentProfile)(id))
|
|
||||||
ipcMain.handle('addProfileUpdater', (_e, item: IProfileItem) => wrapAsync(addProfileUpdater)(item))
|
|
||||||
ipcMain.handle('removeProfileUpdater', (_e, id: string) => wrapAsync(removeProfileUpdater)(id))
|
|
||||||
|
|
||||||
// Override
|
// Override
|
||||||
ipcMain.handle('getOverrideConfig', (_e, force?: boolean) => wrapAsync(getOverrideConfig)(force))
|
getOverrideConfig,
|
||||||
ipcMain.handle('setOverrideConfig', (_e, config: IOverrideConfig) =>
|
setOverrideConfig,
|
||||||
wrapAsync(setOverrideConfig)(config)
|
getOverrideItem,
|
||||||
)
|
addOverrideItem,
|
||||||
ipcMain.handle('getOverrideItem', (_e, id: string) => wrapAsync(getOverrideItem)(id))
|
removeOverrideItem,
|
||||||
ipcMain.handle('addOverrideItem', (_e, item: Partial<IOverrideItem>) =>
|
updateOverrideItem,
|
||||||
wrapAsync(addOverrideItem)(item)
|
getOverride,
|
||||||
)
|
setOverride,
|
||||||
ipcMain.handle('removeOverrideItem', (_e, id: string) => wrapAsync(removeOverrideItem)(id))
|
|
||||||
ipcMain.handle('updateOverrideItem', (_e, item: IOverrideItem) =>
|
|
||||||
wrapAsync(updateOverrideItem)(item)
|
|
||||||
)
|
|
||||||
ipcMain.handle('getOverride', (_e, id: string, ext: 'js' | 'yaml' | 'log') =>
|
|
||||||
wrapAsync(getOverride)(id, ext)
|
|
||||||
)
|
|
||||||
ipcMain.handle('setOverride', (_e, id: string, ext: 'js' | 'yaml', str: string) =>
|
|
||||||
wrapAsync(setOverride)(id, ext, str)
|
|
||||||
)
|
|
||||||
|
|
||||||
// File
|
// File
|
||||||
ipcMain.handle('getFileStr', (_e, filePath: string) => wrapAsync(getFileStr)(filePath))
|
getFileStr,
|
||||||
ipcMain.handle('setFileStr', (_e, filePath: string, str: string) =>
|
setFileStr,
|
||||||
wrapAsync(setFileStr)(filePath, str)
|
convertMrsRuleset,
|
||||||
)
|
getRuntimeConfig,
|
||||||
ipcMain.handle('convertMrsRuleset', (_e, filePath: string, behavior: string) =>
|
getRuntimeConfigStr,
|
||||||
wrapAsync(convertMrsRuleset)(filePath, behavior)
|
getSmartOverrideContent,
|
||||||
)
|
getRuleStr,
|
||||||
ipcMain.handle('getRuntimeConfig', wrapAsync(getRuntimeConfig))
|
setRuleStr,
|
||||||
ipcMain.handle('getRuntimeConfigStr', wrapAsync(getRuntimeConfigStr))
|
readTextFile,
|
||||||
ipcMain.handle('getSmartOverrideContent', wrapAsync(getSmartOverrideContent))
|
|
||||||
ipcMain.handle('getRuleStr', (_e, id: string) => wrapAsync(getRuleStr)(id))
|
|
||||||
ipcMain.handle('setRuleStr', (_e, id: string, str: string) => wrapAsync(setRuleStr)(id, str))
|
|
||||||
ipcMain.handle('getFilePath', (_e, ext: string[]) => getFilePath(ext))
|
|
||||||
ipcMain.handle('readTextFile', (_e, filePath: string) => wrapAsync(readTextFile)(filePath))
|
|
||||||
ipcMain.handle('openFile', (_e, type: 'profile' | 'override', id: string, ext?: 'yaml' | 'js') =>
|
|
||||||
openFile(type, id, ext)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
ipcMain.handle('restartCore', wrapAsync(restartCore))
|
restartCore,
|
||||||
ipcMain.handle('startMonitor', (_e, detached?: boolean) => wrapAsync(startMonitor)(detached))
|
startMonitor,
|
||||||
ipcMain.handle('quitWithoutCore', wrapAsync(quitWithoutCore))
|
quitWithoutCore,
|
||||||
|
|
||||||
// System
|
// System
|
||||||
ipcMain.handle('triggerSysProxy', (_e, enable: boolean) => wrapAsync(triggerSysProxy)(enable))
|
triggerSysProxy,
|
||||||
ipcMain.handle('checkTunPermissions', wrapAsync(checkTunPermissions))
|
checkTunPermissions,
|
||||||
ipcMain.handle('grantTunPermissions', wrapAsync(grantTunPermissions))
|
grantTunPermissions,
|
||||||
ipcMain.handle('manualGrantCorePermition', wrapAsync(manualGrantCorePermition))
|
manualGrantCorePermition,
|
||||||
ipcMain.handle('checkAdminPrivileges', wrapAsync(checkAdminPrivileges))
|
checkAdminPrivileges,
|
||||||
ipcMain.handle('restartAsAdmin', (_e, forTun?: boolean) => wrapAsync(restartAsAdmin)(forTun))
|
restartAsAdmin,
|
||||||
ipcMain.handle('checkMihomoCorePermissions', wrapAsync(checkMihomoCorePermissions))
|
checkMihomoCorePermissions,
|
||||||
ipcMain.handle('requestTunPermissions', wrapAsync(requestTunPermissions))
|
requestTunPermissions,
|
||||||
ipcMain.handle('checkHighPrivilegeCore', wrapAsync(checkHighPrivilegeCore))
|
checkHighPrivilegeCore,
|
||||||
ipcMain.handle('showTunPermissionDialog', wrapAsync(showTunPermissionDialog))
|
showTunPermissionDialog,
|
||||||
ipcMain.handle('showErrorDialog', (_e, title: string, message: string) =>
|
showErrorDialog,
|
||||||
wrapAsync(showErrorDialog)(title, message)
|
openUWPTool,
|
||||||
)
|
setupFirewall,
|
||||||
ipcMain.handle('openUWPTool', wrapAsync(openUWPTool))
|
copyEnv,
|
||||||
ipcMain.handle('setupFirewall', wrapAsync(setupFirewall))
|
|
||||||
ipcMain.handle('getInterfaces', getInterfaces)
|
|
||||||
ipcMain.handle('setNativeTheme', (_e, theme: 'system' | 'light' | 'dark') => setNativeTheme(theme))
|
|
||||||
ipcMain.handle('copyEnv', (_e, type: 'bash' | 'cmd' | 'powershell') => wrapAsync(copyEnv)(type))
|
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
ipcMain.handle('checkUpdate', wrapAsync(checkUpdate))
|
checkUpdate,
|
||||||
ipcMain.handle('downloadAndInstallUpdate', (_e, version: string) =>
|
downloadAndInstallUpdate,
|
||||||
wrapAsync(downloadAndInstallUpdate)(version)
|
fetchMihomoTags,
|
||||||
)
|
installSpecificMihomoCore,
|
||||||
ipcMain.handle('getVersion', () => app.getVersion())
|
clearMihomoVersionCache,
|
||||||
ipcMain.handle('platform', () => process.platform)
|
|
||||||
ipcMain.handle('fetchMihomoTags', (_e, forceRefresh?: boolean) =>
|
|
||||||
wrapAsync(fetchMihomoTags)(forceRefresh)
|
|
||||||
)
|
|
||||||
ipcMain.handle('installSpecificMihomoCore', (_e, version: string) =>
|
|
||||||
wrapAsync(installSpecificMihomoCore)(version)
|
|
||||||
)
|
|
||||||
ipcMain.handle('clearMihomoVersionCache', wrapAsync(clearMihomoVersionCache))
|
|
||||||
|
|
||||||
// Backup
|
// Backup
|
||||||
ipcMain.handle('webdavBackup', wrapAsync(webdavBackup))
|
webdavBackup,
|
||||||
ipcMain.handle('webdavRestore', (_e, filename: string) => wrapAsync(webdavRestore)(filename))
|
webdavRestore,
|
||||||
ipcMain.handle('listWebdavBackups', wrapAsync(listWebdavBackups))
|
listWebdavBackups,
|
||||||
ipcMain.handle('webdavDelete', (_e, filename: string) => wrapAsync(webdavDelete)(filename))
|
webdavDelete,
|
||||||
ipcMain.handle('reinitWebdavBackupScheduler', wrapAsync(reinitScheduler))
|
reinitWebdavBackupScheduler: reinitScheduler,
|
||||||
ipcMain.handle('exportLocalBackup', wrapAsync(exportLocalBackup))
|
exportLocalBackup,
|
||||||
ipcMain.handle('importLocalBackup', wrapAsync(importLocalBackup))
|
importLocalBackup,
|
||||||
|
|
||||||
// SubStore
|
// SubStore
|
||||||
ipcMain.handle('startSubStoreFrontendServer', wrapAsync(startSubStoreFrontendServer))
|
startSubStoreFrontendServer,
|
||||||
ipcMain.handle('stopSubStoreFrontendServer', wrapAsync(stopSubStoreFrontendServer))
|
stopSubStoreFrontendServer,
|
||||||
ipcMain.handle('startSubStoreBackendServer', wrapAsync(startSubStoreBackendServer))
|
startSubStoreBackendServer,
|
||||||
ipcMain.handle('stopSubStoreBackendServer', wrapAsync(stopSubStoreBackendServer))
|
stopSubStoreBackendServer,
|
||||||
ipcMain.handle('downloadSubStore', wrapAsync(downloadSubStore))
|
downloadSubStore,
|
||||||
ipcMain.handle('subStorePort', () => subStorePort)
|
subStoreSubs,
|
||||||
ipcMain.handle('subStoreFrontendPort', () => subStoreFrontendPort)
|
subStoreCollections,
|
||||||
ipcMain.handle('subStoreSubs', wrapAsync(subStoreSubs))
|
|
||||||
ipcMain.handle('subStoreCollections', wrapAsync(subStoreCollections))
|
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
ipcMain.handle('resolveThemes', wrapAsync(resolveThemes))
|
resolveThemes,
|
||||||
ipcMain.handle('fetchThemes', wrapAsync(fetchThemes))
|
fetchThemes,
|
||||||
ipcMain.handle('importThemes', (_e, files: string[]) => wrapAsync(importThemes)(files))
|
importThemes,
|
||||||
ipcMain.handle('readTheme', (_e, theme: string) => wrapAsync(readTheme)(theme))
|
readTheme,
|
||||||
ipcMain.handle('writeTheme', (_e, theme: string, css: string) =>
|
writeTheme,
|
||||||
wrapAsync(writeTheme)(theme, css)
|
applyTheme,
|
||||||
)
|
|
||||||
ipcMain.handle('applyTheme', (_e, theme: string) => wrapAsync(applyTheme)(theme))
|
|
||||||
|
|
||||||
// Tray
|
// Tray
|
||||||
ipcMain.handle('showTrayIcon', wrapAsync(showTrayIcon))
|
showTrayIcon,
|
||||||
ipcMain.handle('closeTrayIcon', wrapAsync(closeTrayIcon))
|
closeTrayIcon,
|
||||||
ipcMain.handle('updateTrayIcon', wrapAsync(updateTrayIcon))
|
updateTrayIcon,
|
||||||
ipcMain.handle('updateTrayIconImmediate', (_e, sysProxyEnabled: boolean, tunEnabled: boolean) =>
|
// Floating Window
|
||||||
updateTrayIconImmediate(sysProxyEnabled, tunEnabled)
|
showFloatingWindow,
|
||||||
)
|
closeFloatingWindow,
|
||||||
|
showContextMenu,
|
||||||
// Window
|
|
||||||
ipcMain.handle('showMainWindow', showMainWindow)
|
|
||||||
ipcMain.handle('closeMainWindow', closeMainWindow)
|
|
||||||
ipcMain.handle('triggerMainWindow', (_e, force?: boolean) => triggerMainWindow(force))
|
|
||||||
ipcMain.handle('showFloatingWindow', wrapAsync(showFloatingWindow))
|
|
||||||
ipcMain.handle('closeFloatingWindow', wrapAsync(closeFloatingWindow))
|
|
||||||
ipcMain.handle('showContextMenu', wrapAsync(showContextMenu))
|
|
||||||
ipcMain.handle('setTitleBarOverlay', (_e, overlay: Electron.TitleBarOverlayOptions) =>
|
|
||||||
wrapAsync(setTitleBarOverlay)(overlay)
|
|
||||||
)
|
|
||||||
ipcMain.handle('setAlwaysOnTop', (_e, alwaysOnTop: boolean) =>
|
|
||||||
mainWindow?.setAlwaysOnTop(alwaysOnTop)
|
|
||||||
)
|
|
||||||
ipcMain.handle('isAlwaysOnTop', () => mainWindow?.isAlwaysOnTop())
|
|
||||||
ipcMain.handle('openDevTools', () => mainWindow?.webContents.openDevTools())
|
|
||||||
ipcMain.handle('createHeapSnapshot', () =>
|
|
||||||
v8.writeHeapSnapshot(path.join(logDir(), `${Date.now()}.heapsnapshot`))
|
|
||||||
)
|
|
||||||
|
|
||||||
// Shortcut
|
|
||||||
ipcMain.handle('registerShortcut', (_e, oldShortcut: string, newShortcut: string, action: string) =>
|
|
||||||
wrapAsync(registerShortcut)(oldShortcut, newShortcut, action)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
ipcMain.handle('getGistUrl', wrapAsync(getGistUrl))
|
getGistUrl,
|
||||||
ipcMain.handle('getImageDataURL', (_e, url: string) => wrapAsync(getImageDataURL)(url))
|
getImageDataURL,
|
||||||
ipcMain.handle('relaunchApp', () => {
|
changeLanguage,
|
||||||
|
setTitleBarOverlay,
|
||||||
|
registerShortcut
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncHandlers: Record<string, SyncFn> = {
|
||||||
|
resetAppConfig,
|
||||||
|
getFilePath,
|
||||||
|
openFile,
|
||||||
|
getInterfaces,
|
||||||
|
setNativeTheme,
|
||||||
|
getVersion: () => app.getVersion(),
|
||||||
|
platform: () => process.platform,
|
||||||
|
subStorePort: () => subStorePort,
|
||||||
|
subStoreFrontendPort: () => subStoreFrontendPort,
|
||||||
|
updateTrayIconImmediate,
|
||||||
|
showMainWindow,
|
||||||
|
closeMainWindow,
|
||||||
|
triggerMainWindow,
|
||||||
|
setAlwaysOnTop: (alwaysOnTop: boolean) => mainWindow?.setAlwaysOnTop(alwaysOnTop),
|
||||||
|
isAlwaysOnTop: () => mainWindow?.isAlwaysOnTop(),
|
||||||
|
openDevTools: () => mainWindow?.webContents.openDevTools(),
|
||||||
|
createHeapSnapshot: () => v8.writeHeapSnapshot(path.join(logDir(), `${Date.now()}.heapsnapshot`)),
|
||||||
|
relaunchApp: () => {
|
||||||
app.relaunch()
|
app.relaunch()
|
||||||
app.quit()
|
app.quit()
|
||||||
})
|
},
|
||||||
ipcMain.handle('quitApp', () => app.quit())
|
quitApp: () => app.quit()
|
||||||
ipcMain.handle('changeLanguage', (_e, lng: string) => wrapAsync(changeLanguage)(lng))
|
}
|
||||||
|
|
||||||
|
export function registerIpcMainHandlers(): void {
|
||||||
|
registerHandlers(asyncHandlers, true)
|
||||||
|
registerHandlers(syncHandlers, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect, useState, useCallback } from 'react'
|
import React, { useEffect, useState, useCallback } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { IoCheckmark, IoClose, IoAlertSharp, IoInformationSharp, IoCopy } from 'react-icons/io5'
|
import { IoCheckmark, IoClose, IoAlertSharp, IoInformationSharp, IoCopy } from 'react-icons/io5'
|
||||||
|
import i18next from 'i18next'
|
||||||
|
|
||||||
type ToastType = 'success' | 'error' | 'warning' | 'info'
|
type ToastType = 'success' | 'error' | 'warning' | 'info'
|
||||||
|
|
||||||
@ -125,7 +126,9 @@ const ToastItem: React.FC<{
|
|||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-base font-semibold text-foreground">{data.title || '错误'}</p>
|
<p className="text-base font-semibold text-foreground">
|
||||||
|
{data.title || i18next.t('common.error.default')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative" style={{ zIndex: 99999 }}>
|
<div className="relative" style={{ zIndex: 99999 }}>
|
||||||
<button
|
<button
|
||||||
@ -145,7 +148,7 @@ const ToastItem: React.FC<{
|
|||||||
className={`absolute top-full mt-1 left-1/2 -translate-x-1/2 px-2 py-1 text-xs text-foreground bg-content2 border border-default-200 rounded shadow-md whitespace-nowrap transition-all duration-200 ${copied ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-1 pointer-events-none'}`}
|
className={`absolute top-full mt-1 left-1/2 -translate-x-1/2 px-2 py-1 text-xs text-foreground bg-content2 border border-default-200 rounded shadow-md whitespace-nowrap transition-all duration-200 ${copied ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-1 pointer-events-none'}`}
|
||||||
style={{ zIndex: 99999 }}
|
style={{ zIndex: 99999 }}
|
||||||
>
|
>
|
||||||
已复制
|
{i18next.t('common.copied')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -158,7 +161,7 @@ const ToastItem: React.FC<{
|
|||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
className="self-end px-4 py-1.5 text-sm font-medium text-white bg-danger rounded-lg hover:bg-danger/90 transition-colors"
|
className="self-end px-4 py-1.5 text-sm font-medium text-white bg-danger rounded-lg hover:bg-danger/90 transition-colors"
|
||||||
>
|
>
|
||||||
确定
|
{i18next.t('common.ok')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { createContext, useContext, ReactNode } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { showError } from '@renderer/utils/error-display'
|
import { showError } from '@renderer/utils/error-display'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { getAppConfig, patchAppConfig as patch } from '@renderer/utils/ipc'
|
import { getAppConfig, patchAppConfig as patch } from '@renderer/utils/ipc'
|
||||||
@ -12,13 +13,14 @@ interface AppConfigContextType {
|
|||||||
const AppConfigContext = createContext<AppConfigContextType | undefined>(undefined)
|
const AppConfigContext = createContext<AppConfigContextType | undefined>(undefined)
|
||||||
|
|
||||||
export const AppConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const AppConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { data: appConfig, mutate: mutateAppConfig } = useSWR('getConfig', () => getAppConfig())
|
const { data: appConfig, mutate: mutateAppConfig } = useSWR('getConfig', () => getAppConfig())
|
||||||
|
|
||||||
const patchAppConfig = async (value: Partial<IAppConfig>): Promise<void> => {
|
const patchAppConfig = async (value: Partial<IAppConfig>): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await patch(value)
|
await patch(value)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '更新应用配置失败')
|
await showError(e, t('common.error.updateAppConfigFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateAppConfig()
|
mutateAppConfig()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { createContext, useContext, ReactNode } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { showError } from '@renderer/utils/error-display'
|
import { showError } from '@renderer/utils/error-display'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { getControledMihomoConfig, patchControledMihomoConfig as patch } from '@renderer/utils/ipc'
|
import { getControledMihomoConfig, patchControledMihomoConfig as patch } from '@renderer/utils/ipc'
|
||||||
@ -14,6 +15,7 @@ const ControledMihomoConfigContext = createContext<ControledMihomoConfigContextT
|
|||||||
)
|
)
|
||||||
|
|
||||||
export const ControledMihomoConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const ControledMihomoConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { data: controledMihomoConfig, mutate: mutateControledMihomoConfig } = useSWR(
|
const { data: controledMihomoConfig, mutate: mutateControledMihomoConfig } = useSWR(
|
||||||
'getControledMihomoConfig',
|
'getControledMihomoConfig',
|
||||||
() => getControledMihomoConfig()
|
() => getControledMihomoConfig()
|
||||||
@ -23,7 +25,7 @@ export const ControledMihomoConfigProvider: React.FC<{ children: ReactNode }> =
|
|||||||
try {
|
try {
|
||||||
await patch(value)
|
await patch(value)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '更新内核配置失败')
|
await showError(e, t('common.error.updateCoreConfigFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateControledMihomoConfig()
|
mutateControledMihomoConfig()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { createContext, useContext, ReactNode } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { showError } from '@renderer/utils/error-display'
|
import { showError } from '@renderer/utils/error-display'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import {
|
import {
|
||||||
@ -21,6 +22,7 @@ interface OverrideConfigContextType {
|
|||||||
const OverrideConfigContext = createContext<OverrideConfigContextType | undefined>(undefined)
|
const OverrideConfigContext = createContext<OverrideConfigContextType | undefined>(undefined)
|
||||||
|
|
||||||
export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { data: overrideConfig, mutate: mutateOverrideConfig } = useSWR('getOverrideConfig', () =>
|
const { data: overrideConfig, mutate: mutateOverrideConfig } = useSWR('getOverrideConfig', () =>
|
||||||
getOverrideConfig()
|
getOverrideConfig()
|
||||||
)
|
)
|
||||||
@ -29,7 +31,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await set(config)
|
await set(config)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '保存覆写配置失败')
|
await showError(e, t('common.error.saveOverrideConfigFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
@ -39,7 +41,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await add(item)
|
await add(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '添加覆写失败')
|
await showError(e, t('common.error.addOverrideFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
@ -49,7 +51,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await remove(id)
|
await remove(id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '删除覆写失败')
|
await showError(e, t('common.error.deleteOverrideFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
@ -59,7 +61,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await update(item)
|
await update(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '更新覆写失败')
|
await showError(e, t('common.error.updateOverrideFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, ReactNode, useContext } from 'react'
|
import React, { createContext, ReactNode, useContext } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { showError } from '@renderer/utils/error-display'
|
import { showError } from '@renderer/utils/error-display'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import {
|
import {
|
||||||
@ -23,6 +24,7 @@ interface ProfileConfigContextType {
|
|||||||
const ProfileConfigContext = createContext<ProfileConfigContextType | undefined>(undefined)
|
const ProfileConfigContext = createContext<ProfileConfigContextType | undefined>(undefined)
|
||||||
|
|
||||||
export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { data: profileConfig, mutate: mutateProfileConfig } = useSWR('getProfileConfig', () =>
|
const { data: profileConfig, mutate: mutateProfileConfig } = useSWR('getProfileConfig', () =>
|
||||||
getProfileConfig()
|
getProfileConfig()
|
||||||
)
|
)
|
||||||
@ -33,7 +35,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await set(config)
|
await set(config)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '保存配置失败')
|
await showError(e, t('common.error.saveProfileConfigFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -44,7 +46,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await add(item)
|
await add(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '添加配置失败')
|
await showError(e, t('common.error.addProfileFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -55,7 +57,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await remove(id)
|
await remove(id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '删除配置失败')
|
await showError(e, t('common.error.deleteProfileFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -66,7 +68,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await update(item)
|
await update(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showError(e, '更新配置失败')
|
await showError(e, t('common.error.updateProfileFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -108,7 +110,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
if (errorMsg.includes('reply was never sent')) {
|
if (errorMsg.includes('reply was never sent')) {
|
||||||
setTimeout(() => mutateProfileConfig(), 1000)
|
setTimeout(() => mutateProfileConfig(), 1000)
|
||||||
} else {
|
} else {
|
||||||
await showError(errorMsg, '切换配置失败')
|
await showError(errorMsg, t('common.error.switchProfileFailed'))
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -39,6 +39,27 @@
|
|||||||
"common.error.shortcutRegistrationFailedWithError": "Failed to register shortcut: {{error}}",
|
"common.error.shortcutRegistrationFailedWithError": "Failed to register shortcut: {{error}}",
|
||||||
"common.error.adminRequired": "Please run with administrator privileges for first launch",
|
"common.error.adminRequired": "Please run with administrator privileges for first launch",
|
||||||
"common.error.initFailed": "Application initialization failed",
|
"common.error.initFailed": "Application initialization failed",
|
||||||
|
"common.error.default": "Error",
|
||||||
|
"common.error.updateAppConfigFailed": "Failed to update app config",
|
||||||
|
"common.error.updateCoreConfigFailed": "Failed to update core config",
|
||||||
|
"common.error.saveProfileConfigFailed": "Failed to save profile config",
|
||||||
|
"common.error.addProfileFailed": "Failed to add profile",
|
||||||
|
"common.error.deleteProfileFailed": "Failed to delete profile",
|
||||||
|
"common.error.updateProfileFailed": "Failed to update profile",
|
||||||
|
"common.error.switchProfileFailed": "Failed to switch profile",
|
||||||
|
"common.error.saveOverrideConfigFailed": "Failed to save override config",
|
||||||
|
"common.error.addOverrideFailed": "Failed to add override",
|
||||||
|
"common.error.deleteOverrideFailed": "Failed to delete override",
|
||||||
|
"common.error.updateOverrideFailed": "Failed to update override",
|
||||||
|
"common.error.firewallSetupFailed": "Failed to setup firewall",
|
||||||
|
"common.error.coreAuthFailed": "Failed to authorize core",
|
||||||
|
"common.error.sysproxySetupFailed": "Failed to setup system proxy",
|
||||||
|
"common.error.snifferConfigSaveFailed": "Failed to save sniffer config",
|
||||||
|
"common.error.dnsConfigSaveFailed": "Failed to save DNS config",
|
||||||
|
"common.copied": "Copied",
|
||||||
|
"common.ok": "OK",
|
||||||
|
"common.error.autoUpdateNotSupported": "Auto update not supported, please download manually",
|
||||||
|
"common.dialog.selectSubscriptionFile": "Select Subscription File",
|
||||||
"core.highPrivilege.title": "High Privilege Core Detected",
|
"core.highPrivilege.title": "High Privilege Core Detected",
|
||||||
"core.highPrivilege.message": "A high-privilege core is detected. The application needs to restart in administrator mode to match permissions. Restart now?",
|
"core.highPrivilege.message": "A high-privilege core is detected. The application needs to restart in administrator mode to match permissions. Restart now?",
|
||||||
"common.updater.versionReady": "v{{version}} Version Ready",
|
"common.updater.versionReady": "v{{version}} Version Ready",
|
||||||
|
|||||||
@ -39,6 +39,27 @@
|
|||||||
"common.error.shortcutRegistrationFailedWithError": "ثبت میانبر با خطا مواجه شد: {{error}}",
|
"common.error.shortcutRegistrationFailedWithError": "ثبت میانبر با خطا مواجه شد: {{error}}",
|
||||||
"common.error.adminRequired": "لطفا برای اولین اجرا با دسترسی مدیر برنامه را اجرا کنید",
|
"common.error.adminRequired": "لطفا برای اولین اجرا با دسترسی مدیر برنامه را اجرا کنید",
|
||||||
"common.error.initFailed": "راهاندازی برنامه با خطا مواجه شد",
|
"common.error.initFailed": "راهاندازی برنامه با خطا مواجه شد",
|
||||||
|
"common.error.default": "خطا",
|
||||||
|
"common.error.updateAppConfigFailed": "بهروزرسانی تنظیمات برنامه با خطا مواجه شد",
|
||||||
|
"common.error.updateCoreConfigFailed": "بهروزرسانی تنظیمات هسته با خطا مواجه شد",
|
||||||
|
"common.error.saveProfileConfigFailed": "ذخیره تنظیمات پروفایل با خطا مواجه شد",
|
||||||
|
"common.error.addProfileFailed": "افزودن پروفایل با خطا مواجه شد",
|
||||||
|
"common.error.deleteProfileFailed": "حذف پروفایل با خطا مواجه شد",
|
||||||
|
"common.error.updateProfileFailed": "بهروزرسانی پروفایل با خطا مواجه شد",
|
||||||
|
"common.error.switchProfileFailed": "تغییر پروفایل با خطا مواجه شد",
|
||||||
|
"common.error.saveOverrideConfigFailed": "ذخیره تنظیمات بازنویسی با خطا مواجه شد",
|
||||||
|
"common.error.addOverrideFailed": "افزودن بازنویسی با خطا مواجه شد",
|
||||||
|
"common.error.deleteOverrideFailed": "حذف بازنویسی با خطا مواجه شد",
|
||||||
|
"common.error.updateOverrideFailed": "بهروزرسانی بازنویسی با خطا مواجه شد",
|
||||||
|
"common.error.firewallSetupFailed": "تنظیم فایروال با خطا مواجه شد",
|
||||||
|
"common.error.coreAuthFailed": "احراز هویت هسته با خطا مواجه شد",
|
||||||
|
"common.error.sysproxySetupFailed": "تنظیم پروکسی سیستم با خطا مواجه شد",
|
||||||
|
"common.error.snifferConfigSaveFailed": "ذخیره تنظیمات اسنیفر با خطا مواجه شد",
|
||||||
|
"common.error.dnsConfigSaveFailed": "ذخیره تنظیمات DNS با خطا مواجه شد",
|
||||||
|
"common.copied": "کپی شد",
|
||||||
|
"common.ok": "تأیید",
|
||||||
|
"common.error.autoUpdateNotSupported": "بهروزرسانی خودکار پشتیبانی نمیشود، لطفاً به صورت دستی دانلود کنید",
|
||||||
|
"common.dialog.selectSubscriptionFile": "انتخاب فایل اشتراک",
|
||||||
"core.highPrivilege.title": "هسته با سطح دسترسی بالا شناسایی شد",
|
"core.highPrivilege.title": "هسته با سطح دسترسی بالا شناسایی شد",
|
||||||
"core.highPrivilege.message": "هستهای با سطح دسترسی بالا شناسایی شد. برنامه باید در حالت مدیر سیستم برای تطابق سطح دسترسیها دوباره راهاندازی شود. آیا میخواهید اکنون راهاندازی مجدد شود؟",
|
"core.highPrivilege.message": "هستهای با سطح دسترسی بالا شناسایی شد. برنامه باید در حالت مدیر سیستم برای تطابق سطح دسترسیها دوباره راهاندازی شود. آیا میخواهید اکنون راهاندازی مجدد شود؟",
|
||||||
"common.updater.versionReady": "نسخه v{{version}} آماده است",
|
"common.updater.versionReady": "نسخه v{{version}} آماده است",
|
||||||
|
|||||||
@ -39,6 +39,27 @@
|
|||||||
"common.error.shortcutRegistrationFailedWithError": "Не удалось зарегистрировать сочетание клавиш: {{error}}",
|
"common.error.shortcutRegistrationFailedWithError": "Не удалось зарегистрировать сочетание клавиш: {{error}}",
|
||||||
"common.error.adminRequired": "Для первого запуска требуются права администратора",
|
"common.error.adminRequired": "Для первого запуска требуются права администратора",
|
||||||
"common.error.initFailed": "Не удалось инициализировать приложение",
|
"common.error.initFailed": "Не удалось инициализировать приложение",
|
||||||
|
"common.error.default": "Ошибка",
|
||||||
|
"common.error.updateAppConfigFailed": "Не удалось обновить конфигурацию приложения",
|
||||||
|
"common.error.updateCoreConfigFailed": "Не удалось обновить конфигурацию ядра",
|
||||||
|
"common.error.saveProfileConfigFailed": "Не удалось сохранить конфигурацию профиля",
|
||||||
|
"common.error.addProfileFailed": "Не удалось добавить профиль",
|
||||||
|
"common.error.deleteProfileFailed": "Не удалось удалить профиль",
|
||||||
|
"common.error.updateProfileFailed": "Не удалось обновить профиль",
|
||||||
|
"common.error.switchProfileFailed": "Не удалось переключить профиль",
|
||||||
|
"common.error.saveOverrideConfigFailed": "Не удалось сохранить конфигурацию переопределения",
|
||||||
|
"common.error.addOverrideFailed": "Не удалось добавить переопределение",
|
||||||
|
"common.error.deleteOverrideFailed": "Не удалось удалить переопределение",
|
||||||
|
"common.error.updateOverrideFailed": "Не удалось обновить переопределение",
|
||||||
|
"common.error.firewallSetupFailed": "Не удалось настроить брандмауэр",
|
||||||
|
"common.error.coreAuthFailed": "Не удалось авторизовать ядро",
|
||||||
|
"common.error.sysproxySetupFailed": "Не удалось настроить системный прокси",
|
||||||
|
"common.error.snifferConfigSaveFailed": "Не удалось сохранить конфигурацию сниффера",
|
||||||
|
"common.error.dnsConfigSaveFailed": "Не удалось сохранить конфигурацию DNS",
|
||||||
|
"common.copied": "Скопировано",
|
||||||
|
"common.ok": "ОК",
|
||||||
|
"common.error.autoUpdateNotSupported": "Автообновление не поддерживается, пожалуйста, скачайте вручную",
|
||||||
|
"common.dialog.selectSubscriptionFile": "Выберите файл подписки",
|
||||||
"core.highPrivilege.title": "High Privilege Core Detected",
|
"core.highPrivilege.title": "High Privilege Core Detected",
|
||||||
"core.highPrivilege.message": "Обнаружено ядро с повышенными привилегиями. Приложение необходимо перезапустить в режиме администратора для согласования прав. Перезапустить сейчас?",
|
"core.highPrivilege.message": "Обнаружено ядро с повышенными привилегиями. Приложение необходимо перезапустить в режиме администратора для согласования прав. Перезапустить сейчас?",
|
||||||
"common.updater.versionReady": "Обнаружено ядро с повышенными привилегиями",
|
"common.updater.versionReady": "Обнаружено ядро с повышенными привилегиями",
|
||||||
|
|||||||
@ -39,6 +39,27 @@
|
|||||||
"common.error.shortcutRegistrationFailedWithError": "快捷键注册失败:{{error}}",
|
"common.error.shortcutRegistrationFailedWithError": "快捷键注册失败:{{error}}",
|
||||||
"common.error.adminRequired": "首次启动请以管理员权限运行",
|
"common.error.adminRequired": "首次启动请以管理员权限运行",
|
||||||
"common.error.initFailed": "应用初始化失败",
|
"common.error.initFailed": "应用初始化失败",
|
||||||
|
"common.error.default": "错误",
|
||||||
|
"common.error.updateAppConfigFailed": "更新应用配置失败",
|
||||||
|
"common.error.updateCoreConfigFailed": "更新内核配置失败",
|
||||||
|
"common.error.saveProfileConfigFailed": "保存配置失败",
|
||||||
|
"common.error.addProfileFailed": "添加配置失败",
|
||||||
|
"common.error.deleteProfileFailed": "删除配置失败",
|
||||||
|
"common.error.updateProfileFailed": "更新配置失败",
|
||||||
|
"common.error.switchProfileFailed": "切换配置失败",
|
||||||
|
"common.error.saveOverrideConfigFailed": "保存覆写配置失败",
|
||||||
|
"common.error.addOverrideFailed": "添加覆写失败",
|
||||||
|
"common.error.deleteOverrideFailed": "删除覆写失败",
|
||||||
|
"common.error.updateOverrideFailed": "更新覆写失败",
|
||||||
|
"common.error.firewallSetupFailed": "防火墙设置失败",
|
||||||
|
"common.error.coreAuthFailed": "内核授权失败",
|
||||||
|
"common.error.sysproxySetupFailed": "系统代理设置失败",
|
||||||
|
"common.error.snifferConfigSaveFailed": "嗅探配置保存失败",
|
||||||
|
"common.error.dnsConfigSaveFailed": "DNS 配置保存失败",
|
||||||
|
"common.copied": "已复制",
|
||||||
|
"common.ok": "确定",
|
||||||
|
"common.error.autoUpdateNotSupported": "不支持自动更新,请手动下载更新",
|
||||||
|
"common.dialog.selectSubscriptionFile": "选择订阅文件",
|
||||||
"core.highPrivilege.title": "检测到高权限内核",
|
"core.highPrivilege.title": "检测到高权限内核",
|
||||||
"core.highPrivilege.message": "检测到运行中的高权限内核,需以管理员模式重启应用以匹配权限,确定重启?",
|
"core.highPrivilege.message": "检测到运行中的高权限内核,需以管理员模式重启应用以匹配权限,确定重启?",
|
||||||
"common.updater.versionReady": "v{{version}} 版本就绪",
|
"common.updater.versionReady": "v{{version}} 版本就绪",
|
||||||
|
|||||||
@ -39,6 +39,27 @@
|
|||||||
"common.error.shortcutRegistrationFailedWithError": "快捷鍵註冊失敗:{{error}}",
|
"common.error.shortcutRegistrationFailedWithError": "快捷鍵註冊失敗:{{error}}",
|
||||||
"common.error.adminRequired": "首次啟動請以系統管理員身分執行",
|
"common.error.adminRequired": "首次啟動請以系統管理員身分執行",
|
||||||
"common.error.initFailed": "應用初始化失敗",
|
"common.error.initFailed": "應用初始化失敗",
|
||||||
|
"common.error.default": "錯誤",
|
||||||
|
"common.error.updateAppConfigFailed": "更新應用配置失敗",
|
||||||
|
"common.error.updateCoreConfigFailed": "更新內核配置失敗",
|
||||||
|
"common.error.saveProfileConfigFailed": "保存配置失敗",
|
||||||
|
"common.error.addProfileFailed": "添加配置失敗",
|
||||||
|
"common.error.deleteProfileFailed": "刪除配置失敗",
|
||||||
|
"common.error.updateProfileFailed": "更新配置失敗",
|
||||||
|
"common.error.switchProfileFailed": "切換配置失敗",
|
||||||
|
"common.error.saveOverrideConfigFailed": "保存覆寫配置失敗",
|
||||||
|
"common.error.addOverrideFailed": "添加覆寫失敗",
|
||||||
|
"common.error.deleteOverrideFailed": "刪除覆寫失敗",
|
||||||
|
"common.error.updateOverrideFailed": "更新覆寫失敗",
|
||||||
|
"common.error.firewallSetupFailed": "防火牆設置失敗",
|
||||||
|
"common.error.coreAuthFailed": "內核授權失敗",
|
||||||
|
"common.error.sysproxySetupFailed": "系統代理設置失敗",
|
||||||
|
"common.error.snifferConfigSaveFailed": "嗅探配置保存失敗",
|
||||||
|
"common.error.dnsConfigSaveFailed": "DNS 配置保存失敗",
|
||||||
|
"common.copied": "已複製",
|
||||||
|
"common.ok": "確定",
|
||||||
|
"common.error.autoUpdateNotSupported": "不支持自動更新,請手動下載更新",
|
||||||
|
"common.dialog.selectSubscriptionFile": "選擇訂閱文件",
|
||||||
"core.highPrivilege.title": "檢測到高權限內核",
|
"core.highPrivilege.title": "檢測到高權限內核",
|
||||||
"core.highPrivilege.message": "檢測到運行中的高權限內核,需以系統管理員模式重新啟動應用程式以匹配權限,確定重新啟動?",
|
"core.highPrivilege.message": "檢測到運行中的高權限內核,需以系統管理員模式重新啟動應用程式以匹配權限,確定重新啟動?",
|
||||||
"common.updater.versionReady": "v{{version}} 版本就緒",
|
"common.updater.versionReady": "v{{version}} 版本就緒",
|
||||||
|
|||||||
@ -146,7 +146,7 @@ const DNS: React.FC = () => {
|
|||||||
await restartCore()
|
await restartCore()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrorSync(e, 'DNS 配置保存失败')
|
showErrorSync(e, t('common.error.dnsConfigSaveFailed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,7 @@ const Sniffer: React.FC = () => {
|
|||||||
await restartCore()
|
await restartCore()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrorSync(e, '嵅探配置保存失败')
|
showErrorSync(e, t('common.error.snifferConfigSaveFailed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -106,7 +106,7 @@ const Sysproxy: React.FC = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
setValues({ ...values, enable: previousState })
|
setValues({ ...values, enable: previousState })
|
||||||
setChanged(true)
|
setChanged(true)
|
||||||
showErrorSync(e, '系统代理设置失败')
|
showErrorSync(e, t('common.error.sysproxySetupFailed'))
|
||||||
|
|
||||||
await patchAppConfig({ sysProxy: { enable: false } })
|
await patchAppConfig({ sysProxy: { enable: false } })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,7 +113,7 @@ const Tun: React.FC = () => {
|
|||||||
new Notification(t('tun.notifications.firewallResetSuccess'))
|
new Notification(t('tun.notifications.firewallResetSuccess'))
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrorSync(e, '防火墙设置失败')
|
showErrorSync(e, t('common.error.firewallSetupFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ const Tun: React.FC = () => {
|
|||||||
new Notification(t('tun.notifications.coreAuthSuccess'))
|
new Notification(t('tun.notifications.coreAuthSuccess'))
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrorSync(e, '内核授权失败')
|
showErrorSync(e, t('common.error.coreAuthFailed'))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { toast } from '@renderer/components/base/toast'
|
import { toast } from '@renderer/components/base/toast'
|
||||||
|
import i18next from 'i18next'
|
||||||
|
|
||||||
const DETAILED_ERROR_KEYWORDS = [
|
const DETAILED_ERROR_KEYWORDS = [
|
||||||
'yaml',
|
'yaml',
|
||||||
@ -33,9 +34,10 @@ function shouldShowDetailedError(message: string): boolean {
|
|||||||
|
|
||||||
export async function showError(error: unknown, title?: string): Promise<void> {
|
export async function showError(error: unknown, title?: string): Promise<void> {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
|
const defaultTitle = i18next.t('common.error.default')
|
||||||
|
|
||||||
if (shouldShowDetailedError(message)) {
|
if (shouldShowDetailedError(message)) {
|
||||||
toast.detailedError(message, title || '错误')
|
toast.detailedError(message, title || defaultTitle)
|
||||||
} else {
|
} else {
|
||||||
toast.error(message, title)
|
toast.error(message, title)
|
||||||
}
|
}
|
||||||
@ -43,9 +45,10 @@ export async function showError(error: unknown, title?: string): Promise<void> {
|
|||||||
|
|
||||||
export function showErrorSync(error: unknown, title?: string): void {
|
export function showErrorSync(error: unknown, title?: string): void {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
|
const defaultTitle = i18next.t('common.error.default')
|
||||||
|
|
||||||
if (shouldShowDetailedError(message)) {
|
if (shouldShowDetailedError(message)) {
|
||||||
toast.detailedError(message, title || '错误')
|
toast.detailedError(message, title || defaultTitle)
|
||||||
} else {
|
} else {
|
||||||
toast.error(message, title)
|
toast.error(message, title)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user