1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 17:56:25 +00:00

delete note

This commit is contained in:
Dick Choi
2016-07-23 15:28:17 +09:00
parent b2d34ab95d
commit 45b1cd3942
17 changed files with 498 additions and 329 deletions

View File

@@ -23,7 +23,6 @@ export default class CodeEditor extends React.Component {
} }
el = el.parentNode el = el.parentNode
} }
console.log(isStillFocused)
if (!isStillFocused && this.props.onBlur != null) this.props.onBlur(e) if (!isStillFocused && this.props.onBlur != null) this.props.onBlur(e)
} }
@@ -165,7 +164,7 @@ export default class CodeEditor extends React.Component {
let syntaxMode = mode != null let syntaxMode = mode != null
? mode.mode ? mode.mode
: 'text' : 'text'
session.setMode('ace/mode' + syntaxMode) session.setMode('ace/mode/' + syntaxMode)
} }
if (prevProps.theme !== this.props.theme) { if (prevProps.theme !== this.props.theme) {
editor.setTheme('ace/theme/' + this.props.theme) editor.setTheme('ace/theme/' + this.props.theme)

View File

@@ -1,144 +0,0 @@
import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom'
import ModeIcon from 'browser/components/ModeIcon'
import moment from 'moment'
import FolderMark from 'browser/components/FolderMark'
import _ from 'lodash'
const electron = require('electron')
const remote = electron.remote
const ipc = electron.ipcRenderer
export default class ArticleList extends React.Component {
constructor (props) {
super(props)
this.focusHandler = (e) => this.focus()
}
componentDidMount () {
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
ipc.on('list-focus', this.focusHandler)
this.focus()
}
componentWillUnmount () {
clearInterval(this.refreshTimer)
ipc.removeListener('list-focus', this.focusHandler)
}
componentDidUpdate () {
return false
var index = articles.indexOf(null)
var el = ReactDOM.findDOMNode(this)
var li = el.querySelectorAll('.ArticleList>div')[index]
if (li == null) {
return
}
var overflowBelow = el.clientHeight + el.scrollTop < li.offsetTop + li.clientHeight
if (overflowBelow) {
el.scrollTop = li.offsetTop + li.clientHeight - el.clientHeight
}
var overflowAbove = el.scrollTop > li.offsetTop
if (overflowAbove) {
el.scrollTop = li.offsetTop
}
}
focus () {
ReactDOM.findDOMNode(this).focus()
}
// 移動ができなかったらfalseを返す:
selectPriorArticle () {
let { articles, activeArticle, dispatch } = this.props
let targetIndex = articles.indexOf(activeArticle) - 1
let targetArticle = articles[targetIndex]
if (targetArticle != null) {
dispatch(switchArticle(targetArticle.key))
return true
}
return false
}
selectNextArticle () {
let { articles, activeArticle, dispatch } = this.props
let targetIndex = articles.indexOf(activeArticle) + 1
let targetArticle = articles[targetIndex]
if (targetArticle != null) {
dispatch(switchArticle(targetArticle.key))
return true
}
return false
}
handleArticleClick (article) {
let { dispatch } = this.props
return function (e) {
dispatch(switchArticle(article.key))
}
}
handleArticleListKeyDown (e) {
if (e.metaKey || e.ctrlKey) return true
if (e.keyCode === 65 && !e.shiftKey) {
e.preventDefault()
remote.getCurrentWebContents().send('top-new-post')
}
if (e.keyCode === 65 && e.shiftKey) {
e.preventDefault()
remote.getCurrentWebContents().send('nav-new-folder')
}
if (e.keyCode === 68) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-delete')
}
if (e.keyCode === 84) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-title')
}
if (e.keyCode === 69) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-edit')
}
if (e.keyCode === 83) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-save')
}
if (e.keyCode === 38) {
e.preventDefault()
this.selectPriorArticle()
}
if (e.keyCode === 40) {
e.preventDefault()
this.selectNextArticle()
}
}
render () {
let articleElements = []
return (
<div tabIndex='3' onKeyDown={(e) => this.handleArticleListKeyDown(e)} className='ArticleList'>
{articleElements}
</div>
)
}
}
ArticleList.propTypes = {
dispatch: PropTypes.func,
repositories: PropTypes.array
}

View File

@@ -5,9 +5,9 @@ import MarkdownEditor from 'browser/components/MarkdownEditor'
import StarButton from './StarButton' import StarButton from './StarButton'
import TagSelect from './TagSelect' import TagSelect from './TagSelect'
import FolderSelect from './FolderSelect' import FolderSelect from './FolderSelect'
import Commander from 'browser/main/lib/Commander'
import dataApi from 'browser/main/lib/dataApi' import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router' import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
const electron = require('electron') const electron = require('electron')
const { remote } = electron const { remote } = electron
@@ -22,31 +22,22 @@ class MarkdownNoteDetail extends React.Component {
note: Object.assign({ note: Object.assign({
title: '', title: '',
content: '', content: '',
isMovingNote: false isMovingNote: false,
isDeleting: false
}, props.note) }, props.note)
} }
this.dispatchTimer = null this.dispatchTimer = null
} }
componentDidMount () { focus () {
Commander.bind('note-detail', this) this.refs.content.focus()
}
componentWillUnmount () {
Commander.release(this)
}
fire (command) {
switch (command) {
case 'focus':
this.refs.content.focus()
}
} }
componentWillReceiveProps (nextProps) { componentWillReceiveProps (nextProps) {
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) { if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
this.setState({ this.setState({
note: Object.assign({}, nextProps.note) note: Object.assign({}, nextProps.note),
isDeleting: false
}, () => { }, () => {
this.refs.content.reload() this.refs.content.reload()
this.refs.tags.reset() this.refs.tags.reset()
@@ -176,11 +167,39 @@ class MarkdownNoteDetail extends React.Component {
let menu = new Menu() let menu = new Menu()
menu.append(new MenuItem({ menu.append(new MenuItem({
label: 'Delete', label: 'Delete',
click: (e) => this.handlePreferencesButtonClick(e) click: (e) => this.handleDeleteMenuClick(e)
})) }))
menu.popup(remote.getCurrentWindow()) menu.popup(remote.getCurrentWindow())
} }
handleDeleteMenuClick (e) {
this.setState({
isDeleting: true
})
}
handleDeleteConfirmButtonClick (e) {
let { note, dispatch } = this.props
dataApi
.removeNote(note.storage, note.folder, note.key)
.then(() => {
let dispatchHandler = () => {
dispatch({
type: 'REMOVE_NOTE',
note: note
})
}
ee.once('list:moved', dispatchHandler)
ee.emit('list:next')
})
}
handleDeleteCancelButtonClick (e) {
this.setState({
isDeleting: false
})
}
render () { render () {
let { storages, config } = this.props let { storages, config } = this.props
let { note } = this.state let { note } = this.state
@@ -190,43 +209,58 @@ class MarkdownNoteDetail extends React.Component {
style={this.props.style} style={this.props.style}
styleName='root' styleName='root'
> >
<div styleName='info'> {this.state.isDeleting
<div styleName='info-left'> ? <div styleName='info'>
<div styleName='info-delete'>
<div styleName='info-left-top'> <span styleName='info-delete-message'>
<FolderSelect styleName='info-left-top-folderSelect' Are you sure to delete this note?
value={this.state.note.storage + '-' + this.state.note.folder} </span>
ref='folder' <button styleName='info-delete-cancelButton'
storages={storages} onClick={(e) => this.handleDeleteCancelButtonClick(e)}
onChange={(e) => this.handleFolderChange(e)} >Cancel</button>
/> <button styleName='info-delete-confirmButton'
</div> onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
<div styleName='info-left-bottom'> >Confirm</button>
<TagSelect
styleName='info-left-bottom-tagSelect'
ref='tags'
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
</div> </div>
</div> </div>
<div styleName='info-right'> : <div styleName='info'>
<StarButton styleName='info-right-button' <div styleName='info-left'>
onClick={(e) => this.handleStarButtonClick(e)} <div styleName='info-left-top'>
isActive={note.isStarred} <FolderSelect styleName='info-left-top-folderSelect'
/> value={this.state.note.storage + '-' + this.state.note.folder}
<button styleName='info-right-button' ref='folder'
onClick={(e) => this.handleShareButtonClick(e)} storages={storages}
> onChange={(e) => this.handleFolderChange(e)}
<i className='fa fa-share-alt fa-fw'/> />
</button> </div>
<button styleName='info-right-button' <div styleName='info-left-bottom'>
onClick={(e) => this.handleContextButtonClick(e)} <TagSelect
> styleName='info-left-bottom-tagSelect'
<i className='fa fa-ellipsis-v'/> ref='tags'
</button> value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
</div>
</div>
<div styleName='info-right'>
<StarButton styleName='info-right-button'
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
<button styleName='info-right-button'
onClick={(e) => this.handleShareButtonClick(e)}
>
<i className='fa fa-share-alt fa-fw'/>
</button>
<button styleName='info-right-button'
onClick={(e) => this.handleContextButtonClick(e)}
>
<i className='fa fa-ellipsis-v'/>
</button>
</div>
</div> </div>
</div> }
<div styleName='body'> <div styleName='body'>
<MarkdownEditor <MarkdownEditor
ref='content' ref='content'

View File

@@ -12,6 +12,36 @@ $info-height = 75px
border-bottom $ui-border border-bottom $ui-border
background-color $ui-backgroundColor background-color $ui-backgroundColor
.info-delete
height 80px
clearfix()
.info-delete-message
height 80px
line-height 80px
padding 0 25px
float left
.info-delete-confirmButton
float right
margin 25px 5px 0
height 30px
padding 0 25px
border-radius 2px
border none
color $ui-text-color
colorDangerButton()
.info-delete-cancelButton
float right
height 30px
margin 25px 5px 0
padding 0 25px
border $ui-border
border-radius 2px
color $ui-text-color
colorDefaultButton()
.info-left .info-left
float left float left
padding 0 5px padding 0 5px

View File

@@ -5,10 +5,10 @@ import CodeEditor from 'browser/components/CodeEditor'
import StarButton from './StarButton' import StarButton from './StarButton'
import TagSelect from './TagSelect' import TagSelect from './TagSelect'
import FolderSelect from './FolderSelect' import FolderSelect from './FolderSelect'
import Commander from 'browser/main/lib/Commander'
import dataApi from 'browser/main/lib/dataApi' import dataApi from 'browser/main/lib/dataApi'
import modes from 'browser/lib/modes' import modes from 'browser/lib/modes'
import { hashHistory } from 'react-router' import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
const electron = require('electron') const electron = require('electron')
const { remote } = electron const { remote } = electron
@@ -25,34 +25,26 @@ class SnippetNoteDetail extends React.Component {
description: '' description: ''
}, props.note, { }, props.note, {
snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet)) snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet))
}) }),
isDeleting: false
} }
} }
componentDidMount () { focus () {
Commander.bind('note-detail', this) this.refs.description.focus()
}
componentWillUnmount () {
Commander.release(this)
}
fire (command) {
switch (command) {
case 'focus':
this.refs.description.focus()
}
} }
componentWillReceiveProps (nextProps) { componentWillReceiveProps (nextProps) {
if (nextProps.note.key !== this.props.note.key) { if (nextProps.note.key !== this.props.note.key) {
let nextNote = Object.assign({
description: ''
}, nextProps.note, {
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
})
this.setState({ this.setState({
snippetIndex: 0, snippetIndex: 0,
note: Object.assign({ note: nextNote,
description: '' isDeleting: false
}, nextProps.note, {
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
})
}, () => { }, () => {
let { snippets } = this.state.note let { snippets } = this.state.note
snippets.forEach((snippet, index) => { snippets.forEach((snippet, index) => {
@@ -171,6 +163,7 @@ class SnippetNoteDetail extends React.Component {
let menu = new Menu() let menu = new Menu()
menu.append(new MenuItem({ menu.append(new MenuItem({
label: 'Export as a File', label: 'Export as a File',
disabled: true,
click: (e) => this.handlePreferencesButtonClick(e) click: (e) => this.handlePreferencesButtonClick(e)
})) }))
menu.append(new MenuItem({ menu.append(new MenuItem({
@@ -185,11 +178,39 @@ class SnippetNoteDetail extends React.Component {
let menu = new Menu() let menu = new Menu()
menu.append(new MenuItem({ menu.append(new MenuItem({
label: 'Delete', label: 'Delete',
click: (e) => this.handlePreferencesButtonClick(e) click: (e) => this.handleDeleteMenuClick(e)
})) }))
menu.popup(remote.getCurrentWindow()) menu.popup(remote.getCurrentWindow())
} }
handleDeleteMenuClick (e) {
this.setState({
isDeleting: true
})
}
handleDeleteConfirmButtonClick (e) {
let { note, dispatch } = this.props
dataApi
.removeNote(note.storage, note.folder, note.key)
.then(() => {
let dispatchHandler = () => {
dispatch({
type: 'REMOVE_NOTE',
note: note
})
}
ee.once('list:moved', dispatchHandler)
ee.emit('list:next')
})
}
handleDeleteCancelButtonClick (e) {
this.setState({
isDeleting: false
})
}
handleTabPlusButtonClick (e) { handleTabPlusButtonClick (e) {
let { note } = this.state let { note } = this.state
@@ -360,43 +381,59 @@ class SnippetNoteDetail extends React.Component {
style={this.props.style} style={this.props.style}
styleName='root' styleName='root'
> >
<div styleName='info'> {this.state.isDeleting
<div styleName='info-left'> ? <div styleName='info'>
<div styleName='info-delete'>
<div styleName='info-left-top'> <span styleName='info-delete-message'>
<FolderSelect styleName='info-left-top-folderSelect' Are you sure to delete this note?
value={this.state.note.storage + '-' + this.state.note.folder} </span>
ref='folder' <button styleName='info-delete-cancelButton'
storages={storages} onClick={(e) => this.handleDeleteCancelButtonClick(e)}
onChange={(e) => this.handleFolderChange(e)} >Cancel</button>
/> <button styleName='info-delete-confirmButton'
</div> onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
<div styleName='info-left-bottom'> >Confirm</button>
<TagSelect
styleName='info-left-bottom-tagSelect'
ref='tags'
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
</div> </div>
</div> </div>
<div styleName='info-right'> : <div styleName='info'>
<StarButton styleName='info-right-button' <div styleName='info-left'>
onClick={(e) => this.handleStarButtonClick(e)} <div styleName='info-left-top'>
isActive={note.isStarred} <FolderSelect styleName='info-left-top-folderSelect'
/> value={this.state.note.storage + '-' + this.state.note.folder}
<button styleName='info-right-button' ref='folder'
onClick={(e) => this.handleShareButtonClick(e)} storages={storages}
> onChange={(e) => this.handleFolderChange(e)}
<i className='fa fa-share-alt fa-fw'/> />
</button> </div>
<button styleName='info-right-button' <div styleName='info-left-bottom'>
onClick={(e) => this.handleContextButtonClick(e)} <TagSelect
> styleName='info-left-bottom-tagSelect'
<i className='fa fa-ellipsis-v'/> ref='tags'
</button> value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
</div>
</div>
<div styleName='info-right'>
<StarButton styleName='info-right-button'
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
<button styleName='info-right-button'
onClick={(e) => this.handleShareButtonClick(e)}
>
<i className='fa fa-share-alt fa-fw'/>
</button>
<button styleName='info-right-button'
onClick={(e) => this.handleContextButtonClick(e)}
>
<i className='fa fa-ellipsis-v'/>
</button>
</div>
</div> </div>
</div> }
<div styleName='body'> <div styleName='body'>
<div styleName='body-description'> <div styleName='body-description'>
<textarea styleName='body-description-textarea' <textarea styleName='body-description-textarea'

View File

@@ -12,6 +12,36 @@ $info-height = 75px
border-bottom $ui-border border-bottom $ui-border
background-color $ui-backgroundColor background-color $ui-backgroundColor
.info-delete
height 80px
clearfix()
.info-delete-message
height 80px
line-height 80px
padding 0 25px
float left
.info-delete-confirmButton
float right
margin 25px 5px 0
height 30px
padding 0 25px
border-radius 2px
border none
color $ui-text-color
colorDangerButton()
.info-delete-cancelButton
float right
height 30px
margin 25px 5px 0
padding 0 25px
border $ui-border
border-radius 2px
color $ui-text-color
colorDefaultButton()
.info-left .info-left
float left float left
padding 0 5px padding 0 5px
@@ -68,6 +98,7 @@ $info-height = 75px
resize none resize none
border none border none
padding 10px padding 10px
line-height 1.6
.tabList .tabList
absolute left right absolute left right

View File

@@ -4,14 +4,25 @@ import styles from './Detail.styl'
import _ from 'lodash' import _ from 'lodash'
import MarkdownNoteDetail from './MarkdownNoteDetail' import MarkdownNoteDetail from './MarkdownNoteDetail'
import SnippetNoteDetail from './SnippetNoteDetail' import SnippetNoteDetail from './SnippetNoteDetail'
import dataApi from 'browser/main/lib/dataApi' import ee from 'browser/main/lib/eventEmitter'
const electron = require('electron')
const OSX = global.process.platform === 'darwin' const OSX = global.process.platform === 'darwin'
class Detail extends React.Component { class Detail extends React.Component {
componentDidUpdate (prevProps, prevState) { constructor (props) {
super(props)
this.focusHandler = () => {
this.refs.root.focus()
}
}
componentDidMount () {
ee.on('detail:focus', this.focusHandler)
}
componentWillUnmount () {
ee.off('detail:focus', this.focusHandler)
} }
render () { render () {
@@ -35,6 +46,7 @@ class Detail extends React.Component {
<div styleName='root' <div styleName='root'
style={this.props.style} style={this.props.style}
tabIndex='0' tabIndex='0'
ref='root'
> >
<div styleName='empty'> <div styleName='empty'>
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div> <div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div>
@@ -48,6 +60,7 @@ class Detail extends React.Component {
<SnippetNoteDetail <SnippetNoteDetail
note={note} note={note}
config={config} config={config}
ref='root'
{..._.pick(this.props, [ {..._.pick(this.props, [
'dispatch', 'dispatch',
'storages', 'storages',
@@ -63,6 +76,7 @@ class Detail extends React.Component {
<MarkdownNoteDetail <MarkdownNoteDetail
note={note} note={note}
config={config} config={config}
ref='root'
{..._.pick(this.props, [ {..._.pick(this.props, [
'dispatch', 'dispatch',
'storages', 'storages',

View File

@@ -3,18 +3,32 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './NoteList.styl' import styles from './NoteList.styl'
import moment from 'moment' import moment from 'moment'
import _ from 'lodash' import _ from 'lodash'
import ee from 'browser/main/lib/eventEmitter'
class NoteList extends React.Component { class NoteList extends React.Component {
constructor (props) { constructor (props) {
super(props) super(props)
this.selectNextNoteHandler = () => {
console.log('fired next')
this.selectNextNote()
}
this.selectPriorNoteHandler = () => {
this.selectPriorNote()
}
} }
componentDidMount () { componentDidMount () {
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000) this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
ee.on('list:next', this.selectNextNoteHandler)
ee.on('list:prior', this.selectPriorNoteHandler)
} }
componentWillUnmount () { componentWillUnmount () {
clearInterval(this.refreshTimer) clearInterval(this.refreshTimer)
ee.off('list:next', this.selectNextNoteHandler)
ee.off('list:prior', this.selectPriorNoteHandler)
} }
componentDidUpdate () { componentDidUpdate () {
@@ -51,10 +65,6 @@ class NoteList extends React.Component {
} }
} }
focus () {
// ReactDOM.findDOMNode(this).focus()
}
selectPriorNote () { selectPriorNote () {
if (this.notes == null || this.notes.length === 0) { if (this.notes == null || this.notes.length === 0) {
return return
@@ -104,6 +114,7 @@ class NoteList extends React.Component {
key: this.notes[targetIndex].uniqueKey key: this.notes[targetIndex].uniqueKey
} }
}) })
ee.emit('list:moved')
} }
handleNoteListKeyDown (e) { handleNoteListKeyDown (e) {

View File

@@ -65,7 +65,11 @@ textarea.block-input
modalZIndex= 1000 modalZIndex= 1000
modalBackColor = transparentify(white, 65%) modalBackColor = transparentify(white, 65%)
.ace_focus
outline-color rgb(59, 153, 252)
outline-offset 0px
outline-style auto
outline-width 5px
.ModalBase .ModalBase
fixed top left bottom right fixed top left bottom right
z-index modalZIndex z-index modalZIndex

View File

@@ -282,6 +282,15 @@ function removeStorage (key) {
return Promise.resolve(true) return Promise.resolve(true)
} }
function renameStorage (key, name) {
let storage = _.find(storages, {key: key})
if (storage == null) throw new Error('Storage doesn\'t exist.')
storage.cache.name = name
storage.saveCache()
return Promise.resolve(storage.toJSON())
}
function migrateFromV5 (key, data) { function migrateFromV5 (key, data) {
let oldFolders = data.folders let oldFolders = data.folders
let oldArticles = data.articles let oldArticles = data.articles
@@ -483,17 +492,31 @@ function updateNote (storageKey, folderKey, noteKey, input) {
storage: storageKey, storage: storageKey,
folder: folderKey folder: folderKey
}) })
note.data.title = input.title
note.data.tags = input.tags switch (note.data.type) {
note.data.content = input.content case 'MARKDOWN_NOTE':
note.data.updatedAt = input.updatedAt note.data.title = input.title
note.data.tags = input.tags
note.data.content = input.content
note.data.updatedAt = input.updatedAt
break
case 'SNIPPET_NOTE':
note.data.title = input.title
note.data.tags = input.tags
note.data.description = input.description
note.data.snippets = input.snippets
note.data.updatedAt = input.updatedAt
}
return note.save() return note.save()
.then(() => note.toJSON()) .then(() => note.toJSON())
} }
function removeNote (storageKey, folderKey, noteKey, input) { function removeNote (storageKey, folderKey, noteKey) {
notes = notes.filter((note) => note.storage !== storageKey || note.folder !== folderKey || note.key !== noteKey)
queueSaveFolder(storageKey, folderKey)
return Promise.resolve(null)
} }
function moveNote (storageKey, folderKey, noteKey, newStorageKey, newFolderKey) { function moveNote (storageKey, folderKey, noteKey, newStorageKey, newFolderKey) {
@@ -529,6 +552,7 @@ export default {
init, init,
addStorage, addStorage,
removeStorage, removeStorage,
renameStorage,
createFolder, createFolder,
updateFolder, updateFolder,
removeFolder, removeFolder,

View File

@@ -0,0 +1,26 @@
const electron = require('electron')
const { ipcRenderer, remote } = electron
function on (name, listener) {
ipcRenderer.on(name, listener)
}
function off (name, listener) {
ipcRenderer.removeListener(name, listener)
}
function once (name, listener) {
ipcRenderer.once(name, listener)
}
function emit (name, ...args) {
console.log(name)
remote.getCurrentWindow().webContents.send(name, ...args)
}
export default {
emit,
on,
off,
once
}

View File

@@ -36,7 +36,8 @@ class InitModal extends React.Component {
migrationRequested: true, migrationRequested: true,
isLoading: true, isLoading: true,
data: null, data: null,
legacyStorageExists: false legacyStorageExists: false,
isSending: false
} }
} }
@@ -86,67 +87,77 @@ class InitModal extends React.Component {
} }
handleSubmitButtonClick (e) { handleSubmitButtonClick (e) {
dataApi this.setState({
.addStorage({ isSending: true
name: 'My Storage', }, () => {
path: this.state.path dataApi
}) .addStorage({
.then((data) => { name: 'My Storage',
if (this.state.migrationRequested && _.isObject(this.state.data) && _.isArray(this.state.data.folders) && _.isArray(this.state.data.articles)) { path: this.state.path
return dataApi.migrateFromV5(data.storage.key, this.state.data)
}
return data
})
.then((data) => {
store.dispatch({
type: 'ADD_STORAGE',
storage: data.storage,
notes: data.notes
}) })
.then((data) => {
let defaultMarkdownNote = dataApi if (this.state.migrationRequested && _.isObject(this.state.data) && _.isArray(this.state.data.folders) && _.isArray(this.state.data.articles)) {
.createMarkdownNote(data.storage.key, data.storage.folders[0].key, { return dataApi.migrateFromV5(data.storage.key, this.state.data)
title: 'Welcome to Boostnote :)', }
content: '# Welcome to Boostnote :)\nThis is a markdown note.\n\nClick to edit this note.' return data
}) })
.then((note) => { .then((data) => {
store.dispatch({ store.dispatch({
type: 'CREATE_NOTE', type: 'ADD_STORAGE',
note: note storage: data.storage,
}) notes: data.notes
})
let defaultSnippetNote = dataApi
.createSnippetNote(data.storage.key, data.storage.folders[0].key, {
title: 'Snippet note example',
description: 'Snippet note example\nYou can store a series of snippet as a single note like Gist.',
snippets: [
{
name: 'example.html',
mode: 'html',
content: '<html>\n<body>\n<h1 id=\'hello\'>Hello World</h1>\n</body>\n</html>'
},
{
name: 'example.js',
mode: 'javascript',
content: 'var html = document.getElementById(\'hello\').innerHTML\n\nconsole.log(html)'
}
]
})
.then((note) => {
store.dispatch({
type: 'CREATE_NOTE',
note: note
})
}) })
return Promise.resolve(defaultSnippetNote) let defaultMarkdownNote = dataApi
.then(defaultMarkdownNote) .createMarkdownNote(data.storage.key, data.storage.folders[0].key, {
.then(() => data.storage) title: 'Welcome to Boostnote :)',
}) content: '# Welcome to Boostnote :)\nThis is a markdown note.\n\nClick to edit this note.'
.then((storage) => { })
hashHistory.push('/storages/' + storage.key) .then((note) => {
this.props.close() store.dispatch({
}) type: 'CREATE_NOTE',
note: note
})
})
let defaultSnippetNote = dataApi
.createSnippetNote(data.storage.key, data.storage.folders[0].key, {
title: 'Snippet note example',
description: 'Snippet note example\nYou can store a series of snippet as a single note like Gist.',
snippets: [
{
name: 'example.html',
mode: 'html',
content: '<html>\n<body>\n<h1 id=\'hello\'>Hello World</h1>\n</body>\n</html>'
},
{
name: 'example.js',
mode: 'javascript',
content: 'var html = document.getElementById(\'hello\').innerHTML\n\nconsole.log(html)'
}
]
})
.then((note) => {
store.dispatch({
type: 'CREATE_NOTE',
note: note
})
})
return Promise.resolve(defaultSnippetNote)
.then(defaultMarkdownNote)
.then(() => data.storage)
})
.then((storage) => {
hashHistory.push('/storages/' + storage.key)
this.props.close()
})
.catch((err) => {
this.setState({
isSending: false
})
throw err
})
})
} }
handleMigrationRequestedChange (e) { handleMigrationRequestedChange (e) {
@@ -197,7 +208,15 @@ class InitModal extends React.Component {
<div styleName='body-control'> <div styleName='body-control'>
<button styleName='body-control-createButton' <button styleName='body-control-createButton'
onClick={(e) => this.handleSubmitButtonClick(e)} onClick={(e) => this.handleSubmitButtonClick(e)}
>Let's Go!</button> disabled={this.state.isSending}
>
{this.state.isSending
? <span>
<i className='fa fa-spin fa-spinner'/> Loading...
</span>
: 'Let\'s Go!'
}
</button>
</div> </div>
</div> </div>

View File

@@ -27,7 +27,7 @@
top 10px top 10px
right 10px right 10px
height 30px height 30px
width 0 25px padding 0 25px
border $ui-border border $ui-border
border-radius 2px border-radius 2px
color $ui-text-color color $ui-text-color

View File

@@ -3,6 +3,7 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './NewNoteModal.styl' import styles from './NewNoteModal.styl'
import dataApi from 'browser/main/lib/dataApi' import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router' import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
class NewNoteModal extends React.Component { class NewNoteModal extends React.Component {
constructor (props) { constructor (props) {
@@ -36,6 +37,7 @@ class NewNoteModal extends React.Component {
pathname: location.pathname, pathname: location.pathname,
query: {key: note.uniqueKey} query: {key: note.uniqueKey}
}) })
ee.emit('detail:focus')
this.props.close() this.props.close()
}) })
} }
@@ -67,6 +69,7 @@ class NewNoteModal extends React.Component {
pathname: location.pathname, pathname: location.pathname,
query: {key: note.uniqueKey} query: {key: note.uniqueKey}
}) })
ee.emit('detail:focus')
this.props.close() this.props.close()
}) })
} }

View File

@@ -204,6 +204,14 @@ class UnstyledFolderItem extends React.Component {
const FolderItem = CSSModules(UnstyledFolderItem, styles) const FolderItem = CSSModules(UnstyledFolderItem, styles)
class StorageItem extends React.Component { class StorageItem extends React.Component {
constructor (props) {
super(props)
this.state = {
isLabelEditing: false
}
}
handleNewFolderButtonClick (e) { handleNewFolderButtonClick (e) {
let { storage } = this.props let { storage } = this.props
let input = { let input = {
@@ -242,6 +250,35 @@ class StorageItem extends React.Component {
}) })
} }
handleLabelClick (e) {
let { storage } = this.props
this.setState({
isLabelEditing: true,
name: storage.name
}, () => {
this.refs.label.focus()
})
}
handleLabelChange (e) {
this.setState({
name: this.refs.label.value
})
}
handleLabelBlur (e) {
let { storage } = this.props
dataApi
.renameStorage(storage.key, this.state.name)
.then((storage) => {
store.dispatch({
type: 'RENAME_STORAGE',
storage: storage
})
this.setState({
isLabelEditing: false
})
})
}
render () { render () {
let { storage } = this.props let { storage } = this.props
let folderList = storage.folders.map((folder) => { let folderList = storage.folders.map((folder) => {
@@ -253,9 +290,24 @@ class StorageItem extends React.Component {
return ( return (
<div styleName='root' key={storage.key}> <div styleName='root' key={storage.key}>
<div styleName='header'> <div styleName='header'>
<i className='fa fa-folder-open'/>&nbsp; {this.state.isLabelEditing
{storage.name}&nbsp; ? <div styleName='header-label--edit'>
<span styleName='header-path'>({storage.path})</span> <input styleName='header-label-input'
value={this.state.name}
ref='label'
onChange={(e) => this.handleLabelChange(e)}
onBlur={(e) => this.handleLabelBlur(e)}
/>
</div>
: <div styleName='header-label'
onClick={(e) => this.handleLabelClick(e)}
>
<i className='fa fa-folder-open'/>&nbsp;
{storage.name}&nbsp;
<span styleName='header-label-path'>({storage.path})</span>&nbsp;
<i styleName='header-label-editButton' className='fa fa-pencil'/>
</div>
}
<div styleName='header-control'> <div styleName='header-control'>
<button styleName='header-control-button' <button styleName='header-control-button'
onClick={(e) => this.handleNewFolderButtonClick(e)} onClick={(e) => this.handleNewFolderButtonClick(e)}

View File

@@ -10,10 +10,32 @@
border-bottom $default-border border-bottom $default-border
margin-bottom 5px margin-bottom 5px
.header-path .header-label
float left
cursor pointer
&:hover
.header-label-editButton
opacity 1
.header-label-path
color $ui-inactive-text-color color $ui-inactive-text-color
font-size 10px font-size 10px
margin 0 5px margin 0 5px
.header-label-editButton
color $ui-text-color
opacity 0
transition 0.2s
.header-label--edit
@extend .header-label
.header-label-input
height 25px
box-sizing border-box
vertical-align middle
border $ui-border
border-radius 2px
padding 0 5px
.header-control .header-control
float right float right

View File

@@ -18,6 +18,7 @@ function storages (state = [], action) {
case 'ADD_FOLDER': case 'ADD_FOLDER':
case 'REMOVE_FOLDER': case 'REMOVE_FOLDER':
case 'UPDATE_STORAGE': case 'UPDATE_STORAGE':
case 'RENAME_STORAGE':
{ {
let storages = state.slice() let storages = state.slice()
storages = storages storages = storages
@@ -83,6 +84,12 @@ function notes (state = [], action) {
notes.push(action.newNote) notes.push(action.newNote)
return notes return notes
} }
case 'REMOVE_NOTE':
{
let notes = state.slice()
notes = notes.filter((note) => note.key !== action.note.key || note.folder !== action.note.folder || note.storage !== action.note.storage)
return notes
}
} }
return state return state
} }