mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
new folder modal / key indexing for article / login bugfix
This commit is contained in:
@@ -8,13 +8,27 @@ import ArticleList from './HomePage/ArticleList'
|
|||||||
import ArticleDetail from './HomePage/ArticleDetail'
|
import ArticleDetail from './HomePage/ArticleDetail'
|
||||||
import { findWhere, findIndex, pick } from 'lodash'
|
import { findWhere, findIndex, pick } from 'lodash'
|
||||||
import keygen from 'boost/keygen'
|
import keygen from 'boost/keygen'
|
||||||
import { NEW } from './actions'
|
import { NEW, refreshArticles } from './actions'
|
||||||
|
import api from 'boost/api'
|
||||||
|
|
||||||
class HomeContainer extends React.Component {
|
class HomePage extends React.Component {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { dispatch } = this.props
|
const { dispatch } = this.props
|
||||||
|
|
||||||
dispatch(switchUser(this.props.params.userId))
|
dispatch(switchUser(this.props.params.userId))
|
||||||
|
|
||||||
|
let currentUser = JSON.parse(localStorage.getItem('currentUser'))
|
||||||
|
let users = [currentUser].concat(currentUser.Teams)
|
||||||
|
users.forEach(user => {
|
||||||
|
api.fetchArticles(user.id)
|
||||||
|
.then(res => {
|
||||||
|
dispatch(refreshArticles(user.id, res.body))
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (err.status == null) throw err
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
@@ -29,7 +43,7 @@ class HomeContainer extends React.Component {
|
|||||||
const { dispatch, status, users, activeUser, articles, activeArticle } = this.props
|
const { dispatch, status, users, activeUser, articles, activeArticle } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='HomeContainer'>
|
<div className='HomePage'>
|
||||||
<UserNavigator users={users} />
|
<UserNavigator users={users} />
|
||||||
<ArticleNavigator dispatch={dispatch} activeUser={activeUser} status={status}/>
|
<ArticleNavigator dispatch={dispatch} activeUser={activeUser} status={status}/>
|
||||||
<ArticleTopBar/>
|
<ArticleTopBar/>
|
||||||
@@ -44,13 +58,17 @@ function remap (state) {
|
|||||||
let status = state.status
|
let status = state.status
|
||||||
|
|
||||||
let currentUser = state.currentUser
|
let currentUser = state.currentUser
|
||||||
|
if (currentUser == null) return state
|
||||||
let teams = Array.isArray(currentUser.Teams) ? currentUser.Teams : []
|
let teams = Array.isArray(currentUser.Teams) ? currentUser.Teams : []
|
||||||
|
|
||||||
let users = [currentUser, ...teams]
|
let users = [currentUser, ...teams]
|
||||||
let activeUser = findWhere(users, {id: parseInt(status.userId, 10)})
|
let activeUser = findWhere(users, {id: parseInt(status.userId, 10)})
|
||||||
if (activeUser == null) activeUser = users[0]
|
if (activeUser == null) activeUser = users[0]
|
||||||
|
|
||||||
let articles = state.articles['team-' + activeUser.id]
|
let articles = state.articles['team-' + activeUser.id]
|
||||||
let activeArticle = findWhere(articles, {id: status.articleId})
|
if (articles == null) articles = []
|
||||||
|
|
||||||
|
let activeArticle = findWhere(articles, {key: status.articleKey})
|
||||||
if (activeArticle == null) activeArticle = articles[0]
|
if (activeArticle == null) activeArticle = articles[0]
|
||||||
|
|
||||||
// remove Unsaved new article if user is not CREATE_MODE
|
// remove Unsaved new article if user is not CREATE_MODE
|
||||||
@@ -68,7 +86,8 @@ function remap (state) {
|
|||||||
var newArticle = findWhere(articles, {status: 'NEW'})
|
var newArticle = findWhere(articles, {status: 'NEW'})
|
||||||
if (newArticle == null) {
|
if (newArticle == null) {
|
||||||
newArticle = {
|
newArticle = {
|
||||||
id: keygen(),
|
id: null,
|
||||||
|
key: keygen(),
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
mode: 'markdown',
|
mode: 'markdown',
|
||||||
@@ -84,16 +103,18 @@ function remap (state) {
|
|||||||
status.mode = IDLE_MODE
|
status.mode = IDLE_MODE
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
let props = {
|
||||||
users,
|
users,
|
||||||
activeUser,
|
activeUser,
|
||||||
status,
|
status,
|
||||||
articles,
|
articles,
|
||||||
activeArticle
|
activeArticle
|
||||||
}
|
}
|
||||||
|
console.log(props)
|
||||||
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
HomeContainer.propTypes = {
|
HomePage.propTypes = {
|
||||||
users: PropTypes.array,
|
users: PropTypes.array,
|
||||||
activeUser: PropTypes.object,
|
activeUser: PropTypes.object,
|
||||||
params: PropTypes.shape({
|
params: PropTypes.shape({
|
||||||
@@ -108,4 +129,4 @@ HomeContainer.propTypes = {
|
|||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(remap)(HomeContainer)
|
export default connect(remap)(HomePage)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ var modeOptions = aceModes.map(function (mode) {
|
|||||||
|
|
||||||
function makeInstantArticle (article) {
|
function makeInstantArticle (article) {
|
||||||
let instantArticle = Object.assign({}, article)
|
let instantArticle = Object.assign({}, article)
|
||||||
instantArticle.Tags = instantArticle.Tags.map(tag => tag.name)
|
instantArticle.Tags = typeof instantArticle.Tags === 'array' ? instantArticle.Tags.map(tag => tag.name) : []
|
||||||
return instantArticle
|
return instantArticle
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +102,12 @@ export default class ArticleDetail extends React.Component {
|
|||||||
<div className='deleteConfirm'>
|
<div className='deleteConfirm'>
|
||||||
<div className='right'>
|
<div className='right'>
|
||||||
Are you sure to delete this article?
|
Are you sure to delete this article?
|
||||||
<button onClick={e => this.handleDeleteConfirmButtonClick(e)} className='primary'><i className='fa fa-fw fa-check'/> Sure</button>
|
<button onClick={e => this.handleDeleteConfirmButtonClick(e)} className='primary'>
|
||||||
<button onClick={e => this.handleDeleteCancleButtonClick(e)}><i className='fa fa-fw fa-times'/> Cancle</button>
|
<i className='fa fa-fw fa-check'/> Sure
|
||||||
|
</button>
|
||||||
|
<button onClick={e => this.handleDeleteCancleButtonClick(e)}>
|
||||||
|
<i className='fa fa-fw fa-times'/> Cancle
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -133,7 +137,10 @@ export default class ArticleDetail extends React.Component {
|
|||||||
<ModeIcon className='mode' mode={activeArticle.mode}/>
|
<ModeIcon className='mode' mode={activeArticle.mode}/>
|
||||||
<div className='title'>{activeArticle.title}</div>
|
<div className='title'>{activeArticle.title}</div>
|
||||||
</div>
|
</div>
|
||||||
{activeArticle.mode === 'markdown' ? <MarkdownPreview content={activeArticle.content}/> : <CodeEditor readOnly={true} onChange={this.handleContentChange} mode={activeArticle.mode} code={activeArticle.content}/>}
|
{activeArticle.mode === 'markdown'
|
||||||
|
? <MarkdownPreview content={activeArticle.content}/>
|
||||||
|
: <CodeEditor readOnly={true} onChange={this.handleContentChange} mode={activeArticle.mode} code={activeArticle.content}/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,7 +154,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
handleSaveButtonClick (e) {
|
handleSaveButtonClick (e) {
|
||||||
let { activeArticle } = this.props
|
let { activeArticle } = this.props
|
||||||
|
|
||||||
if (typeof activeArticle.id === 'string') this.saveAsNew()
|
if (activeArticle.id == null) this.saveAsNew()
|
||||||
else this.save()
|
else this.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +261,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
<div className='detailInfo'>
|
<div className='detailInfo'>
|
||||||
<div className='left'>
|
<div className='left'>
|
||||||
<Select ref='folder' onChange={value => this.handleFolderIdChange(value)} clearable={false} placeholder='select folder...' options={folderOptions} value={this.state.article.FolderId} className='folder'/>
|
<Select ref='folder' onChange={value => this.handleFolderIdChange(value)} clearable={false} placeholder='select folder...' options={folderOptions} value={this.state.article.FolderId} className='folder'/>
|
||||||
<Select onChange={(tag, tags) => this.handleTagsChange(tag, tags)} clearable={false} multi={true} placeholder='add some tags...' allowCreate={true} value={this.state.article.Tags} className='tags'/>
|
<Select onChange={(tag, tags) => this.handleTagsChange(tag, tags)} clearable={false} multi placeholder='add some tags...' allowCreate value={this.state.article.Tags} className='tags'/>
|
||||||
</div>
|
</div>
|
||||||
<div className='right'>
|
<div className='right'>
|
||||||
<button onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
|
<button onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import moment from 'moment'
|
|||||||
import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchArticle, NEW } from '../actions'
|
import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchArticle, NEW } from '../actions'
|
||||||
|
|
||||||
export default class ArticleList extends React.Component {
|
export default class ArticleList extends React.Component {
|
||||||
handleArticleClick (id) {
|
handleArticleClick (key) {
|
||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
return function (e) {
|
return function (e) {
|
||||||
dispatch(switchArticle(id))
|
dispatch(switchArticle(key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,8 +25,8 @@ export default class ArticleList extends React.Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={'article-' + article.id}>
|
<div key={'article-' + article.key}>
|
||||||
<div onClick={e => this.handleArticleClick(article.id)(e)} className={'articleItem' + (activeArticle.id === article.id ? ' active' : '')}>
|
<div onClick={e => this.handleArticleClick(article.key)(e)} className={'articleItem' + (activeArticle.key === article.key ? ' active' : '')}>
|
||||||
<div className='top'>
|
<div className='top'>
|
||||||
<i className='fa fa-fw fa-square'/>
|
<i className='fa fa-fw fa-square'/>
|
||||||
by <ProfileImage className='profileImage' size='20' email={article.User.email}/> {article.User.profileName}
|
by <ProfileImage className='profileImage' size='20' email={article.User.email}/> {article.User.profileName}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { PropTypes } from 'react'
|
|||||||
import ProfileImage from 'boost/components/ProfileImage'
|
import ProfileImage from 'boost/components/ProfileImage'
|
||||||
import { findWhere } from 'lodash'
|
import { findWhere } from 'lodash'
|
||||||
import { switchMode, CREATE_MODE } from '../actions'
|
import { switchMode, CREATE_MODE } from '../actions'
|
||||||
|
import { openModal } from 'boost/modal'
|
||||||
|
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
|
||||||
|
|
||||||
export default class ArticleNavigator extends React.Component {
|
export default class ArticleNavigator extends React.Component {
|
||||||
handleNewPostButtonClick (e) {
|
handleNewPostButtonClick (e) {
|
||||||
@@ -10,6 +12,11 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
dispatch(switchMode(CREATE_MODE))
|
dispatch(switchMode(CREATE_MODE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNewFolderButton (e) {
|
||||||
|
let { activeUser } = this.props
|
||||||
|
openModal(CreateNewFolder, {user: activeUser})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { activeUser, status } = this.props
|
let { activeUser, status } = this.props
|
||||||
if (activeUser == null) return (<div className='ArticleNavigator'/>)
|
if (activeUser == null) return (<div className='ArticleNavigator'/>)
|
||||||
@@ -48,7 +55,7 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
<div className='folders'>
|
<div className='folders'>
|
||||||
<div className='header'>
|
<div className='header'>
|
||||||
<div className='title'>Folders</div>
|
<div className='title'>Folders</div>
|
||||||
<button className='addBtn'><i className='fa fa-fw fa-plus'/></button>
|
<button onCLick={e => this.handleNewFolderButton(e)} className='addBtn'><i className='fa fa-fw fa-plus'/></button>
|
||||||
</div>
|
</div>
|
||||||
<div className='folderList'>
|
<div className='folderList'>
|
||||||
<button className={activeFolder == null ? 'active' : ''}>All folders</button>
|
<button className={activeFolder == null ? 'active' : ''}>All folders</button>
|
||||||
@@ -75,7 +82,7 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
|
|
||||||
ArticleNavigator.propTypes = {
|
ArticleNavigator.propTypes = {
|
||||||
activeUser: PropTypes.object,
|
activeUser: PropTypes.object,
|
||||||
state: PropTypes.shape({
|
status: PropTypes.shape({
|
||||||
folderId: PropTypes.number
|
folderId: PropTypes.number
|
||||||
}),
|
}),
|
||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
|
|||||||
@@ -22,40 +22,45 @@ export default class LoginPage extends React.Component {
|
|||||||
error: null
|
error: null
|
||||||
}, function () {
|
}, function () {
|
||||||
login(this.state.user)
|
login(this.state.user)
|
||||||
.then(function (res) {
|
.then(res => {
|
||||||
localStorage.setItem('token', res.body.token)
|
localStorage.setItem('token', res.body.token)
|
||||||
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
|
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
|
||||||
|
|
||||||
try {
|
|
||||||
this.props.history.pushState('home')
|
this.props.history.pushState('home')
|
||||||
} catch (e) {
|
})
|
||||||
console.error(e)
|
.catch(err => {
|
||||||
}
|
|
||||||
}.bind(this))
|
|
||||||
.catch(function (err) {
|
|
||||||
console.error(err)
|
console.error(err)
|
||||||
if (err.response == null) {
|
if (err.code === 'ECONNREFUSED') {
|
||||||
return this.setState({
|
return this.setState({
|
||||||
error: {name: 'CunnectionRefused', message: 'Can\'t connect to API server.'},
|
error: {
|
||||||
|
name: 'CunnectionRefused',
|
||||||
|
message: 'Can\'t connect to API server.'
|
||||||
|
},
|
||||||
isSending: false
|
isSending: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
else if (err.status != null) {
|
||||||
// Connection Failed or Whatever
|
return this.setState({
|
||||||
this.setState({
|
error: {
|
||||||
error: err.response.body,
|
name: err.response.body.name,
|
||||||
|
message: err.response.body.message
|
||||||
|
},
|
||||||
isSending: false
|
isSending: false
|
||||||
})
|
})
|
||||||
}.bind(this))
|
}
|
||||||
|
else throw err
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className='LoginContainer'>
|
<div className='LoginContainer'>
|
||||||
<img className='logo' src='resources/favicon-230x230.png'/>
|
<img className='logo' src='../../resources/favicon-230x230.png'/>
|
||||||
|
|
||||||
<nav className='authNavigator text-center'><Link to='/login' activeClassName='active'>Log In</Link> / <Link to='/signup' activeClassName='active'>Sign Up</Link></nav>
|
<nav className='authNavigator text-center'>
|
||||||
|
<Link to='/login' activeClassName='active'>Log In</Link> / <Link to='/signup' activeClassName='active'>Sign Up</Link>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<form onSubmit={e => this.handleSubmit(e)}>
|
<form onSubmit={e => this.handleSubmit(e)}>
|
||||||
<div className='formField'>
|
<div className='formField'>
|
||||||
@@ -65,7 +70,8 @@ export default class LoginPage extends React.Component {
|
|||||||
<input valueLink={this.linkState('user.password')} onChange={this.handleChange} type='password' placeholder='Password'/>
|
<input valueLink={this.linkState('user.password')} onChange={this.handleChange} type='password' placeholder='Password'/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.state.isSending ? (
|
{this.state.isSending
|
||||||
|
? (
|
||||||
<p className='alertInfo'>Logging in...</p>
|
<p className='alertInfo'>Logging in...</p>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|||||||
@@ -30,23 +30,30 @@ export default class SignupContainer extends React.Component {
|
|||||||
localStorage.setItem('token', res.body.token)
|
localStorage.setItem('token', res.body.token)
|
||||||
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
|
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
|
||||||
|
|
||||||
this.props.history.pushState('userHome', {userId: res.body.user.id})
|
this.props.history.pushState('home')
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
if (err.response == null) {
|
if (err.code === 'ECONNREFUSED') {
|
||||||
return this.setState({
|
return this.setState({
|
||||||
error: {name: 'CunnectionRefused', message: 'Can\'t connect to API server.'},
|
error: {
|
||||||
|
name: 'CunnectionRefused',
|
||||||
|
message: 'Can\'t connect to API server.'
|
||||||
|
},
|
||||||
isSending: false
|
isSending: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
else if (err.status != null) {
|
||||||
// Connection Failed or Whatever
|
return this.setState({
|
||||||
this.setState({
|
error: {
|
||||||
error: err.response.body,
|
name: err.response.body.name,
|
||||||
|
message: err.response.body.message
|
||||||
|
},
|
||||||
isSending: false
|
isSending: false
|
||||||
})
|
})
|
||||||
}.bind(this))
|
}
|
||||||
|
else throw err
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@@ -55,7 +62,7 @@ export default class SignupContainer extends React.Component {
|
|||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className='SignupContainer'>
|
<div className='SignupContainer'>
|
||||||
<img className='logo' src='resources/favicon-230x230.png'/>
|
<img className='logo' src='../../resources/favicon-230x230.png'/>
|
||||||
|
|
||||||
<nav className='authNavigator text-center'><Link to='/login' activeClassName='active'>Log In</Link> / <Link to='/signup' activeClassName='active'>Sign Up</Link></nav>
|
<nav className='authNavigator text-center'><Link to='/login' activeClassName='active'>Log In</Link> / <Link to='/signup' activeClassName='active'>Sign Up</Link></nav>
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ require('../styles/main/index.styl')
|
|||||||
|
|
||||||
function onlyUser (state, replaceState) {
|
function onlyUser (state, replaceState) {
|
||||||
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
|
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
|
||||||
if (currentUser == null) replaceState('login', '/login')
|
if (currentUser == null) return replaceState('login', '/login')
|
||||||
if (state.location.pathname === '/') replaceState('user', '/users/' + currentUser.id)
|
if (state.location.pathname === '/') return replaceState('user', '/users/' + currentUser.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
let routes = (
|
let routes = (
|
||||||
@@ -62,18 +62,6 @@ React.render((
|
|||||||
.then(function (res) {
|
.then(function (res) {
|
||||||
let user = res.body
|
let user = res.body
|
||||||
store.dispatch(updateUser(user))
|
store.dispatch(updateUser(user))
|
||||||
|
|
||||||
let users = [user].concat(user.Teams)
|
|
||||||
users.forEach(user => {
|
|
||||||
fetchArticles(user.id)
|
|
||||||
.then(res => {
|
|
||||||
store.dispatch(refreshArticles(user.id, res.body))
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
if (err.status == null) throw err
|
|
||||||
console.error(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
console.error(err.message)
|
console.error(err.message)
|
||||||
|
|||||||
@@ -2,18 +2,29 @@ import { combineReducers } from 'redux'
|
|||||||
import { findIndex } from 'lodash'
|
import { findIndex } from 'lodash'
|
||||||
import { SWITCH_USER, SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, USER_UPDATE, ARTICLE_REFRESH, ARTICLE_UPDATE, ARTICLE_DESTROY, IDLE_MODE, CREATE_MODE } from './actions'
|
import { SWITCH_USER, SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, USER_UPDATE, ARTICLE_REFRESH, ARTICLE_UPDATE, ARTICLE_DESTROY, IDLE_MODE, CREATE_MODE } from './actions'
|
||||||
|
|
||||||
const initialCurrentUser = JSON.parse(localStorage.getItem('currentUser'))
|
|
||||||
const initialStatus = {
|
const initialStatus = {
|
||||||
mode: IDLE_MODE
|
mode: IDLE_MODE
|
||||||
}
|
}
|
||||||
// init articles
|
|
||||||
|
function getInitialCurrentUser () {
|
||||||
|
return JSON.parse(localStorage.getItem('currentUser'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInitialArticles () {
|
||||||
|
let initialCurrentUser = getInitialCurrentUser()
|
||||||
|
if (initialCurrentUser == null) return []
|
||||||
|
|
||||||
let teams = Array.isArray(initialCurrentUser.Teams) ? initialCurrentUser.Teams : []
|
let teams = Array.isArray(initialCurrentUser.Teams) ? initialCurrentUser.Teams : []
|
||||||
|
|
||||||
let users = [initialCurrentUser, ...teams]
|
let users = [initialCurrentUser, ...teams]
|
||||||
const initialArticles = users.reduce((res, user) => {
|
let initialArticles = users.reduce((res, user) => {
|
||||||
res['team-' + user.id] = JSON.parse(localStorage.getItem('team-' + user.id))
|
res['team-' + user.id] = JSON.parse(localStorage.getItem('team-' + user.id))
|
||||||
return res
|
return res
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
|
return initialArticles
|
||||||
|
}
|
||||||
|
|
||||||
function currentUser (state, action) {
|
function currentUser (state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case USER_UPDATE:
|
case USER_UPDATE:
|
||||||
@@ -21,7 +32,7 @@ function currentUser (state, action) {
|
|||||||
localStorage.setItem('currentUser', JSON.stringify(user))
|
localStorage.setItem('currentUser', JSON.stringify(user))
|
||||||
return user
|
return user
|
||||||
default:
|
default:
|
||||||
if (state == null) return initialCurrentUser
|
if (state == null) return getInitialCurrentUser()
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,10 +50,10 @@ function status (state, action) {
|
|||||||
return state
|
return state
|
||||||
case SWITCH_MODE:
|
case SWITCH_MODE:
|
||||||
state.mode = action.data
|
state.mode = action.data
|
||||||
if (state.mode === CREATE_MODE) state.articleId = null
|
if (state.mode === CREATE_MODE) state.articleKey = null
|
||||||
return state
|
return state
|
||||||
case SWITCH_ARTICLE:
|
case SWITCH_ARTICLE:
|
||||||
state.articleId = action.data
|
state.articleKey = action.data
|
||||||
state.mode = IDLE_MODE
|
state.mode = IDLE_MODE
|
||||||
return state
|
return state
|
||||||
default:
|
default:
|
||||||
@@ -94,7 +105,7 @@ function articles (state, action) {
|
|||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
default:
|
default:
|
||||||
if (state == null) return initialArticles
|
if (state == null) return getInitialArticles()
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ articleNavBgColor = #353535
|
|||||||
width 170px
|
width 170px
|
||||||
border-radius 5px
|
border-radius 5px
|
||||||
font-size 20px
|
font-size 20px
|
||||||
|
transition 0.1s
|
||||||
|
&:hover
|
||||||
|
background-color lighten(brandColor, 7%)
|
||||||
.folders, .members
|
.folders, .members
|
||||||
.header
|
.header
|
||||||
border-bottom 1px solid borderColor
|
border-bottom 1px solid borderColor
|
||||||
|
|||||||
@@ -6,3 +6,4 @@
|
|||||||
|
|
||||||
@require './lib/modal'
|
@require './lib/modal'
|
||||||
@require './lib/CreateNewTeam'
|
@require './lib/CreateNewTeam'
|
||||||
|
@require './lib/CreateNewFolder'
|
||||||
|
|||||||
75
browser/styles/main/HomeContainer/lib/CreateNewFolder.styl
Normal file
75
browser/styles/main/HomeContainer/lib/CreateNewFolder.styl
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
tabNavColor = #999999
|
||||||
|
iptFocusBorderColor = #369DCD
|
||||||
|
|
||||||
|
.CreateNewFolder.modal
|
||||||
|
width 600px
|
||||||
|
height 450px
|
||||||
|
.closeBtn
|
||||||
|
position absolute
|
||||||
|
top 15px
|
||||||
|
right 15px
|
||||||
|
width 33px
|
||||||
|
height 33px
|
||||||
|
font-size 18px
|
||||||
|
line-height 33px
|
||||||
|
padding 0
|
||||||
|
text-align center
|
||||||
|
background-color transparent
|
||||||
|
border none
|
||||||
|
color stripBtnColor
|
||||||
|
&:hover
|
||||||
|
color stripHoverBtnColor
|
||||||
|
.title
|
||||||
|
font-size 32px
|
||||||
|
text-align center
|
||||||
|
font-weight bold
|
||||||
|
margin-top 25px
|
||||||
|
.ipt
|
||||||
|
display block
|
||||||
|
width 330px
|
||||||
|
font-size 14px
|
||||||
|
height 44px
|
||||||
|
line-height 44px
|
||||||
|
padding 0 15px
|
||||||
|
border-radius 5px
|
||||||
|
border solid 1px borderColor
|
||||||
|
outline none
|
||||||
|
margin 100px auto 25px
|
||||||
|
&:focus
|
||||||
|
border-color iptFocusBorderColor
|
||||||
|
.public
|
||||||
|
margin 0 auto
|
||||||
|
text-align center
|
||||||
|
button
|
||||||
|
border none
|
||||||
|
background-color transparent
|
||||||
|
font-size 18px
|
||||||
|
color inactiveTextColor
|
||||||
|
transition 0.1s
|
||||||
|
button:hover
|
||||||
|
font-size 22px
|
||||||
|
button.active
|
||||||
|
color brandColor
|
||||||
|
font-size 22px
|
||||||
|
.divider
|
||||||
|
margin 0 5px
|
||||||
|
.confirmBtn
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
left 180px
|
||||||
|
bottom 44px
|
||||||
|
width 240px
|
||||||
|
font-size 24px
|
||||||
|
height 44px
|
||||||
|
line-height 24px
|
||||||
|
font-weight bold
|
||||||
|
background-color brandColor
|
||||||
|
color white
|
||||||
|
border none
|
||||||
|
border-radius 5px
|
||||||
|
margin 0 auto
|
||||||
|
transition 0.1s
|
||||||
|
&:hover
|
||||||
|
transform scale(1.1)
|
||||||
|
&:disabled
|
||||||
|
opacity 0.7
|
||||||
@@ -157,7 +157,7 @@ stripBtnColor = lighten(stripHoverBtnColor, 35%)
|
|||||||
height 44px
|
height 44px
|
||||||
padding 0 25px
|
padding 0 25px
|
||||||
clearfix()
|
clearfix()
|
||||||
&nth-last-child(1)
|
&:nth-last-child(1)
|
||||||
border-bottom-color transparent
|
border-bottom-color transparent
|
||||||
.userPhoto
|
.userPhoto
|
||||||
width 30px
|
width 30px
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ marked()
|
|||||||
margin-bottom 35px
|
margin-bottom 35px
|
||||||
li
|
li
|
||||||
display list-item
|
display list-item
|
||||||
margin 15px 0
|
line-height 1.8em
|
||||||
&>li>ul
|
&>li>ul
|
||||||
list-style-type circle
|
list-style-type circle
|
||||||
&>li>ul
|
&>li>ul
|
||||||
@@ -57,7 +57,7 @@ marked()
|
|||||||
margin-bottom 35px
|
margin-bottom 35px
|
||||||
li
|
li
|
||||||
display list-item
|
display list-item
|
||||||
margin 15px 0
|
line-height 1.8em
|
||||||
code
|
code
|
||||||
font-family monospace
|
font-family monospace
|
||||||
padding 2px 4px
|
padding 2px 4px
|
||||||
|
|||||||
10
lib/api.js
10
lib/api.js
@@ -88,6 +88,15 @@ export function deleteMember (teamId, input) {
|
|||||||
.send(input)
|
.send(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createFolder (input) {
|
||||||
|
return request
|
||||||
|
.post(apiUrl + 'folders/')
|
||||||
|
.set({
|
||||||
|
Authorization: 'Bearer ' + localStorage.getItem('token')
|
||||||
|
})
|
||||||
|
.send(input)
|
||||||
|
}
|
||||||
|
|
||||||
export function sendEmail (input) {
|
export function sendEmail (input) {
|
||||||
return request
|
return request
|
||||||
.post(apiUrl + 'mail')
|
.post(apiUrl + 'mail')
|
||||||
@@ -109,5 +118,6 @@ export default {
|
|||||||
searchUser,
|
searchUser,
|
||||||
setMember,
|
setMember,
|
||||||
deleteMember,
|
deleteMember,
|
||||||
|
createFolder,
|
||||||
sendEmail
|
sendEmail
|
||||||
}
|
}
|
||||||
|
|||||||
88
lib/components/modal/CreateNewFolder.js
Normal file
88
lib/components/modal/CreateNewFolder.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import linkState from 'boost/linkState'
|
||||||
|
import api from 'boost/api'
|
||||||
|
import { pick } from 'lodash'
|
||||||
|
|
||||||
|
export default class CreateNewFolder extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
name: '',
|
||||||
|
public: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePublicButtonClick (value) {
|
||||||
|
console.log(value)
|
||||||
|
return e => {
|
||||||
|
this.setState({public: value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirmButton (e) {
|
||||||
|
let { user, close } = this.props
|
||||||
|
let input = pick(this.state, ['public', 'name'])
|
||||||
|
input.UserId = user.id
|
||||||
|
|
||||||
|
api.createFolder(input)
|
||||||
|
.then(res => {
|
||||||
|
console.log(res)
|
||||||
|
close()
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err)
|
||||||
|
if (err.code === '') {
|
||||||
|
let alert = {
|
||||||
|
type: 'error',
|
||||||
|
message: 'Can\'t connect to API server.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (err.status != null) {
|
||||||
|
let alert = {
|
||||||
|
type: 'error',
|
||||||
|
message: err.response.body.message
|
||||||
|
}
|
||||||
|
this.setState({alert: alert})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
let alert = this.state.alert
|
||||||
|
let alertEl = alert != null ? (
|
||||||
|
<p className={`alert ${alert.type}`}>
|
||||||
|
{alert.message}
|
||||||
|
</p>
|
||||||
|
) : null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='CreateNewFolder modal'>
|
||||||
|
<button className='closeBtn'><i className='fa fa-fw fa-times'/></button>
|
||||||
|
|
||||||
|
<div className='title'>Create new folder</div>
|
||||||
|
|
||||||
|
<input className='ipt' type='text' valueLink={this.linkState('name')} placeholder='Enter folder name'/>
|
||||||
|
|
||||||
|
<div className='public'>
|
||||||
|
<button className={!this.state.public ? 'active' : ''} onClick={e => this.handlePublicButtonClick(false)(e)}>Private</button>
|
||||||
|
<span className='divider'>/</span>
|
||||||
|
<button className={this.state.public ? 'active' : ''} onClick={e => this.handlePublicButtonClick(true)(e)}>Public</button>
|
||||||
|
</div>
|
||||||
|
{alertEl}
|
||||||
|
|
||||||
|
<button onClick={e => this.handleConfirmButton(e)} className='confirmBtn'>Create</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateNewFolder.propTypes = {
|
||||||
|
user: PropTypes.shape(),
|
||||||
|
close: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateNewFolder.prototype.linkState = linkState
|
||||||
@@ -144,7 +144,7 @@ export default class CreateNewTeam extends React.Component {
|
|||||||
role: 'member'
|
role: 'member'
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (e) {
|
return e => {
|
||||||
deleteMember(selectState.team.id, input)
|
deleteMember(selectState.team.id, input)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
let selectState = this.state.select
|
let selectState = this.state.select
|
||||||
@@ -162,7 +162,7 @@ export default class CreateNewTeam extends React.Component {
|
|||||||
if (err.status != null) throw err
|
if (err.status != null) throw err
|
||||||
else console.error(err)
|
else console.error(err)
|
||||||
})
|
})
|
||||||
}.bind(this)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMemberRoleChange (name) {
|
handleMemberRoleChange (name) {
|
||||||
|
|||||||
Reference in New Issue
Block a user