diff --git a/browser/components/SideNavFilter.js b/browser/components/SideNavFilter.js
index dba26f92..2f839a84 100644
--- a/browser/components/SideNavFilter.js
+++ b/browser/components/SideNavFilter.js
@@ -17,7 +17,7 @@ import styles from './SideNavFilter.styl'
const SideNavFilter = ({
isFolded, isHomeActive, handleAllNotesButtonClick,
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick, counterDelNote,
- counterTotalNote, counterStarredNote
+ counterTotalNote, counterStarredNote, handleFilterButtonContextMenu
}) => (
@@ -26,9 +26,9 @@ const SideNavFilter = ({
>
All Notes
@@ -40,9 +40,9 @@ const SideNavFilter = ({
>
Starred
@@ -54,12 +54,12 @@ const SideNavFilter = ({
>
-
Trash
+
Trash
{counterDelNote}
diff --git a/browser/components/TagListItem.js b/browser/components/TagListItem.js
index 2625412a..ebef7df4 100644
--- a/browser/components/TagListItem.js
+++ b/browser/components/TagListItem.js
@@ -12,10 +12,11 @@ import CSSModules from 'browser/lib/CSSModules'
* @param {bool} isActive
*/
-const TagListItem = ({name, handleClickTagListItem, isActive}) => (
+const TagListItem = ({name, handleClickTagListItem, isActive, count}) => (
)
diff --git a/browser/components/TagListItem.styl b/browser/components/TagListItem.styl
index cd3a5387..b35b30cf 100644
--- a/browser/components/TagListItem.styl
+++ b/browser/components/TagListItem.styl
@@ -48,6 +48,9 @@
overflow hidden
text-overflow ellipsis
+.tagList-item-count
+ padding 0 3px
+
body[data-theme="white"]
.tagList-item
color $ui-inactive-text-color
@@ -63,6 +66,8 @@ body[data-theme="white"]
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
+ .tagList-item-count
+ color $ui-text-color
body[data-theme="dark"]
.tagList-item
@@ -81,4 +86,6 @@ body[data-theme="dark"]
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
&:hover
color $ui-dark-text-color
- background-color alpha($ui-dark-button--active-backgroundColor, 50%)
\ No newline at end of file
+ background-color alpha($ui-dark-button--active-backgroundColor, 50%)
+ .tagList-item-count
+ color $ui-dark-button--active-color
diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js
index 7694f1e7..a543a5aa 100755
--- a/browser/main/Detail/MarkdownNoteDetail.js
+++ b/browser/main/Detail/MarkdownNoteDetail.js
@@ -19,6 +19,7 @@ import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import ConfigManager from 'browser/main/lib/ConfigManager'
import TrashButton from './TrashButton'
import FullscreenButton from './FullscreenButton'
+import RestoreButton from './RestoreButton'
import PermanentDeleteButton from './PermanentDeleteButton'
import InfoButton from './InfoButton'
import ToggleModeButton from './ToggleModeButton'
@@ -321,10 +322,7 @@ class MarkdownNoteDetail extends React.Component {
const trashTopBar =
- this.handleUndoButtonClick(e)}
- />
+ this.handleUndoButtonClick(e)} />
this.handleTrashButtonClick(e)} />
@@ -359,12 +357,10 @@ class MarkdownNoteDetail extends React.Component {
value={this.state.note.tags}
onChange={this.handleUpdateTag.bind(this)}
/>
-
- this.handleSwitchMode(e)} editorType={editorType} />
-
+
this.handleSwitchMode(e)} editorType={editorType} />
this.handleStarButtonClick(e)}
isActive={note.isStarred}
diff --git a/browser/main/Detail/MarkdownNoteDetail.styl b/browser/main/Detail/MarkdownNoteDetail.styl
index 652532c7..ad20f0f2 100644
--- a/browser/main/Detail/MarkdownNoteDetail.styl
+++ b/browser/main/Detail/MarkdownNoteDetail.styl
@@ -7,6 +7,7 @@
background-color $ui-noteDetail-backgroundColor
box-shadow none
padding 20px 40px
+ overflow hidden
.lock-button
padding-bottom 3px
@@ -44,7 +45,7 @@
margin 0 30px
.body-noteEditor
absolute top bottom left right
-
+
body[data-theme="white"]
.root
box-shadow $note-detail-box-shadow
diff --git a/browser/main/Detail/RestoreButton.js b/browser/main/Detail/RestoreButton.js
new file mode 100644
index 00000000..0f9c992e
--- /dev/null
+++ b/browser/main/Detail/RestoreButton.js
@@ -0,0 +1,21 @@
+import PropTypes from 'prop-types'
+import React from 'react'
+import CSSModules from 'browser/lib/CSSModules'
+import styles from './RestoreButton.styl'
+
+const RestoreButton = ({
+ onClick
+}) => (
+
+)
+
+RestoreButton.propTypes = {
+ onClick: PropTypes.func.isRequired
+}
+
+export default CSSModules(RestoreButton, styles)
diff --git a/browser/main/Detail/RestoreButton.styl b/browser/main/Detail/RestoreButton.styl
new file mode 100644
index 00000000..58ce745d
--- /dev/null
+++ b/browser/main/Detail/RestoreButton.styl
@@ -0,0 +1,22 @@
+.control-restoreButton
+ top 115px
+ topBarButtonRight()
+ &:hover .tooltip
+ opacity 1
+
+.tooltip
+ tooltip()
+ position absolute
+ pointer-events none
+ top 50px
+ left 25px
+ z-index 200
+ padding 5px
+ line-height normal
+ border-radius 2px
+ opacity 0
+ transition 0.1s
+
+body[data-theme="dark"]
+ .control-restoreButton
+ topBarButtonDark()
diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js
index 791b490e..dbef37ca 100644
--- a/browser/main/Detail/SnippetNoteDetail.js
+++ b/browser/main/Detail/SnippetNoteDetail.js
@@ -20,6 +20,7 @@ import _ from 'lodash'
import { findNoteTitle } from 'browser/lib/findNoteTitle'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import TrashButton from './TrashButton'
+import RestoreButton from './RestoreButton'
import PermanentDeleteButton from './PermanentDeleteButton'
import InfoButton from './InfoButton'
import InfoPanel from './InfoPanel'
@@ -589,10 +590,7 @@ class SnippetNoteDetail extends React.Component {
const trashTopBar =
- this.handleUndoButtonClick(e)}
- />
+ this.handleUndoButtonClick(e)} />
this.handleTrashButtonClick(e)} />
diff --git a/browser/main/Detail/ToggleModeButton.styl b/browser/main/Detail/ToggleModeButton.styl
index c69401f8..185a780c 100644
--- a/browser/main/Detail/ToggleModeButton.styl
+++ b/browser/main/Detail/ToggleModeButton.styl
@@ -5,8 +5,8 @@
width 52px
display flex
align-items center
- position absolute
- right 165px
+ position: relative
+ top 2px
.active
background-color #1EC38B
width 33px
@@ -55,4 +55,4 @@ body[data-theme="solarized-dark"]
background-color #002B36
.active
background-color #1EC38B
- box-shadow 2px 0px 7px #222222
\ No newline at end of file
+ box-shadow 2px 0px 7px #222222
diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js
index 6ba45faa..ff003479 100644
--- a/browser/main/NoteList/index.js
+++ b/browser/main/NoteList/index.js
@@ -69,6 +69,7 @@ class NoteList extends React.Component {
this.getNoteStorage = this.getNoteStorage.bind(this)
this.getNoteFolder = this.getNoteFolder.bind(this)
this.getViewType = this.getViewType.bind(this)
+ this.restoreNote = this.restoreNote.bind(this)
// TODO: not Selected noteKeys but SelectedNote(for reusing)
this.state = {
@@ -456,6 +457,7 @@ class NoteList extends React.Component {
const pinLabel = note.isPinned ? 'Remove pin' : 'Pin to Top'
const deleteLabel = 'Delete Note'
const cloneNote = 'Clone Note'
+ const restoreNote = 'Restore Note'
const menu = new Menu()
if (!location.pathname.match(/\/starred|\/trash/)) {
@@ -464,6 +466,14 @@ class NoteList extends React.Component {
click: this.pinToTop
}))
}
+
+ if (location.pathname.match(/\/trash/)) {
+ menu.append(new MenuItem({
+ label: restoreNote,
+ click: this.restoreNote
+ }))
+ }
+
menu.append(new MenuItem({
label: deleteLabel,
click: this.deleteNote
@@ -475,28 +485,50 @@ class NoteList extends React.Component {
menu.popup()
}
- pinToTop () {
+ updateSelectedNotes (updateFunc, cleanSelection = true) {
const { selectedNoteKeys } = this.state
const { dispatch } = this.props
const notes = this.notes.map((note) => Object.assign({}, note))
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
+ if (!_.isFunction(updateFunc)) {
+ console.warn('Update function is not defined. No update will happen')
+ updateFunc = (note) => { return note }
+ }
+
Promise.all(
- selectedNotes.map((note) => {
- note.isPinned = !note.isPinned
- return dataApi
- .updateNote(note.storage, note.key, note)
- })
- )
- .then((updatedNotes) => {
- updatedNotes.forEach((note) => {
- dispatch({
- type: 'UPDATE_NOTE',
- note
+ selectedNotes.map((note) => {
+ note = updateFunc(note)
+ return dataApi
+ .updateNote(note.storage, note.key, note)
})
- })
+ )
+ .then((updatedNotes) => {
+ updatedNotes.forEach((note) => {
+ dispatch({
+ type: 'UPDATE_NOTE',
+ note
+ })
+ })
+ })
+
+ if (cleanSelection) {
+ this.selectNextNote()
+ }
+ }
+
+ pinToTop () {
+ this.updateSelectedNotes((note) => {
+ note.isPinned = !note.isPinned
+ return note
+ })
+ }
+
+ restoreNote () {
+ this.updateSelectedNotes((note) => {
+ note.isTrashed = false
+ return note
})
- this.setState({ selectedNoteKeys: [] })
}
deleteNote () {
diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js
index 9b95ae3e..6d05e37b 100644
--- a/browser/main/SideNav/index.js
+++ b/browser/main/SideNav/index.js
@@ -1,6 +1,9 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
+const { remote } = require('electron')
+const { Menu } = remote
+import dataApi from 'browser/main/lib/dataApi'
import styles from './SideNav.styl'
import { openModal } from 'browser/main/lib/modal'
import PreferencesModal from '../modals/PreferencesModal'
@@ -89,6 +92,7 @@ class SideNav extends React.Component {
counterTotalNote={data.noteMap._map.size - data.trashedSet._set.size}
counterStarredNote={data.starredSet._set.size}
counterDelNote={data.trashedSet._set.size}
+ handleFilterButtonContextMenu={this.handleFilterButtonContextMenu.bind(this)}
/>
@@ -113,18 +117,21 @@ class SideNav extends React.Component {
tagListComponent () {
const { data, location } = this.props
- const tagList = data.tagNoteMap.map((tag, key) => {
- return key
+ const tagList = data.tagNoteMap.map((tag, name) => {
+ return { name, size: tag.size }
})
return (
- tagList.map(tag => (
-
- ))
+ tagList.map(tag => {
+ return (
+
+ )
+ })
)
}
@@ -139,6 +146,34 @@ class SideNav extends React.Component {
router.push(`/tags/${name}`)
}
+ emptyTrash (entries) {
+ const { dispatch } = this.props
+ const deletionPromises = entries.map((storageAndNoteKey) => {
+ const storageKey = storageAndNoteKey.split('-')[0]
+ const noteKey = storageAndNoteKey.split('-')[1]
+ return dataApi.deleteNote(storageKey, noteKey)
+ })
+ Promise.all(deletionPromises)
+ .then((arrayOfStorageAndNoteKeys) => {
+ arrayOfStorageAndNoteKeys.forEach(({ storageKey, noteKey }) => {
+ dispatch({ type: 'DELETE_NOTE', storageKey, noteKey })
+ })
+ })
+ .catch((err) => {
+ console.error('Cannot Delete note: ' + err)
+ })
+ console.log('Trash emptied')
+ }
+
+ handleFilterButtonContextMenu (event) {
+ const { data } = this.props
+ const entries = data.trashedSet.toJS()
+ const menu = Menu.buildFromTemplate([
+ { label: 'Empty Trash', click: () => this.emptyTrash(entries) }
+ ])
+ menu.popup()
+ }
+
render () {
const { data, location, config, dispatch } = this.props