1
0
mirror of https://github.com/fazo96/ipfs-boards synced 2025-01-10 12:24:20 +01:00

I guess comments are kinda implemented

This commit is contained in:
Enrico Fasoli 2015-11-23 18:26:34 +01:00
parent ce187c3ddc
commit 573303de39
6 changed files with 173 additions and 30 deletions

View File

@ -11,6 +11,7 @@ var EventEmitter = require('wolfy87-eventemitter')
var asyncjs = require('async') var asyncjs = require('async')
function asObj(str,done){ function asObj(str,done){
if(str.toString) str = str.toString()
if(typeof str === 'string'){ if(typeof str === 'string'){
var obj var obj
try { try {
@ -43,8 +44,9 @@ function replyAsObj(res,isJson,done){
done(null,data) done(null,data)
} }
}) })
} else { } else if(res.split || res.toString){
console.log('got string:',res) console.log('got string or buffer:',res)
if(res.toString) res = res.toString()
// Is a string // Is a string
if(isJson){ if(isJson){
asObj(res,done) asObj(res,done)
@ -88,7 +90,7 @@ BoardsAPI.prototype.resolveIPNS = function(n,handler){
var cached = this.users[n] var cached = this.users[n]
if(cached){ if(cached){
this.ee.emit(n,cached) this.ee.emit(n,cached)
console.log(n,'was cached',cached) //console.log(n,'was cached',cached)
} else { } else {
console.log(n,'not cached') console.log(n,'not cached')
} }
@ -178,7 +180,6 @@ BoardsAPI.prototype.searchUsers = function(){
} }
BoardsAPI.prototype.getProfile = function(userID,done){ BoardsAPI.prototype.getProfile = function(userID,done){
console.log('profile requested for',userID)
this.resolveIPNS(userID,(url,err) => { this.resolveIPNS(userID,(url,err) => {
if(err){ if(err){
this.ee.emit('error',err) this.ee.emit('error',err)
@ -282,41 +283,55 @@ BoardsAPI.prototype.downloadPost = function(hash,adminID,board,op,done){
return this.ee return this.ee
} }
BoardsAPI.prototype.getPostsInBoard = function(adminID,board){ BoardsAPI.prototype.retrieveListOfApproved = function(what,addr,adminID,board){
this.getBoardSettings(adminID,board) var a = addr+this.baseurl+'boards/'+board+'/approved/'+what+'/'
this.ipfs.ls(a,(err,res) => {
if(err){
this.ee.emit('error',err)
} else {
// Send approved posts list
var ret = res.Objects[0].Links.map(item => {
return { date: item.Name, hash: item.Hash }
})
this.emit('approved '+what+' for '+board+'@'+adminID,ret)
}
})
}
BoardsAPI.prototype.getAllowedContentProducers = function(adminID,board,options){
if(!options) return
this.ee.on('settings for'+board+'@'+adminID,function(settings,addr){ this.ee.on('settings for'+board+'@'+adminID,function(settings,addr){
// Download posts based on settings // Get stuff based on settings
if(settings.approval_required == true){ if(settings.approval_required == true){
// Get approved posts list // Get approved posts list
var a = addr+this.baseurl+'boards/'+board+'/approved/posts/' if(options.posts) this.retrieveListOfApproved('posts',addr,adminID,board)
this.ipfs.ls(a,(err,res) => { // Get approved comments list
if(err){ if(options.comments) this.retrieveListOfApproved('comments',addr,adminID,board)
this.ee.emit('error',err)
} else {
// Send approved posts list
var ret = res.Objects[0].Links.map(item => {
return { date: item.Name, hash: item.Hash }
})
this.emit('approved posts for '+board+'@'+adminID,ret)
// Automatically download approved posts
ret.forEach(item => this.downloadPost(item.hash,adminID,board))
}
})
if(settings.whitelist == true){ if(settings.whitelist == true){
// TODO: Download all posts from whitelisted users // TODO: emit all whitelisted users
} }
} else if(settings.whitelist_only == true){ } else if(settings.whitelist_only == true){
// TODO: download all posts from whitelisted users // TODO: emit all whitelisted users
} else if(settings.blacklist == true){ } else if(settings.blacklist == true){
// TODO: get the blacklist, then start downloading posts from everyone not in the blacklist // TODO: emit all users not in the blacklist
} }
}) })
this.getBoardSettings(adminID,board)
return this.ee
}
BoardsAPI.prototype.getPostsInBoard = function(adminID,board){
this.ee.on('approved posts for '+board+'@'+adminID,ret => {
// Automatically download approved posts
ret.forEach(item => this.downloadPost(item.hash,adminID,board))
})
// Get the admin's posts // Get the admin's posts
this.getUserPostListInBoard(adminID,board,(err,res) => { this.getUserPostListInBoard(adminID,board,(err,res) => {
if(err){ if(err){
console.log(err) console.log(err)
} else res.forEach(item => this.downloadPost(item.hash,adminID,board,adminID)) } else res.forEach(item => this.downloadPost(item.hash,adminID,board,adminID))
}) })
this.getAllowedContentProducers(adminID,board,{ posts: true })
return this.ee return this.ee
} }
@ -343,9 +358,57 @@ BoardsAPI.prototype.getUserPostListInBoard = function(user,board,done){
return this.ee return this.ee
} }
BoardsAPI.prototype.downloadComment = function(hash,adminID,board,done){
this.ipfs.cat(hash,(err2,r) => {
if(err2){
this.ee.emit('error',err2)
console.log('Could not download comment',hash,'of',board+'@'+adminID)
if(done && done.apply) done(err2)
} else {
// TODO: add JSON parsing error handling
var cmnt = JSON.parse(r.toString())
cmnt.hash = hash
this.ee.emit(hash,cmnt,adminID,board)
this.ee.emit('comment for '+cmnt.parent,cmnt)
if(done && done.apply) done(null,cmnt)
}
})
return this.ee
}
BoardsAPI.prototype.getCommentsFor = function(parent,board,adminID){ BoardsAPI.prototype.getCommentsFor = function(parent,board,adminID){
// Create an EventEmitter, start looking and emit an event for every new comment this.ee.on('approved comments for '+board+'@'+adminID,ret => {
// Consider the rules of @adminID#board ret.forEach(item => this.downloadComment(item.hash,adminID,board))
})
// get the admin's comments
this.getUserCommentList(parent,adminID,(err,res) => {
if(err){
console.log(err)
} else res.forEach(item => this.downloadComment(item.hash,adminID,board))
})
this.getAllowedContentProducers(adminID,board,{ comments: true })
}
BoardsAPI.prototype.getUserCommentList = function(parent,user,done){
this.resolveIPNS(user,(url,err) => {
if(err){
this.ee.emit('error',err)
done(err)
} else this.ipfs.ls(url+this.baseurl+'comments/'+parent,(e,r) => {
if(e){
this.ee.emit('error',e)
done(e)
} else if(r && !r.split){
console.log('Found',r.Objects[0].Links.length,'comments for',parent,'at',user)
var l = r.Objects[0].Links.map(i => {
return { date: i.Name, hash: i.Hash }
})
done(null,l)
}
})
return true // remove myself from listeners
})
return this.ee
} }
// API for publishing content and managing to be done later... // API for publishing content and managing to be done later...

View File

@ -0,0 +1,39 @@
var React = require('react')
var Markdown = require('markdown.jsx')
var Icon = require('icon.jsx')
module.exports = function(boardsAPI){
var UserID = require('userID.jsx')(boardsAPI)
return React.createClass({
getInitialState: function(){
return { moment: false }
},
componentDidMount: function(){
require.ensure(['moment'],_ => {
if(this.isMounted()) this.setState({ moment: require('moment') })
})
},
getDate: function(){
if(this.props.comment.date){
if(this.state.moment)
return this.state.moment.unix(this.props.comment.date).fromNow()
else return '...'
} else {
return 'Unknown Date'
}
},
render: function(){
if(this.props.comment){
return <div className="comment"><hr/>
<div className="icons">
<UserID id={this.props.comment.op} />
<Icon name="clock-o" className="not-first"/> {this.getDate()}
</div>
<Markdown source={this.props.comment.text} />
<hr/></div>
} else {
return <div><hr/>Invalid Comment<hr/></div>
}
}
})
}

View File

@ -0,0 +1,30 @@
var React = require('react')
module.exports = function(boardsAPI){
var Comment = require('comment.jsx')(boardsAPI)
return React.createClass({
getInitialState: function(){
return { comments: [] }
},
componentDidMount: function(){
boardsAPI.use(boards => {
boards.getEventEmitter().on('comment for '+this.props.parent,cmnt => {
if(this.isMounted()) this.setState({ comments: this.state.comments.concat(cmnt) })
})
if(boards.isInit && this.isMounted()){
boards.getCommentsFor(this.props.parent,this.props.board,this.props.adminID)
}
boards.getEventEmitter().on('init', err => {
if(!err && this.isMounted())
boards.getCommentsFor(this.props.parent,this.props.board,this.props.adminID)
})
})
},
getComments: function(){
return this.state.comments.map(cmnt => (<Comment key={cmnt.hash} comment={cmnt} />) )
},
render: function(){
return <div>{this.getComments()}</div>
}
})
}

View File

@ -24,7 +24,6 @@ module.exports = function(boardsAPI){
}) })
}, },
postLink: function(){ postLink: function(){
console.log('op',this.props.post.op,'board',this.props.board)
if(this.props.post.op){ if(this.props.post.op){
if(this.props.board){ if(this.props.board){
return '/@'+this.props.post.op+'/'+this.props.board+'/'+this.props.post.hash return '/@'+this.props.post.op+'/'+this.props.board+'/'+this.props.post.hash
@ -39,7 +38,7 @@ module.exports = function(boardsAPI){
return <div key={this.props.post.title} className="post"> return <div key={this.props.post.title} className="post">
<div className="content"> <div className="content">
<h5>{this.props.post.title}</h5><hr/> <h5>{this.props.post.title}</h5><hr/>
<Markdown source={this.props.post.text} skipHtml={true} /><hr/> <Markdown source={this.props.post.text} /><hr/>
<div className="icons"> <div className="icons">
<UserID id={this.props.post.op}></UserID> <UserID id={this.props.post.op}></UserID>
<Icon name="clock-o" className="not-first"/> {this.getDate()} <Icon name="clock-o" className="not-first"/> {this.getDate()}

View File

@ -5,6 +5,8 @@ module.exports = function(boardsAPI){
var UserID = require('userID.jsx')(boardsAPI) var UserID = require('userID.jsx')(boardsAPI)
var GetIPFS = require('getipfs.jsx')(boardsAPI) var GetIPFS = require('getipfs.jsx')(boardsAPI)
var Post = require('post.jsx')(boardsAPI) var Post = require('post.jsx')(boardsAPI)
var Comments = require('comments.jsx')(boardsAPI)
return React.createClass({ return React.createClass({
getInitialState: function(){ getInitialState: function(){
return { post: { title: '...', text: '...' }, api: false } return { post: { title: '...', text: '...' }, api: false }
@ -36,7 +38,7 @@ module.exports = function(boardsAPI){
getContext(){ getContext(){
if(this.props.params.userid){ if(this.props.params.userid){
if(this.props.params.boardname) if(this.props.params.boardname)
return <div>Posted by <UserID id={this.props.params.userid} /> in <Link to={'@/'+this.props.params.userid+'/'+this.props.params.boardname}>#{this.props.params.boardname}</Link></div> return <div>Posted by <UserID id={this.props.params.userid} /> in <Link to={'@'+this.props.params.userid+'/'+this.props.params.boardname}>#{this.props.params.boardname}</Link></div>
else else
return <div>Posted by <UserID id={this.props.params.userid} /></div> return <div>Posted by <UserID id={this.props.params.userid} /></div>
} else return <div><h6 className="light">You are viewing a single post</h6></div> } else return <div><h6 className="light">You are viewing a single post</h6></div>
@ -48,6 +50,7 @@ module.exports = function(boardsAPI){
{this.getContext()} {this.getContext()}
</div> </div>
<Post post={this.state.post} board={this.props.params.boardname} /> <Post post={this.state.post} board={this.props.params.boardname} />
<Comments parent={this.props.params.posthash} board={this.props.params.boardname} adminID={this.props.params.userid}/>
</div> </div>
else return <GetIPFS /> else return <GetIPFS />
} }

View File

@ -75,7 +75,7 @@ a:hover {
margin: 2rem; margin: 2rem;
} }
.post .icons .fa { .post .icons .fa, .comment .icons .fa {
color: #707070; color: #707070;
} }
@ -95,6 +95,15 @@ a:hover {
display: inline display: inline
} }
.comment .icons {
margin-bottom: 1rem;
}
.comment .icons .user-id {
margin-right: 1rem;
}
.navbar { .navbar {
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
display: block; display: block;