Changeset View
Changeset View
Standalone View
Standalone View
web/cashtab/src/hooks/useWallet.js
/* eslint-disable react-hooks/exhaustive-deps */ | /* eslint-disable react-hooks/exhaustive-deps */ | ||||
import React, { useState, useEffect } from 'react'; | import React, { useState, useEffect } from 'react'; | ||||
import Paragraph from 'antd/lib/typography/Paragraph'; | import Paragraph from 'antd/lib/typography/Paragraph'; | ||||
import { notification } from 'antd'; | import { notification } from 'antd'; | ||||
import useAsyncTimeout from '@hooks/useAsyncTimeout'; | import useAsyncTimeout from '@hooks/useAsyncTimeout'; | ||||
import usePrevious from '@hooks/usePrevious'; | import usePrevious from '@hooks/usePrevious'; | ||||
import useBCH from '@hooks/useBCH'; | import useBCH from '@hooks/useBCH'; | ||||
import BigNumber from 'bignumber.js'; | import BigNumber from 'bignumber.js'; | ||||
import { | import { | ||||
fromSmallestDenomination, | fromSmallestDenomination, | ||||
loadStoredWallet, | loadStoredWallet, | ||||
isValidStoredWallet, | isValidStoredWallet, | ||||
} from '@utils/cashMethods'; | } from '@utils/cashMethods'; | ||||
import { isValidCashtabSettings } from '@utils/validation'; | |||||
import localforage from 'localforage'; | import localforage from 'localforage'; | ||||
import { currency } from '@components/Common/Ticker'; | import { currency } from '@components/Common/Ticker'; | ||||
import isEmpty from 'lodash.isempty'; | import isEmpty from 'lodash.isempty'; | ||||
import isEqual from 'lodash.isequal'; | import isEqual from 'lodash.isequal'; | ||||
const useWallet = () => { | const useWallet = () => { | ||||
const [wallet, setWallet] = useState(false); | const [wallet, setWallet] = useState(false); | ||||
const [cashtabSettings, setCashtabSettings] = useState(false); | |||||
const [fiatPrice, setFiatPrice] = useState(null); | const [fiatPrice, setFiatPrice] = useState(null); | ||||
const [ws, setWs] = useState(null); | const [ws, setWs] = useState(null); | ||||
const [apiError, setApiError] = useState(false); | const [apiError, setApiError] = useState(false); | ||||
const [checkFiatInterval, setCheckFiatInterval] = useState(null); | |||||
const [walletState, setWalletState] = useState({ | const [walletState, setWalletState] = useState({ | ||||
balances: {}, | balances: {}, | ||||
hydratedUtxoDetails: {}, | hydratedUtxoDetails: {}, | ||||
tokens: [], | tokens: [], | ||||
slpBalancesAndUtxos: {}, | slpBalancesAndUtxos: {}, | ||||
parsedTxHistory: [], | parsedTxHistory: [], | ||||
utxos: [], | utxos: [], | ||||
}); | }); | ||||
▲ Show 20 Lines • Show All 761 Lines • ▼ Show 20 Lines | ) => { | ||||
return false; | return false; | ||||
} | } | ||||
}; | }; | ||||
const handleUpdateWallet = async setWallet => { | const handleUpdateWallet = async setWallet => { | ||||
await loadWalletFromStorageOnStartup(setWallet); | await loadWalletFromStorageOnStartup(setWallet); | ||||
}; | }; | ||||
const loadCashtabSettings = async () => { | |||||
// get settings object from localforage | |||||
let localSettings; | |||||
try { | |||||
localSettings = await localforage.getItem('settings'); | |||||
// If there is no keyvalue pair in localforage with key 'settings' | |||||
if (localSettings === null) { | |||||
// Create one with the default settings from Ticker.js | |||||
localforage.setItem('settings', currency.defaultSettings); | |||||
// Set state to default settings | |||||
setCashtabSettings(currency.defaultSettings); | |||||
return currency.defaultSettings; | |||||
} | |||||
} catch (err) { | |||||
console.log(`Error getting cashtabSettings`, err); | |||||
// TODO If they do not exist, write them | |||||
// TODO add function to change them | |||||
setCashtabSettings(currency.defaultSettings); | |||||
return currency.defaultSettings; | |||||
} | |||||
// If you found an object in localforage at the settings key, make sure it's valid | |||||
if (isValidCashtabSettings(localSettings)) { | |||||
setCashtabSettings(localSettings); | |||||
return localSettings; | |||||
} | |||||
// if not valid, also set cashtabSettings to default | |||||
setCashtabSettings(currency.defaultSettings); | |||||
return currency.defaultSettings; | |||||
}; | |||||
// With different currency selections possible, need unique intervals for price checks | |||||
// Must be able to end them and set new ones with new currencies | |||||
const initializeFiatPriceApi = async selectedFiatCurrency => { | |||||
// Update fiat price and confirm it is set to make sure ap keeps loading state until this is updated | |||||
await fetchBchPrice(selectedFiatCurrency); | |||||
// Set interval for updating the price with given currency | |||||
const thisFiatInterval = setInterval(function () { | |||||
fetchBchPrice(selectedFiatCurrency); | |||||
}, 60000); | |||||
// set interval in state | |||||
setCheckFiatInterval(thisFiatInterval); | |||||
}; | |||||
const clearFiatPriceApi = fiatPriceApi => { | |||||
// Clear fiat price check interval of previously selected currency | |||||
clearInterval(fiatPriceApi); | |||||
}; | |||||
const changeCashtabSettings = async (key, newValue) => { | |||||
// Set loading to true as you do not want to display the fiat price of the last currency | |||||
// loading = true will lock the UI until the fiat price has updated | |||||
setLoading(true); | |||||
// Get settings from localforage | |||||
let currentSettings; | |||||
let newSettings; | |||||
try { | |||||
currentSettings = await localforage.getItem('settings'); | |||||
} catch (err) { | |||||
console.log(`Error in changeCashtabSettings`, err); | |||||
// Set fiat price to null, which disables fiat sends throughout the app | |||||
setFiatPrice(null); | |||||
// Unlock the UI | |||||
setLoading(false); | |||||
return; | |||||
} | |||||
// Make sure function was called with valid params | |||||
if ( | |||||
Object.keys(currentSettings).includes(key) && | |||||
currency.settingsValidation[key].includes(newValue) | |||||
) { | |||||
// Update settings | |||||
newSettings = currentSettings; | |||||
newSettings[key] = newValue; | |||||
} | |||||
// Set new settings in state so they are available in context throughout the app | |||||
setCashtabSettings(newSettings); | |||||
// If this settings change adjusted the fiat currency, update fiat price | |||||
if (key === 'fiatCurrency') { | |||||
clearFiatPriceApi(checkFiatInterval); | |||||
initializeFiatPriceApi(newValue); | |||||
} | |||||
// Write new settings in localforage | |||||
try { | |||||
await localforage.setItem('settings', newSettings); | |||||
} catch (err) { | |||||
console.log( | |||||
`Error writing newSettings object to localforage in changeCashtabSettings`, | |||||
err, | |||||
); | |||||
console.log(`newSettings`, newSettings); | |||||
// do nothing. If this happens, the user will see default currency next time they load the app. | |||||
} | |||||
setLoading(false); | |||||
}; | |||||
// Parse for incoming BCH transactions | // Parse for incoming BCH transactions | ||||
// Only notify if websocket is not connected | // Only notify if websocket is not connected | ||||
if ( | if ( | ||||
(ws === null || ws.readyState !== 1) && | (ws === null || ws.readyState !== 1) && | ||||
previousBalances && | previousBalances && | ||||
balances && | balances && | ||||
'totalBalance' in previousBalances && | 'totalBalance' in previousBalances && | ||||
'totalBalance' in balances && | 'totalBalance' in balances && | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | ) { | ||||
), | ), | ||||
duration: 5, | duration: 5, | ||||
}); | }); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Update price every 1 min | |||||
useAsyncTimeout(async () => { | |||||
fetchBchPrice(); | |||||
}, 60000); | |||||
// Update wallet every 10s | // Update wallet every 10s | ||||
useAsyncTimeout(async () => { | useAsyncTimeout(async () => { | ||||
const wallet = await getWallet(); | const wallet = await getWallet(); | ||||
update({ | update({ | ||||
wallet, | wallet, | ||||
setWalletState, | setWalletState, | ||||
}).finally(() => { | }).finally(() => { | ||||
setLoading(false); | setLoading(false); | ||||
▲ Show 20 Lines • Show All 271 Lines • ▼ Show 20 Lines | const initializeWebsocket = (cashAddress, slpAddress) => { | ||||
}); | }); | ||||
} | } | ||||
}; | }; | ||||
setWs(newWs); | setWs(newWs); | ||||
} | } | ||||
}; | }; | ||||
const fetchBchPrice = async () => { | const fetchBchPrice = async ( | ||||
fiatCode = cashtabSettings ? cashtabSettings.fiatCurrency : 'usd', | |||||
) => { | |||||
// Split this variable out in case coingecko changes | // Split this variable out in case coingecko changes | ||||
const cryptoId = currency.coingeckoId; | const cryptoId = currency.coingeckoId; | ||||
// Keep currency as a variable as eventually it will be a user setting | |||||
const fiatCode = 'usd'; | |||||
// Keep this in the code, because different URLs will have different outputs require different parsing | // Keep this in the code, because different URLs will have different outputs require different parsing | ||||
const priceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${fiatCode}&include_last_updated_at=true`; | const priceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${fiatCode}&include_last_updated_at=true`; | ||||
let bchPrice; | let bchPrice; | ||||
let bchPriceJson; | let bchPriceJson; | ||||
try { | try { | ||||
bchPrice = await fetch(priceApiUrl); | bchPrice = await fetch(priceApiUrl); | ||||
//console.log(`bchPrice`, bchPrice); | //console.log(`bchPrice`, bchPrice); | ||||
} catch (err) { | } catch (err) { | ||||
Show All 21 Lines | ) => { | ||||
setFiatPrice(null); | setFiatPrice(null); | ||||
} | } | ||||
} catch (err) { | } catch (err) { | ||||
console.log(`Error parsing price API response to JSON`); | console.log(`Error parsing price API response to JSON`); | ||||
console.log(err); | console.log(err); | ||||
} | } | ||||
}; | }; | ||||
useEffect(() => { | useEffect(async () => { | ||||
handleUpdateWallet(setWallet); | handleUpdateWallet(setWallet); | ||||
fetchBchPrice(); | const initialSettings = await loadCashtabSettings(); | ||||
initializeFiatPriceApi(initialSettings.fiatCurrency); | |||||
}, []); | }, []); | ||||
useEffect(() => { | useEffect(() => { | ||||
if ( | if ( | ||||
wallet && | wallet && | ||||
wallet.Path145 && | wallet.Path145 && | ||||
wallet.Path145.cashAddress && | wallet.Path145.cashAddress && | ||||
wallet.Path245 && | wallet.Path245 && | ||||
Show All 13 Lines | return { | ||||
wallet, | wallet, | ||||
fiatPrice, | fiatPrice, | ||||
slpBalancesAndUtxos, | slpBalancesAndUtxos, | ||||
balances, | balances, | ||||
tokens, | tokens, | ||||
parsedTxHistory, | parsedTxHistory, | ||||
loading, | loading, | ||||
apiError, | apiError, | ||||
cashtabSettings, | |||||
changeCashtabSettings, | |||||
getActiveWalletFromLocalForage, | getActiveWalletFromLocalForage, | ||||
getWallet, | getWallet, | ||||
validateMnemonic, | validateMnemonic, | ||||
getWalletDetails, | getWalletDetails, | ||||
getSavedWallets, | getSavedWallets, | ||||
migrateLegacyWallet, | migrateLegacyWallet, | ||||
update: async () => | update: async () => | ||||
update({ | update({ | ||||
Show All 36 Lines |