diff --git a/contracts/Forum.sol b/contracts/Forum.sol index 0fc71f3..693514b 100644 --- a/contracts/Forum.sol +++ b/contracts/Forum.sol @@ -2,10 +2,13 @@ pragma solidity ^0.4.23; contract Forum { - //----------------------------------------AUTHENTICATION---------------------------------------- + //----------------------------------------USER---------------------------------------- struct User { string username; // TODO: set an upper bound instead of arbitrary string - // TODO: orbitDBAddress; + string orbitMainDB; // TODO: set an upper bound instead of arbitrary string + string orbitTopicsDB; + string orbitPostsDB; + uint[] topicIDs; // IDs of the topics the user created uint[] postIDs; // IDs of the posts the user created bool signedUp; // Helper variable for hasUserSignedUp() @@ -17,10 +20,10 @@ contract Forum { event UserSignedUp(string username, address userAddress); event UsernameUpdated(string newName, string oldName,address userAddress); - function signUp(string username) public returns (bool) { + function signUp(string username, string orbitMainDB, string orbitTopicsDB, string orbitPostsDB) 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); + users[msg.sender] = User(username, orbitMainDB, orbitTopicsDB, orbitPostsDB, new uint[](0), new uint[](0), true); userAddresses[username] = msg.sender; emit UserSignedUp(username, msg.sender); return true; @@ -55,7 +58,17 @@ contract Forum { return false; } + function getOrbitMainDB(address userAddress) public view returns (string) { + return users[userAddress].orbitMainDB; + } + function getOrbitTopicsDB(address userAddress) public view returns (string) { + return users[userAddress].orbitTopicsDB; + } + + function getOrbitPostsDB(address userAddress) public view returns (string) { + return users[userAddress].orbitPostsDB; + } //----------------------------------------POSTING---------------------------------------- struct Topic { uint topicID; diff --git a/package.json b/package.json index 0de2376..f0f3b4c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "drizzle-react": "^1.1.1", "drizzle-react-components": "^1.1.0", "eth-block-tracker-es5": "^2.3.2", + "ipfs":"^0.28.2", + "orbit-db":"^0.19.7", "prop-types": "^15.6.1", "react": "^16.3.2", "react-dom": "^16.3.2", diff --git a/src/containers/LoadingContainer.js b/src/containers/LoadingContainer.js new file mode 100644 index 0000000..71720db --- /dev/null +++ b/src/containers/LoadingContainer.js @@ -0,0 +1,96 @@ +import { drizzleConnect } from 'drizzle-react' +import React, { Children, Component } from 'react' +import PropTypes from 'prop-types' + +import ipfs_logo from './../resources/ipfs_logo.png'; + +/* + * Create component. + */ + +class LoadingContainer extends Component { + render() { + if (this.props.web3.status === 'failed') + { + if (this.props.errorComp) { + return this.props.errorComp + } + + return( +
+
+
+

⚠️

+

This browser has no connection to the Ethereum network. Please use the Chrome/FireFox extension MetaMask, or dedicated Ethereum browsers Mist or Parity.

+
+
+
+ ) + } + + if (this.props.web3.status === 'initialized' && Object.keys(this.props.accounts).length === 0) + { + return( +
+
+
+

🦊

+

We can't find any Ethereum accounts! Please check and make sure Metamask or you browser are pointed at the correct network and your account is unlocked.

+
+
+
+ ) + } + + if (!this.props.orbitDB.initialized) + { + return( +
+
+
+ ipfs_logo +

Initializing IPFS...

+
+
+
+ ) + } + + if (this.props.drizzleStatus.initialized) + return Children.only(this.props.children); + + if (this.props.loadingComp) + return this.props.loadingComp; + + + return( +
+
+
+

⚙️

+

Loading dapp...

+
+
+
+ ) + } +} + +LoadingContainer.contextTypes = { + drizzle: PropTypes.object +}; + +/* + * Export connected component. + */ + +const mapStateToProps = state => { + return { + accounts: state.accounts, + drizzleStatus: state.drizzleStatus, + web3: state.web3, + orbitDB: state.orbitDB + } +}; + +export default drizzleConnect(LoadingContainer, mapStateToProps) \ No newline at end of file diff --git a/src/containers/UsernameFormContainer.js b/src/containers/UsernameFormContainer.js index 9b9d59d..bf5cdfa 100644 --- a/src/containers/UsernameFormContainer.js +++ b/src/containers/UsernameFormContainer.js @@ -1,5 +1,8 @@ import { drizzleConnect } from 'drizzle-react' import React, { Component } from 'react' + +import { createDatabases } from './../util/orbit' + import PropTypes from 'prop-types' const contract = "Forum"; @@ -17,12 +20,15 @@ class UsernameFormContainer extends Component { this.state = {usernameInput:''}; } - handleSubmit() { - this.setState({usernameInput:''}); + async handleSubmit() { if(this.props.user.hasSignedUp) this.contracts[contract].methods[updateUsernameMethod].cacheSend(...[this.state.usernameInput]); else - this.contracts[contract].methods[signUpMethod].cacheSend(...[this.state.usernameInput]); + { + const orbitdb = await createDatabases(); + this.contracts[contract].methods[signUpMethod].cacheSend(...[this.state.usernameInput, orbitdb.mainDB, orbitdb.topicsDB, orbitdb.postsDB]); + } + } diff --git a/src/index.js b/src/index.js index 7877e8d..92cb377 100644 --- a/src/index.js +++ b/src/index.js @@ -7,10 +7,10 @@ import { DrizzleProvider } from 'drizzle-react' // Layouts import App from './App' import HomeContainer from './layouts/home/HomeContainer' -import { LoadingContainer } from 'drizzle-react-components' +import LoadingContainer from './containers/LoadingContainer' import store from './store' -import drizzleOptions from './drizzleOptions' +import drizzleOptions from './util/drizzleOptions' import './css/index.css' diff --git a/src/reducer.js b/src/reducer.js index d49b26d..bf815d1 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -2,10 +2,12 @@ import { combineReducers } from 'redux' import { routerReducer } from 'react-router-redux' import { drizzleReducers } from 'drizzle' import userReducer from "./userReducer"; +import orbitReducer from "./util/orbitReducer"; const reducer = combineReducers({ routing: routerReducer, user: userReducer, + orbitDB: orbitReducer, ...drizzleReducers }); diff --git a/src/resources/ipfs_logo.png b/src/resources/ipfs_logo.png new file mode 100644 index 0000000..2aa68e8 Binary files /dev/null and b/src/resources/ipfs_logo.png differ diff --git a/src/store.js b/src/store.js index bc333fe..e06624c 100644 --- a/src/store.js +++ b/src/store.js @@ -5,7 +5,7 @@ import reducer from './reducer' import rootSaga from './rootSaga' import createSagaMiddleware from 'redux-saga' import { generateContractsInitialState } from 'drizzle' -import drizzleOptions from './drizzleOptions' +import drizzleOptions from './util/drizzleOptions' // Redux DevTools const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; diff --git a/src/drizzleOptions.js b/src/util/drizzleOptions.js similarity index 79% rename from src/drizzleOptions.js rename to src/util/drizzleOptions.js index 88e4204..249fbc7 100644 --- a/src/drizzleOptions.js +++ b/src/util/drizzleOptions.js @@ -1,4 +1,4 @@ -import Forum from './build/contracts/Forum.json' +import Forum from './../build/contracts/Forum.json' const drizzleOptions = { web3: { diff --git a/src/util/orbit.js b/src/util/orbit.js new file mode 100644 index 0000000..9d9d571 --- /dev/null +++ b/src/util/orbit.js @@ -0,0 +1,32 @@ +import IPFS from 'ipfs'; +import OrbitDB from 'orbit-db'; + +import store from './../store'; + +// OrbitDB uses Pubsub which is an experimental feature +// and need to be turned on manually. +// Note that these options need to be passed to IPFS in +// all examples in this document even if not specified so. +const ipfsOptions = { + EXPERIMENTAL: { + pubsub: true + }, +}; + +// Create IPFS instance +const ipfs = new IPFS(ipfsOptions); + +ipfs.on('ready', async () => { + store.dispatch({type: "IPFS_READY"}); +}); + + +async function createDatabases() { + const orbitdb = new OrbitDB(ipfs); + const topicsDB = await orbitdb.keyvalue('topics'); + const postsDB = await orbitdb.keyvalue('posts'); + console.log("OrbitDBs created successfully!"); + return {mainDB: orbitdb.id, topicsDB: topicsDB.address.toString(), postsDB: postsDB.address.toString()}; //TODO: regex in the latter two +} + +export { createDatabases } \ No newline at end of file diff --git a/src/util/orbitReducer.js b/src/util/orbitReducer.js new file mode 100644 index 0000000..dd6bbbb --- /dev/null +++ b/src/util/orbitReducer.js @@ -0,0 +1,21 @@ +const initialState = { + initialized: false, + databasesReady: false +}; + +const orbitReducer = (state = initialState, action) => { + switch (action.type) { + case 'IPFS_READY': + return { + initialized: true + }; + case 'DATABASES_CREATED': + return { + databasesReady: true + }; + default: + return state + } +}; + +export default orbitReducer