import { CloudUploadOutlined, DnsOutlined, EventOutlined, LaunchOutlined, SpeedOutlined, StorageOutlined, UpdateOutlined, } from '@mui/icons-material' import { Box, Button, LinearProgress, Link, Stack, Typography, alpha, keyframes, useTheme, } from '@mui/material' import { useLockFn } from 'ahooks' import dayjs from 'dayjs' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router' import { useAppRefreshers } from '@/providers/app-data-context' import { updateProfile } from '@/services/cmds' import { mutate } from '@/services/mutate' import { showNotice } from '@/services/notice-service' import { isValidUrl } from '@/utils/network' import { openExternalUrl } from '@/utils/open-external-url' import parseTraffic from '@/utils/parse-traffic' import { EnhancedCard } from './enhanced-card' // 定义旋转动画 const round = keyframes` from { transform: rotate(0deg); } to { transform: rotate(360deg); } ` // 辅助函数解析URL和过期时间 const parseUrl = (url?: string) => { if (!url) return '-' if (isValidUrl(url)) return new URL(url).host return 'local' } const parseExpire = (expire?: number) => { if (!expire) return '-' return dayjs(expire * 1000).format('YYYY-MM-DD') } const openProfileHome = (url: string) => { void openExternalUrl(url).catch(showNotice.error) } type ProfileItem = IProfileItem interface HomeProfileCardProps { current: ProfileItem | null | undefined onProfileUpdated?: () => void } // 提取独立组件减少主组件复杂度 const ProfileDetails = ({ current, onUpdateProfile, updating, }: { current: ProfileItem onUpdateProfile: () => void updating: boolean }) => { const { t } = useTranslation() const theme = useTheme() const usedTraffic = useMemo(() => { if (!current.extra) return 0 return current.extra.upload + current.extra.download }, [current.extra]) const trafficPercentage = useMemo(() => { if (!current.extra || !current.extra.total || current.extra.total <= 0) return 0 return Math.min(Math.round((usedTraffic / current.extra.total) * 100), 100) }, [current.extra, usedTraffic]) return ( {current.url && ( {t('shared.labels.from')}: {current.home ? ( current.home && openProfileHome(current.home)} sx={{ display: 'inline-flex', alignItems: 'center', minWidth: 0, maxWidth: 'calc(100% - 40px)', ml: 0.5, fontWeight: 'medium', }} title={parseUrl(current.url)} > {parseUrl(current.url)} ) : ( {parseUrl(current.url)} )} )} {current.updated && ( {t('shared.labels.updateTime')}:{' '} {dayjs(current.updated * 1000).format('YYYY-MM-DD HH:mm')} )} {current.extra && ( <> {t('shared.labels.usedTotal')}:{' '} {parseTraffic(usedTraffic)} /{' '} {parseTraffic(current.extra.total)} {current.extra.expire > 0 && ( {t('shared.labels.expireTime')}:{' '} {parseExpire(current.extra.expire)} )} {trafficPercentage}% )} ) } // 提取空配置组件 const EmptyProfile = ({ onClick }: { onClick: () => void }) => { const { t } = useTranslation() return ( {t('profiles.page.actions.import')} {t('profiles.page.title')} {t('profiles.components.card.labels.clickToImport')} ) } export const HomeProfileCard = ({ current, onProfileUpdated, }: HomeProfileCardProps) => { const { t } = useTranslation() const navigate = useNavigate() const { refreshAll } = useAppRefreshers() // 更新当前订阅 const [updating, setUpdating] = useState(false) const onUpdateProfile = useLockFn(async () => { if (!current?.uid) return setUpdating(true) try { await mutate( () => updateProfile(current.uid!, current.option ?? undefined), { id: `update-profile:${current.uid}`, errorNotice: false, }, ) onProfileUpdated?.() // 刷新首页数据 refreshAll() } catch (err) { showNotice.error(err, 3000) } finally { setUpdating(false) } }) // 导航到订阅页面 const goToProfiles = useCallback(() => { navigate('/profile') }, [navigate]) // 卡片标题 const cardTitle = useMemo(() => { if (!current) return t('profiles.page.title') if (!current.home) return current.name return ( current.home && openProfileHome(current.home)} sx={{ color: 'inherit', textDecoration: 'none', display: 'flex', alignItems: 'center', minWidth: 0, maxWidth: '100%', fontWeight: 'medium', fontSize: 18, '& > span': { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1, }, }} title={current.name!} > {current.name} ) }, [current, t]) // 卡片操作按钮 const cardAction = useMemo(() => { if (!current) return null return ( ) }, [current, goToProfiles, t]) return ( } iconColor="info" action={cardAction} > {current ? ( ) : ( )} ) }