UNPKG

expo-finance-kit

Version:

Native Expo module for Apple FinanceKit - Access financial data from Apple Card and other accounts

362 lines 10.5 kB
/** * React hooks for Expo Finance Kit * Provides easy integration with React components */ import { useEffect, useState, useCallback, useRef } from 'react'; import { getAccounts, getAccountsWithOptions, getAccountById, } from '../modules/accounts'; import { getTransactions, getRecentTransactions, getTransactionsByAccount, } from '../modules/transactions'; import { getBalances, getBalanceByAccount, getTotalBalance, } from '../modules/balances'; import { getAuthorizationStatus, requestAuthorization, authorizationListener, } from '../modules/authorization'; import { startMonitoringTransactions, stopMonitoringTransactions, addTransactionChangeListener, isMonitoringTransactions, } from '../modules/monitoring'; /** * Hook for managing authorization status */ export function useAuthorizationStatus() { const [status, setStatus] = useState('notDetermined'); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { // Get initial status getAuthorizationStatus() .then(setStatus) .catch(setError) .finally(() => setLoading(false)); // Listen for changes const unsubscribe = authorizationListener.addStatusChangeListener((payload) => { setStatus(payload.status); }); return unsubscribe; }, []); const requestAuth = useCallback(async () => { setLoading(true); setError(null); try { const result = await requestAuthorization(); setStatus(result.status); return result; } catch (err) { setError(err); throw err; } finally { setLoading(false); } }, []); return { status, loading, error, requestAuthorization: requestAuth, isAuthorized: status === 'authorized', }; } /** * Hook for fetching accounts */ export function useAccounts(options) { const [accounts, setAccounts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchAccounts = useCallback(async () => { setLoading(true); setError(null); try { const data = options ? await getAccountsWithOptions(options) : await getAccounts(); setAccounts(data); } catch (err) { setError(err); } finally { setLoading(false); } }, [options]); useEffect(() => { fetchAccounts(); }, [fetchAccounts]); return { accounts, loading, error, refetch: fetchAccounts, }; } /** * Hook for fetching a single account */ export function useAccount(accountId) { const [account, setAccount] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (!accountId) { setLoading(false); return; } setLoading(true); setError(null); getAccountById(accountId) .then(setAccount) .catch(setError) .finally(() => setLoading(false)); }, [accountId]); return { account, loading, error, }; } /** * Hook for fetching transactions */ export function useTransactions(options) { const [transactions, setTransactions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const optionsRef = useRef(options); const fetchTransactions = useCallback(async () => { setLoading(true); setError(null); try { const data = await getTransactions(optionsRef.current); setTransactions(data); } catch (err) { setError(err); } finally { setLoading(false); } }, []); useEffect(() => { optionsRef.current = options; fetchTransactions(); }, [options, fetchTransactions]); return { transactions, loading, error, refetch: fetchTransactions, }; } /** * Hook for fetching recent transactions */ export function useRecentTransactions(limit = 50) { const [transactions, setTransactions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchTransactions = useCallback(async () => { setLoading(true); setError(null); try { const data = await getRecentTransactions(limit); setTransactions(data); } catch (err) { setError(err); } finally { setLoading(false); } }, [limit]); useEffect(() => { fetchTransactions(); }, [fetchTransactions]); return { transactions, loading, error, refetch: fetchTransactions, }; } /** * Hook for fetching account balance */ export function useAccountBalance(accountId) { const [balance, setBalance] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchBalance = useCallback(async () => { setLoading(true); setError(null); try { const data = accountId ? await getBalanceByAccount(accountId) : (await getBalances())[0] || null; setBalance(data); } catch (err) { setError(err); } finally { setLoading(false); } }, [accountId]); useEffect(() => { fetchBalance(); }, [fetchBalance]); return { balance, loading, error, refetch: fetchBalance, }; } /** * Hook for fetching total balance across all accounts */ export function useTotalBalance() { const [totalBalance, setTotalBalance] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchBalance = useCallback(async () => { setLoading(true); setError(null); try { const data = await getTotalBalance(); setTotalBalance(data); } catch (err) { setError(err); } finally { setLoading(false); } }, []); useEffect(() => { fetchBalance(); }, [fetchBalance]); return { totalBalance, loading, error, refetch: fetchBalance, }; } /** * Hook for real-time transaction updates (polling-based) * @deprecated Use useTransactionMonitoring for FinanceKit native change streams */ export function useTransactionStream(accountId, pollingInterval = 30000 // 30 seconds ) { const [transactions, setTransactions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const intervalRef = useRef(null); const fetchTransactions = useCallback(async () => { try { const data = accountId ? await getTransactionsByAccount(accountId, { limit: 100 }) : await getRecentTransactions(100); setTransactions(data); setError(null); } catch (err) { setError(err); } finally { setLoading(false); } }, [accountId]); useEffect(() => { // Initial fetch fetchTransactions(); // Set up polling intervalRef.current = setInterval(fetchTransactions, pollingInterval); // Cleanup return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [fetchTransactions, pollingInterval]); return { transactions, loading, error, refetch: fetchTransactions, }; } /** * Hook for real-time transaction monitoring using FinanceKit change streams * Provides batched updates when transactions are inserted, updated, or deleted */ export function useTransactionMonitoring(accountIds, options) { const [isMonitoring, setIsMonitoring] = useState(false); const [error, setError] = useState(null); const [changeCount, setChangeCount] = useState(0); const autoStart = options?.autoStart ?? true; const onChangesRef = useRef(options?.onChanges); // Update callback ref when it changes useEffect(() => { onChangesRef.current = options?.onChanges; }, [options?.onChanges]); // Set up monitoring useEffect(() => { let unsubscribe = null; const startMonitoring = async () => { try { setError(null); await startMonitoringTransactions(accountIds); setIsMonitoring(isMonitoringTransactions()); // Set up listener for changes unsubscribe = addTransactionChangeListener((payload) => { setChangeCount(prev => prev + 1); if (onChangesRef.current) { onChangesRef.current(payload); } }); } catch (err) { setError(err); setIsMonitoring(false); } }; if (autoStart) { startMonitoring(); } // Cleanup return () => { if (unsubscribe) { unsubscribe(); } stopMonitoringTransactions().catch(() => { // Ignore cleanup errors }); setIsMonitoring(false); }; }, [accountIds?.join(','), autoStart]); const start = useCallback(async () => { try { setError(null); await startMonitoringTransactions(accountIds); setIsMonitoring(isMonitoringTransactions()); } catch (err) { setError(err); throw err; } }, [accountIds]); const stop = useCallback(async () => { try { await stopMonitoringTransactions(); setIsMonitoring(false); } catch (err) { setError(err); throw err; } }, []); return { isMonitoring, error, changeCount, start, stop, }; } //# sourceMappingURL=useFinanceKit.js.map