import { useState } from 'react'; import { Button, Typography, Stack, Alert } from '@mui/material'; import { useLocaleContext } from '@arcblock/ux/lib/Locale/context'; import Toast from '@arcblock/ux/lib/Toast'; import { joinURL } from 'ufo'; import type { Subscription, TSubscriptionExpanded } from '@blocklet/payment-types'; import { useRequest } from 'ahooks'; import Dialog from '@arcblock/ux/lib/Dialog/dialog'; import { usePaymentContext } from '../contexts/payment'; import { formatError, formatToDate, getPrefix, isCrossOrigin } from '../libs/util'; import api from '../libs/api'; import LoadingButton from './loading-button'; type DialogProps = { open?: boolean; onClose?: () => void; title?: string; }; type Props = { subscriptionId: string; onResumed?: (subscription: Subscription | TSubscriptionExpanded) => void; dialogProps?: DialogProps; successToast?: boolean; authToken?: string; }; type RecoverInfo = { subscription: Subscription; needStake?: boolean; }; const fetchSubscriptionDetail = async (params: { subscriptionId: string; authToken?: string; }): Promise => { const url = `/api/subscriptions/${params.subscriptionId}`; const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url); return res.data; }; const fetchRecoverInfo = async (params: { subscriptionId: string; authToken?: string }): Promise => { const url = `/api/subscriptions/${params.subscriptionId}/recover-info`; const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url); return res.data; }; const recoverSubscription = async (params: { subscriptionId: string; authToken?: string; }): Promise<{ subscription: Subscription; needStake?: boolean; }> => { const url = `/api/subscriptions/${params.subscriptionId}/recover`; const res = await api.put(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url); return res.data; }; function RecoverSubscription({ subscriptionId, dialogProps = { open: true, }, onResumed = () => {}, successToast = true, authToken = undefined, }: Props) { const { t, locale } = useLocaleContext(); const { connect } = usePaymentContext(); const [recoverLoading, setRecoverLoading] = useState(false); const [dialogOpen, setDialogOpen] = useState(dialogProps.open || false); const { data, error, loading } = useRequest(() => fetchRecoverInfo({ subscriptionId, authToken }), { ready: !!subscriptionId, }); const isCrossOriginRequest = isCrossOrigin(); const needStake = data?.needStake ?? false; const handleSuccess = async () => { try { // 获取最新的订阅数据 const subscription = await fetchSubscriptionDetail({ subscriptionId, authToken }); if (successToast) { Toast.success(t('payment.customer.recover.success')); } setDialogOpen(false); onResumed(subscription); setRecoverLoading(false); } catch (err) { console.error('Failed to fetch updated subscription:', err); Toast.error(formatError(err)); setRecoverLoading(false); } }; const handleRecoverWithStake = () => { setRecoverLoading(true); try { connect.open({ locale: locale as 'en' | 'zh', containerEl: undefined as unknown as Element, saveConnect: false, action: 're-stake', prefix: joinURL(getPrefix(), '/api/did'), useSocket: !isCrossOriginRequest, extraParams: { subscriptionId }, messages: { scan: t('common.connect.defaultScan'), title: t('payment.customer.recover.reStakeTitle'), confirm: t('common.connect.confirm'), } as any, onSuccess: handleSuccess, onClose: () => { connect.close(); setRecoverLoading(false); }, onError: (err: any) => { Toast.error(formatError(err)); setRecoverLoading(false); }, }); } catch (err) { Toast.error(formatError(err)); setRecoverLoading(false); } }; const handleDirectRecover = async () => { setRecoverLoading(true); try { const result = await recoverSubscription({ subscriptionId, authToken }); if (result.needStake) { handleRecoverWithStake(); return; } const { subscription } = result; if (successToast) { Toast.success(t('payment.customer.recover.success')); } setDialogOpen(false); onResumed(subscription); setRecoverLoading(false); } catch (err: any) { Toast.error(formatError(err)); setRecoverLoading(false); } }; const handleRecover = () => { if (needStake) { handleRecoverWithStake(); } else { handleDirectRecover(); } }; const handleClose = () => { setDialogOpen(false); dialogProps.onClose?.(); }; if (loading) { return null; } return ( {error ? ( {error.message} ) : ( {t('payment.customer.recover.description', { date: formatToDate(Number(data?.subscription?.current_period_end || '0') * 1000), })} {needStake && {t('payment.customer.recover.stakeRequiredDescription')}} {t('common.confirm')} )} ); } export default RecoverSubscription;