mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
Add trash can
This commit is contained in:
@@ -89,7 +89,8 @@ NoteItem.propTypes = {
|
|||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isrequired,
|
title: PropTypes.string.isrequired,
|
||||||
tags: PropTypes.array,
|
tags: PropTypes.array,
|
||||||
isStarred: PropTypes.bool.isRequired
|
isStarred: PropTypes.bool.isRequired,
|
||||||
|
isTrashed: PropTypes.bool.isRequired
|
||||||
}),
|
}),
|
||||||
handleNoteClick: PropTypes.func.isRequired,
|
handleNoteClick: PropTypes.func.isRequired,
|
||||||
handleDragStart: PropTypes.func.isRequired,
|
handleDragStart: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import styles from './SideNavFilter.styl'
|
|||||||
*/
|
*/
|
||||||
const SideNavFilter = ({
|
const SideNavFilter = ({
|
||||||
isFolded, isHomeActive, handleAllNotesButtonClick,
|
isFolded, isHomeActive, handleAllNotesButtonClick,
|
||||||
isStarredActive, handleStarredButtonClick
|
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick
|
||||||
}) => (
|
}) => (
|
||||||
<div styleName={isFolded ? 'menu--folded' : 'menu'}>
|
<div styleName={isFolded ? 'menu--folded' : 'menu'}>
|
||||||
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
|
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
|
||||||
@@ -30,6 +30,12 @@ const SideNavFilter = ({
|
|||||||
<i className='fa fa-star fa-fw' />
|
<i className='fa fa-star fa-fw' />
|
||||||
<span styleName='menu-button-label'>Starred</span>
|
<span styleName='menu-button-label'>Starred</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button styleName={isTrashedActive ? 'menu-button--active' : 'menu-button'}
|
||||||
|
onClick={handleTrashedButtonClick}
|
||||||
|
>
|
||||||
|
<i className='fa fa-trash fa-fw' />
|
||||||
|
<span styleName='menu-button-label'>Trashed</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,7 +44,9 @@ SideNavFilter.propTypes = {
|
|||||||
isHomeActive: PropTypes.bool.isRequired,
|
isHomeActive: PropTypes.bool.isRequired,
|
||||||
handleAllNotesButtonClick: PropTypes.func.isRequired,
|
handleAllNotesButtonClick: PropTypes.func.isRequired,
|
||||||
isStarredActive: PropTypes.bool.isRequired,
|
isStarredActive: PropTypes.bool.isRequired,
|
||||||
handleStarredButtonClick: PropTypes.func.isRequired
|
isTrashedActive: PropTypes.bool.isRequired,
|
||||||
|
handleStarredButtonClick: PropTypes.func.isRequired,
|
||||||
|
handleTrashdButtonClick: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CSSModules(SideNavFilter, styles)
|
export default CSSModules(SideNavFilter, styles)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
note: Object.assign({}, nextProps.note)
|
note: Object.assign({}, nextProps.note)
|
||||||
}, () => {
|
}, () => {
|
||||||
this.refs.content.reload()
|
this.refs.content.reload()
|
||||||
this.refs.tags.reset()
|
if (this.refs.tags) this.refs.tags.reset()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,30 +176,59 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleTrashButtonClick (e) {
|
handleTrashButtonClick (e) {
|
||||||
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
let { note } = this.state
|
||||||
|
const { isTrashed } = note
|
||||||
|
|
||||||
|
const popupMessage = isTrashed ? 'This work cannot be undone.' : 'Throw it into trashbox.'
|
||||||
|
|
||||||
|
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: 'Delete a note',
|
message: 'Delete a note',
|
||||||
detail: 'This work cannot be undone.',
|
detail: popupMessage,
|
||||||
buttons: ['Confirm', 'Cancel']
|
buttons: ['Confirm', 'Cancel']
|
||||||
})
|
})
|
||||||
if (index === 0) {
|
if (dialogueButtonIndex === 0) {
|
||||||
let { note, dispatch } = this.props
|
if (!isTrashed) {
|
||||||
dataApi
|
note.isTrashed = true
|
||||||
.deleteNote(note.storage, note.key)
|
|
||||||
.then((data) => {
|
this.setState({
|
||||||
let dispatchHandler = () => {
|
note
|
||||||
dispatch({
|
}, () => {
|
||||||
type: 'DELETE_NOTE',
|
this.save()
|
||||||
storageKey: data.storageKey,
|
|
||||||
noteKey: data.noteKey
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ee.once('list:moved', dispatchHandler)
|
|
||||||
ee.emit('list:next')
|
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
let { note, dispatch } = this.props
|
||||||
|
dataApi
|
||||||
|
.deleteNote(note.storage, note.key)
|
||||||
|
.then((data) => {
|
||||||
|
let dispatchHandler = () => {
|
||||||
|
dispatch({
|
||||||
|
type: 'DELETE_NOTE',
|
||||||
|
storageKey: data.storageKey,
|
||||||
|
noteKey: data.noteKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ee.once('list:moved', dispatchHandler)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ee.emit('list:next')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleUndoButtonClick (e) {
|
||||||
|
let { note } = this.state
|
||||||
|
|
||||||
|
note.isTrashed = false
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
note
|
||||||
|
}, () => {
|
||||||
|
this.save()
|
||||||
|
this.refs.content.reload()
|
||||||
|
ee.emit('list:next')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handleFullScreenButton (e) {
|
handleFullScreenButton (e) {
|
||||||
ee.emit('editor:fullscreen')
|
ee.emit('editor:fullscreen')
|
||||||
}
|
}
|
||||||
@@ -254,72 +283,79 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
})
|
})
|
||||||
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||||
|
|
||||||
|
const trashTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<div styleName='info-left-top'>
|
||||||
|
<div styleName='info-left-top-folderSelect'>
|
||||||
|
<i styleName='undo-button'
|
||||||
|
className='fa fa-undo fa-fw'
|
||||||
|
onClick={(e) => this.handleUndoButtonClick(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const detailTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<StarButton styleName='info-left-button'
|
||||||
|
onClick={(e) => this.handleStarButtonClick(e)}
|
||||||
|
isActive={note.isStarred}
|
||||||
|
/>
|
||||||
|
<div styleName='info-left-top'>
|
||||||
|
<FolderSelect styleName='info-left-top-folderSelect'
|
||||||
|
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||||
|
ref='folder'
|
||||||
|
data={data}
|
||||||
|
onChange={(e) => this.handleFolderChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TagSelect
|
||||||
|
ref='tags'
|
||||||
|
value={this.state.note.tags}
|
||||||
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
/>
|
||||||
|
<TodoListPercentage
|
||||||
|
percentageOfTodo={this.getPercentageOfCompleteTodo(note.content)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
{(() => {
|
||||||
|
const faClassName = `fa ${this.getToggleLockButton()}`
|
||||||
|
const lockButtonComponent =
|
||||||
|
<button styleName='control-lockButton'
|
||||||
|
onFocus={(e) => this.handleFocus(e)}
|
||||||
|
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
|
||||||
|
>
|
||||||
|
<i className={faClassName} styleName='lock-button' />
|
||||||
|
<span styleName='control-lockButton-tooltip'>
|
||||||
|
{this.state.isLocked ? 'Unlock' : 'Lock'}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
return (
|
||||||
|
this.state.isLockButtonShown ? lockButtonComponent : ''
|
||||||
|
)
|
||||||
|
})()}
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
<button styleName='control-fullScreenButton'
|
||||||
|
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-arrows-alt' styleName='fullScreen-button' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='NoteDetail'
|
<div className='NoteDetail'
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
styleName='root'
|
styleName='root'
|
||||||
>
|
>
|
||||||
<div styleName='info'>
|
|
||||||
<div styleName='info-left'>
|
|
||||||
<StarButton styleName='info-left-button'
|
|
||||||
onClick={(e) => this.handleStarButtonClick(e)}
|
|
||||||
isActive={note.isStarred}
|
|
||||||
/>
|
|
||||||
<div styleName='info-left-top'>
|
|
||||||
<FolderSelect styleName='info-left-top-folderSelect'
|
|
||||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
|
||||||
ref='folder'
|
|
||||||
data={data}
|
|
||||||
onChange={(e) => this.handleFolderChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TagSelect
|
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}
|
||||||
ref='tags'
|
|
||||||
value={this.state.note.tags}
|
|
||||||
onChange={(e) => this.handleChange(e)}
|
|
||||||
/>
|
|
||||||
<TodoListPercentage
|
|
||||||
percentageOfTodo={this.getPercentageOfCompleteTodo(note.content)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div styleName='info-right'>
|
|
||||||
{(() => {
|
|
||||||
const faClassName = `fa ${this.getToggleLockButton()}`
|
|
||||||
const lockButtonComponent =
|
|
||||||
<button styleName='control-lockButton'
|
|
||||||
onFocus={(e) => this.handleFocus(e)}
|
|
||||||
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
|
|
||||||
>
|
|
||||||
<i className={faClassName} styleName='lock-button' />
|
|
||||||
<span styleName='control-lockButton-tooltip'>
|
|
||||||
{this.state.isLocked ? 'Lock' : 'Unlock'}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
return (
|
|
||||||
this.state.isLockButtonShown ? lockButtonComponent : ''
|
|
||||||
)
|
|
||||||
})()}
|
|
||||||
<TrashButton
|
|
||||||
onClick={(e) => this.handleTrashButtonClick(e)}
|
|
||||||
/>
|
|
||||||
<button styleName='control-fullScreenButton'
|
|
||||||
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
|
||||||
>
|
|
||||||
<i className='fa fa-expand' styleName='fullScreen-button' />
|
|
||||||
</button>
|
|
||||||
<InfoButton
|
|
||||||
onClick={(e) => this.handleInfoButtonClick(e)}
|
|
||||||
/>
|
|
||||||
<InfoPanel
|
|
||||||
storageName={currentOption.storage.name}
|
|
||||||
folderName={currentOption.folder.name}
|
|
||||||
noteKey={location.query.key}
|
|
||||||
updatedAt={formatDate(note.updatedAt)}
|
|
||||||
createdAt={formatDate(note.createdAt)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='body'>
|
<div styleName='body'>
|
||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
|
|||||||
@@ -55,6 +55,18 @@ $info-margin-under-border = 27px
|
|||||||
bottom 1px
|
bottom 1px
|
||||||
padding-left 30px
|
padding-left 30px
|
||||||
|
|
||||||
|
.undo-button
|
||||||
|
position relative
|
||||||
|
border solid 1px transparent
|
||||||
|
line-height 34px
|
||||||
|
vertical-align middle
|
||||||
|
border-radius 2px
|
||||||
|
transition 0.15s
|
||||||
|
user-select none
|
||||||
|
cursor pointer
|
||||||
|
&:hover
|
||||||
|
background-color #D9D9D9
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.info
|
.info
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
snippets.forEach((snippet, index) => {
|
snippets.forEach((snippet, index) => {
|
||||||
this.refs['code-' + index].reload()
|
this.refs['code-' + index].reload()
|
||||||
})
|
})
|
||||||
this.refs.tags.reset()
|
if (this.refs.tags) this.refs.tags.reset()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
handleChange (e) {
|
handleChange (e) {
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
|
||||||
note.tags = this.refs.tags.value
|
if (this.refs.tags) note.tags = this.refs.tags.value
|
||||||
note.description = this.refs.description.value
|
note.description = this.refs.description.value
|
||||||
note.updatedAt = new Date()
|
note.updatedAt = new Date()
|
||||||
note.title = findNoteTitle(note.description)
|
note.title = findNoteTitle(note.description)
|
||||||
@@ -170,30 +170,58 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleTrashButtonClick (e) {
|
handleTrashButtonClick (e) {
|
||||||
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
let { note } = this.state
|
||||||
|
const { isTrashed } = note
|
||||||
|
|
||||||
|
const popupMessage = isTrashed ? 'This work cannot be undone.' : 'Throw it into trashbox.'
|
||||||
|
|
||||||
|
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: 'Delete a note',
|
message: 'Delete a note',
|
||||||
detail: 'This work cannot be undone.',
|
detail: popupMessage,
|
||||||
buttons: ['Confirm', 'Cancel']
|
buttons: ['Confirm', 'Cancel']
|
||||||
})
|
})
|
||||||
if (index === 0) {
|
if (dialogueButtonIndex === 0) {
|
||||||
let { note, dispatch } = this.props
|
if (!isTrashed) {
|
||||||
dataApi
|
note.isTrashed = true
|
||||||
.deleteNote(note.storage, note.key)
|
|
||||||
.then((data) => {
|
this.setState({
|
||||||
let dispatchHandler = () => {
|
note
|
||||||
dispatch({
|
}, () => {
|
||||||
type: 'DELETE_NOTE',
|
this.save()
|
||||||
storageKey: data.storageKey,
|
|
||||||
noteKey: data.noteKey
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ee.once('list:moved', dispatchHandler)
|
|
||||||
ee.emit('list:next')
|
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
let { note, dispatch } = this.props
|
||||||
|
dataApi
|
||||||
|
.deleteNote(note.storage, note.key)
|
||||||
|
.then((data) => {
|
||||||
|
let dispatchHandler = () => {
|
||||||
|
dispatch({
|
||||||
|
type: 'DELETE_NOTE',
|
||||||
|
storageKey: data.storageKey,
|
||||||
|
noteKey: data.noteKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ee.once('list:moved', dispatchHandler)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ee.emit('list:next')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleUndoButtonClick (e) {
|
||||||
|
let { note } = this.state
|
||||||
|
|
||||||
|
note.isTrashed = false
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
note
|
||||||
|
}, () => {
|
||||||
|
this.save()
|
||||||
|
ee.emit('list:next')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handleFullScreenButton (e) {
|
handleFullScreenButton (e) {
|
||||||
ee.emit('editor:fullscreen')
|
ee.emit('editor:fullscreen')
|
||||||
}
|
}
|
||||||
@@ -513,54 +541,60 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
})
|
})
|
||||||
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||||
|
|
||||||
|
const trashTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<div styleName='info-left-top'>
|
||||||
|
<div styleName='info-left-top-folderSelect'>
|
||||||
|
<i styleName='undo-button'
|
||||||
|
className='fa fa-undo fa-fw'
|
||||||
|
onClick={(e) => this.handleUndoButtonClick(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const detailTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<StarButton styleName='info-left-button'
|
||||||
|
onClick={(e) => this.handleStarButtonClick(e)}
|
||||||
|
isActive={note.isStarred}
|
||||||
|
/>
|
||||||
|
<div styleName='info-left-top'>
|
||||||
|
<FolderSelect styleName='info-left-top-folderSelect'
|
||||||
|
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||||
|
ref='folder'
|
||||||
|
data={data}
|
||||||
|
onChange={(e) => this.handleFolderChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TagSelect
|
||||||
|
ref='tags'
|
||||||
|
value={this.state.note.tags}
|
||||||
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
<button styleName='control-fullScreenButton'
|
||||||
|
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-arrows-alt' styleName='fullScreen-button' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='NoteDetail'
|
<div className='NoteDetail'
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
styleName='root'
|
styleName='root'
|
||||||
onKeyDown={(e) => this.handleKeyDown(e)}
|
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||||
>
|
>
|
||||||
<div styleName='info'>
|
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}
|
||||||
<div styleName='info-left'>
|
|
||||||
<StarButton styleName='info-left-button'
|
|
||||||
onClick={(e) => this.handleStarButtonClick(e)}
|
|
||||||
isActive={note.isStarred}
|
|
||||||
/>
|
|
||||||
<div styleName='info-left-top'>
|
|
||||||
<FolderSelect styleName='info-left-top-folderSelect'
|
|
||||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
|
||||||
ref='folder'
|
|
||||||
data={data}
|
|
||||||
onChange={(e) => this.handleFolderChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TagSelect
|
|
||||||
ref='tags'
|
|
||||||
value={this.state.note.tags}
|
|
||||||
onChange={(e) => this.handleChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div styleName='info-right'>
|
|
||||||
<TrashButton
|
|
||||||
onClick={(e) => this.handleTrashButtonClick(e)}
|
|
||||||
/>
|
|
||||||
<button styleName='control-fullScreenButton'
|
|
||||||
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
|
||||||
>
|
|
||||||
<i className='fa fa-expand' styleName='fullScreen-button' />
|
|
||||||
</button>
|
|
||||||
<InfoButton
|
|
||||||
onClick={(e) => this.handleInfoButtonClick(e)}
|
|
||||||
/>
|
|
||||||
<InfoPanel
|
|
||||||
storageName={currentOption.storage.name}
|
|
||||||
folderName={currentOption.folder.name}
|
|
||||||
noteKey={location.query.key}
|
|
||||||
updatedAt={formatDate(note.updatedAt)}
|
|
||||||
createdAt={formatDate(note.createdAt)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='body'>
|
<div styleName='body'>
|
||||||
<div styleName='description'>
|
<div styleName='description'>
|
||||||
|
|||||||
@@ -257,6 +257,11 @@ class NoteList extends React.Component {
|
|||||||
return searchFromNotes(this.props.data, searchInputText)
|
return searchFromNotes(this.props.data, searchInputText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (location.pathname.match(/\/trashed/)) {
|
||||||
|
return data.trashedSet.toJS()
|
||||||
|
.map((uniqueKey) => data.noteMap.get(uniqueKey))
|
||||||
|
}
|
||||||
|
|
||||||
let storageKey = params.storageKey
|
let storageKey = params.storageKey
|
||||||
let folderKey = params.folderKey
|
let folderKey = params.folderKey
|
||||||
let storage = data.storageMap.get(storageKey)
|
let storage = data.storageMap.get(storageKey)
|
||||||
@@ -411,6 +416,10 @@ class NoteList extends React.Component {
|
|||||||
: sortByUpdatedAt
|
: sortByUpdatedAt
|
||||||
this.notes = notes = this.getNotes()
|
this.notes = notes = this.getNotes()
|
||||||
.sort(sortFunc)
|
.sort(sortFunc)
|
||||||
|
.filter((note) => {
|
||||||
|
// this is for the trash box
|
||||||
|
if (note.isTrashed !== true || location.pathname === '/trashed') return true
|
||||||
|
})
|
||||||
|
|
||||||
let noteList = notes
|
let noteList = notes
|
||||||
.map(note => {
|
.map(note => {
|
||||||
|
|||||||
@@ -33,12 +33,18 @@ class SideNav extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleTrashedButtonClick (e) {
|
||||||
|
let { router } = this.context
|
||||||
|
router.push('/trashed')
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { data, location, config, dispatch } = this.props
|
let { data, location, config, dispatch } = this.props
|
||||||
|
|
||||||
let isFolded = config.isSideNavFolded
|
let isFolded = config.isSideNavFolded
|
||||||
let isHomeActive = !!location.pathname.match(/^\/home$/)
|
let isHomeActive = !!location.pathname.match(/^\/home$/)
|
||||||
let isStarredActive = !!location.pathname.match(/^\/starred$/)
|
let isStarredActive = !!location.pathname.match(/^\/starred$/)
|
||||||
|
let isTrashedActive = !!location.pathname.match(/^\/trashed$/)
|
||||||
|
|
||||||
let storageList = data.storageMap.map((storage, key) => {
|
let storageList = data.storageMap.map((storage, key) => {
|
||||||
return <StorageItem
|
return <StorageItem
|
||||||
@@ -72,7 +78,9 @@ class SideNav extends React.Component {
|
|||||||
isHomeActive={isHomeActive}
|
isHomeActive={isHomeActive}
|
||||||
handleAllNotesButtonClick={(e) => this.handleHomeButtonClick(e)}
|
handleAllNotesButtonClick={(e) => this.handleHomeButtonClick(e)}
|
||||||
isStarredActive={isStarredActive}
|
isStarredActive={isStarredActive}
|
||||||
|
isTrashedActive={isTrashedActive}
|
||||||
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
|
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
|
||||||
|
handleTrashedButtonClick={(e) => this.handleTrashedButtonClick(e)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div styleName='storageList'>
|
<div styleName='storageList'>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ ReactDOM.render((
|
|||||||
<Route path='home' />
|
<Route path='home' />
|
||||||
<Route path='starred' />
|
<Route path='starred' />
|
||||||
<Route path='searched' />
|
<Route path='searched' />
|
||||||
|
<Route path='trashed' />
|
||||||
<Route path='storages'>
|
<Route path='storages'>
|
||||||
<IndexRedirect to='/home' />
|
<IndexRedirect to='/home' />
|
||||||
<Route path=':storageKey'>
|
<Route path=':storageKey'>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ function validateInput (input) {
|
|||||||
input.tags = input.tags.filter((tag) => _.isString(tag) && tag.trim().length > 0)
|
input.tags = input.tags.filter((tag) => _.isString(tag) && tag.trim().length > 0)
|
||||||
if (!_.isString(input.title)) input.title = ''
|
if (!_.isString(input.title)) input.title = ''
|
||||||
input.isStarred = !!input.isStarred
|
input.isStarred = !!input.isStarred
|
||||||
|
input.isTrashed = !!input.isTrashed
|
||||||
|
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case 'MARKDOWN_NOTE':
|
case 'MARKDOWN_NOTE':
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ function validateInput (input) {
|
|||||||
validatedInput.isStarred = !!input.isStarred
|
validatedInput.isStarred = !!input.isStarred
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input.isTrashed != null) {
|
||||||
|
validatedInput.isTrashed = !!input.isTrashed
|
||||||
|
}
|
||||||
|
|
||||||
validatedInput.type = input.type
|
validatedInput.type = input.type
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case 'MARKDOWN_NOTE':
|
case 'MARKDOWN_NOTE':
|
||||||
@@ -101,6 +105,7 @@ function updateNote (storageKey, noteKey, input) {
|
|||||||
noteData.createdAt = new Date()
|
noteData.createdAt = new Date()
|
||||||
noteData.updatedAt = new Date()
|
noteData.updatedAt = new Date()
|
||||||
noteData.isStarred = false
|
noteData.isStarred = false
|
||||||
|
noteData.isTrashed = false
|
||||||
noteData.tags = []
|
noteData.tags = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ function defaultDataMap () {
|
|||||||
starredSet: new Set(),
|
starredSet: new Set(),
|
||||||
storageNoteMap: new Map(),
|
storageNoteMap: new Map(),
|
||||||
folderNoteMap: new Map(),
|
folderNoteMap: new Map(),
|
||||||
tagNoteMap: new Map()
|
tagNoteMap: new Map(),
|
||||||
|
trashedSet: new Set()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +35,10 @@ function data (state = defaultDataMap(), action) {
|
|||||||
state.starredSet.add(uniqueKey)
|
state.starredSet.add(uniqueKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (note.isTrashed) {
|
||||||
|
state.trashedSet.add(uniqueKey)
|
||||||
|
}
|
||||||
|
|
||||||
let storageNoteList = state.storageNoteMap.get(note.storage)
|
let storageNoteList = state.storageNoteMap.get(note.storage)
|
||||||
if (storageNoteList == null) {
|
if (storageNoteList == null) {
|
||||||
storageNoteList = new Set(storageNoteList)
|
storageNoteList = new Set(storageNoteList)
|
||||||
@@ -78,6 +83,15 @@ function data (state = defaultDataMap(), action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
|
||||||
|
state.trashedSet = new Set(state.trashedSet)
|
||||||
|
if (note.isTrashed) {
|
||||||
|
state.trashedSet.add(uniqueKey)
|
||||||
|
} else {
|
||||||
|
state.trashedSet.delete(uniqueKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update storageNoteMap if oldNote doesn't exist
|
// Update storageNoteMap if oldNote doesn't exist
|
||||||
if (oldNote == null) {
|
if (oldNote == null) {
|
||||||
state.storageNoteMap = new Map(state.storageNoteMap)
|
state.storageNoteMap = new Map(state.storageNoteMap)
|
||||||
@@ -163,6 +177,11 @@ function data (state = defaultDataMap(), action) {
|
|||||||
state.starredSet.delete(originKey)
|
state.starredSet.delete(originKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (originNote.isTrashed) {
|
||||||
|
state.trashedSet = new Set(state.trashedSet)
|
||||||
|
state.trashedSet.delete(originKey)
|
||||||
|
}
|
||||||
|
|
||||||
// From storageNoteMap
|
// From storageNoteMap
|
||||||
state.storageNoteMap = new Map(state.storageNoteMap)
|
state.storageNoteMap = new Map(state.storageNoteMap)
|
||||||
let noteSet = state.storageNoteMap.get(originNote.storage)
|
let noteSet = state.storageNoteMap.get(originNote.storage)
|
||||||
@@ -199,6 +218,15 @@ function data (state = defaultDataMap(), action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
|
||||||
|
state.trashedSet = new Set(state.trashedSet)
|
||||||
|
if (note.isTrashed) {
|
||||||
|
state.trashedSet.add(uniqueKey)
|
||||||
|
} else {
|
||||||
|
state.trashedSet.delete(uniqueKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update storageNoteMap if oldNote doesn't exist
|
// Update storageNoteMap if oldNote doesn't exist
|
||||||
if (oldNote == null) {
|
if (oldNote == null) {
|
||||||
state.storageNoteMap = new Map(state.storageNoteMap)
|
state.storageNoteMap = new Map(state.storageNoteMap)
|
||||||
@@ -283,6 +311,11 @@ function data (state = defaultDataMap(), action) {
|
|||||||
state.starredSet.delete(uniqueKey)
|
state.starredSet.delete(uniqueKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetNote.isTrashed) {
|
||||||
|
state.trashedSet = new Set(state.trashedSet)
|
||||||
|
state.trashedSet.delete(uniqueKey)
|
||||||
|
}
|
||||||
|
|
||||||
// From folderNoteMap
|
// From folderNoteMap
|
||||||
let folderKey = targetNote.storage + '-' + targetNote.folder
|
let folderKey = targetNote.storage + '-' + targetNote.folder
|
||||||
state.folderNoteMap = new Map(state.folderNoteMap)
|
state.folderNoteMap = new Map(state.folderNoteMap)
|
||||||
@@ -348,6 +381,11 @@ function data (state = defaultDataMap(), action) {
|
|||||||
state.starredSet.delete(noteKey)
|
state.starredSet.delete(noteKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (note.isTrashed) {
|
||||||
|
state.trashedSet = new Set(state.trashedSet)
|
||||||
|
state.trashedSet.delete(noteKey)
|
||||||
|
}
|
||||||
|
|
||||||
// Delete key from tag map
|
// Delete key from tag map
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
state.tagNoteMap = new Map(state.tagNoteMap)
|
||||||
note.tags.forEach((tag) => {
|
note.tags.forEach((tag) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user