From dadc615e3c00062f279adc5b284dd32733417578 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Thu, 21 Mar 2019 19:22:38 +0200 Subject: [PATCH] Up --- app/src/components/Post.js | 2 +- app/src/components/Topic.js | 97 +++++++++------------ app/src/config/drizzleOptions.js | 6 +- app/src/containers/BoardContainer.js | 2 +- app/src/containers/LoadingContainer.js | 107 ++++++++++++++++++++++++ app/src/index.js | 9 +- app/src/redux/actions/orbitActions.js | 17 +++- app/src/redux/reducers/orbitReducer.js | 28 +++++-- app/src/redux/reducers/userReducer.js | 2 +- app/src/redux/sagas/drizzleUtilsSaga.js | 10 +-- app/src/redux/sagas/eventSaga.js | 22 +++++ app/src/redux/sagas/orbitSaga.js | 44 ++++++++-- app/src/redux/sagas/rootSaga.js | 2 + app/src/redux/sagas/transactionsSaga.js | 4 +- app/src/redux/sagas/userSaga.js | 6 +- app/src/utils/orbitUtils.js | 37 ++++++-- contracts/Forum.sol | 85 +++++++++++++------ contracts/Posting.sol | 6 +- migrations/2_deploy_contracts.js | 5 +- 19 files changed, 362 insertions(+), 129 deletions(-) create mode 100644 app/src/containers/LoadingContainer.js create mode 100644 app/src/redux/sagas/eventSaga.js diff --git a/app/src/components/Post.js b/app/src/components/Post.js index be886e4..abd8b39 100644 --- a/app/src/components/Post.js +++ b/app/src/components/Post.js @@ -82,7 +82,7 @@ class Post extends Component { orbitPostData = orbitDB.postsDB.get(postID); } else { const fullAddress = `/orbitdb/${postData.value[0]}/posts`; - const store = await orbitDB.orbitdb.open(fullAddress, {type: 'keyvalue'}); + const store = await orbitDB.orbitdb.keyvalue(fullAddress); await store.load(); const localOrbitData = store.get(postID); diff --git a/app/src/components/Topic.js b/app/src/components/Topic.js index 48511a5..cf2f0e6 100644 --- a/app/src/components/Topic.js +++ b/app/src/components/Topic.js @@ -9,71 +9,28 @@ import { Card } from 'semantic-ui-react'; import TimeAgo from 'react-timeago'; import epochTimeConverter from '../helpers/EpochTimeConverter'; +import { addPeerDatabase } from '../redux/actions/orbitActions'; class Topic extends Component { constructor(props) { super(props); - - this.fetchSubject = this.fetchSubject.bind(this); - this.state = { - topicSubject: null, - topicSubjectFetchStatus: 'pending' + askedForReplication: false, + fetchedSubject: false }; } - componentDidMount() { - this.fetchSubject(this.props.topicID); - } - componentDidUpdate() { - this.fetchSubject(this.props.topicID); - } - - async fetchSubject(topicID) { - const { topicSubjectFetchStatus } = this.state; - const { topicData, user, orbitDB } = this.props; - - if (topicData !== null - && topicSubjectFetchStatus === 'pending' - && orbitDB.ipfsInitialized - && orbitDB.orbitdb) { - - let topicSubject; - - if (topicData.value[1] === user.address) { - const orbitData = orbitDB.topicsDB.get(topicID); - if(orbitData) - topicSubject = orbitData.subject; - } else { - const fullAddress = `/orbitdb/${topicData.value[0]}/topics`; - const store = await orbitDB.orbitdb.open(fullAddress, {type: 'keyvalue'}); - await store.load(); - - const localOrbitData = store.get(topicID); - if (localOrbitData) - topicSubject = localOrbitData.subject; - - store.events.on('replicate', () => { - console.log("Initiated OrbitDB data replication."); - }); - - store.events.on('replicated', () => { - console.log("OrbitDB data replicated successfully."); - topicSubject = store.get(topicID).subject; - }); - } - - this.setState({ - topicSubject, - topicSubjectFetchStatus: 'fetched' - }); + const { dispatch, topicData, topicSubject, orbitDB } = this.props; + const { askedForReplication } = this.state; + if(!askedForReplication && orbitDB.ipfsInitialized && orbitDB.orbitdb && dispatch && !topicSubject && topicData) { + this.setState({ askedForReplication: true }); + dispatch(addPeerDatabase(`/orbitdb/${topicData.value[0]}/topics`)); } } render() { - const { topicSubject } = this.state; - const { history, topicID, topicData } = this.props; + const { history, topicID, topicData, topicSubject } = this.props; return ( ({ - user: state.user, - orbitDB: state.orbit -}); +function getTopicSubject(state, props){ + const { user, orbit } = state; + if (orbit.ipfsInitialized && orbit.orbitdb) { + const { topicData, topicID } = props; + if (topicData){ + if(user && topicData.value[1] === user.address) { + const orbitData = orbit.topicsDB.get(topicID); + if(orbitData && orbitData.subject) + return orbitData.subject; + } + const db = orbit.replicatedDatabases.find(db => db.fullAddress === `/orbitdb/${topicData.value[0]}/topics`); + if(db && db.ready && db.store){ + const localOrbitData = db.store.get(topicID); + if (localOrbitData){ + return localOrbitData.subject; + } + } + } + } + return null; +} + + + +function mapStateToProps(state, ownProps) { + return { + user: state.user, + orbitDB: state.orbit, + topicSubject: getTopicSubject(state, ownProps) + } +} + export default withRouter(connect(mapStateToProps)(Topic)); diff --git a/app/src/config/drizzleOptions.js b/app/src/config/drizzleOptions.js index fa0b853..ce31144 100644 --- a/app/src/config/drizzleOptions.js +++ b/app/src/config/drizzleOptions.js @@ -1,5 +1,4 @@ import Forum from '../contracts/Forum.json'; -import Posting from '../contracts/Posting.json'; const drizzleOptions = { web3: { @@ -8,10 +7,9 @@ const drizzleOptions = { url: 'ws://127.0.0.1:9545' } }, - contracts: [Forum, Posting], + contracts: [Forum], events: { - Forum: ['UserSignedUp', 'UsernameUpdated'], - Posting: ['TopicCreated', 'PostCreated'] + Forum: ['UserSignedUp', 'UsernameUpdated', 'TopicCreated', 'PostCreated'] }, polls: { accounts: 2000, diff --git a/app/src/containers/BoardContainer.js b/app/src/containers/BoardContainer.js index 8486ce0..49b1395 100644 --- a/app/src/containers/BoardContainer.js +++ b/app/src/containers/BoardContainer.js @@ -11,7 +11,7 @@ import FloatingButton from '../components/FloatingButton'; /* import { showProgressBar, hideProgressBar } from '../redux/actions/userInterfaceActions'; */ -const contract = 'Posting'; +const contract = 'Forum'; const getNumberOfTopicsMethod = 'getNumberOfTopics'; class BoardContainer extends Component { diff --git a/app/src/containers/LoadingContainer.js b/app/src/containers/LoadingContainer.js new file mode 100644 index 0000000..090ebbe --- /dev/null +++ b/app/src/containers/LoadingContainer.js @@ -0,0 +1,107 @@ +import React, { Children, Component } from 'react'; +import { connect } from 'react-redux'; +import ipfs_logo from '../assets/images/ipfs_logo.png'; + + +//TODO: Add OrbitDB Loading thingy +class LoadingContainer extends Component { + render() { + if (this.props.web3.status === 'failed' || !this.props.web3.networkId) { + + //TODO: wtf is this + if (this.props.errorComp) + return this.props.errorComp; + + return ( +
+
+
+

🦊

+

This browser has no connection to the Ethereum network.

+

+ Please make sure that: +

    +
  • You use MetaMask or a dedicated Ethereum browser (e.g. Mist or Parity)
  • +
  • They are pointed to the correct network
  • +
  • Your account is unlocked and the app has the rights to access it
  • +
+

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

🦊

+

We can't find any Ethereum accounts!.

+
+
+
+ ) + } + + if (!this.props.contractInitialized) { + return( +
+
+
+

âš™

+

Initializing contracts...

+

If this takes too long please make sure they are deployed to the network.

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

Initializing IPFS...

+
+
+
+ ) + } + + //TODO: wtf is this + if (this.props.drizzleStatus.initialized) + return Children.only(this.props.children); + + //TODO: wtf is this + if (this.props.loadingComp) + return this.props.loadingComp; + + return( +
+
+
+

âš™

+

Loading dapp...

+
+
+
+ ) + } +} + +const mapStateToProps = state => { + return { + accounts: state.accounts, + drizzleStatus: state.drizzleStatus, + web3: state.web3, + ipfsInitialized: state.orbit.ipfsInitialized, + contractInitialized: state.contracts.Forum.initialized + } +}; + +export default connect(mapStateToProps)(LoadingContainer); + diff --git a/app/src/index.js b/app/src/index.js index cb205fe..94b9b91 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -11,6 +11,7 @@ import * as serviceWorker from './utils/serviceWorker'; import './assets/css/index.css'; import drizzleOptions from './config/drizzleOptions'; +import LoadingContainer from './containers/LoadingContainer'; initIPFS(); @@ -20,9 +21,11 @@ export { drizzle }; render( - - {routes} - + + + {routes} + + , document.getElementById('root'), ); diff --git a/app/src/redux/actions/orbitActions.js b/app/src/redux/actions/orbitActions.js index 2e2005f..20f9a01 100644 --- a/app/src/redux/actions/orbitActions.js +++ b/app/src/redux/actions/orbitActions.js @@ -2,6 +2,9 @@ const IPFS_INITIALIZED = 'IPFS_INITIALIZED'; const DATABASES_CREATED = 'DATABASES_CREATED'; const DATABASES_LOADED = 'DATABASES_LOADED'; const DATABASES_NOT_READY = 'DATABASES_NOT_READY'; +const ADD_PEER_DATABASE = 'ADD_PEER_DATABASE'; +const OPENING_PEER_DATABASE = 'OPENING_PEER_DATABASE'; +const PEER_DATABASE_LOADED = 'PEER_DATABASE_LOADED'; const UPDATE_PEERS = 'UPDATE_PEERS'; function updateDatabases(type, orbitdb, topicsDB, postsDB) { @@ -14,9 +17,21 @@ function updateDatabases(type, orbitdb, topicsDB, postsDB) { }; } +function addPeerDatabase(fullAddress) { + return { + type: ADD_PEER_DATABASE, + fullAddress + }; +} + export { DATABASES_CREATED, DATABASES_LOADED, DATABASES_NOT_READY, IPFS_INITIALIZED, UPDATE_PEERS, - updateDatabases }; + ADD_PEER_DATABASE, + OPENING_PEER_DATABASE, + PEER_DATABASE_LOADED, + addPeerDatabase, + updateDatabases +}; diff --git a/app/src/redux/reducers/orbitReducer.js b/app/src/redux/reducers/orbitReducer.js index 8374047..7495a99 100644 --- a/app/src/redux/reducers/orbitReducer.js +++ b/app/src/redux/reducers/orbitReducer.js @@ -1,8 +1,8 @@ import { DATABASES_CREATED, DATABASES_LOADED, - DATABASES_NOT_READY, - IPFS_INITIALIZED, UPDATE_PEERS + DATABASES_NOT_READY, OPENING_PEER_DATABASE, + IPFS_INITIALIZED, UPDATE_PEERS, PEER_DATABASE_LOADED } from '../actions/orbitActions'; const initialState = { @@ -12,8 +12,8 @@ const initialState = { orbitdb: null, topicsDB: null, postsDB: null, - topicsDBPeers: [], - postsDBPeers: [], + pubsubPeers: {topicsDBPeers:[], postsDBPeers:[]}, + replicatedDatabases: [], id: null }; @@ -52,11 +52,27 @@ const orbitReducer = (state = initialState, action) => { postsDB: null, id: null }; + case OPENING_PEER_DATABASE: + if(state.replicatedDatabases.find(db => db.fullAddress === action.fullAddress)) + return state; + return { + ...state, + replicatedDatabases:[...state.replicatedDatabases, + {fullAddress: action.fullAddress, ready: false, store: null}] + }; + case PEER_DATABASE_LOADED: + return { + ...state, + replicatedDatabases: [...state.replicatedDatabases.map((db) => { + if (db.fullAddress !== action.fullAddress) + return db; // This isn't the item we care about - keep it as-is + return { ...db, ready: true, store: action.store} // Otherwise return an updated value + })] + }; case UPDATE_PEERS: return { ...state, - topicsDBPeers: action.topicsDBPeers, - postsDBPeers: action.postsDBPeers + pubsubPeers: {topicsDBPeers:action.topicsDBPeers, postsDBPeers:action.postsDBPeers} }; default: return state; diff --git a/app/src/redux/reducers/userReducer.js b/app/src/redux/reducers/userReducer.js index 6efe35c..0e855d8 100644 --- a/app/src/redux/reducers/userReducer.js +++ b/app/src/redux/reducers/userReducer.js @@ -2,7 +2,7 @@ const initialState = { username: '', address: '0x0', avatarUrl: '', - hasSignedUp: null + hasSignedUp: false }; const userReducer = (state = initialState, action) => { diff --git a/app/src/redux/sagas/drizzleUtilsSaga.js b/app/src/redux/sagas/drizzleUtilsSaga.js index ee8b38c..e1988f5 100644 --- a/app/src/redux/sagas/drizzleUtilsSaga.js +++ b/app/src/redux/sagas/drizzleUtilsSaga.js @@ -5,20 +5,20 @@ import Forum from '../../contracts/Forum'; import { DRIZZLE_UTILS_SAGA_INITIALIZED } from '../actions/drizzleUtilsActions'; const accounts = state => state.accounts; -let initFlag; let web3; let - contract; +let initFlag, web3, forumContract; function* init() { if (!initFlag) { web3 = yield call(getWeb3); - contract = yield call(getContractInstance, { + forumContract = yield call(getContractInstance, { web3, artifact: Forum }); initFlag = true; yield put({ type: DRIZZLE_UTILS_SAGA_INITIALIZED, ...[] }); - } else console.warn('Attempted to reinitialize drizzleUtilsSaga!'); + } + else console.warn('Attempted to reinitialize drizzleUtilsSaga!'); } // If the method below proves to be problematic/ineffective (i.e. getting current account @@ -32,6 +32,6 @@ function* drizzleUtilsSaga() { yield takeLatest('DRIZZLE_INITIALIZED', init); } -export { web3, contract, getCurrentAccount }; +export { web3, forumContract, getCurrentAccount }; export default drizzleUtilsSaga; diff --git a/app/src/redux/sagas/eventSaga.js b/app/src/redux/sagas/eventSaga.js new file mode 100644 index 0000000..f9fd9ef --- /dev/null +++ b/app/src/redux/sagas/eventSaga.js @@ -0,0 +1,22 @@ +import { put, takeEvery } from 'redux-saga/effects'; + +const EVENT_FIRED = 'EVENT_FIRED'; // This is fired internally by drizzle +const CONTRACT_EVENT_FIRED = 'CONTRACT_EVENT_FIRED'; + +let eventSet = new Set(); + +// Entire purpose of this saga is to bypass a strange bug where EVENT_FIRED is called +// multiple times for the same event +function* sanitizeEvent(action) { + const size = eventSet.size; + eventSet.add(action.event.transactionHash + action.name); + if(eventSet.size>size) + yield put({ ...action, type: CONTRACT_EVENT_FIRED }); +} + +function* eventSaga() { + yield takeEvery(EVENT_FIRED, sanitizeEvent); +} + +export default eventSaga; +export { CONTRACT_EVENT_FIRED } diff --git a/app/src/redux/sagas/orbitSaga.js b/app/src/redux/sagas/orbitSaga.js index 3bd9f92..34c8d60 100644 --- a/app/src/redux/sagas/orbitSaga.js +++ b/app/src/redux/sagas/orbitSaga.js @@ -1,9 +1,14 @@ import { all, call, put, select, take, takeEvery, takeLatest } from 'redux-saga/effects'; import isEqual from 'lodash.isequal'; -import { contract, getCurrentAccount } from './drizzleUtilsSaga'; -import { loadDatabases } from '../../utils/orbitUtils'; +import { forumContract, getCurrentAccount } from './drizzleUtilsSaga'; +import { loadDatabases, orbitSagaOpen } from '../../utils/orbitUtils'; import { DRIZZLE_UTILS_SAGA_INITIALIZED } from '../actions/drizzleUtilsActions'; -import { DATABASES_NOT_READY, IPFS_INITIALIZED, UPDATE_PEERS } from '../actions/orbitActions'; +import { + ADD_PEER_DATABASE, PEER_DATABASE_LOADED, + DATABASES_NOT_READY, + IPFS_INITIALIZED, OPENING_PEER_DATABASE, + UPDATE_PEERS +} from '../actions/orbitActions'; let latestAccount; @@ -13,19 +18,19 @@ function* getOrbitDBInfo() { }); const account = yield call(getCurrentAccount); if (account !== latestAccount) { - const txObj1 = yield call(contract.methods.hasUserSignedUp, + const txObj1 = yield call(forumContract.methods.hasUserSignedUp, ...[account]); try { const callResult = yield call(txObj1.call, { address: account }); if (callResult) { - const txObj2 = yield call(contract.methods.getOrbitIdentityInfo, + const txObj2 = yield call(forumContract.methods.getOrbitIdentityInfo, ...[account]); const orbitIdentityInfo = yield call(txObj2.call, { address: account }); - const txObj3 = yield call(contract.methods.getOrbitDBInfo, + const txObj3 = yield call(forumContract.methods.getOrbitDBInfo, ...[account]); const orbitDBInfo = yield call(txObj3.call, { address: account @@ -50,6 +55,28 @@ function* getOrbitDBInfo() { } } +let peerOrbitAddresses = new Set(); + +function* addPeerDatabase(action) { + const fullAddress = action.fullAddress; + const size = peerOrbitAddresses.size; + peerOrbitAddresses.add(fullAddress); + + if(peerOrbitAddresses.size>size){ + const { orbitdb } = yield select(state => state.orbit); + if(orbitdb){ + yield put.resolve({ + type: OPENING_PEER_DATABASE, fullAddress + }); + const store = yield call(orbitSagaOpen, orbitdb, fullAddress); + yield put({ + type: PEER_DATABASE_LOADED, fullAddress, store: store + }); + } + } +} + +//Keeps track of connected pubsub peers in Redux store function* updatePeersState() { const orbit = yield select(state => state.orbit); if(orbit.ready){ @@ -57,8 +84,8 @@ function* updatePeersState() { const postsDBAddress = orbit.postsDB.address.toString(); const topicsDBPeers = yield call(orbit.ipfs.pubsub.peers, topicsDBAddress); const postsDBPeers = yield call(orbit.ipfs.pubsub.peers, postsDBAddress); - if(!isEqual(topicsDBPeers.sort(), orbit.topicsDBPeers.sort()) || - !isEqual(postsDBPeers.sort(), orbit.postsDBPeers.sort())){ + if(!isEqual(topicsDBPeers.sort(), orbit.pubsubPeers.topicsDBPeers.sort()) || + !isEqual(postsDBPeers.sort(), orbit.pubsubPeers.postsDBPeers.sort())){ yield put({ type: UPDATE_PEERS, topicsDBPeers, postsDBPeers }); @@ -72,6 +99,7 @@ function* orbitSaga() { take(IPFS_INITIALIZED) ]); yield takeLatest('ACCOUNT_CHANGED', getOrbitDBInfo); + yield takeEvery(ADD_PEER_DATABASE, addPeerDatabase); yield takeEvery('ACCOUNTS_FETCHED', updatePeersState); } diff --git a/app/src/redux/sagas/rootSaga.js b/app/src/redux/sagas/rootSaga.js index c6f276e..2a9ecac 100644 --- a/app/src/redux/sagas/rootSaga.js +++ b/app/src/redux/sagas/rootSaga.js @@ -4,6 +4,7 @@ import drizzleUtilsSaga from './drizzleUtilsSaga'; import userSaga from './userSaga'; import orbitSaga from './orbitSaga'; import transactionsSaga from './transactionsSaga'; +import eventSaga from './eventSaga'; export default function* root() { const sagas = [ @@ -11,6 +12,7 @@ export default function* root() { drizzleUtilsSaga, orbitSaga, userSaga, + eventSaga, transactionsSaga]; yield all( sagas.map(saga => fork(saga)), diff --git a/app/src/redux/sagas/transactionsSaga.js b/app/src/redux/sagas/transactionsSaga.js index 22fa0cc..990c2d7 100644 --- a/app/src/redux/sagas/transactionsSaga.js +++ b/app/src/redux/sagas/transactionsSaga.js @@ -3,6 +3,7 @@ import { call, select, take, takeEvery } from 'redux-saga/effects'; import { drizzle } from '../../index'; import { orbitSagaPut } from '../../utils/orbitUtils'; import { DRIZZLE_UTILS_SAGA_INITIALIZED } from '../actions/drizzleUtilsActions'; +import { CONTRACT_EVENT_FIRED } from './eventSaga'; const transactionsHistory = Object.create(null); @@ -19,7 +20,6 @@ function* initTransaction(action) { function* handleEvent(action) { const transactionStack = yield select(state => state.transactionStack); const dataKey = transactionStack.indexOf(action.event.transactionHash); - switch (action.event.event) { case 'TopicCreated': if (dataKey !== -1 @@ -75,7 +75,7 @@ function* handleError() { function* transactionsSaga() { yield take(DRIZZLE_UTILS_SAGA_INITIALIZED); yield takeEvery('INIT_TRANSACTION', initTransaction); - yield takeEvery('EVENT_FIRED', handleEvent); + yield takeEvery(CONTRACT_EVENT_FIRED, handleEvent); yield takeEvery('TX_ERROR', handleError); } diff --git a/app/src/redux/sagas/userSaga.js b/app/src/redux/sagas/userSaga.js index d11cb3e..703ec68 100644 --- a/app/src/redux/sagas/userSaga.js +++ b/app/src/redux/sagas/userSaga.js @@ -1,6 +1,6 @@ import { call, put, select, take, takeEvery } from 'redux-saga/effects'; -import { contract, getCurrentAccount } from './drizzleUtilsSaga'; +import { forumContract, getCurrentAccount } from './drizzleUtilsSaga'; import { DRIZZLE_UTILS_SAGA_INITIALIZED } from '../actions/drizzleUtilsActions'; let account; @@ -13,14 +13,14 @@ function* updateUserData() { type: 'ACCOUNT_CHANGED', ...[] }); } - const txObj1 = yield call(contract.methods.hasUserSignedUp, ...[account]); + const txObj1 = yield call(forumContract.methods.hasUserSignedUp, ...[account]); try { const userState = yield call(getUserState); const callResult = yield call(txObj1.call, { address: account }); if (callResult) { - const txObj2 = yield call(contract.methods.getUsername, ...[account]); + const txObj2 = yield call(forumContract.methods.getUsername, ...[account]); const username = yield call(txObj2.call, { address: account }); diff --git a/app/src/utils/orbitUtils.js b/app/src/utils/orbitUtils.js index 2249bb9..aa34acf 100644 --- a/app/src/utils/orbitUtils.js +++ b/app/src/utils/orbitUtils.js @@ -13,14 +13,14 @@ function initIPFS() { store.dispatch({ type: IPFS_INITIALIZED, ipfs }); - console.log('IPFS initialized.'); + console.debug('IPFS initialized.'); }); } async function createDatabases() { - console.log("Deleting local storage..."); // Else we are in danger of reusing an existing orbit + console.debug("Deleting local storage..."); // Else we are in danger of reusing an existing orbit localStorage.clear(); // Perhaps not needed at all when orbit ids are used in Orbit 0.20.x+ - console.log('Creating databases...'); + console.debug('Creating databases...'); const ipfs = getIPFS(); const orbitdb = await new OrbitDB(ipfs); const topicsDB = await orbitdb.keyvalue('topics'); @@ -67,7 +67,21 @@ async function loadDatabases(identityId, identityPublicKey, identityPrivateKey, await topicsDB.load().catch((error) => console.error(`TopicsDB loading error: ${error}`)); await postsDB.load().catch((error) => console.error(`PostsDB loading error: ${error}`)); - console.log('Orbit databases loaded successfully.'); + //It is possible that we lack our own data and need to replicate them from somewhere else + topicsDB.events.on('replicate', (address) => { + console.log(`TopicsDB Replicating (${address}).`); + }); + topicsDB.events.on('replicated', (address) => { + console.log(`TopicsDB replicated (${address}).`); + }); + postsDB.events.on('replicate', (address) => { + console.log(`PostsDB replicating (${address}).`); + }); + postsDB.events.on('replicated', (address) => { + console.log(`PostsDB replicated (${address}).`); + }); + + console.debug('Orbit databases loaded successfully.'); store.dispatch(updateDatabases(DATABASES_LOADED, orbitdb, topicsDB, postsDB)); } @@ -79,4 +93,17 @@ async function orbitSagaPut(db, key, value) { await db.put(key, value).catch((error) => console.error(`Orbit put error: ${error}`)); } -export { initIPFS, createDatabases, loadDatabases, orbitSagaPut }; +async function orbitSagaOpen(orbitdb, address) { + const store = await orbitdb.keyvalue(address) + .catch((error) => console.error(`Error opening a peer's db: ${error}`)); + await store.load().catch((error) => console.log(error)); + store.events.on('replicate', (address) => { + console.log(`A peer's DB is being replicated (${address}).`); + }); + store.events.on('replicated', (address) => { + console.log(`A peer's DB was replicated (${address}).`); + }); + return store; +} + +export { initIPFS, createDatabases, loadDatabases, orbitSagaPut, orbitSagaOpen }; diff --git a/contracts/Forum.sol b/contracts/Forum.sol index b8b75b7..5bb6fcf 100644 --- a/contracts/Forum.sol +++ b/contracts/Forum.sol @@ -1,14 +1,6 @@ pragma solidity >=0.5.6 <0.6.0; -import "./Posting.sol"; - contract Forum { - Posting posting; - - constructor(address addr) public { - posting = Posting(addr); - posting.setForumContractAddress(); - } //----------------------------------------USER---------------------------------------- struct User { @@ -163,41 +155,86 @@ contract Forum { ); } - //----POSTING----- + //----------------------------------------POSTING---------------------------------------- + struct Topic { + uint topicID; + address author; + uint timestamp; + uint[] postIDs; + } + + struct Post { + uint postID; + address author; + uint timestamp; + uint topicID; + } + + uint numTopics; // Total number of topics + uint numPosts; // Total number of posts + + mapping (uint => Topic) topics; + mapping (uint => Post) posts; + + event TopicCreated(uint topicID, uint postID); + event PostCreated(uint postID, uint topicID); function createTopic() public returns (uint, uint) { require(hasUserSignedUp(msg.sender)); // Only registered users can create topics - (uint topicID, uint postID) = posting.createTopic(msg.sender); + //Creates topic + uint topicID = numTopics++; + topics[topicID] = Topic(topicID, msg.sender, block.timestamp, new uint[](0)); users[msg.sender].topicIDs.push(topicID); + + //Adds first post to topic + uint postID = numPosts++; + posts[postID] = Post(postID, msg.sender, block.timestamp, topicID); + topics[topicID].postIDs.push(postID); users[msg.sender].postIDs.push(postID); + + emit TopicCreated(topicID, postID); return (topicID, postID); } function createPost(uint topicID) public returns (uint) { require(hasUserSignedUp(msg.sender)); // Only registered users can create posts - uint postID = posting.createPost(topicID, msg.sender); + require(topicID Topic) topics; mapping (uint => Post) posts; - event TopicCreated(uint topicID, uint postID); - event PostCreated(uint postID, uint topicID); - function createTopic(address author) public returns (uint, uint) { require(msg.sender==forumContractAddress); //Creates topic @@ -42,7 +39,6 @@ contract Posting { posts[postID] = Post(postID, author, block.timestamp, topicID); topics[topicID].postIDs.push(postID); - emit TopicCreated(topicID, postID); return (topicID, postID); } @@ -52,7 +48,7 @@ contract Posting { uint postID = numPosts++; posts[postID] = Post(postID, author, block.timestamp, topicID); topics[topicID].postIDs.push(postID); - emit PostCreated(postID, topicID); + return postID; } diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index c57a411..77766ed 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -1,8 +1,5 @@ const Forum = artifacts.require("Forum"); -const Posting = artifacts.require("Posting"); module.exports = function(deployer) { - deployer.deploy(Posting).then(function() { - return deployer.deploy(Forum, Posting.address) - }); + deployer.deploy(Forum); };