From e6663b013daa7725834696a6eb1534acff24db2f Mon Sep 17 00:00:00 2001 From: javayhu Date: Fri, 11 Jul 2025 01:10:43 +0800 Subject: [PATCH] refactor: replace getCreditBalanceAction with useCredits hook in credits-related components --- .../layout/credits-balance-button.tsx | 33 ++++------ .../layout/credits-balance-menu.tsx | 33 ++++------ .../settings/credits/credit-packages.tsx | 51 ++++++--------- src/hooks/use-credits.ts | 62 +++++++++++++++++++ 4 files changed, 105 insertions(+), 74 deletions(-) create mode 100644 src/hooks/use-credits.ts diff --git a/src/components/layout/credits-balance-button.tsx b/src/components/layout/credits-balance-button.tsx index 04650c4..1613c0e 100644 --- a/src/components/layout/credits-balance-button.tsx +++ b/src/components/layout/credits-balance-button.tsx @@ -1,35 +1,26 @@ 'use client'; -import { getCreditBalanceAction } from '@/actions/get-credit-balance'; import { Button } from '@/components/ui/button'; +import { useCredits } from '@/hooks/use-credits'; import { useLocaleRouter } from '@/i18n/navigation'; import { Routes } from '@/routes'; import { useCreditTransactionStore } from '@/stores/transaction-store'; import { CoinsIcon, Loader2Icon } from 'lucide-react'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; export function CreditsBalanceButton() { const router = useLocaleRouter(); const { refreshTrigger } = useCreditTransactionStore(); - const [credits, setCredits] = useState(0); - const [loading, setLoading] = useState(true); + + // Use the new useCredits hook + const { balance, isLoading, refresh } = useCredits(); useEffect(() => { - const fetchCredits = async () => { - try { - const result = await getCreditBalanceAction(); - if (result?.data?.success && result.data.credits !== undefined) { - setCredits(result.data.credits); - } - } catch (error) { - console.error('CreditsBalanceButton, fetch credits error:', error); - } finally { - setLoading(false); - } - }; - - fetchCredits(); - }, [refreshTrigger]); + // Refresh credits when transaction refresh is triggered + if (refreshTrigger) { + refresh(); + } + }, [refreshTrigger, refresh]); const handleClick = () => { router.push(Routes.SettingsCredits); @@ -44,10 +35,10 @@ export function CreditsBalanceButton() { > - {loading ? ( + {isLoading ? ( ) : ( - credits.toLocaleString() + balance.toLocaleString() )} diff --git a/src/components/layout/credits-balance-menu.tsx b/src/components/layout/credits-balance-menu.tsx index 8e559c0..286ff4d 100644 --- a/src/components/layout/credits-balance-menu.tsx +++ b/src/components/layout/credits-balance-menu.tsx @@ -1,36 +1,27 @@ 'use client'; -import { getCreditBalanceAction } from '@/actions/get-credit-balance'; +import { useCredits } from '@/hooks/use-credits'; import { useLocaleRouter } from '@/i18n/navigation'; import { Routes } from '@/routes'; import { useCreditTransactionStore } from '@/stores/transaction-store'; import { CoinsIcon, Loader2Icon } from 'lucide-react'; import { useTranslations } from 'next-intl'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; export function CreditsBalanceMenu() { const t = useTranslations('Marketing.avatar'); const router = useLocaleRouter(); const { refreshTrigger } = useCreditTransactionStore(); - const [credits, setCredits] = useState(0); - const [loading, setLoading] = useState(true); + + // Use the new useCredits hook + const { balance, isLoading, refresh } = useCredits(); useEffect(() => { - const fetchCredits = async () => { - try { - const result = await getCreditBalanceAction(); - if (result?.data?.success && result.data.credits !== undefined) { - setCredits(result.data.credits); - } - } catch (error) { - console.error('CreditsBalanceMenu, fetch credits error:', error); - } finally { - setLoading(false); - } - }; - - fetchCredits(); - }, [refreshTrigger]); + // Refresh credits when transaction refresh is triggered + if (refreshTrigger) { + refresh(); + } + }, [refreshTrigger, refresh]); const handleClick = () => { router.push(Routes.SettingsCredits); @@ -47,10 +38,10 @@ export function CreditsBalanceMenu() {

- {loading ? ( + {isLoading ? ( ) : ( - credits.toLocaleString() + balance.toLocaleString() )}

diff --git a/src/components/settings/credits/credit-packages.tsx b/src/components/settings/credits/credit-packages.tsx index 7126b93..d9e899c 100644 --- a/src/components/settings/credits/credit-packages.tsx +++ b/src/components/settings/credits/credit-packages.tsx @@ -1,6 +1,5 @@ 'use client'; -import { getCreditBalanceAction } from '@/actions/get-credit-balance'; import { Badge } from '@/components/ui/badge'; import { Card, @@ -10,6 +9,7 @@ import { CardTitle, } from '@/components/ui/card'; import { getCreditPackages } from '@/config/credits-config'; +import { useCredits } from '@/hooks/use-credits'; import { useCurrentUser } from '@/hooks/use-current-user'; import { useLocaleRouter } from '@/i18n/navigation'; import { formatPrice } from '@/lib/formatter'; @@ -19,7 +19,7 @@ import { useCreditTransactionStore } from '@/stores/transaction-store'; import { CircleCheckBigIcon, CoinsIcon, Loader2Icon } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { useSearchParams } from 'next/navigation'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { toast } from 'sonner'; import { CreditCheckoutButton } from './credit-checkout-button'; @@ -29,38 +29,21 @@ import { CreditCheckoutButton } from './credit-checkout-button'; */ export function CreditPackages() { const t = useTranslations('Dashboard.settings.credits.packages'); - const [loadingCredits, setLoadingCredits] = useState(true); - const [credits, setCredits] = useState(null); - const { refreshTrigger, triggerRefresh } = useCreditTransactionStore(); - const currentUser = useCurrentUser(); const searchParams = useSearchParams(); const localeRouter = useLocaleRouter(); + // Use the new useCredits hook + const { balance, isLoading, refresh } = useCredits(); + const { refreshTrigger, triggerRefresh } = useCreditTransactionStore(); + + // Get current user + const currentUser = useCurrentUser(); + // show only enabled packages const creditPackages = Object.values(getCreditPackages()).filter( (pkg) => !pkg.disabled && pkg.price.priceId ); - const fetchCredits = async () => { - try { - setLoadingCredits(true); - const result = await getCreditBalanceAction(); - if (result?.data?.success) { - console.log('CreditPackages, fetched credits:', result.data.credits); - setCredits(result.data.credits || 0); - } else { - const errorMessage = result?.data?.error || t('failedToFetchCredits'); - console.error('CreditPackages, failed to fetch credits:', errorMessage); - toast.error(errorMessage); - } - } catch (error) { - console.error('CreditPackages, failed to fetch credits:', error); - toast.error(t('failedToFetchCredits')); - } finally { - setLoadingCredits(false); - } - }; - // Check for payment success and show success message useEffect(() => { const sessionId = searchParams.get('session_id'); @@ -72,6 +55,8 @@ export function CreditPackages() { // Refresh credits data to show updated balance triggerRefresh(); + // Also refresh the credits store + refresh(); // Clean up URL parameters const url = new URL(window.location.href); @@ -79,12 +64,14 @@ export function CreditPackages() { // Use Routes.SettingsCredits + url.search to properly handle locale routing localeRouter.replace(Routes.SettingsCredits + url.search); } - }, [searchParams, localeRouter]); + }, [searchParams, localeRouter, refresh, triggerRefresh, t]); - // Initial fetch and listen for transaction updates + // Listen for transaction updates and refresh credits useEffect(() => { - fetchCredits(); - }, [refreshTrigger]); + if (refreshTrigger) { + refresh(); + } + }, [refreshTrigger, refresh]); return (
@@ -99,11 +86,11 @@ export function CreditPackages() {
{/* */}
- {loadingCredits ? ( + {isLoading ? ( ) : (
- {credits?.toLocaleString() || 0} + {balance.toLocaleString()}
)}
diff --git a/src/hooks/use-credits.ts b/src/hooks/use-credits.ts new file mode 100644 index 0000000..6cad0ef --- /dev/null +++ b/src/hooks/use-credits.ts @@ -0,0 +1,62 @@ +import { authClient } from '@/lib/auth-client'; +import { useCreditsStore } from '@/stores/credits-store'; +import { useEffect } from 'react'; + +/** + * Hook for accessing and managing credits state + * + * This hook provides access to the credits state and methods to manage it. + * It also automatically fetches credits information when the user changes. + */ +export function useCredits() { + const { + balance, + isLoading, + error, + fetchCredits, + consumeCredits, + refreshCredits, + updateBalanceOptimistically, + } = useCreditsStore(); + + const { data: session } = authClient.useSession(); + + useEffect(() => { + const currentUser = session?.user; + // Fetch credits data whenever the user session changes + if (currentUser) { + console.log('fetching credits info for user', currentUser.id); + fetchCredits(currentUser); + } + }, [session, fetchCredits]); + + return { + // State + balance, + isLoading, + error, + + // Methods + consumeCredits, + updateBalanceOptimistically, + + // Utility methods + refetch: () => { + const currentUser = session?.user; + if (currentUser) { + console.log('refetching credits info for user', currentUser.id); + fetchCredits(currentUser); + } + }, + refresh: () => { + const currentUser = session?.user; + if (currentUser) { + console.log('refreshing credits info for user', currentUser.id); + refreshCredits(currentUser); + } + }, + + // Helper methods + hasEnoughCredits: (amount: number) => balance >= amount, + }; +}