import { useLocaleContext } from '@arcblock/ux/lib/Locale/context'; import { Box, LinearProgress, Skeleton, Typography } from '@mui/material'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Check } from '@mui/icons-material'; export function VendorPlaceholder() { return ( ); } interface VendorStatus { success: boolean; name?: string; key?: string; status: 'delivered' | 'pending' | 'failed'; progress: number; message: string; appUrl?: string; title?: string; vendorType: string; } const getVendorLabel = (vendor: VendorStatus, isFailed: boolean, t: any) => { const name = vendor.name || vendor.title; const isCompleted = vendor.status === 'delivered'; if (vendor.vendorType === 'didnames') { if (isFailed) { return t('payment.checkout.vendor.didnames.failed', { name }); } if (isCompleted) { return t('payment.checkout.vendor.didnames.completed', { name }); } return t('payment.checkout.vendor.didnames.processing', { name }); } // Default to launcher type if (isFailed) { return t('payment.checkout.vendor.launcher.failed', { name }); } if (isCompleted) { return t('payment.checkout.vendor.launcher.completed', { name }); } return t('payment.checkout.vendor.launcher.processing', { name }); }; export function VendorProgressItem({ vendor }: { vendor: VendorStatus }) { const { t } = useLocaleContext(); const [displayProgress, setDisplayProgress] = useState(0); const animationRef = useRef(); const startAnimation = useCallback(() => { const realProgress = vendor.progress || 0; let startTime: number; let startProgress: number; const animate = (currentTime: number) => { if (!startTime) { startTime = currentTime; startProgress = displayProgress; } const elapsed = currentTime - startTime; let newProgress: number; if (realProgress === 100) { // If progress is 100%, set directly to 100% without animation newProgress = 100; } else if (realProgress === 0) { // When no data available, increase 1% per second newProgress = Math.min(startProgress + elapsed / 1000, 99); } else if (realProgress > startProgress) { const duration = 1000; // 1 second to reach target quickly const progress = Math.min(elapsed / duration, 1); newProgress = startProgress + (realProgress - startProgress) * progress; } else { // After reaching target, increase 1% per second, max 99% newProgress = Math.min(startProgress + elapsed / 1000, 99); } // Ensure progress is an integer newProgress = Math.round(newProgress); setDisplayProgress((pre) => Math.min(pre > newProgress ? pre : newProgress, 100)); // Stop animation immediately when 100% if (realProgress === 100) { return; } // Continue animation in other cases if (newProgress < 99 && realProgress < 100) { animationRef.current = requestAnimationFrame(animate); } }; if (animationRef.current) { cancelAnimationFrame(animationRef.current); } animationRef.current = requestAnimationFrame(animate); }, [vendor.progress, displayProgress]); useEffect(() => { startAnimation(); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [startAnimation]); const isCompleted = displayProgress >= 100; const isFailed = vendor.status === 'failed'; const nameText = getVendorLabel(vendor, isFailed, t); // 如果是失败状态,显示错误 UI if (isFailed) { return ( {nameText} {t('payment.checkout.vendor.progress', { progress: 0 })} ); } if (!vendor.name && !vendor.title) { return ; } return ( {nameText} {isCompleted ? : null} {isCompleted ? null : ( {t('payment.checkout.vendor.progress', { progress: displayProgress })} )} ); }