Browse Source

Added AuthWrapperContainer, improved contract

develop
Ezerous 7 years ago
parent
commit
3440165fda
  1. 52
      contracts/Forum.sol
  2. 2
      src/App.js
  3. 48
      src/containers/AuthWrapperContainer.js
  4. 69
      src/containers/Menu.js
  5. 103
      src/containers/UsernameFormContainer.js
  6. 0
      src/css/App.css
  7. 0
      src/css/index.css
  8. 2
      src/index.js
  9. 7
      src/layouts/home/Home.js

52
contracts/Forum.sol

@ -1,56 +1,62 @@
pragma solidity ^0.4.21; pragma solidity ^0.4.23;
contract Forum { contract Forum {
//----------------------------------------USER---------------------------------------- //----------------------------------------AUTHENTICATION----------------------------------------
struct User { struct User {
string userName; // TODO: set an upper bound instead of arbitrary string string username; // TODO: set an upper bound instead of arbitrary string
// TODO: orbitDBAddress; // TODO: orbitDBAddress;
uint[] topicIDs; // IDs of the topics the user created uint[] topicIDs; // IDs of the topics the user created
uint[] postIDs; // IDs of the posts the user created uint[] postIDs; // IDs of the posts the user created
bool signedUp; // Helper variable for hasUserSignedUp()
} }
mapping (address => User) users; mapping (address => User) users;
mapping (string => address) userAddresses; mapping (string => address) userAddresses;
event UserSignedUp( event UserSignedUp(string username, address userAddress);
string userName event UsernameUpdated(string newName, string oldName,address userAddress);
);
function signUp(string userName) public returns (bool) { // Also allows user to update his name - TODO: his previous name will appear as taken function signUp(string username) public returns (bool) {
require(!isUserNameTaken(userName)); require (!hasUserSignedUp(msg.sender), "User has already signed up.");
users[msg.sender] = User(userName, new uint[](0), new uint[](0)); require(!isUserNameTaken(username), "Username is already taken.");
userAddresses[userName] = msg.sender; users[msg.sender] = User(username, new uint[](0), new uint[](0), true);
emit UserSignedUp(userName); userAddresses[username] = msg.sender;
emit UserSignedUp(username, msg.sender);
return true; return true;
} }
function login() public view returns (string) { function updateUsername(string newUsername) public returns (bool) {
require (hasUserSignedUp(msg.sender)); require (hasUserSignedUp(msg.sender), "User hasn't signed up yet.");
return users[msg.sender].userName; require(!isUserNameTaken(newUsername), "Username is already taken.");
string memory oldUsername = getUsername(msg.sender);
delete userAddresses[users[msg.sender].username];
users[msg.sender].username = newUsername;
userAddresses[newUsername] = msg.sender;
emit UsernameUpdated(newUsername, oldUsername, msg.sender);
return true;
} }
function getUsername(address userAddress) public view returns (string) { function getUsername(address userAddress) public view returns (string) {
return users[userAddress].userName; return users[userAddress].username;
} }
function getUserAddress(string userName) public view returns (address) { function getUserAddress(string username) public view returns (address) {
return userAddresses[userName]; return userAddresses[username];
} }
function hasUserSignedUp(address userAddress) public view returns (bool) { function hasUserSignedUp(address userAddress) public view returns (bool) {
if (bytes(getUsername(userAddress)).length!=0) return users[userAddress].signedUp;
return true;
return false;
} }
function isUserNameTaken(string userName) public view returns (bool) { function isUserNameTaken(string username) public view returns (bool) {
if (getUserAddress(userName)!=0) if (getUserAddress(username)!=address(0))
return true; return true;
return false; return false;
} }
//----------------------------------------TOPIC----------------------------------------
//----------------------------------------POSTING----------------------------------------
struct Topic { struct Topic {
uint topicID; uint topicID;
address author; address author;

2
src/App.js

@ -4,7 +4,7 @@ import React, { Component } from 'react'
import './css/oswald.css' import './css/oswald.css'
import './css/open-sans.css' import './css/open-sans.css'
import './css/pure-min.css' import './css/pure-min.css'
import './App.css' import './css/App.css'
class App extends Component { class App extends Component {
render() { render() {

48
src/containers/AuthWrapperContainer.js

@ -0,0 +1,48 @@
import { drizzleConnect } from 'drizzle-react'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
const contract = "Forum";
const method = "hasUserSignedUp";
class AuthWrapperContainer extends Component {
constructor(props, context) {
super(props);
this.contracts = context.drizzle.contracts;
this.dataKey = this.contracts[contract].methods[method].cacheCall(...[this.props.accounts[0]]);
}
render() {
// Contract is not yet intialized.
if(!this.props.contracts[contract].initialized)
return (null);
// If the cache key we received earlier isn't in the store yet; the initial value is still being fetched.
if(!(this.dataKey in this.props.contracts[contract][method]))
return (null);
let userHasSignedUp = this.props.contracts[contract][method][this.dataKey].value;
const authRender = this.props.authRender;
const guestRender = this.props.guestRender;
if (userHasSignedUp)
return(<div>{authRender}</div>);
return(<div>{guestRender}</div>);
}
}
AuthWrapperContainer.contextTypes = {
drizzle: PropTypes.object
};
const mapStateToProps = state => {
return {
accounts: state.accounts,
contracts: state.contracts,
}
};
export default drizzleConnect(AuthWrapperContainer, mapStateToProps)

69
src/containers/Menu.js

@ -1,69 +0,0 @@
import { drizzleConnect } from 'drizzle-react'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
const contract = "Forum";
const method = "hasUserSignedUp";
class Menu extends Component {
constructor(props, context) {
super(props);
this.contracts = context.drizzle.contracts;
// Get the contract ABI
const abi = this.contracts[contract].abi;
// Fetch initial value from chain and return cache key for reactive updates.
let methodArgs = this.props.methodArgs ? this.props.methodArgs : [];
this.dataKey = this.contracts[contract].methods[method].cacheCall(...methodArgs);
// Iterate over abi for correct function.
for (let i = 0; i < abi.length; i++) {
if (abi[i].name === this.props.method) {
this.fnABI = abi[i];
break
}
}
}
render() {
// Contract is not yet intialized.
if(!this.props.contracts[contract].initialized) {
return (
<span> </span>
)
}
// If the cache key we received earlier isn't in the store yet; the initial value is still being fetched.
if(!(this.dataKey in this.props.contracts[contract][method])) {
return (
<span> </span>
)
}
let displayData = this.props.contracts[contract][method][this.dataKey].value;
if (displayData) {
return(
<span>User has signed up!</span>
)
}
return(
<span>User doesn't exist!</span>
)
}
}
Menu.contextTypes = {
drizzle: PropTypes.object
};
const mapStateToProps = state => {
return {
contracts: state.contracts
}
};
export default drizzleConnect(Menu, mapStateToProps)

103
src/containers/UsernameFormContainer.js

@ -0,0 +1,103 @@
import { drizzleConnect } from 'drizzle-react'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import AuthWrapperContainer from './AuthWrapperContainer'
const contract = "Forum";
const signUpMethod = "signUp";
const updateUsernameMethod ="updateUsername";
class UsernameFormContainer extends Component {
constructor(props, context) {
super(props);
this.handleSignUp = this.handleSignUp.bind(this);
this.handleSignUpInputChange = this.handleSignUpInputChange.bind(this);
this.handleUsernameUpdate = this.handleUsernameUpdate.bind(this);
this.handleUpdateUsernameInputChange = this.handleUpdateUsernameInputChange.bind(this);
this.contracts = context.drizzle.contracts;
// Get the contract ABI
const abi = this.contracts[contract].abi;
this.inputs = {signUp:[], updateUsername:[]};
let initialState = {signUp:{}, updateUsername:{}};
// Iterate over abi for correct function.
for (let i = 0; i < abi.length; i++) {
if ((abi[i].name === signUpMethod)) {
this.inputs.signUp = abi[i].inputs;
for (let i = 0; i < this.inputs.signUp.length; i++) {
initialState.signUp[this.inputs.signUp[i].name] = '';
}
}
else if ((abi[i].name === updateUsernameMethod)) {
this.inputs.updateUsername = abi[i].inputs;
for (let i = 0; i < this.inputs.updateUsername.length; i++) {
initialState.updateUsername[this.inputs.updateUsername[i].name] = '';
}
}
}
console.dir(initialState);
this.state = initialState;
}
handleSignUp() {
this.contracts[contract].methods[signUpMethod].cacheSend(...Object.values(this.state.signUp));
}
handleUsernameUpdate() {
this.contracts[contract].methods[updateUsernameMethod].cacheSend(...Object.values(this.state.updateUsername));
}
handleSignUpInputChange(event) {
this.setState({ signUp: { ...this.state.signUp, [event.target.name]: event.target.value} });
}
handleUpdateUsernameInputChange(event) {
this.setState({ updateUsername: { ...this.state.updateUsername, [event.target.name]: event.target.value} });
}
render() {
let signUp = this.inputs.signUp[0].name; //username
let updateUsername = this.inputs.updateUsername[0].name; //newUsername
return (
<AuthWrapperContainer
authRender={
<form className="pure-form pure-form-stacked">
<input key={updateUsername} name={updateUsername} type="text" value={this.state.updateUsername.newUsername} placeholder="Username" onChange={this.handleUpdateUsernameInputChange} />
<button key="submit" className="pure-button" type="button" onClick={this.handleUsernameUpdate}>Update</button>
</form>
}
guestRender={
<form className="pure-form pure-form-stacked">
<input key={signUp} name={signUp} type="text" value={this.state.signUp.username} placeholder="Username" onChange={this.handleSignUpInputChange} />
<button key="submit" className="pure-button" type="button" onClick={this.handleSignUp}>Sign Up</button>
</form>
}
/>
)
}
}
UsernameFormContainer.contextTypes = {
drizzle: PropTypes.object
};
const mapStateToProps = state => {
return {
contracts: state.contracts
}
};
export default drizzleConnect(UsernameFormContainer, mapStateToProps)

0
src/App.css → src/css/App.css

0
src/index.css → src/css/index.css

2
src/index.js

@ -12,6 +12,8 @@ import { LoadingContainer } from 'drizzle-react-components'
import store from './store' import store from './store'
import drizzleOptions from './drizzleOptions' import drizzleOptions from './drizzleOptions'
import './css/index.css'
// Initialize react-router-redux. // Initialize react-router-redux.
const history = syncHistoryWithStore(browserHistory, store); const history = syncHistoryWithStore(browserHistory, store);

7
src/layouts/home/Home.js

@ -1,6 +1,6 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { AccountData, ContractData, ContractForm } from 'drizzle-react-components' import { AccountData, ContractData } from 'drizzle-react-components'
import Menu from './../../containers/Menu' import UsernameFormContainer from '../../containers/UsernameFormContainer'
class Home extends Component { class Home extends Component {
render() { render() {
@ -15,8 +15,7 @@ class Home extends Component {
<h2>Account</h2> <h2>Account</h2>
<AccountData accountIndex="0" units="ether" precision="3" /> <AccountData accountIndex="0" units="ether" precision="3" />
<p><strong>Username</strong>: <ContractData contract="Forum" method="getUsername" methodArgs={[this.props.accounts[0]]}/></p> <p><strong>Username</strong>: <ContractData contract="Forum" method="getUsername" methodArgs={[this.props.accounts[0]]}/></p>
<ContractForm contract="Forum" method="signUp" /> <UsernameFormContainer/>
<Menu method="hasUserSignedUp" methodArgs={[this.props.accounts[0]]} />
<br/><br/> <br/><br/>
</div> </div>
</div> </div>

Loading…
Cancel
Save