mirror of
https://github.com/BoostIo/Boostnote
synced 2026-01-10 15:39:21 +00:00
fixed eslint error & integrated with prettier as well as formatted the whole codebase (#3450)
This commit is contained in:
@@ -4,14 +4,17 @@ import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './SwitchButton.styl'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
|
||||
const ListButton = ({
|
||||
onClick, isTagActive
|
||||
}) => (
|
||||
<button styleName={isTagActive ? 'non-active-button' : 'active-button'} onClick={onClick}>
|
||||
<img src={isTagActive
|
||||
? '../resources/icon/icon-list.svg'
|
||||
: '../resources/icon/icon-list-active.svg'
|
||||
}
|
||||
const ListButton = ({ onClick, isTagActive }) => (
|
||||
<button
|
||||
styleName={isTagActive ? 'non-active-button' : 'active-button'}
|
||||
onClick={onClick}
|
||||
>
|
||||
<img
|
||||
src={
|
||||
isTagActive
|
||||
? '../resources/icon/icon-list.svg'
|
||||
: '../resources/icon/icon-list-active.svg'
|
||||
}
|
||||
/>
|
||||
<span styleName='tooltip'>{i18n.__('Notes')}</span>
|
||||
</button>
|
||||
|
||||
@@ -4,10 +4,8 @@ import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './PreferenceButton.styl'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
|
||||
const PreferenceButton = ({
|
||||
onClick
|
||||
}) => (
|
||||
<button styleName='top-menu-preference' onClick={(e) => onClick(e)}>
|
||||
const PreferenceButton = ({ onClick }) => (
|
||||
<button styleName='top-menu-preference' onClick={e => onClick(e)}>
|
||||
<img src='../resources/icon/icon-setting.svg' />
|
||||
<span styleName='tooltip'>{i18n.__('Preferences')}</span>
|
||||
</button>
|
||||
|
||||
@@ -19,7 +19,7 @@ const escapeStringRegexp = require('escape-string-regexp')
|
||||
const path = require('path')
|
||||
|
||||
class StorageItem extends React.Component {
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const { storage } = this.props
|
||||
@@ -30,11 +30,11 @@ class StorageItem extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleHeaderContextMenu (e) {
|
||||
handleHeaderContextMenu(e) {
|
||||
context.popup([
|
||||
{
|
||||
label: i18n.__('Add Folder'),
|
||||
click: (e) => this.handleAddFolderButtonClick(e)
|
||||
click: e => this.handleAddFolderButtonClick(e)
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
@@ -44,11 +44,11 @@ class StorageItem extends React.Component {
|
||||
submenu: [
|
||||
{
|
||||
label: i18n.__('Export as txt'),
|
||||
click: (e) => this.handleExportStorageClick(e, 'txt')
|
||||
click: e => this.handleExportStorageClick(e, 'txt')
|
||||
},
|
||||
{
|
||||
label: i18n.__('Export as md'),
|
||||
click: (e) => this.handleExportStorageClick(e, 'md')
|
||||
click: e => this.handleExportStorageClick(e, 'md')
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -57,75 +57,74 @@ class StorageItem extends React.Component {
|
||||
},
|
||||
{
|
||||
label: i18n.__('Unlink Storage'),
|
||||
click: (e) => this.handleUnlinkStorageClick(e)
|
||||
click: e => this.handleUnlinkStorageClick(e)
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
handleUnlinkStorageClick (e) {
|
||||
handleUnlinkStorageClick(e) {
|
||||
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: i18n.__('Unlink Storage'),
|
||||
detail: i18n.__('This work will just detatches a storage from Boostnote. (Any data won\'t be deleted.)'),
|
||||
detail: i18n.__(
|
||||
"This work will just detatches a storage from Boostnote. (Any data won't be deleted.)"
|
||||
),
|
||||
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
|
||||
})
|
||||
|
||||
if (index === 0) {
|
||||
const { storage, dispatch } = this.props
|
||||
dataApi.removeStorage(storage.key)
|
||||
dataApi
|
||||
.removeStorage(storage.key)
|
||||
.then(() => {
|
||||
dispatch({
|
||||
type: 'REMOVE_STORAGE',
|
||||
storageKey: storage.key
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
throw err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleExportStorageClick (e, fileType) {
|
||||
handleExportStorageClick(e, fileType) {
|
||||
const options = {
|
||||
properties: ['openDirectory', 'createDirectory'],
|
||||
buttonLabel: i18n.__('Select directory'),
|
||||
title: i18n.__('Select a folder to export the files to'),
|
||||
multiSelections: false
|
||||
}
|
||||
dialog.showOpenDialog(remote.getCurrentWindow(), options,
|
||||
(paths) => {
|
||||
if (paths && paths.length === 1) {
|
||||
const { storage, dispatch } = this.props
|
||||
dataApi
|
||||
.exportStorage(storage.key, fileType, paths[0])
|
||||
.then(data => {
|
||||
dispatch({
|
||||
type: 'EXPORT_STORAGE',
|
||||
storage: data.storage,
|
||||
fileType: data.fileType
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
dialog.showOpenDialog(remote.getCurrentWindow(), options, paths => {
|
||||
if (paths && paths.length === 1) {
|
||||
const { storage, dispatch } = this.props
|
||||
dataApi.exportStorage(storage.key, fileType, paths[0]).then(data => {
|
||||
dispatch({
|
||||
type: 'EXPORT_STORAGE',
|
||||
storage: data.storage,
|
||||
fileType: data.fileType
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleToggleButtonClick (e) {
|
||||
handleToggleButtonClick(e) {
|
||||
const { storage, dispatch } = this.props
|
||||
const isOpen = !this.state.isOpen
|
||||
dataApi.toggleStorage(storage.key, isOpen)
|
||||
.then((storage) => {
|
||||
dispatch({
|
||||
type: 'EXPAND_STORAGE',
|
||||
storage,
|
||||
isOpen
|
||||
})
|
||||
dataApi.toggleStorage(storage.key, isOpen).then(storage => {
|
||||
dispatch({
|
||||
type: 'EXPAND_STORAGE',
|
||||
storage,
|
||||
isOpen
|
||||
})
|
||||
})
|
||||
this.setState({
|
||||
isOpen: isOpen
|
||||
})
|
||||
}
|
||||
|
||||
handleAddFolderButtonClick (e) {
|
||||
handleAddFolderButtonClick(e) {
|
||||
const { storage } = this.props
|
||||
|
||||
modal.open(CreateFolderModal, {
|
||||
@@ -133,23 +132,23 @@ class StorageItem extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleHeaderInfoClick (e) {
|
||||
handleHeaderInfoClick(e) {
|
||||
const { storage, dispatch } = this.props
|
||||
dispatch(push('/storages/' + storage.key))
|
||||
}
|
||||
|
||||
handleFolderButtonClick (folderKey) {
|
||||
return (e) => {
|
||||
handleFolderButtonClick(folderKey) {
|
||||
return e => {
|
||||
const { storage, dispatch } = this.props
|
||||
dispatch(push('/storages/' + storage.key + '/folders/' + folderKey))
|
||||
}
|
||||
}
|
||||
|
||||
handleFolderButtonContextMenu (e, folder) {
|
||||
handleFolderButtonContextMenu(e, folder) {
|
||||
context.popup([
|
||||
{
|
||||
label: i18n.__('Rename Folder'),
|
||||
click: (e) => this.handleRenameFolderClick(e, folder)
|
||||
click: e => this.handleRenameFolderClick(e, folder)
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
@@ -159,11 +158,11 @@ class StorageItem extends React.Component {
|
||||
submenu: [
|
||||
{
|
||||
label: i18n.__('Export as txt'),
|
||||
click: (e) => this.handleExportFolderClick(e, folder, 'txt')
|
||||
click: e => this.handleExportFolderClick(e, folder, 'txt')
|
||||
},
|
||||
{
|
||||
label: i18n.__('Export as md'),
|
||||
click: (e) => this.handleExportFolderClick(e, folder, 'md')
|
||||
click: e => this.handleExportFolderClick(e, folder, 'md')
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -172,12 +171,12 @@ class StorageItem extends React.Component {
|
||||
},
|
||||
{
|
||||
label: i18n.__('Delete Folder'),
|
||||
click: (e) => this.handleFolderDeleteClick(e, folder)
|
||||
click: e => this.handleFolderDeleteClick(e, folder)
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
handleRenameFolderClick (e, folder) {
|
||||
handleRenameFolderClick(e, folder) {
|
||||
const { storage } = this.props
|
||||
modal.open(RenameFolderModal, {
|
||||
storage,
|
||||
@@ -185,20 +184,19 @@ class StorageItem extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleExportFolderClick (e, folder, fileType) {
|
||||
handleExportFolderClick(e, folder, fileType) {
|
||||
const options = {
|
||||
properties: ['openDirectory', 'createDirectory'],
|
||||
buttonLabel: i18n.__('Select directory'),
|
||||
title: i18n.__('Select a folder to export the files to'),
|
||||
multiSelections: false
|
||||
}
|
||||
dialog.showOpenDialog(remote.getCurrentWindow(), options,
|
||||
(paths) => {
|
||||
dialog.showOpenDialog(remote.getCurrentWindow(), options, paths => {
|
||||
if (paths && paths.length === 1) {
|
||||
const { storage, dispatch } = this.props
|
||||
dataApi
|
||||
.exportFolder(storage.key, folder.key, fileType, paths[0])
|
||||
.then((data) => {
|
||||
.then(data => {
|
||||
dispatch({
|
||||
type: 'EXPORT_FOLDER',
|
||||
storage: data.storage,
|
||||
@@ -224,66 +222,74 @@ class StorageItem extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleFolderDeleteClick (e, folder) {
|
||||
handleFolderDeleteClick(e, folder) {
|
||||
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: i18n.__('Delete Folder'),
|
||||
detail: i18n.__('This will delete all notes in the folder and can not be undone.'),
|
||||
detail: i18n.__(
|
||||
'This will delete all notes in the folder and can not be undone.'
|
||||
),
|
||||
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
|
||||
})
|
||||
|
||||
if (index === 0) {
|
||||
const { storage, dispatch } = this.props
|
||||
dataApi
|
||||
.deleteFolder(storage.key, folder.key)
|
||||
.then((data) => {
|
||||
dispatch({
|
||||
type: 'DELETE_FOLDER',
|
||||
storage: data.storage,
|
||||
folderKey: data.folderKey
|
||||
})
|
||||
dataApi.deleteFolder(storage.key, folder.key).then(data => {
|
||||
dispatch({
|
||||
type: 'DELETE_FOLDER',
|
||||
storage: data.storage,
|
||||
folderKey: data.folderKey
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleDragEnter (e, key) {
|
||||
handleDragEnter(e, key) {
|
||||
e.preventDefault()
|
||||
if (this.state.draggedOver === key) { return }
|
||||
if (this.state.draggedOver === key) {
|
||||
return
|
||||
}
|
||||
this.setState({
|
||||
draggedOver: key
|
||||
})
|
||||
}
|
||||
|
||||
handleDragLeave (e) {
|
||||
handleDragLeave(e) {
|
||||
e.preventDefault()
|
||||
if (this.state.draggedOver === null) { return }
|
||||
if (this.state.draggedOver === null) {
|
||||
return
|
||||
}
|
||||
this.setState({
|
||||
draggedOver: null
|
||||
})
|
||||
}
|
||||
|
||||
dropNote (storage, folder, dispatch, location, noteData) {
|
||||
noteData = noteData.filter((note) => folder.key !== note.folder)
|
||||
dropNote(storage, folder, dispatch, location, noteData) {
|
||||
noteData = noteData.filter(note => folder.key !== note.folder)
|
||||
if (noteData.length === 0) return
|
||||
|
||||
Promise.all(
|
||||
noteData.map((note) => dataApi.moveNote(note.storage, note.key, storage.key, folder.key))
|
||||
noteData.map(note =>
|
||||
dataApi.moveNote(note.storage, note.key, storage.key, folder.key)
|
||||
)
|
||||
)
|
||||
.then((createdNoteData) => {
|
||||
createdNoteData.forEach((newNote) => {
|
||||
dispatch({
|
||||
type: 'MOVE_NOTE',
|
||||
originNote: noteData.find((note) => note.content === newNote.oldContent),
|
||||
note: newNote
|
||||
.then(createdNoteData => {
|
||||
createdNoteData.forEach(newNote => {
|
||||
dispatch({
|
||||
type: 'MOVE_NOTE',
|
||||
originNote: noteData.find(
|
||||
note => note.content === newNote.oldContent
|
||||
),
|
||||
note: newNote
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`error on delete notes: ${err}`)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(`error on delete notes: ${err}`)
|
||||
})
|
||||
}
|
||||
|
||||
handleDrop (e, storage, folder, dispatch, location) {
|
||||
handleDrop(e, storage, folder, dispatch, location) {
|
||||
e.preventDefault()
|
||||
if (this.state.draggedOver !== null) {
|
||||
this.setState({
|
||||
@@ -294,21 +300,37 @@ class StorageItem extends React.Component {
|
||||
this.dropNote(storage, folder, dispatch, location, noteData)
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { storage, location, isFolded, data, dispatch } = this.props
|
||||
const { folderNoteMap, trashedSet } = data
|
||||
const SortableStorageItemChild = SortableElement(StorageItemChild)
|
||||
const folderList = storage.folders.map((folder, index) => {
|
||||
const folderRegex = new RegExp(escapeStringRegexp(path.sep) + 'storages' + escapeStringRegexp(path.sep) + storage.key + escapeStringRegexp(path.sep) + 'folders' + escapeStringRegexp(path.sep) + folder.key)
|
||||
const isActive = !!(location.pathname.match(folderRegex))
|
||||
const folderRegex = new RegExp(
|
||||
escapeStringRegexp(path.sep) +
|
||||
'storages' +
|
||||
escapeStringRegexp(path.sep) +
|
||||
storage.key +
|
||||
escapeStringRegexp(path.sep) +
|
||||
'folders' +
|
||||
escapeStringRegexp(path.sep) +
|
||||
folder.key
|
||||
)
|
||||
const isActive = !!location.pathname.match(folderRegex)
|
||||
const noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
|
||||
|
||||
let noteCount = 0
|
||||
if (noteSet) {
|
||||
let trashedNoteCount = 0
|
||||
const noteKeys = noteSet.map(noteKey => { return noteKey })
|
||||
const noteKeys = noteSet.map(noteKey => {
|
||||
return noteKey
|
||||
})
|
||||
trashedSet.toJS().forEach(trashedKey => {
|
||||
if (noteKeys.some(noteKey => { return noteKey === trashedKey })) trashedNoteCount++
|
||||
if (
|
||||
noteKeys.some(noteKey => {
|
||||
return noteKey === trashedKey
|
||||
})
|
||||
)
|
||||
trashedNoteCount++
|
||||
})
|
||||
noteCount = noteSet.size - trashedNoteCount
|
||||
}
|
||||
@@ -317,73 +339,80 @@ class StorageItem extends React.Component {
|
||||
key={folder.key}
|
||||
index={index}
|
||||
isActive={isActive || folder.key === this.state.draggedOver}
|
||||
handleButtonClick={(e) => this.handleFolderButtonClick(folder.key)(e)}
|
||||
handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)}
|
||||
handleButtonClick={e => this.handleFolderButtonClick(folder.key)(e)}
|
||||
handleContextMenu={e => this.handleFolderButtonContextMenu(e, folder)}
|
||||
folderName={folder.name}
|
||||
folderColor={folder.color}
|
||||
isFolded={isFolded}
|
||||
noteCount={noteCount}
|
||||
handleDrop={(e) => {
|
||||
handleDrop={e => {
|
||||
this.handleDrop(e, storage, folder, dispatch, location)
|
||||
}}
|
||||
handleDragEnter={(e) => {
|
||||
handleDragEnter={e => {
|
||||
this.handleDragEnter(e, folder.key)
|
||||
}}
|
||||
handleDragLeave={(e) => {
|
||||
handleDragLeave={e => {
|
||||
this.handleDragLeave(e, folder)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const isActive = location.pathname.match(new RegExp(escapeStringRegexp(path.sep) + 'storages' + escapeStringRegexp(path.sep) + storage.key + '$'))
|
||||
const isActive = location.pathname.match(
|
||||
new RegExp(
|
||||
escapeStringRegexp(path.sep) +
|
||||
'storages' +
|
||||
escapeStringRegexp(path.sep) +
|
||||
storage.key +
|
||||
'$'
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<div styleName={isFolded ? 'root--folded' : 'root'}
|
||||
key={storage.key}
|
||||
>
|
||||
<div styleName={isActive
|
||||
? 'header--active'
|
||||
: 'header'
|
||||
}
|
||||
onContextMenu={(e) => this.handleHeaderContextMenu(e)}
|
||||
<div styleName={isFolded ? 'root--folded' : 'root'} key={storage.key}>
|
||||
<div
|
||||
styleName={isActive ? 'header--active' : 'header'}
|
||||
onContextMenu={e => this.handleHeaderContextMenu(e)}
|
||||
>
|
||||
<button styleName='header-toggleButton'
|
||||
onMouseDown={(e) => this.handleToggleButtonClick(e)}
|
||||
<button
|
||||
styleName='header-toggleButton'
|
||||
onMouseDown={e => this.handleToggleButtonClick(e)}
|
||||
>
|
||||
<img src={this.state.isOpen
|
||||
? '../resources/icon/icon-down.svg'
|
||||
: '../resources/icon/icon-right.svg'
|
||||
}
|
||||
<img
|
||||
src={
|
||||
this.state.isOpen
|
||||
? '../resources/icon/icon-down.svg'
|
||||
: '../resources/icon/icon-right.svg'
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
|
||||
{!isFolded &&
|
||||
<button styleName='header-addFolderButton'
|
||||
onClick={(e) => this.handleAddFolderButtonClick(e)}
|
||||
{!isFolded && (
|
||||
<button
|
||||
styleName='header-addFolderButton'
|
||||
onClick={e => this.handleAddFolderButtonClick(e)}
|
||||
>
|
||||
<img src='../resources/icon/icon-plus.svg' />
|
||||
</button>
|
||||
}
|
||||
)}
|
||||
|
||||
<button styleName='header-info'
|
||||
onClick={(e) => this.handleHeaderInfoClick(e)}
|
||||
<button
|
||||
styleName='header-info'
|
||||
onClick={e => this.handleHeaderInfoClick(e)}
|
||||
>
|
||||
<span>
|
||||
{isFolded ? _.truncate(storage.name, {length: 1, omission: ''}) : storage.name}
|
||||
{isFolded
|
||||
? _.truncate(storage.name, { length: 1, omission: '' })
|
||||
: storage.name}
|
||||
</span>
|
||||
{isFolded &&
|
||||
{isFolded && (
|
||||
<span styleName='header-info--folded-tooltip'>
|
||||
{storage.name}
|
||||
</span>
|
||||
}
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
{this.state.isOpen &&
|
||||
<div>
|
||||
{folderList}
|
||||
</div>
|
||||
}
|
||||
{this.state.isOpen && <div>{folderList}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,17 @@ import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './SwitchButton.styl'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
|
||||
const TagButton = ({
|
||||
onClick, isTagActive
|
||||
}) => (
|
||||
<button styleName={isTagActive ? 'active-button' : 'non-active-button'} onClick={onClick}>
|
||||
<img src={isTagActive
|
||||
? '../resources/icon/icon-tag-active.svg'
|
||||
: '../resources/icon/icon-tag.svg'
|
||||
}
|
||||
const TagButton = ({ onClick, isTagActive }) => (
|
||||
<button
|
||||
styleName={isTagActive ? 'active-button' : 'non-active-button'}
|
||||
onClick={onClick}
|
||||
>
|
||||
<img
|
||||
src={
|
||||
isTagActive
|
||||
? '../resources/icon/icon-tag-active.svg'
|
||||
: '../resources/icon/icon-tag.svg'
|
||||
}
|
||||
/>
|
||||
<span styleName='tooltip'>{i18n.__('Tags')}</span>
|
||||
</button>
|
||||
|
||||
@@ -16,7 +16,7 @@ import EventEmitter from 'browser/main/lib/eventEmitter'
|
||||
import PreferenceButton from './PreferenceButton'
|
||||
import ListButton from './ListButton'
|
||||
import TagButton from './TagButton'
|
||||
import {SortableContainer} from 'react-sortable-hoc'
|
||||
import { SortableContainer } from 'react-sortable-hoc'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import context from 'browser/lib/context'
|
||||
import { remote } from 'electron'
|
||||
@@ -24,13 +24,13 @@ import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||
import ColorPicker from 'browser/components/ColorPicker'
|
||||
import { every, sortBy } from 'lodash'
|
||||
|
||||
function matchActiveTags (tags, activeTags) {
|
||||
function matchActiveTags(tags, activeTags) {
|
||||
return every(activeTags, v => tags.indexOf(v) >= 0)
|
||||
}
|
||||
|
||||
class SideNav extends React.Component {
|
||||
// TODO: should not use electron stuff v0.7
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
@@ -47,24 +47,32 @@ class SideNav extends React.Component {
|
||||
this.handleColorPickerReset = this.handleColorPickerReset.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
EventEmitter.off('side:preferences', this.handleMenuButtonClick)
|
||||
}
|
||||
|
||||
deleteTag (tag) {
|
||||
const selectedButton = remote.dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: i18n.__('Confirm tag deletion'),
|
||||
detail: i18n.__('This will permanently remove this tag.'),
|
||||
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
|
||||
})
|
||||
deleteTag(tag) {
|
||||
const selectedButton = remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
type: 'warning',
|
||||
message: i18n.__('Confirm tag deletion'),
|
||||
detail: i18n.__('This will permanently remove this tag.'),
|
||||
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
|
||||
}
|
||||
)
|
||||
|
||||
if (selectedButton === 0) {
|
||||
const { data, dispatch, location, match: { params } } = this.props
|
||||
const {
|
||||
data,
|
||||
dispatch,
|
||||
location,
|
||||
match: { params }
|
||||
} = this.props
|
||||
|
||||
const notes = data.noteMap
|
||||
.map(note => note)
|
||||
@@ -78,44 +86,48 @@ class SideNav extends React.Component {
|
||||
return note
|
||||
})
|
||||
|
||||
Promise
|
||||
.all(notes.map(note => dataApi.updateNote(note.storage, note.key, note)))
|
||||
.then(updatedNotes => {
|
||||
updatedNotes.forEach(note => {
|
||||
dispatch({
|
||||
type: 'UPDATE_NOTE',
|
||||
note
|
||||
})
|
||||
Promise.all(
|
||||
notes.map(note => dataApi.updateNote(note.storage, note.key, note))
|
||||
).then(updatedNotes => {
|
||||
updatedNotes.forEach(note => {
|
||||
dispatch({
|
||||
type: 'UPDATE_NOTE',
|
||||
note
|
||||
})
|
||||
|
||||
if (location.pathname.match('/tags')) {
|
||||
const tags = params.tagname.split(' ')
|
||||
const index = tags.indexOf(tag)
|
||||
if (index !== -1) {
|
||||
tags.splice(index, 1)
|
||||
|
||||
dispatch(push(`/tags/${tags.map(tag => encodeURIComponent(tag)).join(' ')}`))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (location.pathname.match('/tags')) {
|
||||
const tags = params.tagname.split(' ')
|
||||
const index = tags.indexOf(tag)
|
||||
if (index !== -1) {
|
||||
tags.splice(index, 1)
|
||||
|
||||
dispatch(
|
||||
push(
|
||||
`/tags/${tags.map(tag => encodeURIComponent(tag)).join(' ')}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleMenuButtonClick (e) {
|
||||
handleMenuButtonClick(e) {
|
||||
openModal(PreferencesModal)
|
||||
}
|
||||
|
||||
handleHomeButtonClick (e) {
|
||||
handleHomeButtonClick(e) {
|
||||
const { dispatch } = this.props
|
||||
dispatch(push('/home'))
|
||||
}
|
||||
|
||||
handleStarredButtonClick (e) {
|
||||
handleStarredButtonClick(e) {
|
||||
const { dispatch } = this.props
|
||||
dispatch(push('/starred'))
|
||||
}
|
||||
|
||||
handleTagContextMenu (e, tag) {
|
||||
handleTagContextMenu(e, tag) {
|
||||
const menu = []
|
||||
|
||||
menu.push({
|
||||
@@ -125,13 +137,17 @@ class SideNav extends React.Component {
|
||||
|
||||
menu.push({
|
||||
label: i18n.__('Customize Color'),
|
||||
click: this.displayColorPicker.bind(this, tag, e.target.getBoundingClientRect())
|
||||
click: this.displayColorPicker.bind(
|
||||
this,
|
||||
tag,
|
||||
e.target.getBoundingClientRect()
|
||||
)
|
||||
})
|
||||
|
||||
context.popup(menu)
|
||||
}
|
||||
|
||||
dismissColorPicker () {
|
||||
dismissColorPicker() {
|
||||
this.setState({
|
||||
colorPicker: {
|
||||
show: false
|
||||
@@ -139,7 +155,7 @@ class SideNav extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
displayColorPicker (tagName, rect) {
|
||||
displayColorPicker(tagName, rect) {
|
||||
const { config } = this.props
|
||||
this.setState({
|
||||
colorPicker: {
|
||||
@@ -151,10 +167,17 @@ class SideNav extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleColorPickerConfirm (color) {
|
||||
const { dispatch, config: {coloredTags} } = this.props
|
||||
const { colorPicker: { tagName } } = this.state
|
||||
const newColoredTags = Object.assign({}, coloredTags, {[tagName]: color.hex})
|
||||
handleColorPickerConfirm(color) {
|
||||
const {
|
||||
dispatch,
|
||||
config: { coloredTags }
|
||||
} = this.props
|
||||
const {
|
||||
colorPicker: { tagName }
|
||||
} = this.state
|
||||
const newColoredTags = Object.assign({}, coloredTags, {
|
||||
[tagName]: color.hex
|
||||
})
|
||||
|
||||
const config = { coloredTags: newColoredTags }
|
||||
ConfigManager.set(config)
|
||||
@@ -165,9 +188,14 @@ class SideNav extends React.Component {
|
||||
this.dismissColorPicker()
|
||||
}
|
||||
|
||||
handleColorPickerReset () {
|
||||
const { dispatch, config: {coloredTags} } = this.props
|
||||
const { colorPicker: { tagName } } = this.state
|
||||
handleColorPickerReset() {
|
||||
const {
|
||||
dispatch,
|
||||
config: { coloredTags }
|
||||
} = this.props
|
||||
const {
|
||||
colorPicker: { tagName }
|
||||
} = this.state
|
||||
const newColoredTags = Object.assign({}, coloredTags)
|
||||
|
||||
delete newColoredTags[tagName]
|
||||
@@ -181,43 +209,41 @@ class SideNav extends React.Component {
|
||||
this.dismissColorPicker()
|
||||
}
|
||||
|
||||
handleToggleButtonClick (e) {
|
||||
handleToggleButtonClick(e) {
|
||||
const { dispatch, config } = this.props
|
||||
|
||||
ConfigManager.set({isSideNavFolded: !config.isSideNavFolded})
|
||||
ConfigManager.set({ isSideNavFolded: !config.isSideNavFolded })
|
||||
dispatch({
|
||||
type: 'SET_IS_SIDENAV_FOLDED',
|
||||
isFolded: !config.isSideNavFolded
|
||||
})
|
||||
}
|
||||
|
||||
handleTrashedButtonClick (e) {
|
||||
handleTrashedButtonClick(e) {
|
||||
const { dispatch } = this.props
|
||||
dispatch(push('/trashed'))
|
||||
}
|
||||
|
||||
handleSwitchFoldersButtonClick () {
|
||||
handleSwitchFoldersButtonClick() {
|
||||
const { dispatch } = this.props
|
||||
dispatch(push('/home'))
|
||||
}
|
||||
|
||||
handleSwitchTagsButtonClick () {
|
||||
handleSwitchTagsButtonClick() {
|
||||
const { dispatch } = this.props
|
||||
dispatch(push('/alltags'))
|
||||
}
|
||||
|
||||
onSortEnd (storage) {
|
||||
return ({oldIndex, newIndex}) => {
|
||||
onSortEnd(storage) {
|
||||
return ({ oldIndex, newIndex }) => {
|
||||
const { dispatch } = this.props
|
||||
dataApi
|
||||
.reorderFolder(storage.key, oldIndex, newIndex)
|
||||
.then((data) => {
|
||||
dispatch({ type: 'REORDER_FOLDER', storage: data.storage })
|
||||
})
|
||||
dataApi.reorderFolder(storage.key, oldIndex, newIndex).then(data => {
|
||||
dispatch({ type: 'REORDER_FOLDER', storage: data.storage })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
SideNavComponent (isFolded, storageList) {
|
||||
SideNavComponent(isFolded, storageList) {
|
||||
const { location, data, config } = this.props
|
||||
|
||||
const isHomeActive = !!location.pathname.match(/^\/home$/)
|
||||
@@ -227,25 +253,35 @@ class SideNav extends React.Component {
|
||||
let component
|
||||
|
||||
// TagsMode is not selected
|
||||
if (!location.pathname.match('/tags') && !location.pathname.match('/alltags')) {
|
||||
if (
|
||||
!location.pathname.match('/tags') &&
|
||||
!location.pathname.match('/alltags')
|
||||
) {
|
||||
component = (
|
||||
<div>
|
||||
<SideNavFilter
|
||||
isFolded={isFolded}
|
||||
isHomeActive={isHomeActive}
|
||||
handleAllNotesButtonClick={(e) => this.handleHomeButtonClick(e)}
|
||||
handleAllNotesButtonClick={e => this.handleHomeButtonClick(e)}
|
||||
isStarredActive={isStarredActive}
|
||||
isTrashedActive={isTrashedActive}
|
||||
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
|
||||
handleTrashedButtonClick={(e) => this.handleTrashedButtonClick(e)}
|
||||
counterTotalNote={data.noteMap._map.size - data.trashedSet._set.size}
|
||||
handleStarredButtonClick={e => this.handleStarredButtonClick(e)}
|
||||
handleTrashedButtonClick={e => this.handleTrashedButtonClick(e)}
|
||||
counterTotalNote={
|
||||
data.noteMap._map.size - data.trashedSet._set.size
|
||||
}
|
||||
counterStarredNote={data.starredSet._set.size}
|
||||
counterDelNote={data.trashedSet._set.size}
|
||||
handleFilterButtonContextMenu={this.handleFilterButtonContextMenu.bind(this)}
|
||||
handleFilterButtonContextMenu={this.handleFilterButtonContextMenu.bind(
|
||||
this
|
||||
)}
|
||||
/>
|
||||
|
||||
<StorageList storageList={storageList} isFolded={isFolded} />
|
||||
<NavToggleButton isFolded={isFolded} handleToggleButtonClick={this.handleToggleButtonClick.bind(this)} />
|
||||
<NavToggleButton
|
||||
isFolded={isFolded}
|
||||
handleToggleButtonClick={this.handleToggleButtonClick.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
@@ -257,22 +293,26 @@ class SideNav extends React.Component {
|
||||
</div>
|
||||
<div styleName='tag-control-sortTagsBy'>
|
||||
<i className='fa fa-angle-down' />
|
||||
<select styleName='tag-control-sortTagsBy-select'
|
||||
<select
|
||||
styleName='tag-control-sortTagsBy-select'
|
||||
title={i18n.__('Select filter mode')}
|
||||
value={config.sortTagsBy}
|
||||
onChange={(e) => this.handleSortTagsByChange(e)}
|
||||
onChange={e => this.handleSortTagsByChange(e)}
|
||||
>
|
||||
<option title='Sort alphabetically'
|
||||
value='ALPHABETICAL'>{i18n.__('Alphabetically')}</option>
|
||||
<option title='Sort by update time'
|
||||
value='COUNTER'>{i18n.__('Counter')}</option>
|
||||
<option title='Sort alphabetically' value='ALPHABETICAL'>
|
||||
{i18n.__('Alphabetically')}
|
||||
</option>
|
||||
<option title='Sort by update time' value='COUNTER'>
|
||||
{i18n.__('Counter')}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div styleName='tagList'>
|
||||
{this.tagListComponent(data)}
|
||||
</div>
|
||||
<NavToggleButton isFolded={isFolded} handleToggleButtonClick={this.handleToggleButtonClick.bind(this)} />
|
||||
<div styleName='tagList'>{this.tagListComponent(data)}</div>
|
||||
<NavToggleButton
|
||||
isFolded={isFolded}
|
||||
handleToggleButtonClick={this.handleToggleButtonClick.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -280,82 +320,84 @@ class SideNav extends React.Component {
|
||||
return component
|
||||
}
|
||||
|
||||
tagListComponent () {
|
||||
tagListComponent() {
|
||||
const { data, location, config } = this.props
|
||||
const { colorPicker } = this.state
|
||||
const activeTags = this.getActiveTags(location.pathname)
|
||||
const relatedTags = this.getRelatedTags(activeTags, data.noteMap)
|
||||
let tagList = sortBy(data.tagNoteMap.map(
|
||||
(tag, name) => ({ name, size: tag.size, related: relatedTags.has(name) })
|
||||
).filter(
|
||||
tag => tag.size > 0
|
||||
), ['name'])
|
||||
let tagList = sortBy(
|
||||
data.tagNoteMap
|
||||
.map((tag, name) => ({
|
||||
name,
|
||||
size: tag.size,
|
||||
related: relatedTags.has(name)
|
||||
}))
|
||||
.filter(tag => tag.size > 0),
|
||||
['name']
|
||||
)
|
||||
if (config.ui.enableLiveNoteCounts && activeTags.length !== 0) {
|
||||
const notesTags = data.noteMap.map(note => note.tags)
|
||||
tagList = tagList.map(tag => {
|
||||
tag.size = notesTags.filter(tags => tags.includes(tag.name) && matchActiveTags(tags, activeTags)).length
|
||||
tag.size = notesTags.filter(
|
||||
tags => tags.includes(tag.name) && matchActiveTags(tags, activeTags)
|
||||
).length
|
||||
return tag
|
||||
})
|
||||
}
|
||||
if (config.sortTagsBy === 'COUNTER') {
|
||||
tagList = sortBy(tagList, item => (0 - item.size))
|
||||
tagList = sortBy(tagList, item => 0 - item.size)
|
||||
}
|
||||
if (config.ui.showOnlyRelatedTags && (relatedTags.size > 0)) {
|
||||
tagList = tagList.filter(
|
||||
tag => tag.related
|
||||
if (config.ui.showOnlyRelatedTags && relatedTags.size > 0) {
|
||||
tagList = tagList.filter(tag => tag.related)
|
||||
}
|
||||
return tagList.map(tag => {
|
||||
return (
|
||||
<TagListItem
|
||||
name={tag.name}
|
||||
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
||||
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
|
||||
handleContextMenu={this.handleTagContextMenu.bind(this)}
|
||||
isActive={
|
||||
this.getTagActive(location.pathname, tag.name) ||
|
||||
colorPicker.tagName === tag.name
|
||||
}
|
||||
isRelated={tag.related}
|
||||
key={tag.name}
|
||||
count={tag.size}
|
||||
color={config.coloredTags[tag.name]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
tagList.map(tag => {
|
||||
return (
|
||||
<TagListItem
|
||||
name={tag.name}
|
||||
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
||||
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
|
||||
handleContextMenu={this.handleTagContextMenu.bind(this)}
|
||||
isActive={this.getTagActive(location.pathname, tag.name) || (colorPicker.tagName === tag.name)}
|
||||
isRelated={tag.related}
|
||||
key={tag.name}
|
||||
count={tag.size}
|
||||
color={config.coloredTags[tag.name]}
|
||||
/>
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
getRelatedTags (activeTags, noteMap) {
|
||||
getRelatedTags(activeTags, noteMap) {
|
||||
if (activeTags.length === 0) {
|
||||
return new Set()
|
||||
}
|
||||
const relatedNotes = noteMap.map(
|
||||
note => ({key: note.key, tags: note.tags})
|
||||
).filter(
|
||||
note => activeTags.every(tag => note.tags.includes(tag))
|
||||
)
|
||||
const relatedNotes = noteMap
|
||||
.map(note => ({ key: note.key, tags: note.tags }))
|
||||
.filter(note => activeTags.every(tag => note.tags.includes(tag)))
|
||||
const relatedTags = new Set()
|
||||
relatedNotes.forEach(note => note.tags.map(tag => relatedTags.add(tag)))
|
||||
return relatedTags
|
||||
}
|
||||
|
||||
getTagActive (path, tag) {
|
||||
getTagActive(path, tag) {
|
||||
return this.getActiveTags(path).includes(tag)
|
||||
}
|
||||
|
||||
getActiveTags (path) {
|
||||
getActiveTags(path) {
|
||||
const pathSegments = path.split('/')
|
||||
const tags = pathSegments[pathSegments.length - 1]
|
||||
return (tags === 'alltags')
|
||||
? []
|
||||
: decodeURIComponent(tags).split(' ')
|
||||
return tags === 'alltags' ? [] : decodeURIComponent(tags).split(' ')
|
||||
}
|
||||
|
||||
handleClickTagListItem (name) {
|
||||
handleClickTagListItem(name) {
|
||||
const { dispatch } = this.props
|
||||
dispatch(push(`/tags/${encodeURIComponent(name)}`))
|
||||
}
|
||||
|
||||
handleSortTagsByChange (e) {
|
||||
handleSortTagsByChange(e) {
|
||||
const { dispatch } = this.props
|
||||
|
||||
const config = {
|
||||
@@ -369,7 +411,7 @@ class SideNav extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleClickNarrowToTag (tag) {
|
||||
handleClickNarrowToTag(tag) {
|
||||
const { dispatch, location } = this.props
|
||||
const listOfTags = this.getActiveTags(location.pathname)
|
||||
const indexOfTag = listOfTags.indexOf(tag)
|
||||
@@ -381,33 +423,38 @@ class SideNav extends React.Component {
|
||||
dispatch(push(`/tags/${encodeURIComponent(listOfTags.join(' '))}`))
|
||||
}
|
||||
|
||||
emptyTrash (entries) {
|
||||
emptyTrash(entries) {
|
||||
const { dispatch } = this.props
|
||||
const deletionPromises = entries.map((note) => {
|
||||
const deletionPromises = entries.map(note => {
|
||||
return dataApi.deleteNote(note.storage, note.key)
|
||||
})
|
||||
const { confirmDeletion } = this.props.config.ui
|
||||
if (!confirmDeleteNote(confirmDeletion, true)) return
|
||||
Promise.all(deletionPromises)
|
||||
.then((arrayOfStorageAndNoteKeys) => {
|
||||
arrayOfStorageAndNoteKeys.forEach(({ storageKey, noteKey }) => {
|
||||
dispatch({ type: 'DELETE_NOTE', storageKey, noteKey })
|
||||
.then(arrayOfStorageAndNoteKeys => {
|
||||
arrayOfStorageAndNoteKeys.forEach(({ storageKey, noteKey }) => {
|
||||
dispatch({ type: 'DELETE_NOTE', storageKey, noteKey })
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Cannot Delete note: ' + err)
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Cannot Delete note: ' + err)
|
||||
})
|
||||
}
|
||||
|
||||
handleFilterButtonContextMenu (event) {
|
||||
handleFilterButtonContextMenu(event) {
|
||||
const { data } = this.props
|
||||
const trashedNotes = data.trashedSet.toJS().map((uniqueKey) => data.noteMap.get(uniqueKey))
|
||||
const trashedNotes = data.trashedSet
|
||||
.toJS()
|
||||
.map(uniqueKey => data.noteMap.get(uniqueKey))
|
||||
context.popup([
|
||||
{ label: i18n.__('Empty Trash'), click: () => this.emptyTrash(trashedNotes) }
|
||||
{
|
||||
label: i18n.__('Empty Trash'),
|
||||
click: () => this.emptyTrash(trashedNotes)
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { data, location, config, dispatch } = this.props
|
||||
const { colorPicker: colorPickerState } = this.state
|
||||
|
||||
@@ -415,16 +462,18 @@ class SideNav extends React.Component {
|
||||
|
||||
const storageList = data.storageMap.map((storage, key) => {
|
||||
const SortableStorageItem = SortableContainer(StorageItem)
|
||||
return <SortableStorageItem
|
||||
key={storage.key}
|
||||
storage={storage}
|
||||
data={data}
|
||||
location={location}
|
||||
isFolded={isFolded}
|
||||
dispatch={dispatch}
|
||||
onSortEnd={this.onSortEnd.bind(this)(storage)}
|
||||
useDragHandle
|
||||
/>
|
||||
return (
|
||||
<SortableStorageItem
|
||||
key={storage.key}
|
||||
storage={storage}
|
||||
data={data}
|
||||
location={location}
|
||||
isFolded={isFolded}
|
||||
dispatch={dispatch}
|
||||
onSortEnd={this.onSortEnd.bind(this)(storage)}
|
||||
useDragHandle
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
let colorPicker
|
||||
@@ -444,15 +493,22 @@ class SideNav extends React.Component {
|
||||
if (!isFolded) style.width = this.props.width
|
||||
const isTagActive = /tag/.test(location.pathname)
|
||||
return (
|
||||
<div className='SideNav'
|
||||
<div
|
||||
className='SideNav'
|
||||
styleName={isFolded ? 'root--folded' : 'root'}
|
||||
tabIndex='1'
|
||||
style={style}
|
||||
>
|
||||
<div styleName='top'>
|
||||
<div styleName='switch-buttons'>
|
||||
<ListButton onClick={this.handleSwitchFoldersButtonClick.bind(this)} isTagActive={isTagActive} />
|
||||
<TagButton onClick={this.handleSwitchTagsButtonClick.bind(this)} isTagActive={isTagActive} />
|
||||
<ListButton
|
||||
onClick={this.handleSwitchFoldersButtonClick.bind(this)}
|
||||
isTagActive={isTagActive}
|
||||
/>
|
||||
<TagButton
|
||||
onClick={this.handleSwitchTagsButtonClick.bind(this)}
|
||||
isTagActive={isTagActive}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<PreferenceButton onClick={this.handleMenuButtonClick} />
|
||||
|
||||
Reference in New Issue
Block a user