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",