mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-15 02:36:36 +00:00
refactor file structure
This commit is contained in:
108
browser/main/modal/CreateNewFolder.js
Normal file
108
browser/main/modal/CreateNewFolder.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import linkState from 'boost/linkState'
|
||||
import { createFolder } from 'boost/actions'
|
||||
import store from 'boost/store'
|
||||
import FolderMark from 'boost/components/FolderMark'
|
||||
|
||||
export default class CreateNewFolder extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
color: Math.round(Math.random() * 7),
|
||||
alert: null
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
ReactDOM.findDOMNode(this.refs.folderName).focus()
|
||||
}
|
||||
|
||||
handleCloseButton (e) {
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
handleConfirmButton (e) {
|
||||
this.setState({alert: null}, () => {
|
||||
let { close } = this.props
|
||||
let { name, color } = this.state
|
||||
|
||||
let input = {
|
||||
name,
|
||||
color
|
||||
}
|
||||
|
||||
try {
|
||||
store.dispatch(createFolder(input))
|
||||
} catch (e) {
|
||||
this.setState({alert: {
|
||||
type: 'error',
|
||||
message: e.message
|
||||
}})
|
||||
return
|
||||
}
|
||||
close()
|
||||
})
|
||||
}
|
||||
|
||||
handleColorClick (colorIndex) {
|
||||
return e => {
|
||||
this.setState({
|
||||
color: colorIndex
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.handleConfirmButton()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let alert = this.state.alert
|
||||
let alertElement = alert != null ? (
|
||||
<p className={`alert ${alert.type}`}>
|
||||
{alert.message}
|
||||
</p>
|
||||
) : null
|
||||
let colorIndexes = []
|
||||
for (let i = 0; i < 8; i++) {
|
||||
colorIndexes.push(i)
|
||||
}
|
||||
let colorElements = colorIndexes.map(index => {
|
||||
let className = 'option'
|
||||
if (index === this.state.color) className += ' active'
|
||||
|
||||
return (
|
||||
<span className={className} key={index} onClick={e => this.handleColorClick(index)(e)}>
|
||||
<FolderMark color={index}/>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='CreateNewFolder modal'>
|
||||
<button onClick={e => this.handleCloseButton(e)} className='closeBtn'><i className='fa fa-fw fa-times'/></button>
|
||||
|
||||
<div className='title'>Create new folder</div>
|
||||
|
||||
<input ref='folderName' onKeyDown={e => this.handleKeyDown(e)} className='ipt' type='text' valueLink={this.linkState('name')} placeholder='Enter folder name'/>
|
||||
<div className='colorSelect'>
|
||||
{colorElements}
|
||||
</div>
|
||||
{alertElement}
|
||||
|
||||
<button onClick={e => this.handleConfirmButton(e)} className='confirmBtn'>Create</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CreateNewFolder.propTypes = {
|
||||
close: PropTypes.func
|
||||
}
|
||||
|
||||
CreateNewFolder.prototype.linkState = linkState
|
||||
255
browser/main/modal/CreateNewTeam.js
Normal file
255
browser/main/modal/CreateNewTeam.js
Normal file
@@ -0,0 +1,255 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ProfileImage from 'boost/components/ProfileImage'
|
||||
import { searchUser, createTeam, setMember, deleteMember } from 'boost/api'
|
||||
import linkState from 'boost/linkState'
|
||||
import Select from 'react-select'
|
||||
|
||||
function getUsers (input, cb) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
export default class CreateNewTeam extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
create: {
|
||||
name: '',
|
||||
alert: null
|
||||
},
|
||||
select: {
|
||||
team: null,
|
||||
newMember: null,
|
||||
alert: null
|
||||
},
|
||||
currentTab: 'create',
|
||||
currentUser: JSON.parse(localStorage.getItem('currentUser'))
|
||||
}
|
||||
}
|
||||
|
||||
handleCloseClick (e) {
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
handleContinueClick (e) {
|
||||
let createState = this.state.create
|
||||
createState.isSending = true
|
||||
createState.alert = {
|
||||
type: 'info',
|
||||
message: 'sending...'
|
||||
}
|
||||
this.setState({create: createState})
|
||||
|
||||
function onTeamCreate (res) {
|
||||
let createState = this.state.create
|
||||
createState.isSending = false
|
||||
createState.alert = null
|
||||
|
||||
let selectState = this.state.select
|
||||
selectState.team = res.body
|
||||
|
||||
this.setState({
|
||||
currentTab: 'select',
|
||||
create: createState,
|
||||
select: {
|
||||
team: res.body
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function onError (err) {
|
||||
let errorMessage = err.response != null ? err.response.body.message : 'Can\'t connect to API server.'
|
||||
|
||||
let createState = this.state.create
|
||||
createState.isSending = false
|
||||
createState.alert = {
|
||||
type: 'error',
|
||||
message: errorMessage
|
||||
}
|
||||
|
||||
this.setState({
|
||||
create: createState
|
||||
})
|
||||
}
|
||||
|
||||
createTeam({name: this.state.create.name})
|
||||
.then(onTeamCreate.bind(this))
|
||||
.catch(onError.bind(this))
|
||||
}
|
||||
|
||||
renderCreateTab () {
|
||||
let createState = this.state.create
|
||||
let alertEl = createState.alert != null ? (
|
||||
<p className={['alert'].concat([createState.alert.type]).join(' ')}>{createState.alert.message}</p>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<div className='createTab'>
|
||||
<div className='title'>Create new team</div>
|
||||
|
||||
<input valueLink={this.linkState('create.name')} className='ipt' type='text' placeholder='Enter your team name'/>
|
||||
{alertEl}
|
||||
<button onClick={e => this.handleContinueClick(e)} disabled={createState.isSending} className='confirmBtn'>Continue <i className='fa fa-arrow-right fa-fw'/></button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
handleNewMemberChange (value) {
|
||||
let selectState = this.state.select
|
||||
selectState.newMember = value
|
||||
this.setState({select: selectState})
|
||||
}
|
||||
|
||||
handleClickAddMemberButton (e) {
|
||||
let selectState = this.state.select
|
||||
let input = {
|
||||
name: selectState.newMember,
|
||||
role: 'member'
|
||||
}
|
||||
|
||||
setMember(selectState.team.id, input)
|
||||
.then(res => {
|
||||
let selectState = this.state.select
|
||||
let team = res.body
|
||||
team.Members = team.Members.sort((a, b) => {
|
||||
return new Date(a._pivot_createdAt) - new Date(b._pivot_createdAt)
|
||||
})
|
||||
selectState.team = team
|
||||
selectState.newMember = ''
|
||||
|
||||
this.setState({select: selectState})
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
handleMemberDeleteButtonClick (name) {
|
||||
let selectState = this.state.select
|
||||
let input = {
|
||||
name: name
|
||||
}
|
||||
|
||||
return e => {
|
||||
deleteMember(selectState.team.id, input)
|
||||
.then(res => {
|
||||
let selectState = this.state.select
|
||||
let team = res.body
|
||||
team.Members = team.Members.sort((a, b) => {
|
||||
return new Date(a._pivot_createdAt) - new Date(b._pivot_createdAt)
|
||||
})
|
||||
selectState.team = team
|
||||
selectState.newMember = ''
|
||||
|
||||
this.setState({select: selectState})
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err, err.response)
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleMemberRoleChange (name) {
|
||||
return function (e) {
|
||||
let selectState = this.state.select
|
||||
let input = {
|
||||
name: name,
|
||||
role: e.target.value
|
||||
}
|
||||
|
||||
setMember(selectState.team.id, input)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
|
||||
renderSelectTab () {
|
||||
let selectState = this.state.select
|
||||
|
||||
let membersEl = selectState.team.Members.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='selectTab'>
|
||||
<div className='title'>Select member</div>
|
||||
<div className='memberForm'>
|
||||
<Select
|
||||
className='memberName'
|
||||
autoload={false}
|
||||
asyncOptions={getUsers}
|
||||
onChange={val => this.handleNewMemberChange(val)}
|
||||
value={selectState.newMember}
|
||||
/>
|
||||
<button onClick={e => this.handleClickAddMemberButton(e)} className='addMemberBtn'>add</button>
|
||||
</div>
|
||||
<ul className='memberList'>
|
||||
{membersEl}
|
||||
</ul>
|
||||
<button onClick={e => this.props.close(e)}className='confirmBtn'>Done</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
let currentTab = this.state.currentTab === 'create'
|
||||
? this.renderCreateTab()
|
||||
: this.renderSelectTab()
|
||||
|
||||
return (
|
||||
<div className='CreateNewTeam modal'>
|
||||
<button onClick={e => this.handleCloseClick(e)} className='closeBtn'><i className='fa fa-fw fa-times'/></button>
|
||||
|
||||
{currentTab}
|
||||
|
||||
<div className='tabNav'>
|
||||
<i className={'fa fa-circle fa-fw' + (this.state.currentTab === 'create' ? ' active' : '')}/>
|
||||
<i className={'fa fa-circle fa-fw' + (this.state.currentTab === 'select' ? ' active' : '')}/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CreateNewTeam.propTypes = {
|
||||
close: PropTypes.func
|
||||
}
|
||||
CreateNewTeam.prototype.linkState = linkState
|
||||
41
browser/main/modal/EditedAlert.js
Normal file
41
browser/main/modal/EditedAlert.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import store from 'boost/store'
|
||||
import { unlockStatus, clearNewArticle } from 'boost/actions'
|
||||
|
||||
export default class EditedAlert extends React.Component {
|
||||
componentDidMount () {
|
||||
ReactDOM.findDOMNode(this.refs.no).focus()
|
||||
}
|
||||
|
||||
handleNoButtonClick (e) {
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
handleYesButtonClick (e) {
|
||||
store.dispatch(unlockStatus())
|
||||
store.dispatch(this.props.action)
|
||||
store.dispatch(clearNewArticle())
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className='EditedAlert modal'>
|
||||
<div className='title'>Your article is still editing!</div>
|
||||
|
||||
<div className='message'>Do you really want to leave without finishing?</div>
|
||||
|
||||
<div className='control'>
|
||||
<button ref='no' onClick={e => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close'/> No</button>
|
||||
<button ref='yes' onClick={e => this.handleYesButtonClick(e)} className='primary'><i className='fa fa-fw fa-check'/> Yes</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
EditedAlert.propTypes = {
|
||||
action: PropTypes.object,
|
||||
close: PropTypes.func
|
||||
}
|
||||
146
browser/main/modal/Preference/AppSettingTab.js
Normal file
146
browser/main/modal/Preference/AppSettingTab.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import linkState from 'boost/linkState'
|
||||
import { updateUser } from 'boost/actions'
|
||||
|
||||
const electron = require('electron')
|
||||
const ipc = electron.ipcRenderer
|
||||
const remote = electron.remote
|
||||
|
||||
export default class AppSettingTab extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
let keymap = remote.getGlobal('keymap')
|
||||
let userName = props.user != null ? props.user.name : null
|
||||
|
||||
this.state = {
|
||||
user: {
|
||||
name: userName,
|
||||
alert: null
|
||||
},
|
||||
userAlert: null,
|
||||
keymap: {
|
||||
toggleFinder: keymap.toggleFinder
|
||||
},
|
||||
keymapAlert: null
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.handleSettingDone = () => {
|
||||
this.setState({keymapAlert: {
|
||||
type: 'success',
|
||||
message: 'Successfully done!'
|
||||
}})
|
||||
}
|
||||
this.handleSettingError = err => {
|
||||
this.setState({keymapAlert: {
|
||||
type: 'error',
|
||||
message: err.message != null ? err.message : 'Error occurs!'
|
||||
}})
|
||||
}
|
||||
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
|
||||
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone)
|
||||
ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError)
|
||||
}
|
||||
|
||||
submitHotKey () {
|
||||
ipc.send('hotkeyUpdated', {
|
||||
toggleFinder: this.state.keymap.toggleFinder
|
||||
})
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
this.submitHotKey()
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.submitHotKey()
|
||||
}
|
||||
}
|
||||
|
||||
handleNameSaveButtonClick (e) {
|
||||
let { dispatch } = this.props
|
||||
|
||||
dispatch(updateUser({name: this.state.user.name}))
|
||||
this.setState({
|
||||
userAlert: {
|
||||
type: 'success',
|
||||
message: 'Successfully done!'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
let keymapAlert = this.state.keymapAlert
|
||||
let keymapAlertElement = keymapAlert != null
|
||||
? (
|
||||
<p className={`alert ${keymapAlert.type}`}>
|
||||
{keymapAlert.message}
|
||||
</p>
|
||||
) : null
|
||||
let userAlert = this.state.userAlert
|
||||
let userAlertElement = userAlert != null
|
||||
? (
|
||||
<p className={`alert ${userAlert.type}`}>
|
||||
{userAlert.message}
|
||||
</p>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<div className='AppSettingTab content'>
|
||||
<div className='section'>
|
||||
<div className='sectionTitle'>User's info</div>
|
||||
<div className='sectionInput'>
|
||||
<label>User name</label>
|
||||
<input valueLink={this.linkState('user.name')} type='text'/>
|
||||
</div>
|
||||
<div className='sectionConfirm'>
|
||||
<button onClick={e => this.handleNameSaveButtonClick(e)}>Save</button>
|
||||
{userAlertElement}
|
||||
</div>
|
||||
</div>
|
||||
<div className='section'>
|
||||
<div className='sectionTitle'>Hotkey</div>
|
||||
<div className='sectionInput'>
|
||||
<label>Toggle Finder(popup)</label>
|
||||
<input onKeyDown={e => this.handleKeyDown(e)} valueLink={this.linkState('keymap.toggleFinder')} type='text'/>
|
||||
</div>
|
||||
<div className='sectionConfirm'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)}>Save</button>
|
||||
{keymapAlertElement}
|
||||
</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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AppSettingTab.prototype.linkState = linkState
|
||||
AppSettingTab.propTypes = {
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
123
browser/main/modal/Preference/ContactTab.js
Normal file
123
browser/main/modal/Preference/ContactTab.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { getClientKey } from 'boost/activityRecord'
|
||||
import linkState from 'boost/linkState'
|
||||
import _ from 'lodash'
|
||||
import { request, WEB_URL } from 'boost/api'
|
||||
|
||||
const FORM_MODE = 'FORM_MODE'
|
||||
const DONE_MODE = 'DONE_MODE'
|
||||
|
||||
export default class ContactTab extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
title: '',
|
||||
content: '',
|
||||
email: '',
|
||||
mode: FORM_MODE,
|
||||
alert: null
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
let titleInput = ReactDOM.findDOMNode(this.refs.title)
|
||||
if (titleInput != null) titleInput.focus()
|
||||
}
|
||||
|
||||
handleBackButtonClick (e) {
|
||||
this.setState({
|
||||
mode: FORM_MODE
|
||||
})
|
||||
}
|
||||
|
||||
handleSendButtonClick (e) {
|
||||
let input = _.pick(this.state, ['title', 'content', 'email'])
|
||||
input.clientKey = getClientKey()
|
||||
|
||||
this.setState({
|
||||
alert: {
|
||||
type: 'info',
|
||||
message: 'Sending...'
|
||||
}
|
||||
}, () => {
|
||||
request.post(WEB_URL + 'apis/inquiry')
|
||||
.send(input)
|
||||
.then(res => {
|
||||
console.log('sent')
|
||||
this.setState({
|
||||
title: '',
|
||||
content: '',
|
||||
mode: DONE_MODE,
|
||||
alert: null
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.code === 'ECONNREFUSED') {
|
||||
this.setState({
|
||||
alert: {
|
||||
type: 'error',
|
||||
message: 'Can\'t connect to API server.'
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.error(err)
|
||||
this.setState({
|
||||
alert: {
|
||||
type: 'error',
|
||||
message: err.message
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
switch (this.state.mode) {
|
||||
case DONE_MODE:
|
||||
return (
|
||||
<div className='ContactTab content done'>
|
||||
<div className='message'>
|
||||
<i className='checkIcon fa fa-check-circle'/><br/>
|
||||
Your message has been sent successfully!!
|
||||
</div>
|
||||
<div className='control'>
|
||||
<button onClick={e => this.handleBackButtonClick(e)}>Back to Contact form</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
case FORM_MODE:
|
||||
default:
|
||||
let alertElement = this.state.alert != null
|
||||
? (
|
||||
<div className={'alert ' + this.state.alert.type}>{this.state.alert.message}</div>
|
||||
)
|
||||
: null
|
||||
return (
|
||||
<div className='ContactTab content form'>
|
||||
<div className='title'>Contact form</div>
|
||||
<div className='description'>
|
||||
Your feedback is highly appreciated and will help us to improve our app. :D
|
||||
</div>
|
||||
<div className='iptGroup'>
|
||||
<input ref='title' valueLink={this.linkState('title')} placeholder='Title' type='text'/>
|
||||
</div>
|
||||
<div className='iptGroup'>
|
||||
<textarea valueLink={this.linkState('content')} placeholder='Content'/>
|
||||
</div>
|
||||
<div className='iptGroup'>
|
||||
<input valueLink={this.linkState('email')} placeholder='E-mail (Optional)' type='email'/>
|
||||
</div>
|
||||
<div className='formControl'>
|
||||
<button onClick={e => this.handleSendButtonClick(e)} className='primary'>Send</button>
|
||||
{alertElement}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContactTab.prototype.linkState = linkState
|
||||
187
browser/main/modal/Preference/FolderRow.js
Normal file
187
browser/main/modal/Preference/FolderRow.js
Normal file
@@ -0,0 +1,187 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import linkState from 'boost/linkState'
|
||||
import FolderMark from 'boost/components/FolderMark'
|
||||
import store from 'boost/store'
|
||||
import { updateFolder, destroyFolder, replaceFolder } from 'boost/actions'
|
||||
|
||||
const IDLE = 'IDLE'
|
||||
const EDIT = 'EDIT'
|
||||
const DELETE = 'DELETE'
|
||||
|
||||
export default class FolderRow extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
mode: IDLE
|
||||
}
|
||||
}
|
||||
|
||||
handleUpClick (e) {
|
||||
let { index } = this.props
|
||||
if (index > 0) {
|
||||
store.dispatch(replaceFolder(index, index - 1))
|
||||
}
|
||||
}
|
||||
|
||||
handleDownClick (e) {
|
||||
let { index, count } = this.props
|
||||
if (index < count - 1) {
|
||||
store.dispatch(replaceFolder(index, index + 1))
|
||||
}
|
||||
}
|
||||
|
||||
handleCancelButtonClick (e) {
|
||||
this.setState({
|
||||
mode: IDLE
|
||||
})
|
||||
}
|
||||
|
||||
handleEditButtonClick (e) {
|
||||
this.setState({
|
||||
mode: EDIT,
|
||||
name: this.props.folder.name,
|
||||
color: this.props.folder.color,
|
||||
isColorEditing: false
|
||||
})
|
||||
}
|
||||
|
||||
handleDeleteButtonClick (e) {
|
||||
this.setState({mode: DELETE})
|
||||
}
|
||||
|
||||
handleNameInputKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.handleSaveButtonClick()
|
||||
}
|
||||
}
|
||||
|
||||
handleColorSelectClick (e) {
|
||||
this.setState({
|
||||
isColorEditing: true
|
||||
})
|
||||
}
|
||||
|
||||
handleColorButtonClick (index) {
|
||||
return e => {
|
||||
this.setState({
|
||||
color: index,
|
||||
isColorEditing: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
let { folder, setAlert } = this.props
|
||||
|
||||
setAlert(null, () => {
|
||||
let input = {
|
||||
name: this.state.name,
|
||||
color: this.state.color
|
||||
}
|
||||
folder = Object.assign({}, folder, input)
|
||||
|
||||
try {
|
||||
store.dispatch(updateFolder(folder))
|
||||
this.setState({
|
||||
mode: IDLE
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
setAlert({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleDeleteConfirmButtonClick (e) {
|
||||
let { folder } = this.props
|
||||
store.dispatch(destroyFolder(folder.key))
|
||||
}
|
||||
|
||||
render () {
|
||||
let folder = this.props.folder
|
||||
|
||||
switch (this.state.mode) {
|
||||
case EDIT:
|
||||
let colorIndexes = []
|
||||
for (let i = 0; i < 8; i++) {
|
||||
colorIndexes.push(i)
|
||||
}
|
||||
|
||||
let colorOptions = colorIndexes.map(index => {
|
||||
let className = this.state.color === index
|
||||
? 'active'
|
||||
: null
|
||||
return (
|
||||
<button onClick={e => this.handleColorButtonClick(index)(e)} className={className} key={index}>
|
||||
<FolderMark color={index}/>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='FolderRow edit'>
|
||||
<div className='folderColor'>
|
||||
<button onClick={e => this.handleColorSelectClick(e)} className='select'>
|
||||
<FolderMark color={this.state.color}/>
|
||||
</button>
|
||||
{this.state.isColorEditing
|
||||
? (
|
||||
<div className='options'>
|
||||
<div className='label'>Color select</div>
|
||||
{colorOptions}
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
<div className='folderName'>
|
||||
<input onKeyDown={e => this.handleNameInputKeyDown(e)} valueLink={this.linkState('name')} type='text'/>
|
||||
</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)} className='primary'>Save</button>
|
||||
<button onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
case DELETE:
|
||||
return (
|
||||
<div className='FolderRow delete'>
|
||||
<div className='folderDeleteLabel'>Are you sure to delete <strong>{folder.name}</strong> folder?</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleDeleteConfirmButtonClick(e)} className='primary'>Sure</button>
|
||||
<button onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
case IDLE:
|
||||
default:
|
||||
return (
|
||||
<div className='FolderRow'>
|
||||
<div className='sortBtns'>
|
||||
<button onClick={e => this.handleUpClick(e)}><i className='fa fa-sort-up fa-fw'/></button>
|
||||
<button onClick={e => this.handleDownClick(e)}><i className='fa fa-sort-down fa-fw'/></button>
|
||||
</div>
|
||||
<div className='folderColor'><FolderMark color={folder.color}/></div>
|
||||
<div className='folderName'>{folder.name}</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleEditButtonClick(e)}><i className='fa fa-fw fa-edit'/></button>
|
||||
<button onClick={e => this.handleDeleteButtonClick(e)}><i className='fa fa-fw fa-close'/></button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FolderRow.propTypes = {
|
||||
folder: PropTypes.shape(),
|
||||
index: PropTypes.number,
|
||||
count: PropTypes.number,
|
||||
setAlert: PropTypes.func
|
||||
}
|
||||
|
||||
FolderRow.prototype.linkState = linkState
|
||||
99
browser/main/modal/Preference/FolderSettingTab.js
Normal file
99
browser/main/modal/Preference/FolderSettingTab.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import FolderRow from './FolderRow'
|
||||
import linkState from 'boost/linkState'
|
||||
import { createFolder } from 'boost/actions'
|
||||
|
||||
export default class FolderSettingTab extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
name: ''
|
||||
}
|
||||
}
|
||||
|
||||
handleNewFolderNameKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.handleSaveButtonClick()
|
||||
}
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
this.setState({alert: null}, () => {
|
||||
if (this.state.name.trim().length === 0) return false
|
||||
|
||||
let { dispatch } = this.props
|
||||
|
||||
try {
|
||||
dispatch(createFolder({
|
||||
name: this.state.name
|
||||
}))
|
||||
} catch (e) {
|
||||
this.setState({alert: {
|
||||
type: 'error',
|
||||
message: e.message
|
||||
}})
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({name: ''})
|
||||
})
|
||||
}
|
||||
|
||||
setAlert (alert, cb) {
|
||||
this.setState({alert: alert}, cb)
|
||||
}
|
||||
|
||||
render () {
|
||||
let { folders } = this.props
|
||||
let folderElements = folders.map((folder, index) => {
|
||||
return (
|
||||
<FolderRow
|
||||
key={'folder-' + folder.key}
|
||||
folder={folder}
|
||||
index={index}
|
||||
count={folders.length}
|
||||
setAlert={(alert, cb) => this.setAlert(alert, cb)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
let alert = this.state.alert
|
||||
let alertElement = alert != null ? (
|
||||
<p className={`alert ${alert.type}`}>
|
||||
{alert.message}
|
||||
</p>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<div className='FolderSettingTab content'>
|
||||
<div className='section'>
|
||||
<div className='sectionTitle'>Manage folder</div>
|
||||
<div className='folderTable'>
|
||||
<div className='folderHeader'>
|
||||
<div className='folderName'>Folder</div>
|
||||
<div className='folderControl'>Edit/Delete</div>
|
||||
</div>
|
||||
{folderElements}
|
||||
<div className='newFolder'>
|
||||
<div className='folderName'>
|
||||
<input onKeyDown={e => this.handleNewFolderNameKeyDown(e)} valueLink={this.linkState('name')} type='text' placeholder='New Folder'/>
|
||||
</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)} className='primary'>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
{alertElement}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FolderSettingTab.propTypes = {
|
||||
folders: PropTypes.array,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
FolderSettingTab.prototype.linkState = linkState
|
||||
11
browser/main/modal/Preference/HelpTab.js
Normal file
11
browser/main/modal/Preference/HelpTab.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
|
||||
export default class HelpTab extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div className='content help'>
|
||||
Comming soon
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
106
browser/main/modal/Preference/MemberRow.js
Normal file
106
browser/main/modal/Preference/MemberRow.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ProfileImage from 'boost/components/ProfileImage'
|
||||
import api from 'boost/api'
|
||||
|
||||
const IDLE = 'IDLE'
|
||||
const DELETE = 'DELETE'
|
||||
|
||||
export default class MemberRow extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
mode: IDLE
|
||||
}
|
||||
}
|
||||
handleMemberRoleChange (e) {
|
||||
let input = {
|
||||
name: this.props.member.name,
|
||||
role: e.target.value
|
||||
}
|
||||
|
||||
api.setMember(this.props.team.id, input)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
handleDeleteButtonClick (e) {
|
||||
this.setState({mode: DELETE})
|
||||
}
|
||||
|
||||
handleCancelButtonClick (e) {
|
||||
this.setState({mode: IDLE})
|
||||
}
|
||||
|
||||
handleDeleteConfirmButtonClick (e) {
|
||||
let input = {
|
||||
name: this.props.member.name
|
||||
}
|
||||
|
||||
api.deleteMember(this.props.team.id, input)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
let member = this.props.member
|
||||
let currentUser = this.props.currentUser
|
||||
let isDisabled = (currentUser.id === member.id)
|
||||
|
||||
switch (this.state.mode) {
|
||||
case DELETE:
|
||||
return (
|
||||
<li className='MemberRow edit'>
|
||||
<div className='colDescription'>
|
||||
Are you sure to remove <strong>{member.profileName}</strong> ?
|
||||
</div>
|
||||
<div className='colDeleteConfirm'>
|
||||
<button className='deleteButton primary' onClick={e => this.handleDeleteConfirmButtonClick(e)}>Sure</button>
|
||||
<button className='deleteButton' onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
case IDLE:
|
||||
default:
|
||||
return (
|
||||
<li className='MemberRow'>
|
||||
<div className='colUserName'>
|
||||
<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>
|
||||
|
||||
<div className='colRole'>
|
||||
<select onChange={e => this.handleMemberRoleChange(e)} disabled={isDisabled} value={member._pivot_role} className='userRole'>
|
||||
<option value='owner'>Owner</option>
|
||||
<option value='member'>Member</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className='colDelete'>
|
||||
<button className='deleteButton' onClick={e => this.handleDeleteButtonClick(e)} disabled={isDisabled}><i className='fa fa-times fa-fw'/></button>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemberRow.propTypes = {
|
||||
member: PropTypes.shape(),
|
||||
currentUser: PropTypes.shape(),
|
||||
team: PropTypes.shape({
|
||||
id: PropTypes.number
|
||||
})
|
||||
}
|
||||
149
browser/main/modal/Preference/MemberSettingTab.js
Normal file
149
browser/main/modal/Preference/MemberSettingTab.js
Normal file
@@ -0,0 +1,149 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ProfileImage from 'boost/components/ProfileImage'
|
||||
import Select from 'react-select'
|
||||
import api from 'boost/api'
|
||||
import _ from 'lodash'
|
||||
import MemberRow from './MemberRow'
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
export default class MemberSettingTab extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
newMember: ''
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentTeam (props) {
|
||||
if (props == null) props = this.props
|
||||
return _.findWhere(props.teams, {id: props.currentTeamId})
|
||||
}
|
||||
|
||||
handleTeamSelectChange (e) {
|
||||
this.props.switchTeam(e.target.value)
|
||||
}
|
||||
|
||||
handleNewMemberChange (value) {
|
||||
this.setState({newMember: value})
|
||||
}
|
||||
|
||||
handleClickAddMemberButton (e) {
|
||||
let team = this.getCurrentTeam()
|
||||
if (team == null || team.userType !== 'team') return null
|
||||
|
||||
let input = {
|
||||
name: this.state.newMember,
|
||||
role: 'member'
|
||||
}
|
||||
api.setMember(team.id, input)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
renderTeamOptions () {
|
||||
return this.props.teams.map(team => {
|
||||
return (
|
||||
<option key={'team-' + team.id} value={team.id}>{team.name}</option>)
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
console.log(this.props.teams)
|
||||
|
||||
let team = this.getCurrentTeam()
|
||||
|
||||
if (team == null || team.userType === 'person') {
|
||||
return this.renderNoTeam()
|
||||
}
|
||||
|
||||
let membersEl = team.Members.map(member => (
|
||||
<MemberRow key={'user-' + member.id} member={member} team={team} currentUser={this.props.currentUser}/>
|
||||
))
|
||||
|
||||
return (
|
||||
<div className='MemberSettingTab content'>
|
||||
<div className='header'>
|
||||
<span>Setting of</span>
|
||||
<select
|
||||
value={this.props.currentTeamId}
|
||||
onChange={e => this.handleTeamSelectChange(e)}
|
||||
className='teamSelect'>
|
||||
{this.renderTeamOptions()}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className='membersTableSection section'>
|
||||
<div className='sectionTitle'>Members</div>
|
||||
<div className='addMember'>
|
||||
<div className='addMemberLabel'>Add member</div>
|
||||
<div className='addMemberControl'>
|
||||
<Select
|
||||
className='memberName'
|
||||
placeholder='Input username to add'
|
||||
autoload={false}
|
||||
asyncOptions={getUsers}
|
||||
onChange={val => this.handleNewMemberChange(val)}
|
||||
value={this.state.newMember}
|
||||
/>
|
||||
<button onClick={e => this.handleClickAddMemberButton(e)} className='addMemberBtn'>add</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul className='memberList'>
|
||||
<li className='header'>
|
||||
<div className='colUserName'>Username</div>
|
||||
<div className='colRole'>Role</div>
|
||||
<div className='colDelete'>Delete</div>
|
||||
</li>
|
||||
{membersEl}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderNoTeam () {
|
||||
return (
|
||||
<div className='TeamSettingTab content'>
|
||||
<div className='header'>
|
||||
<span>Setting of</span>
|
||||
<select
|
||||
value={this.props.currentTeamId}
|
||||
onChange={e => this.handleTeamSelectChange(e)}
|
||||
className='teamSelect'>
|
||||
{this.renderTeamOptions()}
|
||||
</select>
|
||||
</div>
|
||||
<div className='section'>Please select a team</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
MemberSettingTab.propTypes = {
|
||||
currentUser: PropTypes.shape(),
|
||||
teams: PropTypes.array,
|
||||
currentTeamId: PropTypes.number,
|
||||
switchTeam: PropTypes.func
|
||||
}
|
||||
171
browser/main/modal/Preference/TeamSettingTab.js
Normal file
171
browser/main/modal/Preference/TeamSettingTab.js
Normal file
@@ -0,0 +1,171 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import _ from 'lodash'
|
||||
import linkState from 'boost/linkState'
|
||||
import api from 'boost/api'
|
||||
|
||||
export default class TeamSettingTab extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
let team = this.getCurrentTeam(props)
|
||||
this.state = {
|
||||
teamName: team != null ? team.profileName : '',
|
||||
deleteConfirm: false,
|
||||
alert: null
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
let team = this.getCurrentTeam(nextProps)
|
||||
|
||||
this.setState({
|
||||
teamName: team != null ? team.profileName : '',
|
||||
deleteConfirm: false
|
||||
})
|
||||
}
|
||||
|
||||
getCurrentTeam (props) {
|
||||
if (props == null) props = this.props
|
||||
return _.findWhere(props.teams, {id: props.currentTeamId})
|
||||
}
|
||||
|
||||
handleTeamSelectChange (e) {
|
||||
this.props.switchTeam(e.target.value)
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
let input = {
|
||||
profileName: this.state.teamName
|
||||
}
|
||||
let alert = {
|
||||
type: 'info',
|
||||
message: 'Sending...'
|
||||
}
|
||||
this.setState({alert}, () => {
|
||||
api.updateTeamInfo(this.props.currentTeamId, input)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
let alert = {
|
||||
type: 'success',
|
||||
message: 'Successfully done!'
|
||||
}
|
||||
this.setState({alert})
|
||||
})
|
||||
.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 alert = {
|
||||
type: 'error',
|
||||
message: message
|
||||
}
|
||||
|
||||
this.setState({alert})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
handleDeleteConfirmButtonClick (e) {
|
||||
api.destroyTeam(this.props.currentTeamId)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
})
|
||||
.catch(err => {
|
||||
let 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
|
||||
console.log(message)
|
||||
})
|
||||
}
|
||||
|
||||
renderTeamOptions () {
|
||||
return this.props.teams.map(team => {
|
||||
return (
|
||||
<option key={'team-' + team.id} value={team.id}>{team.name}</option>)
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
let team = this.getCurrentTeam()
|
||||
|
||||
if (team == null || team.userType === 'person') {
|
||||
return this.renderNoTeam()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='TeamSettingTab content'>
|
||||
<div className='header'>
|
||||
<span>Setting of</span>
|
||||
<select
|
||||
value={this.props.currentTeamId}
|
||||
onChange={e => this.handleTeamSelectChange(e)}
|
||||
className='teamSelect'>
|
||||
{this.renderTeamOptions()}
|
||||
</select>
|
||||
</div>
|
||||
<div className='section'>
|
||||
<div className='sectionTitle'>Team profile</div>
|
||||
<div className='sectionInput'>
|
||||
<label className='label'>Team Name</label>
|
||||
<input valueLink={this.linkState('teamName')} type='text'/>
|
||||
</div>
|
||||
<div className='sectionConfirm'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)}>Save</button>
|
||||
|
||||
{this.state.alert != null
|
||||
? (
|
||||
<div className={'alert ' + this.state.alert.type}>{this.state.alert.message}</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!this.state.deleteConfirm
|
||||
? (
|
||||
<div className='section teamDelete'>
|
||||
<label>Delete this team</label>
|
||||
<button onClick={e => this.setState({deleteConfirm: true})} className='deleteBtn'><i className='fa fa-fw fa-trash'/> Delete</button>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div className='section teamDeleteConfirm'>
|
||||
<label>Are you sure to delete this team?</label>
|
||||
<button onClick={e => this.setState({deleteConfirm: false})}>Cancel</button>
|
||||
<button onClick={e => this.handleDeleteConfirmButtonClick(e)} className='deleteBtn'><i className='fa fa-fw fa-check'/> Sure</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderNoTeam () {
|
||||
return (
|
||||
<div className='TeamSettingTab content'>
|
||||
<div className='header'>
|
||||
<span>Setting of</span>
|
||||
<select
|
||||
value={this.props.currentTeamId}
|
||||
onChange={e => this.handleTeamSelectChange(e)}
|
||||
className='teamSelect'>
|
||||
{this.renderTeamOptions()}
|
||||
</select>
|
||||
</div>
|
||||
<div className='section'>Please select a team</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TeamSettingTab.propTypes = {
|
||||
currentTeamId: PropTypes.number,
|
||||
teams: PropTypes.array,
|
||||
switchTeam: PropTypes.func
|
||||
}
|
||||
|
||||
TeamSettingTab.prototype.linkState = linkState
|
||||
122
browser/main/modal/Preferences.js
Normal file
122
browser/main/modal/Preferences.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect, Provider } from 'react-redux'
|
||||
import linkState from 'boost/linkState'
|
||||
import store from 'boost/store'
|
||||
import AppSettingTab from './Preference/AppSettingTab'
|
||||
import HelpTab from './Preference/HelpTab'
|
||||
import FolderSettingTab from './Preference/FolderSettingTab'
|
||||
import ContactTab from './Preference/ContactTab'
|
||||
import { closeModal } from 'boost/modal'
|
||||
|
||||
const APP = 'APP'
|
||||
const HELP = 'HELP'
|
||||
const FOLDER = 'FOLDER'
|
||||
const CONTACT = 'CONTACT'
|
||||
|
||||
class Preferences extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
currentTab: APP
|
||||
}
|
||||
}
|
||||
|
||||
switchTeam (teamId) {
|
||||
this.setState({currentTeamId: teamId})
|
||||
}
|
||||
|
||||
handleNavButtonClick (tab) {
|
||||
return e => {
|
||||
this.setState({currentTab: tab})
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let content = this.renderContent()
|
||||
|
||||
let tabs = [
|
||||
{target: APP, label: 'Preferences'},
|
||||
{target: FOLDER, label: 'Manage folder'},
|
||||
{target: CONTACT, label: 'Contact form'}
|
||||
]
|
||||
|
||||
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 => closeModal()} className='closeBtn'>Done</button>
|
||||
</div>
|
||||
|
||||
<div className='nav'>
|
||||
{navButtons}
|
||||
</div>
|
||||
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderContent () {
|
||||
let { user, folders, dispatch } = this.props
|
||||
|
||||
switch (this.state.currentTab) {
|
||||
case HELP:
|
||||
return (<HelpTab/>)
|
||||
case FOLDER:
|
||||
return (
|
||||
<FolderSettingTab
|
||||
dispatch={dispatch}
|
||||
folders={folders}
|
||||
/>
|
||||
)
|
||||
case CONTACT:
|
||||
return (
|
||||
<ContactTab/>
|
||||
)
|
||||
case APP:
|
||||
default:
|
||||
return (
|
||||
<AppSettingTab
|
||||
user={user}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Preferences.propTypes = {
|
||||
user: PropTypes.shape({
|
||||
name: PropTypes.string
|
||||
}),
|
||||
folders: PropTypes.array,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
Preferences.prototype.linkState = linkState
|
||||
|
||||
function remap (state) {
|
||||
let { user, folders, status } = state
|
||||
|
||||
return {
|
||||
user,
|
||||
folders,
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
let RootComponent = connect(remap)(Preferences)
|
||||
export default class PreferencesModal extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<RootComponent/>
|
||||
</Provider>
|
||||
)
|
||||
}
|
||||
}
|
||||
115
browser/main/modal/Tutorial.js
Normal file
115
browser/main/modal/Tutorial.js
Normal file
@@ -0,0 +1,115 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import MarkdownPreview from 'boost/components/MarkdownPreview'
|
||||
import CodeEditor from 'boost/components/CodeEditor'
|
||||
|
||||
export default class Tutorial extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
slideIndex: 0
|
||||
}
|
||||
}
|
||||
|
||||
handlePriorSlideClick () {
|
||||
if (this.state.slideIndex > 0) this.setState({slideIndex: this.state.slideIndex - 1})
|
||||
}
|
||||
|
||||
handleNextSlideClick () {
|
||||
if (this.state.slideIndex < 4) this.setState({slideIndex: this.state.slideIndex + 1})
|
||||
}
|
||||
|
||||
startButtonClick (e) {
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
render () {
|
||||
let content = this.renderContent(this.state.slideIndex)
|
||||
|
||||
let dotElements = []
|
||||
for (let i = 0; i < 5; i++) {
|
||||
dotElements.push(<i key={i} className={'fa fa-fw fa-circle' + (i === this.state.slideIndex ? ' active' : '')}/>)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='Tutorial modal'>
|
||||
<button onClick={e => this.handlePriorSlideClick()} className={'priorBtn' + (this.state.slideIndex === 0 ? ' hide' : '')}>
|
||||
<i className='fa fa-fw fa-angle-left'/>
|
||||
</button>
|
||||
<button onClick={e => this.handleNextSlideClick()} className={'nextBtn' + (this.state.slideIndex === 4 ? ' hide' : '')}>
|
||||
<i className='fa fa-fw fa-angle-right'/>
|
||||
</button>
|
||||
{content}
|
||||
<div className='dots'>
|
||||
{dotElements}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderContent (index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return (<div className='slide slide0'>
|
||||
<div className='title'>Welcome to Boost</div>
|
||||
<div className='content'>
|
||||
Boost is a brand new note app for software<br/>
|
||||
Don't waste time cleaning up your data.<br/>
|
||||
devote that time to more creative work.<br/>
|
||||
Hack your memory.
|
||||
</div>
|
||||
</div>)
|
||||
case 1:
|
||||
let content = '## Boost is a note app for engineer.\n\n - Write with markdown\n - Stylize beautiful'
|
||||
return (<div className='slide slide1'>
|
||||
<div className='title'>Write with Markdown</div>
|
||||
<div className='content'>
|
||||
Markdown is available.<br/>
|
||||
Your notes will be stylized beautifully and quickly.
|
||||
<div className='markdown'>
|
||||
<pre className='left'>{content}</pre>
|
||||
<MarkdownPreview className='right' content={content}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>)
|
||||
case 2:
|
||||
let code = 'import shell from \'shell\'\r\nvar React = require(\'react\')\r\nvar { PropTypes } = React\r\nimport markdown from \'boost\/markdown\'\r\nvar ReactDOM = require(\'react-dom\')\r\n\r\nfunction handleAnchorClick (e) {\r\n shell.openExternal(e.target.href)\r\n e.preventDefault()\r\n}\r\n\r\nexport default class MarkdownPreview extends React.Component {\r\n componentDidMount () {\r\n this.addListener()\r\n }\r\n\r\n componentDidUpdate () {\r\n this.addListener()\r\n }\r\n\r\n componentWillUnmount () {\r\n this.removeListener()\r\n }'
|
||||
return (<div className='slide slide2'>
|
||||
<div className='title'>Beautiful code highlighting</div>
|
||||
<div className='content'>
|
||||
Boost supports code syntax highlighting.<br/>
|
||||
There are more than 100 different type of language.
|
||||
<div className='code'>
|
||||
<CodeEditor readOnly mode='jsx' code={code}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>)
|
||||
case 3:
|
||||
return (<div className='slide slide3'>
|
||||
<div className='title'>Easy to access with Finder</div>
|
||||
<div className='content'>
|
||||
The Finder helps you organize all of the files and documents.<br/>
|
||||
There is a short-cut key [control + shift + tab] to open the Finder.<br/>
|
||||
It is available to save your articles on the Clipboard<br/>
|
||||
by selecting your file with pressing Enter key,<br/>
|
||||
and to paste the contents of the Clipboard with [{process.platform === 'darwin' ? 'Command' : 'Control'}-V]
|
||||
|
||||
<img width='480' src='../../resources/finder.png'/>
|
||||
</div>
|
||||
</div>)
|
||||
case 4:
|
||||
return (<div className='slide slide4'>
|
||||
<div className='title'>Are you ready?</div>
|
||||
<div className='content'>
|
||||
<button onClick={e => this.startButtonClick(e)}>Start<br/>Boost</button>
|
||||
</div>
|
||||
</div>)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tutorial.propTypes = {
|
||||
close: PropTypes.func
|
||||
}
|
||||
Reference in New Issue
Block a user