Browse Source

Merge remote-tracking branch 'origin/develop' into materialUI

# Conflicts:
#	src/components/Post.js
#	src/components/PostList.js
#	src/components/ProfileInformation.js
#	src/components/Topic.js
#	src/containers/ProfileContainer.js
develop
Apostolos Fanakis 7 years ago
parent
commit
8c6d37585e
  1. 1
      contracts/Forum.sol
  2. 199
      src/components/Post.js
  3. 133
      src/components/PostList.js
  4. 22
      src/components/ProfileInformation.js
  5. 117
      src/components/Topic.js
  6. 132
      src/components/TopicList.js
  7. 81
      src/components/WithBlockchainData.js
  8. 59
      src/containers/BoardContainer.js
  9. 227
      src/containers/ProfileContainer.js
  10. 3
      src/containers/StartTopicContainer.js
  11. 71
      src/containers/TopicContainer.js
  12. 5
      src/index.js

1
contracts/Forum.sol

@ -41,6 +41,7 @@ contract Forum {
}
function getUsername(address userAddress) public view returns (string) {
require (hasUserSignedUp(userAddress), "User hasn't signed up yet.");
return users[userAddress].username;
}

199
src/components/Post.js

@ -1,86 +1,127 @@
import React from 'react';
import React, { Component } from 'react';
import { Link, withRouter } from 'react-router';
import UserAvatar from 'react-user-avatar';
import { drizzleConnect } from 'drizzle-react';
import PropTypes from 'prop-types';
import TimeAgo from 'react-timeago';
import epochTimeConverter from '../helpers/EpochTimeConverter'
import UserAvatar from 'react-user-avatar';
import ReactMarkdown from 'react-markdown';
const Post = (props) => {
return (
props.post !== null
? <div className="post">
<div className="row">
<div className="col s1">
<Link to={"/profile/" + props.post.userAddress + "/" + props.post.username}
onClick={(event) => {event.stopPropagation()}}>
<UserAvatar
size="50"
className="inline user-avatar"
src={props.post.avatarUrl}
name={props.post.username}/>
</Link>
</div>
<div className="col s11">
<div>
<div className="stretch-space-between">
<strong><span>{props.post.username}</span></strong>
<span className="grey-text text-darken-2">
<TimeAgo date={props.post.date}/>, #{props.post.postIndex}
</span>
</div>
<div className="stretch-space-between">
<strong><span>{props.post.subject}</span></strong>
</div>
<div>
{props.post.postContent
? <ReactMarkdown source={props.post.postContent} />
: <p style={{color: 'grey'}}>Post content...</p>
}
</div>
</div>
</div>
</div>
<div className="row">
<div className="post-meta grey-text text-darken-2">
<i className="material-icons waves-effect waves-teal circle">
keyboard_arrow_up
</i>
<span>8</span>
<i className="material-icons waves-effect waves-teal circle">
keyboard_arrow_down
</i>
<i className="material-icons waves-effect waves-teal circle" onClick={props.onHrefClick}>
link
</i>
</div>
</div>
<div className="divider"></div>
</div>
: <div className="post grey-text text-darken-2">
<div className="row">
<div className="col s1">
<div></div>
</div>
<div className="col s11">
<div>
<div className="stretch-space-between">
<span></span>
</div>
<div className="stretch-space-between">
<span>Subject:</span>
</div>
<div>
<p>Post content...</p>
</div>
</div>
</div>
</div>
<div className="row">
<div className="post-meta">
</div>
class Post extends Component {
constructor(props, context) {
super(props);
this.fetchPost = this.fetchPost.bind(this);
this.orbitPostData = {
content: "",
subject: ""
};
this.orbitPostDataFetchStatus = "pending";
}
async fetchPost(postID) {
this.orbitPostDataFetchStatus = "fetching";
var som = this.props.orbitDB.postsDB.get(postID);
if (som){
this.orbitPostData = som;
}
this.orbitPostDataFetchStatus = "fetched";
}
render(){
let avatarView = (this.props.blockchainData[0].returnData
? <UserAvatar
size="40"
className="inline user-avatar"
src={this.props.avatarUrl}
name={this.props.blockchainData[0].returnData[2]}/>
: <div className="user-avatar" style={{width: "40px"}}></div>
);
return (
<div className="post"
onClick={() => { this.context.router.push("/topic/"
+ this.props.blockchainData[0].returnData[4] + "/"
+ this.props.postID)}}>
<div className="row">
<div className="col s1">
{this.props.blockchainData[0].returnData !== null
?<Link to={"/profile/" + this.props.blockchainData[0].returnData[1]
+ "/" + this.props.blockchainData[0].returnData[2]}
onClick={(event) => {event.stopPropagation()}}>
{avatarView}
</Link>
:avatarView
}
</div>
<div className="col s11">
<div>
<div className="stretch-space-between">
<strong><span className={this.props.blockchainData[0].returnData !== null ? "" : "grey-text"}}>
{this.props.blockchainData[0].returnData !== null
?this.props.blockchainData[0].returnData[2]
:"Username"
}
</span></strong>
<span className="grey-text text-darken-2">
{this.props.blockchainData[0].returnData !== null &&
<TimeAgo date={epochTimeConverter(this.props.blockchainData[0].returnData[3])}/>
}, #{this.props.postIndex}
</span>
</div>
<div className="stretch-space-between">
<strong><span className={this.orbitPostData.subject ? "" : "grey-text"}>
Subject: {this.orbitPostData.subject}
</span></strong>
</div>
<div>
{this.orbitPostData.content
? <ReactMarkdown source={this.orbitPostData.content} />
: <p className="grey-text">Post content...</p>
}
</div>
</div>
</div>
</div>
<div className="row">
<div className="post-meta grey-text text-darken-2">
<i className="material-icons waves-effect waves-teal circle">
keyboard_arrow_up
</i>
<span>8</span>
<i className="material-icons waves-effect waves-teal circle">
keyboard_arrow_down
</i>
<i className="material-icons waves-effect waves-teal circle" onClick={props.onHrefClick}>
link
</i>
</div>
</div>
<div className="divider"></div>
</div>
<div className="divider"></div>
</div>
);
);
}
componentDidUpdate() {
if (this.props.blockchainData[0].status === "success"
&& this.orbitPostDataFetchStatus === "pending") {
this.fetchPost(this.props.postID);
}
}
};
Post.contextTypes = {
router: PropTypes.object
};
const mapStateToProps = state => {
return {
user: state.user,
orbitDB: state.orbitDB
}
};
export default withRouter(Post);
export default drizzleConnect(withRouter(Post), mapStateToProps);

133
src/components/PostList.js

@ -1,116 +1,35 @@
import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import React from 'react';
import Post from './Post';
import epochTimeConverter from '../helpers/EpochTimeConverter'
const contract = "Forum";
const contractMethod = "getPost";
class PostList extends Component {
constructor(props, context) {
super(props);
this.fetchPost = this.fetchPost.bind(this);
this.onHrefHandle = this.onHrefHandle.bind(this);
this.drizzle = context.drizzle;
this.dataKeys = [];
this.postsData = new Array(parseInt(this.props.postIDs.length, 10)).fill(undefined);
this.orbitPostsData = new Array(parseInt(this.props.postIDs.length, 10)).fill(undefined);
this.orbitPostsDataFetchStatus = new Array(parseInt(this.props.postIDs.length, 10)).fill("pending");
for (var i = 0; i < this.props.postIDs.length; ++i){
this.dataKeys[i] = this.drizzle.contracts[contract].methods[contractMethod]
.cacheCall(this.props.postIDs[i]);
}
}
async fetchPost(index) {
/*const fullAddress = this.postsData[postID][1];
const store = await this.props.orbitDB.orbitdb.keyvalue(JSON.stringify(fullAddress));
await store.load();
var som = store.get(JSON.stringify(postID));
this.orbitPostsData[postID] = som['subject'];
this.orbitPostsDataFetchStatus[postID] = "fetched";*/
import WithBlockchainData from './WithBlockchainData';
var som = this.props.orbitDB.postsDB.get(this.props.postIDs[index]);
this.orbitPostsData[index] = som;
this.orbitPostsDataFetchStatus[index] = "fetched";
}
onHrefHandle(postIndex){
this.context.router.push("/topic/" + this.postsData[postIndex][4] + "/" +
((this.orbitPostsData[postIndex] !== undefined)
? this.orbitPostsData[postIndex].subject + "/" + this.props.postIDs[postIndex]
: ""))
}
render (){
const posts = this.postsData.map((post, index) => {
if (post) {
return (
<div key={index}>
<Post post={{
avatarUrl:post.avatarUrl,
userAddress:post[1],
username:post[2],
subject:(this.orbitPostsData[index] !== undefined) &&
this.orbitPostsData[index].subject,
date:epochTimeConverter(post[3]),
postIndex:index,
postContent:(this.orbitPostsData[index] !== undefined) &&
this.orbitPostsData[index].content,
}}
onHrefClick={() => this.onHrefHandle(index)}
id={index}
key={index}/>
</div>
);
} else {
return (
<Post post={null}
id={index}
key={index}/>
);
}
});
import Post from './Post';
const PostList = (props) => {
const posts = props.postIDs.map((postID, index) => {
return (
<div className="posts-list">
{posts}
</div>
<WithBlockchainData
component={Post}
callsInfo={[{
contract: 'Forum',
method: 'getPost',
params: [postID]
}]}
avatarUrl={""}
postIndex={index}
postID={postID}
key={postID}
/>
);
}
});
componentWillReceiveProps() {
for (var i = 0; i < this.props.postIDs.length; ++i){
if (this.postsData[i] === undefined) {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState.contracts[contract][contractMethod])[this.dataKeys[i]];
if (dataFetched){
this.postsData[i] = dataFetched.value;
}
} else if (!this.orbitPostsData[i] && this.orbitPostsDataFetchStatus[i] === "pending") {
this.orbitPostsDataFetchStatus[i] = "fetching";
this.fetchPost(i);
return (
<div className="posts-list">
{props.recentToTheTop
?posts.slice(0).reverse()
:posts
}
}
}
};
PostList.contextTypes = {
drizzle: PropTypes.object,
router: PropTypes.object
};
const mapStateToProps = state => {
return {
user: state.user, //Needed!!
orbitDB: state.orbitDB
}
</div>
);
};
export default drizzleConnect(PostList, mapStateToProps);
export default PostList;

22
src/components/ProfileInformation.js

@ -1,20 +1,34 @@
import React from 'react';
import UserAvatar from 'react-user-avatar';
import epochTimeConverter from '../helpers/EpochTimeConverter';
import UsernameFormContainer from '../containers/UsernameFormContainer';
const ProfileInformation = (props) => {
let transaction = props.blockchainData
.find(transaction => transaction.callInfo.method === "getUsername");
let username = transaction ? transaction.returnData : "";
transaction = props.blockchainData
.find(transaction => transaction.callInfo.method === "getUserDateOfRegister");
let dateOfRegister = transaction ? transaction.returnData : "";
transaction = props.blockchainData
.find(transaction => transaction.callInfo.method === "getOrbitDBId")
let orbitDBId = transaction ? transaction.returnData : "";
return (
<div className="user-info">
{props.avatarUrl && <UserAvatar
size="40"
className="inline user-avatar"
src={props.avatarUrl}
name={props.username}/>}
name={username}/>}
<table className="highlight centered responsive-table">
<tbody>
<tr>
<td><strong>Username:</strong></td>
<td>{props.username}</td>
<td>{username}</td>
</tr>
<tr>
<td><strong>Account address:</strong></td>
@ -22,7 +36,7 @@ const ProfileInformation = (props) => {
</tr>
<tr>
<td><strong>OrbitDB:</strong></td>
<td>{props.orbitAddress}</td>
<td>{orbitDBId}</td>
</tr>
<tr>
<td><strong>Number of topics created:</strong></td>
@ -35,7 +49,7 @@ const ProfileInformation = (props) => {
{props.dateOfRegister &&
<tr>
<td><strong>Member since:</strong></td>
<td>{props.dateOfRegister}</td>
<td>{epochTimeConverter(dateOfRegister)}</td>
</tr>
}
</tbody>

117
src/components/Topic.js

@ -1,38 +1,91 @@
import React from 'react';
import React, { Component } from 'react';
import { Link } from 'react-router';
import { drizzleConnect } from 'drizzle-react';
import TimeAgo from 'react-timeago';
import epochTimeConverter from '../helpers/EpochTimeConverter'
const Topic = (props) => {
return (
props.topic !== null
? <div className="topic card white hoverable">
<div className="card-content">
<div className="topic-subject">
<p style={{color: props.topic.topicSubject ? "" : "grey"}}>
{props.topic.topicSubject ? props.topic.topicSubject : "Subject"}
</p>
</div>
<hr/>
<div className="topic-meta">
<p className="no-margin">{props.topic.topicStarter}</p>
<p className="no-margin">Number of replies: {props.topic.numberOfReplies}</p>
<p className="topic-date grey-text darken-3">Started <TimeAgo date={props.topic.date}/></p>
</div>
</div>
</div>
: <div className="topic card white hoverable">
<div className="card-content grey-text">
<div className="topic-subject">
<p>Subject</p>
</div>
<hr/>
<div className="topic-meta">
<p className="no-margin">Username</p>
<p className="no-margin">Number of replies: </p>
<p className="topic-date grey-text darken-3"></p>
class Topic extends Component {
constructor(props){
super(props);
this.fetchSubject = this.fetchSubject.bind(this);
this.topicSubject = null;
this.topicSubjectFetchStatus = "pending";
}
async fetchSubject(topicID) {
this.topicSubjectFetchStatus = "fetching";
if (this.props.blockchainData[0].returnData[1] === this.props.user.address) {
let som = this.props.orbitDB.topicsDB.get(topicID);
this.topicSubject = som['subject'];
this.topicSubjectFetchStatus = "fetched";
} else {
const fullAddress = "/orbitdb" + this.props.blockchainData[0].returnData[0] + "/topics";
const store = await this.props.orbitDB.orbitdb.keyvalue(fullAddress);
/*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(topicID);
this.topicSubject = som['subject'];
this.topicSubjectFetchStatus = "fetched";
}
}
render(){
return (
<Link to={"/topic/" + this.props.topicID}>
<div className="topic card white hoverable">
<div className="card-content">
<div className={"topic-subject" + (this.topicSubject ? "" : "grey-text")}>
<p>
{topic.topicSubject !== null ? topic.topicSubject : "Subject"}
</p>
</div>
<hr/>
<div className="topic-meta">
<p className={"no-margin" + (this.topicSubject ? "" : "grey-text")}>
{this.props.blockchainData[0].returnData !== null
?this.props.blockchainData[0].returnData[2]
:"Username"
}
</p>
<p className={"no-margin" + (this.props.blockchainData[0].returnData !== null ? "" : "grey-text")}>
{"Number of replies: " + (this.props.blockchainData[0].returnData !== null
?this.props.blockchainData[0].returnData[4].length
:"")
}
</p>
<p className="topic-date grey-text darken-3">
Started {this.props.blockchainData[0].returnData !== null &&
<TimeAgo date={epochTimeConverter(this.props.blockchainData[0].returnData[3])}/>
}
</p>
</div>
</div>
</div>
</div>
);
</Link>
);
}
componentDidUpdate(){
if (this.props.blockchainData[0].returnData !== null && this.topicSubjectFetchStatus === "pending") {
this.fetchSubject(this.props.topicID);
}
}
};
export default Topic;
const mapStateToProps = state => {
return {
user: state.user,
orbitDB: state.orbitDB
}
}
export default drizzleConnect(Topic, mapStateToProps);

132
src/components/TopicList.js

@ -1,118 +1,30 @@
import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react';
import { Link } from 'react-router';
import PropTypes from 'prop-types';
import React from 'react';
import Topic from './Topic';
import epochTimeConverter from '../helpers/EpochTimeConverter'
const contract = "Forum";
const contractMethod = "getTopic";
class TopicList extends Component {
constructor(props, context) {
super(props);
this.fetchSubject = this.fetchSubject.bind(this);
this.drizzle = context.drizzle;
this.dataKeys = [];
this.topicsData = new Array(parseInt(this.props.topicIDs.length, 10)).fill(undefined);
this.topicsSubjects = [];
this.topicsSubjectsFetchStatus = new Array(parseInt(this.props.topicIDs.length, 10)).fill("pending");
for (var i = 0; i < this.props.topicIDs.length; ++i){
this.dataKeys[i] = this.drizzle.contracts[contract].methods[contractMethod]
.cacheCall(this.props.topicIDs[i]);
}
this.state = {
};
}
import WithBlockchainData from './WithBlockchainData';
async fetchSubject(topicIndex) {
if (this.topicsData[topicIndex][1] === this.props.user.address){
let som =this.props.orbitDB.topicsDB.get(this.props.topicIDs[topicIndex]);
this.topicsSubjects[topicIndex] = som['subject'];
this.topicsSubjectsFetchStatus[topicIndex] = "fetched";
} else {
const fullAddress = "/orbitdb" + this.topicsData[topicIndex][0] + "/topics";
const store = await this.props.orbitDB.orbitdb.keyvalue(fullAddress);
/*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.topicsSubjectsFetchStatus[topicIndex] = "fetched";
}
}
render (){
const topics = this.topicsData.map((topic, index) => {
if (topic){
return (
<Link to={"/topic/" + this.props.topicIDs[index] + "/" +
((this.topicsSubjects[index] !== undefined) ? this.topicsSubjects[index] + "/" + 0 : "")}
key={index}>
<Topic topic={{
topicSubject: ((this.topicsSubjects[index] !== undefined) && this.topicsSubjects[index]),
topicStarter: topic[2],
numberOfReplies: topic[4].length,
date: epochTimeConverter(topic[3])
}}
id={index}
key={index}/>
</Link>
);
} else {
return (
<Link to={"/topic/" + this.props.topicIDs[index] + "/"}
key={index}>
<Topic topic={null}
id={index}
key={index}/>
</Link>
);
}
});
import Topic from './Topic';
const TopicList = (props) => {
const topics = props.topicIDs.map((topicID) => {
return (
<div className="topics-list">
{topics.slice(0).reverse()}
</div>
<WithBlockchainData
component={Topic}
callsInfo={[{
contract: 'Forum',
method: 'getTopic',
params: [topicID]
}]}
topicID={topicID}
key={topicID}
/>
);
}
componentWillReceiveProps() {
for (var i = 0; i < this.props.topicIDs.length; ++i){
if (this.topicsData[i] === undefined) {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState.contracts[contract][contractMethod])[this.dataKeys[i]];
if (dataFetched){
this.topicsData[i] = dataFetched.value;
}
} else if (!this.topicsSubjects[i] && this.topicsSubjectsFetchStatus[i] === "pending") {
this.topicsSubjectsFetchStatus[i] = "fetching";
this.fetchSubject(i);
}
}
}
};
TopicList.contextTypes = {
drizzle: PropTypes.object
};
});
const mapStateToProps = state => {
return {
user: state.user, //Needed!!
orbitDB: state.orbitDB,
}
return (
<div className="topics-list">
{topics.slice(0).reverse()}
</div>
);
};
export default drizzleConnect(TopicList, mapStateToProps);
export default TopicList;

81
src/components/WithBlockchainData.js

@ -0,0 +1,81 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class WithBlockchainData extends Component {
constructor(props, context) {
super(props);
{
let {component, callsInfo, ...rest } = this.props;
this.component = component;
this.callsInfo = callsInfo;
this.forwardedProps = rest;
}
this.checkContractUpdates = this.checkContractUpdates.bind(this);
this.drizzle = context.drizzle;
this.dataKeys = [];
this.blockchainData = this.callsInfo.map((call) => {
return ({
callInfo: call,
status: "initialized",
returnData: null
});
});
for (var i = 0; i < this.callsInfo.length; ++i){
this.dataKeys[i] = this.drizzle
.contracts[this.callsInfo[i].contract]
.methods[this.callsInfo[i].method]
.cacheCall(...(this.callsInfo[i].params));
this.blockchainData[i].status = "pending";
}
this.state = {
transactionsState: new Array(this.callsInfo.length).fill("pending")
}
}
render() {
let {component, callsInfo, ...rest } = this.props;
return (
<this.component blockchainData={this.blockchainData} {...rest}/>
);
}
componentDidMount() {
this.intervalChecker = setInterval(this.checkContractUpdates, 10); //HOWMUCHMUCHACHO???
}
componentWillUnmount() {
clearInterval(this.intervalChecker);
}
checkContractUpdates() {
for (var i = 0; i < this.callsInfo.length; ++i){
let currentDrizzleState = this.drizzle.store.getState();
if (this.state.transactionsState[i] === "pending") {
let dataFetched = (currentDrizzleState
.contracts[this.callsInfo[i].contract][this.callsInfo[i].method][this.dataKeys[i]]);
if (dataFetched){
this.blockchainData[i].returnData = dataFetched.value;
this.blockchainData[i].status = "success";
this.setState((prevState) => ({
transactionsState: [
...prevState.transactionsState.slice(0, i),
"success",
...prevState.transactionsState.slice(i)
]
}));
}
} //TODO cover errors!!
}
}
}
WithBlockchainData.contextTypes = {
drizzle: PropTypes.object
};
export default WithBlockchainData;

59
src/containers/BoardContainer.js

@ -2,32 +2,28 @@ import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import WithBlockchainData from '../components/WithBlockchainData';
import TopicList from '../components/TopicList';
import FloatingButton from '../components/FloatingButton';
import LoadingSpinner from '../components/LoadingSpinner';
const contract = "Forum";
const contractMethod = "getNumberOfTopics";
class Board extends Component {
constructor(props, context) {
super(props);
this.drizzle = context.drizzle;
this.state = {
transactionState: null
};
}
render() {
var boardContents;
if (this.state.transactionState !== "SUCCESS") {
if (!this.props.blockchainData[0].returnData) {
boardContents = (
<LoadingSpinner/>
);
} else {
boardContents = <TopicList topicIDs={this.topicIDs}/>;
this.topicIDs = [];
for (var i = 0; i < this.props.blockchainData[0].returnData; i++) {
this.topicIDs.push(i);
}
boardContents = <TopicList topicIDs={this.topicIDs}/>
}
return (
@ -37,28 +33,6 @@ class Board extends Component {
</div>
);
}
componentWillReceiveProps() {
if (this.state.transactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
//This gets called only once but should be called every time someone posts
this.dataKey = this.drizzle.contracts[contract].methods[contractMethod].cacheCall();
this.setState({'transactionState': "IN_PROGRESS"});
}
}
if (!this.numberOfTopics) {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState.contracts[contract][contractMethod])[this.dataKey];
if (dataFetched){
this.numberOfTopics = dataFetched.value;
this.topicIDs = [];
for (var i = 0; i < this.numberOfTopics; i++) {
this.topicIDs.push(i);
}
this.setState({'transactionState': "SUCCESS"});
}
}
}
}
Board.contextTypes = {
@ -71,6 +45,23 @@ const mapStateToProps = state => {
}
};
const BoardContainer = drizzleConnect(Board, mapStateToProps);
class BoardContainer extends Component {
constructor(props){
super(props);
this.board = <WithBlockchainData
component={drizzleConnect(Board, mapStateToProps)}
callsInfo={[{
contract: 'Forum',
method: 'getNumberOfTopics',
params: []
}]}
/>;
}
render() {
return(this.board);
}
}
export default BoardContainer;

227
src/containers/ProfileContainer.js

@ -3,47 +3,20 @@ import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import WithBlockchainData from '../components/WithBlockchainData';
import ProfileInformation from '../components/ProfileInformation';
import TopicList from '../components/TopicList';
import PostList from '../components/PostList';
import LoadingSpinner from '../components/LoadingSpinner';
import epochTimeConverter from '../helpers/EpochTimeConverter';
import '../assets/css/materialTabs.css';
const contract = "Forum";
const contractMethods = {
getUsername: "getUsername",
getDateOfRegister: "getUserDateOfRegister",
getOrbitDB: "getOrbitDBId",
getUserTopics: "getUserTopics",
getUserPosts: "getUserPosts"
};
class Profile extends Component {
constructor(props, context) {
super(props);
/*console.log(this.props.params.address);
console.log(this.props.user.address);*/
if (this.props.params.address == this.props.user.address){
this.profile = {
userAddress: this.props.params.address,
username: this.props.params.username ? this.props.params.username : "",
orbitId: "",
self: false
}
} else {
this.profile = {
userAddress: this.props.user.address,
username: this.props.user.username,
orbitId: this.props.orbitDB.id,
self: true
}
}
this.handleTabClick = this.handleTabClick.bind(this);
this.propsToView = this.propsToView.bind(this);
this.drizzle = context.drizzle;
this.underlineBarRef = React.createRef();
@ -53,17 +26,7 @@ class Profile extends Component {
this.state = {
viewSelected: "profile-info-tab",
username: this.profile.username,
userAddress: this.profile.userAddress,
dateOfRegister: null,
orbitDBId: this.profile.orbitId,
getUsernameTransactionState: null,
getDateOfRegisterTransactionState: null,
getOrbitDBTransactionState: this.profile.orbitId ? "SUCCESS" : null,
getTopicsTransactionState: null,
getPostsTransactionState: null,
topicIDs: [],
postIDs: []
userAddress: this.profile.userAddress
};
}
@ -85,26 +48,40 @@ class Profile extends Component {
}
render() {
this.propsToView();
var infoTab =
(<ProfileInformation username={this.state.username}
address={this.state.userAddress}
orbitAddress={this.state.orbitDBId}
numberOfTopics={this.state.topicIDs.length}
numberOfPosts={this.state.postIDs.length}
dateOfRegister={this.state.dateOfRegister}
self={this.profile.self}
/>);
(<WithBlockchainData
component={ProfileInformation}
callsInfo={[{
contract: 'Forum',
method: 'getUsername',
params: [this.state.userAddress]
},{
contract: 'Forum',
method: 'getUserDateOfRegister',
params: [this.state.userAddress]
},{
contract: 'Forum',
method: 'getOrbitDBId',
params: [this.state.userAddress]
}]}
address={this.state.userAddress}
numberOfTopics={this.topicIDs && this.topicIDs.length}
numberOfPosts={this.postIDs && this.postIDs.length}
self={this.state.userAddress === this.props.user.address}
key="profileInfo"
/>);
var topicsTab =
(<div className="profile-tab">
{this.state.getTopicsTransactionState === "SUCCESS"
? <TopicList topicIDs={this.state.topicIDs}/>
{this.topicIDs
? <TopicList topicIDs={this.topicIDs} />
: <LoadingSpinner />
}
</div>);
var postsTab =
(<div className="profile-tab">
{this.state.getPostsTransactionState === "SUCCESS"
? <PostList postIDs={this.state.postIDs}/>
{this.postIDs
? <PostList postIDs={this.postIDs} recentToTheTop />
: <LoadingSpinner />
}
</div>);
@ -152,99 +129,19 @@ class Profile extends Component {
);
}
componentWillReceiveProps() {
if (this.state.getUsernameTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
this.usernameKey = this.drizzle.contracts[contract]
.methods[contractMethods.getUsername].cacheCall(this.state.userAddress);
this.setState({'getUsernameTransactionState': "IN_PROGRESS"});
}
}
if (this.state.getUsernameTransactionState === "IN_PROGRESS") {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState
.contracts[contract][contractMethods.getUsername])[this.usernameKey];
if (dataFetched){
this.setState({
'username': dataFetched.value,
'getUsernameTransactionState': "SUCCESS"
});
}
}
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.drizzle.contracts[contract]){ //Waits until drizzle is initialized
this.orbitDBIdKey = this.drizzle.contracts[contract]
.methods[contractMethods.getOrbitDB].cacheCall(this.state.userAddress);
this.setState({'getOrbitDBTransactionState': "IN_PROGRESS"});
}
}
if (this.state.getOrbitDBTransactionState === "IN_PROGRESS") {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState
.contracts[contract][contractMethods.getOrbitDB])[this.orbitDBIdKey];
if (dataFetched){
this.setState({
'orbitDBId': dataFetched.value,
'getOrbitDBTransactionState': "SUCCESS"
});
}
}
if (this.state.getTopicsTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
this.getTopicsKey = this.drizzle.contracts[contract]
.methods[contractMethods.getUserTopics].cacheCall(this.state.userAddress);
this.setState({'getTopicsTransactionState': "IN_PROGRESS"});
}
}
if (this.state.getTopicsTransactionState === "IN_PROGRESS") {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState
.contracts[contract][contractMethods.getUserTopics])[this.getTopicsKey];
if (dataFetched){
this.setState({
'topicIDs': dataFetched.value,
'getTopicsTransactionState': "SUCCESS"
});
propsToView(){
if (!this.topicIDs){
let transaction = this.props.blockchainData
.find(transaction => transaction.callInfo.method === "getUserTopics");
if (transaction.returnData){
this.topicIDs = transaction.returnData;
}
}
if (this.state.getPostsTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
this.getPostsKey = this.drizzle.contracts[contract]
.methods[contractMethods.getUserPosts].cacheCall(this.state.userAddress);
this.setState({'getPostsTransactionState': "IN_PROGRESS"});
}
}
if (this.state.getPostsTransactionState === "IN_PROGRESS") {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState
.contracts[contract][contractMethods.getUserPosts])[this.getPostsKey];
if (dataFetched){
this.setState({
'postIDs': dataFetched.value,
'getPostsTransactionState': "SUCCESS"
});
if (!this.postIDs){
let transaction = this.props.blockchainData
.find(transaction => transaction.callInfo.method === "getUserPosts");
if (transaction.returnData){
this.postIDs = transaction.returnData;
}
}
}
@ -260,14 +157,46 @@ Profile.contextTypes = {
const mapStateToProps = state => {
return {
accounts: state.accounts,
Forum: state.contracts.Forum,
user: state.user,
orbitDB: state.orbitDB,
drizzleStatus: state.drizzleStatus
orbitDB: state.orbitDB
}
};
const ProfileContainer = drizzleConnect(Profile, mapStateToProps);
class ProfileContainer extends Component {
constructor(props){
super(props);
let userAddress;
if (this.props.params.address){
userAddress = this.props.params.address;
} else {
userAddress = this.props.user.address;
}
this.profile = <WithBlockchainData
component={drizzleConnect(Profile, mapStateToProps)}
callsInfo={[{
contract: 'Forum',
method: 'getUserTopics',
params: [userAddress]
},{
contract: 'Forum',
method: 'getUserPosts',
params: [userAddress]
}]}
params={this.props.params}
/>
}
render() {
return(this.profile);
}
}
const containerProps = state => {
return {
user: state.user
}
};
export default ProfileContainer;
export default drizzleConnect(ProfileContainer, containerProps);

3
src/containers/StartTopicContainer.js

@ -237,8 +237,7 @@ class StartTopic extends Component {
'savingToOrbitDB': null,
'transactionOutputTimerActive': false
});
this.props.router.push("/topic/" + this.topicIDFetched + "/"
+ this.state.topicSubjectInput);
this.props.router.push("/topic/" + this.topicIDFetched);
}, 5000);
}
else if (this.state.savingToOrbitDB === "ERROR"){

71
src/containers/TopicContainer.js

@ -2,22 +2,16 @@ import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import WithBlockchainData from '../components/WithBlockchainData';
import PostList from '../components/PostList';
import NewPost from '../components/NewPost';
import FloatingButton from '../components/FloatingButton';
import LoadingSpinner from '../components/LoadingSpinner';
const contract = "Forum";
const contractMethod = "getTopic";
class Topic extends Component {
constructor(props, context) {
super(props);
if (!/^[0-9]+$/.test(this.props.params.topicId)){
this.props.router.push("/404");
}
this.fetchTopicSubject = this.fetchTopicSubject.bind(this);
this.handleClick = this.handleClick.bind(this);
this.postCreated = this.postCreated.bind(this);
@ -43,7 +37,7 @@ class Topic extends Component {
this.topicsSubjects[this.state.topicID] = som['subject'];
this.topicsSubjectsFetchStatus[this.state.topicID] = "fetched";*/
var som =this.props.orbitDB.topicsDB.get(JSON.stringify(this.state.topicID));
var som =this.props.orbitDB.topicsDB.get(this.state.topicID);
this.setState({'topicSubject': som['subject']});
}
@ -79,7 +73,7 @@ class Topic extends Component {
onPostCreated={() => {this.postCreated()}}
/>
}
<PostList postIDs={this.posts}/>
{this.postList}
{!this.state.posting &&
<FloatingButton onClick={this.handleClick}/>
}
@ -95,25 +89,22 @@ class Topic extends Component {
}
componentWillReceiveProps() {
if (this.state.getPostsTransactionState === null){
if (this.drizzle.contracts[contract]){ //Waits until drizzle is initialized
//This gets called only once but should be called every time someone posts
this.getPostsDataKey = this.drizzle.contracts[contract].methods[contractMethod]
.cacheCall(this.state.topicID);
this.setState({'getPostsTransactionState': "IN_PROGRESS"});
}
}
if (this.state.getPostsTransactionState === "IN_PROGRESS") {
let currentDrizzleState = this.drizzle.store.getState();
let dataFetched = (currentDrizzleState.contracts[contract][contractMethod])[this.getPostsDataKey];
if (dataFetched){
if (dataFetched.value){
this.posts = dataFetched.value[4];
this.setState({'getPostsTransactionState': "SUCCESS"});
this.fetchTopicSubject(dataFetched.value[0]);
} else if (dataFetched.error){
//TODO
}
if (this.props.blockchainData[0].status === "success") {
if (this.state.getPostsTransactionState !== "SUCCESS"){
this.postList = <WithBlockchainData
component={PostList}
callsInfo={this.props.blockchainData[0].returnData[4].map((postID) => {
return {
contract: 'Forum',
method: 'getPost',
params: [postID]
}
})}
postIDs={this.props.blockchainData[0].returnData[4]}
/>
this.setState({'getPostsTransactionState': "SUCCESS"});
this.fetchTopicSubject(this.props.blockchainData[0].returnData[0]);
}
}
}
@ -130,6 +121,28 @@ const mapStateToProps = state => {
}
};
const TopicContainer = drizzleConnect(Topic, mapStateToProps);
class TopicContainer extends Component {
constructor(props){
super(props);
if (!/^[0-9]+$/.test(props.params.topicId)){ //Topic ID should be a positive integer
this.props.router.push("/404");
}
this.topic = <WithBlockchainData
component={drizzleConnect(Topic, mapStateToProps)}
callsInfo={[{
contract: 'Forum',
method: 'getTopic',
params: [this.props.params.topicId]
}]}
params={this.props.params}
/>;
}
render() {
return(this.topic);
}
}
export default TopicContainer;

5
src/index.js

@ -1,6 +1,7 @@
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Router, IndexRoute, browserHistory } from 'react-router';
import { Route } from 'react-router-dom';
import { syncHistoryWithStore } from 'react-router-redux';
import { DrizzleProvider } from 'drizzle-react';
@ -31,7 +32,7 @@ render((
<Router history={history}>
<Route path="/" component={CoreLayout}>
<IndexRoute component={HomeContainer} />
<Route path="/topic/:topicId(/:topicSubject)(/:postId)"
<Route path="/topic/:topicId(/:postId)"
component={TopicContainer} />
<Route path='/profile(/:address)(/:username)'
component={ProfileContainer} />

Loading…
Cancel
Save