165 lines
4.7 KiB
TypeScript
165 lines
4.7 KiB
TypeScript
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
import { FirebaseAuthTypes } from '../firebase';
|
|
import { UserWallet, WalletService } from '../services/walletService';
|
|
import { useUserWalletStore } from '../stores/userWalletStore';
|
|
import type { WalletCacheEntry } from '../stores/userWalletStore';
|
|
import { useGlobalLoading } from './useGlobalLoading';
|
|
|
|
|
|
interface UseUserWalletReturn {
|
|
wallet: UserWallet | null;
|
|
loading: boolean;
|
|
error: string | null;
|
|
refreshWallet: () => Promise<void>;
|
|
balanceInDollars: number; // Helper to get balance in dollars
|
|
formattedBalance: string; // Helper to get formatted balance
|
|
addCreditCard: (
|
|
cardData: {
|
|
cardNumber: string;
|
|
expiryDate: string;
|
|
cvv: string;
|
|
}
|
|
) => Promise<void>;
|
|
removeCreditCard: (cardId: string) => Promise<void>;
|
|
}
|
|
|
|
const defaultEntry: WalletCacheEntry = {
|
|
wallet: null,
|
|
loading: false,
|
|
error: null,
|
|
};
|
|
|
|
export const useUserWallet = (user: FirebaseAuthTypes.User | null): UseUserWalletReturn => {
|
|
const uid = user?.uid ?? null;
|
|
const lastUidRef = useRef<string | null>(null);
|
|
|
|
const ensureSubscription = useUserWalletStore((state) => state.ensureSubscription);
|
|
const refreshFromStore = useUserWalletStore((state) => state.refreshWallet);
|
|
const removeWallet = useUserWalletStore((state) => state.removeWallet);
|
|
const setWalletState = useUserWalletStore((state) => state.setWalletState);
|
|
|
|
const entry = useUserWalletStore(
|
|
useCallback(
|
|
(state) => (uid ? state.wallets[uid] ?? defaultEntry : defaultEntry),
|
|
[uid]
|
|
)
|
|
);
|
|
const { withLoading } = useGlobalLoading();
|
|
|
|
useEffect(() => {
|
|
if (lastUidRef.current && lastUidRef.current !== uid) {
|
|
removeWallet(lastUidRef.current);
|
|
}
|
|
lastUidRef.current = uid;
|
|
}, [uid, removeWallet]);
|
|
|
|
useEffect(() => {
|
|
if (!uid) {
|
|
return;
|
|
}
|
|
ensureSubscription(uid);
|
|
}, [ensureSubscription, uid]);
|
|
|
|
const refreshWallet = useCallback(async () => {
|
|
if (!uid) {
|
|
return;
|
|
}
|
|
await withLoading(() => refreshFromStore(uid));
|
|
}, [refreshFromStore, uid, withLoading]);
|
|
|
|
const addCreditCard = useCallback(async (
|
|
cardData: {
|
|
cardNumber: string;
|
|
expiryDate: string;
|
|
cvv: string;
|
|
}
|
|
) => {
|
|
if (!uid) {
|
|
return;
|
|
}
|
|
|
|
setWalletState(uid, { loading: true, error: null });
|
|
|
|
try {
|
|
const result = await withLoading(() => WalletService.addCreditCard(uid, cardData));
|
|
if (!result.success) {
|
|
setWalletState(uid, {
|
|
loading: false,
|
|
error: result.error || 'Failed to add credit card',
|
|
});
|
|
return;
|
|
}
|
|
setWalletState(uid, { loading: false, error: null });
|
|
} catch (err) {
|
|
setWalletState(uid, {
|
|
loading: false,
|
|
error: err instanceof Error ? err.message : 'Failed to add credit card',
|
|
});
|
|
}
|
|
}, [setWalletState, uid, withLoading]);
|
|
|
|
const removeCreditCard = useCallback(async (cardId: string) => {
|
|
if (!uid) {
|
|
return;
|
|
}
|
|
|
|
setWalletState(uid, { loading: true, error: null });
|
|
|
|
try {
|
|
const result = await withLoading(() => WalletService.removeCreditCard(uid, cardId));
|
|
if (!result.success) {
|
|
setWalletState(uid, {
|
|
loading: false,
|
|
error: result.error || 'Failed to remove credit card',
|
|
});
|
|
return;
|
|
}
|
|
setWalletState(uid, { loading: false, error: null });
|
|
} catch (err) {
|
|
setWalletState(uid, {
|
|
loading: false,
|
|
error: err instanceof Error ? err.message : 'Failed to remove credit card',
|
|
});
|
|
}
|
|
}, [setWalletState, uid, withLoading]);
|
|
|
|
return useMemo(() => {
|
|
const wallet = uid ? entry.wallet : null;
|
|
const balanceInDollars = wallet ? wallet.balance / 100 : 0;
|
|
const formattedBalance = wallet
|
|
? `${wallet.currency} ${(wallet.balance / 100).toFixed(2)}`
|
|
: `USD 0.00`;
|
|
|
|
return {
|
|
wallet,
|
|
loading: uid ? entry.loading : false,
|
|
error: uid ? entry.error : null,
|
|
refreshWallet,
|
|
balanceInDollars,
|
|
formattedBalance,
|
|
addCreditCard,
|
|
removeCreditCard,
|
|
};
|
|
}, [addCreditCard, entry.error, entry.loading, entry.wallet, refreshWallet, removeCreditCard, uid]);
|
|
};
|
|
|
|
// Standalone function to fetch any user's wallet (useful for admin features)
|
|
export const fetchUserWallet = async (uid: string): Promise<UserWallet | null> => {
|
|
if (!uid) {
|
|
return null;
|
|
}
|
|
|
|
const cached = useUserWalletStore.getState().wallets[uid]?.wallet;
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
try {
|
|
return await useUserWalletStore.getState().refreshWallet(uid);
|
|
} catch (error) {
|
|
console.error('Error fetching user wallet:', error);
|
|
const res = await WalletService.getUserWallet(uid);
|
|
return res.wallet ?? null;
|
|
}
|
|
};
|