diff --git a/contracts/Forum.sol b/contracts/Forum.sol index eef2e82..0fc71f3 100644 --- a/contracts/Forum.sol +++ b/contracts/Forum.sol @@ -1,56 +1,62 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.23; contract Forum { - //----------------------------------------USER---------------------------------------- + //----------------------------------------AUTHENTICATION---------------------------------------- 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; uint[] topicIDs; // IDs of the topics the user created uint[] postIDs; // IDs of the posts the user created + bool signedUp; // Helper variable for hasUserSignedUp() } mapping (address => User) users; mapping (string => address) userAddresses; - event UserSignedUp( - string userName - ); + event UserSignedUp(string username, address userAddress); + 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 - require(!isUserNameTaken(userName)); - users[msg.sender] = User(userName, new uint[](0), new uint[](0)); - userAddresses[userName] = msg.sender; - emit UserSignedUp(userName); + function signUp(string username) public returns (bool) { + require (!hasUserSignedUp(msg.sender), "User has already signed up."); + require(!isUserNameTaken(username), "Username is already taken."); + users[msg.sender] = User(username, new uint[](0), new uint[](0), true); + userAddresses[username] = msg.sender; + emit UserSignedUp(username, msg.sender); return true; } - function login() public view returns (string) { - require (hasUserSignedUp(msg.sender)); - return users[msg.sender].userName; + function updateUsername(string newUsername) public returns (bool) { + require (hasUserSignedUp(msg.sender), "User hasn't signed up yet."); + 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) { - return users[userAddress].userName; + return users[userAddress].username; } - function getUserAddress(string userName) public view returns (address) { - return userAddresses[userName]; + function getUserAddress(string username) public view returns (address) { + return userAddresses[username]; } function hasUserSignedUp(address userAddress) public view returns (bool) { - if (bytes(getUsername(userAddress)).length!=0) - return true; - return false; + return users[userAddress].signedUp; } - function isUserNameTaken(string userName) public view returns (bool) { - if (getUserAddress(userName)!=0) + function isUserNameTaken(string username) public view returns (bool) { + if (getUserAddress(username)!=address(0)) return true; return false; } - //----------------------------------------TOPIC---------------------------------------- + + //----------------------------------------POSTING---------------------------------------- struct Topic { uint topicID; address author; diff --git a/src/App.js b/src/App.js index 45d9e02..f51c726 100644 --- a/src/App.js +++ b/src/App.js @@ -4,7 +4,7 @@ import React, { Component } from 'react' import './css/oswald.css' import './css/open-sans.css' import './css/pure-min.css' -import './App.css' +import './css/App.css' class App extends Component { render() { diff --git a/src/containers/AuthWrapperContainer.js b/src/containers/AuthWrapperContainer.js new file mode 100644 index 0000000..ce2ad11 --- /dev/null +++ b/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(
{authRender}
); + + return(
{guestRender}
); + } +} + +AuthWrapperContainer.contextTypes = { + drizzle: PropTypes.object +}; + +const mapStateToProps = state => { + return { + accounts: state.accounts, + contracts: state.contracts, + } +}; + +export default drizzleConnect(AuthWrapperContainer, mapStateToProps) \ No newline at end of file diff --git a/src/containers/Menu.js b/src/containers/Menu.js deleted file mode 100644 index e4be872..0000000 --- a/src/containers/Menu.js +++ /dev/null @@ -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 ( - - ) - } - - // 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 ( - - ) - } - - let displayData = this.props.contracts[contract][method][this.dataKey].value; - - if (displayData) { - return( - User has signed up! - ) - } - - return( - User doesn't exist! - ) - } -} - -Menu.contextTypes = { - drizzle: PropTypes.object -}; - -const mapStateToProps = state => { - return { - contracts: state.contracts - } -}; - -export default drizzleConnect(Menu, mapStateToProps) \ No newline at end of file diff --git a/src/containers/UsernameFormContainer.js b/src/containers/UsernameFormContainer.js new file mode 100644 index 0000000..469a47f --- /dev/null +++ b/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 ( + + + + + } + guestRender={ +
+ + +
+ } + /> + + + ) + } +} + +UsernameFormContainer.contextTypes = { + drizzle: PropTypes.object +}; + +const mapStateToProps = state => { + return { + contracts: state.contracts + } +}; + +export default drizzleConnect(UsernameFormContainer, mapStateToProps) \ No newline at end of file diff --git a/src/App.css b/src/css/App.css similarity index 100% rename from src/App.css rename to src/css/App.css diff --git a/src/index.css b/src/css/index.css similarity index 100% rename from src/index.css rename to src/css/index.css diff --git a/src/index.js b/src/index.js index 2ffbcc6..7877e8d 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,8 @@ import { LoadingContainer } from 'drizzle-react-components' import store from './store' import drizzleOptions from './drizzleOptions' +import './css/index.css' + // Initialize react-router-redux. const history = syncHistoryWithStore(browserHistory, store); diff --git a/src/layouts/home/Home.js b/src/layouts/home/Home.js index eade9af..2b68cc5 100644 --- a/src/layouts/home/Home.js +++ b/src/layouts/home/Home.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' -import { AccountData, ContractData, ContractForm } from 'drizzle-react-components' -import Menu from './../../containers/Menu' +import { AccountData, ContractData } from 'drizzle-react-components' +import UsernameFormContainer from '../../containers/UsernameFormContainer' class Home extends Component { render() { @@ -15,8 +15,7 @@ class Home extends Component {

Account

Username:

- - +