diff --git a/browser/main/Components/BlueprintForm.jsx b/browser/main/Components/BlueprintForm.jsx
index 85307a8e..854754a8 100644
--- a/browser/main/Components/BlueprintForm.jsx
+++ b/browser/main/Components/BlueprintForm.jsx
@@ -5,6 +5,7 @@ var Catalyst = require('../Mixins/Catalyst')
var Markdown = require('../Mixins/Markdown')
var Select = require('react-select')
var request = require('superagent')
+var PlanetActions = require('../Actions/PlanetActions')
var getOptions = function (input, callback) {
request
@@ -30,13 +31,14 @@ var getOptions = function (input, callback) {
var BlueprintForm = React.createClass({
mixins: [Catalyst.LinkedStateMixin, ReactRouter.State, Markdown],
+ propTypes: {
+ close: React.PropTypes.func,
+ blueprint: React.PropTypes.object
+ },
statics: {
EDIT_MODE: 0,
PREVIEW_MODE: 1
},
- propTypes: {
- close: React.PropTypes.func
- },
getInitialState: function () {
return {
blueprint: {
@@ -65,6 +67,22 @@ var BlueprintForm = React.createClass({
},
submit: function () {
console.log(this.state.blueprint)
+ var blueprint = Object.assign({}, this.state.blueprint)
+ blueprint.Tags = blueprint.Tags.map(function (tag) {
+ return tag.value
+ })
+ if (this.props.blueprint == null) {
+ var params = this.getParams()
+ var userName = params.userName
+ var planetName = params.planetName
+
+ PlanetActions.createBlueprint(userName + '/' + planetName, blueprint)
+ } else {
+ var blueprintId = blueprint.id
+ delete blueprint.id
+
+ PlanetActions.updateBlueprint(blueprintId, blueprint)
+ }
},
render: function () {
var content = this.state.mode === BlueprintForm.EDIT_MODE ? (
diff --git a/browser/main/Components/LaunchModal.jsx b/browser/main/Components/LaunchModal.jsx
index 152963dc..53c2fd85 100644
--- a/browser/main/Components/LaunchModal.jsx
+++ b/browser/main/Components/LaunchModal.jsx
@@ -28,6 +28,9 @@ var LaunchModal = React.createClass({
case 'snippetCreated':
this.props.close()
break
+ case 'blueprintCreated':
+ this.props.close()
+ break
}
},
stopPropagation: function (e) {
diff --git a/browser/main/Containers/PlanetContainer.jsx b/browser/main/Containers/PlanetContainer.jsx
index 0763ef8c..fa1eaa7e 100644
--- a/browser/main/Containers/PlanetContainer.jsx
+++ b/browser/main/Containers/PlanetContainer.jsx
@@ -13,6 +13,8 @@ var PlanetStore = require('../Stores/PlanetStore')
var PlanetActions = require('../Actions/PlanetActions')
+var Markdown = require('../Mixins/Markdown')
+
var PlanetHeader = React.createClass({
propTypes: {
currentPlanet: React.PropTypes.object,
@@ -111,16 +113,17 @@ var PlanetNavigator = React.createClass({
})
var PlanetArticleList = React.createClass({
- mixins: [ReactRouter.Navigation, ReactRouter.State, ForceUpdate(60000)],
+ mixins: [ReactRouter.Navigation, ReactRouter.State, ForceUpdate(60000), Markdown],
propTypes: {
planet: React.PropTypes.shape({
Snippets: React.PropTypes.array,
- Blueprints: React.PropTypes.array
+ Blueprints: React.PropTypes.array,
+ Articles: React.PropTypes.array
})
},
render: function () {
- var articles = this.props.planet.Snippets.map(function (snippet) {
- var tags = snippet.Tags.length > 0 ? snippet.Tags.map(function (tag) {
+ var articles = this.props.planet.Articles.map(function (article) {
+ var tags = article.Tags.length > 0 ? article.Tags.map(function (tag) {
return (
#{tag.name}
)
@@ -128,30 +131,57 @@ var PlanetArticleList = React.createClass({
Not tagged yet
)
var params = this.getParams()
+ var isActive = article.type === 'snippet' ? this.isActive('snippets') && parseInt(params.localId, 10) === article.localId : this.isActive('blueprints') && parseInt(params.localId, 10) === article.localId
- var isActive = parseInt(params.localId, 10) === snippet.localId
+ var handleClick
- var handleClick = function () {
- this.transitionTo('snippets', {
+ if (article.type === 'snippet') {
+
+ handleClick = function () {
+ this.transitionTo('snippets', {
+ userName: params.userName,
+ planetName: params.planetName,
+ localId: article.localId
+ })
+ }.bind(this)
+
+ return (
+
+
+
+
{article.callSign}
+
{moment(article.updatedAt).fromNow()}
+
+
{article.description.length > 50 ? article.description.substring(0, 50) + ' …' : article.description}
+
{tags}
+
+
+
+ )
+ }
+
+ handleClick = function () {
+ this.transitionTo('blueprints', {
userName: params.userName,
planetName: params.planetName,
- localId: snippet.localId
+ localId: article.localId
})
}.bind(this)
return (
-
-
+
+
-
{snippet.callSign}
-
{moment(snippet.updatedAt).fromNow()}
+
{article.title}
+
{moment(article.updatedAt).fromNow()}
-
{snippet.description.length > 50 ? snippet.description.substring(0, 50) + ' …' : snippet.description}
+
{this.markdown(article.content.substring(0, 150)).replace(/(<([^>]+)>)/ig, '').substring(0, 75)}
{tags}
)
+
}.bind(this))
return (
@@ -165,9 +195,9 @@ var PlanetArticleList = React.createClass({
})
var PlanetArticleDetail = React.createClass({
- mixins: [ForceUpdate(60000)],
+ mixins: [ForceUpdate(60000), Markdown],
propTypes: {
- snippet: React.PropTypes.object
+ article: React.PropTypes.object
},
getInitialState: function () {
return {
@@ -193,44 +223,69 @@ var PlanetArticleDetail = React.createClass({
this.setState({isDeleteModalOpen: false})
},
render: function () {
- var snippet = this.props.snippet
+ var article = this.props.article
- var tags = snippet.Tags.length > 0 ? snippet.Tags.map(function (tag) {
+ var tags = article.Tags.length > 0 ? article.Tags.map(function (tag) {
return (
#{tag.name}
)
}) : (
Not tagged yet
)
+ if (article.type === 'snippet') {
+ return (
+
+
+ {article.callSign} {moment(article.updatedAt).fromNow()}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{article.description}
+
{tags}
+
+
+
+
+
+
+ )
+ }
return (
-
+
- {snippet.callSign} {moment(snippet.updatedAt).fromNow()}
+ {article.title} {moment(article.updatedAt).fromNow()}
-
+
-
+
-
-
{snippet.description}
-
{tags}
-
-
-
-
+
{tags}
+
)
+
}
})
@@ -261,11 +316,20 @@ module.exports = React.createClass({
case 'planetFetched':
var planet = res.data
this.setState({currentPlanet: planet}, function () {
- if (planet.Snippets.length > 0) {
- this.transitionTo('snippets', {
- userName: this.props.params.userName,
- planetName: this.props.params.planetName,
- localId: this.props.params.localId == null ? planet.Snippets[0].localId : this.props.params.localId})
+ if (planet.Articles.length > 0) {
+ if (this.isActive('snippets')) {
+ this.transitionTo('snippets', {
+ userName: this.props.params.userName,
+ planetName: this.props.params.planetName,
+ localId: this.props.params.localId == null ? planet.Articles[0].localId : this.props.params.localId
+ })
+ } else if (this.isActive('blueprints')) {
+ this.transitionTo('blueprints', {
+ userName: this.props.params.userName,
+ planetName: this.props.params.planetName,
+ localId: this.props.params.localId == null ? planet.Articles[0].localId : this.props.params.localId
+ })
+ }
}
})
break
@@ -326,14 +390,26 @@ module.exports = React.createClass({
if (user == null) return (
)
if (this.state.currentPlanet == null) return (
)
- var content = (
No selected
)
+ var content = (
Nothing selected
)
if (this.isActive('snippets')) {
var localId = parseInt(this.props.params.localId, 10)
- this.state.currentPlanet.Snippets.some(function (_snippet) {
- if (localId === _snippet.localId) {
+ this.state.currentPlanet.Articles.some(function (article) {
+ if (article.type === 'snippet' && localId === article.localId) {
content = (
-
+
+ )
+ return true
+ }
+ return false
+ })
+ } else if (this.isActive('blueprints')) {
+ var localId = parseInt(this.props.params.localId, 10)
+
+ this.state.currentPlanet.Articles.some(function (article) {
+ if (article.type === 'blueprint' && localId === article.localId) {
+ content = (
+
)
return true
}
diff --git a/browser/main/Mixins/Markdown.js b/browser/main/Mixins/Markdown.js
index f2cccf91..6d26080e 100644
--- a/browser/main/Mixins/Markdown.js
+++ b/browser/main/Mixins/Markdown.js
@@ -1,6 +1,7 @@
var markdownit = require('markdown-it')
var md = markdownit({
- typographer: true
+ typographer: true,
+ linkify: true
})
var Markdown = {
diff --git a/browser/main/Stores/PlanetStore.js b/browser/main/Stores/PlanetStore.js
index 121ea87e..a33b52e2 100644
--- a/browser/main/Stores/PlanetStore.js
+++ b/browser/main/Stores/PlanetStore.js
@@ -4,16 +4,21 @@ var request = require('superagent')
var PlanetActions = require('../Actions/PlanetActions')
+var apiUrl = 'http://localhost:8000/'
+
var PlanetStore = Reflux.createStore({
init: function () {
this.listenTo(PlanetActions.fetchPlanet, this.fetchPlanet)
this.listenTo(PlanetActions.createSnippet, this.createSnippet)
this.listenTo(PlanetActions.updateSnippet, this.updateSnippet)
this.listenTo(PlanetActions.deleteSnippet, this.deleteSnippet)
+ this.listenTo(PlanetActions.createBlueprint, this.createBlueprint)
+ this.listenTo(PlanetActions.updateBlueprint, this.updateBlueprint)
+ this.listenTo(PlanetActions.deleteBlueprint, this.deleteBlueprint)
},
fetchPlanet: function (planetName) {
request
- .get('http://localhost:8000/' + planetName)
+ .get(apiUrl + planetName)
.send()
.end(function (err, res) {
if (err) {
@@ -23,7 +28,17 @@ var PlanetStore = Reflux.createStore({
}
var planet = res.body
- planet.Snippets = planet.Snippets.sort(function (a, b) {
+ planet.Snippets = planet.Snippets.map(function (snippet) {
+ snippet.type = 'snippet'
+ return snippet
+ })
+
+ planet.Blueprints = planet.Blueprints.map(function (blueprint) {
+ blueprint.type = 'blueprint'
+ return blueprint
+ })
+
+ planet.Articles = planet.Snippets.concat(planet.Blueprints).sort(function (a, b) {
a = new Date(a.updatedAt)
b = new Date(b.updatedAt)
return a < b ? 1 : a > b ? -1 : 0
@@ -38,13 +53,12 @@ var PlanetStore = Reflux.createStore({
createSnippet: function (planetName, input) {
input.description = input.description.substring(0, 255)
request
- .post('http://localhost:8000/' + planetName + '/snippets')
+ .post(apiUrl + planetName + '/snippets')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
.end(function (req, res) {
- console.log('snippet created', res.body)
this.trigger({
status: 'snippetCreated',
data: res.body
@@ -54,7 +68,7 @@ var PlanetStore = Reflux.createStore({
updateSnippet: function (id, input) {
input.description = input.description.substring(0, 255)
request
- .put('http://localhost:8000/snippets/id/' + id)
+ .put(apiUrl + 'snippets/id/' + id)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
@@ -75,7 +89,7 @@ var PlanetStore = Reflux.createStore({
},
deleteSnippet: function (id) {
request
- .del('http://localhost:8000/snippets/id/' + id)
+ .del(apiUrl + 'snippets/id/' + id)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
@@ -93,6 +107,64 @@ var PlanetStore = Reflux.createStore({
data: snippet
})
}.bind(this))
+ },
+ createBlueprint: function (planetName, input) {
+ input.title = input.title.substring(0, 255)
+ request
+ .post(apiUrl + planetName + '/blueprints')
+ .set({
+ Authorization: 'Bearer ' + localStorage.getItem('token')
+ })
+ .send(input)
+ .end(function (req, res) {
+ this.trigger({
+ status: 'blueprintCreated',
+ data: res.body
+ })
+ }.bind(this))
+ },
+ updateBlueprint: function (id, input) {
+ input.description = input.description.substring(0, 255)
+ request
+ .put(apiUrl + 'blueprints/id/' + id)
+ .set({
+ Authorization: 'Bearer ' + localStorage.getItem('token')
+ })
+ .send(input)
+ .end(function (err, res) {
+ if (err) {
+ console.error(err)
+ this.trigger(null)
+ return
+ }
+
+ var blueprint = res.body
+ this.trigger({
+ status: 'blueprintUpdated',
+ data: blueprint
+ })
+ }.bind(this))
+ },
+ deleteBlueprint: function (id) {
+ request
+ .del(apiUrl + 'blueprints/id/' + id)
+ .set({
+ Authorization: 'Bearer ' + localStorage.getItem('token')
+ })
+ .send()
+ .end(function (err, res) {
+ if (err) {
+ console.error(err)
+ this.trigger(null)
+ return
+ }
+
+ var blueprint = res.body
+ this.trigger({
+ status: 'blueprintDeleted',
+ data: blueprint
+ })
+ }.bind(this))
}
})
diff --git a/browser/styles/main/containers/PlanetContainer.styl b/browser/styles/main/containers/PlanetContainer.styl
index 46bb3727..bbe0bb68 100644
--- a/browser/styles/main/containers/PlanetContainer.styl
+++ b/browser/styles/main/containers/PlanetContainer.styl
@@ -133,7 +133,7 @@
absolute top bottom left right
overflow-y auto
li
- .snippetItem
+ .articleItem
user-select none
border solid 2px transparent
padding 10px
@@ -142,7 +142,7 @@
.itemHeader
clearfix()
margin-bottom 5px
- .callSign
+ .callSign, .title
float left
font-weight 600
font-size 1.1em
@@ -151,7 +151,7 @@
line-height 16px
color lighten(textColor, 25%)
font-size 0.8em
- .description
+ .description, .content
margin 10px 0 15px
&:hover, &.hover
background-color hoverBackgroundColor
@@ -183,6 +183,7 @@
absolute bottom right
left 1px
top 44px
+ &.snippetDetail>.viewer-body
.viewer-detail
border-bottom solid 1px borderColor
height 150px
@@ -202,3 +203,20 @@
absolute left right
top 155px
bottom 5px
+ &.blueprintDetail>.viewer-body
+ .tags
+ absolute top
+ left 15px
+ right 15px
+ height 24px
+ line-height 24px
+ .content
+ absolute left right bottom
+ top 30px
+ box-sizing border-box
+ padding 5px
+ border-top solid 1px borderColor
+ padding 10px
+ overflow-x hidden
+ overflow-y auto
+ marked()
diff --git a/browser/styles/mixins/marked.styl b/browser/styles/mixins/marked.styl
index 87ddccb3..f4ae7dc3 100644
--- a/browser/styles/mixins/marked.styl
+++ b/browser/styles/mixins/marked.styl
@@ -1,5 +1,9 @@
marked()
line-height 1.2em
+ hr
+ border-top none
+ border-bottom solid 1px borderColor
+ margin 15px 0
h1
font-size 2em
margin 0.67em auto
@@ -64,6 +68,8 @@ marked()
border none
border-radius 0
table
+ width 100%
+ margin 15px 0
thead
tr
background-color tableHeadBgColor