diff --git a/config/contracts.js b/config/contracts.js index 8a06264..e870eeb 100644 --- a/config/contracts.js +++ b/config/contracts.js @@ -39,11 +39,11 @@ module.exports = { ], }, // order of connections the dapp should connect to - dappConnection: [ - '$WEB3', // uses pre existing web3 object if available (e.g in Mist) - 'ws://localhost:8546', - 'http://localhost:8545', - ], + // dappConnection: [ + // '$WEB3', // uses pre existing web3 object if available (e.g in Mist) + // 'ws://localhost:8546', + // 'http://localhost:8545', + // ], // Automatically call `ethereum.enable` if true. // If false, the following code must run before sending any transaction: `await EmbarkJS.enableEthereum();` @@ -106,7 +106,15 @@ module.exports = { // merges with the settings in default // used with "embark run testnet" - testnet: {}, + testnet: { + deployment: { + accounts: [{ mnemonic: wallet.mnemonic }], + host: `ropsten.infura.io/v3/`, + port: false, + type: 'rpc', + protocol: 'https', + }, + }, // merges with the settings in default // used with "embark run livenet" diff --git a/src/common/blockchain/services/config.js b/src/common/blockchain/services/config.js index a37d448..4a9437c 100644 --- a/src/common/blockchain/services/config.js +++ b/src/common/blockchain/services/config.js @@ -2,7 +2,7 @@ import Web3 from '../../../embarkArtifacts/modules/web3' // Todo: Should be moved to .env -const RPC_URL = 'http://localhost:8545' +const RPC_URL = 'https://ropsten.infura.io/v3/8675214b97b44e96b70d05326c61fd6a' export default function() { web3 = new Web3(new Web3.providers.HttpProvider(RPC_URL)) diff --git a/src/common/blockchain/utils.js b/src/common/blockchain/utils.js index 53a5ddf..78f4139 100644 --- a/src/common/blockchain/utils.js +++ b/src/common/blockchain/utils.js @@ -6,10 +6,24 @@ const TRANSACTION_STATUSES = { Pending: 2, } +const waitOneMoreBlock = async function(prevBlockNumber) { + return new Promise(resolve => { + setTimeout(async () => { + const blockNumber = await web3.eth.getBlockNumber() + if (prevBlockNumber == blockNumber) { + return waitOneMoreBlock(prevBlockNumber) + } + resolve() + }, 30000) + }) +} + export default { getTxStatus: async txHash => { const txReceipt = await web3.eth.getTransactionReceipt(txHash) if (txReceipt) { + await waitOneMoreBlock(txReceipt.blockNumber) + return txReceipt.status ? TRANSACTION_STATUSES.Successful : TRANSACTION_STATUSES.Failed diff --git a/src/common/data/submit.js b/src/common/data/submit.js index ee5842c..6cf7e49 100644 --- a/src/common/data/submit.js +++ b/src/common/data/submit.js @@ -1,5 +1,6 @@ const submit = { visible: false, + id: '', name: '', desc: '', url: '', diff --git a/src/common/data/transaction-status.js b/src/common/data/transaction-status.js index 69f218d..554852b 100644 --- a/src/common/data/transaction-status.js +++ b/src/common/data/transaction-status.js @@ -1,25 +1,3 @@ -import { - getTransactionData, - setTransactionData, -} from '../../modules/TransactionStatus/TransactionStatus.utilities' +import { transactionStatusFetchedInstance } from '../../modules/TransactionStatus/TransactionStatus.utilities' -let transactionStatus -const transactionData = getTransactionData() - -if (transactionData !== '') { - transactionStatus = JSON.parse(transactionData) - if (transactionStatus.dappTransactionHash === '') { - transactionStatus.dappName = '' - transactionStatus.dappImg = '' - } -} else { - transactionStatus = { - dappTransactionHash: '', - dappName: '', - dappImg: '', - progress: false, - published: false, - } -} - -export default transactionStatus +export default Object.assign({}, transactionStatusFetchedInstance()) diff --git a/src/modules/App/Router.container.js b/src/modules/App/Router.container.js index 1c7c1d4..1ded0ac 100644 --- a/src/modules/App/Router.container.js +++ b/src/modules/App/Router.container.js @@ -1,14 +1,10 @@ import { connect } from 'react-redux' import { withRouter } from 'react-router-dom' import Router from './Router' -import { - fetchHighestRankedAction, - fetchRecentlyAddedAction, -} from '../Dapps/Dapps.reducer' +import { fetchAllDappsAction } from '../Dapps/Dapps.reducer' const mapDispatchToProps = dispatch => ({ - fetchHighestRanked: () => dispatch(fetchHighestRankedAction()), - fetchRecentlyAdded: () => dispatch(fetchRecentlyAddedAction()), + fetchAllDapps: () => dispatch(fetchAllDappsAction()), }) export default withRouter( diff --git a/src/modules/App/Router.jsx b/src/modules/App/Router.jsx index 7da6e9b..647e5cc 100644 --- a/src/modules/App/Router.jsx +++ b/src/modules/App/Router.jsx @@ -14,9 +14,8 @@ import Example from '../BlockchainExample' class Router extends React.Component { componentDidMount() { - const { fetchHighestRanked, fetchRecentlyAdded } = this.props - fetchHighestRanked() - fetchRecentlyAdded() + const { fetchAllDapps } = this.props + fetchAllDapps() } render() { @@ -37,8 +36,7 @@ class Router extends React.Component { } Router.propTypes = { - fetchHighestRanked: PropTypes.func.isRequired, - fetchRecentlyAdded: PropTypes.func.isRequired, + fetchAllDapps: PropTypes.func.isRequired, } export default Router diff --git a/src/modules/Dapps/Dapps.reducer.js b/src/modules/Dapps/Dapps.reducer.js index 738ee5a..ede4286 100644 --- a/src/modules/Dapps/Dapps.reducer.js +++ b/src/modules/Dapps/Dapps.reducer.js @@ -1,6 +1,9 @@ -import hardcodedDapps from '../../common/data/dapps' +// import hardcodedDapps from '../../common/data/dapps' import * as Categories from '../../common/data/categories' import reducerUtil from '../../common/utils/reducer' +import BlockchainTool from '../../common/blockchain' + +const ON_FINISH_FETCH_ALL_DAPPS_ACTION = 'ON_FINISH_FETCH_ALL_DAPPS_ACTION' const ON_START_FETCH_HIGHEST_RANKED = 'ON_START_FETCH_HIGHEST_RANKED' const ON_FINISH_FETCH_HIGHEST_RANKED = 'ON_FINISH_FETCH_HIGHEST_RANKED' @@ -10,6 +13,13 @@ const ON_FINISH_FETCH_RECENTLY_ADDED = 'ON_FINISH_FETCH_RECENTLY_ADDED' const ON_START_FETCH_BY_CATEGORY = 'ON_START_FETCH_BY_CATEGORY' const ON_FINISH_FETCH_BY_CATEGORY = 'ON_FINISH_FETCH_BY_CATEGORY' +const ON_ADD_NEW_DAPP = 'ON_ADD_NEW_DAPP' + +const RECENTLY_ADDED_SIZE = 50 +const HIGHEST_RANKED_SIZE = 50 + +const BlockchainSDK = BlockchainTool.init() + class DappsState { constructor() { this.items = [] @@ -25,7 +35,7 @@ class DappsState { this.fetched = fetched } - appendItemsAndSort(items) { + appendItems(items) { const availableNames = new Set() let addedItems = 0 for (let i = 0; i < this.items.length; i += 1) @@ -37,13 +47,18 @@ class DappsState { } } - this.items.sort((a, b) => { - return new Date(b.dateAdded).getTime() - new Date(a.dateAdded).getTime() - }) + // this.items.sort((a, b) => { + // return new Date(b.dateAdded).getTime() - new Date(a.dateAdded).getTime() + // }) this.hasMore = addedItems !== 0 } } +export const onFinishFetchAllDappsAction = dapps => ({ + type: ON_FINISH_FETCH_ALL_DAPPS_ACTION, + payload: dapps, +}) + export const onStartFetchHighestRankedAction = () => ({ type: ON_START_FETCH_HIGHEST_RANKED, payload: null, @@ -74,49 +89,103 @@ export const onFinishFetchByCategoryAction = (category, dapps) => ({ payload: { category, dapps }, }) -export const fetchHighestRankedAction = () => { - return dispatch => { +const fetchAllDappsInState = async (dispatch, getState) => { + const stateDapps = getState().dapps + if (stateDapps.dapps === null) { + let dapps = await BlockchainSDK.DiscoverService.getDApps() + + dapps = dapps.map(dapp => { + return Object.assign(dapp.metadata, { sntValue: parseInt(dapp.rate) }) + }) + dapps.sort((a, b) => { + return b.sntValue - a.sntValue + }) + + dispatch(onFinishFetchAllDappsAction(dapps)) + return dapps + } + return stateDapps.dapps +} + +export const fetchAllDappsAction = () => { + return async (dispatch, getState) => { dispatch(onStartFetchHighestRankedAction()) - setTimeout(() => { - const result = hardcodedDapps - .sort((a, b) => { - return b.sntValue - a.sntValue - }) - .slice(0, 50) - dispatch(onFinishFetchHighestRankedAction(result)) - }, 100) + dispatch(onStartFetchRecentlyAddedAction()) + + const dapps = await fetchAllDappsInState(dispatch, getState) + + const highestRanked = dapps.slice(0, HIGHEST_RANKED_SIZE) + let recentlyAdded = [...dapps] + recentlyAdded.sort((a, b) => { + return new Date().getTime(b.dateAdded) - new Date(a.dateAdded).getTime() + }) + recentlyAdded = recentlyAdded.slice(0, RECENTLY_ADDED_SIZE) + + dispatch(onFinishFetchHighestRankedAction(highestRanked)) + dispatch(onFinishFetchRecentlyAddedAction(recentlyAdded)) } } -export const fetchRecentlyAddedAction = () => { - return dispatch => { - dispatch(onStartFetchRecentlyAddedAction()) - setTimeout(() => { - const result = hardcodedDapps - .sort((a, b) => { - return ( - new Date().getTime(b.dateAdded) - new Date(a.dateAdded).getTime() - ) - }) - .slice(0, 20) - dispatch(onFinishFetchRecentlyAddedAction(result)) - }, 100) - } -} +// export const fetchHighestRankedAction = () => { +// return dispatch => { +// dispatch(onStartFetchHighestRankedAction()) +// setTimeout(() => { +// const result = hardcodedDapps +// .sort((a, b) => { +// return b.sntValue - a.sntValue +// }) +// .slice(0, 50) +// dispatch(onFinishFetchHighestRankedAction(result)) +// }, 100) +// } +// } + +// export const fetchRecentlyAddedAction = () => { +// return dispatch => { +// dispatch(onStartFetchRecentlyAddedAction()) +// setTimeout(() => { +// const result = hardcodedDapps +// .sort((a, b) => { +// return ( +// new Date().getTime(b.dateAdded) - new Date(a.dateAdded).getTime() +// ) +// }) +// .slice(0, 20) +// dispatch(onFinishFetchRecentlyAddedAction(result)) +// }, 100) +// } +// } export const fetchByCategoryAction = category => { - return dispatch => { + return async (dispatch, getState) => { dispatch(onStartFetchByCategoryAction(category)) - setTimeout(() => { - const filtered = hardcodedDapps - .filter(dapp => dapp.category === category) - .sort(() => Math.random() - 0.5) - .slice(0, 5) - dispatch(onFinishFetchByCategoryAction(category, filtered)) - }, 1000) + // setTimeout(() => { + // const filtered = hardcodedDapps + // .filter(dapp => dapp.category === category) + // .sort(() => Math.random() - 0.5) + // .slice(0, 5) + + const dapps = await fetchAllDappsInState(dispatch, getState) + const filteredByCategory = dapps.filter(dapp => dapp.category === category) + const dappsCategoryState = getState().dapps.dappsCategoryMap.get(category) + const from = dappsCategoryState.items.length + const to = Math.min(from + 5, filteredByCategory.length) + const dappsCategorySlice = filteredByCategory.slice(from, to) + + dispatch(onFinishFetchByCategoryAction(category, dappsCategorySlice)) + // }, 1000) } } +export const onAddNewDappAction = dapp => ({ + type: ON_ADD_NEW_DAPP, + payload: dapp, +}) + +const onFinishFetchAllDapps = (state, dapps) => { + return Object.assign({}, state, { dapps }) +} + const onStartFetchHightestRanked = state => { return Object.assign({}, state, { highestRankedFetched: false, @@ -162,7 +231,7 @@ const onFinishFetchByCategory = (state, payload) => { dappsCategoryMap.set(category_, dappState) if (category_ === category) { dappState.setFetched(false) - dappState.appendItemsAndSort(dapps) + dappState.appendItems(dapps) } }) return Object.assign({}, state, { @@ -170,13 +239,60 @@ const onFinishFetchByCategory = (state, payload) => { }) } +const insertDappIntoSortedArray = (source, dapp, cmp) => { + for (let i = 0; i < source.length; i += 1) { + if (cmp(source[i], dapp) === true) { + source.splice(i, 0, dapp) + break + } + } +} + +const onAddNewDapp = (state, dapp) => { + const dappsCategoryMap = new Map() + const { dapps } = state + let { highestRanked, recentlyAdded } = state + + insertDappIntoSortedArray(dapps, dapp, (target, dappItem) => { + return target.sntValue < dappItem.sntValue + }) + insertDappIntoSortedArray(highestRanked, dapp, (target, dappItem) => { + return target.sntValue < dappItem.sntValue + }) + highestRanked = state.highestRanked.splice(0, HIGHEST_RANKED_SIZE) + insertDappIntoSortedArray(recentlyAdded, dapp, (target, dappItem) => { + return ( + new Date().getTime(target.dateAdded) < + new Date(dappItem.dateAdded).getTime() + ) + }) + recentlyAdded = recentlyAdded.splice(0, RECENTLY_ADDED_SIZE) + + state.dappsCategoryMap.forEach((dappState, category_) => { + dappsCategoryMap.set(category_, dappState) + }) + const dappState = dappsCategoryMap.get(dapp.metadata.category) + insertDappIntoSortedArray(dappState.items, dapp, (target, dappItem) => { + return target.sntValue < dappItem.sntValue + }) + + return Object.assign({}, state, { + dapps, + highestRanked, + recentlyAdded, + dappsCategoryMap, + }) +} + const map = { + [ON_FINISH_FETCH_ALL_DAPPS_ACTION]: onFinishFetchAllDapps, [ON_START_FETCH_HIGHEST_RANKED]: onStartFetchHightestRanked, [ON_FINISH_FETCH_HIGHEST_RANKED]: onFinishFetchHighestRanked, [ON_START_FETCH_RECENTLY_ADDED]: onStartFetchRecentlyAdded, [ON_FINISH_FETCH_RECENTLY_ADDED]: onFinishFetchRecentlyAdded, [ON_START_FETCH_BY_CATEGORY]: onStartFetchByCategory, [ON_FINISH_FETCH_BY_CATEGORY]: onFinishFetchByCategory, + [ON_ADD_NEW_DAPP]: onAddNewDapp, } const dappsCategoryMap = new Map() @@ -189,6 +305,7 @@ dappsCategoryMap.set(Categories.UTILITIES, new DappsState()) dappsCategoryMap.set(Categories.OTHER, new DappsState()) const dappsInitialState = { + dapps: null, highestRanked: [], highestRankedFetched: null, recentlyAdded: [], diff --git a/src/modules/Submit/Submit.reducer.js b/src/modules/Submit/Submit.reducer.js index 21dcdc8..0e3c320 100644 --- a/src/modules/Submit/Submit.reducer.js +++ b/src/modules/Submit/Submit.reducer.js @@ -1,13 +1,13 @@ import submitInitialState from '../../common/data/submit' import reducerUtil from '../../common/utils/reducer' import { - onPublishedSuccessAction, - onReceiveTransactionHashAction, + onReceiveTransactionInfoAction, + checkTransactionStatusAction, } from '../TransactionStatus/TransactionStatus.recuder' import BlockchainTool from '../../common/blockchain' -const blockchainSDK = BlockchainTool.init() +const BlockchainSDK = BlockchainTool.init() const SHOW_SUBMIT = 'SHOW_SUBMIT' const CLOSE_SUBMIT = 'CLOSE_SUBMIT' @@ -91,31 +91,27 @@ export const onImgDoneAction = imgBase64 => ({ export const submitAction = dapp => { return async dispatch => { dispatch(closeSubmitAction()) - const createdDapp = await blockchainSDK.DiscoverService.createDApp(1, { + const { tx, id } = await BlockchainSDK.DiscoverService.createDApp(1, { name: dapp.name, url: dapp.url, desc: dapp.desc, category: dapp.category, image: dapp.img, }) - dispatch(onReceiveTransactionHashAction(createdDapp.tx)) - - await blockchainSDK.utils.getTxStatus(createdDapp.tx) - dispatch(onPublishedSuccessAction()) - } -} - -export const statusCheckAction = hash => { - return async dispatch => { - await blockchainSDK.utils.getTxStatus(hash) - dispatch(onPublishedSuccessAction()) + dispatch(onReceiveTransactionInfoAction(id, tx)) + dispatch(checkTransactionStatusAction(tx)) } } const showSubmit = state => { return Object.assign({}, state, { visible: true, + id: '', + name: '', + desc: '', + url: '', img: '', + category: '', imgControl: false, imgControlZoom: 0, imgControlMove: false, diff --git a/src/modules/TransactionStatus/TransactionStatus.container.js b/src/modules/TransactionStatus/TransactionStatus.container.js index 030bcc5..ded9507 100644 --- a/src/modules/TransactionStatus/TransactionStatus.container.js +++ b/src/modules/TransactionStatus/TransactionStatus.container.js @@ -1,12 +1,14 @@ import { connect } from 'react-redux' import TransactionStatus from './TransactionStatus' -import { hideAction } from './TransactionStatus.recuder' -import { statusCheckAction } from '../Submit/Submit.reducer' +import { + hideAction, + checkTransactionStatusAction, +} from './TransactionStatus.recuder' const mapStateToProps = state => state.transactionStatus const mapDispatchToProps = dispatch => ({ hide: () => dispatch(hideAction()), - statusCheck: hash => dispatch(statusCheckAction(hash)), + checkTransactionStatus: hash => dispatch(checkTransactionStatusAction(hash)), }) export default connect( diff --git a/src/modules/TransactionStatus/TransactionStatus.jsx b/src/modules/TransactionStatus/TransactionStatus.jsx index afdf4bb..9f1e2fc 100644 --- a/src/modules/TransactionStatus/TransactionStatus.jsx +++ b/src/modules/TransactionStatus/TransactionStatus.jsx @@ -7,31 +7,31 @@ import loadingSpinner from '../../common/assets/images/loading-spinner.svg' class TransactionStatus extends React.Component { componentDidMount() { - this.checkPublished() + // this.checkPublished() this.checkTransactionHash() } - componentDidUpdate() { - this.checkPublished() - } + // componentDidUpdate() { + // this.checkPublished() + // } - checkPublished() { - const { published, hide } = this.props - if (published) { - setTimeout(() => { - hide() - }, 1000) - } - } + // checkPublished() { + // const { published, hide } = this.props + // if (published) { + // setTimeout(() => { + // hide() + // }, 1000) + // } + // } checkTransactionHash() { - const { dappTransactionHash, statusCheck } = this.props - if (dappTransactionHash === '') return - statusCheck(dappTransactionHash) + const { dappTx, checkTransactionStatus } = this.props + if (dappTx === '') return + checkTransactionStatus(dappTx) } render() { - const { dappName, dappImg, published, progress } = this.props + const { dappName, dappImg, published, progress, failed, hide } = this.props return (