diff --git a/browser/main/Components/MarkdownPreview.js b/browser/main/Components/MarkdownPreview.js
new file mode 100644
index 00000000..1dbe3c15
--- /dev/null
+++ b/browser/main/Components/MarkdownPreview.js
@@ -0,0 +1,53 @@
+import shell from 'shell'
+import React, { PropTypes } from 'react'
+import markdown from '../HomeContainer/lib/markdown'
+
+function handleAnchorClick (e) {
+ shell.openExternal(e.target.href)
+ e.preventDefault()
+}
+
+export default class MarkdownPreview extends React.Component {
+ componentDidMount () {
+ this.addListener()
+ }
+
+ componentDidUpdate () {
+ this.addListener()
+ }
+
+ componentWillUnmount () {
+ this.removeListener()
+ }
+
+ componentWillUpdate () {
+ this.removeListener()
+ }
+
+ addListener () {
+ var anchors = React.findDOMNode(this).querySelectorAll('a')
+
+ for (var i = 0; i < anchors.length; i++) {
+ anchors[i].addEventListener('click', handleAnchorClick)
+ }
+ }
+
+ removeListener () {
+ var anchors = React.findDOMNode(this).querySelectorAll('a')
+
+ for (var i = 0; i < anchors.length; i++) {
+ anchors[i].removeEventListener('click', handleAnchorClick)
+ }
+ }
+
+ render () {
+ return (
+
+ )
+ }
+}
+
+MarkdownPreview.propTypes = {
+ className: PropTypes.string,
+ content: PropTypes.string
+}
diff --git a/browser/main/Components/MarkdownPreview.jsx b/browser/main/Components/MarkdownPreview.jsx
deleted file mode 100644
index 80df45c5..00000000
--- a/browser/main/Components/MarkdownPreview.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-var React = require('react')
-
-var Markdown = require('../Mixins/Markdown')
-var ExternalLink = require('../Mixins/ExternalLink')
-
-module.exports = React.createClass({
- mixins: [Markdown, ExternalLink],
- propTypes: {
- className: React.PropTypes.string,
- content: React.PropTypes.string
- },
- componentDidMount: function () {
- this.addListener()
- },
- componentDidUpdate: function () {
- this.addListener()
- },
- componentWillUnmount: function () {
- this.removeListener()
- },
- componentWillUpdate: function () {
- this.removeListener()
- },
- addListener: function () {
- var anchors = React.findDOMNode(this).querySelectorAll('a')
-
- for (var i = 0; i < anchors.length; i++) {
- anchors[i].addEventListener('click', this.openExternal)
- }
- },
- removeListener: function () {
- var anchors = React.findDOMNode(this).querySelectorAll('a')
-
- for (var i = 0; i < anchors.length; i++) {
- anchors[i].removeEventListener('click', this.openExternal)
- }
- },
- render: function () {
- return (
-
- )
- }
-})
diff --git a/browser/main/Components/ModeIcon.jsx b/browser/main/Components/ModeIcon.js
similarity index 88%
rename from browser/main/Components/ModeIcon.jsx
rename to browser/main/Components/ModeIcon.js
index 6e1bfb6c..0b3e4886 100644
--- a/browser/main/Components/ModeIcon.jsx
+++ b/browser/main/Components/ModeIcon.js
@@ -1,11 +1,7 @@
-var React = require('react')
+import React, { PropTypes } from 'react'
-module.exports = React.createClass({
- propTypes: {
- className: React.PropTypes.string,
- mode: React.PropTypes.string
- },
- getClassName: function () {
+export default class ModeIcon extends React.Component {
+ getClassName () {
var mode = this.props.mode
switch (mode) {
// Script
@@ -69,11 +65,17 @@ module.exports = React.createClass({
return 'fa fa-fw fa-file-text-o'
}
return 'fa fa-fw fa-code'
- },
- render: function () {
+ }
+
+ render () {
var className = this.getClassName()
return (
)
}
-})
+}
+
+ModeIcon.propTypes = {
+ className: PropTypes.string,
+ mode: PropTypes.string
+}
diff --git a/browser/main/HomeContainer/Components/ArticleDetail.js b/browser/main/HomeContainer/Components/ArticleDetail.js
index e8d31aa5..afb4aa71 100644
--- a/browser/main/HomeContainer/Components/ArticleDetail.js
+++ b/browser/main/HomeContainer/Components/ArticleDetail.js
@@ -1,9 +1,59 @@
import React, { PropTypes } from 'react'
+import moment from 'moment'
+import { findWhere } from 'lodash'
+import ModeIcon from '../../Components/ModeIcon'
+import MarkdownPreview from '../../Components/MarkdownPreview'
+import CodeEditor from '../../Components/CodeEditor'
export default class ArticleDetail extends React.Component {
render () {
+ let { article, status, user } = this.props
+
+ let tags = article.Tags.length > 0 ? article.Tags.map(tag => {
+ return (
+ {tag.name}
+ )
+ }) : (
+ Not tagged yet
+ )
+ let folder = findWhere(user.Folders, {id: article.FolderId})
+ let folderName = folder != null ? folder.name : '(unknown)'
+
return (
-
+
+
+
+
+ {folderName}
+ by {article.User.profileName}
+ Created {moment(article.createdAt).format('YYYY/MM/DD')}
+ Updated {moment(article.updatedAt).format('YYYY/MM/DD')}
+
+
{tags}
+
+
+
+
+
+
+
+
+
+
+
+ {article.mode === 'markdown' ?
: }
+
+
+
)
}
}
+
+ArticleDetail.propTypes = {
+ article: PropTypes.shape(),
+ status: PropTypes.shape(),
+ user: PropTypes.shape()
+}
diff --git a/browser/main/HomeContainer/Components/ArticleList.js b/browser/main/HomeContainer/Components/ArticleList.js
index ef0f0042..a59af0d5 100644
--- a/browser/main/HomeContainer/Components/ArticleList.js
+++ b/browser/main/HomeContainer/Components/ArticleList.js
@@ -1,11 +1,66 @@
import React, { PropTypes } from 'react'
+import ProfileImage from '../../components/ProfileImage'
+import ModeIcon from '../../Components/ModeIcon'
+import moment from 'moment'
+import { IDLE_MODE, CREATE_MODE, EDIT_MODE } from '../actions'
+
+export default class ArticleList extends React.Component {
+ render () {
+ let { articles, status } = this.props
+
+ let articlesEl = articles.map(article => {
+ let tags = Array.isArray(article.Tags) && article.Tags.length > 0 ? article.Tags.map(tag => {
+ return (
+ #{tag.name}
+ )
+ }) : (
+ Not tagged yet
+ )
+
+ return (
+
+
+
+
+ by
{article.User.profileName}
+ {article.status != null ? article.status : moment(article.updatedAt).fromNow()}
+
+
+
{article.status !== 'new' ? article.title : '(New article)'}
+
+
+
+
+
+ )
+ })
-class ArticleList extends React.Component {
- render() {
return (
-
+
+ { status.mode === 'CREATE_MODE' ? (
+
+ ) : null}
+ {articlesEl}
+
)
}
}
-export default ArticleList
+ArticleList.propTypes = {
+ articles: PropTypes.array
+}
diff --git a/browser/main/HomeContainer/Components/ArticleNavigator.js b/browser/main/HomeContainer/Components/ArticleNavigator.js
index 1c1ef5c8..8ff56a80 100644
--- a/browser/main/HomeContainer/Components/ArticleNavigator.js
+++ b/browser/main/HomeContainer/Components/ArticleNavigator.js
@@ -6,7 +6,6 @@ export default class ArticleNavigator extends React.Component {
render () {
let { user, status } = this.props
if (user == null) return ()
- console.log(user.Folders)
let activeFolder = findWhere(user.Folders, {id: status.folderId})
@@ -68,5 +67,8 @@ export default class ArticleNavigator extends React.Component {
}
ArticleNavigator.propTypes = {
- user: PropTypes.object
+ user: PropTypes.object,
+ state: PropTypes.shape({
+ folderId: PropTypes.number
+ })
}
diff --git a/browser/main/HomeContainer/actions.js b/browser/main/HomeContainer/actions.js
index ba6eeb5e..55a4a497 100644
--- a/browser/main/HomeContainer/actions.js
+++ b/browser/main/HomeContainer/actions.js
@@ -1,6 +1,12 @@
export const USER_UPDATE = 'USER_UPDATE'
+export const ARTICLE_UPDATE = 'ARTICLE_UPDATE'
export const SWITCH_USER = 'SWITCH_USER'
export const SWITCH_FOLDER = 'SWITCH_FOLDER'
+export const SWITCH_MODE = 'SWITCH_MODE'
+
+export const IDLE_MODE = 'IDLE_MODE'
+export const CREATE_MODE = 'CREATE_MODE'
+export const EDIT_MODE = 'EDIT_MODE'
export function updateUser (user) {
return {
@@ -9,6 +15,13 @@ export function updateUser (user) {
}
}
+export function updateArticles (userId, articles) {
+ return {
+ type: ARTICLE_UPDATE,
+ data: {userId, articles}
+ }
+}
+
export function switchUser (userId) {
return {
type: SWITCH_USER,
@@ -22,3 +35,10 @@ export function switchFolder (folderId) {
data: folderId
}
}
+
+export function switchMode (mode) {
+ return {
+ type: SWITCH_MODE,
+ data: mode
+ }
+}
diff --git a/browser/main/HomeContainer/index.js b/browser/main/HomeContainer/index.js
index 59bd72f7..6bbb5319 100644
--- a/browser/main/HomeContainer/index.js
+++ b/browser/main/HomeContainer/index.js
@@ -27,15 +27,15 @@ class HomeContainer extends React.Component {
}
render () {
- const { users, user, status } = this.props
+ const { users, user, status, articles, article } = this.props
return (
)
}
@@ -49,11 +49,17 @@ function remap (state) {
let users = [currentUser, ...teams]
let user = findWhere(users, {id: parseInt(status.userId, 10)})
+ if (user == null) user = users[0]
+ let articles = state.articles['team-' + user.id]
+ let article = findWhere(users, {id: status.articleId})
+ if (article == null) article = articles[0]
return {
users,
user,
- status
+ status,
+ articles,
+ article
}
}
@@ -67,6 +73,7 @@ HomeContainer.propTypes = {
userId: PropTypes.string,
folderId: PropTypes.number
}),
+ articles: PropTypes.array,
dispatch: PropTypes.func
}
diff --git a/browser/main/HomeContainer/lib/api.js b/browser/main/HomeContainer/lib/api.js
index b212b5c1..7d5cfdf2 100644
--- a/browser/main/HomeContainer/lib/api.js
+++ b/browser/main/HomeContainer/lib/api.js
@@ -1,6 +1,22 @@
var request = require('superagent-promise')(require('superagent'), Promise)
var apiUrl = require('../../../../config').apiUrl
+export function fetchCurrentUser () {
+ return request
+ .get(apiUrl + 'auth/user')
+ .set({
+ Authorization: 'Bearer ' + localStorage.getItem('token')
+ })
+}
+
+export function fetchArticles (userId) {
+ return request
+ .get(apiUrl + 'teams/' + userId + '/articles')
+ .set({
+ Authorization: 'Bearer ' + localStorage.getItem('token')
+ })
+}
+
export function createTeam (input) {
return request
.post(apiUrl + 'teams')
diff --git a/browser/main/HomeContainer/lib/markdown.js b/browser/main/HomeContainer/lib/markdown.js
new file mode 100644
index 00000000..2f17530e
--- /dev/null
+++ b/browser/main/HomeContainer/lib/markdown.js
@@ -0,0 +1,11 @@
+import markdownit from 'markdown-it'
+
+var md = markdownit({
+ typographer: true,
+ linkify: true
+})
+
+export default function markdown (content) {
+ if (content == null) content = ''
+ return md.render(content.toString())
+}
diff --git a/browser/main/HomeContainer/reducer.js b/browser/main/HomeContainer/reducer.js
index 329fa448..166f48e9 100644
--- a/browser/main/HomeContainer/reducer.js
+++ b/browser/main/HomeContainer/reducer.js
@@ -1,8 +1,17 @@
import { combineReducers } from 'redux'
-import { SWITCH_USER, SWITCH_FOLDER, USER_UPDATE } from './actions'
+import { SWITCH_USER, SWITCH_FOLDER, SWITCH_MODE, USER_UPDATE, ARTICLE_UPDATE, IDLE_MODE, CREATE_MODE, EDIT_MODE } from './actions'
const initialCurrentUser = JSON.parse(localStorage.getItem('currentUser'))
-const initialParams = {}
+const initialStatus = {
+ mode: IDLE_MODE
+}
+// init articles
+let teams = Array.isArray(initialCurrentUser.Teams) ? initialCurrentUser.Teams : []
+let users = [initialCurrentUser, ...teams]
+const initialArticles = users.reduce((res, user) => {
+ res['team-' + user.id] = JSON.parse(localStorage.getItem('team-' + user.id))
+ return res
+}, {})
function currentUser (state, action) {
switch (action.type) {
@@ -26,13 +35,31 @@ function status (state, action) {
case SWITCH_FOLDER:
state.folderId = action.data
return state
+ case SWITCH_MODE:
+ state.mode = action.data
+ return state
default:
- if (state == null) return initialParams
+ if (state == null) return initialStatus
+ return state
+ }
+}
+
+function articles (state, action) {
+ switch (action.type) {
+ case ARTICLE_UPDATE:
+ let { userId, articles } = action.data
+ let teamKey = 'team-' + userId
+ localStorage.setItem(teamKey, JSON.stringify(articles))
+ state[teamKey] = articles
+ return state
+ default:
+ if (state == null) return initialArticles
return state
}
}
export default combineReducers({
currentUser,
- status
+ status,
+ articles
})
diff --git a/browser/main/index.js b/browser/main/index.js
index ca966b99..82f7c834 100644
--- a/browser/main/index.js
+++ b/browser/main/index.js
@@ -1,9 +1,9 @@
import React from 'react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
-import { updateUser } from './HomeContainer/actions'
+import { updateUser, updateArticles } from './HomeContainer/actions'
import reducer from './HomeContainer/reducer'
-import Hq from './Services/Hq'
+import { fetchCurrentUser, fetchArticles } from './HomeContainer/lib/api'
import { Router, Route, IndexRoute } from 'react-router'
import MainContainer from './Containers/MainContainer'
import LoginContainer from './Containers/LoginContainer'
@@ -58,9 +58,22 @@ React.render((
loadingCover.parentNode.removeChild(loadingCover)
// Refresh user information
- Hq.getUser()
+ fetchCurrentUser()
.then(function (res) {
- store.dispatch(updateUser(res.body))
+ let user = res.body
+ store.dispatch(updateUser(user))
+
+ let users = [user].concat(user.Teams)
+ users.forEach(user => {
+ fetchArticles(user.id)
+ .then(res => {
+ store.dispatch(updateArticles(user.id, res.body))
+ })
+ .catch(err => {
+ if (err.status == null) throw err
+ console.error(err)
+ })
+ })
})
.catch(function (err) {
console.error(err.message)
diff --git a/browser/styles/main/HomeContainer/components/ArticleDetail.styl b/browser/styles/main/HomeContainer/components/ArticleDetail.styl
index bf58b525..3783ca2a 100644
--- a/browser/styles/main/HomeContainer/components/ArticleDetail.styl
+++ b/browser/styles/main/HomeContainer/components/ArticleDetail.styl
@@ -5,6 +5,9 @@ noTagsColor = #999
top 60px
left 510px
padding 10px
+ background-color #E6E6E6
+ border-top 1px solid borderColor
+ border-left 1px solid borderColor
*
-webkit-user-select all
.detailInfo
diff --git a/browser/styles/main/HomeContainer/components/ArticleList.styl b/browser/styles/main/HomeContainer/components/ArticleList.styl
index 876b5b70..d7ad7c05 100644
--- a/browser/styles/main/HomeContainer/components/ArticleList.styl
+++ b/browser/styles/main/HomeContainer/components/ArticleList.styl
@@ -6,65 +6,65 @@ articleItemColor = #777
top 60px
left 260px
width 250px
- border-right solid 1px highlightenBorderColor
- &>ul
- absolute top bottom left right
- overflow-y auto
- noSelect()
- li
- .articleItem
- border solid 2px transparent
- position relative
- height 88px
- width 100%
- cursor pointer
- transition 0.1s
- background-color white
- padding 0 10px
- font-size 12px
- .top
- clearfix()
+ border-top 1px solid borderColor
+ border-right 4px solid #E6E6E6
+ overflow-y auto
+ noSelect()
+ &>div
+ border-right 1px solid borderColor
+ .articleItem
+ border solid 2px transparent
+ position relative
+ height 88px
+ width 100%
+ cursor pointer
+ transition 0.1s
+ background-color white
+ padding 0 10px
+ font-size 12px
+ .top
+ clearfix()
+ line-height 20px
+ padding 5px 0
+ color articleItemColor
+ .profileImage
+ vertical-align middle
+ .updatedAt
+ float right
line-height 20px
- padding 5px 0
+ .middle
+ clearfix()
+ padding 3px 0 7px
+ font-size 16px
+ .mode
+ float left
+ font-size 12px
+ line-height 16px
+ .title
+ float left
+ overflow ellipsis
+ padding 0 5px
+ .bottom
+ padding 5px 0
+ overflow-x auto
+ white-space nowrap
+ .tags
color articleItemColor
- .profileImage
- vertical-align middle
- .updatedAt
- float right
- line-height 20px
- .middle
- clearfix()
- padding 3px 0 7px
- font-size 16px
- .mode
- float left
- font-size 12px
- line-height 16px
- .title
- float left
- overflow ellipsis
- padding 0 5px
- .bottom
- padding 5px 0
- overflow-x auto
- white-space nowrap
- .tags
- color articleItemColor
- a
- background-color brandColor
- color white
- border-radius 2px
- padding 1.5px 5px
- margin 2px
- font-size 10px
- opacity 0.8
- &:hover
- opacity 1
- &:hover, &.hover
- background-color articleItemHoverBgColor
- &:active, &.active
- background-color white
+ a
+ background-color brandColor
+ color white
+ border-radius 2px
+ padding 1.5px 5px
+ margin 2px
+ font-size 10px
+ opacity 0.8
+ &:hover
+ opacity 1
+ &:hover, &.hover
+ background-color articleItemHoverBgColor
&:active, &.active
- border-color brandBorderColor
- .divider
- border-bottom solid 1px borderColor
+ background-color white
+ &:active, &.active
+ border-color brandBorderColor
+ .divider
+ border-bottom solid 1px borderColor
diff --git a/browser/styles/main/HomeContainer/components/ArticleNavigator.styl b/browser/styles/main/HomeContainer/components/ArticleNavigator.styl
index d65b5280..a2f02d45 100644
--- a/browser/styles/main/HomeContainer/components/ArticleNavigator.styl
+++ b/browser/styles/main/HomeContainer/components/ArticleNavigator.styl
@@ -5,6 +5,7 @@ articleNavBgColor = #353535
absolute top bottom
left 60px
width 200px
+ border-right 1px solid borderColor
color white
.userInfo
height 60px
@@ -48,7 +49,7 @@ articleNavBgColor = #353535
.header
border-bottom 1px solid borderColor
padding-bottom 5px
- margin-bottom 5px
+ margin-bottom 10px
clearfix()
.title
float left
@@ -75,8 +76,8 @@ articleNavBgColor = #353535
.folders
margin-bottom 15px
.folderList button
- height 44px
- width 200px
+ height 33px
+ width 199px
border none
text-align left
font-size 14px
diff --git a/webpack.config.js b/webpack.config.js
index 962a8110..e711ec55 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -43,7 +43,9 @@ module.exports = {
'react-transform-catch-errors',
'redux-devtools',
'redux-devtools/lib/react',
- 'react-select'
+ 'react-select',
+ 'markdown-it',
+ 'moment'
],
resolve: {
extensions: ['', '.js', '.jsx', 'styl']