diff --git a/browser/main/HomePage.js b/browser/main/HomePage.js index eb8f3f84..ffd3b790 100644 --- a/browser/main/HomePage.js +++ b/browser/main/HomePage.js @@ -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 = { diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index 0f2f2682..9a33af2a 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -101,8 +101,8 @@ export default class ArticleDetail extends React.Component {
{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')}
{tags}
@@ -183,7 +183,6 @@ export default class ArticleDetail extends React.Component { ) }) - console.log('edit rendered') return (
diff --git a/browser/main/HomePage/ArticleNavigator.js b/browser/main/HomePage/ArticleNavigator.js index a3a022ba..5db3b832 100644 --- a/browser/main/HomePage/ArticleNavigator.js +++ b/browser/main/HomePage/ArticleNavigator.js @@ -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 (
-
{process.env.USER}
+
{userName}
local
diff --git a/browser/main/HomePage/ArticleTopBar.js b/browser/main/HomePage/ArticleTopBar.js index cf612a24..317d7e4a 100644 --- a/browser/main/HomePage/ArticleTopBar.js +++ b/browser/main/HomePage/ArticleTopBar.js @@ -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 {
- this.handleSearchChange(e)} placeholder='Search' type='text'/> + this.handleSearchChange(e)} placeholder='Search' type='text'/> + { + this.props.status.search != null && this.props.status.search.length > 0 + ? + : null + }
diff --git a/browser/main/index.js b/browser/main/index.js index 95304268..32a4f106 100644 --- a/browser/main/index.js +++ b/browser/main/index.js @@ -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 = ( @@ -22,7 +16,6 @@ let routes = ( ) let el = document.getElementById('content') - ReactDOM.render((
diff --git a/browser/styles/main/HomeContainer/components/ArticleDetail.styl b/browser/styles/main/HomeContainer/components/ArticleDetail.styl index 88cf5e5f..39e4f10b 100644 --- a/browser/styles/main/HomeContainer/components/ArticleDetail.styl +++ b/browser/styles/main/HomeContainer/components/ArticleDetail.styl @@ -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 diff --git a/browser/styles/main/HomeContainer/components/ArticleNavigator.styl b/browser/styles/main/HomeContainer/components/ArticleNavigator.styl index fbabb65d..fa8b1fed 100644 --- a/browser/styles/main/HomeContainer/components/ArticleNavigator.styl +++ b/browser/styles/main/HomeContainer/components/ArticleNavigator.styl @@ -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 diff --git a/browser/styles/main/HomeContainer/components/ArticleTopBar.styl b/browser/styles/main/HomeContainer/components/ArticleTopBar.styl index a37f43d0..7369f21d 100644 --- a/browser/styles/main/HomeContainer/components/ArticleTopBar.styl +++ b/browser/styles/main/HomeContainer/components/ArticleTopBar.styl @@ -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 diff --git a/lib/actions.js b/lib/actions.js index d9827572..ac0a2059 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -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, diff --git a/lib/components/FolderMark.js b/lib/components/FolderMark.js index c1593b1d..ef258bb1 100644 --- a/lib/components/FolderMark.js +++ b/lib/components/FolderMark.js @@ -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 } } diff --git a/lib/components/TagSelect.js b/lib/components/TagSelect.js index 862d105d..91a2ed63 100644 --- a/lib/components/TagSelect.js +++ b/lib/components/TagSelect.js @@ -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'/>
) diff --git a/lib/components/modal/CreateNewFolder.js b/lib/components/modal/CreateNewFolder.js index 7b07ea05..e50c79ca 100644 --- a/lib/components/modal/CreateNewFolder.js +++ b/lib/components/modal/CreateNewFolder.js @@ -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)) diff --git a/lib/components/modal/Preference/FolderRow.js b/lib/components/modal/Preference/FolderRow.js index 0c9355d2..c4eec716 100644 --- a/lib/components/modal/Preference/FolderRow.js +++ b/lib/components/modal/Preference/FolderRow.js @@ -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 {
-
- -
@@ -102,8 +86,7 @@ export default class FolderRow extends React.Component { default: return (
-
{folder.name}
-
{folder.public ? 'Public' : 'Private'}
+
{folder.name}
diff --git a/lib/components/modal/Preference/FolderSettingTab.js b/lib/components/modal/Preference/FolderSettingTab.js index 83f272b6..097a641b 100644 --- a/lib/components/modal/Preference/FolderSettingTab.js +++ b/lib/components/modal/Preference/FolderSettingTab.js @@ -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 ( - ) - }) + 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 ( - + ) }) return (
-
- Setting of - -
-
Folders
+
Manage folder
Folder name
-
Public/Private
Edit/Delete
{folderElements} @@ -89,12 +46,6 @@ export default class FolderSettingTab extends React.Component {
-
- -
@@ -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 diff --git a/lib/components/modal/Preferences.js b/lib/components/modal/Preferences.js index bd09681f..edda1b0b 100644 --- a/lib/components/modal/Preferences.js +++ b/lib/components/modal/Preferences.js @@ -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 () + case FOLDER: + return ( + + ) case APP: default: return () } } - 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 ( -
-
-
User Info
-
- - -
-
- - -
-
- + // renderProfile () { + // let profileState = this.state.profile + // return ( + //
+ //
+ //
User Info
+ //
+ // + // + //
+ //
+ // + // + //
+ //
+ // - {this.state.profile.userInfo.alert != null - ? ( -
{profileState.userInfo.alert.message}
- ) - : null} -
-
+ // {this.state.profile.userInfo.alert != null + // ? ( + //
{profileState.userInfo.alert.message}
+ // ) + // : null} + //
+ //
-
-
Password
-
- - -
-
- - -
-
- - -
-
- + //
+ //
Password
+ //
+ // + // + //
+ //
+ // + // + //
+ //
+ // + // + //
+ //
+ // - {profileState.password.alert != null - ? ( -
{profileState.password.alert.message}
- ) - : null} -
-
-
- ) - } + // {profileState.password.alert != null + // ? ( + //
{profileState.password.alert.message}
+ // ) + // : null} + //
+ //
+ //
+ // ) + // } } 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 } } diff --git a/lib/dataStore.js b/lib/dataStore.js new file mode 100644 index 00000000..ca3fc58c --- /dev/null +++ b/lib/dataStore.js @@ -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 + } +})() diff --git a/lib/reducer.js b/lib/reducer.js index 36b4136d..a51d48a8 100644 --- a/lib/reducer.js +++ b/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 }) diff --git a/package.json b/package.json index 2406d898..7d862a9c 100644 --- a/package.json +++ b/package.json @@ -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",