fix: profiles cannot click

This commit is contained in:
pompurin404 2025-02-04 11:04:15 +08:00
parent 9acab62d1f
commit a69d24b29c
5 changed files with 322 additions and 329 deletions

View File

@ -80,11 +80,13 @@ const App: React.FC = () => {
const location = useLocation() const location = useLocation()
const page = useRoutes(routes) const page = useRoutes(routes)
const setTitlebar = (): void => { const setTitlebar = (): void => {
if (!useWindowFrame && platform !== 'darwin') { if (!useWindowFrame) {
const options = { height: 48 } as TitleBarOverlayOptions const options = { height: 48 } as TitleBarOverlayOptions
try { try {
options.color = window.getComputedStyle(document.documentElement).backgroundColor if (platform !== 'darwin') {
options.symbolColor = window.getComputedStyle(document.documentElement).color options.color = window.getComputedStyle(document.documentElement).backgroundColor
options.symbolColor = window.getComputedStyle(document.documentElement).color
}
setTitleBarOverlay(options) setTitleBarOverlay(options)
} catch (e) { } catch (e) {
// ignore // ignore

View File

@ -30,7 +30,7 @@ interface Props {
updateProfileItem: (item: IProfileItem) => Promise<void> updateProfileItem: (item: IProfileItem) => Promise<void>
removeProfileItem: (id: string) => Promise<void> removeProfileItem: (id: string) => Promise<void>
mutateProfileConfig: () => void mutateProfileConfig: () => void
onClick: () => Promise<void> onPress: () => Promise<void>
} }
interface MenuItem { interface MenuItem {
@ -48,7 +48,7 @@ const ProfileItem: React.FC<Props> = (props) => {
removeProfileItem, removeProfileItem,
mutateProfileConfig, mutateProfileConfig,
updateProfileItem, updateProfileItem,
onClick, onPress,
isCurrent isCurrent
} = props } = props
const extra = info?.extra const extra = info?.extra
@ -175,145 +175,142 @@ const ProfileItem: React.FC<Props> = (props) => {
)} )}
<Card <Card
fullWidth fullWidth
isPressable
onPress={() => {
if (disableSelect) return
setSelecting(true)
onPress().finally(() => {
setSelecting(false)
})
}}
className={`${isCurrent ? 'bg-primary' : ''} ${selecting ? 'blur-sm' : ''}`} className={`${isCurrent ? 'bg-primary' : ''} ${selecting ? 'blur-sm' : ''}`}
> >
<div <div ref={setNodeRef} {...attributes} {...listeners} className="w-full h-full">
className="w-full h-full cursor-pointer" <CardBody className="pb-1">
onClick={() => { <div className="flex justify-between h-[32px]">
if (disableSelect) return <h3
setSelecting(true) title={info?.name}
onClick().finally(() => { className={`text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
setSelecting(false) >
}) {info?.name}
}} </h3>
> <div className="flex">
<div ref={setNodeRef} {...attributes} {...listeners} className="w-full h-full"> {info.type === 'remote' && (
<CardBody className="pb-1"> <Tooltip placement="left" content={dayjs(info.updated).fromNow()}>
<div className="flex justify-between h-[32px]"> <Button
<h3 isIconOnly
title={info?.name} size="sm"
className={`text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`} variant="light"
> color="default"
{info?.name} disabled={updating}
</h3> onPress={async () => {
<div className="flex"> setUpdating(true)
{info.type === 'remote' && ( await addProfileItem(info)
<Tooltip placement="left" content={dayjs(info.updated).fromNow()}> setUpdating(false)
<Button }}
isIconOnly >
size="sm" <IoMdRefresh
variant="light"
color="default" color="default"
disabled={updating} className={`${isCurrent ? 'text-primary-foreground' : 'text-foreground'} text-[24px] ${updating ? 'animate-spin' : ''}`}
onPress={async () => { />
setUpdating(true) </Button>
await addProfileItem(info) </Tooltip>
setUpdating(false) )}
}}
>
<IoMdRefresh
color="default"
className={`${isCurrent ? 'text-primary-foreground' : 'text-foreground'} text-[24px] ${updating ? 'animate-spin' : ''}`}
/>
</Button>
</Tooltip>
)}
<Dropdown> <Dropdown>
<DropdownTrigger> <DropdownTrigger>
<Button isIconOnly size="sm" variant="light" color="default"> <Button isIconOnly size="sm" variant="light" color="default">
<IoMdMore <IoMdMore
color="default" color="default"
className={`text-[24px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`} className={`text-[24px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
/> />
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu onAction={onMenuAction}> <DropdownMenu onAction={onMenuAction}>
{menuItems.map((item) => ( {menuItems.map((item) => (
<DropdownItem <DropdownItem
showDivider={item.showDivider} showDivider={item.showDivider}
key={item.key} key={item.key}
color={item.color} color={item.color}
className={item.className} className={item.className}
> >
{item.label} {item.label}
</DropdownItem> </DropdownItem>
))} ))}
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
</div>
</div> </div>
{info.type === 'remote' && extra && ( </div>
<div {info.type === 'remote' && extra && (
className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`} <div
> className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
<small>{`${calcTraffic(usage)}/${calcTraffic(total)}`}</small> >
{profileDisplayDate === 'expire' ? ( <small>{`${calcTraffic(usage)}/${calcTraffic(total)}`}</small>
<Button {profileDisplayDate === 'expire' ? (
size="sm" <Button
variant="light"
className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
onPress={async () => {
await patchAppConfig({ profileDisplayDate: 'update' })
}}
>
{extra.expire ? dayjs.unix(extra.expire).format('YYYY-MM-DD') : '长期有效'}
</Button>
) : (
<Button
size="sm"
variant="light"
className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
onPress={async () => {
await patchAppConfig({ profileDisplayDate: 'expire' })
}}
>
{dayjs(info.updated).fromNow()}
</Button>
)}
</div>
)}
</CardBody>
<CardFooter className="pt-0">
{info.type === 'remote' && !extra && (
<div
className={`w-full mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
>
<Chip
size="sm" size="sm"
variant="bordered" variant="light"
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`} className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
onPress={async () => {
await patchAppConfig({ profileDisplayDate: 'update' })
}}
> >
{extra.expire ? dayjs.unix(extra.expire).format('YYYY-MM-DD') : '长期有效'}
</Button>
) : (
<Button
size="sm"
variant="light"
className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
onPress={async () => {
await patchAppConfig({ profileDisplayDate: 'expire' })
}}
>
{dayjs(info.updated).fromNow()}
</Button>
)}
</div>
)}
</CardBody>
<CardFooter className="pt-0">
{info.type === 'remote' && !extra && (
<div
className={`w-full mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
>
<Chip
size="sm"
variant="bordered"
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`}
>
{t('profiles.remote')} {t('profiles.remote')}
</Chip> </Chip>
<small>{dayjs(info.updated).fromNow()}</small> <small>{dayjs(info.updated).fromNow()}</small>
</div> </div>
)} )}
{info.type === 'local' && ( {info.type === 'local' && (
<div <div
className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`} className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
>
<Chip
size="sm"
variant="bordered"
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`}
> >
<Chip
size="sm"
variant="bordered"
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`}
>
{t('profiles.local')} {t('profiles.local')}
</Chip> </Chip>
</div> </div>
)} )}
{extra && ( {extra && (
<Progress <Progress
className="w-full" className="w-full"
aria-label={t('profiles.trafficUsage')} aria-label={t('profiles.trafficUsage')}
classNames={{ classNames={{
indicator: isCurrent ? 'bg-primary-foreground' : 'bg-foreground' indicator: isCurrent ? 'bg-primary-foreground' : 'bg-foreground'
}} }}
value={calcPercent(extra?.upload, extra?.download, extra?.total)} value={calcPercent(extra?.upload, extra?.download, extra?.total)}
/> />
)} )}
</CardFooter> </CardFooter>
</div>
</div> </div>
</Card> </Card>
</div> </div>

View File

@ -51,6 +51,8 @@ const ProxyItem: React.FC<Props> = (props) => {
return ( return (
<Card <Card
onPress={() => onSelect(group.name, proxy.name)}
isPressable
fullWidth fullWidth
shadow="sm" shadow="sm"
className={`${ className={`${
@ -62,98 +64,93 @@ const ProxyItem: React.FC<Props> = (props) => {
}`} }`}
radius="sm" radius="sm"
> >
<div <CardBody className="p-1">
onClick={() => onSelect(group.name, proxy.name)} {proxyDisplayMode === 'full' ? (
className="cursor-pointer" <div className="flex flex-col gap-1">
> <div className="flex justify-between items-center pl-1">
<CardBody className="p-1"> <div className="text-ellipsis overflow-hidden whitespace-nowrap" title={proxy.name}>
{proxyDisplayMode === 'full' ? ( {proxy.name}
<div className="flex flex-col gap-1">
<div className="flex justify-between items-center pl-1">
<div className="text-ellipsis overflow-hidden whitespace-nowrap" title={proxy.name}>
{proxy.name}
</div>
{fixed && (
<Button
isIconOnly
title={t('proxies.unpin')}
color="danger"
onPress={async () => {
await mihomoUnfixedProxy(group.name)
mutateProxies()
}}
variant="light"
className="h-[20px] p-0 text-sm"
>
<FaMapPin className="text-md le" />
</Button>
)}
</div> </div>
<div className="flex justify-between items-center pl-1"> {fixed && (
<div className="flex gap-1 items-center">
<div className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
{proxy.type}
</div>
{['tfo', 'udp', 'xudp', 'mptcp', 'smux'].map(protocol =>
proxy[protocol as keyof IMihomoProxy] && (
<div key={protocol} className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
{protocol}
</div>
)
)}
</div>
<Button <Button
isIconOnly isIconOnly
title={proxy.type}
isLoading={loading}
color={delayColor(delay)}
onPress={onDelay}
variant="light"
className="h-full text-sm ml-auto -mt-0.5"
>
{delayText(delay)}
</Button>
</div>
</div>
) : (
<div className="flex justify-between items-center pl-1 p-1">
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
<div className="flag-emoji inline" title={proxy.name}>
{proxy.name}
</div>
</div>
<div className="flex justify-end">
{fixed && (
<Button
isIconOnly
title={t('proxies.unpin')} title={t('proxies.unpin')}
color="danger" color="danger"
onPress={async () => { onPress={async () => {
await mihomoUnfixedProxy(group.name) await mihomoUnfixedProxy(group.name)
mutateProxies() mutateProxies()
}} }}
variant="light"
className="h-[20px] p-0 text-sm"
>
<FaMapPin className="text-md le" />
</Button>
)}
<Button
isIconOnly
title={proxy.type}
isLoading={loading}
color={delayColor(delay)}
onPress={onDelay}
variant="light" variant="light"
className="h-full p-0 text-sm" className="h-[20px] p-0 text-sm"
> >
{delayText(delay)} <FaMapPin className="text-md le" />
</Button> </Button>
</div> )}
</div> </div>
)} <div className="flex justify-between items-center pl-1">
</CardBody> <div className="flex gap-1 items-center">
</div> <div className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
{proxy.type}
</div>
{['tfo', 'udp', 'xudp', 'mptcp', 'smux'].map(protocol =>
proxy[protocol as keyof IMihomoProxy] && (
<div key={protocol} className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
{protocol}
</div>
)
)}
</div>
<Button
isIconOnly
title={proxy.type}
isLoading={loading}
color={delayColor(delay)}
onPress={onDelay}
variant="light"
className="h-full text-sm ml-auto -mt-0.5"
>
{delayText(delay)}
</Button>
</div>
</div>
) : (
<div className="flex justify-between items-center pl-1 p-1">
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
<div className="flag-emoji inline" title={proxy.name}>
{proxy.name}
</div>
</div>
<div className="flex justify-end">
{fixed && (
<Button
isIconOnly
title={t('proxies.unpin')}
color="danger"
onPress={async () => {
await mihomoUnfixedProxy(group.name)
mutateProxies()
}}
variant="light"
className="h-[20px] p-0 text-sm"
>
<FaMapPin className="text-md le" />
</Button>
)}
<Button
isIconOnly
title={proxy.type}
isLoading={loading}
color={delayColor(delay)}
onPress={onDelay}
variant="light"
className="h-full p-0 text-sm"
>
{delayText(delay)}
</Button>
</div>
</div>
)}
</CardBody>
</Card> </Card>
) )
} }

View File

@ -395,7 +395,7 @@ const Profiles: React.FC = () => {
mutateProfileConfig={mutateProfileConfig} mutateProfileConfig={mutateProfileConfig}
updateProfileItem={updateProfileItem} updateProfileItem={updateProfileItem}
info={item} info={item}
onClick={async () => { onPress={async () => {
await changeCurrentProfile(item.id) await changeCurrentProfile(item.id)
}} }}
/> />

View File

@ -329,121 +329,118 @@ const Proxies: React.FC = () => {
className={`w-full pt-2 ${index === groupCounts.length - 1 && !isOpen[index] ? 'pb-2' : ''} px-2`} className={`w-full pt-2 ${index === groupCounts.length - 1 && !isOpen[index] ? 'pb-2' : ''} px-2`}
> >
<Card <Card
isPressable
fullWidth fullWidth
onPress={() => {
setIsOpen((prev) => {
const newOpen = [...prev]
newOpen[index] = !prev[index]
return newOpen
})
}}
> >
<div <CardBody className="w-full">
onClick={(): void => { <div className="flex justify-between">
setIsOpen((prev) => { <div className="flex text-ellipsis overflow-hidden whitespace-nowrap">
const newOpen = [...prev] {groups[index].icon ? (
newOpen[index] = !prev[index] <Avatar
return newOpen className="bg-transparent mr-2"
}) size="sm"
}} radius="sm"
className="cursor-pointer" src={
> groups[index].icon.startsWith('<svg')
<CardBody className="w-full"> ? `data:image/svg+xml;utf8,${groups[index].icon}`
<div className="flex justify-between"> : localStorage.getItem(groups[index].icon) || groups[index].icon
<div className="flex text-ellipsis overflow-hidden whitespace-nowrap"> }
{groups[index].icon ? ( />
<Avatar ) : null}
className="bg-transparent mr-2" <div className="text-ellipsis overflow-hidden whitespace-nowrap">
size="sm" <div
radius="sm" title={groups[index].name}
src={ className="inline flag-emoji h-[32px] text-md leading-[32px]"
groups[index].icon.startsWith('<svg') >
? `data:image/svg+xml;utf8,${groups[index].icon}` {groups[index].name}
: localStorage.getItem(groups[index].icon) || groups[index].icon
}
/>
) : null}
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
<div
title={groups[index].name}
className="inline flag-emoji h-[32px] text-md leading-[32px]"
>
{groups[index].name}
</div>
{proxyDisplayMode === 'full' && (
<div
title={groups[index].type}
className="inline ml-2 text-sm text-foreground-500"
>
{groups[index].type}
</div>
)}
{proxyDisplayMode === 'full' && (
<div className="inline flag-emoji ml-2 text-sm text-foreground-500">
{groups[index].now}
</div>
)}
</div> </div>
</div>
<div className="flex">
{proxyDisplayMode === 'full' && ( {proxyDisplayMode === 'full' && (
<Chip size="sm" className="my-1 mr-2"> <div
{groups[index].all.length} title={groups[index].type}
</Chip> className="inline ml-2 text-sm text-foreground-500"
>
{groups[index].type}
</div>
)}
{proxyDisplayMode === 'full' && (
<div className="inline flag-emoji ml-2 text-sm text-foreground-500">
{groups[index].now}
</div>
)} )}
<CollapseInput
title={t('proxies.search.placeholder')}
value={searchValue[index]}
onValueChange={(v) => {
setSearchValue((prev) => {
const newSearchValue = [...prev]
newSearchValue[index] = v
return newSearchValue
})
}}
/>
<Button
title={t('proxies.locate')}
variant="light"
size="sm"
isIconOnly
onPress={() => {
if (!isOpen[index]) {
setIsOpen((prev) => {
const newOpen = [...prev]
newOpen[index] = true
return newOpen
})
}
let i = 0
for (let j = 0; j < index; j++) {
i += groupCounts[j]
}
i += Math.floor(
allProxies[index].findIndex(
(proxy) => proxy.name === groups[index].now
) / cols
)
virtuosoRef.current?.scrollToIndex({
index: Math.floor(i),
align: 'start'
})
}}
>
<FaLocationCrosshairs className="text-lg text-foreground-500" />
</Button>
<Button
title={t('proxies.delay.test')}
variant="light"
isLoading={delaying[index]}
size="sm"
isIconOnly
onPress={() => {
onGroupDelay(index)
}}
>
<MdOutlineSpeed className="text-lg text-foreground-500" />
</Button>
<IoIosArrowBack
className={`transition duration-200 ml-2 h-[32px] text-lg text-foreground-500 ${isOpen[index] ? '-rotate-90' : ''}`}
/>
</div> </div>
</div> </div>
</CardBody> <div className="flex">
</div> {proxyDisplayMode === 'full' && (
<Chip size="sm" className="my-1 mr-2">
{groups[index].all.length}
</Chip>
)}
<CollapseInput
title={t('proxies.search.placeholder')}
value={searchValue[index]}
onValueChange={(v) => {
setSearchValue((prev) => {
const newSearchValue = [...prev]
newSearchValue[index] = v
return newSearchValue
})
}}
/>
<Button
title={t('proxies.locate')}
variant="light"
size="sm"
isIconOnly
onPress={() => {
if (!isOpen[index]) {
setIsOpen((prev) => {
const newOpen = [...prev]
newOpen[index] = true
return newOpen
})
}
let i = 0
for (let j = 0; j < index; j++) {
i += groupCounts[j]
}
i += Math.floor(
allProxies[index].findIndex(
(proxy) => proxy.name === groups[index].now
) / cols
)
virtuosoRef.current?.scrollToIndex({
index: Math.floor(i),
align: 'start'
})
}}
>
<FaLocationCrosshairs className="text-lg text-foreground-500" />
</Button>
<Button
title={t('proxies.delay.test')}
variant="light"
isLoading={delaying[index]}
size="sm"
isIconOnly
onPress={() => {
onGroupDelay(index)
}}
>
<MdOutlineSpeed className="text-lg text-foreground-500" />
</Button>
<IoIosArrowBack
className={`transition duration-200 ml-2 h-[32px] text-lg text-foreground-500 ${isOpen[index] ? '-rotate-90' : ''}`}
/>
</div>
</div>
</CardBody>
</Card> </Card>
</div> </div>
) : ( ) : (