Browse Source

Fix NewPost causing PostList re-rendering, Add date of user registration, other minor fixes

develop
Apostolos Fanakis 7 years ago
parent
commit
7d8ba7c6e4
  1. 17
      contracts/Forum.sol
  2. 10
      src/components/FloatingButton.js
  3. 2
      src/components/NewPost.js
  4. 5
      src/components/ProfileInformation.js
  5. 22
      src/components/TopicList.js
  6. 5
      src/containers/BoardContainer.js
  7. 35
      src/containers/ProfileContainer.js
  8. 10
      src/containers/TopicContainer.js
  9. 26
      src/util/orbit.js

17
contracts/Forum.sol

@ -8,6 +8,7 @@ contract Forum {
OrbitDB orbitdb; OrbitDB orbitdb;
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
uint timestamp;
bool signedUp; // Helper variable for hasUserSignedUp() bool signedUp; // Helper variable for hasUserSignedUp()
} }
@ -20,7 +21,9 @@ contract Forum {
function signUp(string username, string orbitDBId, string orbitTopicsDB, string orbitPostsDB, string orbitPublicKey, string orbitPrivateKey) public returns (bool) { function signUp(string username, string orbitDBId, string orbitTopicsDB, string orbitPostsDB, string orbitPublicKey, string orbitPrivateKey) public returns (bool) {
require (!hasUserSignedUp(msg.sender), "User has already signed up."); require (!hasUserSignedUp(msg.sender), "User has already signed up.");
require(!isUserNameTaken(username), "Username is already taken."); require(!isUserNameTaken(username), "Username is already taken.");
users[msg.sender] = User(username, OrbitDB(orbitDBId, orbitTopicsDB, orbitPostsDB, orbitPublicKey, orbitPrivateKey), new uint[](0), new uint[](0), true); users[msg.sender] = User(username,
OrbitDB(orbitDBId,orbitTopicsDB, orbitPostsDB, orbitPublicKey, orbitPrivateKey),
new uint[](0), new uint[](0), block.timestamp, true);
userAddresses[username] = msg.sender; userAddresses[username] = msg.sender;
emit UserSignedUp(username, msg.sender); emit UserSignedUp(username, msg.sender);
return true; return true;
@ -56,15 +59,20 @@ contract Forum {
} }
function getUserTopics(address userAddress) public view returns (uint[]) { function getUserTopics(address userAddress) public view returns (uint[]) {
require (hasUserSignedUp(msg.sender), "User hasn't signed up yet."); require (hasUserSignedUp(userAddress), "User hasn't signed up yet.");
return users[userAddress].topicIDs; return users[userAddress].topicIDs;
} }
function getUserPosts(address userAddress) public view returns (uint[]) { function getUserPosts(address userAddress) public view returns (uint[]) {
require (hasUserSignedUp(msg.sender), "User hasn't signed up yet."); require (hasUserSignedUp(userAddress), "User hasn't signed up yet.");
return users[userAddress].postIDs; return users[userAddress].postIDs;
} }
function getUserDateOfRegister(address userAddress) public view returns (uint) {
require (hasUserSignedUp(userAddress), "User hasn't signed up yet.");
return users[userAddress].timestamp;
}
//----------------------------------------OrbitDB---------------------------------------- //----------------------------------------OrbitDB----------------------------------------
struct OrbitDB { struct OrbitDB {
string id; // TODO: set an upper bound instead of arbitrary string string id; // TODO: set an upper bound instead of arbitrary string
@ -136,8 +144,6 @@ contract Forum {
event TopicCreated(uint topicID, uint postID); event TopicCreated(uint topicID, uint postID);
event PostCreated(uint postID, uint topicID); event PostCreated(uint postID, uint topicID);
/* event NumberOfTopicsReceived(uint numTopics);
event TopicReceived(string orbitTopicsDB, address author, string username, uint timestamp, uint[] postIDs); */
function createTopic() public returns (uint, uint) { function createTopic() public returns (uint, uint) {
require(hasUserSignedUp(msg.sender)); // Only registered users can create topics require(hasUserSignedUp(msg.sender)); // Only registered users can create topics
@ -168,7 +174,6 @@ contract Forum {
} }
function getNumberOfTopics() public view returns (uint) { function getNumberOfTopics() public view returns (uint) {
/* emit NumberOfTopicsReceived(numTopics); */
return numTopics; return numTopics;
} }

10
src/components/FloatingButton.js

@ -1,11 +1,19 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router';
const FloatingButton = (props) => { const FloatingButton = (props) => {
return ( return (
<div className="pure-u-1-1" onClick={props.onClick}> <div className="pure-u-1-1">
{props.to
?<Link to={props.to}>
<p className="no-margin floating-button" data-fa-transform="down-6"> <p className="no-margin floating-button" data-fa-transform="down-6">
<i className="fa fa-plus fa-2x"></i> <i className="fa fa-plus fa-2x"></i>
</p> </p>
</Link>
:<p className="no-margin floating-button" data-fa-transform="down-6" onClick={props.onClick}>
<i className="fa fa-plus fa-2x"></i>
</p>
}
</div> </div>
); );
}; };

2
src/components/NewPost.js

@ -22,7 +22,7 @@ class NewPost extends Component {
this.drizzle = context.drizzle; this.drizzle = context.drizzle;
this.state = { this.state = {
postSubjectInput: this.props.subject, postSubjectInput: this.props.subject ? this.props.subject : "",
postContentInput: '', postContentInput: '',
postSubjectInputEmptySubmit: false, postSubjectInputEmptySubmit: false,
postContentInputEmptySubmit: false, postContentInputEmptySubmit: false,

5
src/components/ProfileInformation.js

@ -25,6 +25,11 @@ const ProfileInformation = (props) => {
<p className="no-margin"> <p className="no-margin">
<strong>Number of posts:</strong> {props.numberOfPosts} <strong>Number of posts:</strong> {props.numberOfPosts}
</p> </p>
{props.dateOfRegister &&
<p className="no-margin">
<strong>Member since:</strong> {props.dateOfRegister}
</p>
}
{props.self && <UsernameFormContainer/>} {props.self && <UsernameFormContainer/>}
</div> </div>
); );

22
src/components/TopicList.js

@ -32,17 +32,25 @@ class TopicList extends Component {
} }
async fetchSubject(topicIndex) { async fetchSubject(topicIndex) {
/*const fullAddress = this.topicsData[topicID][1]; if (this.topicsData[topicIndex][1] === this.props.user.address){
const store = await this.props.orbitDB.orbitdb.keyvalue(JSON.stringify(fullAddress)); let som =this.props.orbitDB.topicsDB.get(this.props.topicIDs[topicIndex]);
await store.load(); this.topicsSubjects[topicIndex] = som['subject'];
var som = store.get(JSON.stringify(topicID)); this.topicsSubjectsFetchStatus[topicIndex] = "fetched";
this.topicsSubjects[topicID] = som['subject']; } else {
this.topicsSubjectsFetchStatus[topicID] = "fetched";*/ const fullAddress = "/orbitdb" + this.topicsData[topicIndex][0] + "/topics";
const store = await this.props.orbitDB.orbitdb.keyvalue(fullAddress);
var som =this.props.orbitDB.topicsDB.get(this.props.topicIDs[topicIndex]); /*store.events.on('replicated', () => {
const result = store.iterator({ limit: -1 }).collect().map(e => e.payload.value)
console.log(result.join('\n'))
})*/
await store.load();
let som = store.get(this.props.topicIDs[topicIndex]);
this.topicsSubjects[topicIndex] = som['subject']; this.topicsSubjects[topicIndex] = som['subject'];
this.topicsSubjectsFetchStatus[topicIndex] = "fetched"; this.topicsSubjectsFetchStatus[topicIndex] = "fetched";
} }
}
render (){ render (){
const topics = this.topicsData.map((topic, index) => { const topics = this.topicsData.map((topic, index) => {

5
src/containers/BoardContainer.js

@ -1,7 +1,6 @@
import { drizzleConnect } from 'drizzle-react'; import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Link } from 'react-router';
import TopicList from '../components/TopicList'; import TopicList from '../components/TopicList';
import FloatingButton from '../components/FloatingButton'; import FloatingButton from '../components/FloatingButton';
@ -34,9 +33,7 @@ class Board extends Component {
return ( return (
<div style={{marginBottom: '70px'}}> <div style={{marginBottom: '70px'}}>
{boardContents} {boardContents}
<Link to="/startTopic"> <FloatingButton to="/startTopic"/>
<FloatingButton onClick={this.handleClick}/>
</Link>
</div> </div>
); );
} }

35
src/containers/ProfileContainer.js

@ -6,9 +6,11 @@ import ProfileInformation from '../components/ProfileInformation';
import TopicList from '../components/TopicList'; import TopicList from '../components/TopicList';
import PostList from '../components/PostList'; import PostList from '../components/PostList';
import LoadingSpinner from '../components/LoadingSpinner'; import LoadingSpinner from '../components/LoadingSpinner';
import epochTimeConverter from '../helpers/EpochTimeConverter';
const contract = "Forum"; const contract = "Forum";
const contractMethods = { const contractMethods = {
getDateOfRegister: "getUserDateOfRegister",
getOrbitDB: "getOrbitDBId", getOrbitDB: "getOrbitDBId",
getUserTopics: "getUserTopics", getUserTopics: "getUserTopics",
getUserPosts: "getUserPosts" getUserPosts: "getUserPosts"
@ -31,8 +33,10 @@ class Profile extends Component {
this.state = { this.state = {
viewSelected: "topics", viewSelected: "topics",
username: this.match.username, // TODO actually get them from match username: this.match.username, // TODO actually get them from match
userAddress: this.match.address, // when router is fixed userAddress: this.match.userAddress, // when router is fixed
dateOfRegister: null,
orbitDBId: this.match.address === this.props.user.address ? this.props.orbitDB.id : "", orbitDBId: this.match.address === this.props.user.address ? this.props.orbitDB.id : "",
getDateOfRegisterTransactionState: null,
getOrbitDBTransactionState: this.match.address === this.props.user.address ? "SUCCESS" : null, getOrbitDBTransactionState: this.match.address === this.props.user.address ? "SUCCESS" : null,
getTopicsTransactionState: null, getTopicsTransactionState: null,
getPostsTransactionState: null, getPostsTransactionState: null,
@ -53,6 +57,7 @@ class Profile extends Component {
orbitAddress={this.state.orbitDBId} orbitAddress={this.state.orbitDBId}
numberOfTopics={this.state.topicIDs.length} numberOfTopics={this.state.topicIDs.length}
numberOfPosts={this.state.postIDs.length} numberOfPosts={this.state.postIDs.length}
dateOfRegister={this.state.dateOfRegister}
self/> self/>
<div className="pure-u-1-1 profile-tabs-header"> <div className="pure-u-1-1 profile-tabs-header">
<p onClick={() => (this.handleTabClick("topics"))} <p onClick={() => (this.handleTabClick("topics"))}
@ -83,11 +88,29 @@ class Profile extends Component {
} }
componentWillReceiveProps() { componentWillReceiveProps() {
if (this.state.getDateOfRegisterTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
this.dateOfRegisterKey = this.drizzle.contracts[contract]
.methods[contractMethods.getDateOfRegister].cacheCall(this.state.userAddress);
this.setState({'getDateOfRegisterTransactionState': "IN_PROGRESS"});
}
}
if (this.state.getDateOfRegisterTransactionState === "IN_PROGRESS") {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState
.contracts[contract][contractMethods.getDateOfRegister])[this.dateOfRegisterKey];
if (dataFetched){
this.setState({
'dateOfRegister': epochTimeConverter(dataFetched.value),
'getDateOfRegisterTransactionState': "SUCCESS"
});
}
}
if (this.state.getOrbitDBTransactionState === null){ if (this.state.getOrbitDBTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
//This gets called only once but should be called every time someone posts
this.orbitDBIdKey = this.drizzle.contracts[contract] this.orbitDBIdKey = this.drizzle.contracts[contract]
.methods[contractMethods.getOrbitDB].cacheCall(this.match.userAddress); .methods[contractMethods.getOrbitDB].cacheCall(this.state.userAddress);
this.setState({'getOrbitDBTransactionState': "IN_PROGRESS"}); this.setState({'getOrbitDBTransactionState': "IN_PROGRESS"});
} }
} }
@ -105,9 +128,8 @@ class Profile extends Component {
if (this.state.getTopicsTransactionState === null){ if (this.state.getTopicsTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
//This gets called only once but should be called every time someone posts
this.getTopicsKey = this.drizzle.contracts[contract] this.getTopicsKey = this.drizzle.contracts[contract]
.methods[contractMethods.getUserTopics].cacheCall(this.match.userAddress); .methods[contractMethods.getUserTopics].cacheCall(this.state.userAddress);
this.setState({'getTopicsTransactionState': "IN_PROGRESS"}); this.setState({'getTopicsTransactionState': "IN_PROGRESS"});
} }
} }
@ -125,9 +147,8 @@ class Profile extends Component {
if (this.state.getPostsTransactionState === null){ if (this.state.getPostsTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
//This gets called only once but should be called every time someone posts
this.getPostsKey = this.drizzle.contracts[contract] this.getPostsKey = this.drizzle.contracts[contract]
.methods[contractMethods.getUserPosts].cacheCall(this.match.userAddress); .methods[contractMethods.getUserPosts].cacheCall(this.state.userAddress);
this.setState({'getPostsTransactionState': "IN_PROGRESS"}); this.setState({'getPostsTransactionState': "IN_PROGRESS"});
} }
} }

10
src/containers/TopicContainer.js

@ -63,18 +63,18 @@ class Topic extends Component {
); );
} else { } else {
topicContents = ( topicContents = (
this.state.posting (<div style={{marginBottom: '100px'}}>
?(<div style={{marginBottom: '100px'}}> {this.state.posting &&
<NewPost topicID={1} <NewPost topicID={1}
subject={this.state.topicSubject} subject={this.state.topicSubject}
onCancelClick={() => {this.handleClick()}} onCancelClick={() => {this.handleClick()}}
onPostCreated={() => {this.postCreated()}} onPostCreated={() => {this.postCreated()}}
/> />
}
<PostList postIDs={this.posts}/> <PostList postIDs={this.posts}/>
</div>) {!this.state.posting &&
:(<div style={{marginBottom: '100px'}}>
<PostList postIDs={this.posts}/>
<FloatingButton onClick={this.handleClick}/> <FloatingButton onClick={this.handleClick}/>
}
</div>) </div>)
) )
} }

26
src/util/orbit.js

@ -12,24 +12,25 @@ const ipfsOptions = {
EXPERIMENTAL: { EXPERIMENTAL: {
pubsub: true pubsub: true
}, config: { }, config: {
Addresses: {
Swarm: []
}
},
};
/*,
config: {
Addresses: { Addresses: {
Swarm: [ Swarm: [
// Use IPFS dev signal server // Use IPFS dev signal server
// Prefer websocket over webrtc
//
// Websocket:
// '/dns4/ws-star-signal-2.servep2p.com/tcp/443//wss/p2p-websocket-star',
// '/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star',
// Local signal server
//'/ip4/127.0.0.1/tcp/4711/ws/p2p-websocket-star'
//
// WebRTC:
// '/dns4/star-signal.cloud.ipfs.team/wss/p2p-webrtc-star', // '/dns4/star-signal.cloud.ipfs.team/wss/p2p-webrtc-star',
'/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star', // Local signal server
// Use local signal server // '/ip4/127.0.0.1/tcp/1337/ws/p2p-webrtc-star'
// '/ip4/0.0.0.0/tcp/9090/wss/p2p-webrtc-star',
] ]
} }
}*/ },
};
// Create IPFS instance // Create IPFS instance
const ipfs = new IPFS(ipfsOptions); const ipfs = new IPFS(ipfsOptions);
@ -39,7 +40,6 @@ ipfs.on('ready', async () => {
store.dispatch({type: "IPFS_INITIALIZED"}); store.dispatch({type: "IPFS_INITIALIZED"});
}); });
async function createDatabases() { async function createDatabases() {
orbitdb = new OrbitDB(ipfs); orbitdb = new OrbitDB(ipfs);
topicsDB = await orbitdb.keyvalue('topics'); topicsDB = await orbitdb.keyvalue('topics');

Loading…
Cancel
Save