refactor: optimize credits balance card logic after successful payment

This commit is contained in:
javayhu 2025-08-21 09:50:40 +08:00
parent 1fb89a2a05
commit cf8a7f1242
2 changed files with 23 additions and 41 deletions

View File

@ -11,11 +11,7 @@ import {
} from '@/components/ui/card'; } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton'; import { Skeleton } from '@/components/ui/skeleton';
import { websiteConfig } from '@/config/website'; import { websiteConfig } from '@/config/website';
import { import { useCreditBalance, useCreditStats } from '@/hooks/use-credits';
creditsKeys,
useCreditBalance,
useCreditStats,
} from '@/hooks/use-credits';
import { useMounted } from '@/hooks/use-mounted'; import { useMounted } from '@/hooks/use-mounted';
import { useCurrentPlan } from '@/hooks/use-payment'; import { useCurrentPlan } from '@/hooks/use-payment';
import { useLocaleRouter } from '@/i18n/navigation'; import { useLocaleRouter } from '@/i18n/navigation';
@ -23,7 +19,6 @@ import { authClient } from '@/lib/auth-client';
import { formatDate } from '@/lib/formatter'; import { formatDate } from '@/lib/formatter';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Routes } from '@/routes'; import { Routes } from '@/routes';
import { useQueryClient } from '@tanstack/react-query';
import { RefreshCwIcon } from 'lucide-react'; import { RefreshCwIcon } from 'lucide-react';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
@ -42,7 +37,6 @@ export default function CreditsBalanceCard() {
const t = useTranslations('Dashboard.settings.credits.balance'); const t = useTranslations('Dashboard.settings.credits.balance');
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const localeRouter = useLocaleRouter(); const localeRouter = useLocaleRouter();
const queryClient = useQueryClient();
const hasHandledSession = useRef(false); const hasHandledSession = useRef(false);
const mounted = useMounted(); const mounted = useMounted();
@ -67,6 +61,21 @@ export default function CreditsBalanceCard() {
refetch: refetchStats, refetch: refetchStats,
} = useCreditStats(); } = useCreditStats();
// Handle payment success after credits purchase
const handlePaymentSuccess = useCallback(async () => {
// Use queueMicrotask to avoid React rendering conflicts
queueMicrotask(() => {
toast.success(t('creditsAdded'));
});
// Wait for webhook to process (simplified approach)
await new Promise((resolve) => setTimeout(resolve, 1000));
// Force refresh data
refetchBalance();
refetchStats();
}, [t, refetchBalance, refetchStats]);
// Check for payment success and show success message // Check for payment success and show success message
useEffect(() => { useEffect(() => {
const sessionId = searchParams.get('credits_session_id'); const sessionId = searchParams.get('credits_session_id');
@ -78,40 +87,17 @@ export default function CreditsBalanceCard() {
url.searchParams.delete('credits_session_id'); url.searchParams.delete('credits_session_id');
localeRouter.replace(Routes.SettingsCredits + url.search); localeRouter.replace(Routes.SettingsCredits + url.search);
// Handle payment success with proper timing // Handle payment success
const handlePaymentSuccess = async () => {
// Show success toast (must be in setTimeout to avoid errors)
setTimeout(() => {
toast.success(t('creditsAdded'));
}, 0);
// Wait for webhook to process
await new Promise((resolve) => setTimeout(resolve, 1000));
// Force refresh data
queryClient.invalidateQueries({
queryKey: creditsKeys.balance(),
});
queryClient.invalidateQueries({
queryKey: creditsKeys.stats(),
});
};
handlePaymentSuccess(); handlePaymentSuccess();
} }
}, [searchParams, localeRouter, queryClient, t]); }, [searchParams, localeRouter, handlePaymentSuccess]);
// Retry all data fetching // Retry all data fetching using refetch methods
const handleRetry = useCallback(() => { const handleRetry = useCallback(() => {
// console.log('handleRetry, refetch credits data'); // Use refetch methods for immediate data refresh
// Force invalidate cache to ensure fresh data refetchBalance();
queryClient.invalidateQueries({ refetchStats();
queryKey: creditsKeys.balance(), }, [refetchBalance, refetchStats]);
});
queryClient.invalidateQueries({
queryKey: creditsKeys.stats(),
});
}, [queryClient]);
// Render loading skeleton // Render loading skeleton
if (!mounted || isLoadingBalance || isLoadingStats) { if (!mounted || isLoadingBalance || isLoadingStats) {

View File

@ -32,8 +32,6 @@ export function useCreditBalance() {
console.log('Credit balance fetched:', result.data.credits); console.log('Credit balance fetched:', result.data.credits);
return result.data.credits || 0; return result.data.credits || 0;
}, },
staleTime: 30 * 1000, // 30 seconds - reasonable stale time
retry: 2, // Retry up to 2 times on failure
}); });
} }
@ -50,8 +48,6 @@ export function useCreditStats() {
console.log('Credit stats fetched:', result.data.data); console.log('Credit stats fetched:', result.data.data);
return result.data.data; return result.data.data;
}, },
staleTime: 30 * 1000, // 30 seconds - reasonable stale time
retry: 2, // Retry up to 2 times on failure
}); });
} }