import { createContext, useState, useContext, useEffect, useMemo, useCallback, } from 'react'; import { useKeysContext } from './keys'; import { db } from '../db/initializeFirebase'; import { DID_OPEN_TABLES_EVENT_NAME, getSavedPOSTransactions, pointOfSaleEventEmitter, POS_EVENT_UPDATE, queuePOSTransactions, } from '../app/functions/pos'; import { getTwoWeeksAgoDate } from '../app/functions/rotateAddressDateChecker'; import { collection, getDocs, onSnapshot, orderBy, query, startAfter, where, } from '@react-native-firebase/firestore'; // Initiate context const POSTransactionsContextManager = createContext(null); const POSTransactionsProvider = ({ children }) => { const { publicKey, contactsPrivateKey } = useKeysContext(); const [txList, setTxList] = useState([]); const [didOpenTable, setDidOpenTable] = useState(false); const updateTxListFunction = useCallback(async () => { const txs = await getSavedPOSTransactions(); setTxList(txs || []); return txs || []; }, []); const groupedTxs = useMemo(() => { try { let totals = {}; for (const tx of txList) { const serverName = tx.serverName?.toLowerCase()?.trim(); let savedAccount = totals[serverName]; if (!savedAccount) { totals[serverName] = { totalTipAmount: 0, txs: [], unpaidTxs: [], lastActivity: 0, totalUnpaidTxs: 0, totalPaidTxs: 0, }; } savedAccount = totals[serverName]; let newUnpaidTxArray = [...savedAccount.unpaidTxs]; if (!tx.didPay) newUnpaidTxArray.push(tx); totals[serverName] = { totalTipAmount: savedAccount.totalTipAmount + (tx.didPay ? 0 : tx.tipAmountSats), txs: [tx, ...savedAccount.txs], unpaidTxs: newUnpaidTxArray, lastActivity: tx.timestamp > savedAccount.lastActivity ? tx.timestamp : savedAccount.lastActivity, totalUnpaidTxs: savedAccount.totalUnpaidTxs + (!tx.didPay ? tx.tipAmountSats : 0), totalPaidTxs: savedAccount.totalPaidTxs + (!!tx.didPay ? tx.tipAmountSats : 0), }; } if (!Object.keys(totals).length) { return []; } return Object.entries(totals); } catch (err) { console.log('getting tip totals error', err); return []; } }, [txList]); useEffect(() => { if (!didOpenTable) return; if (!publicKey) return; console.log('running pos transactions listener...'); const now = new Date().getTime(); const transaction = query( collection(db, 'posTransactions'), where('storePubKey', '==', publicKey), orderBy('dateAdded'), startAfter(now), ); const unsubscribe = onSnapshot(transaction, snapshot => { snapshot?.docChanges()?.forEach(change => { console.log('recived a new pos transaction', change.type); if (change.type === 'added') { const newTX = change.doc.data(); queuePOSTransactions({ transactionsList: [newTX], privateKey: contactsPrivateKey, }); } }); }); return () => { console.log(`unsubscribing pos transactions listener...`, !!unsubscribe); if (unsubscribe) unsubscribe(); }; }, [publicKey, didOpenTable]); useEffect(() => { async function loadSavedTxs() { const txs = await updateTxListFunction(); console.log('saved pos transactions', txs); const lastMessageTimestamp = txs.length ? txs[0]?.dbDateAdded : getTwoWeeksAgoDate(); console.log('last pos transaction timestamp', txs[0]?.dbDateAdded); const transactionsRef = collection(db, 'posTransactions'); const messagesQuery = query( transactionsRef, where('storePubKey', '==', publicKey), where('dateAdded', '>', lastMessageTimestamp), ); const querySnapshot = await getDocs(messagesQuery); if (querySnapshot.empty) return; let messsageList = []; for (const doc of querySnapshot.docs) { const data = doc.data(); messsageList.push(data); } console.log('loaded missed pos transactions', messsageList); queuePOSTransactions({ transactionsList: messsageList, privateKey: contactsPrivateKey, }); } if (!didOpenTable) return; if (!publicKey) return; loadSavedTxs(); }, [publicKey, didOpenTable]); const handlePosTableOpen = useCallback(eventType => { if (eventType === 'opened') { setDidOpenTable(true); } }, []); useEffect(() => { // listens for events from the db and updates the state pointOfSaleEventEmitter.on(DID_OPEN_TABLES_EVENT_NAME, handlePosTableOpen); pointOfSaleEventEmitter.on(POS_EVENT_UPDATE, updateTxListFunction); return () => { pointOfSaleEventEmitter.removeAllListeners(POS_EVENT_UPDATE); pointOfSaleEventEmitter.removeAllListeners(DID_OPEN_TABLES_EVENT_NAME); }; }, []); const contextValue = useMemo( () => ({ groupedTxs, }), [groupedTxs], ); return ( {children} ); }; function usePOSTransactions() { const context = useContext(POSTransactionsContextManager); if (!context) { throw new Error( 'usePOSTransactions must be used within a POSTransactionsProvider', ); } return context; } export { POSTransactionsContextManager, POSTransactionsProvider, usePOSTransactions, };