diff --git a/src/components/App.tsx b/src/components/App.tsx index b3ca053e..0e732792 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -8,6 +8,7 @@ import { NavigationContainer, NavigationContainerRef } from '@react-navigation/n import { CustomErrorBoundary } from '@screens/ErrorScreen/ErrorBoundary' import { ErrorDetails } from '@screens/ErrorScreen/ErrorDetails' import * as Sentry from '@sentry/react-native' +import { BalanceProvider } from '@src/context/Balance' import { FocusClaimProvider } from '@src/context/FocusClaim' import { HistoryProvider } from '@src/context/History' import { KeyboardProvider } from '@src/context/Keyboard' @@ -207,25 +208,27 @@ function _App() { - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/components/Balance.tsx b/src/components/Balance.tsx index ac162b66..83294e6f 100644 --- a/src/components/Balance.tsx +++ b/src/components/Balance.tsx @@ -4,6 +4,7 @@ import { type TTXType, txType } from '@model' import type { RootStackParamList } from '@model/nav' import type { NativeStackNavigationProp } from '@react-navigation/native-stack' import EntryTime from '@screens/History/entryTime' +import { useBalanceContext } from '@src/context/Balance' import { useHistoryContext } from '@src/context/History' import { usePrivacyContext } from '@src/context/Privacy' import { useThemeContext } from '@src/context/Theme' @@ -21,15 +22,15 @@ import Logo from './Logo' import Txt from './Txt' interface IBalanceProps { - balance: number nav?: NativeStackNavigationProp } -export default function Balance({ balance, nav }: IBalanceProps) { +export default function Balance({ nav }: IBalanceProps) { const { t } = useTranslation([NS.common]) const { pref, color, highlight } = useThemeContext() const { hidden, handleLogoPress } = usePrivacyContext() const [formatSats, setFormatSats] = useState(pref?.formatBalance) + const { balance } = useBalanceContext() const { latestHistory } = useHistoryContext() const toggleBalanceFormat = () => { diff --git a/src/context/Balance.tsx b/src/context/Balance.tsx new file mode 100644 index 00000000..488ad44b --- /dev/null +++ b/src/context/Balance.tsx @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/require-await */ +/* eslint-disable require-await */ +import { getBalance } from '@db' +import { l } from '@log' +import { createContext, useContext, useEffect, useState } from 'react' + +import { useFocusClaimContext } from './FocusClaim' + +// Total Balance state (all mints) +const useBalance = () => { + const [balance, setBalance] = useState(0) + const { claimed } = useFocusClaimContext() + + const updateBalance = async () => { + const bal = await getBalance() + setBalance(bal) + } + + useEffect(() => { + void updateBalance() + }, []) + + useEffect(() => { + void updateBalance() + }, [claimed]) + + return { + balance, + updateBalance + } +} +type useBalanceType = ReturnType + +const BalanceCtx = createContext({ + balance: 0, + updateBalance: async () => l(''), +}) + +export const useBalanceContext = () => useContext(BalanceCtx) + +export const BalanceProvider = ({ children }: { children: React.ReactNode }) => ( + + {children} + +) \ No newline at end of file diff --git a/src/context/History.tsx b/src/context/History.tsx index c00865ce..c6c33c45 100644 --- a/src/context/History.tsx +++ b/src/context/History.tsx @@ -12,6 +12,7 @@ import { requestToken } from '@wallet' import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useBalanceContext } from './Balance' import { useFocusClaimContext } from './FocusClaim' import { usePromptContext } from './Prompt' @@ -22,6 +23,7 @@ const useHistory = () => { // State to indicate token claim from clipboard after app comes to the foreground, to re-render total balance const { claimed } = useFocusClaimContext() const { openPromptAutoClose } = usePromptContext() + const { updateBalance } = useBalanceContext() const allHisoryEntries = useRef([]) const hasEntries = useMemo(() => Object.keys(history).length > 0, [history]) const lastCalled = useRef(0) @@ -99,7 +101,6 @@ const useHistory = () => { if (entry) { await updateHistoryEntry(entry, { ...entry, isPending: false }) } - // TODO update balance await delInvoice(invoice.hash) } else { openPromptAutoClose({ msg: t('paymentPending'), success: false }) @@ -109,12 +110,14 @@ const useHistory = () => { const addHistoryEntry = async (entry: Omit) => { const resp = await addToHistory(entry) await setHistoryEntries() + await updateBalance() return resp } const updateHistoryEntry = async (oldEntry: IHistoryEntry, newEntry: IHistoryEntry) => { await updateHistory(oldEntry, newEntry) await setHistoryEntries() + await updateBalance() } const deleteHistory = async () => { diff --git a/src/screens/Dashboard.tsx b/src/screens/Dashboard.tsx index ecdb4a32..4e4f5ce4 100644 --- a/src/screens/Dashboard.tsx +++ b/src/screens/Dashboard.tsx @@ -8,7 +8,7 @@ import OptsModal from '@comps/modal/OptsModal' import { PromptModal } from '@comps/modal/Prompt' import Txt from '@comps/Txt' import { _testmintUrl, env } from '@consts' -import { addMint, getBalance, getMintsUrls, hasMints } from '@db' +import { addMint, getMintsUrls, hasMints } from '@db' import { l } from '@log' import TrustMintModal from '@modal/TrustMint' import type { TBeforeRemoveEvent, TDashboardPageProps } from '@model/nav' @@ -56,8 +56,6 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) { trustModal, setTrustModal } = useCashuToken() - // Total Balance state (all mints) - const [balance, setBalance] = useState(0) const [hasMint, setHasMint] = useState(false) // modals const [modal, setModal] = useState({ @@ -236,7 +234,6 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) { })) clearTimeout(t) }, 1000) - })() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) @@ -244,16 +241,13 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) { // check for available mints of the user useEffect(() => { void (async () => { - const [userHasMints, explainerSeen, balance] = await Promise.all([ + const [userHasMints, explainerSeen] = await Promise.all([ hasMints(), store.get(STORE_KEYS.explainer), - getBalance(), ]) setHasMint(userHasMints) setModal(prev => ({ ...prev, mint: !userHasMints && explainerSeen !== '1' })) - setBalance(balance) })() - // eslint-disable-next-line react-hooks/exhaustive-deps }, [claimed]) // handle deep links @@ -274,15 +268,11 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [url]) - // get balance after navigating to this page + // update states after navigating to this page useEffect(() => { const focusHandler = navigation.addListener('focus', async () => { - const data = await Promise.all([ - getBalance(), - hasMints() - ]) - setBalance(data[0]) - setHasMint(data[1]) + const data = await hasMints() + setHasMint(data) }) return focusHandler // eslint-disable-next-line react-hooks/exhaustive-deps @@ -298,7 +288,7 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) { return ( {/* Balance, Disclaimer & History */} - + {/* Receive/send/mints buttons */} {/* Send button or add first mint */} diff --git a/src/screens/Payment/Send/EncodedToken.tsx b/src/screens/Payment/Send/EncodedToken.tsx index 4cd9e153..983ebe0d 100644 --- a/src/screens/Payment/Send/EncodedToken.tsx +++ b/src/screens/Payment/Send/EncodedToken.tsx @@ -7,9 +7,11 @@ import type { TBeforeRemoveEvent, TEncodedTokenPageProps } from '@model/nav' import TopNav from '@nav/TopNav' import { preventBack } from '@nav/utils' import { isIOS } from '@src/consts' +import { useBalanceContext } from '@src/context/Balance' +import { useHistoryContext } from '@src/context/History' import { useThemeContext } from '@src/context/Theme' import { NS } from '@src/i18n' -import { historyStore, store } from '@store' +import { store } from '@store' import { STORE_KEYS } from '@store/consts' import { globals, highlight as hi, mainColors } from '@styles' import { formatInt, formatSatStr, share, vib } from '@util' @@ -27,6 +29,8 @@ export default function EncodedTokenPage({ navigation, route }: TEncodedTokenPag const { value, amount } = route.params.entry const { t } = useTranslation([NS.common]) const { color, highlight } = useThemeContext() + const { updateHistoryEntry } = useHistoryContext() + const { updateBalance } = useBalanceContext() const [error, setError] = useState({ msg: '', open: false }) const [spent, setSpent] = useState(false) const { copied, copy } = useCopy() @@ -43,8 +47,7 @@ export default function EncodedTokenPage({ navigation, route }: TEncodedTokenPag setSpent(!isSpendable) if (!isSpendable) { clearTokenInterval() - // update history item - await historyStore.updateHistoryEntry(route.params.entry, { ...route.params.entry, isSpent: true }) + await updateHistoryEntry(route.params.entry, { ...route.params.entry, isSpent: true }) } } @@ -63,6 +66,7 @@ export default function EncodedTokenPage({ navigation, route }: TEncodedTokenPag // auto check payment in intervals useEffect(() => { + void updateBalance() intervalRef.current = setInterval(() => { void checkPayment() }, 3000) diff --git a/src/screens/Payment/Success.tsx b/src/screens/Payment/Success.tsx index 76b7ff3b..f4acefbf 100644 --- a/src/screens/Payment/Success.tsx +++ b/src/screens/Payment/Success.tsx @@ -5,9 +5,9 @@ import { isIOS } from '@consts' import type { TBeforeRemoveEvent, TSuccessPageProps } from '@model/nav' import { preventBack } from '@nav/utils' import ProfilePic from '@screens/Addressbook/ProfilePic' +import { useBalanceContext } from '@src/context/Balance' import { useThemeContext } from '@src/context/Theme' import { NS } from '@src/i18n' -import { l } from '@src/logger' import { formatSatStr, isNum, vib } from '@util' import LottieView from 'lottie-react-native' import { useEffect } from 'react' @@ -33,9 +33,14 @@ export default function SuccessPage({ navigation, route }: TSuccessPageProps) { } = route.params const { t } = useTranslation([NS.common]) const { color } = useThemeContext() + const { updateBalance } = useBalanceContext() const insets = useSafeAreaInsets() - useEffect(() => vib(400), []) + useEffect(() => { + vib(400) + void updateBalance() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) // prevent back navigation - https://reactnavigation.org/docs/preventing-going-back/ useEffect(() => { @@ -44,8 +49,6 @@ export default function SuccessPage({ navigation, route }: TSuccessPageProps) { return () => navigation.removeListener('beforeRemove', backHandler) }, [navigation]) - l({ amount, memo, fee, mint, isClaim, isMelt, nostr, isScanned }) - return ( {nostr && nostr.contact && nostr.contact.picture ?