Implement API integration logic

This commit is contained in:
Lyubomir Kiprov 2019-05-31 19:07:23 +03:00
parent 4805add080
commit 27358ed66b
20 changed files with 759 additions and 661 deletions

View File

@ -6,13 +6,14 @@
"dependencies": {
"@babel/runtime-corejs2": "^7.4.3",
"@trailofbits/embark-contract-info": "^1.0.0",
"axios": "^0.18.0",
"bignumber.js": "^8.1.1",
"bs58": "^4.0.1",
"connected-react-router": "^6.3.2",
"debounce": "^1.2.0",
"decimal.js": "^10.0.2",
"embark": "^4.0.2",
"embark-solium": "0.0.1",
"decimal.js": "^10.0.2",
"history": "^4.7.2",
"moment": "^2.24.0",
"node-sass": "^4.11.0",

View File

@ -1,48 +1,46 @@
/* global web3 */
import utils from './utils'
import EmbarkJS from '../../embarkArtifacts/embarkjs'
import utils from './utils';
import EmbarkJS from '../../embarkArtifacts/embarkjs';
import * as IPFSService from './ipfs'
import SNTService from './services/contracts-services/snt-service/snt-service'
import DiscoverService from './services/contracts-services/discover-service/discover-service'
import SNTService from './services/snt-service/snt-service';
import DiscoverService from './services/discover-service/discover-service';
const initServices = function() {
const sharedContext = {
account: '0x0000000000000000000000000000000000000000',
}
account: '0x0000000000000000000000000000000000000000'
};
sharedContext.SNTService = new SNTService(sharedContext)
sharedContext.DiscoverService = new DiscoverService(sharedContext)
sharedContext.SNTService = new SNTService(sharedContext);
sharedContext.DiscoverService = new DiscoverService(sharedContext);
return {
IPFSService,
SNTService: sharedContext.SNTService,
DiscoverService: sharedContext.DiscoverService,
utils,
}
}
utils
};
};
const getInstance = async () => {
return new Promise((resolve, reject) => {
const returnInstance = () => {
try {
const services = initServices()
resolve(services)
const services = initServices();
resolve(services);
} catch (error) {
reject(error.message)
reject(error.message);
}
}
};
if (web3.currentProvider) {
returnInstance()
returnInstance();
} else {
EmbarkJS.onReady(err => {
if (err) reject(err)
if (err) reject(err);
returnInstance()
})
returnInstance();
});
}
})
}
});
};
export default { getInstance, utils }
export default { getInstance, utils };

View File

@ -1,28 +0,0 @@
import bs58 from 'bs58'
export const base64ToBlob = base64Text => {
const byteString = atob(base64Text.split(',')[1])
const arrayBuffer = new ArrayBuffer(byteString.length)
const uintArray = new Uint8Array(arrayBuffer)
for (let i = 0; i < byteString.length; i++) {
uintArray[i] = byteString.charCodeAt(i)
}
return new Blob([arrayBuffer])
}
export const getBytes32FromIpfsHash = ipfsListing => {
const decodedHash = bs58
.decode(ipfsListing)
.slice(2)
.toString('hex')
return `0x${decodedHash}`
}
export const getIpfsHashFromBytes32 = bytes32Hex => {
const hashHex = `1220${bytes32Hex.slice(2)}`
const hashBytes = Buffer.from(hashHex, 'hex')
const hashStr = bs58.encode(hashBytes)
return hashStr
}

View File

@ -1,63 +0,0 @@
import * as helpers from './helpers'
import EmbarkJS from '../../../embarkArtifacts/embarkjs'
const checkIPFSAvailability = async () => {
const isAvailable = await EmbarkJS.Storage.isAvailable()
if (!isAvailable) {
throw new Error('IPFS Storage is unavailable')
}
}
const uploadImage = async base64Image => {
const imageFile = [
{
files: [helpers.base64ToBlob(base64Image)],
},
]
return EmbarkJS.Storage.uploadFile(imageFile)
}
const uploadMetadata = async metadata => {
const hash = await EmbarkJS.Storage.saveText(metadata)
return helpers.getBytes32FromIpfsHash(hash)
}
export const uploadDAppMetadata = async metadata => {
try {
await checkIPFSAvailability()
metadata.image = await uploadImage(metadata.image)
const uploadedMetadataHash = await uploadMetadata(JSON.stringify(metadata))
return uploadedMetadataHash
} catch (error) {
throw new Error(
`Uploading DApp metadata to IPFS failed. Details: ${error.message}`,
)
}
}
const retrieveMetadata = async metadataBytes32 => {
const metadataHash = helpers.getIpfsHashFromBytes32(metadataBytes32)
return EmbarkJS.Storage.get(metadataHash)
}
const retrieveImageUrl = async imageHash => {
return EmbarkJS.Storage.getUrl(imageHash)
}
export const retrieveDAppMetadataByHash = async metadataBytes32 => {
try {
await checkIPFSAvailability()
const metadata = JSON.parse(await retrieveMetadata(metadataBytes32))
metadata.image = await retrieveImageUrl(metadata.image)
return metadata
} catch (error) {
throw new Error(
`Fetching metadata from IPFS failed. Details: ${error.message}`,
)
}
}

View File

@ -0,0 +1,39 @@
/* global web3 */
import EmbarkJS from '../../../embarkArtifacts/embarkjs';
class BlockchainService {
constructor(sharedContext, contract, Validator) {
this.contract = contract.address;
this.sharedContext = sharedContext;
this.validator = new Validator(this);
}
async getAccount() {
try {
if (web3 && EmbarkJS.Blockchain.Providers.web3) {
const account = (await EmbarkJS.enableEthereum())[0];
return (
account || (await EmbarkJS.Blockchain.Providers.web3.getAccounts())[0]
);
}
return '0x0000000000000000000000000000000000000000';
} catch (error) {
throw new Error(
'Could not unlock an account. Consider installing Status on your mobile or Metamask extension'
);
}
}
async __unlockServiceAccount() {
this.sharedContext.account = await this.getAccount();
if (!this.sharedContext.account) {
throw new Error('web3 is missing');
}
}
}
export default BlockchainService;

View File

@ -1,35 +0,0 @@
/* global web3 */
import EmbarkJS from '../../../../embarkArtifacts/embarkjs'
const getAccount = async () => {
try {
const account = (await EmbarkJS.enableEthereum())[0]
return (
account || (await EmbarkJS.Blockchain.Providers.web3.getAccounts())[0]
)
} catch (error) {
throw new Error(
'Could not unlock an account. Consider installing Status on your mobile or Metamask extension',
)
}
}
class BlockchainService {
constructor(sharedContext, contract, Validator) {
this.contract = contract.address
this.sharedContext = sharedContext
this.validator = new Validator(this)
}
async __unlockServiceAccount() {
if (web3 && EmbarkJS.Blockchain.Providers.web3) {
this.sharedContext.account = await getAccount()
} else {
throw new Error('web3 is missing')
}
}
}
export default BlockchainService

View File

@ -1,178 +0,0 @@
/* global web3 */
import { broadcastContractFn } from '../helpers'
import * as ipfsSDK from '../../../ipfs'
import BlockchainService from '../blockchain-service'
import DiscoverValidator from './discover-validator'
import DiscoverContract from '../../../../../embarkArtifacts/contracts/Discover'
class DiscoverService extends BlockchainService {
constructor(sharedContext) {
super(sharedContext, DiscoverContract, DiscoverValidator)
}
// View methods
async upVoteEffect(id, amount) {
await this.validator.validateUpVoteEffect(id, amount)
return DiscoverContract.methods
.upvoteEffect(id, amount)
.call({ from: this.sharedContext.account })
}
async downVoteCost(id) {
const dapp = await this.getDAppById(id)
return DiscoverContract.methods
.downvoteCost(dapp.id)
.call({ from: this.sharedContext.account })
}
async getDAppsCount() {
return DiscoverContract.methods
.getDAppsCount()
.call({ from: this.sharedContext.account })
}
async getDApps() {
const dapps = []
const dappsCount = await this.getDAppsCount()
try {
for (let i = 0; i < dappsCount; i++) {
const dapp = await DiscoverContract.methods
.dapps(i)
.call({ from: this.sharedContext.account })
dapp.metadata = await ipfsSDK.retrieveDAppMetadataByHash(dapp.metadata)
dapps.push(dapp)
}
return dapps
} catch (error) {
throw new Error(`Error fetching dapps. Details: ${error.message}`)
}
}
async getDAppById(id) {
let dapp
try {
const dappId = await DiscoverContract.methods
.id2index(id)
.call({ from: this.sharedContext.account })
dapp = await DiscoverContract.methods
.dapps(dappId)
.call({ from: this.sharedContext.account })
} catch (error) {
throw new Error('Searching DApp does not exists')
}
if (dapp.id != id) {
throw new Error('Error fetching correct data from contract')
}
return dapp
}
async getDAppDataById(id) {
const dapp = await this.getDAppById(id)
try {
dapp.metadata = await ipfsSDK.retrieveDAppMetadataByHash(dapp.metadata)
return dapp
} catch (error) {
throw new Error('Error fetching correct data from IPFS')
}
}
async safeMax() {
return DiscoverContract.methods
.safeMax()
.call({ from: this.sharedContext.account })
}
async isDAppExists(id) {
return DiscoverContract.methods
.existingIDs(id)
.call({ from: this.sharedContext.account })
}
// Transaction methods
async createDApp(amount, metadata) {
const dappMetadata = JSON.parse(JSON.stringify(metadata))
const dappId = web3.utils.keccak256(JSON.stringify(dappMetadata))
await this.validator.validateDAppCreation(dappId, amount)
const uploadedMetadata = await ipfsSDK.uploadDAppMetadata(dappMetadata)
const callData = DiscoverContract.methods
.createDApp(dappId, amount, uploadedMetadata)
.encodeABI()
const createdTx = await this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
)
return { tx: createdTx, id: dappId }
}
async upVote(id, amount) {
await this.validator.validateUpVoting(id, amount)
const callData = DiscoverContract.methods.upvote(id, amount).encodeABI()
return this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
)
}
async downVote(id) {
const dapp = await this.getDAppById(id)
const amount = (await this.downVoteCost(dapp.id)).c
const callData = DiscoverContract.methods
.downvote(dapp.id, amount)
.encodeABI()
return this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
)
}
async withdraw(id, amount) {
await super.__unlockServiceAccount()
await this.validator.validateWithdrawing(id, amount)
try {
return broadcastContractFn(
DiscoverContract.methods.withdraw(id, amount).send,
this.sharedContext.account,
)
} catch (error) {
throw new Error(`Transfer on withdraw failed. Details: ${error.message}`)
}
}
async setMetadata(id, metadata) {
await super.__unlockServiceAccount()
await this.validator.validateMetadataSet(id)
const dappMetadata = JSON.parse(JSON.stringify(metadata))
const uploadedMetadata = await ipfsSDK.uploadDAppMetadata(dappMetadata)
try {
return broadcastContractFn(
DiscoverContract.methods.setMetadata(id, uploadedMetadata).send,
this.sharedContext.account,
)
} catch (error) {
throw new Error(`Uploading metadata failed. Details: ${error.message}`)
}
}
}
export default DiscoverService

View File

@ -0,0 +1,175 @@
/* global web3 */
import { broadcastContractFn } from '../helpers';
import MetadataClient from '../../../clients/metadata-client';
import BlockchainService from '../blockchain-service';
import DiscoverValidator from './discover-validator';
import DiscoverContract from '../../../../embarkArtifacts/contracts/Discover';
class DiscoverService extends BlockchainService {
constructor(sharedContext) {
super(sharedContext, DiscoverContract, DiscoverValidator);
}
// View methods
async upVoteEffect(id, amount) {
await this.validator.validateUpVoteEffect(id, amount);
return DiscoverContract.methods
.upvoteEffect(id, amount)
.call({ from: this.sharedContext.account });
}
async downVoteCost(id) {
const dapp = await this.getDAppById(id);
return DiscoverContract.methods
.downvoteCost(dapp.id)
.call({ from: this.sharedContext.account });
}
async getDAppsCount() {
return DiscoverContract.methods
.getDAppsCount()
.call({ from: this.sharedContext.account });
}
async getDAppById(id) {
let dapp;
try {
const dappId = await DiscoverContract.methods
.id2index(id)
.call({ from: this.sharedContext.account });
dapp = await DiscoverContract.methods
.dapps(dappId)
.call({ from: this.sharedContext.account });
} catch (error) {
throw new Error('Searching DApp does not exists');
}
if (dapp.id != id) {
throw new Error('Error fetching correct data from contract');
}
return dapp;
}
async getDAppDataById(id) {
const dapp = await this.getDAppById(id);
try {
const dappMetadata = await MetadataClient.retrieveMetadata(dapp.metadata);
dapp.metadata = dappMetadata.details;
dapp.metadata.status = dappMetadata.status;
return dapp;
} catch (error) {
throw new Error('Error fetching correct data');
}
}
async safeMax() {
return DiscoverContract.methods
.safeMax()
.call({ from: this.sharedContext.account });
}
async isDAppExists(id) {
return DiscoverContract.methods
.existingIDs(id)
.call({ from: this.sharedContext.account });
}
async checkIfCreatorOfDApp(id) {
const dapp = this.getDAppById(id);
this.sharedContext.account = await super.getAccount();
return dapp.developer == this.sharedContext.account;
}
// Transaction methods
async createDApp(amount, metadata) {
await super.__unlockServiceAccount();
const dappMetadata = JSON.parse(JSON.stringify(metadata));
dappMetadata.uploader = this.sharedContext.account;
const dappId = web3.utils.keccak256(JSON.stringify(dappMetadata));
await this.validator.validateDAppCreation(dappId, amount);
const uploadedMetadata = await MetadataClient.upload(dappMetadata);
const callData = DiscoverContract.methods
.createDApp(dappId, amount, uploadedMetadata)
.encodeABI();
const createdTx = await this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData
);
return { tx: createdTx, id: dappId };
}
async upVote(id, amount) {
await this.validator.validateUpVoting(id, amount);
const callData = DiscoverContract.methods.upvote(id, amount).encodeABI();
return this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData
);
}
async downVote(id) {
const dapp = await this.getDAppById(id);
const amount = (await this.downVoteCost(dapp.id)).c;
const callData = DiscoverContract.methods
.downvote(dapp.id, amount)
.encodeABI();
return this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData
);
}
async withdraw(id, amount) {
await super.__unlockServiceAccount();
await this.validator.validateWithdrawing(id, amount);
try {
return broadcastContractFn(
DiscoverContract.methods.withdraw(id, amount).send,
this.sharedContext.account
);
} catch (error) {
throw new Error(`Transfer on withdraw failed. Details: ${error.message}`);
}
}
async setMetadata(id, metadata) {
await super.__unlockServiceAccount();
await this.validator.validateMetadataSet(id);
const dappMetadata = JSON.parse(JSON.stringify(metadata));
dappMetadata.uploader = this.sharedContext.account;
const uploadedMetadata = await MetadataClient.upload(dappMetadata);
try {
return broadcastContractFn(
DiscoverContract.methods.setMetadata(id, uploadedMetadata).send,
this.sharedContext.account
);
} catch (error) {
throw new Error(`Uploading metadata failed. Details: ${error.message}`);
}
}
}
export default DiscoverService;

View File

@ -1,67 +1,69 @@
class DiscoverValidator {
constructor(service) {
this.service = service
this.service = service;
}
async validateUpVoteEffect(id, amount) {
const dapp = await this.service.getDAppById(id)
const dapp = await this.service.getDAppById(id);
const safeMax = await this.service.safeMax()
const safeMax = await this.service.safeMax();
if (Number(dapp.balance) + amount > safeMax) {
throw new Error(
`You cannot upvote by this much, try with a lower amount. Maximum upvote amount:
${Number(safeMax) - Number(dapp.balance)}`,
)
${Number(safeMax) - Number(dapp.balance)}`
);
}
}
async validateDAppCreation(id, amount) {
const dappExists = await this.service.isDAppExists(id)
const dappExists = await this.service.isDAppExists(id);
if (dappExists) {
throw new Error('You must submit a unique ID')
throw new Error('You must submit a unique ID');
}
if (amount <= 0) {
throw new Error(
'You must spend some SNT to submit a ranking in order to avoid spam',
)
'You must spend some SNT to submit a ranking in order to avoid spam'
);
}
const safeMax = await this.service.safeMax()
const safeMax = await this.service.safeMax();
if (amount > safeMax) {
throw new Error('You cannot stake more SNT than the ceiling dictates')
throw new Error('You cannot stake more SNT than the ceiling dictates');
}
}
async validateUpVoting(id, amount) {
await this.validateUpVoteEffect(id, amount)
await this.validateUpVoteEffect(id, amount);
if (amount <= 0) {
throw new Error('You must send some SNT in order to upvote')
throw new Error('You must send some SNT in order to upvote');
}
}
async validateWithdrawing(id, amount) {
const dapp = await this.service.getDAppById(id)
const dapp = await this.service.getDAppById(id);
if (dapp.developer.toLowerCase() != this.service.sharedContext.account) {
throw new Error('Only the developer can withdraw SNT staked on this data')
throw new Error(
'Only the developer can withdraw SNT staked on this data'
);
}
if (amount > dapp.available) {
throw new Error(
'You can only withdraw a percentage of the SNT staked, less what you have already received',
)
'You can only withdraw a percentage of the SNT staked, less what you have already received'
);
}
}
async validateMetadataSet(id) {
const dapp = await this.service.getDAppById(id)
const dapp = await this.service.getDAppById(id);
if (dapp.developer.toLowerCase() != this.service.sharedContext.account) {
throw new Error('Only the developer can update the metadata')
throw new Error('Only the developer can update the metadata');
}
}
}
export default DiscoverValidator
export default DiscoverValidator;

View File

@ -2,10 +2,10 @@ export const broadcastContractFn = (contractMethod, account) => {
return new Promise((resolve, reject) => {
contractMethod({ from: account })
.on('transactionHash', hash => {
resolve(hash)
resolve(hash);
})
.on('error', error => {
reject(error.message)
})
})
}
reject(error.message);
});
});
};

View File

@ -1,57 +1,57 @@
import { broadcastContractFn } from '../helpers'
import { broadcastContractFn } from '../helpers';
import BlockchainService from '../blockchain-service'
import BlockchainService from '../blockchain-service';
import SNTValidator from './snt-validator'
import SNTToken from '../../../../../embarkArtifacts/contracts/SNT'
import SNTValidator from './snt-validator';
import SNTToken from '../../../../embarkArtifacts/contracts/SNT';
class SNTService extends BlockchainService {
constructor(sharedContext) {
super(sharedContext, SNTToken, SNTValidator)
super(sharedContext, SNTToken, SNTValidator);
}
async allowance(from, to) {
return SNTToken.methods
.allowance(from, to)
.call({ from: this.sharedContext.account })
.call({ from: this.sharedContext.account });
}
async balanceOf(account) {
return SNTToken.methods
.balanceOf(account)
.call({ from: this.sharedContext.account })
.call({ from: this.sharedContext.account });
}
async controller() {
return SNTToken.methods
.controller()
.call({ from: this.sharedContext.account })
.call({ from: this.sharedContext.account });
}
async transferable() {
return SNTToken.methods
.transfersEnabled()
.call({ from: this.sharedContext.account })
.call({ from: this.sharedContext.account });
}
async approveAndCall(spender, amount, callData) {
await super.__unlockServiceAccount()
await this.validator.validateApproveAndCall(spender, amount)
await super.__unlockServiceAccount();
await this.validator.validateApproveAndCall(spender, amount);
return broadcastContractFn(
SNTToken.methods.approveAndCall(spender, amount, callData).send,
this.sharedContext.account,
)
this.sharedContext.account
);
}
// This is for testing purpose only
async generateTokens() {
await super.__unlockServiceAccount()
await super.__unlockServiceAccount();
await SNTToken.methods
.generateTokens(this.sharedContext.account, 10000)
.send({ from: this.sharedContext.account })
.send({ from: this.sharedContext.account });
}
}
export default SNTService
export default SNTService;

View File

@ -1,35 +1,35 @@
class SNTValidator {
constructor(service) {
this.service = service
this.service = service;
}
async validateSNTTransferFrom(amount) {
const toBalance = await this.service.balanceOf(
this.service.sharedContext.account,
)
this.service.sharedContext.account
);
if (toBalance < amount) {
throw new Error('Not enough SNT balance')
throw new Error('Not enough SNT balance');
}
}
async validateApproveAndCall(spender, amount) {
const isTransferableToken = await this.service.transferable()
const isTransferableToken = await this.service.transferable();
if (!isTransferableToken) {
throw new Error('Token is not transferable')
throw new Error('Token is not transferable');
}
await this.validateSNTTransferFrom(amount)
await this.validateSNTTransferFrom(amount);
const allowance = await this.service.allowance(
this.service.sharedContext.account,
spender,
)
spender
);
if (amount != 0 && allowance != 0) {
throw new Error('You have allowance already')
throw new Error('You have allowance already');
}
}
}
export default SNTValidator
export default SNTValidator;

View File

@ -0,0 +1,6 @@
{
"UPLOAD": "http://localhost:4000/metadata",
"UPDATE": "http://localhost:4000/metadata/update",
"RETRIEVE_METADATA": "http://localhost:4000/metadata",
"RETRIEVE_ALL_METADATA": "http://localhost:4000/metadata/all"
}

View File

@ -0,0 +1,32 @@
const axios = require('axios');
const DEFAULT_HEADERS = {
Accept: 'application/json',
'Content-type': 'application/json'
};
const executeRequest = async function(method, url, reqStruct) {
return axios({
method,
url,
data: {
...reqStruct.body
},
headers: {
...DEFAULT_HEADERS,
...reqStruct.headers
}
});
};
class HTTPClient {
static async postRequest(url, body, headers = '') {
return executeRequest('POST', url, { body, headers });
}
static async getRequest(url, headers = '') {
return executeRequest('GET', url, { headers });
}
}
export default HTTPClient;

View File

@ -0,0 +1,6 @@
// If you want to use IPFSClient -> extend it
import APIClient from './metadata-clients/api-client';
class MetadataClient extends APIClient {}
export default new MetadataClient();

View File

@ -0,0 +1,45 @@
import HTTPClient from '../http-client';
import * as helpers from '../../utils/metadata-utils';
import metadataClientEndpoints from '../endpoints/metadata-client-endpoints';
class APIClient {
async upload(metadata) {
const uploadedDataResponse = await HTTPClient.postRequest(
metadataClientEndpoints.UPLOAD,
{ details: metadata }
);
return helpers.getBytes32FromIpfsHash(uploadedDataResponse.data.hash);
}
async retrieveMetadata(metadataBytes32) {
const convertedHash = helpers.getIpfsHashFromBytes32(metadataBytes32);
const retrievedMetadataResponse = await HTTPClient.getRequest(
`${metadataClientEndpoints.RETRIEVE_METADATA}/${convertedHash}`
);
return retrievedMetadataResponse.data;
}
async retrieveAllDappsMetadata() {
const retrievedDAppsMetadataResponse = await HTTPClient.getRequest(
`${metadataClientEndpoints.RETRIEVE_ALL_METADATA}`
);
const formatedDappsMetadata = {};
const metadataHashes = Object.keys(retrievedDAppsMetadataResponse.data);
for (let i = 0; i < metadataHashes.length; i++) {
const convertedDappMetadataHash = helpers.getBytes32FromIpfsHash(
metadataHashes[i]
);
formatedDappsMetadata[convertedDappMetadataHash] =
retrievedDAppsMetadataResponse.data[metadataHashes[i]];
}
return formatedDappsMetadata;
}
}
export default APIClient;

View File

@ -0,0 +1,69 @@
import * as helpers from '../../utils/metadata-utils';
import EmbarkJS from '../../../embarkArtifacts/embarkjs';
const checkIPFSAvailability = async () => {
const isAvailable = await EmbarkJS.Storage.isAvailable();
if (!isAvailable) {
throw new Error('IPFS Storage is unavailable');
}
};
const uploadImage = async base64Image => {
const imageFile = [
{
files: [helpers.base64ToBlob(base64Image)]
}
];
return EmbarkJS.Storage.uploadFile(imageFile);
};
const uploadMetadata = async metadata => {
const hash = await EmbarkJS.Storage.saveText(metadata);
return helpers.getBytes32FromIpfsHash(hash);
};
const retrieveMetadata = async metadataBytes32 => {
const metadataHash = helpers.getIpfsHashFromBytes32(metadataBytes32);
return EmbarkJS.Storage.get(metadataHash);
};
const retrieveImageUrl = async imageHash => {
return EmbarkJS.Storage.getUrl(imageHash);
};
class IPFSClient {
async upload(metadata) {
try {
await checkIPFSAvailability();
metadata.image = await uploadImage(metadata.image);
const uploadedMetadataHash = await uploadMetadata(
JSON.stringify(metadata)
);
return uploadedMetadataHash;
} catch (error) {
throw new Error(
`Uploading DApp metadata to IPFS failed. Details: ${error.message}`
);
}
}
async retrieveMetadata(metadataBytes32) {
try {
await checkIPFSAvailability();
const metadata = JSON.parse(await retrieveMetadata(metadataBytes32));
metadata.image = await retrieveImageUrl(metadata.image);
return metadata;
} catch (error) {
throw new Error(
`Fetching metadata from IPFS failed. Details: ${error.message}`
);
}
}
}
export default IPFSClient;

View File

@ -0,0 +1,28 @@
import bs58 from 'bs58';
export const base64ToBlob = base64Text => {
const byteString = atob(base64Text.split(',')[1]);
const arrayBuffer = new ArrayBuffer(byteString.length);
const uintArray = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
uintArray[i] = byteString.charCodeAt(i);
}
return new Blob([arrayBuffer]);
};
export const getBytes32FromIpfsHash = ipfsListing => {
const decodedHash = bs58
.decode(ipfsListing)
.slice(2)
.toString('hex');
return `0x${decodedHash}`;
};
export const getIpfsHashFromBytes32 = bytes32Hex => {
const hashHex = `1220${bytes32Hex.slice(2)}`;
const hashBytes = Buffer.from(hashHex, 'hex');
const hashStr = bs58.encode(hashBytes);
return hashStr;
};

View File

@ -1,308 +1,308 @@
// import hardcodedDapps from '../../common/data/dapps'
import * as Categories from '../../common/data/categories'
import reducerUtil from '../../common/utils/reducer'
import { showAlertAction } from '../Alert/Alert.reducer'
import BlockchainSDK from '../../common/blockchain'
import { TYPE_SUBMIT } from '../TransactionStatus/TransactionStatus.utilities'
import * as Categories from '../../common/data/categories';
import reducerUtil from '../../common/utils/reducer';
import { showAlertAction } from '../Alert/Alert.reducer';
import BlockchainSDK from '../../common/blockchain';
import { TYPE_SUBMIT } from '../TransactionStatus/TransactionStatus.utilities';
const ON_FINISH_FETCH_ALL_DAPPS_ACTION =
'DAPPS_ON_FINISH_FETCH_ALL_DAPPS_ACTION'
'DAPPS_ON_FINISH_FETCH_ALL_DAPPS_ACTION';
const ON_START_FETCH_HIGHEST_RANKED = 'DAPPS_ON_START_FETCH_HIGHEST_RANKED'
const ON_FINISH_FETCH_HIGHEST_RANKED = 'DAPPS_ON_FINISH_FETCH_HIGHEST_RANKED'
const ON_START_FETCH_RECENTLY_ADDED = 'DAPPS_ON_START_FETCH_RECENTLY_ADDED'
const ON_FINISH_FETCH_RECENTLY_ADDED = 'DAPPS_ON_FINISH_FETCH_RECENTLY_ADDED'
const ON_START_FETCH_HIGHEST_RANKED = 'DAPPS_ON_START_FETCH_HIGHEST_RANKED';
const ON_FINISH_FETCH_HIGHEST_RANKED = 'DAPPS_ON_FINISH_FETCH_HIGHEST_RANKED';
const ON_START_FETCH_RECENTLY_ADDED = 'DAPPS_ON_START_FETCH_RECENTLY_ADDED';
const ON_FINISH_FETCH_RECENTLY_ADDED = 'DAPPS_ON_FINISH_FETCH_RECENTLY_ADDED';
const ON_START_FETCH_BY_CATEGORY = 'DAPPS_ON_START_FETCH_BY_CATEGORY'
const ON_FINISH_FETCH_BY_CATEGORY = 'DAPPS_ON_FINISH_FETCH_BY_CATEGORY'
const ON_START_FETCH_BY_CATEGORY = 'DAPPS_ON_START_FETCH_BY_CATEGORY';
const ON_FINISH_FETCH_BY_CATEGORY = 'DAPPS_ON_FINISH_FETCH_BY_CATEGORY';
const ON_UPDATE_DAPP_DATA = 'DAPPS_ON_UPDATE_DAPP_DATA'
const ON_UPDATE_DAPP_DATA = 'DAPPS_ON_UPDATE_DAPP_DATA';
const RECENTLY_ADDED_SIZE = 50
const HIGHEST_RANKED_SIZE = 50
const RECENTLY_ADDED_SIZE = 50;
const HIGHEST_RANKED_SIZE = 50;
class DappsState {
constructor() {
this.items = []
this.hasMore = true
this.fetched = null
this.items = [];
this.hasMore = true;
this.fetched = null;
}
canFetch() {
return this.hasMore && this.fetched !== true
return this.hasMore && this.fetched !== true;
}
setFetched(fetched) {
this.fetched = fetched
this.fetched = fetched;
}
appendItems(items) {
const availableNames = new Set()
let addedItems = 0
const availableNames = new Set();
let addedItems = 0;
for (let i = 0; i < this.items.length; i += 1)
availableNames.add(this.items[i].name)
availableNames.add(this.items[i].name);
for (let i = 0; i < items.length; i += 1) {
if (availableNames.has(items[i].name) === false) {
addedItems += 1
this.items.push(items[i])
addedItems += 1;
this.items.push(items[i]);
}
}
this.hasMore = addedItems !== 0
this.hasMore = addedItems !== 0;
}
cloneWeakItems() {
this.items = [...this.items]
return this
this.items = [...this.items];
return this;
}
}
export const onFinishFetchAllDappsAction = dapps => ({
type: ON_FINISH_FETCH_ALL_DAPPS_ACTION,
payload: dapps,
})
payload: dapps
});
export const onStartFetchHighestRankedAction = () => ({
type: ON_START_FETCH_HIGHEST_RANKED,
payload: null,
})
payload: null
});
export const onFinishFetchHighestRankedAction = highestRanked => ({
type: ON_FINISH_FETCH_HIGHEST_RANKED,
payload: highestRanked,
})
payload: highestRanked
});
export const onStartFetchRecentlyAddedAction = () => ({
type: ON_START_FETCH_RECENTLY_ADDED,
payload: null,
})
payload: null
});
export const onFinishFetchRecentlyAddedAction = recentlyAdded => ({
type: ON_FINISH_FETCH_RECENTLY_ADDED,
payload: recentlyAdded,
})
payload: recentlyAdded
});
export const onStartFetchByCategoryAction = category => ({
type: ON_START_FETCH_BY_CATEGORY,
payload: category,
})
payload: category
});
export const onFinishFetchByCategoryAction = (category, dapps) => ({
type: ON_FINISH_FETCH_BY_CATEGORY,
payload: { category, dapps },
})
payload: { category, dapps }
});
const fetchAllDappsInState = async (dispatch, getState) => {
const state = getState()
const { transactionStatus } = state
const stateDapps = state.dapps
const state = getState();
const { transactionStatus } = state;
const stateDapps = state.dapps;
if (stateDapps.dapps === null) {
try {
const blockchain = await BlockchainSDK.getInstance()
let dapps = await blockchain.DiscoverService.getDApps()
const blockchain = await BlockchainSDK.getInstance();
let dapps = await blockchain.DiscoverService.getDApps();
dapps = dapps.map(dapp => {
return Object.assign(dapp.metadata, {
id: dapp.id,
sntValue: parseInt(dapp.effectiveBalance, 10),
})
})
sntValue: parseInt(dapp.effectiveBalance, 10)
});
});
dapps.sort((a, b) => {
return b.sntValue - a.sntValue
})
return b.sntValue - a.sntValue;
});
if (transactionStatus.type === TYPE_SUBMIT) {
for (let i = 0; i < dapps.length; i += 1) {
if (dapps[i].id === transactionStatus.dappId) {
dapps.splice(i, 1)
break
dapps.splice(i, 1);
break;
}
}
}
dispatch(onFinishFetchAllDappsAction(dapps))
return dapps
dispatch(onFinishFetchAllDappsAction(dapps));
return dapps;
} catch (e) {
dispatch(showAlertAction(e.message))
dispatch(onFinishFetchAllDappsAction([]))
return []
dispatch(showAlertAction(e.message));
dispatch(onFinishFetchAllDappsAction([]));
return [];
}
}
return stateDapps.dapps
}
return stateDapps.dapps;
};
export const fetchAllDappsAction = () => {
return async (dispatch, getState) => {
dispatch(onStartFetchHighestRankedAction())
dispatch(onStartFetchRecentlyAddedAction())
dispatch(onStartFetchHighestRankedAction());
dispatch(onStartFetchRecentlyAddedAction());
const dapps = await fetchAllDappsInState(dispatch, getState)
const dapps = await fetchAllDappsInState(dispatch, getState);
const highestRanked = dapps.slice(0, HIGHEST_RANKED_SIZE)
let recentlyAdded = [...dapps]
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)
return new Date().getTime(b.dateAdded) - new Date(a.dateAdded).getTime();
});
recentlyAdded = recentlyAdded.slice(0, RECENTLY_ADDED_SIZE);
dispatch(onFinishFetchHighestRankedAction(highestRanked))
dispatch(onFinishFetchRecentlyAddedAction(recentlyAdded))
}
}
dispatch(onFinishFetchHighestRankedAction(highestRanked));
dispatch(onFinishFetchRecentlyAddedAction(recentlyAdded));
};
};
export const fetchByCategoryAction = category => {
return async (dispatch, getState) => {
dispatch(onStartFetchByCategoryAction(category))
dispatch(onStartFetchByCategoryAction(category));
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)
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))
}
}
dispatch(onFinishFetchByCategoryAction(category, dappsCategorySlice));
};
};
export const onUpdateDappDataAction = dapp => ({
type: ON_UPDATE_DAPP_DATA,
payload: dapp,
})
payload: dapp
});
const onFinishFetchAllDapps = (state, dapps) => {
return Object.assign({}, state, { dapps })
}
return Object.assign({}, state, { dapps });
};
const onStartFetchHightestRanked = state => {
return Object.assign({}, state, {
highestRankedFetched: false,
})
}
highestRankedFetched: false
});
};
const onFinishFetchHighestRanked = (state, payload) => {
return Object.assign({}, state, {
highestRanked: payload,
highestRankedFetched: true,
})
}
highestRankedFetched: true
});
};
const onStartFetchRecentlyAdded = state => {
return Object.assign({}, state, {
recentlyAddedFetched: false,
})
}
recentlyAddedFetched: false
});
};
const onFinishFetchRecentlyAdded = (state, payload) => {
return Object.assign({}, state, {
recentlyAdded: payload,
recentlyAddedFetched: true,
})
}
recentlyAddedFetched: true
});
};
const onStartFetchByCategory = (state, payload) => {
const dappsCategoryMap = new Map()
const dappsCategoryMap = new Map();
state.dappsCategoryMap.forEach((dappState, category) => {
dappsCategoryMap.set(category, dappState.cloneWeakItems())
if (category === payload) dappState.setFetched(true)
})
dappsCategoryMap.set(category, dappState.cloneWeakItems());
if (category === payload) dappState.setFetched(true);
});
return Object.assign({}, state, {
dappsCategoryMap,
})
}
dappsCategoryMap
});
};
const onFinishFetchByCategory = (state, payload) => {
const { category, dapps } = payload
const { category, dapps } = payload;
const dappsCategoryMap = new Map()
const dappsCategoryMap = new Map();
state.dappsCategoryMap.forEach((dappState, category_) => {
dappsCategoryMap.set(category_, dappState)
dappsCategoryMap.set(category_, dappState);
if (category_ === category) {
dappState.setFetched(false)
dappState.appendItems(dapps)
dappState.setFetched(false);
dappState.appendItems(dapps);
}
})
});
return Object.assign({}, state, {
dappsCategoryMap,
})
}
dappsCategoryMap
});
};
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
source.splice(i, 0, dapp);
break;
}
}
}
};
const onUpdateDappData = (state, dapp) => {
const dappsCategoryMap = new Map()
const { dapps } = state
let { highestRanked, recentlyAdded } = state
let update = false
const dappsCategoryMap = new Map();
const { dapps } = state;
let { highestRanked, recentlyAdded } = state;
let update = false;
state.dappsCategoryMap.forEach((dappState, category_) => {
dappsCategoryMap.set(category_, dappState.cloneWeakItems())
})
dappsCategoryMap.set(category_, dappState.cloneWeakItems());
});
for (let i = 0; i < dapps.length; i += 1) {
if (dapps[i].id === dapp.id) {
dapps[i] = dapp
update = true
break
dapps[i] = dapp;
update = true;
break;
}
}
if (update === false) {
insertDappIntoSortedArray(dapps, dapp, (target, dappItem) => {
return target.sntValue < dappItem.sntValue
})
return target.sntValue < dappItem.sntValue;
});
insertDappIntoSortedArray(highestRanked, dapp, (target, dappItem) => {
return target.sntValue < dappItem.sntValue
})
highestRanked = state.highestRanked.splice(0, HIGHEST_RANKED_SIZE)
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)
);
});
recentlyAdded = recentlyAdded.splice(0, RECENTLY_ADDED_SIZE);
const dappState = dappsCategoryMap.get(dapp.category)
const dappState = dappsCategoryMap.get(dapp.category);
insertDappIntoSortedArray(dappState.items, dapp, (target, dappItem) => {
return target.sntValue < dappItem.sntValue
})
return target.sntValue < dappItem.sntValue;
});
} else {
for (let i = 0; i < highestRanked.length; i += 1) {
if (highestRanked[i].id === dapp.id) {
highestRanked[i] = dapp
break
highestRanked[i] = dapp;
break;
}
}
for (let i = 0; i < recentlyAdded.length; i += 1) {
if (recentlyAdded[i].id === dapp.id) {
recentlyAdded[i] = dapp
break
recentlyAdded[i] = dapp;
break;
}
}
dappsCategoryMap.forEach(dappState => {
const dappStateRef = dappState
const dappStateRef = dappState;
for (let i = 0; i < dappStateRef.items.length; i += 1) {
if (dappStateRef.items[i].id === dapp.id) {
dappStateRef.items[i] = dapp
break
dappStateRef.items[i] = dapp;
break;
}
}
})
});
}
return Object.assign({}, state, {
dapps: [...dapps],
highestRanked: [...highestRanked],
recentlyAdded: [...recentlyAdded],
dappsCategoryMap,
})
}
dappsCategoryMap
});
};
const map = {
[ON_FINISH_FETCH_ALL_DAPPS_ACTION]: onFinishFetchAllDapps,
@ -312,17 +312,17 @@ const map = {
[ON_FINISH_FETCH_RECENTLY_ADDED]: onFinishFetchRecentlyAdded,
[ON_START_FETCH_BY_CATEGORY]: onStartFetchByCategory,
[ON_FINISH_FETCH_BY_CATEGORY]: onFinishFetchByCategory,
[ON_UPDATE_DAPP_DATA]: onUpdateDappData,
}
[ON_UPDATE_DAPP_DATA]: onUpdateDappData
};
const dappsCategoryMap = new Map()
dappsCategoryMap.set(Categories.EXCHANGES, new DappsState())
dappsCategoryMap.set(Categories.MARKETPLACES, new DappsState())
dappsCategoryMap.set(Categories.COLLECTIBLES, new DappsState())
dappsCategoryMap.set(Categories.GAMES, new DappsState())
dappsCategoryMap.set(Categories.SOCIAL_NETWORKS, new DappsState())
dappsCategoryMap.set(Categories.UTILITIES, new DappsState())
dappsCategoryMap.set(Categories.OTHER, new DappsState())
const dappsCategoryMap = new Map();
dappsCategoryMap.set(Categories.EXCHANGES, new DappsState());
dappsCategoryMap.set(Categories.MARKETPLACES, new DappsState());
dappsCategoryMap.set(Categories.COLLECTIBLES, new DappsState());
dappsCategoryMap.set(Categories.GAMES, new DappsState());
dappsCategoryMap.set(Categories.SOCIAL_NETWORKS, new DappsState());
dappsCategoryMap.set(Categories.UTILITIES, new DappsState());
dappsCategoryMap.set(Categories.OTHER, new DappsState());
const dappsInitialState = {
dapps: null,
@ -330,7 +330,7 @@ const dappsInitialState = {
highestRankedFetched: null,
recentlyAdded: [],
recentlyAddedFetched: null,
dappsCategoryMap,
}
dappsCategoryMap
};
export default reducerUtil(map, dappsInitialState)
export default reducerUtil(map, dappsInitialState);

View File

@ -1,149 +1,150 @@
import submitInitialState from '../../common/data/submit'
import reducerUtil from '../../common/utils/reducer'
import submitInitialState from '../../common/data/submit';
import reducerUtil from '../../common/utils/reducer';
import {
onReceiveTransactionInfoAction,
checkTransactionStatusAction,
onStartProgressAction,
hideAction,
} from '../TransactionStatus/TransactionStatus.recuder'
import { TYPE_SUBMIT } from '../TransactionStatus/TransactionStatus.utilities'
import { showAlertAction } from '../Alert/Alert.reducer'
hideAction
} from '../TransactionStatus/TransactionStatus.recuder';
import { TYPE_SUBMIT } from '../TransactionStatus/TransactionStatus.utilities';
import { showAlertAction } from '../Alert/Alert.reducer';
import BlockchainSDK from '../../common/blockchain'
import BlockchainSDK from '../../common/blockchain';
const SHOW_SUBMIT_AFTER_CHECK = 'SUBMIT_SHOW_SUBMIT_AFTER_CHECK'
const CLOSE_SUBMIT = 'SUBMIT_CLOSE_SUBMIT'
const ON_INPUT_NAME = 'SUBMIT_ON_INPUT_NAME'
const ON_INPUT_DESC = 'SUBMIT_ON_INPUT_DESC'
const ON_INPUT_URL = 'SUBMIT_ON_INPUT_URL'
const ON_SELECT_CATEGORY = 'SUBMIT_ON_SELECT_CATEGORY'
const ON_IMG_READ = 'SUBMIT_ON_IMG_READ'
const ON_IMG_ZOOM = 'SUBMIT_ON_IMG_ZOOM'
const ON_IMG_MOVE_CONTROL = 'SUBMIT_ON_IMG_MOVE_CONTROL'
const ON_IMG_MOVE = 'SUBMIT_ON_IMG_MOVE'
const ON_IMG_CANCEL = 'SUBMIT_ON_IMG_CANCEL'
const ON_IMG_DONE = 'SUBMIT_ON_IMG_DONE'
const SHOW_SUBMIT_AFTER_CHECK = 'SUBMIT_SHOW_SUBMIT_AFTER_CHECK';
const CLOSE_SUBMIT = 'SUBMIT_CLOSE_SUBMIT';
const ON_INPUT_NAME = 'SUBMIT_ON_INPUT_NAME';
const ON_INPUT_DESC = 'SUBMIT_ON_INPUT_DESC';
const ON_INPUT_URL = 'SUBMIT_ON_INPUT_URL';
const ON_SELECT_CATEGORY = 'SUBMIT_ON_SELECT_CATEGORY';
const ON_IMG_READ = 'SUBMIT_ON_IMG_READ';
const ON_IMG_ZOOM = 'SUBMIT_ON_IMG_ZOOM';
const ON_IMG_MOVE_CONTROL = 'SUBMIT_ON_IMG_MOVE_CONTROL';
const ON_IMG_MOVE = 'SUBMIT_ON_IMG_MOVE';
const ON_IMG_CANCEL = 'SUBMIT_ON_IMG_CANCEL';
const ON_IMG_DONE = 'SUBMIT_ON_IMG_DONE';
const SWITCH_TO_RATING = 'SUBMIT_SWITCH_TO_RATING'
const ON_INPUT_SNT_VALUE = 'SUBMIT_ON_INPUT_SNT_VALUE'
const SWITCH_TO_RATING = 'SUBMIT_SWITCH_TO_RATING';
const ON_INPUT_SNT_VALUE = 'SUBMIT_ON_INPUT_SNT_VALUE';
export const showSubmitActionAfterCheck = () => {
window.location.hash = 'submit'
window.location.hash = 'submit';
return {
type: SHOW_SUBMIT_AFTER_CHECK,
payload: null,
}
}
payload: null
};
};
export const showSubmitAction = () => {
return (dispatch, getState) => {
const state = getState()
const state = getState();
if (state.transactionStatus.progress) {
dispatch(
showAlertAction(
'There is an active transaction. Please wait for it to finish and then you could be able to create your Ðapp',
),
)
} else dispatch(showSubmitActionAfterCheck())
}
}
'There is an active transaction. Please wait for it to finish and then you could be able to create your Ðapp'
)
);
} else dispatch(showSubmitActionAfterCheck());
};
};
export const closeSubmitAction = () => {
window.history.back()
window.history.back();
return {
type: CLOSE_SUBMIT,
payload: null,
}
}
payload: null
};
};
export const onInputNameAction = name => ({
type: ON_INPUT_NAME,
payload: name,
})
payload: name
});
export const onInputDescAction = desc => ({
type: ON_INPUT_DESC,
payload: desc.substring(0, 140),
})
payload: desc.substring(0, 140)
});
export const onInputUrlAction = url => ({
type: ON_INPUT_URL,
payload: url,
})
payload: url
});
export const onSelectCategoryAction = category => ({
type: ON_SELECT_CATEGORY,
payload: category,
})
payload: category
});
export const onImgReadAction = imgBase64 => ({
type: ON_IMG_READ,
payload: imgBase64,
})
payload: imgBase64
});
export const onImgZoomAction = zoom => ({
type: ON_IMG_ZOOM,
payload: zoom,
})
payload: zoom
});
export const onImgMoveControlAction = move => ({
type: ON_IMG_MOVE_CONTROL,
payload: move,
})
payload: move
});
export const onImgMoveAction = (x, y) => ({
type: ON_IMG_MOVE,
payload: { x, y },
})
payload: { x, y }
});
export const onImgCancelAction = () => ({
type: ON_IMG_CANCEL,
payload: null,
})
payload: null
});
export const onImgDoneAction = imgBase64 => ({
type: ON_IMG_DONE,
payload: imgBase64,
})
payload: imgBase64
});
export const submitAction = (dapp, sntValue) => {
return async dispatch => {
dispatch(closeSubmitAction())
dispatch(closeSubmitAction());
dispatch(
onStartProgressAction(
dapp.name,
dapp.img,
'Status is an open source mobile DApp browser and messenger build for #Etherium',
TYPE_SUBMIT,
),
)
TYPE_SUBMIT
)
);
try {
const blockchain = await BlockchainSDK.getInstance()
const blockchain = await BlockchainSDK.getInstance();
const { tx, id } = await blockchain.DiscoverService.createDApp(sntValue, {
name: dapp.name,
url: dapp.url,
desc: dapp.desc,
description: dapp.desc,
category: dapp.category,
image: dapp.img,
})
dispatch(onReceiveTransactionInfoAction(id, tx))
dispatch(checkTransactionStatusAction(tx))
dateAdded: Date.now()
});
dispatch(onReceiveTransactionInfoAction(id, tx));
dispatch(checkTransactionStatusAction(tx));
} catch (e) {
dispatch(hideAction())
dispatch(showAlertAction(e.message))
dispatch(hideAction());
dispatch(showAlertAction(e.message));
}
}
}
};
};
export const switchToRatingAction = () => ({
type: SWITCH_TO_RATING,
paylaod: null,
})
paylaod: null
});
export const onInputSntValueAction = sntValue => ({
type: ON_INPUT_SNT_VALUE,
payload: sntValue,
})
payload: sntValue
});
const showSubmitAfterCheck = state => {
return Object.assign({}, state, {
@ -160,39 +161,39 @@ const showSubmitAfterCheck = state => {
imgControlMove: false,
imgControlX: 0,
imgControlY: 0,
sntValue: '0',
})
}
sntValue: '0'
});
};
const closeSubmit = state => {
return Object.assign({}, state, {
visible: false,
})
}
visible: false
});
};
const onInputName = (state, name) => {
return Object.assign({}, state, {
name,
})
}
name
});
};
const onInputDesc = (state, desc) => {
return Object.assign({}, state, {
desc,
})
}
desc
});
};
const onInputUrl = (state, url) => {
return Object.assign({}, state, {
url,
})
}
url
});
};
const onSelectCategory = (state, category) => {
return Object.assign({}, state, {
category,
})
}
category
});
};
const onImgRead = (state, imgBase64) => {
return Object.assign({}, state, {
@ -201,55 +202,55 @@ const onImgRead = (state, imgBase64) => {
imgControlZoom: 0,
imgControlMove: false,
imgControlX: 0,
imgControlY: 0,
})
}
imgControlY: 0
});
};
const onImgZoom = (state, zoom) => {
return Object.assign({}, state, {
imgControlZoom: zoom,
})
}
imgControlZoom: zoom
});
};
const onImgMoveControl = (state, move) => {
return Object.assign({}, state, {
imgControlMove: move,
})
}
imgControlMove: move
});
};
const onImgMove = (state, payload) => {
return Object.assign({}, state, {
imgControlX: payload.x,
imgControlY: payload.y,
})
}
imgControlY: payload.y
});
};
const onImgCancel = state => {
return Object.assign({}, state, {
img: '',
imgControl: false,
})
}
imgControl: false
});
};
const onImgDone = (state, imgBase64) => {
return Object.assign({}, state, {
img: imgBase64,
imgControl: false,
})
}
imgControl: false
});
};
const switchToRating = state => {
return Object.assign({}, state, {
visible_submit: false,
visible_rating: true,
})
}
visible_rating: true
});
};
const onInputSntValue = (state, sntValue) => {
return Object.assign({}, state, {
sntValue,
})
}
sntValue
});
};
const map = {
[SHOW_SUBMIT_AFTER_CHECK]: showSubmitAfterCheck,
@ -265,7 +266,7 @@ const map = {
[ON_IMG_CANCEL]: onImgCancel,
[ON_IMG_DONE]: onImgDone,
[SWITCH_TO_RATING]: switchToRating,
[ON_INPUT_SNT_VALUE]: onInputSntValue,
}
[ON_INPUT_SNT_VALUE]: onInputSntValue
};
export default reducerUtil(map, submitInitialState)
export default reducerUtil(map, submitInitialState);