mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 10:16:26 +00:00
add Preferences modal(30%done) & move some modules (actions, reducer, socket, store -> lib/~)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React, { PropTypes} from 'react'
|
import React, { PropTypes} from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { CREATE_MODE, IDLE_MODE, switchUser } from './actions'
|
import { CREATE_MODE, IDLE_MODE, switchUser, NEW, refreshArticles } from 'boost/actions'
|
||||||
import UserNavigator from './HomePage/UserNavigator'
|
import UserNavigator from './HomePage/UserNavigator'
|
||||||
import ArticleNavigator from './HomePage/ArticleNavigator'
|
import ArticleNavigator from './HomePage/ArticleNavigator'
|
||||||
import ArticleTopBar from './HomePage/ArticleTopBar'
|
import ArticleTopBar from './HomePage/ArticleTopBar'
|
||||||
@@ -8,10 +8,9 @@ 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, refreshArticles } from './actions'
|
|
||||||
import api from 'boost/api'
|
import api from 'boost/api'
|
||||||
import auth from 'boost/auth'
|
import auth from 'boost/auth'
|
||||||
import './socket'
|
import 'boost/socket'
|
||||||
|
|
||||||
class HomePage extends React.Component {
|
class HomePage extends React.Component {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { findWhere, uniq } from 'lodash'
|
|||||||
import ModeIcon from 'boost/components/ModeIcon'
|
import ModeIcon from 'boost/components/ModeIcon'
|
||||||
import MarkdownPreview from 'boost/components/MarkdownPreview'
|
import MarkdownPreview from 'boost/components/MarkdownPreview'
|
||||||
import CodeEditor from 'boost/components/CodeEditor'
|
import CodeEditor from 'boost/components/CodeEditor'
|
||||||
import { UNSYNCED, IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, updateArticle, destroyArticle } from '../actions'
|
import { UNSYNCED, IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, updateArticle, destroyArticle } from 'boost/actions'
|
||||||
import aceModes from 'boost/ace-modes'
|
import aceModes from 'boost/ace-modes'
|
||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
import linkState from 'boost/linkState'
|
import linkState from 'boost/linkState'
|
||||||
@@ -20,7 +20,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 = (typeof instantArticle.Tags === 'array') ? instantArticle.Tags.map(tag => tag.name) : []
|
instantArticle.Tags = Array.isArray(instantArticle.Tags) ? instantArticle.Tags.map(tag => tag.name) : []
|
||||||
return instantArticle
|
return instantArticle
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
<i className='fa fa-fw fa-check'/> Sure
|
<i className='fa fa-fw fa-check'/> Sure
|
||||||
</button>
|
</button>
|
||||||
<button onClick={e => this.handleDeleteCancleButtonClick(e)}>
|
<button onClick={e => this.handleDeleteCancleButtonClick(e)}>
|
||||||
<i className='fa fa-fw fa-times'/> Cancle
|
<i className='fa fa-fw fa-times'/> Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'
|
|||||||
import ProfileImage from 'boost/components/ProfileImage'
|
import ProfileImage from 'boost/components/ProfileImage'
|
||||||
import ModeIcon from 'boost/components/ModeIcon'
|
import ModeIcon from 'boost/components/ModeIcon'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { switchArticle, NEW } from '../actions'
|
import { switchArticle, NEW } from 'boost/actions'
|
||||||
import FolderMark from 'boost/components/FolderMark'
|
import FolderMark from 'boost/components/FolderMark'
|
||||||
|
|
||||||
export default class ArticleList extends React.Component {
|
export default class ArticleList extends React.Component {
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
import React, { PropTypes } from 'react'
|
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 'boost/actions'
|
||||||
import { openModal } from 'boost/modal'
|
import { openModal } from 'boost/modal'
|
||||||
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
|
|
||||||
import FolderMark from 'boost/components/FolderMark'
|
import FolderMark from 'boost/components/FolderMark'
|
||||||
|
import Preferences from 'boost/components/modal/Preferences'
|
||||||
|
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
|
||||||
|
|
||||||
export default class ArticleNavigator extends React.Component {
|
export default class ArticleNavigator extends React.Component {
|
||||||
|
componentDidMount () {
|
||||||
|
this.handlePreferencesButtonClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePreferencesButtonClick (e) {
|
||||||
|
openModal(Preferences)
|
||||||
|
}
|
||||||
|
|
||||||
handleNewPostButtonClick (e) {
|
handleNewPostButtonClick (e) {
|
||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
|
|
||||||
@@ -27,7 +36,7 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
let folders = activeUser.Folders.map((folder, index) => {
|
let folders = activeUser.Folders.map((folder, index) => {
|
||||||
return (
|
return (
|
||||||
<button key={'folder-' + folder.id} className={activeFolder != null && activeFolder.id === folder.id ? 'active' : ''}>
|
<button key={'folder-' + folder.id} className={activeFolder != null && activeFolder.id === folder.id ? 'active' : ''}>
|
||||||
<FolderMark id={folder.id}/> {folder.name}</button>
|
<FolderMark id={folder.id}/> {folder.name} {folder.public ? <i className='fa fa-fw fa-lock'/> : null}</button>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -47,7 +56,7 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
<div className='userInfo'>
|
<div className='userInfo'>
|
||||||
<div className='userProfileName'>{activeUser.profileName}</div>
|
<div className='userProfileName'>{activeUser.profileName}</div>
|
||||||
<div className='userName'>{activeUser.name}</div>
|
<div className='userName'>{activeUser.name}</div>
|
||||||
<button className='settingBtn'><i className='fa fa-fw fa-chevron-down'/></button>
|
<button onClick={e => this.handlePreferencesButtonClick(e)} className='settingBtn'><i className='fa fa-fw fa-chevron-down'/></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='controlSection'>
|
<div className='controlSection'>
|
||||||
@@ -89,3 +98,4 @@ ArticleNavigator.propTypes = {
|
|||||||
}),
|
}),
|
||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,12 +53,16 @@
|
|||||||
<div id="content"></div>
|
<div id="content"></div>
|
||||||
|
|
||||||
<script src="../../submodules/ace/src-min/ace.js"></script>
|
<script src="../../submodules/ace/src-min/ace.js"></script>
|
||||||
<script>
|
<script type="text/javascript">
|
||||||
var version = require('remote').require('app').getVersion()
|
var version = require('remote').require('app').getVersion()
|
||||||
global.version = version
|
|
||||||
document.title = 'Boost' + ((version == null || version.length === 0) ? ' DEV' : '')
|
document.title = 'Boost' + ((version == null || version.length === 0) ? ' DEV' : '')
|
||||||
// require("babel-core/register")
|
// require("babel-core/register")
|
||||||
// require('./index')
|
// require('./index')
|
||||||
|
document.addEventListener('mousewheel', function(e) {
|
||||||
|
if(e.deltaY % 1 !== 0) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<script src="http://localhost:8080/assets/main.js"></script>
|
<script src="http://localhost:8080/assets/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { updateUser } from './actions'
|
import { updateUser } from 'boost/actions'
|
||||||
import { fetchCurrentUser } from 'boost/api'
|
import { fetchCurrentUser } from 'boost/api'
|
||||||
import { Router, Route, IndexRoute } from 'react-router'
|
import { Router, Route, IndexRoute } from 'react-router'
|
||||||
import MainPage from './MainPage'
|
import MainPage from './MainPage'
|
||||||
@@ -8,7 +8,7 @@ import LoginPage from './LoginPage'
|
|||||||
import SignupPage from './SignupPage'
|
import SignupPage from './SignupPage'
|
||||||
import HomePage from './HomePage'
|
import HomePage from './HomePage'
|
||||||
import auth from 'boost/auth'
|
import auth from 'boost/auth'
|
||||||
import store, { devToolElement } from './store'
|
import store, { devToolElement } from 'boost/store'
|
||||||
require('../styles/main/index.styl')
|
require('../styles/main/index.styl')
|
||||||
|
|
||||||
function onlyUser (state, replaceState) {
|
function onlyUser (state, replaceState) {
|
||||||
|
|||||||
@@ -7,3 +7,4 @@
|
|||||||
@require './lib/modal'
|
@require './lib/modal'
|
||||||
@require './lib/CreateNewTeam'
|
@require './lib/CreateNewTeam'
|
||||||
@require './lib/CreateNewFolder'
|
@require './lib/CreateNewFolder'
|
||||||
|
@require './lib/Preferences'
|
||||||
|
|||||||
126
browser/styles/main/HomeContainer/lib/Preferences.styl
Normal file
126
browser/styles/main/HomeContainer/lib/Preferences.styl
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
menuColor = #808080
|
||||||
|
menuBgColor = #E6E6E6
|
||||||
|
closeBtnBgColor = #1790C6
|
||||||
|
iptFocusBorderColor = #369DCD
|
||||||
|
|
||||||
|
.Preferences.modal
|
||||||
|
padding 0
|
||||||
|
border-radius 5px
|
||||||
|
overflow hidden
|
||||||
|
width 720px
|
||||||
|
height 450px
|
||||||
|
&>.header
|
||||||
|
absolute top left right
|
||||||
|
height 50px
|
||||||
|
border-bottom 1px solid borderColor
|
||||||
|
background-color menuBgColor
|
||||||
|
&>.title
|
||||||
|
font-size 22px
|
||||||
|
font-weight bold
|
||||||
|
float left
|
||||||
|
padding-left 30px
|
||||||
|
line-height 50px
|
||||||
|
&>.closeBtn
|
||||||
|
float right
|
||||||
|
font-size 14px
|
||||||
|
background-color closeBtnBgColor
|
||||||
|
color white
|
||||||
|
padding 0 15px
|
||||||
|
height 33px
|
||||||
|
margin-top 9px
|
||||||
|
margin-right 15px
|
||||||
|
border none
|
||||||
|
border-radius 5px
|
||||||
|
&:hover
|
||||||
|
background-color lighten(closeBtnBgColor, 10%)
|
||||||
|
&>.nav
|
||||||
|
absolute left bottom
|
||||||
|
top 50px
|
||||||
|
width 180px
|
||||||
|
background-color menuBgColor
|
||||||
|
border-right 1px solid borderColor
|
||||||
|
&>button
|
||||||
|
width 100%
|
||||||
|
height 44px
|
||||||
|
font-size 18px
|
||||||
|
color menuColor
|
||||||
|
border none
|
||||||
|
background-color transparent
|
||||||
|
transition 0.1s
|
||||||
|
text-align left
|
||||||
|
padding-left 15px
|
||||||
|
&:hover
|
||||||
|
background-color darken(menuBgColor, 10%)
|
||||||
|
&.active, &:active
|
||||||
|
background-color brandColor
|
||||||
|
color white
|
||||||
|
&>.content
|
||||||
|
absolute right bottom
|
||||||
|
top 50px
|
||||||
|
left 180px
|
||||||
|
&>.section
|
||||||
|
padding 10px
|
||||||
|
border-bottom 1px solid borderColor
|
||||||
|
overflow-y auto
|
||||||
|
&:nth-last-child(1)
|
||||||
|
border-bottom none
|
||||||
|
&>.sectionTitle
|
||||||
|
font-size 18px
|
||||||
|
margin 10px 0 5px
|
||||||
|
color brandColor
|
||||||
|
&>.sectionInput
|
||||||
|
height 33px
|
||||||
|
margin-bottom 5px
|
||||||
|
clearfix()
|
||||||
|
label
|
||||||
|
width 180px
|
||||||
|
padding-left 15px
|
||||||
|
float left
|
||||||
|
line-height 33px
|
||||||
|
input
|
||||||
|
width 300px
|
||||||
|
float left
|
||||||
|
height 33px
|
||||||
|
border-radius 5px
|
||||||
|
border 1px solid borderColor
|
||||||
|
padding 0 10px
|
||||||
|
font-size 14px
|
||||||
|
outline none
|
||||||
|
&:focus
|
||||||
|
border-color iptFocusBorderColor
|
||||||
|
&>.sectionConfirm
|
||||||
|
clearfix()
|
||||||
|
padding 5px 15px
|
||||||
|
button
|
||||||
|
float right
|
||||||
|
background-color brandColor
|
||||||
|
color white
|
||||||
|
border none
|
||||||
|
border-radius 5px
|
||||||
|
height 33px
|
||||||
|
padding 0 15px
|
||||||
|
font-size 14px
|
||||||
|
&:hover
|
||||||
|
background-color lighten(brandColor, 10%)
|
||||||
|
.alert
|
||||||
|
float right
|
||||||
|
height 33px
|
||||||
|
padding 0 15px
|
||||||
|
border-radius 5px
|
||||||
|
margin-right 5px
|
||||||
|
line-height 33px
|
||||||
|
font-size 14px
|
||||||
|
&.info
|
||||||
|
background-color infoBackgroundColor
|
||||||
|
color infoTextColor
|
||||||
|
&.error
|
||||||
|
background-color errorBackgroundColor
|
||||||
|
color errorTextColor
|
||||||
|
&.success
|
||||||
|
background-color successBackgroundColor
|
||||||
|
color successTextColor
|
||||||
|
|
||||||
|
.description
|
||||||
|
marked()
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
borderColor = #D0D0D0
|
borderColor = #D0D0D0 // using
|
||||||
highlightenBorderColor = darken(borderColor, 20%)
|
highlightenBorderColor = darken(borderColor, 20%)
|
||||||
invBorderColor = #404849
|
invBorderColor = #404849
|
||||||
brandBorderColor = #3FB399
|
brandBorderColor = #3FB399
|
||||||
@@ -7,12 +7,12 @@ buttonBorderColor = #4C4C4C
|
|||||||
|
|
||||||
lightButtonColor = #898989
|
lightButtonColor = #898989
|
||||||
|
|
||||||
hoverBackgroundColor= transparentify(#444, 4%)
|
hoverBackgroundColor= transparentify(#444, 4%) // using
|
||||||
|
|
||||||
inactiveTextColor = #888
|
inactiveTextColor = #888 // using
|
||||||
textColor = #4D4D4D
|
textColor = #4D4D4D // using
|
||||||
backgroundColor= white
|
backgroundColor= white
|
||||||
fontSize= 14px
|
fontSize= 14px // using
|
||||||
|
|
||||||
shadowColor= #C5C5C5
|
shadowColor= #C5C5C5
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ tableEvenBgColor = white
|
|||||||
facebookColor= #3b5998
|
facebookColor= #3b5998
|
||||||
githubBtn= #201F1F
|
githubBtn= #201F1F
|
||||||
|
|
||||||
|
// using
|
||||||
successBackgroundColor= #E0F0D9
|
successBackgroundColor= #E0F0D9
|
||||||
successTextColor= #3E753F
|
successTextColor= #3E753F
|
||||||
errorBackgroundColor= #F2DEDE
|
errorBackgroundColor= #F2DEDE
|
||||||
|
|||||||
20
lib/api.js
20
lib/api.js
@@ -17,6 +17,24 @@ export function signup (input) {
|
|||||||
.send(input)
|
.send(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateUserInfo (input) {
|
||||||
|
return request
|
||||||
|
.put(API_URL + 'auth/user')
|
||||||
|
.set({
|
||||||
|
Authorization: 'Bearer ' + auth.token()
|
||||||
|
})
|
||||||
|
.send(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updatePassword (input) {
|
||||||
|
return request
|
||||||
|
.post(API_URL + 'auth/password')
|
||||||
|
.set({
|
||||||
|
Authorization: 'Bearer ' + auth.token()
|
||||||
|
})
|
||||||
|
.send(input)
|
||||||
|
}
|
||||||
|
|
||||||
export function fetchCurrentUser () {
|
export function fetchCurrentUser () {
|
||||||
return request
|
return request
|
||||||
.get(API_URL + 'auth/user')
|
.get(API_URL + 'auth/user')
|
||||||
@@ -113,6 +131,8 @@ export function sendEmail (input) {
|
|||||||
export default {
|
export default {
|
||||||
login,
|
login,
|
||||||
signup,
|
signup,
|
||||||
|
updateUserInfo,
|
||||||
|
updatePassword,
|
||||||
fetchCurrentUser,
|
fetchCurrentUser,
|
||||||
fetchArticles,
|
fetchArticles,
|
||||||
createArticle,
|
createArticle,
|
||||||
|
|||||||
@@ -32,22 +32,22 @@ export default class CreateNewFolder extends React.Component {
|
|||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
if (err.code === '') {
|
var alert
|
||||||
let alert = {
|
if (err.code === 'ECONNREFUSED') {
|
||||||
|
alert = {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'Can\'t connect to API server.'
|
message: 'Can\'t connect to API server.'
|
||||||
}
|
}
|
||||||
}
|
} else if (err.status != null) {
|
||||||
else if (err.status != null) {
|
alert = {
|
||||||
let alert = {
|
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: err.response.body.message
|
message: err.response.body.message
|
||||||
}
|
}
|
||||||
this.setState({alert: alert})
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({alert: alert})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
427
lib/components/modal/Preferences.js
Normal file
427
lib/components/modal/Preferences.js
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
import React, { PropTypes, findDOMNode } from 'react'
|
||||||
|
import { connect, Provider } from 'react-redux'
|
||||||
|
import auth from 'boost/auth'
|
||||||
|
import linkState from 'boost/linkState'
|
||||||
|
import Select from 'react-select'
|
||||||
|
import api from 'boost/api'
|
||||||
|
import ProfileImage from 'boost/components/ProfileImage'
|
||||||
|
import store from 'boost/store'
|
||||||
|
|
||||||
|
const PROFILE = 'PROFILE'
|
||||||
|
const PREFERENCES = 'PREFERENCES'
|
||||||
|
const HELP = 'HELP'
|
||||||
|
const TEAM = 'TEAM'
|
||||||
|
const MEMBER = 'MEMBER'
|
||||||
|
const FOLDER = 'FOLDER'
|
||||||
|
|
||||||
|
function getUsers (input, cb) {
|
||||||
|
api.searchUser(input)
|
||||||
|
.then(function (res) {
|
||||||
|
let users = res.body
|
||||||
|
|
||||||
|
cb(null, {
|
||||||
|
options: users.map(user => {
|
||||||
|
return { value: user.name, label: user.name }
|
||||||
|
}),
|
||||||
|
complete: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
class Preferences extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
currentTab: PROFILE,
|
||||||
|
profile: {
|
||||||
|
userInfo: {
|
||||||
|
profileName: props.currentUser.profileName,
|
||||||
|
email: props.currentUser.email,
|
||||||
|
alert: null
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
currentPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmation: '',
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNavButtonClick (tab) {
|
||||||
|
return e => {
|
||||||
|
this.setState({currentTab: tab})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
let content = this.renderContent()
|
||||||
|
|
||||||
|
let tabs = [
|
||||||
|
{target: PROFILE, label: 'Profile'},
|
||||||
|
{target: PREFERENCES, label: 'Preferences'},
|
||||||
|
{target: HELP, label: 'Help & Feedback'},
|
||||||
|
{target: TEAM, label: 'Team setting'},
|
||||||
|
{target: MEMBER, label: 'Manage member'},
|
||||||
|
{target: FOLDER, label: 'Manage folder'}
|
||||||
|
]
|
||||||
|
|
||||||
|
let navButtons = tabs.map(tab => (
|
||||||
|
<button key={tab.target} onClick={e => this.handleNavButtonClick(tab.target)(e)} className={this.state.currentTab === tab.target ? 'active' : ''}>{tab.label}</button>
|
||||||
|
))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='Preferences modal'>
|
||||||
|
<div className='header'>
|
||||||
|
<div className='title'>Setting</div>
|
||||||
|
<button onClick={e => this.props.close()} className='closeBtn'>Done</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='nav'>
|
||||||
|
{navButtons}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent () {
|
||||||
|
switch (this.state.currentTab) {
|
||||||
|
case PREFERENCES:
|
||||||
|
return this.renderPreferences()
|
||||||
|
case HELP:
|
||||||
|
return this.renderHelp()
|
||||||
|
case TEAM:
|
||||||
|
return this.renderTeamSetting()
|
||||||
|
case MEMBER:
|
||||||
|
return this.renderMemberSetting()
|
||||||
|
case FOLDER:
|
||||||
|
return this.renderFolderSetting()
|
||||||
|
case PROFILE:
|
||||||
|
default:
|
||||||
|
return this.renderProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleProfileSaveButtonClick (e) {
|
||||||
|
let profileState = this.state.profile
|
||||||
|
profileState.userInfo.alert = {
|
||||||
|
type: 'info',
|
||||||
|
message: 'Sending...'
|
||||||
|
}
|
||||||
|
this.setState({profile: profileState}, () => {
|
||||||
|
let input = {
|
||||||
|
profileName: profileState.userInfo.profileName,
|
||||||
|
email: profileState.userInfo.email
|
||||||
|
}
|
||||||
|
api.updateUserInfo(input)
|
||||||
|
.then(res => {
|
||||||
|
let profileState = this.state.profile
|
||||||
|
profileState.userInfo.alert = {
|
||||||
|
type: 'success',
|
||||||
|
message: 'Successfully done!'
|
||||||
|
}
|
||||||
|
this.setState({profile: profileState})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
var message
|
||||||
|
if (err.status != null) {
|
||||||
|
message = err.response.body.message
|
||||||
|
} else if (err.code === 'ECONNREFUSED') {
|
||||||
|
message = 'Can\'t connect to API server.'
|
||||||
|
} else throw err
|
||||||
|
|
||||||
|
let profileState = this.state.profile
|
||||||
|
profileState.userInfo.alert = {
|
||||||
|
type: 'error',
|
||||||
|
message: message
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({profile: profileState})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePasswordSaveButton (e) {
|
||||||
|
let profileState = this.state.profile
|
||||||
|
|
||||||
|
if (profileState.password.newPassword !== profileState.password.confirmation) {
|
||||||
|
profileState.password.alert = {
|
||||||
|
type: 'error',
|
||||||
|
message: 'Confirmation doesn\'t match'
|
||||||
|
}
|
||||||
|
this.setState({profile: profileState})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
profileState.password.alert = {
|
||||||
|
type: 'info',
|
||||||
|
message: 'Sending...'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({profile: profileState}, () => {
|
||||||
|
let input = {
|
||||||
|
password: profileState.password.currentPassword,
|
||||||
|
newPassword: profileState.password.newPassword
|
||||||
|
}
|
||||||
|
api.updatePassword(input)
|
||||||
|
.then(res => {
|
||||||
|
let profileState = this.state.profile
|
||||||
|
profileState.password.alert = {
|
||||||
|
type: 'success',
|
||||||
|
message: 'Successfully done!'
|
||||||
|
}
|
||||||
|
profileState.password.currentPassword = ''
|
||||||
|
profileState.password.newPassword = ''
|
||||||
|
profileState.password.confirmation = ''
|
||||||
|
|
||||||
|
this.setState({profile: profileState})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
var message
|
||||||
|
if (err.status != null) {
|
||||||
|
message = err.response.body.message
|
||||||
|
} else if (err.code === 'ECONNREFUSED') {
|
||||||
|
message = 'Can\'t connect to API server.'
|
||||||
|
} else throw err
|
||||||
|
|
||||||
|
let profileState = this.state.profile
|
||||||
|
profileState.password.alert = {
|
||||||
|
type: 'error',
|
||||||
|
message: message
|
||||||
|
}
|
||||||
|
profileState.password.currentPassword = ''
|
||||||
|
profileState.password.newPassword = ''
|
||||||
|
profileState.password.confirmation = ''
|
||||||
|
|
||||||
|
this.setState({profile: profileState}, () => {
|
||||||
|
if (this.refs.currentPassword != null) findDOMNode(this.refs.currentPassword).focus()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderProfile () {
|
||||||
|
let profileState = this.state.profile
|
||||||
|
return (
|
||||||
|
<div className='content profile'>
|
||||||
|
<div className='section userSection'>
|
||||||
|
<div className='sectionTitle'>User Info</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Profile Name</label>
|
||||||
|
<input valueLink={this.linkState('profile.userInfo.profileName')} type='text'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>E-mail</label>
|
||||||
|
<input valueLink={this.linkState('profile.userInfo.email')} type='text'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionConfirm'>
|
||||||
|
<button onClick={e => this.handleProfileSaveButtonClick(e)}>Save</button>
|
||||||
|
|
||||||
|
{this.state.profile.userInfo.alert != null
|
||||||
|
? (
|
||||||
|
<div className={'alert ' + profileState.userInfo.alert.type}>{profileState.userInfo.alert.message}</div>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='section passwordSection'>
|
||||||
|
<div className='sectionTitle'>Password</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Current Password</label>
|
||||||
|
<input ref='currentPassword' valueLink={this.linkState('profile.password.currentPassword')} type='password' placeholder='Current Password'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>New Password</label>
|
||||||
|
<input valueLink={this.linkState('profile.password.newPassword')} type='password' placeholder='New Password'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Confirmation</label>
|
||||||
|
<input valueLink={this.linkState('profile.password.confirmation')} type='password' placeholder='Confirmation'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionConfirm'>
|
||||||
|
<button onClick={e => this.handlePasswordSaveButton(e)}>Save</button>
|
||||||
|
|
||||||
|
{this.state.profile.password.alert != null
|
||||||
|
? (
|
||||||
|
<div className={'alert ' + profileState.password.alert.type}>{profileState.password.alert.message}</div>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPreferences () {
|
||||||
|
return (
|
||||||
|
<div className='content preferences'>
|
||||||
|
<div className='section passwordSection'>
|
||||||
|
<div className='sectionTitle'>Hotkey</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Toggle Finder(popup)</label>
|
||||||
|
<input type='text'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionConfirm'>
|
||||||
|
<button>Save</button>
|
||||||
|
</div>
|
||||||
|
<div className='description'>
|
||||||
|
<ul>
|
||||||
|
<li><code>0</code> to <code>9</code></li>
|
||||||
|
<li><code>A</code> to <code>Z</code></li>
|
||||||
|
<li><code>F1</code> to <code>F24</code></li>
|
||||||
|
<li>Punctuations like <code>~</code>, <code>!</code>, <code>@</code>, <code>#</code>, <code>$</code>, etc.</li>
|
||||||
|
<li><code>Plus</code></li>
|
||||||
|
<li><code>Space</code></li>
|
||||||
|
<li><code>Backspace</code></li>
|
||||||
|
<li><code>Delete</code></li>
|
||||||
|
<li><code>Insert</code></li>
|
||||||
|
<li><code>Return</code> (or <code>Enter</code> as alias)</li>
|
||||||
|
<li><code>Up</code>, <code>Down</code>, <code>Left</code> and <code>Right</code></li>
|
||||||
|
<li><code>Home</code> and <code>End</code></li>
|
||||||
|
<li><code>PageUp</code> and <code>PageDown</code></li>
|
||||||
|
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
|
||||||
|
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
|
||||||
|
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHelp () {
|
||||||
|
return (
|
||||||
|
<div className='content help'>
|
||||||
|
Comming soon
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTeamSetting () {
|
||||||
|
return (
|
||||||
|
<div className='content teamSetting'>
|
||||||
|
<div className='header'>
|
||||||
|
<select>
|
||||||
|
<option></option>
|
||||||
|
</select>
|
||||||
|
<div>'s Team Setting</div>
|
||||||
|
</div>
|
||||||
|
<div className='section'>
|
||||||
|
<div className='sectionTitle'>Team profile</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<div className='label'>Team Name</div>
|
||||||
|
<input type='text'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionConfirm'>
|
||||||
|
<button>Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{false
|
||||||
|
? (
|
||||||
|
<div className='section teamDelete'>
|
||||||
|
<div className='label'>Delete this team</div>
|
||||||
|
<button>Delete</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<div className='section teamDeleteConfirm'>
|
||||||
|
<div>Are you sure to delete this team?</div>
|
||||||
|
<button>Sure</button>
|
||||||
|
<button>Cancel</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMemberSetting () {
|
||||||
|
let membersEl = [].map(member => {
|
||||||
|
let isCurrentUser = this.state.currentUser.id === member.id
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={'user-' + member.id}>
|
||||||
|
<ProfileImage className='userPhoto' email={member.email} size='30'/>
|
||||||
|
<div className='userInfo'>
|
||||||
|
<div className='userName'>{`${member.profileName} (${member.name})`}</div>
|
||||||
|
<div className='userEmail'>{member.email}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='userControl'>
|
||||||
|
<select onChange={e => this.handleMemberRoleChange(member.name)(e)} disabled={isCurrentUser} value={member._pivot_role} className='userRole'>
|
||||||
|
<option value='owner'>Owner</option>
|
||||||
|
<option value='member'>Member</option>
|
||||||
|
</select>
|
||||||
|
<button onClick={e => this.handleMemberDeleteButtonClick(member.name)(e)} disabled={isCurrentUser}><i className='fa fa-times fa-fw'/></button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='content memberSetting'>
|
||||||
|
<div className='header'>
|
||||||
|
<select>
|
||||||
|
<option></option>
|
||||||
|
</select>
|
||||||
|
<div>'s Team Setting</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
className='memberName'
|
||||||
|
autoload={false}
|
||||||
|
asyncOptions={getUsers}
|
||||||
|
onChange={val => this.handleNewMemberChange(val)}
|
||||||
|
value={null}
|
||||||
|
/>
|
||||||
|
<button onClick={e => this.handleClickAddMemberButton(e)} className='addMemberBtn'>add</button>
|
||||||
|
</div>
|
||||||
|
<ul className='memberList'>
|
||||||
|
{membersEl}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFolderSetting () {
|
||||||
|
return (
|
||||||
|
<div className='content folderSetting'></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences.propTypes = {
|
||||||
|
currentUser: PropTypes.shape(),
|
||||||
|
close: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences.prototype.linkState = linkState
|
||||||
|
|
||||||
|
function remap (state) {
|
||||||
|
let currentUser = state.currentUser
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let RootComponent = connect(remap)(Preferences)
|
||||||
|
export default class PreferencesModal extends React.Component {
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
{() => <RootComponent/>}
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { API_URL } from '../../config'
|
import { API_URL } from '../config'
|
||||||
import socketio from 'socket.io-client'
|
import socketio from 'socket.io-client'
|
||||||
import auth from 'boost/auth'
|
import auth from './auth'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import { updateArticle, destroyArticle } from './actions'
|
import { updateArticle, destroyArticle } from './actions'
|
||||||
|
|
||||||
Reference in New Issue
Block a user