Post.js
1 import {Card, CardActions, CardContent, CardHeader} from '@material-ui/core'; 2 import React, {Component} from 'react'; 3 import Blockies from 'react-blockies'; 4 import CircularProgress from '@material-ui/core/CircularProgress'; 5 import DownvoteIcon from '@material-ui/icons/ExpandMore'; 6 import IconButton from '@material-ui/core/IconButton'; 7 import MoreVertIcon from '@material-ui/icons/MoreVert'; 8 import PropTypes from 'prop-types'; 9 import Typography from '@material-ui/core/Typography'; 10 import UpvoteIcon from '@material-ui/icons/ExpandLess'; 11 import dateformat from 'dateformat'; 12 import markdownJS from "markdown"; 13 import {withStyles} from '@material-ui/core/styles'; 14 15 import EmbarkJS from '../embarkArtifacts/embarkjs'; 16 import DReddit from '../embarkArtifacts/contracts/DReddit'; 17 18 const markdown = markdownJS.markdown; 19 20 const styles = theme => ({ 21 actions: { 22 marginRight: theme.spacing.unit * 5, 23 fontSize: 15, 24 display: 'flex' 25 }, 26 card: { 27 margin: theme.spacing.unit, 28 marginTop: theme.spacing.unit * 4, 29 position: 'relative' 30 }, 31 title: { 32 borderBottom: '1px solid #ccc', 33 color: '#666' 34 }, 35 spinner: { 36 position: 'absolute', 37 right: theme.spacing.unit * 3 38 } 39 }); 40 41 const ballot = { 42 NONE: 0, 43 UPVOTE: 1, 44 DOWNVOTE: 2 45 }; 46 47 const contains = (filterBy, content, title, date, owner) => { 48 if(!filterBy) return true; 49 filterBy = filterBy.trim().toLowerCase(); 50 if(filterBy === '') return true; 51 return content.toLowerCase().indexOf(filterBy) > -1 || 52 title.toLowerCase().indexOf(filterBy) > -1 || 53 date.indexOf(filterBy) > -1 || 54 owner.toLowerCase().indexOf(filterBy) > -1; 55 }; 56 57 class Post extends Component { 58 59 constructor(props){ 60 super(props); 61 62 this.state = { 63 title: '', 64 content: '', 65 isSubmitting: false, 66 canVote: true, 67 upvotes: props.upvotes, 68 downvotes: props.downvotes 69 }; 70 } 71 72 componentDidMount(){ 73 EmbarkJS.onReady(() => { 74 this._loadAttributes(); 75 }); 76 } 77 78 _loadAttributes = async () => { 79 // const ipfsHash = web3.utils.toAscii(this.props.description); 80 81 const ipfsText = await EmbarkJS.Storage.get(this.props.description); 82 83 const jsonContent = JSON.parse(ipfsText); 84 const title = jsonContent.title; 85 const content = jsonContent.content; 86 87 const canVote = await DReddit.methods.canVote(this.props.id).call(); 88 89 this.setState({ 90 title, 91 content, 92 canVote 93 }); 94 } 95 96 _vote = choice => async event => { 97 event.preventDefault(); 98 this.setState({isSubmitting: true}); 99 100 const {vote} = DReddit.methods; 101 const toSend = vote(this.props.id, choice); 102 const estimatedGas = await toSend.estimateGas(); 103 104 await toSend.send({gas: estimatedGas + 1000}); 105 106 this.setState({ 107 canVote: false, 108 upvotes: this.state.upvotes + (choice === ballot.UPVOTE ? 1 : 0), 109 downvotes: this.state.downvotes + (choice === ballot.DOWNVOTE ? 1 : 0) 110 }); 111 112 this.setState({isSubmitting: false}); 113 } 114 115 render(){ 116 const {title, content, upvotes, downvotes, isSubmitting, canVote} = this.state; 117 const {creationDate, classes, owner, filterBy} = this.props; 118 const disabled = isSubmitting || !canVote; 119 const formattedDate = dateformat(new Date(creationDate * 1000), "yyyy-mm-dd HH:MM:ss"); 120 const mdText = markdown.toHTML(content); 121 122 const display = contains(filterBy, content, title, formattedDate, owner); 123 124 return display&& <Card className={classes.card}> 125 <CardHeader title={owner} subheader={formattedDate} 126 avatar={ 127 <Blockies seed={owner} size={7} scale={5} /> 128 } 129 action={ 130 <IconButton> 131 <MoreVertIcon /> 132 </IconButton> 133 } /> 134 <CardContent> 135 <Typography variant="h6" className={classes.title} gutterBottom> 136 {title} 137 </Typography> 138 <Typography component="div" dangerouslySetInnerHTML={{__html: mdText}} /> 139 </CardContent> 140 <CardActions disableActionSpacing> 141 <IconButton className={classes.actions} disabled={disabled} onClick={this._vote(ballot.UPVOTE)}> 142 <UpvoteIcon /> 143 {upvotes} 144 </IconButton> 145 <IconButton className={classes.actions} disabled={disabled} onClick={this._vote(ballot.DOWNVOTE)}> 146 <DownvoteIcon /> 147 {downvotes} 148 </IconButton> 149 { isSubmitting && <CircularProgress size={14} className={classes.spinner} /> } 150 </CardActions> 151 </Card>; 152 } 153 154 } 155 156 Post.propTypes = { 157 filterBy: PropTypes.string, 158 upvotes: PropTypes.number.isRequired, 159 downvotes: PropTypes.number.isRequired, 160 classes: PropTypes.object.isRequired, 161 id: PropTypes.number.isRequired, 162 owner: PropTypes.string.isRequired, 163 creationDate: PropTypes.string.isRequired, 164 description: PropTypes.string.isRequired 165 }; 166 167 168 export default withStyles(styles)(Post);