2015-11-14 01:34:03 +01:00
|
|
|
var React = require('react')
|
|
|
|
var ReactDOM = require('react-dom')
|
2015-11-14 13:12:48 +01:00
|
|
|
var Router = require('react-router').Router
|
|
|
|
var Route = require('react-router').Route
|
|
|
|
var IndexRoute = require('react-router').IndexRoute
|
|
|
|
var Link = require('react-router').Link
|
|
|
|
|
2015-11-17 20:32:52 +01:00
|
|
|
var MarkdownLib = require('react-markdown')
|
2015-11-14 12:06:37 +01:00
|
|
|
var ipfs = require('ipfs-api')('localhost',5001)
|
|
|
|
var BoardsAPI = require('../lib/boards-api.js')
|
|
|
|
|
|
|
|
var boards = new BoardsAPI(ipfs)
|
|
|
|
|
2015-11-17 20:32:52 +01:00
|
|
|
// Components
|
|
|
|
|
|
|
|
var Markdown = React.createClass({
|
|
|
|
renderIfApplicable: function(){
|
|
|
|
if(this.props.source)
|
|
|
|
return <MarkdownLib source={this.props.source} skipHtml={true} />
|
|
|
|
return <p>...</p>
|
|
|
|
},
|
2015-11-14 12:06:37 +01:00
|
|
|
render: function(){
|
2015-11-17 20:32:52 +01:00
|
|
|
return this.renderIfApplicable()
|
2015-11-14 12:06:37 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-17 20:32:52 +01:00
|
|
|
var Icon = React.createClass({
|
|
|
|
class: function(){
|
|
|
|
return 'fa fa-'+this.props.name+' '+this.props.class
|
|
|
|
},
|
2015-11-14 12:06:37 +01:00
|
|
|
render: function(){
|
2015-11-17 20:32:52 +01:00
|
|
|
return ( <i className={this.class()}></i> )
|
2015-11-14 12:06:37 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-17 20:32:52 +01:00
|
|
|
var Container = React.createClass({
|
2015-11-14 12:06:37 +01:00
|
|
|
render: function(){
|
2015-11-17 20:32:52 +01:00
|
|
|
return ( <div className="container app">{this.props.children}</div> )
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
var App = React.createClass({
|
|
|
|
render: function(){
|
|
|
|
return ( <div><Navbar /><Container>{this.props.children}</Container></div> )
|
2015-11-14 12:06:37 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
var Navbar = React.createClass({
|
|
|
|
render: function(){
|
|
|
|
return (
|
|
|
|
<div className="navbar">
|
|
|
|
<div className="container">
|
2015-11-17 20:32:52 +01:00
|
|
|
<div className="row">
|
|
|
|
<div className="twelve columns">
|
|
|
|
<h4><Link to="/"><Icon name="comments" class="light"/> Boards</Link></h4>
|
|
|
|
<div className="u-pull-right iconbar">
|
|
|
|
<Link className="nounderline" to="/users"><Icon name="globe" class="fa-2x light"/></Link>
|
|
|
|
<Link className="nounderline" to="/settings"><Icon name="cog" class="fa-2x light"/></Link>
|
|
|
|
<a className="nounderline" href="https://github.com/fazo96/ipfs-boards"><Icon name="github" class="fa-2x light"/></a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2015-11-14 12:06:37 +01:00
|
|
|
</div>
|
|
|
|
</div>)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-14 15:03:38 +01:00
|
|
|
var PostList = React.createClass({
|
2015-11-14 13:12:48 +01:00
|
|
|
getInitialState: function(){
|
|
|
|
return { posts: [] }
|
|
|
|
},
|
2015-11-14 15:03:38 +01:00
|
|
|
componentDidMount: function(){
|
2015-11-14 18:26:56 +01:00
|
|
|
console.log('Initial POSTS',this.state.posts.length)
|
2015-11-16 14:35:27 +01:00
|
|
|
boards.getPostsInBoard(this.props.admin,this.props.board)
|
|
|
|
.on('post in '+this.props.board+'@'+this.props.admin,(post,hash) => {
|
2015-11-14 18:26:56 +01:00
|
|
|
if(!this.isMounted()) return true
|
2015-11-15 21:35:13 +01:00
|
|
|
this.setState({ posts: this.state.posts.concat(post) })
|
2015-11-14 15:03:38 +01:00
|
|
|
})
|
|
|
|
},
|
2015-11-14 13:12:48 +01:00
|
|
|
render: function(){
|
|
|
|
return (
|
2015-11-14 15:03:38 +01:00
|
|
|
<div className="postList">
|
|
|
|
{this.state.posts.map(post => {
|
2015-11-14 16:26:03 +01:00
|
|
|
return (<div key={post.title} className="post">
|
|
|
|
<h5>{post.title}</h5>
|
2015-11-17 20:32:52 +01:00
|
|
|
<Markdown source={post.text} skipHtml={true} />
|
2015-11-14 15:03:38 +01:00
|
|
|
</div>)
|
|
|
|
})}
|
|
|
|
</div>
|
2015-11-14 13:12:48 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-14 16:26:03 +01:00
|
|
|
var UserID = React.createClass({
|
|
|
|
getInitialState: function(){
|
|
|
|
return { name: '@'+this.props.id }
|
|
|
|
},
|
|
|
|
componentDidMount: function(){
|
|
|
|
boards.getProfile(this.props.id, (err,res) => {
|
2015-11-14 18:26:56 +01:00
|
|
|
if(!this.isMounted()) return true
|
2015-11-14 16:26:03 +01:00
|
|
|
if(!err) {
|
2015-11-17 20:32:52 +01:00
|
|
|
this.setState({ name: res.name.trim() })
|
2015-11-14 16:26:03 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
2015-11-14 15:03:38 +01:00
|
|
|
render: function(){
|
|
|
|
return (<div className="board">
|
2015-11-17 20:32:52 +01:00
|
|
|
<Link className="light nounderline" to={'/@'+this.props.id}>
|
|
|
|
<Icon name="user"/> {this.state.name}
|
2015-11-14 15:03:38 +01:00
|
|
|
</Link>
|
2015-11-14 16:26:03 +01:00
|
|
|
</div>)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-17 20:32:52 +01:00
|
|
|
// Static pages
|
|
|
|
|
|
|
|
var Homepage = React.createClass({
|
2015-11-14 16:26:03 +01:00
|
|
|
render: function(){
|
2015-11-17 20:32:52 +01:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>Welcome to the IPFS Boards Prototype</h3>
|
|
|
|
<p>Not much is implemented...</p>
|
|
|
|
<p>You can try <Link to="@QmXnfA41SXMX3tqFD4kjED7ehyvgTsuAho86TkEoTbZdpw">Opening my Profile</Link> though :)</p>
|
|
|
|
<p>More information about the project on <a href="https://github.com/fazo96/ipfs-board">GitHub</a></p>
|
|
|
|
</div>
|
|
|
|
)
|
2015-11-14 15:03:38 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-14 23:33:50 +01:00
|
|
|
var GetIPFS = React.createClass({
|
|
|
|
render: function(){
|
|
|
|
return (
|
|
|
|
<div className="text-center">
|
|
|
|
<h1>Missing IPFS Node</h1>
|
|
|
|
<p>You don't have an IPFS node running at <code>localhost:5001</code>
|
|
|
|
or it is not reachable</p>
|
|
|
|
<p>The IPFS Boards prototype requires a full IPFS node running at localhost.
|
|
|
|
Please start one by following the
|
|
|
|
<a href="https://github.com/ipfs/go-ipfs"><code>go-ipfs</code> documentation.</a></p>
|
|
|
|
<p>If you have a running node but still this doesn't work, it's probably a CORS issue</p>
|
|
|
|
<p>You can find out how to fix CORS issues related to this app <a href="https://github.com/fazo96/ipfs-board/blob/master/ipfs_daemon_set_cors.sh">here</a>.</p>
|
|
|
|
<p>Still can't fix it? <a href="https://github.com/fazo96/ipfs-board/issues">File a issue on GitHub</a>, we'll be happy to help!</p>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-17 20:32:52 +01:00
|
|
|
var NotFound = React.createClass({
|
|
|
|
render: function(){
|
|
|
|
return (<div className="text-center">
|
|
|
|
<h1><Icon name="ban"/></h1>
|
|
|
|
<p>Sorry, there's nothing here!</p>
|
|
|
|
</div>)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
var NotImplemented = React.createClass({
|
|
|
|
render: function(){
|
|
|
|
return ( <div className="text-center">
|
|
|
|
<h1>Not yet implemented</h1>
|
|
|
|
<h1><Icon name="cog" class="fa-spin"/></h1>
|
|
|
|
<p>Sorry, working on it!</p>
|
|
|
|
</div> )
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// Dynamic pages
|
|
|
|
|
|
|
|
var Profile = React.createClass({
|
|
|
|
getInitialState: function(){
|
|
|
|
return { name: '...', boards: [] }
|
|
|
|
},
|
|
|
|
componentDidMount: function(){
|
|
|
|
var ee = boards.getProfile(this.props.params.userid, (err,res) => {
|
|
|
|
if(!this.isMounted()) return true
|
|
|
|
if(err){
|
|
|
|
console.log(err)
|
|
|
|
this.setState({
|
|
|
|
name: '?',
|
|
|
|
error: 'Invalid profile'
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
console.log(res)
|
|
|
|
this.setState({ name: res.name, description: res.description })
|
|
|
|
}
|
|
|
|
})
|
|
|
|
ee.on('boards for '+this.props.params.userid,l => {
|
|
|
|
if(!this.isMounted()) return true
|
|
|
|
this.setState({ boards: l })
|
|
|
|
})
|
|
|
|
},
|
|
|
|
render: function(){
|
|
|
|
return (<div className="profile">
|
|
|
|
<h1>{this.state.name}</h1>
|
|
|
|
<p>{this.state.error}</p>
|
|
|
|
<Markdown source={this.state.description} skipHtml={true} />
|
|
|
|
<hr/>
|
|
|
|
<h5 className="light">@{this.props.params.userid}</h5>
|
|
|
|
{this.state.boards.map(n => {
|
|
|
|
return <h6 className="light" key={this.props.params.userid+'/'+n.name}>
|
|
|
|
<Link to={'/@'+this.props.params.userid+'/'+n.name}># {n.name}</Link>
|
|
|
|
</h6>
|
|
|
|
})}
|
|
|
|
</div>)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
var Board = React.createClass({
|
|
|
|
getInitialState: function(){
|
|
|
|
return { name: this.props.params.boardname }
|
|
|
|
},
|
|
|
|
componentDidMount: function(){
|
|
|
|
var ee = boards.getBoardSettings(this.props.params.userid,this.props.params.boardname)
|
|
|
|
ee.on('settings for '+this.props.params.boardname+'@'+this.props.params.userid, (res) => {
|
|
|
|
if(!this.isMounted()) return true
|
|
|
|
console.log('Found name:',res.fullname)
|
|
|
|
this.setState({ name: res.fullname.trim(), description: res.description })
|
|
|
|
})
|
|
|
|
},
|
|
|
|
render: function(){
|
|
|
|
return (<div className="board">
|
|
|
|
<h2>{this.state.name}</h2>
|
|
|
|
<Markdown source={this.state.description} skipHtml={true} />
|
|
|
|
<h5><UserID id={this.props.params.userid} /></h5>
|
|
|
|
<PostList board={this.props.params.boardname} admin={this.props.params.userid}/>
|
|
|
|
</div>)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
var Users = React.createClass({
|
|
|
|
render: function(){
|
|
|
|
return <NotImplemented />
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
var Settings = React.createClass({
|
2015-11-17 22:18:30 +01:00
|
|
|
getInitialState: function(){
|
|
|
|
// get from localstorage
|
|
|
|
return { addr: 'localhost', port: '5001' }
|
|
|
|
},
|
|
|
|
save: function(){
|
|
|
|
// write to localstorage
|
|
|
|
|
|
|
|
},
|
|
|
|
setDefaults: function(){
|
|
|
|
this.setState({ addr: 'localhost', port: '5001' })
|
|
|
|
},
|
|
|
|
onChange: function(event){
|
|
|
|
console.log(event.target.id)
|
|
|
|
//this.setState({})
|
|
|
|
},
|
2015-11-17 20:32:52 +01:00
|
|
|
render: function(){
|
2015-11-17 22:18:30 +01:00
|
|
|
return (
|
|
|
|
<div className="settings">
|
|
|
|
<h2><Icon name="cog"/> Settings</h2>
|
|
|
|
<h4>Note that this page doesn't work yet.</h4>
|
|
|
|
<p>Use this page to customize the application's behavior. For now, you can change how it connects to IPFS.</p>
|
|
|
|
<p>All settings are saved in your browser.</p>
|
|
|
|
<div className="row">
|
|
|
|
<div className="six columns">
|
|
|
|
<label for="nodeAddress">IPFS Node</label>
|
|
|
|
<input className="u-full-width" type="text" id="nodeAddess" value={this.state.addr} onChange={this.onChange} placeholder="localhost" />
|
|
|
|
</div>
|
|
|
|
<div className="six columns">
|
|
|
|
<label for="nodePort">API Port</label>
|
|
|
|
<input className="u-full-width" type="text" id="nodePort" value={this.state.port} onChange={this.onChange} placeholder="5001" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="buttons">
|
|
|
|
<button className="button button-primary" onClick={this.save}>Save</button>
|
|
|
|
<button className="button not-first" onClick={this.setDefaults}>Defaults</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
2015-11-17 20:32:52 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// Start
|
|
|
|
|
2015-11-14 23:33:50 +01:00
|
|
|
boards.init(err => {
|
|
|
|
if(err){
|
|
|
|
console.log('FATAL: IPFS NODE NOT AVAILABLE')
|
|
|
|
ReactDOM.render(<App><GetIPFS/></App>, document.getElementById('root'))
|
|
|
|
} else {
|
|
|
|
ReactDOM.render(
|
|
|
|
<Router>
|
|
|
|
<Route path="/" component={App}>
|
|
|
|
<IndexRoute component={Homepage} />
|
|
|
|
<Route path="/@:userid">
|
|
|
|
<IndexRoute component={Profile} />
|
2015-11-17 20:32:52 +01:00
|
|
|
<Route path=":boardname" component={Board} />
|
2015-11-14 23:33:50 +01:00
|
|
|
</Route>
|
2015-11-17 20:32:52 +01:00
|
|
|
<Route path="/users" component={Users} />
|
|
|
|
<Route path="/settings" component={Settings} />
|
|
|
|
<Route path="*" component={NotFound} />
|
2015-11-14 23:33:50 +01:00
|
|
|
</Route>
|
|
|
|
</Router>, document.getElementById('root')
|
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|