1
0
mirror of https://github.com/fazo96/ipfs-boards synced 2025-03-11 21:38:38 +01:00

implemented writing comments

This commit is contained in:
Enrico Fasoli 2015-12-23 11:47:31 +01:00
parent fb9e3db821
commit 047dad1c4c
6 changed files with 139 additions and 13 deletions

View File

@ -207,6 +207,48 @@ BoardsAPI.prototype.createPost = function (post, board, done) {
}
], done)
}
BoardsAPI.prototype.createComment = function (comment, parent, done) {
try {
comment.date = parseInt((new Date()).getTime() / 1000, 10)
comment.op = this.id
comment.parent = parent
var comment_str = JSON.stringify(comment)
} catch (e) {
console.log('Error, invalid Post:', e)
return done(e)
}
if (!comment.text) return done('empty comment')
console.log('Commenting:', comment)
var dest = '/ipfs-boards-profile/comments/' + parent + '/' + comment.date + '.json'
var commenthash
asyncjs.waterfall([
// Create required directories
cb => this.ipfs.files.mkdir('/ipfs-boards-profile/comments/' + parent + '/', { p: true }, cb),
(e, cb) => {
// Remove old comment file if present
this.ipfs.files.rm(dest, { r: true }, res => cb())
},
(cb) => {
// Serialize comment and add to IPFS
this.ipfs.add(new Buffer(comment_str), cb)
},
(res, cb) => {
// Move post into mfs
console.log('added Comment to IPFS:', res.Hash)
commenthash = res.Hash
var spath = '/ipfs/' + res.Hash
this.ipfs.files.cp([spath, dest], cb)
},
(e, cb) => this.ipfs.files.stat('/', cb),
(res, cb) => {
var profile_hash = res.Hash
console.log('Publishing profile...')
this.ipfs.name.publish(profile_hash, err => {
done(err, commenthash)
})
}
], done)
}
BoardsAPI.prototype.delete = function (opts, done) {
var url = '/ipfs-boards-profile/'

View File

@ -4,6 +4,62 @@ var Icon = require('icon.jsx')
var Clock = require('clock.jsx')
var Link = require('react-router').Link
var UserID = require('userID.jsx')
var { Error, Success } = require('status-components.jsx')
var CommentEditor = React.createClass({
getInitialState () {
return { }
},
componentDidMount () {
this.init(this.props)
},
componentWillReceiveProps (props) {
this.init(props)
},
init (props) {
this.setState({ api: props.api })
},
handleChange (event) {
var obj = {}
obj[event.target.id] = event.target.value
this.setState(obj)
},
save () {
var boards = this.props.api
var comment = { text: this.state.text }
this.setState({ loading: true })
boards.createComment(comment, this.props.parent, (err, hash) => {
if (err) {
this.setState({ loading: false, error: err })
} else {
this.setState({ loading: false, success: true, hash })
}
})
},
render () {
if (this.state.error) {
return <Error error={this.state.error} />
} else if (this.state.loading) {
return <div className="center-block text-center find-content">
<Icon name="refresh" className="fa-3x center-block light fa-spin" />
<h4>Publishing Comment</h4>
</div>
} else if (this.state.success) {
var url = '/@' + this.props.adminID + '/' + this.props.board + '/' + (this.props.post || this.props.parent) + '/' + this.state.hash
return <Success title="Comment Published">
<Link className="button button-primary" to={url}>View</Link>
</Success>
} else {
return <div>
<textarea className="u-full-width" id="text" value={this.state.text} onChange={this.handleChange} placeholder="Speak your mind! Markdown is supported." />
<p><b>Note</b>: this version of the app doesn't check wether you are allowed to post on this board, so there are no guarantees that your post will be visible.</p>
<div className="buttons">
<button className="button button-primary" onClick={this.save}>Send</button>
</div>
</div>
}
}
})
var Comment = React.createClass({
getInitialState () {
@ -29,7 +85,10 @@ var Comment = React.createClass({
}
},
getComments () {
return <Comments className="shifted" parent={this.props.comment.hash} post={this.props.post} adminID={this.props.adminID} board={this.props.board} api={this.props.api} />
return <Comments className="shifted" allowReply={this.props.allowReply} parent={this.props.comment.hash} post={this.props.post} adminID={this.props.adminID} board={this.props.board} api={this.props.api} />
},
toggleReply () {
this.setState({ reply: !this.state.reply })
},
render () {
if (this.props.comment) {
@ -38,9 +97,15 @@ var Comment = React.createClass({
<UserID id={this.props.comment.op} api={this.props.api} />
<Clock date={this.props.comment.date} />
{this.getPermalink()}
{ this.props.allowReply
? <a className="nounderline" onClick={this.toggleReply}><Icon className="not-first" name="reply" /> Reply</a>
: <a/> }
{this.getParentlink()}
</div>
<Markdown source={this.props.comment.text} />
{ this.state.reply
? <CommentEditor parent={this.props.comment.hash} post={this.props.post} api={this.props.api} adminID={this.props.adminID} board={this.props.board} />
: <div/>}
<hr/>{this.getComments()}</div>
} else {
return <div><hr/>Invalid Comment<hr/></div>
@ -56,7 +121,7 @@ var Comments = React.createClass({
if (this.props.api) this.init(this.props.api)
},
componentWillReceiveProps (props) {
if (props.api) this.init(props.api)
if (props.api !== this.props.api) this.init(props.api)
},
init (boards) {
boards.getEventEmitter().on('comment for ' + this.props.parent, cmnt => {
@ -76,7 +141,7 @@ var Comments = React.createClass({
},
getComments () {
if (this.state.comments.length > 0) {
return this.state.comments.map(cmnt => (<Comment key={cmnt.hash} comment={cmnt} post={this.props.post} adminID={this.props.adminID} board={this.props.board} api={this.props.api} />))
return this.state.comments.map(cmnt => (<Comment allowReply={this.props.allowReply} key={cmnt.hash} comment={cmnt} post={this.props.post} adminID={this.props.adminID} board={this.props.board} api={this.props.api} />))
} else return <div></div>
},
render () {
@ -90,4 +155,4 @@ var Comments = React.createClass({
}
})
module.exports = { Comment, Comments }
module.exports = { Comment, Comments, CommentEditor }

View File

@ -5,7 +5,7 @@ var Link = require('react-router').Link
var Clock = require('clock.jsx')
var UserID = require('userID.jsx')
var { Error } = require('status-components.jsx')
var Comments = require('comment.jsx').Comments
var { Comments, CommentEditor } = require('comment.jsx')
module.exports = React.createClass({
getInitialState () {
@ -54,6 +54,9 @@ module.exports = React.createClass({
}
} else return <span/>
},
toggleReply () {
this.setState({ reply: !this.state.reply })
},
getContent () {
if (this.state.error) {
return <Error className="content" error={this.state.error} />
@ -75,6 +78,9 @@ module.exports = React.createClass({
<UserID id={this.state.post.op} api={this.props.api} ></UserID>
<Clock className="not-first" date={this.state.post.date} />
<Icon name="comments" className="not-first" /> <Link className="nounderline" to={this.postLink()}>View</Link>
{ this.props.allowReply
? <a className="nounderline" onClick={this.toggleReply}><Icon className="not-first" name="reply" /> Reply</a>
: <span/>}
{this.editorLink()}
</div>
</div>
@ -83,7 +89,10 @@ module.exports = React.createClass({
render () {
return <div>
<div className="post">{this.getContent()}</div>
<Comments parent={this.props.hash} post={this.props.hash} api={this.props.api} adminID={this.props.adminID} board={this.props.board} />
{ this.state.reply
? <CommentEditor parent={this.props.hash} api={this.props.api} adminID={this.props.adminID} board={this.props.board} />
: <div/>}
<Comments allowReply={this.props.allowReply} parent={this.props.hash} post={this.props.hash} api={this.props.api} adminID={this.props.adminID} board={this.props.board} />
</div>
}
})

View File

@ -12,7 +12,6 @@ module.exports = function (boardsAPI) {
},
componentDidMount: function () {
boardsAPI.use(boards => {
boards.init()
boards.getEventEmitter().on('init', err => {
if (!err && this.isMounted()) {
this.init(boards)
@ -24,7 +23,9 @@ module.exports = function (boardsAPI) {
})
},
componentWillReceiveProps: function (nextProps) {
boardsAPI.use(boards => this.downloadComment(boards, nextProps))
if (this.props.params !== nextProps.params) {
boardsAPI.use(boards => this.downloadComment(boards, nextProps))
}
},
downloadComment: function (boards, props) {
this.setState({ comment: false })
@ -34,13 +35,18 @@ module.exports = function (boardsAPI) {
comment: { title: 'Error', text: err.Message || err.Error }
})
} else {
this.setState({ comment })
if (!comment.parent && comment.text) {
// Redirect to post page
this.props.history.push('/@' + this.props.params.userid + '/' + this.props.params.boardname + '/' + this.props.params.commenthash)
} else {
this.setState({ comment })
}
}
})
},
init: function (boards) {
if (this.state.init) return
this.setState({ api: true, boards: boards })
this.setState({ api: true, boards, canReply: boards.isInit && !boards.limited })
this.downloadComment(boards, this.props)
},
getContext: function () {
@ -54,7 +60,7 @@ module.exports = function (boardsAPI) {
},
showComment: function () {
if (this.state.comment) {
return <Comment comment={this.state.comment} post={this.props.params.posthash} adminID={this.props.params.userid} board={this.props.params.boardname} showParent={true} api={this.state.boards} />
return <Comment canReply={this.state.canReply} comment={this.state.comment} post={this.props.params.posthash} adminID={this.props.params.userid} board={this.props.params.boardname} showParent={true} api={this.state.boards} />
} else {
return <div className="center-block text-center find-content">
<Icon name="refresh" className="fa-3x center-block light fa-spin" />

View File

@ -11,7 +11,10 @@ module.exports = function (boardsAPI) {
},
componentDidMount: function () {
boardsAPI.use(boards => {
this.setState({ api: boards })
this.setState({ api: boards, allowReply: boards.isInit && !boards.limited })
boards.getEventEmitter().on('init', (err, limited) => {
this.setState({ api: boards, allowReply: !err && !limited })
})
})
},
getContext: function () {
@ -29,7 +32,7 @@ module.exports = function (boardsAPI) {
<div className="text-center">
{this.getContext()}
</div>
<Post hash={this.props.params.posthash} board={this.props.params.boardname} api={this.state.api} adminID={this.props.params.userid} />
<Post allowReply={true} hash={this.props.params.posthash} board={this.props.params.boardname} api={this.state.api} adminID={this.props.params.userid} />
</div>
} else {
return <GetIPFS api={this.state.boards} />

View File

@ -14,6 +14,7 @@ hr {
a {
text-decoration: underline;
color: #ababab;
cursor: pointer;
}
.nounderline {