mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
prepare alpha.5 (remain work: MD preview, keybind)
This commit is contained in:
@@ -44,10 +44,8 @@ class HomePage extends React.Component {
|
||||
}
|
||||
|
||||
function remap (state) {
|
||||
let status = state.status
|
||||
// Fetch articles
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
let { folders, articles } = data
|
||||
let { folders, articles, status } = state
|
||||
|
||||
if (articles == null) articles = []
|
||||
articles.sort((a, b) => {
|
||||
return new Date(b.updatedAt) - new Date(a.updatedAt)
|
||||
@@ -112,7 +110,13 @@ function remap (state) {
|
||||
// 1. team have one folder at least
|
||||
// or Change IDLE MODE
|
||||
if (status.mode === CREATE_MODE) {
|
||||
var newArticle = _.findWhere(articles, {status: 'NEW'})
|
||||
let newArticle = _.findWhere(articles, {status: 'NEW'})
|
||||
let FolderKey = folders[0].key
|
||||
if (folderFilters.length > 0) {
|
||||
let targetFolder = _.findWhere(folders, {name: folderFilters[0].value})
|
||||
if (targetFolder != null) FolderKey = targetFolder.key
|
||||
}
|
||||
|
||||
if (newArticle == null) {
|
||||
newArticle = {
|
||||
id: null,
|
||||
@@ -121,7 +125,7 @@ function remap (state) {
|
||||
content: '',
|
||||
mode: 'markdown',
|
||||
tags: [],
|
||||
FolderKey: folders[0].key,
|
||||
FolderKey: FolderKey,
|
||||
status: NEW
|
||||
}
|
||||
articles.unshift(newArticle)
|
||||
@@ -131,14 +135,12 @@ function remap (state) {
|
||||
status.mode = IDLE_MODE
|
||||
}
|
||||
|
||||
let props = {
|
||||
return {
|
||||
folders,
|
||||
status,
|
||||
articles,
|
||||
activeArticle
|
||||
}
|
||||
|
||||
return props
|
||||
}
|
||||
|
||||
HomePage.propTypes = {
|
||||
|
||||
@@ -101,8 +101,8 @@ export default class ArticleDetail extends React.Component {
|
||||
<div className='left'>
|
||||
<div className='info'>
|
||||
<FolderMark color={folder.color}/> {folder.name}
|
||||
Created {moment(activeArticle.createdAt).format('YYYY/MM/DD')}
|
||||
Updated {moment(activeArticle.updatedAt).format('YYYY/MM/DD')}
|
||||
Created : {moment(activeArticle.createdAt).format('YYYY/MM/DD')}
|
||||
Updated : {moment(activeArticle.updatedAt).format('YYYY/MM/DD')}
|
||||
</div>
|
||||
<div className='tags'><i className='fa fa-fw fa-tags'/>{tags}</div>
|
||||
</div>
|
||||
@@ -183,7 +183,6 @@ export default class ArticleDetail extends React.Component {
|
||||
<option key={folder.key} value={folder.key}>{folder.name}</option>
|
||||
)
|
||||
})
|
||||
console.log('edit rendered')
|
||||
|
||||
return (
|
||||
<div className='ArticleDetail edit'>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ProfileImage from 'boost/components/ProfileImage'
|
||||
import { findWhere } from 'lodash'
|
||||
import { setSearchFilter, switchFolder, switchMode, CREATE_MODE } from 'boost/actions'
|
||||
import { openModal } from 'boost/modal'
|
||||
@@ -7,6 +6,8 @@ import FolderMark from 'boost/components/FolderMark'
|
||||
import Preferences from 'boost/components/modal/Preferences'
|
||||
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
|
||||
|
||||
import remote from 'remote'
|
||||
|
||||
export default class ArticleNavigator extends React.Component {
|
||||
handlePreferencesButtonClick (e) {
|
||||
openModal(Preferences)
|
||||
@@ -50,10 +51,12 @@ export default class ArticleNavigator extends React.Component {
|
||||
)
|
||||
})
|
||||
|
||||
let userName = remote.getGlobal('process').env.USER
|
||||
|
||||
return (
|
||||
<div className='ArticleNavigator'>
|
||||
<div className='userInfo'>
|
||||
<div className='userProfileName'>{process.env.USER}</div>
|
||||
<div className='userProfileName'>{userName}</div>
|
||||
<div className='userName'>local</div>
|
||||
<button onClick={e => this.handlePreferencesButtonClick(e)} className='settingBtn'><i className='fa fa-fw fa-chevron-down'/></button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ExternalLink from 'boost/components/ExternalLink'
|
||||
import { setSearchFilter } from 'boost/actions'
|
||||
|
||||
@@ -8,6 +9,12 @@ export default class ArticleTopBar extends React.Component {
|
||||
|
||||
dispatch(setSearchFilter(e.target.value))
|
||||
}
|
||||
handleSearchClearButton (e) {
|
||||
let { dispatch } = this.props
|
||||
|
||||
dispatch(setSearchFilter(''))
|
||||
ReactDOM.findDOMNode(this.refs.searchInput).focus()
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
@@ -15,7 +22,12 @@ export default class ArticleTopBar extends React.Component {
|
||||
<div className='left'>
|
||||
<div className='search'>
|
||||
<i className='fa fa-search fa-fw' />
|
||||
<input value={this.props.status.search} onChange={e => this.handleSearchChange(e)} placeholder='Search' type='text'/>
|
||||
<input ref='searchInput' value={this.props.status.search} onChange={e => this.handleSearchChange(e)} placeholder='Search' type='text'/>
|
||||
{
|
||||
this.props.status.search != null && this.props.status.search.length > 0
|
||||
? <button onClick={e => this.handleSearchClearButton(e)} className='searchClearBtn'><i className='fa fa-times'/></button>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='right'>
|
||||
|
||||
@@ -6,15 +6,9 @@ import MainPage from './MainPage'
|
||||
import HomePage from './HomePage'
|
||||
// import auth from 'boost/auth'
|
||||
import store from 'boost/store'
|
||||
let ReactDOM = require('react-dom')
|
||||
import ReactDOM from 'react-dom'
|
||||
require('../styles/main/index.styl')
|
||||
|
||||
function onlyUser (state, replaceState) {
|
||||
// let currentUser = auth.user()
|
||||
// if (currentUser == null) return replaceState('login', '/login')
|
||||
// if (state.location.pathname === '/') return replaceState('user', '/users/' + currentUser.id)
|
||||
}
|
||||
|
||||
let routes = (
|
||||
<Route path='/' component={MainPage}>
|
||||
<IndexRoute name='home' component={HomePage}/>
|
||||
@@ -22,7 +16,6 @@ let routes = (
|
||||
)
|
||||
|
||||
let el = document.getElementById('content')
|
||||
|
||||
ReactDOM.render((
|
||||
<div>
|
||||
<Provider store={store}>
|
||||
|
||||
@@ -100,6 +100,7 @@ iptFocusBorderColor = #369DCD
|
||||
color white
|
||||
margin 0 2px
|
||||
padding 0
|
||||
border 1px solid darken(brandColor, 10%)
|
||||
button.tagRemoveBtn
|
||||
color white
|
||||
border-radius 2px
|
||||
@@ -117,13 +118,13 @@ iptFocusBorderColor = #369DCD
|
||||
font-size 12px
|
||||
line-height 12px
|
||||
input.tagInput
|
||||
background-color white
|
||||
background-color transparent
|
||||
outline none
|
||||
margin 0 2px
|
||||
border-radius 2px
|
||||
border 1px solid borderColor
|
||||
border none
|
||||
transition 0.1s
|
||||
&:focus
|
||||
border-color iptFocusBorderColor
|
||||
height 18px
|
||||
|
||||
|
||||
.right
|
||||
@@ -165,8 +166,7 @@ iptFocusBorderColor = #369DCD
|
||||
border none
|
||||
background-color transparent
|
||||
line-height 60px
|
||||
font-size 32px
|
||||
font-weight bold
|
||||
font-size 24px
|
||||
outline none
|
||||
&.idle
|
||||
.detailInfo
|
||||
@@ -217,9 +217,9 @@ iptFocusBorderColor = #369DCD
|
||||
absolute top bottom
|
||||
left 45px
|
||||
right 15px
|
||||
font-size 32px
|
||||
font-size 24px
|
||||
line-height 60px
|
||||
font-weight bold
|
||||
|
||||
white-space nowrap
|
||||
overflow-x auto
|
||||
overflow-y hidden
|
||||
|
||||
@@ -39,6 +39,7 @@ articleNavBgColor = #353535
|
||||
.controlSection
|
||||
height 88px
|
||||
padding 22px 15px
|
||||
margin-bottom 44px
|
||||
.newPostBtn
|
||||
border none
|
||||
background-color brandColor
|
||||
@@ -80,6 +81,9 @@ articleNavBgColor = #353535
|
||||
border-color brandColor
|
||||
.folders
|
||||
margin-bottom 15px
|
||||
.folderList
|
||||
height 340px
|
||||
overflow-y auto
|
||||
.folderList button
|
||||
height 33px
|
||||
width 199px
|
||||
|
||||
@@ -2,8 +2,9 @@ bgColor = #E6E6E6
|
||||
inputBgColor = white
|
||||
iptFocusBorderColor = #369DCD
|
||||
|
||||
refreshBtColor = #B3B3B3
|
||||
refreshBtnActiveColor = #3A3A3A
|
||||
topBarBtnColor = #B3B3B3
|
||||
topBarBtnBgColor = #B3B3B3
|
||||
topBarBtnBgActiveColor = #3A3A3A
|
||||
|
||||
infoBtnColor = bgColor
|
||||
infoBtnBgColor = #B3B3B3
|
||||
@@ -41,7 +42,7 @@ infoBtnActiveBgColor = #3A3A3A
|
||||
z-index 0
|
||||
&:focus
|
||||
border-color iptFocusBorderColor
|
||||
i.fa
|
||||
i.fa.fa-search
|
||||
position absolute
|
||||
display block
|
||||
top 0
|
||||
@@ -49,6 +50,23 @@ infoBtnActiveBgColor = #3A3A3A
|
||||
line-height 33px
|
||||
z-index 1
|
||||
pointer-events none
|
||||
.searchClearBtn
|
||||
position absolute
|
||||
top 6px
|
||||
right 10px
|
||||
width 20px
|
||||
height 20px
|
||||
border-radius 10px
|
||||
border none
|
||||
background-color transparent
|
||||
color topBarBtnColor
|
||||
transition 0.1s
|
||||
line-height 20px
|
||||
text-align center
|
||||
padding 0
|
||||
&:hover
|
||||
color white
|
||||
background-color topBarBtnBgColor
|
||||
&>.refreshBtn
|
||||
float left
|
||||
width 33px
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
export const ARTICLE_UPDATE = 'ARTICLE_UPDATE'
|
||||
export const ARTICLE_DESTROY = 'ARTICLE_DESTROY'
|
||||
export const FOLDER_CREATE = 'FOLDER_CREATE'
|
||||
export const FOLDER_UPDATE = 'FOLDER_UPDATE'
|
||||
export const FOLDER_DESTROY = 'FOLDER_DESTROY'
|
||||
|
||||
export const SWITCH_FOLDER = 'SWITCH_FOLDER'
|
||||
@@ -28,10 +29,10 @@ export function updateArticle (article) {
|
||||
}
|
||||
}
|
||||
|
||||
export function destroyArticle (articleKey) {
|
||||
export function destroyArticle (key) {
|
||||
return {
|
||||
type: ARTICLE_DESTROY,
|
||||
data: { articleKey }
|
||||
data: { key }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +43,13 @@ export function createFolder (folder) {
|
||||
}
|
||||
}
|
||||
|
||||
export function updateFolder (folder) {
|
||||
return {
|
||||
type: FOLDER_UPDATE,
|
||||
data: { folder }
|
||||
}
|
||||
}
|
||||
|
||||
export function destroyFolder (key) {
|
||||
return {
|
||||
type: FOLDER_DESTROY,
|
||||
|
||||
@@ -8,6 +8,7 @@ const GREEN = '#02FF26'
|
||||
const DARKGREEN = '#008A59'
|
||||
const RED = '#E10051'
|
||||
const PURPLE = '#B013A4'
|
||||
const BRAND_COLOR = '#2BAC8F'
|
||||
|
||||
function getColorByIndex (index) {
|
||||
switch (index % 8) {
|
||||
@@ -28,7 +29,7 @@ function getColorByIndex (index) {
|
||||
case 7:
|
||||
return PURPLE
|
||||
default:
|
||||
return 'gray'
|
||||
return BRAND_COLOR
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export default class TagSelect extends React.Component {
|
||||
onKeyDown={e => this.handleKeyDown(e)}
|
||||
ref='tagInput'
|
||||
valueLink={this.linkState('input')}
|
||||
placeholder='new tag'
|
||||
placeholder='Click here to add tags'
|
||||
className='tagInput'/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import linkState from 'boost/linkState'
|
||||
import keygen from 'boost/keygen'
|
||||
import { createFolder } from 'boost/actions'
|
||||
import store from 'boost/store'
|
||||
|
||||
@@ -20,15 +19,9 @@ export default class CreateNewFolder extends React.Component {
|
||||
|
||||
handleConfirmButton (e) {
|
||||
let { close } = this.props
|
||||
let key = keygen()
|
||||
let name = this.state.name
|
||||
let input = {
|
||||
name,
|
||||
key,
|
||||
createAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
// random number (0-7)
|
||||
color: Math.round(Math.random() * 7)
|
||||
name
|
||||
}
|
||||
|
||||
store.dispatch(createFolder(input))
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import api from 'boost/api'
|
||||
import linkState from 'boost/linkState'
|
||||
import FolderMark from 'boost/components/FolderMark'
|
||||
import store from 'boost/store'
|
||||
import { updateFolder, destroyFolder } from 'boost/actions'
|
||||
|
||||
const IDLE = 'IDLE'
|
||||
const EDIT = 'EDIT'
|
||||
@@ -12,22 +13,21 @@ export default class FolderRow extends React.Component {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
mode: IDLE,
|
||||
name: props.folder.name,
|
||||
public: props.folder.public
|
||||
mode: IDLE
|
||||
}
|
||||
}
|
||||
|
||||
handleCancelButtonClick (e) {
|
||||
this.setState({
|
||||
mode: IDLE,
|
||||
name: this.props.folder.name,
|
||||
public: this.props.folder.public
|
||||
mode: IDLE
|
||||
})
|
||||
}
|
||||
|
||||
handleEditButtonClick (e) {
|
||||
this.setState({mode: EDIT})
|
||||
this.setState({
|
||||
mode: EDIT,
|
||||
name: this.props.folder.name
|
||||
})
|
||||
}
|
||||
|
||||
handleDeleteButtonClick (e) {
|
||||
@@ -39,31 +39,21 @@ export default class FolderRow extends React.Component {
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
let { folder } = this.props
|
||||
let input = {
|
||||
name: this.state.name,
|
||||
public: !!parseInt(this.state.public, 10)
|
||||
name: this.state.name
|
||||
}
|
||||
Object.assign(folder, input)
|
||||
|
||||
api.updateFolder(this.props.folder.id, input)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
this.setState({mode: IDLE})
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
store.dispatch(updateFolder(folder))
|
||||
this.setState({
|
||||
mode: IDLE
|
||||
})
|
||||
}
|
||||
|
||||
handleDeleteConfirmButtonClick (e) {
|
||||
api.destroyFolder(this.props.folder.id)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
let { folder } = this.props
|
||||
store.dispatch(destroyFolder(folder.key))
|
||||
}
|
||||
|
||||
render () {
|
||||
@@ -76,12 +66,6 @@ export default class FolderRow extends React.Component {
|
||||
<div className='folderName'>
|
||||
<input valueLink={this.linkState('name')} type='text'/>
|
||||
</div>
|
||||
<div className='folderPublic'>
|
||||
<select value={this.state.public} onChange={e => this.handleFolderPublicChange(e)}>
|
||||
<option value='0'>Private</option>
|
||||
<option value='1'>Public</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)} className='primary'>Save</button>
|
||||
<button onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
|
||||
@@ -102,8 +86,7 @@ export default class FolderRow extends React.Component {
|
||||
default:
|
||||
return (
|
||||
<div className='FolderRow'>
|
||||
<div className='folderName'><FolderMark id={folder.id}/> {folder.name}</div>
|
||||
<div className='folderPublic'>{folder.public ? 'Public' : 'Private'}</div>
|
||||
<div className='folderName'><FolderMark color={folder.color}/> {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>
|
||||
|
||||
@@ -1,87 +1,44 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import _ from 'lodash'
|
||||
import FolderRow from './FolderRow'
|
||||
import linkState from 'boost/linkState'
|
||||
import api from 'boost/api'
|
||||
import { createFolder } from 'boost/actions'
|
||||
|
||||
export default class FolderSettingTab extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
public: 0
|
||||
name: ''
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentTeam (props) {
|
||||
if (props == null) props = this.props
|
||||
return _.findWhere(props.teams, {id: props.currentTeamId})
|
||||
}
|
||||
|
||||
handleTeamSelectChange (e) {
|
||||
this.props.switchTeam(e.target.value)
|
||||
}
|
||||
|
||||
handleFolderPublicChange (e) {
|
||||
this.setState({public: e.target.value})
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
let team = this.getCurrentTeam()
|
||||
let input = {
|
||||
UserId: team.id,
|
||||
name: this.state.name,
|
||||
public: !!parseInt(this.state.public, 10)
|
||||
}
|
||||
if (this.state.name.trim().length === 0) return false
|
||||
|
||||
api.createFolder(input)
|
||||
.then(res => {
|
||||
console.log(res.body)
|
||||
this.setState({
|
||||
name: '',
|
||||
public: 0
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != null) throw err
|
||||
else console.error(err)
|
||||
})
|
||||
}
|
||||
let { dispatch } = this.props
|
||||
|
||||
renderTeamOptions () {
|
||||
return this.props.teams.map(team => {
|
||||
return (
|
||||
<option key={'team-' + team.id} value={team.id}>{team.name}</option>)
|
||||
})
|
||||
dispatch(createFolder({
|
||||
name: this.state.name
|
||||
}))
|
||||
|
||||
this.setState({name: ''})
|
||||
}
|
||||
|
||||
render () {
|
||||
let team = this.getCurrentTeam()
|
||||
console.log(team.Folders)
|
||||
let folderElements = team.Folders.map(folder => {
|
||||
let { folders } = this.props
|
||||
let folderElements = folders.map(folder => {
|
||||
return (
|
||||
<FolderRow key={'folder-' + folder.id} folder={folder}/>
|
||||
<FolderRow key={'folder-' + folder.key} folder={folder}/>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='FolderSettingTab 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'>Folders</div>
|
||||
<div className='sectionTitle'>Manage folder</div>
|
||||
<div className='folderTable'>
|
||||
<div className='folderHeader'>
|
||||
<div className='folderName'>Folder name</div>
|
||||
<div className='folderPublic'>Public/Private</div>
|
||||
<div className='folderControl'>Edit/Delete</div>
|
||||
</div>
|
||||
{folderElements}
|
||||
@@ -89,12 +46,6 @@ export default class FolderSettingTab extends React.Component {
|
||||
<div className='folderName'>
|
||||
<input valueLink={this.linkState('name')} type='text' placeholder='New Folder'/>
|
||||
</div>
|
||||
<div className='folderPublic'>
|
||||
<select value={this.state.public} onChange={e => this.handleFolderPublicChange(e)}>
|
||||
<option value='0'>Private</option>
|
||||
<option value='1'>Public</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)} className='primary'>Add</button>
|
||||
</div>
|
||||
@@ -107,9 +58,8 @@ export default class FolderSettingTab extends React.Component {
|
||||
}
|
||||
|
||||
FolderSettingTab.propTypes = {
|
||||
currentTeamId: PropTypes.number,
|
||||
teams: PropTypes.array,
|
||||
switchTeam: PropTypes.func
|
||||
folders: PropTypes.array,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
FolderSettingTab.prototype.linkState = linkState
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect, Provider } from 'react-redux'
|
||||
import linkState from 'boost/linkState'
|
||||
import api from 'boost/api'
|
||||
import store from 'boost/store'
|
||||
import AppSettingTab from './Preference/AppSettingTab'
|
||||
import HelpTab from './Preference/HelpTab'
|
||||
import TeamSettingTab from './Preference/TeamSettingTab'
|
||||
import MemberSettingTab from './Preference/MemberSettingTab'
|
||||
import FolderSettingTab from './Preference/FolderSettingTab'
|
||||
import { closeModal } from 'boost/modal'
|
||||
|
||||
var { findDOMNode } = require('react-dom')
|
||||
|
||||
const PROFILE = 'PROFILE'
|
||||
const APP = 'APP'
|
||||
const HELP = 'HELP'
|
||||
const TEAM = 'TEAM'
|
||||
const MEMBER = 'MEMBER'
|
||||
const FOLDER = 'FOLDER'
|
||||
|
||||
class Preferences extends React.Component {
|
||||
@@ -42,8 +34,8 @@ class Preferences extends React.Component {
|
||||
let content = this.renderContent()
|
||||
|
||||
let tabs = [
|
||||
{target: APP, label: 'Preferences'}
|
||||
// {target: FOLDER, label: 'Manage folder'}
|
||||
{target: APP, label: 'Preferences'},
|
||||
{target: FOLDER, label: 'Manage folder'}
|
||||
]
|
||||
|
||||
let navButtons = tabs.map(tab => (
|
||||
@@ -67,181 +59,190 @@ class Preferences extends React.Component {
|
||||
}
|
||||
|
||||
renderContent () {
|
||||
let { folders, dispatch } = this.props
|
||||
|
||||
switch (this.state.currentTab) {
|
||||
case HELP:
|
||||
return (<HelpTab/>)
|
||||
case FOLDER:
|
||||
return (
|
||||
<FolderSettingTab
|
||||
dispatch={dispatch}
|
||||
folders={folders}
|
||||
/>
|
||||
)
|
||||
case APP:
|
||||
default:
|
||||
return (<AppSettingTab/>)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
}
|
||||
// let profileState = this.state.profile
|
||||
// profileState.userInfo.alert = {
|
||||
// type: 'error',
|
||||
// message: message
|
||||
// }
|
||||
|
||||
this.setState({profile: profileState})
|
||||
})
|
||||
})
|
||||
}
|
||||
// this.setState({profile: profileState})
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
handlePasswordSaveButton (e) {
|
||||
let profileState = this.state.profile
|
||||
// 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
|
||||
}
|
||||
// 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...'
|
||||
}
|
||||
// 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}, () => {
|
||||
// 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
|
||||
// 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 = ''
|
||||
// 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()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
// 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>
|
||||
// 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>
|
||||
// {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>
|
||||
// <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>
|
||||
|
||||
{profileState.password.alert != null
|
||||
? (
|
||||
<div className={'alert ' + profileState.password.alert.type}>{profileState.password.alert.message}</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// {profileState.password.alert != null
|
||||
// ? (
|
||||
// <div className={'alert ' + profileState.password.alert.type}>{profileState.password.alert.message}</div>
|
||||
// )
|
||||
// : null}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
Preferences.propTypes = {
|
||||
currentUser: PropTypes.shape(),
|
||||
close: PropTypes.func
|
||||
folders: PropTypes.array,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
Preferences.prototype.linkState = linkState
|
||||
|
||||
function remap (state) {
|
||||
let currentUser = state.currentUser
|
||||
let status = state.status
|
||||
let { folders, status } = state
|
||||
console.log(state)
|
||||
|
||||
return {
|
||||
currentUser,
|
||||
folders,
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
64
lib/dataStore.js
Normal file
64
lib/dataStore.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import keygen from 'boost/keygen'
|
||||
|
||||
let defaultContent = '**Boost**は全く新しいエンジニアライクのノートアプリです。\n\n# ◎特徴\nBoostはエンジニアの仕事を圧倒的に効率化するいくつかの機能を備えています。\nその一部をご紹介します。\n1. Folderで情報を分類\n2. 豊富なsyantaxに対応\n3. Finder機能\n4. チーム機能(リアルタイム搭載)\n\n* * * *\n\n# 1. Folderで情報を分類、欲しい情報にすぐアクセス。\n左側のバーに存在する「Folders」。\n今すぐプラスボタンを押しましょう。\n分類の仕方も自由自在です。\n- 言語やフレームワークごとにFolderを作成\n- 自分用のカジュアルなメモをまとめる場としてFolderを作成\n\n\n# 2. 豊富なsyantaxに対応、自分の脳の代わりに。\nプログラミングに関する情報を全て、手軽に保存しましょう。\n- mdで、apiの仕様をまとめる\n- よく使うモジュールやスニペット\n\nBoostに保存しておくことで、何度も同じコードを書いたり調べたりする必要がなくなります。\n\n# 3. Finder機能を搭載、もうコマンドを手打ちする必要はありません。\n**「shift+cmd+tab」** を同時に押してみてください。\nここでは、一瞬でBoostの中身を検索するウィンドウを表示させることができます。\n\n矢印キーで選択、Enterを押し、cmd+vでペーストすると…続きはご自身の目でお確かめください。\n- sqlやlinux等の、よく使うが手打ちが面倒なコマンド\n- (メールやカスタマーサポート等でよく使うフレーズ)\n\n私たちは、圧倒的な効率性を支援します。\n\n# 4. チーム機能を搭載、シームレスな情報共有の場を実現。\n開発の設計思想やmdファイルの共有等、チームによって用途は様々ですが、Boostは多くの情報共有の課題について解決策を投げかけます。\n魅力を感じたら、左下のプラスボタンを今すぐクリック。\n\n\n* * * *\n\n\n## ◎詳しくは\nこちらのブログ( http://blog-jp.b00st.io )にて随時更新しています。\n\nそれでは素晴らしいエンジニアライフを!\n\n## Hack your memory**'
|
||||
|
||||
export function init () {
|
||||
console.log('initialize data store')
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
if (data == null) {
|
||||
let defaultFolder = {
|
||||
name: 'default',
|
||||
key: keygen()
|
||||
}
|
||||
let defaultArticle = {
|
||||
title: 'Boostとは',
|
||||
tags: ['boost', 'intro'],
|
||||
content: defaultContent,
|
||||
mode: 'markdown',
|
||||
key: keygen(),
|
||||
FolderKey: defaultFolder.key
|
||||
}
|
||||
|
||||
data = {
|
||||
articles: [defaultArticle],
|
||||
folders: [defaultFolder],
|
||||
version: '0.4'
|
||||
}
|
||||
localStorage.setItem('local', JSON.stringify(data))
|
||||
}
|
||||
}
|
||||
|
||||
function getKey (teamId) {
|
||||
return teamId == null
|
||||
? 'local'
|
||||
: `team-${teamId}`
|
||||
}
|
||||
|
||||
export function getData (teamId) {
|
||||
let key = getKey(teamId)
|
||||
return JSON.parse(localStorage.getItem(key))
|
||||
}
|
||||
|
||||
export function setArticles (teamId, articles) {
|
||||
let key = getKey(teamId)
|
||||
let data = JSON.parse(localStorage.getItem(key))
|
||||
data.articles = articles
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
}
|
||||
|
||||
export function setFolders (teamId, folders) {
|
||||
let key = getKey(teamId)
|
||||
let data = JSON.parse(localStorage.getItem(key))
|
||||
data.folders = folders
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
}
|
||||
|
||||
export default (function () {
|
||||
init()
|
||||
return {
|
||||
init,
|
||||
getData,
|
||||
setArticles,
|
||||
setFolders
|
||||
}
|
||||
})()
|
||||
201
lib/reducer.js
201
lib/reducer.js
@@ -1,56 +1,114 @@
|
||||
import { combineReducers } from 'redux'
|
||||
import _ from 'lodash'
|
||||
import { SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, SET_SEARCH_FILTER, SET_TAG_FILTER, USER_UPDATE, ARTICLE_UPDATE, ARTICLE_DESTROY, FOLDER_CREATE, FOLDER_DESTROY, IDLE_MODE, CREATE_MODE } from './actions'
|
||||
import auth from 'boost/auth'
|
||||
import { SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, SET_SEARCH_FILTER, SET_TAG_FILTER, ARTICLE_UPDATE, ARTICLE_DESTROY, FOLDER_CREATE, FOLDER_UPDATE, FOLDER_DESTROY, IDLE_MODE, CREATE_MODE } from './actions'
|
||||
import dataStore from 'boost/dataStore'
|
||||
import keygen from 'boost/keygen'
|
||||
|
||||
let defaultContent = '**Boost**は全く新しいエンジニアライクのノートアプリです。\n\n# ◎特徴\nBoostはエンジニアの仕事を圧倒的に効率化するいくつかの機能を備えています。\nその一部をご紹介します。\n1. Folderで情報を分類\n2. 豊富なsyantaxに対応\n3. Finder機能\n4. チーム機能(リアルタイム搭載)\n\n* * * *\n\n# 1. Folderで情報を分類、欲しい情報にすぐアクセス。\n左側のバーに存在する「Folders」。\n今すぐプラスボタンを押しましょう。\n分類の仕方も自由自在です。\n- 言語やフレームワークごとにFolderを作成\n- 自分用のカジュアルなメモをまとめる場としてFolderを作成\n\n\n# 2. 豊富なsyantaxに対応、自分の脳の代わりに。\nプログラミングに関する情報を全て、手軽に保存しましょう。\n- mdで、apiの仕様をまとめる\n- よく使うモジュールやスニペット\n\nBoostに保存しておくことで、何度も同じコードを書いたり調べたりする必要がなくなります。\n\n# 3. Finder機能を搭載、もうコマンドを手打ちする必要はありません。\n**「shift+cmd+tab」** を同時に押してみてください。\nここでは、一瞬でBoostの中身を検索するウィンドウを表示させることができます。\n\n矢印キーで選択、Enterを押し、cmd+vでペーストすると…続きはご自身の目でお確かめください。\n- sqlやlinux等の、よく使うが手打ちが面倒なコマンド\n- (メールやカスタマーサポート等でよく使うフレーズ)\n\n私たちは、圧倒的な効率性を支援します。\n\n# 4. チーム機能を搭載、シームレスな情報共有の場を実現。\n開発の設計思想やmdファイルの共有等、チームによって用途は様々ですが、Boostは多くの情報共有の課題について解決策を投げかけます。\n魅力を感じたら、左下のプラスボタンを今すぐクリック。\n\n\n* * * *\n\n\n## ◎詳しくは\nこちらのブログ( http://blog-jp.b00st.io )にて随時更新しています。\n\nそれでは素晴らしいエンジニアライフを!\n\n## Hack your memory**'
|
||||
|
||||
const initialStatus = {
|
||||
mode: IDLE_MODE,
|
||||
search: ''
|
||||
}
|
||||
|
||||
function getInitialArticles () {
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
if (data == null) {
|
||||
let defaultFolder = {
|
||||
name: 'default',
|
||||
key: keygen()
|
||||
}
|
||||
let defaultArticle = {
|
||||
title: 'Boostとは',
|
||||
tags: ['boost', 'intro'],
|
||||
content: defaultContent,
|
||||
mode: 'markdown',
|
||||
key: keygen(),
|
||||
FolderKey: defaultFolder.key
|
||||
}
|
||||
let data = dataStore.getData()
|
||||
let initialArticles = data.articles
|
||||
let initialFolders = data.folders
|
||||
|
||||
data = {
|
||||
articles: [defaultArticle],
|
||||
folders: [defaultFolder],
|
||||
version: require('remote').require('app').getVersion()
|
||||
}
|
||||
localStorage.setItem('local', JSON.stringify(data))
|
||||
}
|
||||
|
||||
return data.articles
|
||||
}
|
||||
|
||||
function currentUser (state, action) {
|
||||
function folders (state = initialFolders, action) {
|
||||
state = state.slice()
|
||||
switch (action.type) {
|
||||
case USER_UPDATE:
|
||||
let user = action.data.user
|
||||
case FOLDER_CREATE:
|
||||
{
|
||||
let newFolder = action.data.folder
|
||||
Object.assign(newFolder, {
|
||||
key: keygen(),
|
||||
createAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
// random number (0-7)
|
||||
color: Math.round(Math.random() * 7)
|
||||
})
|
||||
|
||||
return auth.user(user)
|
||||
let conflictFolder = _.findWhere(state, {name: newFolder.name})
|
||||
if (conflictFolder != null) throw new Error('name conflicted!')
|
||||
state.push(newFolder)
|
||||
|
||||
dataStore.setFolders(null, state)
|
||||
return state
|
||||
}
|
||||
case FOLDER_UPDATE:
|
||||
{
|
||||
let folder = action.data.folder
|
||||
let targetFolder = _.findWhere(state, {key: folder.key})
|
||||
|
||||
// Folder existence check
|
||||
if (targetFolder == null) throw new Error('Folder doesnt exist')
|
||||
// Name conflict check
|
||||
if (targetFolder.name !== folder.name) {
|
||||
let conflictFolder = _.findWhere(state, {name: folder.name})
|
||||
if (conflictFolder != null) throw new Error('Name conflicted')
|
||||
}
|
||||
Object.assign(targetFolder, folder, {
|
||||
updatedAt: new Date()
|
||||
})
|
||||
|
||||
dataStore.setFolders(null, state)
|
||||
return state
|
||||
}
|
||||
case FOLDER_DESTROY:
|
||||
{
|
||||
if (state.length < 2) throw new Error('Folder must exist more than one')
|
||||
|
||||
let targetKey = action.data.key
|
||||
let targetIndex = _.findIndex(state, folder => folder.key === targetKey)
|
||||
if (targetIndex >= 0) {
|
||||
state.splice(targetIndex, 1)
|
||||
}
|
||||
dataStore.setFolders(null, state)
|
||||
return state
|
||||
}
|
||||
default:
|
||||
if (state == null) return auth.user()
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function status (state, action) {
|
||||
function articles (state = initialArticles, action) {
|
||||
state = state.slice()
|
||||
switch (action.type) {
|
||||
case ARTICLE_UPDATE:
|
||||
{
|
||||
let article = action.data.article
|
||||
|
||||
let targetIndex = _.findIndex(state, _article => article.key === _article.key)
|
||||
if (targetIndex < 0) state.unshift(article)
|
||||
else state.splice(targetIndex, 1, article)
|
||||
|
||||
dataStore.setArticles(null, state)
|
||||
return state
|
||||
}
|
||||
case ARTICLE_DESTROY:
|
||||
{
|
||||
let articleKey = action.data.key
|
||||
|
||||
let targetIndex = _.findIndex(state, _article => articleKey === _article.key)
|
||||
if (targetIndex >= 0) state.splice(targetIndex, 1)
|
||||
|
||||
dataStore.setArticles(null, state)
|
||||
return state
|
||||
}
|
||||
case FOLDER_DESTROY:
|
||||
{
|
||||
let folderKey = action.data.key
|
||||
|
||||
state = state.filter(article => article.FolderKey !== folderKey)
|
||||
|
||||
dataStore.setArticles(null, state)
|
||||
return state
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function status (state = initialStatus, action) {
|
||||
switch (action.type) {
|
||||
case SWITCH_FOLDER:
|
||||
state.mode = IDLE_MODE
|
||||
@@ -78,77 +136,12 @@ function status (state, action) {
|
||||
|
||||
return state
|
||||
default:
|
||||
if (state == null) return initialStatus
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function articles (state, action) {
|
||||
switch (action.type) {
|
||||
case ARTICLE_UPDATE:
|
||||
{
|
||||
console.log(action)
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
let { articles } = data
|
||||
let article = action.data.article
|
||||
|
||||
let targetIndex = _.findIndex(articles, _article => article.key === _article.key)
|
||||
if (targetIndex < 0) articles.unshift(article)
|
||||
else articles.splice(targetIndex, 1, article)
|
||||
|
||||
localStorage.setItem('local', JSON.stringify(data))
|
||||
state.articles = articles
|
||||
}
|
||||
return state
|
||||
case ARTICLE_DESTROY:
|
||||
{
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
let { articles } = data
|
||||
let articleKey = action.data.articleKey
|
||||
|
||||
let targetIndex = _.findIndex(articles, _article => articleKey === _article.key)
|
||||
if (targetIndex >= 0) articles.splice(targetIndex, 1)
|
||||
|
||||
state.articles = articles
|
||||
localStorage.setItem('local', JSON.stringify(data))
|
||||
}
|
||||
return state
|
||||
case FOLDER_CREATE:
|
||||
{
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
let { folders } = data
|
||||
let newFolder = action.data.folder
|
||||
|
||||
let conflictFolder = _.findWhere(folders, {name: newFolder.name})
|
||||
if (conflictFolder != null) throw new Error('name conflicted!')
|
||||
folders.push(newFolder)
|
||||
|
||||
localStorage.setItem('local', JSON.stringify(data))
|
||||
state.folders = folders
|
||||
}
|
||||
return state
|
||||
case FOLDER_DESTROY:
|
||||
{
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
let { folderKey } = action.data
|
||||
let articles = data.articles
|
||||
articles = articles.filter(article => article.FolderKey !== folderKey)
|
||||
let folders = data.folders
|
||||
folders = folders.filter(folder => folder.key !== folderKey)
|
||||
|
||||
localStorage.setItem('local', JSON.stringify(data))
|
||||
state.folders = folders
|
||||
state.articles = articles
|
||||
}
|
||||
return state
|
||||
default:
|
||||
if (state == null) return getInitialArticles()
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
currentUser,
|
||||
status,
|
||||
articles
|
||||
folders,
|
||||
articles,
|
||||
status
|
||||
})
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"nib": "^1.1.0",
|
||||
"react": "^0.14.0",
|
||||
"react-dom": "^0.14.0",
|
||||
"react-redux": "^3.1.0",
|
||||
"react-redux": "^4.0.0",
|
||||
"react-router": "^1.0.0-rc1",
|
||||
"react-select": "^0.8.1",
|
||||
"react-transform-catch-errors": "^1.0.0",
|
||||
|
||||
Reference in New Issue
Block a user