diff --git a/src/assets/css/App.css b/src/assets/css/App.css
index d985067..da787e8 100644
--- a/src/assets/css/App.css
+++ b/src/assets/css/App.css
@@ -6,10 +6,6 @@ html, body {
height: 100%;
}
-body {
- font-family: 'Roboto', sans-serif;
-}
-
strong {
font-weight: bold !important;
}
@@ -57,6 +53,11 @@ strong {
right: 0;
}
+.sidebar-message {
+ margin: 0px 5px 12px 12px;
+ padding: 0px;
+}
+
.view-container {
width: 100%;
height: 100%;
diff --git a/src/assets/css/index.css b/src/assets/css/index.css
index b4cc725..ea1e941 100644
--- a/src/assets/css/index.css
+++ b/src/assets/css/index.css
@@ -1,5 +1,4 @@
body {
margin: 0;
padding: 0;
- font-family: sans-serif;
}
diff --git a/src/assets/css/progress-bar.css b/src/assets/css/progress-bar.css
new file mode 100644
index 0000000..aa8386d
--- /dev/null
+++ b/src/assets/css/progress-bar.css
@@ -0,0 +1,108 @@
+/* Progress Bar */
+
+.progress-bar-container {
+ position: absolute;
+ top: 54px;
+ left: 0px;
+ width: 100%;
+}
+
+.progress {
+ position: relative;
+ height: 4px;
+ display: block;
+ width: 100%;
+ background-color: #acece6;
+ border-radius: 2px;
+ background-clip: padding-box;
+ margin: 0.5rem 0 1rem 0;
+ overflow: hidden;
+}
+
+.progress .indeterminate {
+ background-color: #00b5ad;
+}
+
+.progress .indeterminate:before {
+ content: '';
+ position: absolute;
+ background-color: inherit;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ will-change: left, right;
+ -webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
+ animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
+}
+
+.progress .indeterminate:after {
+ content: '';
+ position: absolute;
+ background-color: inherit;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ will-change: left, right;
+ -webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
+ animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
+ -webkit-animation-delay: 1.15s;
+ animation-delay: 1.15s;
+}
+
+@-webkit-keyframes indeterminate {
+ 0% {
+ left: -35%;
+ right: 100%;
+ }
+ 60% {
+ left: 100%;
+ right: -90%;
+ }
+ 100% {
+ left: 100%;
+ right: -90%;
+ }
+}
+
+@keyframes indeterminate {
+ 0% {
+ left: -35%;
+ right: 100%;
+ }
+ 60% {
+ left: 100%;
+ right: -90%;
+ }
+ 100% {
+ left: 100%;
+ right: -90%;
+ }
+}
+@-webkit-keyframes indeterminate-short {
+ 0% {
+ left: -200%;
+ right: 100%;
+ }
+ 60% {
+ left: 107%;
+ right: -8%;
+ }
+ 100% {
+ left: 107%;
+ right: -8%;
+ }
+}
+@keyframes indeterminate-short {
+ 0% {
+ left: -200%;
+ right: 100%;
+ }
+ 60% {
+ left: 107%;
+ right: -8%;
+ }
+ 100% {
+ left: 107%;
+ right: -8%;
+ }
+}
\ No newline at end of file
diff --git a/src/assets/css/start-topic-container.css b/src/assets/css/start-topic-container.css
index 44370a4..dd11687 100644
--- a/src/assets/css/start-topic-container.css
+++ b/src/assets/css/start-topic-container.css
@@ -3,8 +3,4 @@
.topic-form {
width: 100%;
margin: 20px 0px;
-}
-
-.markdown-preview {
- padding: 0px 20px;
}
\ No newline at end of file
diff --git a/src/assets/css/topic-container.css b/src/assets/css/topic-container.css
index 7a8bad1..0467228 100644
--- a/src/assets/css/topic-container.css
+++ b/src/assets/css/topic-container.css
@@ -1,5 +1,10 @@
/* POSTS LIST SCREEN */
+.posts-list-spacer {
+ margin-bottom: 85px;
+ height: 0px;
+}
+
.post {
width: 100%;
background-color: #FFFFFF;
diff --git a/src/components/NewPost.js b/src/components/NewPost.js
index 380ebc8..5c240f2 100644
--- a/src/components/NewPost.js
+++ b/src/components/NewPost.js
@@ -1,7 +1,5 @@
-import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import uuidv4 from 'uuid/v4';
+import { drizzleConnect } from 'drizzle-react';
import { Grid, Form, TextArea, Button, Icon, Divider } from 'semantic-ui-react'
@@ -9,8 +7,7 @@ import TimeAgo from 'react-timeago';
import UserAvatar from 'react-user-avatar';
import ReactMarkdown from 'react-markdown';
-const contract = "Forum";
-const contractMethod = "createPost";
+import { createPost } from '../redux/actions/transactionsMonitorActions';
class NewPost extends Component {
constructor(props, context) {
@@ -23,20 +20,13 @@ class NewPost extends Component {
this.newPostOuterRef = React.createRef();
- this.transactionProgressText = [];
- this.drizzle = context.drizzle;
-
this.state = {
postSubjectInput: this.props.subject ? this.props.subject : "",
postContentInput: '',
postSubjectInputEmptySubmit: false,
postContentInputEmptySubmit: false,
previewEnabled: false,
- previewDate: "",
- creatingPost: false,
- transactionState: null,
- savingToOrbitDB: null,
- transactionOutputTimerActive: false
+ previewDate: ""
};
}
@@ -49,13 +39,14 @@ class NewPost extends Component {
return;
}
- this.stackId = this.drizzle.contracts[contract].methods[contractMethod].cacheSend(this.props.topicID);
- this.transactionProgressText.push(
);
- this.transactionProgressText.push("Waiting for transaction acceptance...");
- this.setState({
- 'creatingPost': true,
- 'transactionState': "ACCEPTANCE_PENDING"
- });
+ this.props.store.dispatch(
+ createPost(this.props.topicID, ((returnData) => {
+ this.topicIDFetched = returnData.topicID;
+ this.postIDFetched = returnData.postID;
+ this.pushToDatabase();
+ }))
+ );
+ this.props.onPostCreated();
}
async pushToDatabase() {
@@ -63,7 +54,6 @@ class NewPost extends Component {
subject: this.state.postSubjectInput,
content: this.state.postContentInput
});
- this.setState({'savingToOrbitDB': "SUCCESS"});
}
handleInputChange(event) {
@@ -90,14 +80,6 @@ class NewPost extends Component {
render() {
return (
- {this.state.creatingPost &&
-
-
-
- {this.transactionProgressText}
-
-
- }
#{this.props.postIndex}
@@ -184,156 +166,6 @@ class NewPost extends Component {
);
}
- componentWillReceiveProps(){
- if(this.state.creatingPost && !this.state.transactionOutputTimerActive){
- /* User submitted a new Post */
-
- if (this.state.transactionState === "ACCEPTANCE_PENDING" &&
- this.props.transactionStack[this.stackId]) {
- /* User confirmed the transaction */
-
- //Gets transaciton's hash
- this.txHash = this.props.transactionStack[this.stackId];
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push("Transaction in progress: txHash = " + this.txHash);
- this.setState({'transactionState': "IN_PROGRESS"});
- }
- else if (this.state.transactionState === "IN_PROGRESS") {
- if (this.props.transactions[this.txHash].status === "success"){
- /* Transaction completed successfully */
-
- //Gets post's id returned by contract
- let postData = this.props.transactions[this.txHash].receipt.events.PostCreated
- .returnValues;
- this.topicIDFetched = postData.topicID;
- this.postIDFetched = postData.postID;
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Transaction completed successfully.
-
- );
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- TopicID = {this.topicIDFetched}, PostID = {this.postIDFetched}
-
- );
- this.setState({'transactionState': "SUCCESS"});
- } else if (this.props.transactions[this.txHash].status === "error"){
- /* Transaction failed to complete */
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Transaction failed to complete with error:
-
- );
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- {this.props.transactions[this.txHash].error}
-
- );
- this.setState({
- 'transactionState': "ERROR",
- 'transactionOutputTimerActive': true
- });
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingPost': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- this.props.onPostCreated();
- }, 5000);
- }
- }
- else if (this.state.transactionState === "SUCCESS") {
- /* Transaction completed successfully */
-
- //Tries to store data in OrbitDB
- this.pushToDatabase();
- if (this.state.savingToOrbitDB === "SUCCESS"){
- /* Data successfully saved in OrbitDB */
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Post successfully saved in OrbitDB.
-
- );
- this.setState({'transactionOutputTimerActive': true});
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingPost': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- this.props.onPostCreated();
- }, 5000);
- }
- else if (this.state.savingToOrbitDB === "ERROR"){
- /* Failed to save data in OrbitDB */
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- An error occurred while trying to save post in OrbitDB.
-
- );
- this.setState({'transactionOutputTimerActive': true});
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingPost': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- this.props.onPostCreated();
- }, 5000);
- }
- }
- else if (this.state.transactionState === "ACCEPTANCE_PENDING" &&
- this.props.transactions.undefined !== undefined &&
- this.props.transactions.undefined.status === "error"){
- /* User probably canceled the transaction */
-
- //TODO user can't post after this!
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Transaction canceled.
-
- );
- this.setState({'transactionState': "SUCCESS"});
- this.setState({'transactionOutputTimerActive': true});
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingPost': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- this.props.onPostCreated();
- }, 5000);
- }
- }
- }
-
componentDidUpdate(prevProps, prevState){
if (!this.state.previewEnabled && prevState.previewEnabled){
this.newPostOuterRef.current.scrollIntoView(true);
@@ -345,14 +177,8 @@ class NewPost extends Component {
}
}
-NewPost.contextTypes = {
- drizzle: PropTypes.object
-};
-
const mapStateToProps = state => {
return {
- transactions: state.transactions,
- transactionStack: state.transactionStack,
orbitDB: state.orbitDB,
user: state.user
}
diff --git a/src/components/PostList.js b/src/components/PostList.js
index 8ca570e..86f8fc6 100644
--- a/src/components/PostList.js
+++ b/src/components/PostList.js
@@ -23,7 +23,7 @@ const PostList = (props) => {
});
return (
-
+
{props.recentToTheTop
?posts.slice(0).reverse()
:posts
diff --git a/src/components/ProfileInformation.js b/src/components/ProfileInformation.js
index 6d96eee..5c7ca37 100644
--- a/src/components/ProfileInformation.js
+++ b/src/components/ProfileInformation.js
@@ -17,46 +17,47 @@ const ProfileInformation = (props) => {
transaction = props.blockchainData
.find(transaction => transaction.callInfo.method === "getOrbitDBId")
let orbitDBId = transaction ? transaction.returnData : "";
- return (
-
- {props.avatarUrl &&
}
-
-
-
- Username: |
- {username} |
-
-
- Account address: |
- {props.address} |
-
-
- OrbitDB: |
- {orbitDBId} |
-
-
- Number of topics created: |
- {props.numberOfTopics} |
-
+
+ return (
+
+ {props.avatarUrl &&
}
+
+
+
+ Username: |
+ {username} |
+
+
+ Account address: |
+ {props.address} |
+
+
+ OrbitDB: |
+ {orbitDBId} |
+
+
+ Number of topics created: |
+ {props.numberOfTopics} |
+
+
+ Number of posts: |
+ {props.numberOfPosts} |
+
+ {dateOfRegister &&
- Number of posts: |
- {props.numberOfPosts} |
+ Member since: |
+ {epochTimeConverter(dateOfRegister)} |
- {props.dateOfRegister &&
-
- Member since: |
- {epochTimeConverter(dateOfRegister)} |
-
- }
-
-
- {props.self && }
-
- );
+ }
+
+
+ {props.self && }
+
+ );
};
export default ProfileInformation;
\ No newline at end of file
diff --git a/src/components/WithBlockchainData.js b/src/components/WithBlockchainData.js
index 640e5da..414b118 100644
--- a/src/components/WithBlockchainData.js
+++ b/src/components/WithBlockchainData.js
@@ -12,11 +12,9 @@ class WithBlockchainData extends Component {
this.forwardedProps = rest;
}
- this.checkContractUpdates = this.checkContractUpdates.bind(this);
-
this.drizzle = context.drizzle;
this.dataKeys = [];
- this.blockchainData = this.callsInfo.map((call) => {
+ let blockchainData = this.callsInfo.map((call) => {
return ({
callInfo: call,
status: "initialized",
@@ -24,52 +22,53 @@ class WithBlockchainData extends Component {
});
});
+ //Initial call
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";
+ blockchainData[i].status = "pending";
}
this.state = {
- transactionsState: new Array(this.callsInfo.length).fill("pending")
+ callState: new Array(this.callsInfo.length).fill("pending"),
+ blockchainData: blockchainData
}
}
render() {
- let {component, callsInfo, ...rest } = this.props;
+ let {component, callsInfo, ...rest } = this.props; //Update rest arguments
return (
-
+
);
}
- componentDidMount() {
- this.intervalChecker = setInterval(this.checkContractUpdates, 10); //HOWMUCHMUCHACHO???
- }
-
- componentWillUnmount() {
- clearInterval(this.intervalChecker);
- }
-
- checkContractUpdates() {
+ componentDidUpdate(){
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!!
+ let dataFetched = (currentDrizzleState
+ .contracts[this.callsInfo[i].contract][this.callsInfo[i].method][this.dataKeys[i]]);
+ if (dataFetched && dataFetched.value !== this.state.blockchainData[i].returnData){
+ /* There are new data in the blockchain*/
+
+ //Immutable update
+ let newBlockchainData = this.state.blockchainData.map((callData, index) => {
+ if (index !== i) return callData;
+ return {
+ ...callData,
+ returnData: dataFetched.value,
+ status: "success"
+ }
+ })
+
+ let newStates = this.state.callState.slice();
+ newStates[i] = "success"
+ this.setState({
+ callState: newStates,
+ blockchainData: newBlockchainData
+ });
+ }
}
}
}
diff --git a/src/containers/BoardContainer.js b/src/containers/BoardContainer.js
index ba040f5..94c2597 100644
--- a/src/containers/BoardContainer.js
+++ b/src/containers/BoardContainer.js
@@ -7,13 +7,20 @@ import { Header } from 'semantic-ui-react';
import WithBlockchainData from '../components/WithBlockchainData';
import TopicList from '../components/TopicList';
import FloatingButton from '../components/FloatingButton';
-import LoadingSpinner from '../components/LoadingSpinner';
+
+import { showProgressBar, hideProgressBar } from '../redux/actions/userInterfaceActions';
class Board extends Component {
constructor(props) {
super(props);
+ this.props.store.dispatch(showProgressBar());
+
this.handleCreateTopicClick = this.handleCreateTopicClick.bind(this);
+
+ this.state = {
+ pageLoaded: false
+ }
}
handleCreateTopicClick() {
@@ -22,17 +29,14 @@ class Board extends Component {
render() {
var boardContents;
- if (!this.props.blockchainData[0].returnData) {
- boardContents = (
-
- );
- } else if (this.props.blockchainData[0].returnData !== '0'){
+ if (this.props.blockchainData[0].returnData !== '0'){
this.topicIDs = [];
for (var i = 0; i < this.props.blockchainData[0].returnData; i++) {
this.topicIDs.push(i);
}
boardContents = ([
,
+
,
this.props.user.hasSignedUp &&
@@ -45,7 +49,7 @@ class Board extends Component {
There are no topics yet!
- Sign up to be the first post.
+ Sign up to be the first to post.
);
@@ -68,10 +72,16 @@ class Board extends Component {
return (
);
}
+
+ componentDidUpdate(){
+ if (!this.state.pageLoaded && this.props.blockchainData[0].returnData){
+ this.props.store.dispatch(hideProgressBar());
+ this.setState({ pageLoaded: true });
+ }
+ }
}
Board.contextTypes = {
diff --git a/src/containers/SignUpContainer.js b/src/containers/SignUpContainer.js
index c4ec30f..eb68d63 100644
--- a/src/containers/SignUpContainer.js
+++ b/src/containers/SignUpContainer.js
@@ -5,6 +5,16 @@ import UsernameFormContainer from './UsernameFormContainer';
import { Header } from 'semantic-ui-react';
class SignUp extends Component {
+ constructor(props){
+ super(props);
+
+ this.signedUp = this.signedUp.bind(this);
+ }
+
+ signedUp(){
+ this.props.router.push("/home");
+ }
+
render() {
return (
this.props.user.hasSignedUp
@@ -22,14 +32,13 @@ class SignUp extends Component {
Account address: {this.props.user.address}
-
+
)
);
}
}
-// May still need this even with data function to refresh component on updates for this contract.
const mapStateToProps = state => {
return {
user: state.user
diff --git a/src/containers/StartTopicContainer.js b/src/containers/StartTopicContainer.js
index 3e67f49..5a85499 100644
--- a/src/containers/StartTopicContainer.js
+++ b/src/containers/StartTopicContainer.js
@@ -1,14 +1,11 @@
-import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react';
+import { drizzleConnect } from 'drizzle-react';
import PropTypes from 'prop-types';
-import uuidv4 from 'uuid/v4';
import { Form, TextArea, Button, Icon } from 'semantic-ui-react'
-
import NewTopicPreview from '../components/NewTopicPreview'
-const contract = "Forum";
-const contractMethod = "createTopic";
+import { createTopic } from '../redux/actions/transactionsMonitorActions';
class StartTopic extends Component {
constructor(props, context) {
@@ -19,9 +16,6 @@ class StartTopic extends Component {
this.validateAndPost = this.validateAndPost.bind(this);
this.pushToDatabase = this.pushToDatabase.bind(this);
- this.transactionProgressText = [];
- this.drizzle = context.drizzle;
-
this.state = {
topicSubjectInput: '',
topicMessageInput: '',
@@ -29,10 +23,7 @@ class StartTopic extends Component {
topicMessageInputEmptySubmit: false,
previewEnabled: false,
previewDate: "",
- creatingTopic: false,
- transactionState: null,
- savingToOrbitDB: null,
- transactionOutputTimerActive: false
+ creatingTopic: false
};
}
@@ -42,16 +33,19 @@ class StartTopic extends Component {
topicSubjectInputEmptySubmit: this.state.topicSubjectInput === '',
topicMessageInputEmptySubmit: this.state.topicMessageInput === ''
});
-
return;
}
- this.stackId = this.drizzle.contracts[contract].methods[contractMethod].cacheSend();
- this.transactionProgressText.push(
);
- this.transactionProgressText.push("Waiting for transaction acceptance...");
+ this.props.store.dispatch(
+ createTopic(((returnData) => {
+ this.topicIDFetched = returnData.topicID;
+ this.postIDFetched = returnData.postID;
+ this.pushToDatabase();
+ this.props.router.push("/topic/" + this.topicIDFetched);
+ }))
+ );
this.setState({
- 'creatingTopic': true,
- 'transactionState': "ACCEPTANCE_PENDING"
+ 'creatingTopic': true
});
}
@@ -64,7 +58,6 @@ class StartTopic extends Component {
subject: this.state.topicSubjectInput,
content: this.state.topicMessageInput
});
- this.setState({'savingToOrbitDB': "SUCCESS"});
}
handleInputChange(event) {
@@ -97,13 +90,13 @@ class StartTopic extends Component {
var previewEditText = this.state.previewEnabled ? "Edit" : "Preview";
return (
- {this.state.creatingTopic &&
+ {/*this.state.creatingTopic &&
{this.transactionProgressText}
-
+
*/
}
{this.state.previewEnabled &&
);
}
-
- componentWillReceiveProps(){
- if(this.state.creatingTopic && !this.state.transactionOutputTimerActive){
- /* User submitted a new Topic */
-
- if (this.state.transactionState === "ACCEPTANCE_PENDING" &&
- this.props.transactionStack[this.stackId]) {
- /* User confirmed the transaction */
-
- //Gets transaciton's hash
- this.txHash = this.props.transactionStack[this.stackId];
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push("Transaction in progress: txHash = " + this.txHash);
- this.setState({'transactionState': "IN_PROGRESS"});
- }
- else if (this.state.transactionState === "IN_PROGRESS") {
- if (this.props.transactions[this.txHash].status === "success"){
- /* Transaction completed successfully */
-
- //Gets topic's id returned by contract
- let topicData = this.props.transactions[this.txHash].receipt.events.TopicCreated
- .returnValues;
- this.topicIDFetched = topicData.topicID;
- this.postIDFetched = topicData.postID;
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Transaction completed successfully.
-
- );
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- TopicID = {this.topicIDFetched}, PostID = {this.postIDFetched}
-
- );
- this.setState({'transactionState': "SUCCESS"});
- } else if (this.props.transactions[this.txHash].status === "error"){
- /* Transaction failed to complete */
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Transaction failed to complete with error:
-
- );
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- {this.props.transactions[this.txHash].error}
-
- );
- this.setState({
- 'transactionState': "ERROR",
- 'transactionOutputTimerActive': true
- });
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingTopic': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- }, 5000);
- }
- }
- else if (this.state.transactionState === "SUCCESS") {
- /* Transaction completed successfully */
-
- //Tries to store data in OrbitDB
- this.pushToDatabase();
- if (this.state.savingToOrbitDB === "SUCCESS"){
- /* Data successfully saved in OrbitDB */
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Topic successfully saved in OrbitDB.
-
- );
- this.setState({'transactionOutputTimerActive': true});
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingTopic': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- this.props.router.push("/topic/" + this.topicIDFetched);
- }, 5000);
- }
- else if (this.state.savingToOrbitDB === "ERROR"){
- /* Failed to save data in OrbitDB */
-
- //Updates output and state
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- An error occurred while trying to save post in OrbitDB.
-
- );
- this.setState({'transactionOutputTimerActive': true});
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingTopic': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- }, 5000);
- }
- }
- else if (this.state.transactionState === "ACCEPTANCE_PENDING" &&
- this.props.transactions.undefined !== undefined &&
- this.props.transactions.undefined.status === "error"){
- /* User probably canceled the transaction */
-
- //TODO user can't post after this!
- this.transactionProgressText.push(
);
- this.transactionProgressText.push(
-
- Transaction canceled.
-
- );
- this.setState({'transactionState': "SUCCESS"});
- this.setState({'transactionOutputTimerActive': true});
- this.transactionOutputTimer = setTimeout(() => {
- this.transactionProgressText = [];
- this.setState({
- 'creatingTopic': false,
- 'transactionState': null,
- 'savingToOrbitDB': null,
- 'transactionOutputTimerActive': false
- });
- }, 5000);
- }
- }
- }
}
StartTopic.contextTypes = {
- drizzle: PropTypes.object,
router: PropTypes.object
};
diff --git a/src/containers/TopicContainer.js b/src/containers/TopicContainer.js
index 88001ee..1b3ccdf 100644
--- a/src/containers/TopicContainer.js
+++ b/src/containers/TopicContainer.js
@@ -1,47 +1,44 @@
-import { drizzleConnect } from 'drizzle-react';
import React, { Component } from 'react';
-import PropTypes from 'prop-types';
+import { drizzleConnect } from 'drizzle-react';
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';
+
+import { showProgressBar, hideProgressBar } from '../redux/actions/userInterfaceActions';
class Topic extends Component {
- constructor(props, context) {
+ constructor(props) {
super(props);
+ this.props.store.dispatch(showProgressBar());
+
this.fetchTopicSubject = this.fetchTopicSubject.bind(this);
- this.handleClick = this.handleClick.bind(this);
+ this.togglePostingState = this.togglePostingState.bind(this);
this.postCreated = this.postCreated.bind(this);
- this.drizzle = context.drizzle;
-
this.state = {
topicID: this.props.params.topicId,
- topicSubject: this.props.params.topicSubject ? this.props.params.topicSubject : null,
+ topicSubject: null,
postFocus: this.props.params.postId && /^[0-9]+$/.test(this.props.params.postId)
? this.props.params.postId
: null,
- getPostsTransactionState: null,
+ fetchTopicSubjectStatus: null,
posting: false
};
}
async fetchTopicSubject(orbitDBAddress) {
- /*const fullAddress = this.topicsData[this.state.topicID][1];
- const store = await this.props.orbitDB.orbitdb.keyvalue(JSON.stringify(fullAddress));
- await store.load();
- var som = store.get(JSON.stringify(this.state.topicID));
- this.topicsSubjects[this.state.topicID] = som['subject'];
- this.topicsSubjectsFetchStatus[this.state.topicID] = "fetched";*/
-
- var som =this.props.orbitDB.topicsDB.get(this.state.topicID);
- this.setState({'topicSubject': som['subject']});
+ var orbitData =this.props.orbitDB.topicsDB.get(this.state.topicID);
+ this.props.store.dispatch(hideProgressBar());
+ this.setState({
+ 'topicSubject': orbitData['subject'],
+ fetchTopicSubjectStatus: "fetched"
+ });
}
- handleClick(event) {
+ togglePostingState(event) {
if (event){
event.preventDefault();
}
@@ -52,31 +49,28 @@ class Topic extends Component {
postCreated(){
this.setState(prevState => ({
- getPostsTransactionState: null,
posting: false
}));
+ //TODO reload topic
}
render() {
var topicContents;
- if (this.state.getPostsTransactionState !== "SUCCESS") {
- topicContents = (
-
- );
- } else {
+ if (this.props.blockchainData[0].status === "success") {
topicContents = (
- (
- {this.postList}
+ (
+
{this.state.posting &&
{this.handleClick()}}
+ onCancelClick={() => {this.togglePostingState()}}
onPostCreated={() => {this.postCreated()}}
/>
}
+
{this.props.user.hasSignedUp && !this.state.posting &&
-
+
}
)
)
@@ -92,32 +86,16 @@ class Topic extends Component {
);
}
- componentWillReceiveProps() {
+ componentDidUpdate() {
if (this.props.blockchainData[0].status === "success") {
- if (this.state.getPostsTransactionState !== "SUCCESS"){
- this.postList =
{
- return {
- contract: 'Forum',
- method: 'getPost',
- params: [postID]
- }
- })}
- postIDs={this.props.blockchainData[0].returnData[4]}
- />
-
- this.setState({'getPostsTransactionState': "SUCCESS"});
+ if (this.state.fetchTopicSubjectStatus === null){
+ this.setState({ fetchTopicSubjectStatus: "fetching"})
this.fetchTopicSubject(this.props.blockchainData[0].returnData[0]);
}
}
}
}
-Topic.contextTypes = {
- drizzle: PropTypes.object
-};
-
const mapStateToProps = state => {
return {
user: state.user,
@@ -132,20 +110,20 @@ class TopicContainer extends Component {
if (!/^[0-9]+$/.test(props.params.topicId)){ //Topic ID should be a positive integer
this.props.router.push("/404");
}
-
- this.topic = ;
- }
-
- render() {
- return(this.topic);
+ params={this.props.params}
+ />
+ );
}
}
diff --git a/src/containers/TransactionsMonitorContainer.js b/src/containers/TransactionsMonitorContainer.js
new file mode 100644
index 0000000..4e0ca91
--- /dev/null
+++ b/src/containers/TransactionsMonitorContainer.js
@@ -0,0 +1,172 @@
+import React, { Component } from 'react';
+import { drizzleConnect } from 'drizzle-react';
+import PropTypes from 'prop-types';
+
+import { Message } from 'semantic-ui-react';
+
+import { updateTransaction } from '../redux/actions/transactionsMonitorActions';
+
+class RightSideBar extends Component {
+ constructor(props, context) {
+ super(props);
+
+ this.handleMessageDismiss = this.handleMessageDismiss.bind(this);
+
+ this.drizzle = context.drizzle;
+ this.transactionsStackIds = [];
+ this.transactionsTxHashes = [];
+
+ this.state = {
+ transactionsCompletionTime: [],
+ isTransactionMessageActive: []
+ }
+ }
+
+ handleMessageDismiss(messageIndex) {
+ let isTransactionMessageActiveShallowCopy = this.state.isTransactionMessageActive.slice();
+ isTransactionMessageActiveShallowCopy[messageIndex] = false;
+ this.setState({
+ isTransactionMessageActive: isTransactionMessageActiveShallowCopy
+ });
+ }
+
+ render() {
+ let transactionMessages = this.props.transactionsQueue.map((transaction, index) => {
+ if (!this.state.isTransactionMessageActive[index]){
+ return null;
+ }
+ let color = 'black';
+ let message = [];
+
+ while(true) {
+ if (transaction.status === 'initialized') break;
+ message.push("New transaction has been queued and is waiting your confirmation.");
+
+ if (transaction.status === 'acceptance_pending') break;
+ message.push(
);
+ message.push("- transaction confirmed");
+
+ if (transaction.status === 'mining_pending') break;
+ message.push(
);
+ message.push("- transaction mined");
+
+ if (transaction.status === 'success') {
+ color = 'green';
+ message.push(
);
+ message.push("- transaction completed successfully");
+ break;
+ }
+ if (transaction.status === 'error') {
+ color = 'red';
+ message.push(
);
+ message.push("Transaction failed to complete!");
+ break;
+ }
+ }
+
+ return (
+
+ {this.handleMessageDismiss(index)}}>
+ {message}
+
+
+ );
+ });
+
+ return (transactionMessages);
+ }
+
+ componentDidUpdate(){
+ for (var index = 0; index < this.props.transactionsQueue.length; ++index) {
+ let transaction = this.props.transactionsQueue[index];
+
+ if (transaction.status === 'initialized' &&
+ this.transactionsStackIds[index] === undefined){
+ /* User submitted a new transaction */
+
+ let isTransactionMessageActiveShallowCopy = this.state
+ .isTransactionMessageActive.slice();
+ isTransactionMessageActiveShallowCopy[index] = true;
+ this.setState({
+ isTransactionMessageActive: isTransactionMessageActiveShallowCopy
+ });
+
+ this.transactionsStackIds[index] = (this.drizzle
+ .contracts[transaction.contract]
+ .methods[transaction.method]
+ .cacheSend(...(transaction.params)));
+ this.props.store.dispatch(updateTransaction(index, {
+ status: 'acceptance_pending'
+ }));
+ } else if (transaction.status === 'acceptance_pending'){
+ if (this.props.transactionStack[this.transactionsStackIds[index]]){
+ /* User confirmed the transaction */
+
+ //Gets transaciton's hash
+ this.transactionsTxHashes[index] = (this.props
+ .transactionStack[this.transactionsStackIds[index]]);
+ this.props.store.dispatch(updateTransaction(index, {
+ status: 'mining_pending'
+ }));
+ }
+ } else if (transaction.status === 'mining_pending'){
+ if (this.props.transactions[this.transactionsTxHashes[index]]
+ .status === "success"){
+ /* Transaction completed successfully */
+
+ //Gets returned data by contract
+ let data = this.props.transactions[this.transactionsTxHashes[index]]
+ .receipt.events[transaction.event].returnValues;
+
+ this.props.store.dispatch(updateTransaction(index, {
+ status: 'success',
+ returnData: data
+ }));
+
+ let transactionsCompletionTimeShallowCopy = this.state
+ .transactionsCompletionTime.slice();
+ transactionsCompletionTimeShallowCopy[index] = new Date().getTime();
+ this.setState({
+ transactionsCompletionTime: transactionsCompletionTimeShallowCopy
+ });
+ if (this.props.transactionsQueue[index].callback){
+ this.props.transactionsQueue[index].callback(data);
+ }
+ } else if (this.props.transactions[this.transactionsTxHashes[index]]
+ .status === "error"){
+ /* Transaction failed to complete */
+
+ this.props.store.dispatch(updateTransaction(index, {
+ status: 'error'
+ }));
+
+ let transactionsCompletionTimeShallowCopy = this.state
+ .transactionsCompletionTime.slice();
+ transactionsCompletionTimeShallowCopy[index] = new Date().getTime();
+ this.setState({
+ transactionsCompletionTime: transactionsCompletionTimeShallowCopy
+ });
+ if (this.props.transactionsQueue[index].callback){
+ this.props.transactionsQueue[index].callback(null);
+ }
+ }
+ }
+ }
+ }
+}
+
+RightSideBar.contextTypes = {
+ drizzle: PropTypes.object
+};
+
+const mapStateToProps = state => {
+ return {
+ transactionsQueue: state.transactionsQueue.transactions,
+ transactions: state.transactions,
+ transactionStack: state.transactionStack
+ }
+};
+
+const RightSideBarContainer = drizzleConnect(RightSideBar, mapStateToProps);
+
+export default RightSideBarContainer;
\ No newline at end of file
diff --git a/src/containers/UsernameFormContainer.js b/src/containers/UsernameFormContainer.js
index d38eee8..d6a36ce 100644
--- a/src/containers/UsernameFormContainer.js
+++ b/src/containers/UsernameFormContainer.js
@@ -3,11 +3,12 @@ import { drizzleConnect } from 'drizzle-react';
import PropTypes from 'prop-types';
import { Button, Message, Form, Dimmer, Loader, Header } from 'semantic-ui-react';
+
import { createDatabases } from './../util/orbit';
+import { updateUsername } from '../redux/actions/transactionsMonitorActions';
const contract = "Forum";
const signUpMethod = "signUp";
-const updateUsernameMethod ="updateUsername";
class UsernameFormContainer extends Component {
constructor(props, context) {
@@ -22,7 +23,7 @@ class UsernameFormContainer extends Component {
this.state = {
usernameInput: '',
error: false,
- completingAction: false
+ signingUp: false
};
}
@@ -39,14 +40,26 @@ class UsernameFormContainer extends Component {
}
async completeAction() {
- this.setState({ completingAction: true });
- if(this.props.user.hasSignedUp)
- this.contracts[contract].methods[updateUsernameMethod].cacheSend(...[this.state.usernameInput]);
- else
- {
+ if(this.props.user.hasSignedUp){
+ this.props.store.dispatch(updateUsername(...[this.state.usernameInput], null));
+ } else {
+ this.setState({ signingUp: true });
const orbitdbInfo = await createDatabases();
- this.contracts[contract].methods[signUpMethod].cacheSend(...[this.state.usernameInput, orbitdbInfo.id,
- orbitdbInfo.topicsDB, orbitdbInfo.postsDB, orbitdbInfo.publicKey, orbitdbInfo.privateKey]);
+ this.contracts[contract].methods[signUpMethod]
+ .cacheSend(...[this.state.usernameInput,
+ orbitdbInfo.id,
+ orbitdbInfo.topicsDB,
+ orbitdbInfo.postsDB,
+ orbitdbInfo.publicKey,
+ orbitdbInfo.privateKey
+ ]);
+ }
+ this.setState({ usernameInput: '' });
+ }
+
+ componentWillReceiveProps(nextProps){
+ if (this.state.signingUp && nextProps.user.hasSignedUp){
+ this.props.signedUp();
}
}
@@ -77,7 +90,7 @@ class UsernameFormContainer extends Component {
/>
-
+
Magic elfs are processing your nobel request.
diff --git a/src/layouts/CoreLayout/CoreLayout.js b/src/layouts/CoreLayout/CoreLayout.js
index 41127e2..6739f43 100644
--- a/src/layouts/CoreLayout/CoreLayout.js
+++ b/src/layouts/CoreLayout/CoreLayout.js
@@ -1,5 +1,8 @@
import React, { Component } from 'react';
+import { drizzleConnect } from 'drizzle-react';
+
import NavBar from '../../components/NavBar';
+import TransactionsMonitorContainer from '../../containers/TransactionsMonitorContainer';
// Styles
import '../../assets/fonts/fontawesome-free-5.0.13/fontawesome-all.js';
@@ -11,12 +14,19 @@ import '../../assets/css/board-container.css';
import '../../assets/css/start-topic-container.css';
import '../../assets/css/topic-container.css';
import '../../assets/css/profile-container.css';
+import '../../assets/css/progress-bar.css';
class CoreLayout extends Component {
render() {
return (
+
@@ -26,6 +36,7 @@ class CoreLayout extends Component {
@@ -33,4 +44,10 @@ class CoreLayout extends Component {
}
}
-export default CoreLayout;
\ No newline at end of file
+const mapStateToProps = state => {
+ return {
+ isProgressBarVisible: state.interface.displayProgressBar
+ }
+};
+
+export default drizzleConnect(CoreLayout, mapStateToProps)
\ No newline at end of file
diff --git a/src/redux/actions/transactionsMonitorActions.js b/src/redux/actions/transactionsMonitorActions.js
new file mode 100644
index 0000000..7492a4f
--- /dev/null
+++ b/src/redux/actions/transactionsMonitorActions.js
@@ -0,0 +1,54 @@
+//Action creators
+
+export const INIT_TRANSACTION = 'INIT_TRANSACTION';
+export const UPDATE_TRANSACTION = 'UPDATE_TRANSACTION';
+
+export function updateUsername(newUsername, callback){
+ return {
+ type: INIT_TRANSACTION,
+ transactionDescriptor:
+ {
+ contract: 'Forum',
+ method: 'updateUsername',
+ params: [newUsername],
+ event: 'UsernameUpdated',
+ },
+ callback: callback
+ };
+}
+
+export function createTopic(callback){
+ return {
+ type: INIT_TRANSACTION,
+ transactionDescriptor:
+ {
+ contract: 'Forum',
+ method: 'createTopic',
+ params: [],
+ event: 'TopicCreated',
+ },
+ callback: callback
+ };
+}
+
+export function createPost(topicID, callback){
+ return {
+ type: INIT_TRANSACTION,
+ transactionDescriptor:
+ {
+ contract: 'Forum',
+ method: 'createPost',
+ params: [topicID],
+ event: 'PostCreated',
+ },
+ callback: callback
+ };
+}
+
+export function updateTransaction(transactionIndex, updateDescriptor){
+ return {
+ type: UPDATE_TRANSACTION,
+ index: transactionIndex,
+ transactionUpdates: updateDescriptor
+ };
+}
\ No newline at end of file
diff --git a/src/redux/actions/userInterfaceActions.js b/src/redux/actions/userInterfaceActions.js
new file mode 100644
index 0000000..77591e0
--- /dev/null
+++ b/src/redux/actions/userInterfaceActions.js
@@ -0,0 +1,12 @@
+//Action creators
+
+export const SHOW_PROGRESS_BAR = 'SHOW_PROGRESS_BAR';
+export const HIDE_PROGRESS_BAR = 'HIDE_PROGRESS_BAR';
+
+export function showProgressBar(){
+ return { type: 'SHOW_PROGRESS_BAR'};
+}
+
+export function hideProgressBar(){
+ return { type: 'HIDE_PROGRESS_BAR'};
+}
\ No newline at end of file
diff --git a/src/redux/reducer/reducer.js b/src/redux/reducer/reducer.js
index a18bf91..aacc6ea 100644
--- a/src/redux/reducer/reducer.js
+++ b/src/redux/reducer/reducer.js
@@ -4,12 +4,17 @@ import { drizzleReducers } from 'drizzle';
import userReducer from "./userReducer";
import contractReducer from "./contractReducer";
import orbitReducer from "../../util/orbitReducer";
+import userInterfaceReducer from "./userInterfaceReducer";
+import transactionsMonitorReducer from "./transactionsMonitorReducer";
+
const reducer = combineReducers({
routing: routerReducer,
user: userReducer,
orbitDB: orbitReducer,
forumContract: contractReducer,
+ interface: userInterfaceReducer,
+ transactionsQueue: transactionsMonitorReducer,
...drizzleReducers
});
diff --git a/src/redux/reducer/transactionsMonitorReducer.js b/src/redux/reducer/transactionsMonitorReducer.js
new file mode 100644
index 0000000..c3111cf
--- /dev/null
+++ b/src/redux/reducer/transactionsMonitorReducer.js
@@ -0,0 +1,39 @@
+import { INIT_TRANSACTION, UPDATE_TRANSACTION } from '../actions/transactionsMonitorActions';
+
+const initialState = {
+ transactions: []
+};
+
+const transactionsReducer = (state = initialState, action) => {
+ switch (action.type) {
+ case INIT_TRANSACTION:
+ let transactionsShallowCopy = state.transactions.slice();
+ transactionsShallowCopy.push({
+ status: 'initialized',
+ contract: action.transactionDescriptor.contract,
+ method: action.transactionDescriptor.method,
+ params: action.transactionDescriptor.params,
+ event: action.transactionDescriptor.event,
+ returnData: null,
+ callback: action.callback
+ });
+ return {
+ transactions: transactionsShallowCopy
+ };
+ case UPDATE_TRANSACTION:
+ return { transactions: state.transactions.map( (transaction, index) => {
+ if (index !== action.index){
+ return transaction;
+ }
+
+ return {
+ ...transaction,
+ ...action.transactionUpdates
+ }
+ })};
+ default:
+ return state;
+ }
+};
+
+export default transactionsReducer;
\ No newline at end of file
diff --git a/src/redux/reducer/userInterfaceReducer.js b/src/redux/reducer/userInterfaceReducer.js
new file mode 100644
index 0000000..e0baf4f
--- /dev/null
+++ b/src/redux/reducer/userInterfaceReducer.js
@@ -0,0 +1,22 @@
+import { SHOW_PROGRESS_BAR, HIDE_PROGRESS_BAR } from '../actions/userInterfaceActions';
+
+const initialState = {
+ displayProgressBar: false
+};
+
+const userInterfaceReducer = (state = initialState, action) => {
+ switch (action.type) {
+ case SHOW_PROGRESS_BAR:
+ return {
+ displayProgressBar: true
+ };
+ case HIDE_PROGRESS_BAR:
+ return {
+ displayProgressBar: false
+ };
+ default:
+ return state;
+ }
+};
+
+export default userInterfaceReducer;
\ No newline at end of file