1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 09:46:22 +00:00

Merge branch 'master' into html-to-md

# Conflicts:
#	browser/main/modals/NewNoteModal.js
#	package-lock.json
#	package.json
#	yarn.lock
This commit is contained in:
AWolf81
2019-06-29 23:25:52 +02:00
249 changed files with 234965 additions and 4645 deletions

View File

@@ -23,7 +23,7 @@ body[data-theme="dark"]
border-left 1px solid $ui-dark-borderColor
.empty-message
color $ui-dark-inactive-text-color
body[data-theme="solarized-dark"]
.root
background-color $ui-solarized-dark-noteDetail-backgroundColor
@@ -37,3 +37,10 @@ body[data-theme="monokai"]
border-left 1px solid $ui-monokai-borderColor
.empty-message
color $ui-monokai-text-color
body[data-theme="dracula"]
.root
background-color $ui-dracula-noteDetail-backgroundColor
border-left 1px solid $ui-dracula-borderColor
.empty-message
color $ui-dracula-text-color

View File

@@ -36,7 +36,7 @@
height 34px
width 20px
line-height 34px
.search-input
vertical-align middle
position relative
@@ -71,7 +71,7 @@
overflow ellipsis
cursor pointer
&:hover
background-color $ui-button--hover-backgroundColor
background-color $ui-button--hover-backgroundColor
.search-optionList-item--active
@extend .search-optionList-item
@@ -159,3 +159,29 @@ body[data-theme="monokai"]
color $ui-monokai-button--active-color
.search-optionList-item-name-surfix
color $ui-monokai-inactive-text-color
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
&:hover
color #f8f8f2
background-color $ui-dark-button--hover-backgroundColor
border-color $ui-dracula-borderColor
.search-optionList
color #f8f8f2
border-color $ui-dracula-borderColor
background-color $ui-dracula-button-backgroundColor
.search-optionList-item
&:hover
background-color lighten($ui-dracula-button--hover-backgroundColor, 15%)
.search-optionList-item--active
background-color $ui-dracula-button--active-backgroundColor
color $ui-dracula-button--active-color
&:hover
background-color $ui-dark-button--hover-backgroundColor
color $ui-dracula-button--active-color
.search-optionList-item-name-surfix
color $ui-dracula-inactive-text-color

View File

@@ -4,14 +4,18 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './FullscreenButton.styl'
import i18n from 'browser/lib/i18n'
const OSX = global.process.platform === 'darwin'
const FullscreenButton = ({
onClick
}) => (
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')} onMouseDown={(e) => onClick(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
<span styleName='tooltip'>{i18n.__('Fullscreen')}</span>
</button>
)
}) => {
const hotkey = (OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')) + '+B'
return (
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')} onMouseDown={(e) => onClick(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Fullscreen')}({hotkey})</span>
</button>
)
}
FullscreenButton.propTypes = {
onClick: PropTypes.func.isRequired

View File

@@ -17,6 +17,10 @@
opacity 0
transition 0.1s
.tooltip:lang(ja)
@extend .tooltip
right 35px
body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()

View File

@@ -14,7 +14,7 @@ class InfoPanel extends React.Component {
render () {
const {
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, wordCount, letterCount, type, print
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, exportAsPdf, wordCount, letterCount, type, print
} = this.props
return (
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
@@ -70,22 +70,27 @@ class InfoPanel extends React.Component {
<hr />
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
<i className='fa fa-file-code-o' />
<p>{i18n.__('.md')}</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
<i className='fa fa-file-text-o' />
<p>{i18n.__('.txt')}</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
<i className='fa fa-html5' />
<p>{i18n.__('.html')}</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e)}>
<button styleName='export--enable' onClick={(e) => exportAsPdf(e, 'export-pdf')}>
<i className='fa fa-file-pdf-o' />
<p>{i18n.__('.pdf')}</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e, 'print')}>
<i className='fa fa-print' />
<p>{i18n.__('Print')}</p>
</button>
@@ -104,6 +109,7 @@ InfoPanel.propTypes = {
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired,
exportAsPdf: PropTypes.func.isRequired,
wordCount: PropTypes.number,
letterCount: PropTypes.number,
type: PropTypes.string.isRequired,

View File

@@ -11,10 +11,11 @@
.control-infoButton-panel
z-index 200
margin-top 0px
top: 50px
right 25px
position absolute
padding 20px 25px 0 25px
width 300px
// width 300px
overflow auto
background-color $ui-noteList-backgroundColor
box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1)
@@ -32,6 +33,7 @@
.control-infoButton-panel-trash
z-index 200
margin-top 0px
top 50px
right 0px
position absolute
padding 20px 25px 0 25px
@@ -255,3 +257,43 @@ body[data-theme="monokai"]
color $ui-dark-inactive-text-color
&:hover
color $ui-monokai-text-color
body[data-theme="dracula"]
.control-infoButton-panel
background-color $ui-dracula-noteList-backgroundColor
.control-infoButton-panel-trash
background-color $ui-dracula-noteList-backgroundColor
.modification-date
color $ui-dracula-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-dracula-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-dracula-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-dracula-borderColor, 20%)
color $ui-dracula-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-dracula-borderColor, 20%)
color $ui-dracula-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-dracula-text-color

View File

@@ -5,7 +5,7 @@ import styles from './InfoPanel.styl'
import i18n from 'browser/lib/i18n'
const InfoPanelTrashed = ({
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, exportAsPdf
}) => (
<div className='infoPanel' styleName='control-infoButton-panel-trash' style={{display: 'none'}}>
<div>
@@ -31,22 +31,22 @@ const InfoPanelTrashed = ({
</div>
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
<i className='fa fa-file-code-o' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
<i className='fa fa-file-text-o' />
<p>.txt</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
<i className='fa fa-html5' />
<p>.html</p>
</button>
<button styleName='export--unable'>
<button styleName='export--enable' onClick={(e) => exportAsPdf(e, 'export-pdf')}>
<i className='fa fa-file-pdf-o' />
<p>.pdf</p>
</button>
@@ -61,7 +61,8 @@ InfoPanelTrashed.propTypes = {
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired
exportAsHtml: PropTypes.func.isRequired,
exportAsPdf: PropTypes.func.isRequired
}
export default CSSModules(InfoPanelTrashed, styles)

View File

@@ -9,7 +9,6 @@ import StarButton from './StarButton'
import TagSelect from './TagSelect'
import FolderSelect from './FolderSelect'
import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import markdown from 'browser/lib/markdownTextHelper'
import StatusBar from '../StatusBar'
@@ -29,6 +28,9 @@ import { formatDate } from 'browser/lib/date-formatter'
import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
import striptags from 'striptags'
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
import markdownToc from 'browser/lib/markdown-toc-generator'
import queryString from 'query-string'
import { replace } from 'connected-react-router'
class MarkdownNoteDetail extends React.Component {
constructor (props) {
@@ -38,15 +40,19 @@ class MarkdownNoteDetail extends React.Component {
isMovingNote: false,
note: Object.assign({
title: '',
content: ''
content: '',
linesHighlighted: []
}, props.note),
isLockButtonShown: false,
isLockButtonShown: props.config.editor.type !== 'SPLIT',
isLocked: false,
editorType: props.config.editor.type
editorType: props.config.editor.type,
switchPreview: props.config.editor.switchPreview
}
this.dispatchTimer = null
this.toggleLockButton = this.handleToggleLockButton.bind(this)
this.generateToc = () => this.handleGenerateToc()
}
focus () {
@@ -59,22 +65,41 @@ class MarkdownNoteDetail extends React.Component {
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
this.handleSwitchMode(reversedType)
})
ee.on('hotkey:deletenote', this.handleDeleteNote.bind(this))
ee.on('code:generate-toc', this.generateToc)
}
componentWillReceiveProps (nextProps) {
if (nextProps.note.key !== this.props.note.key && !this.state.isMovingNote) {
const isNewNote = nextProps.note.key !== this.props.note.key
const hasDeletedTags = nextProps.note.tags.length < this.props.note.tags.length
if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) {
if (this.saveQueue != null) this.saveNow()
this.setState({
note: Object.assign({}, nextProps.note)
note: Object.assign({linesHighlighted: []}, nextProps.note)
}, () => {
this.refs.content.reload()
if (this.refs.tags) this.refs.tags.reset()
})
}
// Focus content if using blur or double click
// --> Moved here from componentDidMount so a re-render during search won't set focus to the editor
const {switchPreview} = nextProps.config.editor
if (this.state.switchPreview !== switchPreview) {
this.setState({
switchPreview
})
if (switchPreview === 'BLUR' || switchPreview === 'DBL_CLICK') {
console.log('setting focus', switchPreview)
this.focus()
}
}
}
componentWillUnmount () {
ee.off('topbar:togglelockbutton', this.toggleLockButton)
ee.off('code:generate-toc', this.generateToc)
if (this.saveQueue != null) this.saveNow()
}
@@ -87,7 +112,12 @@ class MarkdownNoteDetail extends React.Component {
handleUpdateContent () {
const { note } = this.state
note.content = this.refs.content.value
note.title = markdown.strip(striptags(findNoteTitle(note.content)))
let title = findNoteTitle(note.content, this.props.config.editor.enableFrontMatterTitle, this.props.config.editor.frontMatterTitleField)
title = striptags(title)
title = markdown.strip(title)
note.title = title
this.updateNote(note)
}
@@ -122,6 +152,7 @@ class MarkdownNoteDetail extends React.Component {
}
handleFolderChange (e) {
const { dispatch } = this.props
const { note } = this.state
const value = this.refs.folder.value
const splitted = value.split('-')
@@ -141,12 +172,12 @@ class MarkdownNoteDetail extends React.Component {
originNote: note,
note: newNote
})
hashHistory.replace({
dispatch(replace({
pathname: location.pathname,
query: {
search: queryString.stringify({
key: newNote.key
}
})
})
}))
this.setState({
isMovingNote: false
})
@@ -183,6 +214,40 @@ class MarkdownNoteDetail extends React.Component {
ee.emit('export:save-html')
}
exportAsPdf () {
ee.emit('export:save-pdf')
}
handleKeyDown (e) {
switch (e.keyCode) {
// tab key
case 9:
if (e.ctrlKey && !e.shiftKey) {
e.preventDefault()
this.jumpNextTab()
} else if (e.ctrlKey && e.shiftKey) {
e.preventDefault()
this.jumpPrevTab()
} else if (!e.ctrlKey && !e.shiftKey && e.target === this.refs.description) {
e.preventDefault()
this.focusEditor()
}
break
// I key
case 73:
{
const isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (isSuper) {
e.preventDefault()
this.handleInfoButtonClick(e)
}
}
break
}
}
handleTrashButtonClick (e) {
const { note } = this.state
const { isTrashed } = note
@@ -255,13 +320,18 @@ class MarkdownNoteDetail extends React.Component {
handleToggleLockButton (event, noteStatus) {
// first argument event is not used
if (this.props.config.editor.switchPreview === 'BLUR' && noteStatus === 'CODE') {
if (noteStatus === 'CODE') {
this.setState({isLockButtonShown: true})
} else {
this.setState({isLockButtonShown: false})
}
}
handleGenerateToc () {
const editor = this.refs.content.refs.code.editor
markdownToc.generateInEditor(editor)
}
handleFocus (e) {
this.focus()
}
@@ -276,16 +346,42 @@ class MarkdownNoteDetail extends React.Component {
}
handleSwitchMode (type) {
this.setState({ editorType: type }, () => {
// If in split mode, hide the lock button
this.setState({ editorType: type, isLockButtonShown: !(type === 'SPLIT') }, () => {
this.focus()
const newConfig = Object.assign({}, this.props.config)
newConfig.editor.type = type
ConfigManager.set(newConfig)
})
}
handleDeleteNote () {
this.handleTrashButtonClick()
}
handleClearTodo () {
const { note } = this.state
const splitted = note.content.split('\n')
const clearTodoContent = splitted.map((line) => {
const trimmedLine = line.trim()
if (trimmedLine.match(/\[x\]/i)) {
return line.replace(/\[x\]/i, '[ ]')
} else {
return line
}
}).join('\n')
note.content = clearTodoContent
this.refs.content.setValue(note.content)
this.updateNote(note)
}
renderEditor () {
const { config, ignorePreviewPointerEvents } = this.props
const { note } = this.state
if (this.state.editorType === 'EDITOR_PREVIEW') {
return <MarkdownEditor
ref='content'
@@ -294,7 +390,9 @@ class MarkdownNoteDetail extends React.Component {
value={note.content}
storageKey={note.storage}
noteKey={note.key}
linesHighlighted={note.linesHighlighted}
onChange={this.handleUpdateContent.bind(this)}
isLocked={this.state.isLocked}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
} else {
@@ -304,6 +402,7 @@ class MarkdownNoteDetail extends React.Component {
value={note.content}
storageKey={note.storage}
noteKey={note.key}
linesHighlighted={note.linesHighlighted}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
@@ -311,7 +410,7 @@ class MarkdownNoteDetail extends React.Component {
}
render () {
const { data, location } = this.props
const { data, location, config } = this.props
const { note, editorType } = this.state
const storageKey = note.storage
const folderKey = note.folder
@@ -344,6 +443,7 @@ class MarkdownNoteDetail extends React.Component {
exportAsHtml={this.exportAsHtml}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsPdf={this.exportAsPdf}
/>
</div>
</div>
@@ -362,9 +462,13 @@ class MarkdownNoteDetail extends React.Component {
<TagSelect
ref='tags'
value={this.state.note.tags}
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
data={data}
onChange={this.handleUpdateTag.bind(this)}
coloredTags={config.coloredTags}
/>
<TodoListPercentage percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
<TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
</div>
<div styleName='info-right'>
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />
@@ -401,12 +505,13 @@ class MarkdownNoteDetail extends React.Component {
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
noteLink={`[${note.title}](:note:${location.query.key})`}
noteLink={`[${note.title}](:note:${queryString.parse(location.search).key})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsHtml={this.exportAsHtml}
exportAsPdf={this.exportAsPdf}
wordCount={note.content.split(' ').length}
letterCount={note.content.replace(/\r?\n/g, '').length}
type={note.type}
@@ -419,6 +524,7 @@ class MarkdownNoteDetail extends React.Component {
<div className='NoteDetail'
style={this.props.style}
styleName='root'
onKeyDown={(e) => this.handleKeyDown(e)}
>
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}

View File

@@ -76,3 +76,16 @@ body[data-theme="monokai"]
.root
border-left 1px solid $ui-monokai-borderColor
background-color $ui-monokai-noteDetail-backgroundColor
body[data-theme="dracula"]
.root
border-left 1px solid $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor
div
> button, div
-webkit-user-drag none
user-select none
> img, span
-webkit-user-drag none
user-select none

View File

@@ -13,6 +13,7 @@ $info-margin-under-border = 30px
display flex
align-items center
padding 0 20px
z-index 99
.info-left
padding 0 10px
@@ -97,8 +98,13 @@ body[data-theme="solarized-dark"]
.info
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteDetail-backgroundColor
body[data-theme="monokai"]
.info
border-color $ui-monokai-borderColor
background-color $ui-monokai-noteDetail-backgroundColor
background-color $ui-monokai-noteDetail-backgroundColor
body[data-theme="dracula"]
.info
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor

View File

@@ -8,7 +8,6 @@ import StarButton from './StarButton'
import TagSelect from './TagSelect'
import FolderSelect from './FolderSelect'
import dataApi from 'browser/main/lib/dataApi'
import {hashHistory} from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
@@ -18,8 +17,8 @@ import context from 'browser/lib/context'
import ConfigManager from 'browser/main/lib/ConfigManager'
import _ from 'lodash'
import {findNoteTitle} from 'browser/lib/findNoteTitle'
import convertModeName from 'browser/lib/convertModeName'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import FullscreenButton from './FullscreenButton'
import TrashButton from './TrashButton'
import RestoreButton from './RestoreButton'
import PermanentDeleteButton from './PermanentDeleteButton'
@@ -29,10 +28,13 @@ import InfoPanelTrashed from './InfoPanelTrashed'
import { formatDate } from 'browser/lib/date-formatter'
import i18n from 'browser/lib/i18n'
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
import markdownToc from 'browser/lib/markdown-toc-generator'
import queryString from 'query-string'
import { replace } from 'connected-react-router'
const electron = require('electron')
const { remote } = electron
const { Menu, MenuItem, dialog } = remote
const { dialog } = remote
class SnippetNoteDetail extends React.Component {
constructor (props) {
@@ -47,11 +49,12 @@ class SnippetNoteDetail extends React.Component {
note: Object.assign({
description: ''
}, props.note, {
snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet))
snippets: props.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet))
})
}
this.scrollToNextTabThreshold = 0.7
this.generateToc = () => this.handleGenerateToc()
}
componentDidMount () {
@@ -65,6 +68,7 @@ class SnippetNoteDetail extends React.Component {
enableLeftArrow: allTabs.offsetLeft !== 0
})
}
ee.on('code:generate-toc', this.generateToc)
}
componentWillReceiveProps (nextProps) {
@@ -73,8 +77,9 @@ class SnippetNoteDetail extends React.Component {
const nextNote = Object.assign({
description: ''
}, nextProps.note, {
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
snippets: nextProps.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet))
})
this.setState({
snippetIndex: 0,
note: nextNote
@@ -91,6 +96,16 @@ class SnippetNoteDetail extends React.Component {
componentWillUnmount () {
if (this.saveQueue != null) this.saveNow()
ee.off('code:generate-toc', this.generateToc)
}
handleGenerateToc () {
const { note, snippetIndex } = this.state
const currentMode = note.snippets[snippetIndex].mode
if (currentMode.includes('Markdown')) {
const currentEditor = this.refs[`code-${snippetIndex}`].refs.code.editor
markdownToc.generateInEditor(currentEditor)
}
}
handleChange (e) {
@@ -99,7 +114,7 @@ class SnippetNoteDetail extends React.Component {
if (this.refs.tags) note.tags = this.refs.tags.value
note.description = this.refs.description.value
note.updatedAt = new Date()
note.title = findNoteTitle(note.description)
note.title = findNoteTitle(note.description, false)
this.setState({
note
@@ -151,12 +166,12 @@ class SnippetNoteDetail extends React.Component {
originNote: note,
note: newNote
})
hashHistory.replace({
dispatch(replace({
pathname: location.pathname,
query: {
search: queryString.stringify({
key: newNote.key
}
})
})
}))
this.setState({
isMovingNote: false
})
@@ -341,12 +356,10 @@ class SnippetNoteDetail extends React.Component {
this.refs['code-' + this.state.snippetIndex].reload()
if (this.visibleTabs.offsetWidth > this.allTabs.scrollWidth) {
console.log('no need for arrows')
this.moveTabBarBy(0)
} else {
const lastTab = this.allTabs.lastChild
if (lastTab.offsetLeft + lastTab.offsetWidth < this.visibleTabs.offsetWidth) {
console.log('need to scroll')
const width = this.visibleTabs.offsetWidth
const newLeft = lastTab.offsetLeft + lastTab.offsetWidth - width
this.moveTabBarBy(newLeft > 0 ? -newLeft : 0)
@@ -399,6 +412,8 @@ class SnippetNoteDetail extends React.Component {
return (e) => {
const snippets = this.state.note.snippets.slice()
snippets[index].content = this.refs['code-' + index].value
snippets[index].linesHighlighted = e.options.linesHighlighted
this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})}))
this.setState(state => ({
note: state.note
@@ -423,6 +438,18 @@ class SnippetNoteDetail extends React.Component {
this.focusEditor()
}
break
// I key
case 73:
{
const isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (isSuper) {
e.preventDefault()
this.handleInfoButtonClick(e)
}
}
break
// L key
case 76:
{
@@ -441,7 +468,7 @@ class SnippetNoteDetail extends React.Component {
const isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (isSuper) {
if (isSuper && !e.shiftKey && !e.altKey) {
e.preventDefault()
this.addSnippet()
}
@@ -451,14 +478,14 @@ class SnippetNoteDetail extends React.Component {
}
handleModeButtonClick (e, index) {
const menu = new Menu()
const templetes = []
CodeMirror.modeInfo.sort(function (a, b) { return a.name.localeCompare(b.name) }).forEach((mode) => {
menu.append(new MenuItem({
templetes.push({
label: mode.name,
click: (e) => this.handleModeOptionClick(index, mode.name)(e)
}))
})
})
menu.popup(remote.getCurrentWindow())
context.popup(templetes)
}
handleIndentTypeButtonClick (e) {
@@ -573,12 +600,16 @@ class SnippetNoteDetail extends React.Component {
}
addSnippet () {
const { config: { editor: { snippetDefaultLanguage } } } = this.props
const { note } = this.state
const defaultLanguage = snippetDefaultLanguage === 'Auto Detect' ? null : snippetDefaultLanguage
note.snippets = note.snippets.concat([{
name: '',
mode: 'Plain Text',
content: ''
mode: defaultLanguage,
content: '',
linesHighlighted: []
}])
const snippetIndex = note.snippets.length - 1
@@ -613,7 +644,6 @@ class SnippetNoteDetail extends React.Component {
}
focusEditor () {
console.log('code-' + this.state.snippetIndex)
this.refs['code-' + this.state.snippetIndex].focus()
}
@@ -622,11 +652,19 @@ class SnippetNoteDetail extends React.Component {
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
}
showWarning () {
showWarning (e, msg) {
const warningMessage = (msg) => ({
'export-txt': 'Text export',
'export-md': 'Markdown export',
'export-html': 'HTML export',
'export-pdf': 'PDF export',
'print': 'Print'
})[msg]
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: i18n.__('Sorry!'),
detail: i18n.__('md/text import is available only a markdown note.'),
detail: i18n.__(warningMessage(msg) + ' is available only in markdown notes.'),
buttons: [i18n.__('OK')]
})
}
@@ -638,6 +676,8 @@ class SnippetNoteDetail extends React.Component {
const storageKey = note.storage
const folderKey = note.folder
const autoDetect = config.editor.snippetDefaultLanguage === 'Auto Detect'
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
let editorIndentSize = parseInt(config.editor.indentSize, 10)
@@ -662,10 +702,6 @@ class SnippetNoteDetail extends React.Component {
const viewList = note.snippets.map((snippet, index) => {
const isActive = this.state.snippetIndex === index
let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
return <div styleName='tabView'
key={index}
style={{zIndex: isActive ? 5 : 4}}
@@ -674,25 +710,34 @@ class SnippetNoteDetail extends React.Component {
? <MarkdownEditor styleName='tabView-content'
value={snippet.content}
config={config}
linesHighlighted={snippet.linesHighlighted}
onChange={(e) => this.handleCodeChange(index)(e)}
ref={'code-' + index}
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
storageKey={storageKey}
/>
: <CodeEditor styleName='tabView-content'
mode={snippet.mode}
mode={snippet.mode || (autoDetect ? null : config.editor.snippetDefaultLanguage)}
value={snippet.content}
linesHighlighted={snippet.linesHighlighted}
theme={config.editor.theme}
fontFamily={config.editor.fontFamily}
fontSize={editorFontSize}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
displayLineNumbers={config.editor.displayLineNumbers}
matchingPairs={config.editor.matchingPairs}
matchingTriples={config.editor.matchingTriples}
explodingPairs={config.editor.explodingPairs}
keyMap={config.editor.keyMap}
scrollPastEnd={config.editor.scrollPastEnd}
fetchUrlTitle={config.editor.fetchUrlTitle}
enableTableEditor={config.editor.enableTableEditor}
onChange={(e) => this.handleCodeChange(index)(e)}
ref={'code-' + index}
enableSmartPaste={config.editor.enableSmartPaste}
hotkey={config.hotkey}
autoDetect={autoDetect}
/>
}
</div>
@@ -726,6 +771,7 @@ class SnippetNoteDetail extends React.Component {
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
exportAsHtml={this.showWarning}
exportAsPdf={this.showWarning}
/>
</div>
</div>
@@ -744,7 +790,11 @@ class SnippetNoteDetail extends React.Component {
<TagSelect
ref='tags'
value={this.state.note.tags}
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
data={data}
onChange={(e) => this.handleChange(e)}
coloredTags={config.coloredTags}
/>
</div>
<div styleName='info-right'>
@@ -753,11 +803,7 @@ class SnippetNoteDetail extends React.Component {
isActive={note.isStarred}
/>
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')}
onMouseDown={(e) => this.handleFullScreenButton(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
<span styleName='tooltip'>{i18n.__('Fullscreen')}</span>
</button>
<FullscreenButton onClick={(e) => this.handleFullScreenButton(e)} />
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
@@ -768,12 +814,15 @@ class SnippetNoteDetail extends React.Component {
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
noteLink={`[${note.title}](:note:${location.query.key})`}
noteLink={`[${note.title}](:note:${queryString.parse(location.search).key})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
exportAsHtml={this.showWarning}
exportAsPdf={this.showWarning}
type={note.type}
print={this.showWarning}
/>
</div>
</div>

View File

@@ -31,7 +31,7 @@
.tabList
absolute left right
top 55px
top 70px
height 30px
display flex
background-color $ui-noteDetail-backgroundColor
@@ -57,6 +57,9 @@
.tabList .tabButton
navWhiteButtonColor()
width 30px
border-left 1px solid $ui-borderColor
border-top 1px solid $ui-borderColor
border-right 1px solid $ui-borderColor
.tabView
absolute left right bottom
@@ -98,17 +101,34 @@
opacity 0
transition 0.1s
body[data-theme="white"]
body[data-theme="white"], body[data-theme="default"]
.root
box-shadow $note-detail-box-shadow
border none
.tabButton
&:hover
background-color alpha($ui-button--active-backgroundColor, 20%)
color $ui-text-color
transition 0.15s
body[data-theme="dark"]
.root
border-left 1px solid $ui-dark-borderColor
background-color $ui-dark-noteDetail-backgroundColor
box-shadow none
.tabList .tabButton
border-color $ui-dark-borderColor
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
.tabButton
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
transition 0.15s
.body
background-color $ui-dark-noteDetail-backgroundColor
@@ -118,7 +138,6 @@ body[data-theme="dark"]
border 1px solid $ui-dark-borderColor
.tabList
background-color $ui-button--active-backgroundColor
background-color $ui-dark-noteDetail-backgroundColor
.tabList .list
@@ -150,6 +169,15 @@ body[data-theme="solarized-dark"]
color $ui-solarized-dark-text-color
border 1px solid $ui-solarized-dark-borderColor
.tabList .tabButton
border-color $ui-solarized-dark-borderColor
.tabButton
&:hover
color $ui-solarized-dark-button--active-color
background-color $ui-solarized-dark-noteDetail-backgroundColor
transition 0.15s
.tabList
background-color $ui-solarized-dark-noteDetail-backgroundColor
color $ui-solarized-dark-text-color
@@ -167,6 +195,39 @@ body[data-theme="monokai"]
color $ui-monokai-text-color
border 1px solid $ui-monokai-borderColor
.tabList .tabButton
border-color $ui-monokai-borderColor
.tabButton
&:hover
color $ui-monokai-text-color
background-color $ui-monokai-noteDetail-backgroundColor
.tabList
background-color $ui-monokai-noteDetail-backgroundColor
color $ui-monokai-text-color
color $ui-monokai-text-color
body[data-theme="dracula"]
.root
border-left 1px solid $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor
.body
background-color $ui-dracula-noteDetail-backgroundColor
.body .description textarea
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color
border 1px solid $ui-dracula-borderColor
.tabList .tabButton
border-color $ui-dracula-borderColor
.tabButton
&:hover
color $ui-dracula-text-color
background-color $ui-dracula-noteDetail-backgroundColor
.tabList
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color

View File

@@ -54,7 +54,7 @@ class StarButton extends React.Component {
: '../resources/icon/icon-star.svg'
}
/>
<span styleName='tooltip'>{i18n.__('Star')}</span>
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Star')}</span>
</button>
)
}

View File

@@ -21,6 +21,11 @@
opacity 0
transition 0.1s
.tooltip:lang(ja)
@extend .tooltip
right 103px
width 70px
.root--active
@extend .root
transition 0.15s

View File

@@ -1,65 +1,39 @@
import PropTypes from 'prop-types'
import React from 'react'
import invertColor from 'invert-color'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TagSelect.styl'
import _ from 'lodash'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import i18n from 'browser/lib/i18n'
import ee from 'browser/main/lib/eventEmitter'
import Autosuggest from 'react-autosuggest'
class TagSelect extends React.Component {
constructor (props) {
super(props)
this.state = {
newTag: ''
newTag: '',
suggestions: []
}
this.handleAddTag = this.handleAddTag.bind(this)
this.onInputBlur = this.onInputBlur.bind(this)
this.onInputChange = this.onInputChange.bind(this)
this.onInputKeyDown = this.onInputKeyDown.bind(this)
this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this)
this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this)
this.onSuggestionSelected = this.onSuggestionSelected.bind(this)
}
componentDidMount () {
this.value = this.props.value
}
componentDidUpdate () {
this.value = this.props.value
}
handleNewTagInputKeyDown (e) {
switch (e.keyCode) {
case 9:
e.preventDefault()
this.submitTag()
break
case 13:
this.submitTag()
break
case 8:
if (this.refs.newTag.value.length === 0) {
this.removeLastTag()
}
}
}
handleNewTagBlur (e) {
this.submitTag()
}
removeLastTag () {
this.removeTagByCallback((value) => {
value.pop()
})
}
reset () {
this.setState({
newTag: ''
})
}
submitTag () {
addNewTag (newTag) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')
let { value } = this.props
let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
newTag = newTag.charAt(0) === '#' ? newTag.substring(1) : newTag
newTag = newTag.trim().replace(/ +/g, '_')
if (newTag.charAt(0) === '#') {
newTag.substring(1)
}
if (newTag.length <= 0) {
this.setState({
@@ -68,11 +42,18 @@ class TagSelect extends React.Component {
return
}
let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
value.push(newTag)
value = _.uniq(value)
if (!_.includes(value, newTag)) {
value.push(newTag)
}
if (this.props.saveTagsAlphabetically) {
value = _.sortBy(value)
}
this.setState({
newTag: ''
@@ -82,10 +63,41 @@ class TagSelect extends React.Component {
})
}
handleNewTagInputChange (e) {
this.setState({
newTag: this.refs.newTag.value
})
buildSuggestions () {
this.suggestions = _.sortBy(this.props.data.tagNoteMap.map(
(tag, name) => ({
name,
nameLC: name.toLowerCase(),
size: tag.size
})
).filter(
tag => tag.size > 0
), ['name'])
}
componentDidMount () {
this.value = this.props.value
this.buildSuggestions()
ee.on('editor:add-tag', this.handleAddTag)
}
componentDidUpdate () {
this.value = this.props.value
}
componentWillUnmount () {
ee.off('editor:add-tag', this.handleAddTag)
}
handleAddTag () {
this.refs.newTag.input.focus()
}
handleTagLabelClick (tag) {
const { router } = this.context
router.push(`/tags/${tag}`)
}
handleTagRemoveButtonClick (tag) {
@@ -94,6 +106,60 @@ class TagSelect extends React.Component {
}, tag)
}
onInputBlur (e) {
this.submitNewTag()
}
onInputChange (e, { newValue, method }) {
this.setState({
newTag: newValue
})
}
onInputKeyDown (e) {
switch (e.keyCode) {
case 9:
e.preventDefault()
this.submitNewTag()
break
case 13:
this.submitNewTag()
break
case 8:
if (this.state.newTag.length === 0) {
this.removeLastTag()
}
}
}
onSuggestionsClearRequested () {
this.setState({
suggestions: []
})
}
onSuggestionsFetchRequested ({ value }) {
const valueLC = value.toLowerCase()
const suggestions = _.filter(
this.suggestions,
tag => !_.includes(this.value, tag.name) && tag.nameLC.indexOf(valueLC) !== -1
)
this.setState({
suggestions
})
}
onSuggestionSelected (event, { suggestion, suggestionValue }) {
this.addNewTag(suggestionValue)
}
removeLastTag () {
this.removeTagByCallback((value) => {
value.pop()
})
}
removeTagByCallback (callback, tag = null) {
let { value } = this.props
@@ -107,26 +173,55 @@ class TagSelect extends React.Component {
this.props.onChange()
}
reset () {
this.buildSuggestions()
this.setState({
newTag: ''
})
}
submitNewTag () {
this.addNewTag(this.refs.newTag.input.value)
}
render () {
const { value, className } = this.props
const { value, className, showTagsAlphabetically, coloredTags } = this.props
const tagList = _.isArray(value)
? value.map((tag) => {
? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
const wrapperStyle = {}
const textStyle = {}
const BLACK = '#333333'
const WHITE = '#f1f1f1'
const color = coloredTags[tag]
const invertedColor = color && invertColor(color, { black: BLACK, white: WHITE })
let iconRemove = '../resources/icon/icon-x.svg'
if (color) {
wrapperStyle.backgroundColor = color
textStyle.color = invertedColor
}
if (invertedColor === WHITE) {
iconRemove = '../resources/icon/icon-x-light.svg'
}
return (
<span styleName='tag'
key={tag}
style={wrapperStyle}
>
<span styleName='tag-label'>#{tag}</span>
<span styleName='tag-label' style={textStyle} onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>
<button styleName='tag-removeButton'
onClick={(e) => this.handleTagRemoveButtonClick(tag)}
>
<img className='tag-removeButton-icon' src='../resources/icon/icon-x.svg' width='8px' />
<img className='tag-removeButton-icon' src={iconRemove} width='8px' />
</button>
</span>
)
})
: []
const { newTag, suggestions } = this.state
return (
<div className={_.isString(className)
? 'TagSelect ' + className
@@ -135,24 +230,40 @@ class TagSelect extends React.Component {
styleName='root'
>
{tagList}
<input styleName='newTag'
<Autosuggest
ref='newTag'
value={this.state.newTag}
placeholder={i18n.__('Add tag...')}
onChange={(e) => this.handleNewTagInputChange(e)}
onKeyDown={(e) => this.handleNewTagInputKeyDown(e)}
onBlur={(e) => this.handleNewTagBlur(e)}
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
getSuggestionValue={suggestion => suggestion.name}
renderSuggestion={suggestion => (
<div>
{suggestion.name}
</div>
)}
inputProps={{
placeholder: i18n.__('Add tag...'),
value: newTag,
onChange: this.onInputChange,
onKeyDown: this.onInputKeyDown,
onBlur: this.onInputBlur
}}
/>
</div>
)
}
}
TagSelect.contextTypes = {
router: PropTypes.shape({})
}
TagSelect.propTypes = {
className: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
onChange: PropTypes.func
onChange: PropTypes.func,
coloredTags: PropTypes.object
}
export default CSSModules(TagSelect, styles)

View File

@@ -3,19 +3,18 @@
align-items center
user-select none
vertical-align middle
width 100%
overflow-x scroll
width 96%
overflow-x auto
white-space nowrap
margin-top 31px
top 50px
position absolute
.root::-webkit-scrollbar
display none
&::-webkit-scrollbar
height 8px
.tag
display flex
align-items center
margin 0px 2px
margin 0px 2px 2px
padding 2px 4px
background-color alpha($ui-tag-backgroundColor, 3%)
border-radius 4px
@@ -39,16 +38,9 @@
.tag-label
font-size 13px
color: $ui-text-color
color $ui-text-color
padding 4px 16px 4px 8px
.newTag
box-sizing border-box
border none
background-color transparent
outline none
padding 0 4px
font-size 13px
cursor pointer
body[data-theme="dark"]
.tag
@@ -62,11 +54,6 @@ body[data-theme="dark"]
.tag-label
color $ui-dark-text-color
.newTag
border-color none
background-color transparent
color $ui-dark-text-color
body[data-theme="solarized-dark"]
.tag
background-color $ui-solarized-dark-tag-backgroundColor
@@ -78,14 +65,9 @@ body[data-theme="solarized-dark"]
.tag-label
color $ui-solarized-dark-text-color
.newTag
border-color none
background-color transparent
color $ui-solarized-dark-text-color
body[data-theme="monokai"]
.tag
background-color $ui-monokai-button-backgroundColor
background-color $ui-monokai-tag-backgroundColor
.tag-removeButton
border-color $ui-button--focus-borderColor
@@ -93,8 +75,14 @@ body[data-theme="monokai"]
.tag-label
color $ui-monokai-text-color
.newTag
border-color none
body[data-theme="dracula"]
.tag
background-color $ui-dracula-tag-backgroundColor
.tag-removeButton
border-color $ui-dracula-button--focus-borderColor
background-color transparent
color $ui-monokai-text-color
.tag-label
color $ui-dracula-borderColor

View File

@@ -1,26 +1,26 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ToggleModeButton.styl'
import i18n from 'browser/lib/i18n'
const ToggleModeButton = ({
onClick, editorType
}) => (
<div styleName='control-toggleModeButton'>
<div styleName={editorType === 'SPLIT' ? 'active' : 'non-active'} onClick={() => onClick('SPLIT')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : ''} />
</div>
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
</div>
<span styleName='tooltip'>{i18n.__('Toggle Mode')}</span>
</div>
)
ToggleModeButton.propTypes = {
onClick: PropTypes.func.isRequired,
editorType: PropTypes.string.Required
}
export default CSSModules(ToggleModeButton, styles)
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ToggleModeButton.styl'
import i18n from 'browser/lib/i18n'
const ToggleModeButton = ({
onClick, editorType
}) => (
<div styleName='control-toggleModeButton'>
<div styleName={editorType === 'SPLIT' ? 'active' : 'non-active'} onClick={() => onClick('SPLIT')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : ''} />
</div>
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
</div>
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Toggle Mode')}</span>
</div>
)
ToggleModeButton.propTypes = {
onClick: PropTypes.func.isRequired,
editorType: PropTypes.string.Required
}
export default CSSModules(ToggleModeButton, styles)

View File

@@ -40,6 +40,11 @@
opacity 0
transition 0.1s
.tooltip:lang(ja)
@extend .tooltip
left -8px
width 70px
body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()
@@ -59,7 +64,14 @@ body[data-theme="solarized-dark"]
body[data-theme="monokai"]
.control-toggleModeButton
background-color #272822
background-color #373831
.active
background-color #1EC38B
background-color #f92672
box-shadow 2px 0px 7px #222222
body[data-theme="dracula"]
.control-toggleModeButton
background-color #44475a
.active
background-color #bd93f9
box-shadow 2px 0px 7px #222222

View File

@@ -11,7 +11,7 @@ const TrashButton = ({
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
<span styleName='tooltip'>{i18n.__('Trash')}</span>
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Trash')}</span>
</button>
)

View File

@@ -17,6 +17,10 @@
opacity 0
transition 0.1s
.tooltip:lang(ja)
@extend .tooltip
right 46px
.control-trashButton--in-trash
top 60px
topBarButtonRight()

View File

@@ -8,6 +8,9 @@ import SnippetNoteDetail from './SnippetNoteDetail'
import ee from 'browser/main/lib/eventEmitter'
import StatusBar from '../StatusBar'
import i18n from 'browser/lib/i18n'
import debounceRender from 'react-debounce-render'
import searchFromNotes from 'browser/lib/search'
import queryString from 'query-string'
const OSX = global.process.platform === 'darwin'
@@ -34,11 +37,38 @@ class Detail extends React.Component {
}
render () {
const { location, data, config } = this.props
const { location, data, match: { params }, config } = this.props
const noteKey = location.search !== '' && queryString.parse(location.search).key
let note = null
if (location.query.key != null) {
const noteKey = location.query.key
note = data.noteMap.get(noteKey)
if (location.search !== '') {
const allNotes = data.noteMap.map(note => note)
const trashedNotes = data.trashedSet.toJS().map(uniqueKey => data.noteMap.get(uniqueKey))
let displayedNotes = allNotes
if (location.pathname.match(/\/searched/)) {
const searchStr = params.searchword
displayedNotes = searchStr === undefined || searchStr === '' ? allNotes
: searchFromNotes(allNotes, searchStr)
}
if (location.pathname.match(/\/tags/)) {
const listOfTags = params.tagname.split(' ')
displayedNotes = data.noteMap.map(note => note).filter(note =>
listOfTags.every(tag => note.tags.includes(tag))
)
}
if (location.pathname.match(/\/trashed/)) {
displayedNotes = trashedNotes
} else {
displayedNotes = _.differenceWith(displayedNotes, trashedNotes, (note, trashed) => note.key === trashed.key)
}
const noteKeys = displayedNotes.map(note => note.key)
if (noteKeys.includes(noteKey)) {
note = data.noteMap.get(noteKey)
}
}
if (note == null) {
@@ -99,4 +129,4 @@ Detail.propTypes = {
ignorePreviewPointerEvents: PropTypes.bool
}
export default CSSModules(Detail, styles)
export default debounceRender(CSSModules(Detail, styles))