CodeSandBox:https://codesandbox.io/s/funny-moon-krrsj
HeaderMenu.js
import {Component} from 'react';
import {
Menu,
Container,
Button,
Label,
Loader,
List,
Image,
Icon,
Dropdown
} from 'semantic-ui-react';
import Head from 'next/head';
import web3 from '../ethereum/web3';
import Constant from '../support/Constant';
import Config from '../support/Config';
import appDispatcher from '../core/AppDispatcher';
import contractManager from '../core/ContractManager';
class HeaderMenu extends Component {
constructor(props) {
super(props);
this.account = props.account;
this.contractManager = contractManager;
// console.log(contractManager);
this.transactionDispatcher = props.transactionDispatcher;
this.state = {address: "", balance: "", name: "",
avatarUrl: "", isLoading: true, isJoinButtonLoading: false,
isJoined: false, numPendingTx: 0};
this.reloadCount = 0;
}
clearAllData = () => {
window.localStorage.clear();
}
componentDidMount() {
if (this.account) {
this.getAccountInfo();
appDispatcher.register((payload) => {
if (payload.action == Constant.EVENT.ACCOUNT_BALANCE_UPDATED) {
this.setState({balance: this.account.balance});
} else if (payload.action == Constant.EVENT.ACCOUNT_INFO_UPDATED) {
this.setState({name: payload.profile.name, avatarUrl: payload.profile.avatarUrl, isJoined: payload.profile.isJoined});
}
});
this.transactionDispatcher.register((payload) => {
if (payload.action == Constant.EVENT.PENDING_TRANSACTION_UPDATED) {
this.setState({numPendingTx: payload.numPendingTx});
}
});
}
}
getAccountInfo = () => {
var address = this.account.getAddress();
if (address) {
this.setState({address: address, balance: this.account.balance, isLoading: false, isJoined: this.account.isJoined});
} else {
if (this.reloadCount == 1) {
this.setState({isLoading: false});
} else {
this.reloadCount++;
setTimeout(this.getAccountInfo, 800);
}
}
}
handleDropdownClicked = (event, data) => {
if (data.name == 'updateProfile') {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_UPDATE_PROFILE
});
} else if (data.name == 'logOutItem') {
this.clearAllData();
window.location.reload();
} else if (data.name == 'settingsItem') {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_SETTINGS_MODAL
})
}
else if (data.name == 'changeEthNetwork') {
if (data.networkid != Config.ENV.EthNetworkId) {
Config.ENV.EthNetworkId = data.networkid;
this.removeNetworkDependentData();
window.location.reload();
}
}
}
removeNetworkDependentData = () => {
this.account.storageManager.removeNetworkDependentData();
}
handleJoinClicked = () => {
var publicKeyBuffer = this.account.getPublicKeyBuffer();
this.contractManager.joinContract(publicKeyBuffer, (resultEvent) => {
if (resultEvent == Constant.EVENT.ON_REJECTED || resultEvent == Constant.EVENT.ON_ERROR) {
this.setState({isJoinButtonLoading: false});
} else if (resultEvent == Constant.EVENT.ON_RECEIPT) {
window.location.reload();
}
});
this.setState({isJoinButtonLoading: true});
}
handleImportPrivateKeyClicked = () => {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_PRIVATE_KEY_MODAL
});
}
render() {
var accountInfo = (<div></div>);
if (this.account) {
if (this.state.isLoading == false) {
if (this.state.address) {
var addressExplorerUrl = Config.ENV.ExplorerUrl + 'address/' + this.state.address;
var dropdownTrigger;
if (this.state.avatarUrl) {
dropdownTrigger = (
<span><Image src={this.state.avatarUrl} avatar/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
);
} else {
dropdownTrigger = (
<span><Icon name='user' size='large'/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
);
}
var networkItems = [];
for (var i=0;i<Config.NETWORK_LIST.length;i++) {
networkItems.push(
<Dropdown.Item key={'networkItem' + i} networkid={Config.NETWORK_LIST[i].id} name='changeEthNetwork' onClick={this.handleDropdownClicked}>
{Config.NETWORK_LIST[i].name}
</Dropdown.Item>
);
}
var memberInfo;
if (this.account.isJoined) {
memberInfo = (
<Dropdown item trigger={dropdownTrigger}>
<Dropdown.Menu>
<Dropdown.Item name='updateProfile' onClick={this.handleDropdownClicked}>
<Icon name='write'/>Update profile
</Dropdown.Item>
<Dropdown.Item name='settingsItem' onClick={this.handleDropdownClicked}>
<Icon name='settings'/>Settings
</Dropdown.Item>
<Dropdown.Item name='logOutItem' onClick={this.handleDropdownClicked}>
<Icon name='log out'/>Log out
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
} else {
memberInfo = (
<Button color='orange' onClick={this.handleJoinClicked}
loading={this.state.isJoinButtonLoading}
disabled={this.state.isJoinButtonLoading}>Join {Constant.APP_NAME}</Button>
);
}
var pendingTxItem;
if (this.state.numPendingTx > 0) {
pendingTxItem = (
<Label as='a' color='yellow' href={addressExplorerUrl} target='_blank'>
<Icon name='spinner' loading/>
{this.state.numPendingTx} pending tx
</Label>
);
}
accountInfo = (
<Menu.Menu position='right'>
<Menu.Item>
<Dropdown item text={Config.ENV.NetworkName}>
<Dropdown.Menu>
{networkItems}
</Dropdown.Menu>
</Dropdown>
</Menu.Item>
<Menu.Item>
<List>
<List.Item>
<a href={addressExplorerUrl} target='_blank'>
{this.state.address}
</a>
</List.Item>
<List.Item>
Balance: <Label as='a' href={addressExplorerUrl} target='_blank' color='orange'>{parseFloat(web3.utils.fromWei("" +this.state.balance, 'ether')).toFixed(8) + ' ETH' }</Label>
{pendingTxItem}
</List.Item>
</List>
</Menu.Item>
<Menu.Item>
{memberInfo}
</Menu.Item>
</Menu.Menu>
);
} else {
accountInfo = (
<Menu.Menu position='right'>
<Menu.Item>
<Button onClick={this.handleImportPrivateKeyClicked} color='blue'>Import private key</Button>
</Menu.Item>
</Menu.Menu>
);
}
} else {
accountInfo = (<Loader inverted active />);
}
}
return (
<Menu fixed='top' color='grey' inverted>
<Head>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.12/semantic.min.css"></link>
</Head>
<Container>
<Menu.Item>
<a href='/'><Image src='static/images/blockchat.png' height={55} /></a>
</Menu.Item>
{this.account ? accountInfo: (<div></div>)}
</Container>
</Menu>
);
}
}
export default HeaderMenu;
ContractManager.js
import web3 from '../ethereum/web3';
import compiledContract from '../ethereum/build/EtherChat.json';
import TransactionsManager from './TransactionManager';
import appDispatcher from './AppDispatcher';
import Config from '../support/Config';
import Constant from '../support/Constant';
import utils from '../support/Utils';
import crypto from 'crypto';
/**
* Responsible for interacting with the Ethereum smart contract
*/
export class ContractManager {
constructor(accountManager, storageManager) {
this.getContract();
this.accountManager = accountManager;
this.storageManager = storageManager;
this.transactionManager = new TransactionsManager(accountManager);
}
// Create a web3 contract object that represent the ethereum smart contract
getContract = async () => {
this.contract = await new web3.eth.Contract(JSON.parse(compiledContract.interface),
Config.ENV.ContractAddress);
appDispatcher.dispatch({
action: Constant.EVENT.CONTRACT_READY
})
}
// Get current account profile from EtherChat contract's storage
getProfile = async (address) => {
var result = await this.contract.methods.members(this.accountManager.getAddress()).call();
var profile = {};
if (result.isMember == 1) {
profile.isJoined = true;
profile.avatarUrl = utils.hexStringToAsciiString(result.avatarUrl);
profile.name = utils.hexStringToAsciiString(result.name);
this.storageManager.setJoinedStatus(true);
this.storageManager.setName(this.name);
this.storageManager.setAvatarUrl(this.avatarUrl);
appDispatcher.dispatch({
action: Constant.EVENT.ACCOUNT_INFO_UPDATED,
profile: profile
})
}
return profile;
}
getMemberInfo = async (address, relationship) => {
var memberInfo = await this.contract.methods.members(address).call();
if (memberInfo.isMember) {
var publicKey = '04' + memberInfo.publicKeyLeft.substr(2) + memberInfo.publicKeyRight.substr(2);
var name = utils.hexStringToAsciiString(memberInfo.name);
var avatarUrl = utils.hexStringToAsciiString(memberInfo.avatarUrl);
this.storageManager.updateContact(address, publicKey, name, avatarUrl, relationship);
}
}
getPastEvents = async (eventName, filters) => {
return await this.contract.getPastEvents(eventName, filters);
}
joinContract = async(publicKeyBuffer, callback) => {
var publicKeyLeft = '0x' + publicKeyBuffer.toString('hex', 0, 32);
var publicKeyRight = '0x' + publicKeyBuffer.toString('hex', 32, 64);
this.transactionManager.executeMethod(this.contract.methods.join(publicKeyLeft, publicKeyRight))
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_REJECTED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_REJECTED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
// joinContract = async (publicKeyBuffer, callback) => {
addContact = async (address, callback) => {
console.log(address);
var method = this.contract.methods.addContact(address);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
acceptContactRequest = async (address, callback) => {
var method = this.contract.methods.acceptContactRequest(address);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
updateProfile = async (name, avatarUrl, callback) => {
var nameHex = '0x' + Buffer.from(name, 'ascii').toString('hex');
var avatarUrlHex = '0x' + Buffer.from(avatarUrl, 'ascii').toString('hex');
var method = this.contract.methods.updateProfile(nameHex, avatarUrlHex);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
// A message will be encrypted locally before sending to the smart contract
sendMessage = async (toAddress, publicKey, message) => {
var publicKeyBuffer = Buffer.from(publicKey, 'hex');
var encryptedRaw = utils.encrypt(message, this.accountManager.computeSecret(publicKeyBuffer));
var encryptedMessage = '0x' + encryptedRaw.toString('hex');
var method = this.contract.methods.sendMessage(toAddress, encryptedMessage, utils.getEncryptAlgorithmInHex());
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
this.storageManager.addMyLocalMessage(encryptedMessage, toAddress, utils.getEncryptAlgorithm(), txHash);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
})
.on(Constant.EVENT.ON_REJECTED, (data) => {
// do nothing
})
.on(Constant.EVENT.ON_RECEIPT, (receipt, ) => {
this.storageManager.updateLocalMessage(toAddress, receipt.transactionHash, Constant.SENT_STATUS.SUCCESS);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
this.storageManager.updateLocalMessage(toAddress, txHash, Constant.SENT_STATUS.FAILED);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
});
}
}
export default ContractManager;
Here, I'm trying to call the function 'joinContract' {the function of ContractManager class} in HeaderMenu.js using the function handleJoinClicked(). And,boom... code is getting crash after clicking the join button. It's showing the error. This[coontractManager.joinContract] is not a function. Please help.
Please help.
Top comments (0)