mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
add Folder
This commit is contained in:
@@ -3,6 +3,7 @@ const fs = require('fs')
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const CSON = require('season')
|
const CSON = require('season')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
|
const consts = require('browser/lib/consts')
|
||||||
|
|
||||||
let repositories = []
|
let repositories = []
|
||||||
|
|
||||||
@@ -19,10 +20,10 @@ let repositories = []
|
|||||||
* .then(() => repo.load())
|
* .then(() => repo.load())
|
||||||
*
|
*
|
||||||
* // Update Cached
|
* // Update Cached
|
||||||
* repo.updateCache({name: 'renamed'})
|
* repo.saveCache({name: 'renamed'})
|
||||||
*
|
*
|
||||||
* // Update JSON
|
* // Update JSON
|
||||||
* repo.updateJSON({author: 'Other user'})
|
* repo.saveJSON({author: 'Other user'})
|
||||||
*
|
*
|
||||||
* // Remove repository
|
* // Remove repository
|
||||||
* repo.unmount()
|
* repo.unmount()
|
||||||
@@ -142,6 +143,7 @@ class Repository {
|
|||||||
return Promise.all([resolveDataDirectory, resolveBoostrepoJSON])
|
return Promise.all([resolveDataDirectory, resolveBoostrepoJSON])
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.json = data[1]
|
this.json = data[1]
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +169,7 @@ class Repository {
|
|||||||
return Promise.all(notes)
|
return Promise.all(notes)
|
||||||
.then((notes) => {
|
.then((notes) => {
|
||||||
this.notes = notes
|
this.notes = notes
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,10 +181,11 @@ class Repository {
|
|||||||
|
|
||||||
return this.getData()
|
return this.getData()
|
||||||
})
|
})
|
||||||
.catch(function handleError (err) {
|
.catch((err) => {
|
||||||
this.status = 'ERROR'
|
this.status = 'ERROR'
|
||||||
this.error = err
|
this.error = err
|
||||||
return err
|
console.error(err)
|
||||||
|
return this
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,14 +257,23 @@ class Repository {
|
|||||||
* @return {Promise} all data of a repository
|
* @return {Promise} all data of a repository
|
||||||
*/
|
*/
|
||||||
getData () {
|
getData () {
|
||||||
if (this.status !== 'READY') {
|
function carbonCopy (obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj))
|
||||||
|
}
|
||||||
|
if (this.status === 'IDLE') {
|
||||||
return this.load()
|
return this.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(Object.assign({}, this.json, this.cached, {
|
if (this.status === 'ERROR') {
|
||||||
|
return Promise.resolve(carbonCopy(Object.assign({}, this.json, this.cached, {
|
||||||
|
status: this.status
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(carbonCopy(Object.assign({}, this.json, this.cached, {
|
||||||
status: this.status,
|
status: this.status,
|
||||||
notes: this.notes
|
notes: this.notes
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,11 +300,13 @@ class Repository {
|
|||||||
*/
|
*/
|
||||||
saveJSON (newJSON) {
|
saveJSON (newJSON) {
|
||||||
let jsonPath = path.join(this.cached.path, 'boostrepo.json')
|
let jsonPath = path.join(this.cached.path, 'boostrepo.json')
|
||||||
Object.assign(this.json, newJSON)
|
if (_.isObject(newJSON)) {
|
||||||
|
Object.assign(this.json, newJSON)
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
CSON
|
CSON
|
||||||
.writeFile(jsonPath, this.json, function (err) {
|
.writeFile(jsonPath, this.json, (err) => {
|
||||||
if (err != null) return reject(err)
|
if (err != null) return reject(err)
|
||||||
resolve(this.json)
|
resolve(this.json)
|
||||||
})
|
})
|
||||||
@@ -316,9 +331,9 @@ class Repository {
|
|||||||
|
|
||||||
newFolder = _.pick(newFolder, ['color', 'name'])
|
newFolder = _.pick(newFolder, ['color', 'name'])
|
||||||
if (!_.isString(newFolder.name) || newFolder.name.trim().length === 0) newFolder.name = 'unnamed'
|
if (!_.isString(newFolder.name) || newFolder.name.trim().length === 0) newFolder.name = 'unnamed'
|
||||||
else newFolder.name = newFolder.trim()
|
else newFolder.name = newFolder.name.trim()
|
||||||
|
|
||||||
if (!_.isString(newFolder.color)) newFolder.color = ''
|
if (!_.isString(newFolder.color)) newFolder.color = this.constructor.randomColor()
|
||||||
|
|
||||||
newFolder.key = keygen()
|
newFolder.key = keygen()
|
||||||
while (_.findIndex(folders, {key: newFolder.key}) > -1) {
|
while (_.findIndex(folders, {key: newFolder.key}) > -1) {
|
||||||
@@ -327,7 +342,7 @@ class Repository {
|
|||||||
|
|
||||||
folders.push(newFolder)
|
folders.push(newFolder)
|
||||||
|
|
||||||
return this.updateJSON(this.json)
|
return this.saveJSON(this.json)
|
||||||
.then(() => newFolder)
|
.then(() => newFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,6 +466,10 @@ class Repository {
|
|||||||
return Promise.resolve(this.notes)
|
return Promise.resolve(this.notes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static Methods
|
||||||
|
*/
|
||||||
|
|
||||||
static generateDefaultJSON (override) {
|
static generateDefaultJSON (override) {
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
name: 'unnamed',
|
name: 'unnamed',
|
||||||
@@ -458,7 +477,7 @@ class Repository {
|
|||||||
folders: [{
|
folders: [{
|
||||||
key: keygen(),
|
key: keygen(),
|
||||||
name: 'general',
|
name: 'general',
|
||||||
color: 'green'
|
color: this.randomColor()
|
||||||
}]
|
}]
|
||||||
}, override)
|
}, override)
|
||||||
}
|
}
|
||||||
@@ -474,21 +493,28 @@ class Repository {
|
|||||||
* @return {Promise} resolving parsed data
|
* @return {Promise} resolving parsed data
|
||||||
*/
|
*/
|
||||||
static resolveJSON (targetPath, defaultOverrides) {
|
static resolveJSON (targetPath, defaultOverrides) {
|
||||||
return new Promise(function checkIfExists (resolve, reject) {
|
return (new Promise((resolve, reject) => {
|
||||||
// If JSON doesn't exist, make a new one.
|
let writeNew = () => {
|
||||||
if (CSON.resolve(targetPath) == null) {
|
let newRepoJSON = this.generateDefaultJSON(defaultOverrides)
|
||||||
let newRepoJSON = this.constructor.generateDefaultJSON(defaultOverrides)
|
CSON.writeFile(targetPath, newRepoJSON, (err) => {
|
||||||
CSON.writeFile(targetPath, newRepoJSON, function (err) {
|
|
||||||
if (err != null) return reject(err)
|
if (err != null) return reject(err)
|
||||||
resolve(newRepoJSON)
|
resolve(newRepoJSON)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
// If JSON doesn't exist, make a new one.
|
||||||
|
if (CSON.resolve(targetPath) == null) {
|
||||||
|
writeNew()
|
||||||
} else {
|
} else {
|
||||||
CSON.readFile(targetPath, function (err, obj) {
|
CSON.readFile(targetPath, (err, obj) => {
|
||||||
if (err != null) return reject(err)
|
if (err != null) return reject(err)
|
||||||
resolve(obj)
|
if (obj == null) {
|
||||||
|
writeNew()
|
||||||
|
} else {
|
||||||
|
resolve(obj)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -553,7 +579,6 @@ class Repository {
|
|||||||
throw new Error('Data is corrupted. it must be an array.')
|
throw new Error('Data is corrupted. it must be an array.')
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
|
||||||
data = []
|
data = []
|
||||||
this.saveAllCached(data)
|
this.saveAllCached(data)
|
||||||
}
|
}
|
||||||
@@ -582,6 +607,10 @@ class Repository {
|
|||||||
let repository = _.find(repositories, {cached: {key: repoKey}})
|
let repository = _.find(repositories, {cached: {key: repoKey}})
|
||||||
return Promise.resolve(repository)
|
return Promise.resolve(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static randomColor () {
|
||||||
|
return consts.FOLDER_COLORS[Math.floor(Math.random() * consts.FOLDER_COLORS.length)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Repository
|
export default Repository
|
||||||
|
|||||||
14
browser/lib/consts.js
Normal file
14
browser/lib/consts.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const consts = {
|
||||||
|
FOLDER_COLORS: [
|
||||||
|
'#3460C7',
|
||||||
|
'#2BA5F7',
|
||||||
|
'#FF8E00',
|
||||||
|
'#E8D252',
|
||||||
|
'#3FD941',
|
||||||
|
'#1FAD85',
|
||||||
|
'#E10051',
|
||||||
|
'#B013A4'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = consts
|
||||||
@@ -9,10 +9,28 @@ const Menu = remote.Menu
|
|||||||
const MenuItem = remote.MenuItem
|
const MenuItem = remote.MenuItem
|
||||||
|
|
||||||
class RepositorySection extends React.Component {
|
class RepositorySection extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isOpen: true,
|
||||||
|
isCreatingFolder: false,
|
||||||
|
isSaving: false,
|
||||||
|
newFolder: {
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRepository () {
|
||||||
|
let { repository } = this.props
|
||||||
|
return Repository.find(repository.key)
|
||||||
|
}
|
||||||
|
|
||||||
handleUnlinkButtonClick () {
|
handleUnlinkButtonClick () {
|
||||||
let { dispatch, repository } = this.props
|
let { dispatch, repository } = this.props
|
||||||
|
|
||||||
Repository.find(repository.key)
|
this.getRepository()
|
||||||
.then((repositoryInstance) => {
|
.then((repositoryInstance) => {
|
||||||
return repositoryInstance.unmount()
|
return repositoryInstance.unmount()
|
||||||
})
|
})
|
||||||
@@ -25,16 +43,16 @@ class RepositorySection extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleToggleButtonClick (e) {
|
handleToggleButtonClick (e) {
|
||||||
|
this.setState({
|
||||||
|
isOpen: !this.state.isOpen
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContextButtonClick (e) {
|
handleContextButtonClick (e) {
|
||||||
var menu = new Menu()
|
var menu = new Menu()
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'New Note'
|
label: 'New Folder',
|
||||||
}))
|
click: () => this.handleNewFolderButtonClick()
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'New Folder'
|
|
||||||
}))
|
}))
|
||||||
menu.append(new MenuItem({ type: 'separator' }))
|
menu.append(new MenuItem({ type: 'separator' }))
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
@@ -45,17 +63,62 @@ class RepositorySection extends React.Component {
|
|||||||
menu.popup(remote.getCurrentWindow())
|
menu.popup(remote.getCurrentWindow())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNewFolderButtonClick (e) {
|
||||||
|
this.setState({
|
||||||
|
isCreatingFolder: true,
|
||||||
|
newFolder: {
|
||||||
|
name: 'New Folder'
|
||||||
|
}
|
||||||
|
}, () => {
|
||||||
|
this.refs.nameInput.select()
|
||||||
|
this.refs.nameInput.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNewFolderFormChange (e) {
|
||||||
|
let newFolder = this.state.newFolder
|
||||||
|
newFolder.name = this.refs.nameInput.value
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
newFolder
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNameInputBlur (e) {
|
||||||
|
let { dispatch, repository } = this.props
|
||||||
|
|
||||||
|
this.getRepository()
|
||||||
|
.then((repositoryInstance) => {
|
||||||
|
return repositoryInstance.addFolder({
|
||||||
|
name: this.state.newFolder.name
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((folder) => {
|
||||||
|
console.log(folder)
|
||||||
|
dispatch({
|
||||||
|
type: 'ADD_FOLDER',
|
||||||
|
key: repository.key,
|
||||||
|
folder: folder
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isCreatingFolder: false,
|
||||||
|
isSaving: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { repository } = this.props
|
let { repository } = this.props
|
||||||
|
|
||||||
let folderElements = repository.folders.map((folder) => {
|
let folderElements = repository.folders.map((folder) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={folder.name}
|
key={folder.key}
|
||||||
styleName='folder'
|
styleName='folder'
|
||||||
>
|
>
|
||||||
<div styleName='folder-label'>
|
<div styleName='folder-label'>
|
||||||
<i className='fa fa-cube'/> {folder.name}
|
<i className='fa fa-cube' style={{color: folder.color}}/> {folder.name}
|
||||||
</div>
|
</div>
|
||||||
<div styleName='folder-control'>
|
<div styleName='folder-control'>
|
||||||
<button styleName='folder-control-button'>
|
<button styleName='folder-control-button'>
|
||||||
@@ -66,6 +129,10 @@ class RepositorySection extends React.Component {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let toggleButtonIconClassName = this.state.isOpen
|
||||||
|
? 'fa fa-minus'
|
||||||
|
: 'fa fa-plus'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='RepositorySection'
|
className='RepositorySection'
|
||||||
@@ -85,17 +152,29 @@ class RepositorySection extends React.Component {
|
|||||||
<button styleName='header-control-button'
|
<button styleName='header-control-button'
|
||||||
onClick={(e) => this.handleToggleButtonClick(e)}
|
onClick={(e) => this.handleToggleButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-angle-down'/>
|
<i className={toggleButtonIconClassName}/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{this.state.isOpen && <div>
|
||||||
|
{folderElements}
|
||||||
|
|
||||||
{folderElements}
|
{this.state.isCreatingFolder
|
||||||
|
? <div styleName='newFolderForm'>
|
||||||
<button styleName='newFolderButton'
|
<input styleName='newFolderForm-nameInput'
|
||||||
>
|
ref='nameInput'
|
||||||
<i className='fa fa-plus'/> New Folder
|
value={this.state.newFolder.name}
|
||||||
</button>
|
onChange={(e) => this.handleNewFolderFormChange(e)}
|
||||||
|
onBlur={(e) => this.handleNameInputBlur(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
: <button styleName='newFolderButton'
|
||||||
|
onClick={(e) => this.handleNewFolderButtonClick(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-plus'/> New Folder
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,21 @@
|
|||||||
color white
|
color white
|
||||||
background-color $brand-color
|
background-color $brand-color
|
||||||
|
|
||||||
|
.newFolderForm
|
||||||
|
width 100%
|
||||||
|
padding 0 15px
|
||||||
|
height 33px
|
||||||
|
|
||||||
|
.newFolderForm-nameInput
|
||||||
|
width 100%
|
||||||
|
height 33px
|
||||||
|
padding 0 10px
|
||||||
|
border-radius 5px
|
||||||
|
border solid 1px $nav-border-color
|
||||||
|
outline none
|
||||||
|
&:focus
|
||||||
|
border-color $focus-border-color
|
||||||
|
|
||||||
.newFolderButton
|
.newFolderButton
|
||||||
height 33px
|
height 33px
|
||||||
width 100%
|
width 100%
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './SideNav.styl'
|
import styles from './SideNav.styl'
|
||||||
import { openModal, isModalOpen } from 'browser/lib/modal'
|
import { openModal } from 'browser/lib/modal'
|
||||||
import Preferences from '../../modal/Preferences'
|
import Preferences from '../../modal/Preferences'
|
||||||
import CreateNewFolder from '../../modal/CreateNewFolder'
|
|
||||||
import RepositorySection from './RepositorySection'
|
import RepositorySection from './RepositorySection'
|
||||||
import NewRepositoryModal from '../../modal/NewRepositoryModal'
|
import NewRepositoryModal from '../../modal/NewRepositoryModal'
|
||||||
|
|
||||||
|
|||||||
@@ -35,16 +35,36 @@ function repositories (state = initialRepositories, action) {
|
|||||||
case 'ADD_REPOSITORY':
|
case 'ADD_REPOSITORY':
|
||||||
{
|
{
|
||||||
let repos = state.slice()
|
let repos = state.slice()
|
||||||
|
|
||||||
repos.push(action.repository)
|
repos.push(action.repository)
|
||||||
|
|
||||||
return repos
|
return repos
|
||||||
}
|
}
|
||||||
case 'REMOVE_REPOSITORY':
|
case 'REMOVE_REPOSITORY':
|
||||||
{
|
{
|
||||||
let repos = state.slice()
|
let repos = state.slice()
|
||||||
|
|
||||||
let targetIndex = _.findIndex(repos, {key: action.key})
|
let targetIndex = _.findIndex(repos, {key: action.key})
|
||||||
if (targetIndex > -1) {
|
if (targetIndex > -1) {
|
||||||
repos.splice(targetIndex, 1)
|
repos.splice(targetIndex, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return repos
|
||||||
|
}
|
||||||
|
case 'ADD_FOLDER':
|
||||||
|
{
|
||||||
|
let repos = state.slice()
|
||||||
|
let targetRepo = _.find(repos, {key: action.key})
|
||||||
|
|
||||||
|
if (targetRepo == null) return state
|
||||||
|
|
||||||
|
let targetFolderIndex = _.findIndex(targetRepo.folders, {key: action.folder.key})
|
||||||
|
if (targetFolderIndex < 0) {
|
||||||
|
targetRepo.folders.push(action.folder)
|
||||||
|
} else {
|
||||||
|
targetRepo.folders.splice(targetFolderIndex, 1, action.folder)
|
||||||
|
}
|
||||||
|
|
||||||
return repos
|
return repos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ $danger-lighten-color = #FFE5E6
|
|||||||
*/
|
*/
|
||||||
$border-color = #D0D0D0
|
$border-color = #D0D0D0
|
||||||
$active-border-color = #369DCD
|
$active-border-color = #369DCD
|
||||||
|
$focus-border-color = #369DCD
|
||||||
|
|
||||||
$default-border = solid 1px $border-color
|
$default-border = solid 1px $border-color
|
||||||
$active-border = solid 1px $active-border-color
|
$active-border = solid 1px $active-border-color
|
||||||
|
|||||||
Reference in New Issue
Block a user