diff --git a/package.json b/package.json index 2b5e380..b19aa34 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "react-dev-utils": "10.0.0", "react-device-detect": "^1.11.14", "react-dom": "^16.12.0", + "react-modal": "^3.11.2", "react-toast-notifications": "^2.4.0", "react-tooltip": "^3.11.6", "resolve": "1.12.2", @@ -83,6 +84,7 @@ "ts-pnp": "1.1.5", "typescript": "^3.7.5", "url-loader": "2.3.0", + "userbase-js": "^1.4.1", "webpack": "4.41.2", "webpack-dev-server": "3.9.0", "webpack-manifest-plugin": "2.2.0", diff --git a/public/index-dynamicContracts.html b/public/index-dynamicContracts.html new file mode 100644 index 0000000..c528e15 --- /dev/null +++ b/public/index-dynamicContracts.html @@ -0,0 +1,555 @@ + + + + + DappHero HTML Generation + + + + + + + + + +
+ +

Perfect-edmund-58

+

+ + Powered by DappHero.io + +

+

+ This is a description of what you are trying to do with your project. + Edit it so you have something snazzy! +

+
+

+ +

+
+
+
+

TokenFactory---Mainnet

+
+
+

Public Methods

+
+
+

Method "createdContracts"

+
+ +
+
+
+
+
+
+
+

Transaction Methods

+
+
+

Method "createToken"

+
+ +
+
+
+
+
+
+ + + + + + diff --git a/public/index-stanard.html b/public/index-stanard.html index 8cadef0..4a1da82 100644 --- a/public/index-stanard.html +++ b/public/index-stanard.html @@ -838,7 +838,7 @@

src="" type="text/javascript" id="dh-apiKey" - data-api="1581368358384x830713010847744000" + data-api="1592342853928x247583279535882240" > diff --git a/public/index.html b/public/index.html index 8cadef0..7029ec4 100644 --- a/public/index.html +++ b/public/index.html @@ -834,11 +834,29 @@

crossorigin="anonymous" > --> + + diff --git a/src/Activator.tsx b/src/Activator.tsx index 160f3cf..c076241 100644 --- a/src/Activator.tsx +++ b/src/Activator.tsx @@ -26,6 +26,7 @@ type ActivatorProps = { timeStamp: any; contractElements: any; domElementsFilteredForContracts: any; + db: any; } export const Activator: React.FC = ({ @@ -37,6 +38,7 @@ export const Activator: React.FC = ({ supportedNetworks, contractElements, domElementsFilteredForContracts, + db, }: ActivatorProps) => { // Ethereum @@ -66,6 +68,7 @@ export const Activator: React.FC = ({ retriggerEngine, projectId: consts.global.apiKey, provider: ethereum, + db, toggleHighlight(): void { dappHero.highlightEnabled = !dappHero.highlightEnabled highlightDomElements(dappHero.highlightEnabled, domElements) @@ -75,6 +78,13 @@ export const Activator: React.FC = ({ listenToTransactionStatusChange: (cb): void => listenToEvent(EVENT_NAMES.contract.statusChange, cb), listenToContractInvokeTriggerChange: (cb): void => listenToEvent(EVENT_NAMES.contract.invokeTrigger, cb), listenToSmartContractBlockchainEvent: (cb): void => listenToEvent(EVENT_NAMES.contract.contractEvent, cb), + listenToUserAddressChange: (cb): void => listenToEvent(EVENT_NAMES.user.addressStatusChange, cb), + listenToUserBalanceChange: (cb): void => listenToEvent(EVENT_NAMES.user.balanceStatusChange, cb), + listenToNFTLoadSingleToken: (cb): void => listenToEvent(EVENT_NAMES.nft.loadSingleToken, cb), + listenToNFTLoadMultipleToken: (cb): void => listenToEvent(EVENT_NAMES.nft.loadMultipleTokens, cb), + listenToNFTLoadAllToken: (cb): void => listenToEvent(EVENT_NAMES.nft.loadAllTokens, cb), + listenTo3BoxProfile: (cb): void => listenToEvent(EVENT_NAMES.threeBox.loadProfile, cb), + listenToEthTransfer: (cb): void => listenToEvent(EVENT_NAMES.ethTransfer.sendEther, cb), } Object.assign(window, { dappHero }) diff --git a/src/ProvidersWrapper.tsx b/src/ProvidersWrapper.tsx index ca4fbd9..cf7e3aa 100644 --- a/src/ProvidersWrapper.tsx +++ b/src/ProvidersWrapper.tsx @@ -9,6 +9,9 @@ import { DomElementsContext, EthereumContext } from 'contexts' import { EmitterProvider } from 'providers/EmitterProvider/provider' import { useWeb3Provider } from 'hooks' import { Activator } from './Activator' +import { UserBase as UBase } from './components/userBase/UserBase' + +import { DB } from './api/database' export const ProvidersWrapper: React.FC = () => { // react hooks @@ -23,6 +26,18 @@ export const ProvidersWrapper: React.FC = () => { const ethereum = useWeb3Provider(consts.global.POLLING_INTERVAL) // This sets refresh speed of the whole app + // load serBase + const [ db, setDB ] = useState(null) + useEffect(() => { + + const makeDBConnection = async () => { + setDB(await new DB({ appId: process.env.REACT_APP_USERBASE_APP_ID, projectId: consts.global.apiKey })) + } + if (consts.global.apiKey) { + makeDBConnection() + } + }, []) + // load contracts effects only if not paused useEffect(() => { const getConfig = async () => { @@ -82,6 +97,7 @@ export const ProvidersWrapper: React.FC = () => { + {/* */} { supportedNetworks={supportedNetworks} domElementsFilteredForContracts={smartcontractElements.domElementsFilteredForContracts} contractElements={smartcontractElements.contractElements} + db={db} /> diff --git a/src/api/dappHero.ts b/src/api/dappHero.ts index 08a8fa5..29f1ab9 100644 --- a/src/api/dappHero.ts +++ b/src/api/dappHero.ts @@ -65,15 +65,15 @@ export const getContractsByProjectKeyDappHero = async (projectId) => { } } -const compareResponses = async (originalOutput, projectId) => { - const compareOutput = await getContractsByProjectKeyV2(projectId) - const isEqual = !!(JSON.stringify(originalOutput) === JSON.stringify(compareOutput)) - // logger.info(`Cache Check isEqual: ${isEqual.toString()}`) - if (!isEqual) { - logger.info('', compareOutput) - logger.info('', originalOutput) - } -} +// const compareResponses = async (originalOutput, projectId) => { +// const compareOutput = await getContractsByProjectKeyV2(projectId) +// const isEqual = !!(JSON.stringify(originalOutput) === JSON.stringify(compareOutput)) +// // logger.info(`Cache Check isEqual: ${isEqual.toString()}`) +// if (!isEqual) { +// logger.info('', compareOutput) +// logger.info('', originalOutput) +// } +// } export const getContractsByProjectKeyBubble = async (projectId) => { logger.log(`projectId: ${projectId}`) @@ -114,14 +114,15 @@ export const getContractsByProjectKey = async (projectId) => { // first try our cache server try { + return (await getContractsByProjectKeyBubble(projectId)) return (await getContractsByProjectKeyDappHero(projectId)) } catch (error) { // If the error fails, then try bubble - logger.log('(DH-CORE) Error in Global Cache Network, re-trying...', error) + logger.error('(DH-CORE) Error in Global Cache Network, re-trying...', error) try { return (await getContractsByProjectKeyBubble(projectId) ) } catch (error) { - logger.log('(DH-CORE) Failure in project cache backend', error) + logger.error('(DH-CORE) Failure in project cache backend', error) } } } diff --git a/src/api/database.ts b/src/api/database.ts new file mode 100644 index 0000000..de66f4c --- /dev/null +++ b/src/api/database.ts @@ -0,0 +1,43 @@ +import userbase from 'userbase-js' + +export class DB { + projectId: string + + constructor({ appId, projectId }) { + this.projectId = projectId + userbase.init({ appId }) + } + + async signUp(details): Promise { + return userbase.signUp(details) + } + + async signIn(details): Promise { + return userbase.signIn({ ...details }) + } + + async signOut(): Promise { + return userbase.signOut() + } + + async forgotPassword(username): Promise { + return userbase.forgotPassword(username) + } + + async openDatabase(changeFunc): Promise { + return userbase.openDatabase({ databaseName: this.projectId, changeHandler: changeFunc }) + } + + async insertItem(item): Promise { + return userbase.insertItem({ databaseName: this.projectId, item }) + } + + async updateItem(item, itemId): Promise { + return userbase.updateItem({ databaseName: this.projectId, item, itemId }) + } + + async deleteItem(itemId): Promise { + return userbase.deleteItem({ databaseName: this.projectId, itemId }) + } + +} diff --git a/src/components/userBase/UserBase.tsx b/src/components/userBase/UserBase.tsx new file mode 100644 index 0000000..c2cac4d --- /dev/null +++ b/src/components/userBase/UserBase.tsx @@ -0,0 +1,62 @@ +import React, { useState, useEffect } from 'react' +import Modal from 'react-modal' + +// Make sure to bind modal to your appElement (http://reactcommunity.org/react-modal/accessibility/) +// Modal.setAppElement('#yourAppElement') + +const customStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + }, +} + +export const UserBase = ({ db }) => { + let subtitle + + const [ modalIsOpen, setIsOpen ] = useState(false) + + const openModal = () => { + setIsOpen(true) + } + + const closeModal = () => { + setIsOpen(false) + } + + // useEffect(() => { + // if (window.dappHero) { + // // window.dappHero.db = db + // window.dappHero.openModal = openModal + // window.dappHero.closeModal = closeModal + // } + // }, [ window.dappHero, db ]) + + return ( +
+ + + + {/*

(subtitle = _subtitle)}>Hello

*/} + +
I am a modal
+
+ + + +
+
+
+ ) +} + diff --git a/src/consts/global.ts b/src/consts/global.ts index 07fafcf..31c8cc8 100644 --- a/src/consts/global.ts +++ b/src/consts/global.ts @@ -9,6 +9,7 @@ export const ethNetworkName = { 77: 'Sokol', 99: 'Core', 100: 'Xdai', + 80001: 'maticMumbaiTestnet', } const apiKeyElement = document.getElementById('dh-apiKey') export const apiKey = apiKeyElement.getAttribute('data-api') diff --git a/src/consts/provider.ts b/src/consts/provider.ts index 963cd10..17dc025 100644 --- a/src/consts/provider.ts +++ b/src/consts/provider.ts @@ -30,9 +30,13 @@ export const readProviders = { http: 'https://eth-goerli.alchemyapi.io/v2/mo2KeoBlZY6CAyc2o1i4BcBNioVN_wpJ', ws: 'wss://eth-goerli.ws.alchemyapi.io/v2/mo2KeoBlZY6CAyc2o1i4BcBNioVN_wpJ', }, - xDai: { + xdai: { http: 'https://dai.poa.network', ws: 'wss://dai-trace-ws.blockscout.com/ws', }, + maticmumbaitestnet: { + http: 'https://rpc-mumbai.matic.today', + ws: '', + }, } diff --git a/src/protocol/ethereum/customContract/Manager.tsx b/src/protocol/ethereum/customContract/Manager.tsx index b3a044f..c480754 100644 --- a/src/protocol/ethereum/customContract/Manager.tsx +++ b/src/protocol/ethereum/customContract/Manager.tsx @@ -22,19 +22,24 @@ export const Manager: React.FunctionComponent = ({ customContractE // TODO: [DEV-318] Create a function to add a new contract name and add a new configuration - for (const contractName of uniqueContractNames) { - - const newDomElements = getDomElements(configuration) - const contractElements = newDomElements.filter((element) => element.feature === 'customContract') - const methodsByContractAsElements = contractElements.filter((element) => element.contract.contractName === contractName) - const contract = configuration.contracts.filter((thisContract) => (thisContract.contractName === contractName))[0] - - return ( - - ) - } + // for (const contractName of uniqueContractNames) + return ( + <> + {Array.from(uniqueContractNames).map((contractName) => { + const newDomElements = getDomElements(configuration) + const contractElements = newDomElements.filter((element) => element.feature === 'customContract') + const methodsByContractAsElements = contractElements.filter((element) => element.contract.contractName === contractName) + const contract = configuration.contracts.filter((thisContract) => (thisContract.contractName === contractName))[0] + + return ( + + ) + })} + + ) } diff --git a/src/protocol/ethereum/customContract/Router.tsx b/src/protocol/ethereum/customContract/Router.tsx index ed83c7c..4347771 100644 --- a/src/protocol/ethereum/customContract/Router.tsx +++ b/src/protocol/ethereum/customContract/Router.tsx @@ -25,7 +25,6 @@ type RouterProps = { } export const Router: React.FunctionComponent = ({ listOfContractMethods, contract, timestamp }) => { - const ethereum = useContext(contexts.EthereumContext) const { signer, isEnabled: writeEnabled, chainId: writeChainId, provider } = ethereum const { contractAddress, contractAbi, networkId } = contract @@ -47,10 +46,8 @@ export const Router: React.FunctionComponent = ({ listOfContractMet let readContractInstance = null // Make the contract instance from either the local provider or remote provider if (provider && contractNetwork === provider?._network?.name) { - console.log('Using local provider for contract reads.') readContractInstance = new ethers.Contract(contractAddress, contractAbi, provider) } else { - console.log('Using DH-backend provider for contract reads.') readContractInstance = new ethers.Contract(contractAddress, contractAbi, stableReadProvider) } readContractInstance.on('*', (data) => emitToEvent( diff --git a/src/protocol/ethereum/network/EthTransfer.tsx b/src/protocol/ethereum/network/EthTransfer.tsx index d7f4430..885544f 100644 --- a/src/protocol/ethereum/network/EthTransfer.tsx +++ b/src/protocol/ethereum/network/EthTransfer.tsx @@ -4,6 +4,8 @@ import { logger } from 'logger/customLogger' import * as utils from 'utils' import * as contexts from 'contexts' import { ethers } from 'ethers' +import { EmitterContext } from 'providers/EmitterProvider/context' +import { EVENT_NAMES, EVENT_STATUS } from 'providers/EmitterProvider/constants' const apiKey = process.env.REACT_APP_BLOCKNATIVE_API interface EthTransferProps { @@ -17,6 +19,8 @@ interface EthTransferProps { export const EthTransfer: FunctionComponent = ({ element, amountObj, addressObj, outputObj, info }) => { const ethereum = useContext(contexts.EthereumContext) + const { actions: { emitToEvent } } = useContext(EmitterContext) + const { signer, provider, isEnabled } = ethereum useEffect(() => { @@ -48,15 +52,36 @@ export const EthTransfer: FunctionComponent = ({ element, amou } ] if (from && isEnabled) { // We will only attempt this if we actually got our address from the signer ourslves. + emitToEvent( + EVENT_NAMES.ethTransfer.sendEther, + { value: params, step: 'Send Ether', status: EVENT_STATUS.pending }, + ) provider.send('eth_sendTransaction', params) - .then(notify.hash) - .catch((err) => logger.info('There was an error sending ether with metaMask', err)) + .then((hash) => { + emitToEvent( + EVENT_NAMES.ethTransfer.sendEther, + { value: params, step: 'Send Ether Params', status: EVENT_STATUS.pending }, + ) + notify.hash(hash) + }) + .catch((err) => { + emitToEvent( + EVENT_NAMES.ethTransfer.sendEther, + { value: err, step: 'Send Ether Error', status: EVENT_STATUS.rejected }, + ) + + logger.info('There was an error sending ether with metaMask', err) + }) .finally(() => { amountObj.element.value = '' addressObj.element.value = '' }) } } catch (err) { + emitToEvent( + EVENT_NAMES.ethTransfer.sendEther, + { value: err, step: 'Send Ether Error', status: EVENT_STATUS.rejected }, + ) logger.warn('There was an error transfering ether', err) } } diff --git a/src/protocol/ethereum/nft/useGetTokensForContractAddress.tsx b/src/protocol/ethereum/nft/useGetTokensForContractAddress.tsx index 2eb8680..39d7477 100644 --- a/src/protocol/ethereum/nft/useGetTokensForContractAddress.tsx +++ b/src/protocol/ethereum/nft/useGetTokensForContractAddress.tsx @@ -1,10 +1,14 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState, useContext } from 'react' +import { EmitterContext } from 'providers/EmitterProvider/context' +import { EVENT_NAMES, EVENT_STATUS } from 'providers/EmitterProvider/constants' import { openSeaApi } from './api' export const useGetTokensForContractAddress = ({ isSingleToken, isMultipleTokens, isAllTokens, parsedTokens, assetContractAddress, assetOwnerAddress, limit, offset, tokens }) => { const [ nfts, setNfts ] = useState(null) const [ error, setError ] = useState(null) + const { actions: { emitToEvent } } = useContext(EmitterContext) + useEffect(() => { if (assetOwnerAddress || !assetContractAddress) return @@ -15,8 +19,20 @@ export const useGetTokensForContractAddress = ({ isSingleToken, isMultipleTokens openSeaApi.contract .getSingleAsset({ assetContractAddress, token }) - .then(setNfts) - .catch((error) => setError({ simpleErrorMessage, completeErrorMessage, error })) + .then((nfts) => { + emitToEvent( + EVENT_NAMES.nft.loadSingleToken, + { value: nfts, step: 'Load Single Token', status: EVENT_STATUS.resolved }, + ) + setNfts(nfts) + }) + .catch((error) => { + emitToEvent( + EVENT_NAMES.nft.loadSingleToken, + { value: error, step: 'Load Single Token', status: EVENT_STATUS.rejected }, + ) + setError({ simpleErrorMessage, completeErrorMessage, error }) + }) } if (isMultipleTokens) { @@ -25,8 +41,20 @@ export const useGetTokensForContractAddress = ({ isSingleToken, isMultipleTokens openSeaApi.contract .getMultipleAssets({ assetContractAddress, tokens, limit, offset }) - .then(setNfts) - .catch((error) => setError({ simpleErrorMessage, completeErrorMessage, error })) + .then((nfts) => { + emitToEvent( + EVENT_NAMES.nft.loadMultipleTokens, + { value: nfts, step: 'Load Single Token', status: EVENT_STATUS.resolved }, + ) + setNfts(nfts) + }) + .catch((error) => { + emitToEvent( + EVENT_NAMES.nft.loadMultipleTokens, + { value: error, step: 'Load Single Token', status: EVENT_STATUS.rejected }, + ) + setError({ simpleErrorMessage, completeErrorMessage, error }) + }) } if (isAllTokens) { @@ -35,8 +63,20 @@ export const useGetTokensForContractAddress = ({ isSingleToken, isMultipleTokens openSeaApi.contract .getAllAssets({ assetContractAddress, limit, offset }) - .then(setNfts) - .catch((error) => setError({ simpleErrorMessage, completeErrorMessage, error })) + .then((nfts) => { + emitToEvent( + EVENT_NAMES.nft.loadAllTokens, + { value: nfts, step: 'Load Single Token', status: EVENT_STATUS.resolved }, + ) + setNfts(nfts) + }) + .catch((error) => { + emitToEvent( + EVENT_NAMES.nft.loadAllTokens, + { value: error, step: 'Load Single Token', status: EVENT_STATUS.rejected }, + ) + setError({ simpleErrorMessage, completeErrorMessage, error }) + }) } }, [ assetContractAddress, offset ]) diff --git a/src/protocol/ethereum/nft/useGetTokensFromOwner.tsx b/src/protocol/ethereum/nft/useGetTokensFromOwner.tsx index cd5e17e..96a17b8 100644 --- a/src/protocol/ethereum/nft/useGetTokensFromOwner.tsx +++ b/src/protocol/ethereum/nft/useGetTokensFromOwner.tsx @@ -1,10 +1,14 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState, useContext } from 'react' +import { EmitterContext } from 'providers/EmitterProvider/context' +import { EVENT_NAMES, EVENT_STATUS } from 'providers/EmitterProvider/constants' import { openSeaApi } from './api' export const useGetTokensFromOwner = ({ assetOwnerAddress, isAllTokens, parsedTokens, isSingleToken, isMultipleTokens, assetContractAddress, tokens, limit, offset }) => { const [ nfts, setNfts ] = useState(null) const [ error, setError ] = useState(null) + const { actions: { emitToEvent } } = useContext(EmitterContext) + useEffect(() => { if (!assetOwnerAddress) return @@ -15,8 +19,20 @@ export const useGetTokensFromOwner = ({ assetOwnerAddress, isAllTokens, parsedTo openSeaApi.owner .getSingleAsset({ assetOwnerAddress, assetContractAddress, token }) - .then(setNfts) - .catch((error) => setError({ simpleErrorMessage, completeErrorMessage, error })) + .then((nfts) => { + emitToEvent( + EVENT_NAMES.nft.loadSingleToken, + { value: nfts, step: 'Load Single Token', status: EVENT_STATUS.resolved }, + ) + setNfts(nfts) + }) + .catch((error) => { + emitToEvent( + EVENT_NAMES.nft.loadSingleToken, + { value: error, step: 'Load Single Token', status: EVENT_STATUS.rejected }, + ) + setError({ simpleErrorMessage, completeErrorMessage, error }) + }) } if (isMultipleTokens) { @@ -25,8 +41,20 @@ export const useGetTokensFromOwner = ({ assetOwnerAddress, isAllTokens, parsedTo openSeaApi.owner .getMultipleAssets({ assetOwnerAddress, assetContractAddress, tokens, limit, offset }) - .then(setNfts) - .catch((error) => setError({ simpleErrorMessage, completeErrorMessage, error })) + .then((nfts) => { + emitToEvent( + EVENT_NAMES.nft.loadMultipleTokens, + { value: nfts, step: 'Load Single Token', status: EVENT_STATUS.resolved }, + ) + setNfts(nfts) + }) + .catch((error) => { + emitToEvent( + EVENT_NAMES.nft.loadMultipleTokens, + { value: error, step: 'Load Single Token', status: EVENT_STATUS.rejected }, + ) + setError({ simpleErrorMessage, completeErrorMessage, error }) + }) } if (isAllTokens) { @@ -35,8 +63,20 @@ export const useGetTokensFromOwner = ({ assetOwnerAddress, isAllTokens, parsedTo openSeaApi.owner .getAllAssets({ assetOwnerAddress, assetContractAddress, limit, offset }) - .then(setNfts) - .catch((error) => setError({ simpleErrorMessage, completeErrorMessage, error })) + .then((nfts) => { + emitToEvent( + EVENT_NAMES.nft.loadAllTokens, + { value: nfts, step: 'Load Single Token', status: EVENT_STATUS.resolved }, + ) + setNfts(nfts) + }) + .catch((error) => { + emitToEvent( + EVENT_NAMES.nft.loadAllTokens, + { value: error, step: 'Load Single Token', status: EVENT_STATUS.rejected }, + ) + setError({ simpleErrorMessage, completeErrorMessage, error }) + }) } }, [ assetOwnerAddress, offset ]) diff --git a/src/protocol/ethereum/nft/useRenderNfts.tsx b/src/protocol/ethereum/nft/useRenderNfts.tsx index 9018279..7400293 100644 --- a/src/protocol/ethereum/nft/useRenderNfts.tsx +++ b/src/protocol/ethereum/nft/useRenderNfts.tsx @@ -125,7 +125,7 @@ export const useRenderNfts = ({ nfts, item, element, getAssetElements }) => { 'data-dh-property-method-id', 'data-dh-property-asset-contract-address' ] // Replace the Inner text for any element type. - Array.from(item.root.children).forEach((element) => { + Array.from(item.root.children).forEach((element: HTMLElement) => { displayKeyValueElementInnerText(clonedItem, '$THIS_TokenID', nft?.token_id, element.nodeName) displayKeyValueElementInnerText(clonedItem, '$THIS_ContractAddress', nft?.asset_contract.address, element.nodeName) displayKeyValueElementInnerText(clonedItem, '$THIS_OwnerAddress', nft?.owner.address, element.nodeName) @@ -134,7 +134,7 @@ export const useRenderNfts = ({ nfts, item, element, getAssetElements }) => { // TODO: Add recursion to get inner children elements // Substitute the $THIS value on any attribute from array above, for any child element. - Array.from(item.root.children).forEach((element) => { + Array.from(item.root.children).forEach((element: HTMLElement) => { attributes.forEach((attribute) => { displayValueOnElementAttribute(clonedItem, '$THIS_TokenID', nft?.token_id, attribute) displayValueOnElementAttribute(clonedItem, '$THIS_ContractAddress', nft?.asset_contract.address, attribute) diff --git a/src/protocol/ethereum/threeBox/Reducer.tsx b/src/protocol/ethereum/threeBox/Reducer.tsx index 8fb529c..69558f9 100644 --- a/src/protocol/ethereum/threeBox/Reducer.tsx +++ b/src/protocol/ethereum/threeBox/Reducer.tsx @@ -3,6 +3,8 @@ import * as contexts from 'contexts' import { useWeb3React } from '@web3-react/core' import { logger } from 'logger/customLogger' import { getProfile } from '3box/lib/api' +import { EmitterContext } from 'providers/EmitterProvider/context' +import { EVENT_NAMES, EVENT_STATUS } from 'providers/EmitterProvider/constants' import { ThreeBoxProfileDataElement } from './ThreeBoxProfileDataElement' import { ThreeBoxProfileImgElement } from './ThreeBoxProfileImgElement' @@ -15,6 +17,9 @@ interface ReducerProps { } export const Reducer: FunctionComponent = ({ element, info }) => { + + const { actions: { emitToEvent } } = useContext(EmitterContext) + // const injectedContext = useWeb3React() // const { address } = injectedContext const defaultProfile = { @@ -46,9 +51,21 @@ export const Reducer: FunctionComponent = ({ element, info }) => { const fetchProfile = async () => { try { // TODO: [DEV-97] How to we check the status of a request? When no Profile this 404's + emitToEvent( + EVENT_NAMES.threeBox.loadProfile, + { value: null, step: 'Three Box Profile Load', status: EVENT_STATUS.pending }, + ) const profile = await get3boxProfile(address) + emitToEvent( + EVENT_NAMES.threeBox.loadProfile, + { value: profile, step: 'Three Box Profile Load', status: EVENT_STATUS.resolved }, + ) setThreeBoxProfile(profile) } catch (error) { + emitToEvent( + EVENT_NAMES.threeBox.loadProfile, + { value: error, step: 'Three Box Profile Load Error', status: EVENT_STATUS.rejected }, + ) logger.log('You have no profile. ', error) } } diff --git a/src/protocol/ethereum/user/EthUserAddress.tsx b/src/protocol/ethereum/user/EthUserAddress.tsx index ca2ce8d..35ce3ce 100644 --- a/src/protocol/ethereum/user/EthUserAddress.tsx +++ b/src/protocol/ethereum/user/EthUserAddress.tsx @@ -2,6 +2,8 @@ import { useEffect, FunctionComponent, useContext, useMemo } from 'react' import * as contexts from 'contexts' import { logger } from 'logger/customLogger' +import { EmitterContext } from 'providers/EmitterProvider/context' +import { EVENT_NAMES, EVENT_STATUS } from 'providers/EmitterProvider/constants' interface EthUserAddressProps { element: HTMLElement; @@ -10,6 +12,8 @@ interface EthUserAddressProps { export const EthUserAddress: FunctionComponent = ({ element, displayFormat }) => { const ethereum = useContext(contexts.EthereumContext) + const { actions: { emitToEvent } } = useContext(EmitterContext) + const { address, isEnabled } = ethereum const memoizedValue = useMemo( @@ -17,6 +21,13 @@ export const EthUserAddress: FunctionComponent = ({ element , [], ) + useEffect(() => { + emitToEvent( + EVENT_NAMES.user.addressStatusChange, + { value: address, step: 'User address value change', status: EVENT_STATUS.resolved }, + ) + }, [ address, isEnabled ]) + useEffect(() => { try { if (address && isEnabled) { diff --git a/src/protocol/ethereum/user/EthUserBalance.tsx b/src/protocol/ethereum/user/EthUserBalance.tsx index 14f3375..c4343a4 100644 --- a/src/protocol/ethereum/user/EthUserBalance.tsx +++ b/src/protocol/ethereum/user/EthUserBalance.tsx @@ -1,7 +1,9 @@ import { logger } from 'logger/customLogger' -import { useEffect, useState, useContext, FunctionComponent, useMemo } from 'react' +import { useEffect, useState, useContext, FunctionComponent, useMemo, useRef } from 'react' import { useInterval } from 'beautiful-react-hooks' import { EthereumUnits } from 'types/types' +import { EmitterContext } from 'providers/EmitterProvider/context' +import { EVENT_NAMES, EVENT_STATUS } from 'providers/EmitterProvider/constants' import { useNetworkStatus } from 'react-adaptive-hooks/network' import * as utils from 'utils' import * as contexts from 'contexts' @@ -14,11 +16,34 @@ interface EthUserBalanceProps { } export const EthUserBalance: FunctionComponent = ({ element, units, decimals }) => { + + const { actions: { emitToEvent } } = useContext(EmitterContext) + const memoizedValue = useMemo( () => element.innerText , [], ) + // // Hook + const useMemoCompare = (value, compare) => { + // Ref for storing previous value + const previousRef = useRef() + const previous = previousRef.current + + // Pass previous and new value to compare function + const isEqual = compare(previous, value) + + // If not equal update previous to new value (for next render) + // and then return new new value below. + useEffect(() => { + if (!isEqual) { + previousRef.current = value + } + }) + + return isEqual ? previous : value + } + const initialEffectiveConnectionType = '4g' const { effectiveConnectionType } = useNetworkStatus(initialEffectiveConnectionType) units = units ?? 'wei' //eslint-disable-line @@ -29,17 +54,21 @@ export const EthUserBalance: FunctionComponent = ({ element const [ balance, setBalance ] = useState(null) + const emitBalanceValue = useMemoCompare(balance, (prev) => prev && prev._hex === balance._hex) + // TODO: [DEV-264] Feature: NotifyJS for UserBalance polling const poll = async () => { try { - const balance = await provider.getBalance(address) - setBalance(balance) + const newBalance = await provider.getBalance(address) + setBalance(newBalance) } catch (error) { logger.log(`Error getting balance: ${error}`) } } - if (address && isEnabled) poll() + useEffect(() => { + if (address && isEnabled) poll() + }, []) useInterval(() => { if (address && isEnabled) poll() @@ -58,5 +87,14 @@ export const EthUserBalance: FunctionComponent = ({ element if (address && isEnabled && balance) { getBalance() } else { element.innerHTML = memoizedValue } }, [ address, isEnabled, balance ]) + useEffect(() => { + + emitToEvent( + EVENT_NAMES.user.balanceStatusChange, + { value: balance?.toString(), step: 'User balance value change', status: EVENT_STATUS.resolved }, + ) + + }, [ emitBalanceValue ]) + return null } diff --git a/src/providers/EmitterProvider/constants.ts b/src/providers/EmitterProvider/constants.ts index 64cfc9b..8cfccaf 100644 --- a/src/providers/EmitterProvider/constants.ts +++ b/src/providers/EmitterProvider/constants.ts @@ -6,6 +6,17 @@ export const EVENT_NAMES = { invokeTrigger: 'contract:invokeTriggerChange', contractEvent: 'contract:contractEvent', }, + user: { + addressStatusChange: 'address:statusChange', + balanceStatusChange: 'balance:statusChange', + }, + nft: { + loadSingleToken: 'nft:loadSingleToken', + loadMultipleTokens: 'nft:loadMultipleTokens', + loadAllTokens: 'nft:loadAllTokens', + }, + threeBox: { loadProfile: 'threebox:loadProfile' }, + ethTransfer: { sendEther: 'ethTransfer:sendEther' }, } export const EVENT_STATUS = { pending: 'PENDING', resolved: 'RESOLVED', rejected: 'REJECTED' }