Initialization constant

This commit is contained in:
pompurin404 2024-08-05 14:04:40 +08:00
parent ae5939d2aa
commit 279f8fe1d3
No known key found for this signature in database
9 changed files with 169 additions and 46 deletions

View File

@ -51,9 +51,14 @@ export async function addProfileItem(item: Partial<IProfileItem>): Promise<void>
export function removeProfileItem(id: string): void {
profileConfig.items = profileConfig.items?.filter((item) => item.id !== id)
if (profileConfig.current === id) {
profileConfig.current = profileConfig.items[0]?.id
if (profileConfig.items.length > 0) {
profileConfig.current = profileConfig.items[0]?.id
} else {
profileConfig.current = undefined
}
}
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
fs.rmSync(profilePath(id))
window?.webContents.send('profileConfigUpdated')
}

View File

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta charset="UTF-8" lang="zh" />
<title>Mihomo Party</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta

View File

@ -1,39 +1,131 @@
import { Button, Card, CardBody, CardFooter, Progress } from '@nextui-org/react'
import {
Button,
Card,
CardBody,
CardFooter,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownTrigger,
Progress
} from '@nextui-org/react'
import { calcPercent, calcTraffic } from '@renderer/utils/calc'
import React from 'react'
import { IoMdRefresh } from 'react-icons/io'
import { IoMdMore, IoMdRefresh } from 'react-icons/io'
import dayjs from 'dayjs'
import React, { Key, useMemo } from 'react'
interface Props {
info: IProfileItem
isCurrent: boolean
removeProfileItem: (id: string) => Promise<void>
mutateProfileConfig: () => void
onClick: () => Promise<void>
}
interface MenuItem {
key: string
label: string
showDivider: boolean
color: 'default' | 'danger'
className: string
}
const ProfileItem: React.FC<Props> = (props) => {
const { info, onClick, isCurrent } = props
const { info, removeProfileItem, mutateProfileConfig, onClick, isCurrent } = props
const extra = info?.extra
const usage = (extra?.upload ?? 0) + (extra?.download ?? 0)
const total = extra?.total ?? 0
const menuItems: MenuItem[] = useMemo(() => {
const list = [
{
key: 'edit',
label: '编辑文件',
showDivider: true,
color: 'default',
className: ''
} as MenuItem,
{
key: 'delete',
label: '删除',
showDivider: false,
color: 'danger',
className: 'text-danger'
} as MenuItem
]
if (info.home) {
list.unshift({
key: 'home',
label: '主页',
showDivider: false,
color: 'default',
className: ''
} as MenuItem)
}
return list
}, [info])
const onMenuAction = (key: Key): void => {
switch (key) {
case 'edit':
break
case 'delete': {
removeProfileItem(info.id)
mutateProfileConfig()
break
}
case 'home': {
open(info.home)
break
}
}
}
return (
<Card fullWidth isPressable onPress={onClick} className={isCurrent ? 'bg-primary' : ''}>
<CardBody>
<CardBody className="pb-1">
<div className="flex justify-between h-[32px]">
<h3 className="select-none text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px]">
{info?.name}
</h3>
<Button isIconOnly size="sm" variant="light" color="default">
<IoMdRefresh color="default" className="text-[24px]" />
</Button>
<div className="flex">
<Button isIconOnly size="sm" variant="light" color="default">
<IoMdRefresh color="default" className="text-[24px]" />
</Button>
<Dropdown>
<DropdownTrigger>
<Button isIconOnly size="sm" variant="light" color="default">
<IoMdMore color="default" className="text-[24px]" />
</Button>
</DropdownTrigger>
<DropdownMenu onAction={onMenuAction}>
{menuItems.map((item) => (
<DropdownItem
showDivider={item.showDivider}
key={item.key}
color={item.color}
className={item.className}
>
{item.label}
</DropdownItem>
))}
</DropdownMenu>
</Dropdown>
</div>
</div>
<div className="mt-2 flex justify-between">
<small>{extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}</small>
<small>{dayjs(info.updated).fromNow()}</small>
</div>
</CardBody>
<CardFooter className="pt-1">
<Progress
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
label={extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
className="max-w-md"
/>
<CardFooter className="pt-0">
{extra && (
<Progress
className="w-full"
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
/>
)}
</CardFooter>
</Card>
)

View File

@ -3,6 +3,12 @@ import { useProfileConfig } from '@renderer/hooks/use-profile-config'
import { useLocation, useNavigate } from 'react-router-dom'
import { calcTraffic, calcPercent } from '@renderer/utils/calc'
import { IoMdRefresh } from 'react-icons/io'
import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/zh-cn'
import dayjs from 'dayjs'
dayjs.extend(relativeTime)
dayjs.locale('zh-cn')
const ProfileCard: React.FC = () => {
const navigate = useNavigate()
@ -16,6 +22,7 @@ const ProfileCard: React.FC = () => {
type: 'local',
name: '空白订阅'
}
const extra = info?.extra
const usage = (extra?.upload ?? 0) + (extra?.download ?? 0)
const total = extra?.total ?? 0
@ -27,7 +34,7 @@ const ProfileCard: React.FC = () => {
isPressable
onPress={() => navigate('/profiles')}
>
<CardBody>
<CardBody className="pb-1">
<div className="flex justify-between h-[32px]">
<h3 className="select-none text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px]">
{info?.name}
@ -36,14 +43,19 @@ const ProfileCard: React.FC = () => {
<IoMdRefresh color="default" className="text-[24px]" />
</Button>
</div>
<div className="mt-2 flex justify-between">
<small>{extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}</small>
<small>{dayjs(info.updated).fromNow()}</small>
</div>
</CardBody>
<CardFooter className="pt-1">
<Progress
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
label={extra ? `${calcTraffic(usage)}/${calcTraffic(total)}` : undefined}
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
className="max-w-md"
/>
<CardFooter className="pt-0">
{extra && (
<Progress
className="w-full"
classNames={{ indicator: 'bg-foreground', label: 'select-none' }}
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
/>
)}
</CardFooter>
</Card>
)

View File

@ -3,12 +3,8 @@ import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-c
import BorderSwitch from '@renderer/components/base/border-swtich'
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
import { useLocation, useNavigate } from 'react-router-dom'
import {
platform,
encryptString,
patchMihomoConfig,
isEncryptionAvailable
} from '@renderer/utils/ipc'
import { encryptString, patchMihomoConfig, isEncryptionAvailable } from '@renderer/utils/ipc'
import { platform } from '@renderer/utils/init'
import React, { useState } from 'react'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import BasePasswordModal from '../base/base-password-modal'
@ -24,7 +20,7 @@ const TunSwitcher: React.FC = () => {
const { enable } = tun || {}
const onChange = async (enable: boolean): Promise<void> => {
if (enable && (await platform()) !== 'win32') {
if (enable && platform !== 'win32') {
const encryptionAvailable = await isEncryptionAvailable()
if (!appConfig?.encryptedPassword && encryptionAvailable) {
setOpenPasswordModal(true)

View File

@ -3,18 +3,20 @@ import ReactDOM from 'react-dom/client'
import { HashRouter } from 'react-router-dom'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { NextUIProvider } from '@nextui-org/react'
import '@renderer/utils/init'
import { init } from '@renderer/utils/init'
import '@renderer/assets/main.css'
import App from '@renderer/App'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<NextUIProvider>
<NextThemesProvider attribute="class" defaultTheme="dark">
<HashRouter>
<App />
</HashRouter>
</NextThemesProvider>
</NextUIProvider>
</React.StrictMode>
)
init().then(() => {
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<NextUIProvider>
<NextThemesProvider attribute="class" defaultTheme="dark">
<HashRouter>
<App />
</HashRouter>
</NextThemesProvider>
</NextUIProvider>
</React.StrictMode>
)
})

View File

@ -6,7 +6,13 @@ import { useState } from 'react'
import { MdContentPaste } from 'react-icons/md'
const Profiles: React.FC = () => {
const { profileConfig, addProfileItem, changeCurrentProfile } = useProfileConfig()
const {
profileConfig,
addProfileItem,
removeProfileItem,
changeCurrentProfile,
mutateProfileConfig
} = useProfileConfig()
const { current, items } = profileConfig || {}
const [importing, setImporting] = useState(false)
const [url, setUrl] = useState('')
@ -55,6 +61,8 @@ const Profiles: React.FC = () => {
<ProfileItem
key={item.id}
isCurrent={item.id === current}
removeProfileItem={removeProfileItem}
mutateProfileConfig={mutateProfileConfig}
info={item}
onClick={async () => {
await changeCurrentProfile(item.id)

View File

@ -1,4 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { getPlatform } from './ipc'
const originError = console.error
const originWarn = console.warn
console.error = function (...args: any[]): void {
@ -13,3 +15,9 @@ console.warn = function (...args): void {
}
originWarn.call(console, args)
}
export let platform: NodeJS.Platform
export async function init(): Promise<void> {
platform = await getPlatform()
}

View File

@ -115,7 +115,7 @@ export async function encryptString(str: string): Promise<Buffer> {
return await window.electron.ipcRenderer.invoke('encryptString', str)
}
export async function platform(): Promise<NodeJS.Platform> {
export async function getPlatform(): Promise<NodeJS.Platform> {
return await window.electron.ipcRenderer.invoke('platform')
}