mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
refactor: replace alert() with toast notification system
This commit is contained in:
parent
8f2e956fd0
commit
dcbd837949
@ -148,3 +148,48 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Toast */
|
||||||
|
.toast-enter {
|
||||||
|
animation: toast-in 0.2s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-exit {
|
||||||
|
animation: toast-out 0.15s ease-in forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes toast-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes toast-out {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-progress {
|
||||||
|
animation: progress-shrink linear forwards;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progress-shrink {
|
||||||
|
from {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
157
src/renderer/src/components/base/toast.tsx
Normal file
157
src/renderer/src/components/base/toast.tsx
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import React, { useEffect, useState, useCallback } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
import { IoCheckmark, IoClose, IoAlertSharp, IoInformationSharp } from 'react-icons/io5'
|
||||||
|
|
||||||
|
type ToastType = 'success' | 'error' | 'warning' | 'info'
|
||||||
|
|
||||||
|
interface ToastData {
|
||||||
|
id: string
|
||||||
|
type: ToastType
|
||||||
|
title?: string
|
||||||
|
message: string
|
||||||
|
duration?: number
|
||||||
|
exiting?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToastListener = (toasts: ToastData[]) => void
|
||||||
|
|
||||||
|
let toasts: ToastData[] = []
|
||||||
|
let listeners: ToastListener[] = []
|
||||||
|
|
||||||
|
const notifyListeners = (): void => {
|
||||||
|
listeners.forEach((listener) => listener([...toasts]))
|
||||||
|
}
|
||||||
|
|
||||||
|
const addToast = (type: ToastType, message: string, title?: string, duration = 1500): void => {
|
||||||
|
const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`
|
||||||
|
toasts = [...toasts.slice(-4), { id, type, message, title, duration }]
|
||||||
|
notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
const markExiting = (id: string): void => {
|
||||||
|
toasts = toasts.map((t) => (t.id === id ? { ...t, exiting: true } : t))
|
||||||
|
notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeToast = (id: string): void => {
|
||||||
|
toasts = toasts.filter((t) => t.id !== id)
|
||||||
|
notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toast = {
|
||||||
|
success: (message: string, title?: string): void => addToast('success', message, title),
|
||||||
|
error: (message: string, title?: string): void => addToast('error', message, title, 1800),
|
||||||
|
warning: (message: string, title?: string): void => addToast('warning', message, title),
|
||||||
|
info: (message: string, title?: string): void => addToast('info', message, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToastItem: React.FC<{
|
||||||
|
data: ToastData
|
||||||
|
onRemove: (id: string) => void
|
||||||
|
}> = ({ data, onRemove }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const duration = data.duration || 3500
|
||||||
|
const exitTimer = setTimeout(() => markExiting(data.id), duration - 200)
|
||||||
|
const removeTimer = setTimeout(() => onRemove(data.id), duration)
|
||||||
|
return () => {
|
||||||
|
clearTimeout(exitTimer)
|
||||||
|
clearTimeout(removeTimer)
|
||||||
|
}
|
||||||
|
}, [data.id, data.duration, onRemove])
|
||||||
|
|
||||||
|
const theme: Record<ToastType, { icon: React.ReactNode; bg: string; iconBg: string }> = {
|
||||||
|
success: {
|
||||||
|
icon: <IoCheckmark className="text-white text-sm" />,
|
||||||
|
bg: 'bg-content1',
|
||||||
|
iconBg: 'bg-success'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
icon: <IoClose className="text-white text-sm" />,
|
||||||
|
bg: 'bg-content1',
|
||||||
|
iconBg: 'bg-danger'
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
icon: <IoAlertSharp className="text-white text-sm" />,
|
||||||
|
bg: 'bg-content1',
|
||||||
|
iconBg: 'bg-warning'
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
icon: <IoInformationSharp className="text-white text-sm" />,
|
||||||
|
bg: 'bg-content1',
|
||||||
|
iconBg: 'bg-primary'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { icon, iconBg } = theme[data.type]
|
||||||
|
const duration = data.duration || 3500
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
relative overflow-hidden
|
||||||
|
flex items-center gap-3 p-3
|
||||||
|
bg-content1/80 rounded-large
|
||||||
|
shadow-large border border-default-200/50
|
||||||
|
backdrop-blur-xl backdrop-saturate-150
|
||||||
|
${data.exiting ? 'toast-exit' : 'toast-enter'}
|
||||||
|
`}
|
||||||
|
style={{ width: 340 }}
|
||||||
|
>
|
||||||
|
<div className={`flex-shrink-0 w-7 h-7 ${iconBg} rounded-full flex items-center justify-center`}>
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
{data.title && (
|
||||||
|
<p className="text-sm font-medium text-foreground">{data.title}</p>
|
||||||
|
)}
|
||||||
|
<p className="text-sm text-foreground-500 break-words select-text">
|
||||||
|
{data.message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
markExiting(data.id)
|
||||||
|
setTimeout(() => onRemove(data.id), 150)
|
||||||
|
}}
|
||||||
|
className="flex-shrink-0 p-1 rounded-full hover:bg-default-200/60 transition-colors"
|
||||||
|
>
|
||||||
|
<IoClose className="text-base text-foreground-400" />
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className={`absolute bottom-0 left-0 h-[2px] ${iconBg} toast-progress`}
|
||||||
|
style={{ animationDuration: `${duration}ms` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
|
const [currentToasts, setCurrentToasts] = useState<ToastData[]>([])
|
||||||
|
|
||||||
|
const handleRemove = useCallback((id: string) => {
|
||||||
|
removeToast(id)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const listener: ToastListener = (newToasts) => setCurrentToasts(newToasts)
|
||||||
|
listeners.push(listener)
|
||||||
|
return () => {
|
||||||
|
listeners = listeners.filter((l) => l !== listener)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{children}
|
||||||
|
{currentToasts.length > 0 &&
|
||||||
|
createPortal(
|
||||||
|
<div className="fixed top-[60px] right-4 z-[9999] flex flex-col gap-2">
|
||||||
|
{currentToasts.map((t) => (
|
||||||
|
<ToastItem key={t.id} data={t} onRemove={handleRemove} />
|
||||||
|
))}
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@heroui/react'
|
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { BaseEditor } from '../base/base-editor'
|
import { BaseEditor } from '../base/base-editor'
|
||||||
import { getOverride, restartCore, setOverride } from '@renderer/utils/ipc'
|
import { getOverride, restartCore, setOverride } from '@renderer/utils/ipc'
|
||||||
@ -58,7 +59,7 @@ const EditFileModal: React.FC<Props> = (props) => {
|
|||||||
await restartCore()
|
await restartCore()
|
||||||
onClose()
|
onClose()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownTrigger
|
DropdownTrigger
|
||||||
} from '@heroui/react'
|
} from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
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, useMemo, useState } from 'react'
|
import React, { Key, useMemo, useState } from 'react'
|
||||||
@ -197,7 +198,7 @@ const OverrideItem: React.FC<Props> = (props) => {
|
|||||||
await addOverrideItem(info)
|
await addOverrideItem(info)
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setUpdating(false)
|
setUpdating(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownItem
|
DropdownItem
|
||||||
} from '@heroui/react'
|
} from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { useOverrideConfig } from '@renderer/hooks/use-override-config'
|
import { useOverrideConfig } from '@renderer/hooks/use-override-config'
|
||||||
@ -49,7 +50,7 @@ const EditInfoModal: React.FC<Props> = (props) => {
|
|||||||
await restartCore()
|
await restartCore()
|
||||||
onClose()
|
onClose()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import yaml from 'js-yaml'
|
|||||||
import { IoMdTrash, IoMdArrowUp, IoMdArrowDown, IoMdUndo } from 'react-icons/io'
|
import { IoMdTrash, IoMdArrowUp, IoMdArrowDown, IoMdUndo } from 'react-icons/io'
|
||||||
import { MdVerticalAlignTop, MdVerticalAlignBottom } from 'react-icons/md'
|
import { MdVerticalAlignTop, MdVerticalAlignBottom } from 'react-icons/md'
|
||||||
import { platform } from '@renderer/utils/init'
|
import { platform } from '@renderer/utils/init'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: string
|
||||||
@ -642,7 +643,7 @@ const EditRulesModal: React.FC<Props> = (props) => {
|
|||||||
await setRuleStr(id, ruleYaml);
|
await setRuleStr(id, ruleYaml);
|
||||||
onClose();
|
onClose();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(t('profiles.editRules.saveError') + ': ' + (e instanceof Error ? e.message : String(e)));
|
toast.error(t('profiles.editRules.saveError') + ': ' + (e instanceof Error ? e.message : String(e)));
|
||||||
}
|
}
|
||||||
}, [prependRules, deletedRules, rules, appendRules, id, onClose, t])
|
}, [prependRules, deletedRules, rules, appendRules, id, onClose, t])
|
||||||
|
|
||||||
@ -688,7 +689,7 @@ const EditRulesModal: React.FC<Props> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newRule.type !== 'MATCH' && newRule.payload.trim() !== '' && !validateRulePayload(newRule.type, newRule.payload)) {
|
if (newRule.type !== 'MATCH' && newRule.payload.trim() !== '' && !validateRulePayload(newRule.type, newRule.payload)) {
|
||||||
alert(t('profiles.editRules.invalidPayload') + ': ' + getRuleExample(newRule.type));
|
toast.error(t('profiles.editRules.invalidPayload') + ': ' + getRuleExample(newRule.type));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Button, Input, Switch, Tab, Tabs } from '@heroui/react'
|
import { Button, Input, Switch, Tab, Tabs } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||||
@ -123,7 +124,7 @@ const GeoData: React.FC = () => {
|
|||||||
await mihomoUpgradeGeo()
|
await mihomoUpgradeGeo()
|
||||||
new Notification(t('resources.geoData.updateSuccess'))
|
new Notification(t('resources.geoData.updateSuccess'))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setUpdating(false)
|
setUpdating(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import useSWR from 'swr'
|
|||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { Button, Chip } from '@heroui/react'
|
import { Button, Chip } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import { IoMdRefresh } from 'react-icons/io'
|
import { IoMdRefresh } from 'react-icons/io'
|
||||||
import { CgLoadbarDoc } from 'react-icons/cg'
|
import { CgLoadbarDoc } from 'react-icons/cg'
|
||||||
import { MdEditDocument } from 'react-icons/md'
|
import { MdEditDocument } from 'react-icons/md'
|
||||||
@ -67,7 +68,7 @@ const ProxyProvider: React.FC = () => {
|
|||||||
await mihomoUpdateProxyProviders(name)
|
await mihomoUpdateProxyProviders(name)
|
||||||
mutate()
|
mutate()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setUpdating((prev) => {
|
setUpdating((prev) => {
|
||||||
prev[index] = false
|
prev[index] = false
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import useSWR from 'swr'
|
|||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { Button, Chip } from '@heroui/react'
|
import { Button, Chip } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import { IoMdRefresh } from 'react-icons/io'
|
import { IoMdRefresh } from 'react-icons/io'
|
||||||
import { CgLoadbarDoc } from 'react-icons/cg'
|
import { CgLoadbarDoc } from 'react-icons/cg'
|
||||||
import { MdEditDocument } from 'react-icons/md'
|
import { MdEditDocument } from 'react-icons/md'
|
||||||
@ -71,7 +72,7 @@ const RuleProvider: React.FC = () => {
|
|||||||
await mihomoUpdateRuleProviders(name)
|
await mihomoUpdateRuleProviders(name)
|
||||||
mutate()
|
mutate()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setUpdating((prev) => {
|
setUpdating((prev) => {
|
||||||
prev[index] = false
|
prev[index] = false
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, Tooltip } from '@heroui/react'
|
import { Button, Tooltip } from '@heroui/react'
|
||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import {
|
import {
|
||||||
checkUpdate,
|
checkUpdate,
|
||||||
@ -69,7 +70,7 @@ const Actions: React.FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setCheckingUpdate(false)
|
setCheckingUpdate(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { Button, Input, Select, SelectItem, Switch, Tab, Tabs, Tooltip } from '@heroui/react'
|
import { Button, Input, Select, SelectItem, Switch, Tab, Tabs, Tooltip } from '@heroui/react'
|
||||||
import { BiCopy, BiSolidFileImport } from 'react-icons/bi'
|
import { BiCopy, BiSolidFileImport } from 'react-icons/bi'
|
||||||
@ -105,7 +106,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
await patchAppConfig({ disableHardwareAcceleration: pendingHardwareAccelValue })
|
await patchAppConfig({ disableHardwareAcceleration: pendingHardwareAccelValue })
|
||||||
await relaunchApp()
|
await relaunchApp()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
setIsRelaunching(false)
|
setIsRelaunching(false)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -151,7 +152,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
await disableAutoRun()
|
await disableAutoRun()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateEnable()
|
mutateEnable()
|
||||||
}
|
}
|
||||||
@ -248,7 +249,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
envType: Array.from(v) as ('bash' | 'cmd' | 'powershell')[]
|
envType: Array.from(v) as ('bash' | 'cmd' | 'powershell')[]
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -413,7 +414,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
await patchAppConfig({ useWindowFrame: v })
|
await patchAppConfig({ useWindowFrame: v })
|
||||||
await relaunchApp()
|
await relaunchApp()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
setIsRelaunching(false)
|
setIsRelaunching(false)
|
||||||
}
|
}
|
||||||
}, 1000)}
|
}, 1000)}
|
||||||
@ -503,7 +504,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
await fetchThemes()
|
await fetchThemes()
|
||||||
setCustomThemes(await resolveThemes())
|
setCustomThemes(await resolveThemes())
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setFetching(false)
|
setFetching(false)
|
||||||
}
|
}
|
||||||
@ -523,7 +524,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
await importThemes(files)
|
await importThemes(files)
|
||||||
setCustomThemes(await resolveThemes())
|
setCustomThemes(await resolveThemes())
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -555,7 +556,7 @@ const GeneralConfig: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
await patchAppConfig({ customTheme: v.currentKey as string })
|
await patchAppConfig({ customTheme: v.currentKey as string })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { Button, useDisclosure } from '@heroui/react'
|
import { Button, useDisclosure } from '@heroui/react'
|
||||||
import { exportLocalBackup, importLocalBackup } from '@renderer/utils/ipc'
|
import { exportLocalBackup, importLocalBackup } from '@renderer/utils/ipc'
|
||||||
@ -22,7 +23,7 @@ const LocalBackupConfig: React.FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setExporting(false)
|
setExporting(false)
|
||||||
}
|
}
|
||||||
@ -38,7 +39,7 @@ const LocalBackupConfig: React.FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(t('common.error.importFailed', { error: e }))
|
toast.error(t('common.error.importFailed', { error: e }))
|
||||||
} finally {
|
} finally {
|
||||||
setImporting(false)
|
setImporting(false)
|
||||||
onClose()
|
onClose()
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { Button, Input, Select, SelectItem, Switch, Tooltip } from '@heroui/react'
|
import { Button, Input, Select, SelectItem, Switch, Tooltip } from '@heroui/react'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
@ -122,7 +123,7 @@ const MihomoConfig: React.FC = () => {
|
|||||||
await navigator.clipboard.writeText(`${url}/raw/clash-party.yaml`)
|
await navigator.clipboard.writeText(`${url}/raw/clash-party.yaml`)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -176,7 +177,7 @@ const MihomoConfig: React.FC = () => {
|
|||||||
})
|
})
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -208,7 +209,7 @@ const MihomoConfig: React.FC = () => {
|
|||||||
await patchAppConfig({ diffWorkDir: v })
|
await patchAppConfig({ diffWorkDir: v })
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, Input } from '@heroui/react'
|
import { Button, Input } from '@heroui/react'
|
||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
import React, { KeyboardEvent, useState } from 'react'
|
import React, { KeyboardEvent, useState } from 'react'
|
||||||
@ -213,10 +214,10 @@ const ShortcutInput: React.FC<{
|
|||||||
await patchAppConfig({ [action]: inputValue })
|
await patchAppConfig({ [action]: inputValue })
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
} else {
|
} else {
|
||||||
alert(t('common.error.shortcutRegistrationFailed'))
|
toast.error(t('common.error.shortcutRegistrationFailed'))
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(t('common.error.shortcutRegistrationFailedWithError', { error: e }))
|
toast.error(t('common.error.shortcutRegistrationFailedWithError', { error: e }))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||||
import { Button, Input, Switch } from '@heroui/react'
|
import { Button, Input, Switch } from '@heroui/react'
|
||||||
import {
|
import {
|
||||||
@ -55,7 +56,7 @@ const SubStoreConfig: React.FC = () => {
|
|||||||
await stopSubStoreBackendServer()
|
await stopSubStoreBackendServer()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -76,7 +77,7 @@ const SubStoreConfig: React.FC = () => {
|
|||||||
await startSubStoreFrontendServer()
|
await startSubStoreFrontendServer()
|
||||||
await startSubStoreBackendServer()
|
await startSubStoreBackendServer()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -94,7 +95,7 @@ const SubStoreConfig: React.FC = () => {
|
|||||||
await startSubStoreBackendServer()
|
await startSubStoreBackendServer()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -123,7 +124,7 @@ const SubStoreConfig: React.FC = () => {
|
|||||||
await patchAppConfig({ useProxyInSubStore: v })
|
await patchAppConfig({ useProxyInSubStore: v })
|
||||||
await startSubStoreBackendServer()
|
await startSubStoreBackendServer()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -144,7 +145,7 @@ const SubStoreConfig: React.FC = () => {
|
|||||||
})
|
})
|
||||||
new Notification(t('common.notification.restartRequired'))
|
new Notification(t('common.notification.restartRequired'))
|
||||||
} else {
|
} else {
|
||||||
alert(t('common.error.invalidCron'))
|
toast.warning(t('common.error.invalidCron'))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -177,7 +178,7 @@ const SubStoreConfig: React.FC = () => {
|
|||||||
})
|
})
|
||||||
new Notification(t('common.notification.restartRequired'))
|
new Notification(t('common.notification.restartRequired'))
|
||||||
} else {
|
} else {
|
||||||
alert(t('common.error.invalidCron'))
|
toast.warning(t('common.error.invalidCron'))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -210,7 +211,7 @@ const SubStoreConfig: React.FC = () => {
|
|||||||
})
|
})
|
||||||
new Notification(t('common.notification.restartRequired'))
|
new Notification(t('common.notification.restartRequired'))
|
||||||
} else {
|
} else {
|
||||||
alert(t('common.error.invalidCron'))
|
toast.warning(t('common.error.invalidCron'))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { Button, Input, Select, SelectItem } from '@heroui/react'
|
import { Button, Input, Select, SelectItem } from '@heroui/react'
|
||||||
import { listWebdavBackups, webdavBackup, reinitWebdavBackupScheduler } from '@renderer/utils/ipc'
|
import { listWebdavBackups, webdavBackup, reinitWebdavBackupScheduler } from '@renderer/utils/ipc'
|
||||||
@ -47,7 +48,7 @@ const WebdavConfig: React.FC = () => {
|
|||||||
body: t('webdav.notification.backupSuccess.body')
|
body: t('webdav.notification.backupSuccess.body')
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setBackuping(false)
|
setBackuping(false)
|
||||||
}
|
}
|
||||||
@ -60,7 +61,7 @@ const WebdavConfig: React.FC = () => {
|
|||||||
setFilenames(filenames)
|
setFilenames(filenames)
|
||||||
setRestoreOpen(true)
|
setRestoreOpen(true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(t('common.error.getBackupListFailed', { error: e }))
|
toast.error(t('common.error.getBackupListFailed', { error: e }))
|
||||||
} finally {
|
} finally {
|
||||||
setRestoring(false)
|
setRestoring(false)
|
||||||
}
|
}
|
||||||
@ -156,7 +157,7 @@ const WebdavConfig: React.FC = () => {
|
|||||||
new Notification(t('webdav.notification.cronUpdateFailed'))
|
new Notification(t('webdav.notification.cronUpdateFailed'))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(t('common.error.invalidCron'))
|
toast.warning(t('common.error.invalidCron'))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@heroui/react'
|
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import { relaunchApp, webdavDelete, webdavRestore } from '@renderer/utils/ipc'
|
import { relaunchApp, webdavDelete, webdavRestore } from '@renderer/utils/ipc'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { MdDeleteForever } from 'react-icons/md'
|
import { MdDeleteForever } from 'react-icons/md'
|
||||||
@ -43,7 +44,7 @@ const WebdavRestoreModal: React.FC<Props> = (props) => {
|
|||||||
await webdavRestore(filename)
|
await webdavRestore(filename)
|
||||||
await relaunchApp()
|
await relaunchApp()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(t('common.error.restoreFailed', { error: e }))
|
toast.error(t('common.error.restoreFailed', { error: e }))
|
||||||
} finally {
|
} finally {
|
||||||
setRestoring(false)
|
setRestoring(false)
|
||||||
}
|
}
|
||||||
@ -61,7 +62,7 @@ const WebdavRestoreModal: React.FC<Props> = (props) => {
|
|||||||
await webdavDelete(filename)
|
await webdavDelete(filename)
|
||||||
setFilenames(filenames.filter((name) => name !== filename))
|
setFilenames(filenames.filter((name) => name !== filename))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(t('common.error.deleteFailed', { error: e }))
|
toast.error(t('common.error.deleteFailed', { error: e }))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||||
import BorderSwitch from '@renderer/components/base/border-swtich'
|
import BorderSwitch from '@renderer/components/base/border-swtich'
|
||||||
import { LuServer } from 'react-icons/lu'
|
import { LuServer } from 'react-icons/lu'
|
||||||
@ -39,7 +40,7 @@ const DNSCard: React.FC<Props> = (props) => {
|
|||||||
await patchControledMihomoConfig({})
|
await patchControledMihomoConfig({})
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import { calcTraffic } from '@renderer/utils/calc'
|
import { calcTraffic } from '@renderer/utils/calc'
|
||||||
import { mihomoVersion, restartCore } from '@renderer/utils/ipc'
|
import { mihomoVersion, restartCore } from '@renderer/utils/ipc'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
@ -112,7 +113,7 @@ const MihomoCoreCard: React.FC<Props> = (props) => {
|
|||||||
try {
|
try {
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutate()
|
mutate()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import BorderSwitch from '@renderer/components/base/border-swtich'
|
import BorderSwitch from '@renderer/components/base/border-swtich'
|
||||||
import { RiScan2Fill } from 'react-icons/ri'
|
import { RiScan2Fill } from 'react-icons/ri'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
@ -39,7 +40,7 @@ const SniffCard: React.FC<Props> = (props) => {
|
|||||||
await patchControledMihomoConfig({})
|
await patchControledMihomoConfig({})
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import BorderSwitch from '@renderer/components/base/border-swtich'
|
import BorderSwitch from '@renderer/components/base/border-swtich'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
@ -54,7 +55,7 @@ const SysproxySwitcher: React.FC<Props> = (props) => {
|
|||||||
await patchAppConfig({ sysProxy: { enable: previousState } })
|
await patchAppConfig({ sysProxy: { enable: previousState } })
|
||||||
// 回滚图标
|
// 回滚图标
|
||||||
updateTrayIconImmediate(previousState, tunEnabled)
|
updateTrayIconImmediate(previousState, tunEnabled)
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,5 @@
|
|||||||
import {
|
import { Button, Code, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@heroui/react'
|
||||||
Modal,
|
import { toast } from '@renderer/components/base/toast'
|
||||||
ModalContent,
|
|
||||||
ModalHeader,
|
|
||||||
ModalBody,
|
|
||||||
ModalFooter,
|
|
||||||
Button,
|
|
||||||
Code
|
|
||||||
} from '@heroui/react'
|
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { downloadAndInstallUpdate } from '@renderer/utils/ipc'
|
import { downloadAndInstallUpdate } from '@renderer/utils/ipc'
|
||||||
@ -27,7 +20,7 @@ const UpdaterModal: React.FC<Props> = (props) => {
|
|||||||
try {
|
try {
|
||||||
await downloadAndInstallUpdate(version)
|
await downloadAndInstallUpdate(version)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +75,7 @@ const UpdaterModal: React.FC<Props> = (props) => {
|
|||||||
await onUpdate()
|
await onUpdate()
|
||||||
onClose()
|
onClose()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setDownloading(false)
|
setDownloading(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { createContext, useContext, ReactNode } from 'react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
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'
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ export const AppConfigProvider: React.FC<{ children: ReactNode }> = ({ children
|
|||||||
try {
|
try {
|
||||||
await patch(value)
|
await patch(value)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateAppConfig()
|
mutateAppConfig()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { createContext, useContext, ReactNode } from 'react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
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'
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ export const ControledMihomoConfigProvider: React.FC<{ children: ReactNode }> =
|
|||||||
try {
|
try {
|
||||||
await patch(value)
|
await patch(value)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateControledMihomoConfig()
|
mutateControledMihomoConfig()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { createContext, useContext, ReactNode } from 'react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import {
|
import {
|
||||||
getOverrideConfig,
|
getOverrideConfig,
|
||||||
@ -28,7 +29,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await set(config)
|
await set(config)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
@ -38,7 +39,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await add(item)
|
await add(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
@ -48,7 +49,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await remove(id)
|
await remove(id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
@ -58,7 +59,7 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
try {
|
try {
|
||||||
await update(item)
|
await update(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, ReactNode, useContext } from 'react'
|
import React, { createContext, ReactNode, useContext } from 'react'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import {
|
import {
|
||||||
addProfileItem as add,
|
addProfileItem as add,
|
||||||
@ -32,7 +33,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await set(config)
|
await set(config)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -43,7 +44,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await add(item)
|
await add(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -54,7 +55,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await remove(id)
|
await remove(id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -65,7 +66,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
try {
|
try {
|
||||||
await update(item)
|
await update(item)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
@ -107,7 +108,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 {
|
||||||
alert(`切换 Profile 失败: ${errorMsg}`)
|
toast.error(errorMsg, '切换配置失败')
|
||||||
mutateProfileConfig()
|
mutateProfileConfig()
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { OverrideConfigProvider } from './hooks/use-override-config'
|
|||||||
import { ProfileConfigProvider } from './hooks/use-profile-config'
|
import { ProfileConfigProvider } from './hooks/use-profile-config'
|
||||||
import { RulesProvider } from './hooks/use-rules'
|
import { RulesProvider } from './hooks/use-rules'
|
||||||
import { GroupsProvider } from './hooks/use-groups'
|
import { GroupsProvider } from './hooks/use-groups'
|
||||||
|
import { ToastProvider } from './components/base/toast'
|
||||||
import './i18n'
|
import './i18n'
|
||||||
|
|
||||||
let F12Count = 0
|
let F12Count = 0
|
||||||
@ -53,7 +54,9 @@ init().then(() => {
|
|||||||
<OverrideConfigProvider>
|
<OverrideConfigProvider>
|
||||||
<GroupsProvider>
|
<GroupsProvider>
|
||||||
<RulesProvider>
|
<RulesProvider>
|
||||||
<App />
|
<ToastProvider>
|
||||||
|
<App />
|
||||||
|
</ToastProvider>
|
||||||
</RulesProvider>
|
</RulesProvider>
|
||||||
</GroupsProvider>
|
</GroupsProvider>
|
||||||
</OverrideConfigProvider>
|
</OverrideConfigProvider>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, Tab, Input, Switch, Tabs, Divider } from '@heroui/react'
|
import { Button, Tab, Input, Switch, Tabs, Divider } from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import { MdDeleteForever } from 'react-icons/md'
|
import { MdDeleteForever } from 'react-icons/md'
|
||||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||||
@ -145,7 +146,7 @@ const DNS: React.FC = () => {
|
|||||||
await restartCore()
|
await restartCore()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, Divider, Input, Select, SelectItem, Switch, Tooltip, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, useDisclosure, Spinner, Chip } from '@heroui/react'
|
import { Button, Divider, Input, Select, SelectItem, Switch, Tooltip, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, useDisclosure, Spinner, Chip } from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
@ -308,7 +309,7 @@ const Mihomo: React.FC = () => {
|
|||||||
if (errorMessage.includes('配置检查失败') || errorMessage.includes('Profile Check Failed')) {
|
if (errorMessage.includes('配置检查失败') || errorMessage.includes('Profile Check Failed')) {
|
||||||
await showDetailedError(t('mihomo.error.profileCheckFailed'), errorMessage)
|
await showDetailedError(t('mihomo.error.profileCheckFailed'), errorMessage)
|
||||||
} else {
|
} else {
|
||||||
alert(errorMessage)
|
toast.error(errorMessage)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
PubSub.publish('mihomo-core-changed')
|
PubSub.publish('mihomo-core-changed')
|
||||||
@ -324,7 +325,7 @@ const Mihomo: React.FC = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch tags:', error)
|
console.error('Failed to fetch tags:', error)
|
||||||
setTags([])
|
setTags([])
|
||||||
alert(t('mihomo.error.fetchTagsFailed'))
|
toast.error(t('mihomo.error.fetchTagsFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingTags(false)
|
setLoadingTags(false)
|
||||||
}
|
}
|
||||||
@ -355,7 +356,7 @@ const Mihomo: React.FC = () => {
|
|||||||
new Notification(t('mihomo.coreUpgradeSuccess'))
|
new Notification(t('mihomo.coreUpgradeSuccess'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to install specific core:', error)
|
console.error('Failed to install specific core:', error)
|
||||||
alert(t('mihomo.error.installCoreFailed'))
|
toast.error(t('mihomo.error.installCoreFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
setInstalling(false)
|
setInstalling(false)
|
||||||
}
|
}
|
||||||
@ -493,7 +494,7 @@ const Mihomo: React.FC = () => {
|
|||||||
if (typeof e === 'string' && e.includes('already using latest version')) {
|
if (typeof e === 'string' && e.includes('already using latest version')) {
|
||||||
new Notification(t('mihomo.alreadyLatestVersion'))
|
new Notification(t('mihomo.alreadyLatestVersion'))
|
||||||
} else {
|
} else {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setUpgrading(false)
|
setUpgrading(false)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
Input
|
Input
|
||||||
} from '@heroui/react'
|
} from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import { getFilePath, readTextFile } from '@renderer/utils/ipc'
|
import { getFilePath, readTextFile } from '@renderer/utils/ipc'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { MdContentPaste } from 'react-icons/md'
|
import { MdContentPaste } from 'react-icons/md'
|
||||||
@ -115,7 +116,7 @@ const Override: React.FC = () => {
|
|||||||
setFileOver(false)
|
setFileOver(false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(tRef.current('override.unsupportedFileType'))
|
toast.warning(tRef.current('override.unsupportedFileType'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setFileOver(false)
|
setFileOver(false)
|
||||||
@ -222,7 +223,7 @@ const Override: React.FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
} else if (key === 'new-yaml') {
|
} else if (key === 'new-yaml') {
|
||||||
await addOverrideItem({
|
await addOverrideItem({
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from '@heroui/react'
|
} from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import ProfileItem from '@renderer/components/profiles/profile-item'
|
import ProfileItem from '@renderer/components/profiles/profile-item'
|
||||||
import { useProfileConfig } from '@renderer/hooks/use-profile-config'
|
import { useProfileConfig } from '@renderer/hooks/use-profile-config'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
@ -197,10 +198,10 @@ const Profiles: React.FC = () => {
|
|||||||
const content = await readTextFile(path)
|
const content = await readTextFile(path)
|
||||||
await addProfileItemRef.current({ name: file.name, type: 'local', file: content })
|
await addProfileItemRef.current({ name: file.name, type: 'local', file: content })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(tRef.current('profiles.error.unsupportedFileType'))
|
toast.warning(tRef.current('profiles.error.unsupportedFileType'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setFileOver(false)
|
setFileOver(false)
|
||||||
@ -345,7 +346,7 @@ const Profiles: React.FC = () => {
|
|||||||
useProxy
|
useProxy
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setSubStoreImporting(false)
|
setSubStoreImporting(false)
|
||||||
}
|
}
|
||||||
@ -366,7 +367,7 @@ const Profiles: React.FC = () => {
|
|||||||
useProxy
|
useProxy
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setSubStoreImporting(false)
|
setSubStoreImporting(false)
|
||||||
}
|
}
|
||||||
@ -398,7 +399,7 @@ const Profiles: React.FC = () => {
|
|||||||
await addProfileItem({ name: fileName, type: 'local', file: content })
|
await addProfileItem({ name: fileName, type: 'local', file: content })
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
} else if (key === 'new') {
|
} else if (key === 'new') {
|
||||||
await addProfileItem({
|
await addProfileItem({
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, Divider, Input, Switch } from '@heroui/react'
|
import { Button, Divider, Input, Switch } from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||||
@ -70,7 +71,7 @@ const Sniffer: React.FC = () => {
|
|||||||
await restartCore()
|
await restartCore()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, Input, Tab, Tabs } from '@heroui/react'
|
import { Button, Input, Tab, Tabs } from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||||
import PacEditorModal from '@renderer/components/sysproxy/pac-editor-modal'
|
import PacEditorModal from '@renderer/components/sysproxy/pac-editor-modal'
|
||||||
@ -105,7 +106,7 @@ const Sysproxy: React.FC = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
setValues({ ...values, enable: previousState })
|
setValues({ ...values, enable: previousState })
|
||||||
setChanged(true)
|
setChanged(true)
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
|
|
||||||
await patchAppConfig({ sysProxy: { enable: false } })
|
await patchAppConfig({ sysProxy: { enable: false } })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, Input, Switch, Tab, Tabs } from '@heroui/react'
|
import { Button, Input, Switch, Tab, Tabs } from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
|
import { toast } from '@renderer/components/base/toast'
|
||||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||||
@ -112,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) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
@ -133,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) {
|
||||||
alert(e)
|
toast.error(String(e))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -556,10 +556,3 @@ export async function getRuleStr(id: string): Promise<string> {
|
|||||||
export async function setRuleStr(id: string, str: string): Promise<void> {
|
export async function setRuleStr(id: string, str: string): Promise<void> {
|
||||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setRuleStr', id, str))
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setRuleStr', id, str))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function alert<T>(msg: T): Promise<void> {
|
|
||||||
const msgStr = typeof msg === 'string' ? msg : JSON.stringify(msg)
|
|
||||||
return await window.electron.ipcRenderer.invoke('alert', msgStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
window.alert = alert
|
|
||||||
Loading…
x
Reference in New Issue
Block a user