From 2fc37d54f275f2f16c9263074b7ca52ac269c731 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 8 Nov 2018 13:19:46 +0100 Subject: [PATCH 01/65] fix height of mermaid's diagrams --- browser/components/MarkdownPreview.js | 18 +++++++++++++++++ browser/components/markdown.styl | 3 +++ browser/components/render/MermaidRender.js | 23 ++++++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index d9ff7074..90107ffd 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -205,6 +205,7 @@ export default class MarkdownPreview extends React.Component { this.saveAsMdHandler = () => this.handleSaveAsMd() this.saveAsHtmlHandler = () => this.handleSaveAsHtml() this.printHandler = () => this.handlePrint() + this.resizeHandler = _.throttle(this.handleResize.bind(this), 100) this.linkClickHandler = this.handlelinkClick.bind(this) this.initMarkdown = this.initMarkdown.bind(this) @@ -485,6 +486,10 @@ export default class MarkdownPreview extends React.Component { 'scroll', this.scrollHandler ) + this.refs.root.contentWindow.addEventListener( + 'resize', + this.resizeHandler + ) eventEmitter.on('export:save-text', this.saveAsTextHandler) eventEmitter.on('export:save-md', this.saveAsMdHandler) eventEmitter.on('export:save-html', this.saveAsHtmlHandler) @@ -520,6 +525,10 @@ export default class MarkdownPreview extends React.Component { 'scroll', this.scrollHandler ) + this.refs.root.contentWindow.removeEventListener( + 'resize', + this.resizeHandler + ) eventEmitter.off('export:save-text', this.saveAsTextHandler) eventEmitter.off('export:save-md', this.saveAsMdHandler) eventEmitter.off('export:save-html', this.saveAsHtmlHandler) @@ -797,6 +806,15 @@ export default class MarkdownPreview extends React.Component { ) } + handleResize () { + _.forEach( + this.refs.root.contentWindow.document.querySelectorAll('svg[ratio]'), + el => { + el.setAttribute('height', el.clientWidth / el.getAttribute('ratio')) + } + ) + } + focus () { this.refs.root.focus() } diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index b7f219b8..7b8911d7 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -416,6 +416,9 @@ pre.fence canvas, svg max-width 100% !important + svg[ratio] + width 100% + themeDarkBackground = darken(#21252B, 10%) themeDarkText = #f9f9f9 themeDarkBorder = lighten(themeDarkBackground, 20%) diff --git a/browser/components/render/MermaidRender.js b/browser/components/render/MermaidRender.js index 7a3b3ea2..c2380504 100644 --- a/browser/components/render/MermaidRender.js +++ b/browser/components/render/MermaidRender.js @@ -22,17 +22,36 @@ function getId () { function render (element, content, theme) { try { const height = element.attributes.getNamedItem('data-height') - if (height && height.value !== 'undefined') { + const isPredefined = height && height.value !== 'undefined' + if (isPredefined) { element.style.height = height.value + 'vh' } let isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula' mermaidAPI.initialize({ theme: isDarkTheme ? 'dark' : 'default', themeCSS: isDarkTheme ? darkThemeStyling : '', - useMaxWidth: false + gantt: { + useWidth: element.clientWidth + } }) mermaidAPI.render(getId(), content, (svgGraph) => { element.innerHTML = svgGraph + + if (!isPredefined) { + const el = element.firstChild + const viewBox = el.getAttribute('viewBox').split(' ') + + let ratio = viewBox[2] / viewBox[3] + + if (el.style.maxWidth) { + const maxWidth = parseFloat(el.style.maxWidth) + + ratio *= el.parentNode.clientWidth / maxWidth + } + + el.setAttribute('ratio', ratio) + el.setAttribute('height', el.parentNode.clientWidth / ratio) + } }) } catch (e) { element.className = 'mermaid-error' From b6b29e02f3a947095f904afe9e5dd066005daba0 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 8 Nov 2018 14:41:25 +0100 Subject: [PATCH 02/65] fix lint error --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 90107ffd..afab9a8d 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -807,7 +807,7 @@ export default class MarkdownPreview extends React.Component { } handleResize () { - _.forEach( + _.forEach( this.refs.root.contentWindow.document.querySelectorAll('svg[ratio]'), el => { el.setAttribute('height', el.clientWidth / el.getAttribute('ratio')) From 1cdac943bad5edae2dfa7f3b7e365b5a12707ec5 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 8 Feb 2019 00:50:47 +0100 Subject: [PATCH 03/65] adding Nord theme and streamlining UI theming --- browser/components/MarkdownPreview.js | 11 +- browser/components/NoteItem.styl | 200 ++++++------------ browser/components/NoteItemSimple.styl | 179 ++++++---------- browser/components/RealtimeNotification.styl | 44 ++-- browser/components/SideNavFilter.styl | 158 ++++---------- browser/components/SnippetTab.styl | 126 +++-------- browser/components/StorageItem.styl | 75 ++----- browser/components/TagListItem.styl | 45 ++-- browser/components/TodoListPercentage.styl | 32 ++- browser/components/markdown.styl | 172 ++++----------- browser/components/render/MermaidRender.js | 7 +- browser/lib/ui-themes.js | 39 ++++ browser/main/Detail/Detail.styl | 29 +-- browser/main/Detail/FolderSelect.styl | 73 +++---- browser/main/Detail/FullscreenButton.styl | 50 ++--- browser/main/Detail/InfoPanel.styl | 183 +++------------- browser/main/Detail/MarkdownNoteDetail.styl | 22 +- browser/main/Detail/NoteDetailInfo.styl | 21 +- browser/main/Detail/SnippetNoteDetail.styl | 93 +++----- browser/main/Detail/TagSelect.styl | 41 ++-- browser/main/Detail/ToggleModeButton.styl | 154 +++++++------- browser/main/Main.js | 5 +- browser/main/NewNoteButton/NewNoteButton.styl | 17 +- browser/main/NoteList/NoteList.styl | 114 +++------- browser/main/SideNav/PreferenceButton.styl | 102 +++++---- browser/main/SideNav/SideNav.styl | 21 +- browser/main/SideNav/StorageItem.styl | 90 ++++---- browser/main/SideNav/SwitchButton.styl | 118 +++++------ browser/main/StatusBar/StatusBar.styl | 36 ++-- browser/main/TopBar/TopBar.styl | 84 ++------ browser/main/global.styl | 51 ++--- browser/main/lib/ConfigManager.js | 13 +- browser/main/modals/CreateFolderModal.styl | 126 +++-------- browser/main/modals/NewNoteModal.styl | 80 ++----- .../modals/PreferencesModal/ConfigTab.styl | 118 +++-------- .../modals/PreferencesModal/Crowdfunding.styl | 25 +-- .../modals/PreferencesModal/FolderItem.styl | 83 ++------ .../main/modals/PreferencesModal/InfoTab.styl | 30 +-- .../PreferencesModal/PreferencesModal.styl | 121 +++-------- .../modals/PreferencesModal/SnippetTab.styl | 81 ++----- .../modals/PreferencesModal/StoragesTab.styl | 139 +++--------- browser/main/modals/PreferencesModal/UiTab.js | 21 +- browser/main/modals/RenameFolderModal.styl | 36 ++-- browser/styles/Detail/TagSelect.styl | 94 +++----- browser/styles/index.styl | 139 ++++++------ 45 files changed, 1192 insertions(+), 2306 deletions(-) create mode 100644 browser/lib/ui-themes.js diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index a6819ce9..2cde0fb7 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -24,6 +24,7 @@ import fs from 'fs' import { render } from 'react-dom' import Carousel from 'react-image-carousel' import ConfigManager from '../main/lib/ConfigManager' +import uiThemes from 'browser/lib/ui-themes' const { remote, shell } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') @@ -426,15 +427,7 @@ export default class MarkdownPreview extends React.Component { getScrollBarStyle () { const { theme } = this.props - switch (theme) { - case 'dark': - case 'solarized-dark': - case 'monokai': - case 'dracula': - return scrollBarDarkStyle - default: - return scrollBarStyle - } + return uiThemes.some(item => item.name === theme && item.isDark) ? scrollBarDarkStyle : scrollBarStyle } componentDidMount () { diff --git a/browser/components/NoteItem.styl b/browser/components/NoteItem.styl index a31f00a4..75078db4 100644 --- a/browser/components/NoteItem.styl +++ b/browser/components/NoteItem.styl @@ -194,7 +194,7 @@ body[data-theme="dark"] color $ui-dark-text-color .item-bottom-tagList-item transition 0.15s - background-color alpha(#fff, 20%) + background-color alpha($ui-dark-tagList-backgroundColor, 20%) color $ui-dark-text-color &:active transition 0.15s @@ -207,7 +207,7 @@ body[data-theme="dark"] color $ui-dark-text-color .item-bottom-tagList-item transition 0.15s - background-color alpha(white, 10%) + background-color alpha($ui-dark-tagList-backgroundColor, 10%) color $ui-dark-text-color .item-wrapper @@ -223,13 +223,13 @@ body[data-theme="dark"] .item-bottom-time color $ui-dark-text-color .item-bottom-tagList-item - background-color alpha(white, 10%) + background-color alpha($ui-dark-tagList-backgroundColor, 10%) color $ui-dark-text-color &:hover background-color alpha($ui-dark-button--active-backgroundColor, 60%) - color #c0392b + color $ui-dark-button--hover-color .item-bottom-tagList-item - background-color alpha(#fff, 20%) + background-color alpha($ui-dark-tagList-backgroundColor, 20%) .item-title color $ui-inactive-text-color @@ -322,148 +322,82 @@ body[data-theme="solarized-dark"] color $ui-inactive-text-color vertical-align middle -body[data-theme="monokai"] - .root - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteList-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .root + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteList-backgroundColor') - .item - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteList-backgroundColor - &:hover - transition 0.15s - // background-color alpha($ui-monokai-noteList-backgroundColor, 20%) - color $ui-monokai-text-color - .item-title - .item-title-icon - .item-bottom-time + .item + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteList-backgroundColor') + &:hover transition 0.15s - color $ui-monokai-text-color - .item-bottom-tagList-item + // background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 20%) + color get-theme-var(theme, 'text-color') + .item-title + .item-title-icon + .item-bottom-time + transition 0.15s + color get-theme-var(theme, 'text-color') + .item-bottom-tagList-item + transition 0.15s + background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 20%) + color get-theme-var(theme, 'text-color') + &:active transition 0.15s - background-color alpha($ui-monokai-noteList-backgroundColor, 20%) - color $ui-monokai-text-color - &:active - transition 0.15s - background-color $ui-monokai-noteList-backgroundColor - color $ui-monokai-text-color - .item-title - .item-title-icon - .item-bottom-time - transition 0.15s - color $ui-monokai-text-color - .item-bottom-tagList-item - transition 0.15s - background-color alpha($ui-monokai-noteList-backgroundColor, 10%) - color $ui-monokai-text-color + background-color get-theme-var(theme, 'noteList-backgroundColor') + color get-theme-var(theme, 'text-color') + .item-title + .item-title-icon + .item-bottom-time + transition 0.15s + color get-theme-var(theme, 'text-color') + .item-bottom-tagList-item + transition 0.15s + background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 10%) + color get-theme-var(theme, 'text-color') - .item-wrapper - border-color alpha($ui-monokai-button-backgroundColor, 60%) - - .item--active - border-color $ui-monokai-borderColor - background-color $ui-monokai-button-backgroundColor .item-wrapper - border-color transparent - .item-title - .item-title-icon - .item-bottom-time - color $ui-monokai-active-color - .item-bottom-tagList-item - background-color alpha(white, 10%) - color $ui-monokai-text-color - &:hover - // background-color alpha($ui-monokai-button--active-backgroundColor, 60%) - color #f92672 - .item-bottom-tagList-item - background-color alpha(#fff, 20%) + border-color alpha(get-theme-var(theme, 'button-backgroundColor'), 60%) - .item-title - color $ui-inactive-text-color - - .item-title-icon - color $ui-inactive-text-color - - .item-title-empty - color $ui-inactive-text-color - - .item-bottom-tagList-item - background-color alpha($ui-dark-button--active-backgroundColor, 40%) - color $ui-inactive-text-color - - .item-bottom-tagList-empty - color $ui-inactive-text-color - vertical-align middle - -body[data-theme="dracula"] - .root - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteList-backgroundColor - - .item - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteList-backgroundColor - &:hover - transition 0.15s - // background-color alpha($ui-dracula-noteList-backgroundColor, 20%) - color $ui-dracula-text-color + .item--active + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'button-backgroundColor') + .item-wrapper + border-color transparent .item-title .item-title-icon .item-bottom-time - transition 0.15s - color $ui-dracula-text-color + color get-theme-var(theme, 'active-color') .item-bottom-tagList-item - transition 0.15s - background-color alpha($ui-dracula-noteList-backgroundColor, 20%) - color $ui-dracula-text-color - &:active - transition 0.15s - background-color $ui-dracula-noteList-backgroundColor - color $ui-dracula-text-color - .item-title - .item-title-icon - .item-bottom-time - transition 0.15s - color $ui-dracula-text-color - .item-bottom-tagList-item - transition 0.15s - background-color alpha($ui-dracula-noteList-backgroundColor, 10%) - color $ui-dracula-text-color + background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%) + color get-theme-var(theme, 'text-color') + &:hover + // background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%) + color get-theme-var(theme, 'button--hover-color') + .item-bottom-tagList-item + background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%) - .item-wrapper - border-color alpha($ui-dracula-button-backgroundColor, 60%) - - .item--active - border-color $ui-dracula-borderColor - background-color $ui-dracula-button-backgroundColor - .item-wrapper - border-color transparent .item-title + color $ui-inactive-text-color + .item-title-icon - .item-bottom-time - color $ui-dracula-active-color + color $ui-inactive-text-color + + .item-title-empty + color $ui-inactive-text-color + .item-bottom-tagList-item - background-color alpha(#f8f8f2, 10%) - color $ui-dracula-text-color - &:hover - // background-color alpha($ui-dracula-button--active-backgroundColor, 60%) - color #ff79c6 - .item-bottom-tagList-item - background-color alpha(#f8f8f2, 20%) + background-color alpha($ui-dark-button--active-backgroundColor, 40%) + color $ui-inactive-text-color - .item-title - color $ui-inactive-text-color + .item-bottom-tagList-empty + color $ui-inactive-text-color + vertical-align middle - .item-title-icon - color $ui-inactive-text-color +for theme in 'dracula' + apply-theme(theme) - .item-title-empty - color $ui-inactive-text-color - - .item-bottom-tagList-item - background-color alpha($ui-dark-button--active-backgroundColor, 40%) - color $ui-inactive-text-color - - .item-bottom-tagList-empty - color $ui-inactive-text-color - vertical-align middle \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/NoteItemSimple.styl b/browser/components/NoteItemSimple.styl index 70b74be7..a36de5ed 100644 --- a/browser/components/NoteItemSimple.styl +++ b/browser/components/NoteItemSimple.styl @@ -223,130 +223,73 @@ body[data-theme="solarized-dark"] padding-left 4px opacity 0.4 -body[data-theme="monokai"] - .root - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteList-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .root + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteList-backgroundColor') - .item-simple - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteList-backgroundColor - &:hover - transition 0.15s - background-color alpha($ui-monokai-button-backgroundColor, 60%) - color $ui-monokai-text-color + .item-simple + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteList-backgroundColor') + &:hover + transition 0.15s + background-color alpha(get-theme-var(theme, 'button-backgroundColor'), 60%) + color get-theme-var(theme, 'text-color') + .item-simple-title + .item-simple-title-empty + .item-simple-title-icon + .item-simple-bottom-time + transition 0.15s + color get-theme-var(theme, 'text-color') + .item-simple-bottom-tagList-item + transition 0.15s + background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%) + color get-theme-var(theme, 'text-color') + &:active + transition 0.15s + background-color get-theme-var(theme, 'button--active-backgroundColor') + color get-theme-var(theme, 'text-color') + .item-simple-title + .item-simple-title-empty + .item-simple-title-icon + .item-simple-bottom-time + transition 0.15s + color get-theme-var(theme, 'text-color') + .item-simple-bottom-tagList-item + transition 0.15s + background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%) + color get-theme-var(theme, 'text-color') + + .item-simple--active + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'button--active-backgroundColor') + .item-simple-wrapper + border-color transparent .item-simple-title .item-simple-title-empty .item-simple-title-icon .item-simple-bottom-time - transition 0.15s - color $ui-monokai-text-color + color get-theme-var(theme, 'text-color') .item-simple-bottom-tagList-item - transition 0.15s - background-color alpha(#fff, 20%) - color $ui-monokai-text-color - &:active - transition 0.15s - background-color $ui-monokai-button--active-backgroundColor - color $ui-monokai-text-color - .item-simple-title - .item-simple-title-empty - .item-simple-title-icon - .item-simple-bottom-time - transition 0.15s - color $ui-monokai-text-color - .item-simple-bottom-tagList-item - transition 0.15s - background-color alpha(white, 10%) - color $ui-monokai-text-color - - .item-simple--active - border-color $ui-monokai-borderColor - background-color $ui-monokai-button--active-backgroundColor - .item-simple-wrapper - border-color transparent + background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%) + color get-theme-var(theme, 'text-color') + &:hover + // background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%) + color #c0392b + .item-simple-bottom-tagList-item + background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%) .item-simple-title - .item-simple-title-empty - .item-simple-title-icon - .item-simple-bottom-time - color $ui-monokai-text-color - .item-simple-bottom-tagList-item - background-color alpha(white, 10%) - color $ui-monokai-text-color - &:hover - // background-color alpha($ui-dark-button--active-backgroundColor, 60%) - color #c0392b - .item-simple-bottom-tagList-item - background-color alpha(#fff, 20%) - .item-simple-title - color $ui-dark-text-color - border-bottom $ui-dark-borderColor - .item-simple-right - float right - .item-simple-right-storageName - padding-left 4px - opacity 0.4 + color $ui-dark-text-color + border-bottom $ui-dark-borderColor + .item-simple-right + float right + .item-simple-right-storageName + padding-left 4px + opacity 0.4 -body[data-theme="dracula"] - .root - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteList-backgroundColor +for theme in 'dracula' + apply-theme(theme) - .item-simple - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteList-backgroundColor - &:hover - transition 0.15s - background-color alpha($ui-dracula-button-backgroundColor, 60%) - color $ui-dracula-text-color - .item-simple-title - .item-simple-title-empty - .item-simple-title-icon - .item-simple-bottom-time - transition 0.15s - color $ui-dracula-text-color - .item-simple-bottom-tagList-item - transition 0.15s - background-color alpha(#f8f8f2, 20%) - color $ui-dracula-text-color - &:active - transition 0.15s - background-color $ui-dracula-button--active-backgroundColor - color $ui-dracula-text-color - .item-simple-title - .item-simple-title-empty - .item-simple-title-icon - .item-simple-bottom-time - transition 0.15s - color $ui-dracula-text-color - .item-simple-bottom-tagList-item - transition 0.15s - background-color alpha(#f8f8f2, 10%) - color $ui-dracula-text-color - - .item-simple--active - border-color $ui-dracula-borderColor - background-color $ui-dracula-button--active-backgroundColor - .item-simple-wrapper - border-color transparent - .item-simple-title - .item-simple-title-empty - .item-simple-title-icon - .item-simple-bottom-time - color $ui-dracula-text-color - .item-simple-bottom-tagList-item - background-color alpha(#f8f8f2, 10%) - color $ui-dracula-text-color - &:hover - // background-color alpha($ui-dark-button--active-backgroundColor, 60%) - color #c0392b - .item-simple-bottom-tagList-item - background-color alpha(#f8f8f2, 20%) - .item-simple-title - color $ui-dark-text-color - border-bottom $ui-dark-borderColor - .item-simple-right - float right - .item-simple-right-storageName - padding-left 4px - opacity 0.4 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/RealtimeNotification.styl b/browser/components/RealtimeNotification.styl index 36330c48..795807b9 100644 --- a/browser/components/RealtimeNotification.styl +++ b/browser/components/RealtimeNotification.styl @@ -30,36 +30,20 @@ body[data-theme="dark"] &:hover color #5CB85C +apply-theme(theme) + body[data-theme={theme}] + .notification-area + background-color none -body[data-theme="solarized-dark"] - .notification-area - background-color none + .notification-link + color get-theme-var(theme, 'text-color') + border none + background-color get-theme-var(theme, 'button-backgroundColor') + &:hover + color get-theme-var(theme, 'button--hover-color') - .notification-link - color $ui-solarized-dark-text-color - border none - background-color $ui-solarized-dark-button-backgroundColor - &:hover - color #5CB85C +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="monokai"] - .notification-area - background-color none - - .notification-link - color $ui-monokai-text-color - border none - background-color $ui-monokai-button-backgroundColor - &:hover - color #5CB85C - -body[data-theme="dracula"] - .notification-area - background-color none - - .notification-link - color $ui-dracula-text-color - border none - background-color $ui-dracula-button-backgroundColor - &:hover - color #ff79c6 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/SideNavFilter.styl b/browser/components/SideNavFilter.styl index 1da8c7e4..41e679ca 100644 --- a/browser/components/SideNavFilter.styl +++ b/browser/components/SideNavFilter.styl @@ -180,129 +180,51 @@ body[data-theme="dark"] .menu-button-label color $ui-dark-text-color +apply-theme(theme) + body[data-theme={theme}] + .menu-button + &:active + background-color get-theme-var(theme, 'noteList-backgroundColor') + color get-theme-var(theme, 'text-color') + &:hover + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') -body[data-theme="solarized-dark"] - .menu-button - &:active - background-color $ui-solarized-dark-noteList-backgroundColor - color $ui-solarized-dark-text-color - &:hover - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color - - .menu-button--active - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor - .menu-button-label - color $ui-solarized-dark-text-color - &:hover - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color + .menu-button--active + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') .menu-button-label - color $ui-solarized-dark-text-color + color get-theme-var(theme, 'text-color') + &:hover + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') + .menu-button-label + color get-theme-var(theme, 'text-color') - .menu-button-star--active - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor - .menu-button-label - color $ui-solarized-dark-text-color - &:hover - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color + .menu-button-star--active + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') .menu-button-label - color $ui-solarized-dark-text-color + color get-theme-var(theme, 'text-color') + &:hover + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') + .menu-button-label + color get-theme-var(theme, 'text-color') - .menu-button-trash--active - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor - .menu-button-label - color $ui-solarized-dark-text-color - &:hover - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color + .menu-button-trash--active + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') .menu-button-label - color $ui-solarized-dark-text-color + color get-theme-var(theme, 'text-color') + &:hover + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') + .menu-button-label + color get-theme-var(theme, 'text-color') -body[data-theme="monokai"] - .menu-button - &:active - background-color $ui-monokai-noteList-backgroundColor - color $ui-monokai-text-color - &:hover - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .menu-button--active - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor - .menu-button-label - color $ui-monokai-text-color - &:hover - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - .menu-button-label - color $ui-monokai-text-color - - .menu-button-star--active - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor - .menu-button-label - color $ui-monokai-text-color - &:hover - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - .menu-button-label - color $ui-monokai-text-color - - .menu-button-trash--active - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor - .menu-button-label - color $ui-monokai-text-color - &:hover - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - .menu-button-label - color $ui-monokai-text-color - -body[data-theme="dracula"] - .menu-button - &:active - background-color $ui-dracula-noteList-backgroundColor - color $ui-dracula-text-color - &:hover - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - - .menu-button--active - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - .menu-button-label - color $ui-dracula-text-color - &:hover - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - .menu-button-label - color $ui-dracula-text-color - - .menu-button-star--active - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - .menu-button-label - color $ui-dracula-text-color - &:hover - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - .menu-button-label - color $ui-dracula-text-color - - .menu-button-trash--active - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - .menu-button-label - color $ui-dracula-text-color - &:hover - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - .menu-button-label - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/SnippetTab.styl b/browser/components/SnippetTab.styl index d101f318..994e3b5a 100644 --- a/browser/components/SnippetTab.styl +++ b/browser/components/SnippetTab.styl @@ -61,7 +61,7 @@ width 100% outline none -body[data-theme="default"], body[data-theme="white"] +body[data-theme="default"], body[data-theme="white"] .root--active &:hover background-color alpha($ui-button--active-backgroundColor, 60%) @@ -100,103 +100,43 @@ body[data-theme="dark"] color $ui-dark-text-color transition 0.15s -body[data-theme="solarized-dark"] - .root - border-color $ui-solarized-dark-borderColor - &:hover - background-color $ui-solarized-dark-noteDetail-backgroundColor - transition 0.15s +apply-theme(theme) + body[data-theme={theme}] + .root + border-color get-theme-var(theme, 'borderColor') + &:hover + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + transition 0.15s + .deleteButton + color get-theme-var(theme, 'text-color') + transition 0.15s + .button + color get-theme-var(theme, 'text-color') + transition 0.15s + + .root--active + color get-theme-var(theme, 'active-color') + background-color get-theme-var(theme, 'button-backgroundColor') + border-color get-theme-var(theme, 'borderColor') .deleteButton - color $ui-solarized-dark-button--active-color - transition 0.15s + color get-theme-var(theme, 'text-color') .button - color $ui-solarized-dark-button--active-color - transition 0.15s + color get-theme-var(theme, 'active-color') - .root--active - color $ui-solarized-dark-button--active-color - background-color $ui-solarized-dark-button-backgroundColor - border-color $ui-solarized-dark-borderColor - .deleteButton - color $ui-solarized-dark-button--active-color .button - color $ui-solarized-dark-button--active-color + border none + color $ui-inactive-text-color + background-color transparent + transition color background-color 0.15s + border-left 4px solid transparent - .button - border none - color $ui-solarized-dark-text-color - background-color transparent - transition color background-color 0.15s - border-left 4px solid transparent - - .input - background-color $ui-solarized-dark-noteDetail-backgroundColor - color $ui-solarized-dark-button--active-color - transition 0.15s - -body[data-theme="monokai"] - .root - border-color $ui-monokai-borderColor - &:hover - background-color $ui-monokai-noteDetail-backgroundColor + .input + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + color get-theme-var(theme, 'text-color') transition 0.15s - .deleteButton - color $ui-monokai-text-color - transition 0.15s - .button - color $ui-monokai-text-color - transition 0.15s - .root--active - color $ui-monokai-active-color - background-color $ui-monokai-button-backgroundColor - border-color $ui-monokai-borderColor - .deleteButton - color $ui-monokai-text-color - .button - color $ui-monokai-active-color - - .button - border none - color $ui-inactive-text-color - background-color transparent - transition color background-color 0.15s - border-left 4px solid transparent +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .input - background-color $ui-monokai-noteDetail-backgroundColor - color $ui-monokai-text-color - transition 0.15s - -body[data-theme="dracula"] - .root - border-color $ui-dracula-borderColor - &:hover - background-color $ui-dracula-noteDetail-backgroundColor - transition 0.15s - .deleteButton - color $ui-dracula-text-color - transition 0.15s - .button - color $ui-dracula-text-color - transition 0.15s - - .root--active - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - border-color $ui-dracula-borderColor - .deleteButton - color $ui-dracula-text-color - .button - color $ui-dracula-active-color - - .button - border none - color $ui-inactive-text-color - background-color transparent - transition color background-color 0.15s - border-left 4px solid transparent - - .input - background-color $ui-dracula-noteDetail-backgroundColor - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/StorageItem.styl b/browser/components/StorageItem.styl index df8cbbc6..e0a3c6cd 100644 --- a/browser/components/StorageItem.styl +++ b/browser/components/StorageItem.styl @@ -120,59 +120,28 @@ body[data-theme="dark"] color $ui-dark-text-color background-color alpha($ui-dark-button--active-backgroundColor, 50%) -body[data-theme="solarized-dark"] - .folderList-item - &:hover - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color - &:active - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .folderList-item + &:hover + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') + &:active + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') - .folderList-item--active - @extend .folderList-item - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor - &:active - background-color $ui-solarized-dark-button-backgroundColor - &:hover - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor + .folderList-item--active + @extend .folderList-item + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') + &:active + background-color get-theme-var(theme, 'button-backgroundColor') + &:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') -body[data-theme="monokai"] - .folderList-item - &:hover - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - &:active - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .folderList-item--active - @extend .folderList-item - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor - &:active - background-color $ui-monokai-button-backgroundColor - &:hover - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor - -body[data-theme="dracula"] - .folderList-item - &:hover - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - &:active - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - - .folderList-item--active - @extend .folderList-item - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - &:active - background-color $ui-dracula-button-backgroundColor - &:hover - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/TagListItem.styl b/browser/components/TagListItem.styl index 9f407a17..1f78c46f 100644 --- a/browser/components/TagListItem.styl +++ b/browser/components/TagListItem.styl @@ -94,23 +94,30 @@ body[data-theme="white"] .tagList-item-count color $ui-text-color -body[data-theme="dark"] - .tagList-item - color $ui-dark-inactive-text-color - &:hover - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - &:active - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .tagList-item + color get-theme-var(theme, 'inactive-text-color') + &:hover + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%) + &:active + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') - .tagList-item-active - background-color $ui-dark-button--active-backgroundColor - color $ui-dark-text-color - &:active - background-color alpha($ui-dark-button--active-backgroundColor, 50%) - &:hover - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 50%) - .tagList-item-count - color $ui-dark-button--active-color + .tagList-item-active + background-color get-theme-var(theme, 'button--active-backgroundColor') + color get-theme-var(theme, 'text-color') + &:active + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 50%) + &:hover + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 50%) + .tagList-item-count + color get-theme-var(theme, 'button--active-color') + +for theme in 'dark' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/TodoListPercentage.styl b/browser/components/TodoListPercentage.styl index 5a0f3257..4a0081cd 100644 --- a/browser/components/TodoListPercentage.styl +++ b/browser/components/TodoListPercentage.styl @@ -54,7 +54,7 @@ body[data-theme="dark"] .percentageText color $ui-dark-text-color - + .todoClearText color $ui-dark-text-color @@ -71,25 +71,19 @@ body[data-theme="solarized-dark"] .todoClearText color #fdf6e3 -body[data-theme="monokai"] - .percentageBar - background-color: $ui-monokai-borderColor +apply-theme(theme) + body[data-theme={theme}] + .percentageBar + background-color: get-theme-var(theme, 'borderColor') - .progressBar - background-color $ui-monokai-active-color + .progressBar + background-color get-theme-var(theme, 'active-color') - .percentageText - color $ui-monokai-text-color + .percentageText + color get-theme-var(theme, 'text-color') -body[data-theme="dracula"] - .percentageBar - background-color $ui-dracula-borderColor +for theme in 'dracula' + apply-theme(theme) - .progressBar - background-color: $ui-dracula-active-color - - .percentageText - color $ui-dracula-text-color - - .percentageText - color $ui-dracula-text-color +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index 4921b531..ca1f9cdd 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -511,137 +511,51 @@ body[data-theme="dark"] color $ui-dark-text-color background-color $ui-dark-tag-backgroundColor -themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor -themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%) -themeSolarizedDarkTableHead = themeSolarizedDarkTableEven -themeSolarizedDarkTableBorder = themeDarkBorder - -body[data-theme="solarized-dark"] - color $ui-solarized-dark-text-color - border-color themeDarkBorder - background-color $ui-solarized-dark-noteDetail-backgroundColor - table - thead - tr - background-color themeSolarizedDarkTableHead - th - border-color themeSolarizedDarkTableBorder - &:last-child - border-right solid 1px themeSolarizedDarkTableBorder - tbody - tr:nth-child(2n + 1) - background-color themeSolarizedDarkTableOdd - tr:nth-child(2n) - background-color themeSolarizedDarkTableEven - td - border-color themeSolarizedDarkTableBorder - &:last-child - border-right solid 1px themeSolarizedDarkTableBorder - dl +apply-theme(theme) + body[data-theme={theme}] + color get-theme-var(theme, 'text-color') border-color themeDarkBorder - background-color themeSolarizedDarkTableHead - dt - border-color themeDarkBorder - dd - border-color themeDarkBorder - background-color $ui-solarized-dark-noteDetail-backgroundColor + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + table + thead + tr + background-color get-theme-var(theme, 'table-head-backgroundColor') + th + border-color get-theme-var(theme, 'table-borderColor') + &:last-child + border-right solid 1px get-theme-var(theme, 'table-borderColor') + tbody + tr:nth-child(2n + 1) + background-color get-theme-var(theme, 'table-odd-backgroundColor') + tr:nth-child(2n) + background-color get-theme-var(theme, 'table-even-backgroundColor') + td + border-color get-theme-var(theme, 'table-borderColor') + &:last-child + border-right solid 1px get-theme-var(theme, 'table-borderColor') + kbd + background-color get-theme-var(theme, 'kbd-backgroundColor') + color get-theme-var(theme, 'kbd-color') - pre.fence - .gallery - .carousel-main, .carousel-footer - background-color $ui-solarized-dark-noteDetail-backgroundColor - .prev, .next - color $ui-solarized-dark-button--active-color - background-color $ui-solarized-dark-button-backgroundColor + dl + border-color themeDarkBorder + background-color get-theme-var(theme, 'table-head-backgroundColor') + dt + border-color themeDarkBorder + dd + border-color themeDarkBorder + background-color get-theme-var(theme, 'noteDetail-backgroundColor') -themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor -themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%) -themeMonokaiTableHead = themeMonokaiTableEven -themeMonokaiTableBorder = themeDarkBorder + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + .prev, .next + color get-theme-var(theme, 'button--active-color') + background-color get-theme-var(theme, 'button-backgroundColor') -body[data-theme="monokai"] - color $ui-monokai-text-color - border-color themeDarkBorder - background-color $ui-monokai-noteDetail-backgroundColor - table - thead - tr - background-color themeMonokaiTableHead - th - border-color themeMonokaiTableBorder - &:last-child - border-right solid 1px themeMonokaiTableBorder - tbody - tr:nth-child(2n + 1) - background-color themeMonokaiTableOdd - tr:nth-child(2n) - background-color themeMonokaiTableEven - td - border-color themeMonokaiTableBorder - &:last-child - border-right solid 1px themeMonokaiTableBorder - kbd - background-color themeDarkBackground +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - dl - border-color themeDarkBorder - background-color themeMonokaiTableHead - dt - border-color themeDarkBorder - dd - border-color themeDarkBorder - background-color $ui-monokai-noteDetail-backgroundColor - - pre.fence - .gallery - .carousel-main, .carousel-footer - background-color $ui-monokai-noteDetail-backgroundColor - .prev, .next - color $ui-monokai-button--active-color - background-color $ui-monokai-button-backgroundColor - -themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor -themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%) -themeDraculaTableHead = themeDraculaTableEven -themeDraculaTableBorder = themeDarkBorder - -body[data-theme="dracula"] - color $ui-dracula-text-color - border-color themeDarkBorder - background-color $ui-dracula-noteDetail-backgroundColor - table - thead - tr - background-color themeDraculaTableHead - th - border-color themeDraculaTableBorder - &:last-child - border-right solid 1px themeDraculaTableBorder - tbody - tr:nth-child(2n + 1) - background-color themeDraculaTableOdd - tr:nth-child(2n) - background-color themeDraculaTableEven - td - border-color themeDraculaTableBorder - &:last-child - border-right solid 1px themeDraculaTableBorder - kbd - background-color themeDarkBackground - - dl - border-color themeDarkBorder - background-color themeDraculaTableHead - dt - border-color themeDarkBorder - dd - border-color themeDarkBorder - background-color $ui-dracula-noteDetail-backgroundColor - - pre.fence - .gallery - .carousel-main, .carousel-footer - background-color $ui-dracula-noteDetail-backgroundColor - .prev, .next - color $ui-dracula-button--active-color - background-color $ui-dracula-button-backgroundColor +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/components/render/MermaidRender.js b/browser/components/render/MermaidRender.js index e28e06ea..61bd8504 100644 --- a/browser/components/render/MermaidRender.js +++ b/browser/components/render/MermaidRender.js @@ -1,4 +1,5 @@ import mermaidAPI from 'mermaid' +import uiThemes from 'browser/lib/ui-themes' // fixes bad styling in the mermaid dark theme const darkThemeStyling = ` @@ -22,15 +23,19 @@ function getId () { function render (element, content, theme) { try { const height = element.attributes.getNamedItem('data-height') + if (height && height.value !== 'undefined') { element.style.height = height.value + 'vh' } - const isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula' + + const isDarkTheme = uiThemes.some(item => item.name === theme && item.isDark) + mermaidAPI.initialize({ theme: isDarkTheme ? 'dark' : 'default', themeCSS: isDarkTheme ? darkThemeStyling : '', useMaxWidth: false }) + mermaidAPI.render(getId(), content, (svgGraph) => { element.innerHTML = svgGraph }) diff --git a/browser/lib/ui-themes.js b/browser/lib/ui-themes.js new file mode 100644 index 00000000..3d9044c6 --- /dev/null +++ b/browser/lib/ui-themes.js @@ -0,0 +1,39 @@ +import i18n from 'browser/lib/i18n' + +export default [ + { + name: 'dark', + label: i18n.__('Dark'), + isDark: true + }, + { + name: 'default', + label: i18n.__('Default'), + isDark: false + }, + { + name: 'dracula', + label: i18n.__('Dracula'), + isDark: true + }, + { + name: 'monokai', + label: i18n.__('Monokai'), + isDark: true + }, + { + name: 'nord', + label: i18n.__('Nord'), + isDark: true + }, + { + name: 'solarized-dark', + label: i18n.__('Solarized Dark'), + isDark: true + }, + { + name: 'white', + label: i18n.__('White'), + isDark: false + } +] diff --git a/browser/main/Detail/Detail.styl b/browser/main/Detail/Detail.styl index 1b7bd606..f03de5d7 100644 --- a/browser/main/Detail/Detail.styl +++ b/browser/main/Detail/Detail.styl @@ -24,23 +24,16 @@ body[data-theme="dark"] .empty-message color $ui-dark-inactive-text-color -body[data-theme="solarized-dark"] - .root - background-color $ui-solarized-dark-noteDetail-backgroundColor - border-left 1px solid $ui-solarized-dark-borderColor - .empty-message - color $ui-solarized-dark-text-color +apply-theme(theme) + body[data-theme={theme}] + .root + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + border-left 1px solid get-theme-var(theme, 'borderColor') + .empty-message + color get-theme-var(theme, 'text-color') -body[data-theme="monokai"] - .root - background-color $ui-monokai-noteDetail-backgroundColor - border-left 1px solid $ui-monokai-borderColor - .empty-message - color $ui-monokai-text-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -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 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Detail/FolderSelect.styl b/browser/main/Detail/FolderSelect.styl index fe045e3a..76ce1eb8 100644 --- a/browser/main/Detail/FolderSelect.styl +++ b/browser/main/Detail/FolderSelect.styl @@ -134,54 +134,37 @@ body[data-theme="dark"] .search-optionList-item-name-surfix color $ui-dark-inactive-text-color -body[data-theme="monokai"] - .root - color $ui-dark-text-color - &:hover - color white - background-color $ui-monokai-button--hover-backgroundColor - border-color $ui-monokai-borderColor +apply-theme(theme) + body[data-theme={theme}] + .root + &:hover + background-color get-theme-var(theme, 'button--hover-backgroundColor') + border-color get-theme-var(theme, 'borderColor') - .search-optionList - color white - border-color $ui-monokai-borderColor - background-color $ui-monokai-button-backgroundColor + .search-input + color get-theme-var(theme, 'text-color') + background-color transparent + border-color get-theme-var(theme, 'borderColor') - .search-optionList-item - &:hover - background-color lighten($ui-monokai-button--hover-backgroundColor, 15%) + .search-optionList + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'button-backgroundColor') - .search-optionList-item--active - background-color $ui-monokai-button--active-backgroundColor - color $ui-monokai-button--active-color - &:hover - background-color $ui-monokai-button--active-backgroundColor - color $ui-monokai-button--active-color - .search-optionList-item-name-surfix - color $ui-monokai-inactive-text-color + .search-optionList-item + &:hover + background-color lighten(get-theme-var(theme, 'button--hover-backgroundColor'), 15%) -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-item--active + background-color get-theme-var(theme, 'button--active-backgroundColor') + color get-theme-var(theme, 'button--active-color') + &:hover + background-color get-theme-var(theme, 'button--active-backgroundColor') + color get-theme-var(theme, 'button--active-color') + .search-optionList-item-name-surfix + color get-theme-var(theme, 'inactive-text-color') - .search-optionList - color #f8f8f2 - border-color $ui-dracula-borderColor - background-color $ui-dracula-button-backgroundColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .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 +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Detail/FullscreenButton.styl b/browser/main/Detail/FullscreenButton.styl index 133577f3..52a3b961 100644 --- a/browser/main/Detail/FullscreenButton.styl +++ b/browser/main/Detail/FullscreenButton.styl @@ -1,26 +1,26 @@ -.control-fullScreenButton - top 80px - topBarButtonRight() - &:hover .tooltip - opacity 1 - -.tooltip - tooltip() - position absolute - pointer-events none - top 50px - right 70px - z-index 200 - padding 5px - line-height normal - border-radius 2px - opacity 0 - transition 0.1s - -.tooltip:lang(ja) - @extend .tooltip - right 35px - -body[data-theme="dark"] - .control-fullScreenButton +.control-fullScreenButton + top 80px + topBarButtonRight() + &:hover .tooltip + opacity 1 + +.tooltip + tooltip() + position absolute + pointer-events none + top 50px + right 70px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + +.tooltip:lang(ja) + @extend .tooltip + right 35px + +body[data-theme="dark"] + .control-fullScreenButton topBarButtonDark() \ No newline at end of file diff --git a/browser/main/Detail/InfoPanel.styl b/browser/main/Detail/InfoPanel.styl index 1f774174..41625816 100644 --- a/browser/main/Detail/InfoPanel.styl +++ b/browser/main/Detail/InfoPanel.styl @@ -138,162 +138,49 @@ .export--unable cursor not-allowed -body[data-theme="dark"] - .control-infoButton-panel - background-color $ui-dark-noteList-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .control-infoButton-panel + background-color get-theme-var(theme, 'noteList-backgroundColor') - .control-infoButton-panel-trash - background-color $ui-dark-noteList-backgroundColor + .control-infoButton-panel-trash + background-color get-theme-var(theme, 'noteList-backgroundColor') - .modification-date - color $ui-dark-text-color + .modification-date + color get-theme-var(theme, 'text-color') - .modification-date-desc - color $ui-inactive-text-color + .modification-date-desc + color $ui-inactive-text-color - .infoPanel-defaul-count - color $ui-dark-text-color + .infoPanel-defaul-count + color get-theme-var(theme, 'text-color') - .infoPanel-sub-count - color $ui-inactive-text-color + .infoPanel-sub-count + color $ui-inactive-text-color - .infoPanel-default - color $ui-dark-text-color + .infoPanel-default + color get-theme-var(theme, 'text-color') - .infoPanel-sub - color $ui-inactive-text-color + .infoPanel-sub + color $ui-inactive-text-color - .infoPanel-noteLink - background-color alpha($ui-dark-borderColor, 60%) - color $ui-dark-text-color + .infoPanel-noteLink + background-color alpha(get-theme-var(theme, 'borderColor'), 20%) + color get-theme-var(theme, 'text-color') - [id=export-wrap] - button - color $ui-dark-inactive-text-color - &:hover - background-color alpha($ui-dark-borderColor, 20%) - color $ui-dark-text-color - p - color $ui-dark-inactive-text-color - &:hover - color $ui-dark-text-color + [id=export-wrap] + button + color $ui-dark-inactive-text-color + &:hover + background-color alpha(get-theme-var(theme, 'borderColor'), 20%) + color get-theme-var(theme, 'text-color') + p + color $ui-dark-inactive-text-color + &:hover + color get-theme-var(theme, 'text-color') -body[data-theme="solarized-dark"] - .control-infoButton-panel - background-color $ui-solarized-dark-noteList-backgroundColor +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) - .control-infoButton-panel-trash - background-color $ui-solarized-ark-noteList-backgroundColor - - .modification-date - color $ui-solarized-ark-text-color - - .modification-date-desc - color $ui-inactive-text-color - - .infoPanel-defaul-count - color $ui-solarized-dark-text-color - - .infoPanel-sub-count - color $ui-inactive-text-color - - .infoPanel-default - color $ui-solarized-ark-text-color - - .infoPanel-sub - color $ui-inactive-text-color - - .infoPanel-noteLink - background-color alpha($ui-solarized-dark-borderColor, 20%) - color $ui-solarized-dark-text-color - - [id=export-wrap] - button - color $ui-dark-inactive-text-color - &:hover - background-color alpha($ui-solarized-dark-borderColor, 20%) - color $ui-solarized-ark-text-color - p - color $ui-dark-inactive-text-color - &:hover - color $ui-solarized-ark-text-color - -body[data-theme="monokai"] - .control-infoButton-panel - background-color $ui-monokai-noteList-backgroundColor - - .control-infoButton-panel-trash - background-color $ui-monokai-noteList-backgroundColor - - .modification-date - color $ui-monokai-text-color - - .modification-date-desc - color $ui-inactive-text-color - - .infoPanel-defaul-count - color $ui-monokai-text-color - - .infoPanel-sub-count - color $ui-inactive-text-color - - .infoPanel-default - color $ui-monokai-text-color - - .infoPanel-sub - color $ui-inactive-text-color - - .infoPanel-noteLink - background-color alpha($ui-monokai-borderColor, 20%) - color $ui-monokai-text-color - - [id=export-wrap] - button - color $ui-dark-inactive-text-color - &:hover - background-color alpha($ui-monokai-borderColor, 20%) - color $ui-monokai-text-color - p - 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 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Detail/MarkdownNoteDetail.styl b/browser/main/Detail/MarkdownNoteDetail.styl index cdfeaf3a..678969a5 100644 --- a/browser/main/Detail/MarkdownNoteDetail.styl +++ b/browser/main/Detail/MarkdownNoteDetail.styl @@ -66,18 +66,14 @@ body[data-theme="dark"] .control-fullScreenButton topBarButtonDark() +apply-theme(theme) + body[data-theme={theme}] + .root + border-left 1px solid get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteDetail-backgroundColor') -body[data-theme="solarized-dark"] - .root - border-left 1px solid $ui-solarized-dark-borderColor - background-color $ui-solarized-dark-noteDetail-backgroundColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -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 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Detail/NoteDetailInfo.styl b/browser/main/Detail/NoteDetailInfo.styl index 1ca46516..d44f61b6 100644 --- a/browser/main/Detail/NoteDetailInfo.styl +++ b/browser/main/Detail/NoteDetailInfo.styl @@ -94,17 +94,14 @@ body[data-theme="dark"] .undo-button topBarButtonDark() -body[data-theme="solarized-dark"] - .info - border-color $ui-solarized-dark-borderColor - background-color $ui-solarized-dark-noteDetail-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .info + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteDetail-backgroundColor') -body[data-theme="monokai"] - .info - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteDetail-backgroundColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="dracula"] - .info - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteDetail-backgroundColor \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl index 1af93645..c312d271 100644 --- a/browser/main/Detail/SnippetNoteDetail.styl +++ b/browser/main/Detail/SnippetNoteDetail.styl @@ -156,78 +156,35 @@ body[data-theme="dark"] .control-fullScreenButton topBarButtonDark() -body[data-theme="solarized-dark"] - .root - border-left 1px solid $ui-solarized-dark-borderColor - background-color $ui-solarized-dark-noteDetail-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .root + border-left 1px solid get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteDetail-backgroundColor') - .body - background-color $ui-solarized-dark-noteDetail-backgroundColor + .body + background-color get-theme-var(theme, 'noteDetail-backgroundColor') - .body .description textarea - background-color $ui-solarized-dark-noteDetail-backgroundColor - color $ui-solarized-dark-text-color - border 1px solid $ui-solarized-dark-borderColor + .body .description textarea + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + color get-theme-var(theme, 'text-color') + border 1px solid get-theme-var(theme, 'borderColor') - .tabList .tabButton - border-color $ui-solarized-dark-borderColor + .tabList .tabButton + border-color get-theme-var(theme, '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 + .tabButton + &:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + transition 0.15s -body[data-theme="monokai"] - .root - border-left 1px solid $ui-monokai-borderColor - background-color $ui-monokai-noteDetail-backgroundColor + .tabList + background-color get-theme-var(theme, 'noteDetail-backgroundColor') + color get-theme-var(theme, 'text-color') - .body - background-color $ui-monokai-noteDetail-backgroundColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .body .description textarea - background-color $ui-monokai-noteDetail-backgroundColor - 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 - -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 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Detail/TagSelect.styl b/browser/main/Detail/TagSelect.styl index 844561c6..6107b731 100644 --- a/browser/main/Detail/TagSelect.styl +++ b/browser/main/Detail/TagSelect.styl @@ -54,35 +54,20 @@ body[data-theme="dark"] .tag-label color $ui-dark-text-color -body[data-theme="solarized-dark"] - .tag - background-color $ui-solarized-dark-tag-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .tag + background-color get-theme-var(theme, 'tag-backgroundColor') - .tag-removeButton - border-color $ui-button--focus-borderColor - background-color transparent + .tag-removeButton + border-color $ui-button--focus-borderColor + background-color transparent - .tag-label - color $ui-solarized-dark-text-color + .tag-label + color get-theme-var(theme, 'text-color') -body[data-theme="monokai"] - .tag - background-color $ui-monokai-tag-backgroundColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .tag-removeButton - border-color $ui-button--focus-borderColor - background-color transparent - - .tag-label - color $ui-monokai-text-color - -body[data-theme="dracula"] - .tag - background-color $ui-dracula-tag-backgroundColor - - .tag-removeButton - border-color $ui-dracula-button--focus-borderColor - background-color transparent - - .tag-label - color $ui-dracula-borderColor +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Detail/ToggleModeButton.styl b/browser/main/Detail/ToggleModeButton.styl index 2b47b932..481e6747 100644 --- a/browser/main/Detail/ToggleModeButton.styl +++ b/browser/main/Detail/ToggleModeButton.styl @@ -1,77 +1,77 @@ -.control-toggleModeButton - height 25px - border-radius 50px - background-color #F4F4F4 - width 52px - display flex - align-items center - position: relative - top 2px - .active - background-color #1EC38B - width 33px - height 24px - box-shadow 2px 0px 7px #eee - z-index 1 - - div - width 40px - height 100% - border-radius 50% - display flex - align-items center - justify-content center - cursor pointer - - &:hover .tooltip - opacity 1 - -.tooltip - tooltip() - position absolute - pointer-events none - top 33px - left -10px - z-index 200 - width 80px - padding 5px - line-height normal - border-radius 2px - opacity 0 - transition 0.1s - -.tooltip:lang(ja) - @extend .tooltip - left -8px - width 70px - -body[data-theme="dark"] - .control-fullScreenButton - topBarButtonDark() - - .control-toggleModeButton - background-color #3A404C - .active - background-color #1EC38B - box-shadow 2px 0px 7px #444444 - -body[data-theme="solarized-dark"] - .control-toggleModeButton - background-color #002B36 - .active - background-color #1EC38B - box-shadow 2px 0px 7px #222222 - -body[data-theme="monokai"] - .control-toggleModeButton - background-color #373831 - .active - 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 +.control-toggleModeButton + height 25px + border-radius 50px + background-color #F4F4F4 + width 52px + display flex + align-items center + position: relative + top 2px + .active + background-color #1EC38B + width 33px + height 24px + box-shadow 2px 0px 7px #eee + z-index 1 + + div + width 40px + height 100% + border-radius 50% + display flex + align-items center + justify-content center + cursor pointer + + &:hover .tooltip + opacity 1 + +.tooltip + tooltip() + position absolute + pointer-events none + top 33px + left -10px + z-index 200 + width 80px + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + +.tooltip:lang(ja) + @extend .tooltip + left -8px + width 70px + +body[data-theme="dark"] + .control-fullScreenButton + topBarButtonDark() + + .control-toggleModeButton + background-color #3A404C + .active + background-color #1EC38B + box-shadow 2px 0px 7px #444444 + +body[data-theme="solarized-dark"] + .control-toggleModeButton + background-color #002B36 + .active + background-color #1EC38B + box-shadow 2px 0px 7px #222222 + +apply-theme(theme) + body[data-theme={theme}] + .control-toggleModeButton + background-color get-theme-var(theme, 'borderColor') + .active + background-color get-theme-var(theme, 'active-color') + box-shadow 2px 0px 7px #222222 + +for theme in 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/Main.js b/browser/main/Main.js index 26fc8377..8d346f4a 100644 --- a/browser/main/Main.js +++ b/browser/main/Main.js @@ -17,6 +17,7 @@ import store from 'browser/main/store' import i18n from 'browser/lib/i18n' import { getLocales } from 'browser/lib/Languages' import applyShortcuts from 'browser/main/lib/shortcutManager' +import uiThemes from 'browser/lib/ui-themes' const path = require('path') const electron = require('electron') const { remote } = electron @@ -142,9 +143,7 @@ class Main extends React.Component { componentDidMount () { const { dispatch, config } = this.props - const supportedThemes = ['dark', 'white', 'solarized-dark', 'monokai', 'dracula'] - - if (supportedThemes.indexOf(config.ui.theme) !== -1) { + if (uiThemes.some(theme => theme.name === config.ui.theme)) { document.body.setAttribute('data-theme', config.ui.theme) } else { document.body.setAttribute('data-theme', 'default') diff --git a/browser/main/NewNoteButton/NewNoteButton.styl b/browser/main/NewNoteButton/NewNoteButton.styl index 75a9061c..57b0fc03 100644 --- a/browser/main/NewNoteButton/NewNoteButton.styl +++ b/browser/main/NewNoteButton/NewNoteButton.styl @@ -72,14 +72,13 @@ body[data-theme="dark"] .control-newNoteButton-tooltip darkTooltip() -body[data-theme="solarized-dark"] - .root, .root--expanded - background-color $ui-solarized-dark-noteList-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .root, .root--expanded + background-color get-theme-var(theme, 'noteList-backgroundColor') -body[data-theme="monokai"] - .root, .root--expanded - background-color $ui-monokai-noteList-backgroundColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="dracula"] - .root, .root--expanded - background-color $ui-dracula-noteList-backgroundColor \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/NoteList/NoteList.styl b/browser/main/NoteList/NoteList.styl index 73959c9b..2b42ae3d 100644 --- a/browser/main/NoteList/NoteList.styl +++ b/browser/main/NoteList/NoteList.styl @@ -66,99 +66,33 @@ body[data-theme="white"] .control background-color $ui-white-noteList-backgroundColor -body[data-theme="dark"] - .root - border-color $ui-dark-borderColor - background-color $ui-dark-noteList-backgroundColor +apply-theme(theme) + body[data-theme={theme}] + .root + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteList-backgroundColor') - .control - background-color $ui-dark-noteList-backgroundColor - border-color $ui-dark-borderColor + .control + background-color get-theme-var(theme, 'noteList-backgroundColor') + border-color get-theme-var(theme, 'borderColor') - .control-sortBy-select - &:hover - transition 0.2s - color $ui-dark-text-color + .control-sortBy-select + &:hover + transition 0.2s + color get-theme-var(theme, 'text-color') - .control-button - color $ui-dark-inactive-text-color - &:hover - color $ui-dark-text-color + .control-button + color get-theme-var(theme, 'inactive-text-color') + &:hover + color get-theme-var(theme, 'text-color') - .control-button--active - color $ui-dark-text-color - &:active - color $ui-dark-text-color + .control-button--active + color get-theme-var(theme, 'text-color') + &:active + color get-theme-var(theme, 'text-color') +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="solarized-dark"] - .root - border-color $ui-solarized-dark-borderColor - background-color $ui-solarized-dark-noteList-backgroundColor - - .control - background-color $ui-solarized-dark-noteList-backgroundColor - border-color $ui-solarized-dark-borderColor - - .control-sortBy-select - &:hover - transition 0.2s - color $ui-solarized-dark-text-color - - .control-button - color $ui-solarized-dark-inactive-text-color - &:hover - color $ui-solarized-dark-text-color - - .control-button--active - color $ui-solarized-dark-text-color - &:active - color $ui-solarized-dark-text-color - -body[data-theme="monokai"] - .root - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteList-backgroundColor - - .control - background-color $ui-monokai-noteList-backgroundColor - border-color $ui-monokai-borderColor - - .control-sortBy-select - &:hover - transition 0.2s - color $ui-monokai-text-color - - .control-button - color $ui-monokai-inactive-text-color - &:hover - color $ui-monokai-text-color - - .control-button--active - color $ui-monokai-text-color - &:active - color $ui-monokai-text-color - -body[data-theme="dracula"] - .root - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteList-backgroundColor - - .control - background-color $ui-dracula-noteList-backgroundColor - border-color $ui-dracula-borderColor - - .control-sortBy-select - &:hover - transition 0.2s - color $ui-dracula-text-color - - .control-button - color $ui-dracula-inactive-text-color - &:hover - color $ui-dracula-text-color - - .control-button--active - color $ui-dracula-text-color - &:active - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/SideNav/PreferenceButton.styl b/browser/main/SideNav/PreferenceButton.styl index 54513cb6..33422b4f 100644 --- a/browser/main/SideNav/PreferenceButton.styl +++ b/browser/main/SideNav/PreferenceButton.styl @@ -1,52 +1,50 @@ -.top-menu-preference - navButtonColor() - position absolute - top 22px - right 10px - width 2em - background-color transparent - &:hover - color $ui-button-default--active-backgroundColor - background-color transparent - .tooltip - opacity 1 - &:active, &:active:hover - color $ui-button-default--active-backgroundColor - -body[data-theme="white"] - .top-menu-preference - navWhiteButtonColor() - background-color transparent - &:hover - color #0B99F1 - background-color transparent - &:active, &:active:hover - color #0B99F1 - background-color transparent - -body[data-theme="dark"] - .top-menu-preference - navDarkButtonColor() - background-color transparent - &:active - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - background-color transparent - &:hover - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - background-color transparent - - - -.tooltip - tooltip() - position absolute - pointer-events none - top 26px - left -20px - z-index 200 - padding 5px - line-height normal - border-radius 2px - opacity 0 - transition 0.1s - white-space nowrap +.top-menu-preference + navButtonColor() + position absolute + top 22px + right 10px + width 2em + background-color transparent + &:hover + color $ui-button-default--active-backgroundColor + background-color transparent + .tooltip + opacity 1 + &:active, &:active:hover + color $ui-button-default--active-backgroundColor + +body[data-theme="white"] + .top-menu-preference + navWhiteButtonColor() + background-color transparent + &:hover + color #0B99F1 + background-color transparent + &:active, &:active:hover + color #0B99F1 + background-color transparent + +body[data-theme="dark"] + .top-menu-preference + navDarkButtonColor() + background-color transparent + &:active + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + &:hover + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + +.tooltip + tooltip() + position absolute + pointer-events none + top 26px + left -20px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + white-space nowrap diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl index 9fa6d4fa..86b6caeb 100644 --- a/browser/main/SideNav/SideNav.styl +++ b/browser/main/SideNav/SideNav.styl @@ -113,17 +113,14 @@ body[data-theme="dark"] .top border-color $ui-dark-borderColor -body[data-theme="solarized-dark"] - .root, .root--folded - background-color $ui-solarized-dark-backgroundColor - border-right 1px solid $ui-solarized-dark-borderColor +apply-theme(theme) + body[data-theme={theme}] + .root, .root--folded + background-color get-theme-var(theme, 'backgroundColor') + border-right 1px solid get-theme-var(theme, 'borderColor') -body[data-theme="monokai"] - .root, .root--folded - background-color $ui-monokai-backgroundColor - border-right 1px solid $ui-monokai-borderColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="dracula"] - .root, .root--folded - background-color $ui-dracula-backgroundColor - border-right 1px solid $ui-dracula-borderColor \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/SideNav/StorageItem.styl b/browser/main/SideNav/StorageItem.styl index a06ecb11..375a989f 100644 --- a/browser/main/SideNav/StorageItem.styl +++ b/browser/main/SideNav/StorageItem.styl @@ -132,55 +132,57 @@ body[data-theme="white"] background-color alpha($ui-button--active-backgroundColor, 40%) color $ui-text-color -body[data-theme="dark"] - .header--active - background-color $ui-dark-button--active-backgroundColor - transition color background-color 0.15s +apply-theme(theme) + body[data-theme={theme}] + .header--active + background-color get-theme-var(theme, 'button--active-backgroundColor') + transition color background-color 0.15s + + .header--active + .header-toggleButton + color get-theme-var(theme, 'text-color') + + .header--active + .header-info + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') + &:active + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') + + .header--active + .header-addFolderButton + color get-theme-var(theme, 'text-color') - .header--active .header-toggleButton - color $ui-dark-text-color + &:hover + transition 0.2s + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%) + &:active, &:active:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') - .header--active .header-info - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - &:active - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%) + &:hover + transition 0.2s + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%) + &:active, &:active:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') - .header--active .header-addFolderButton - color $ui-dark-text-color - - .header-toggleButton - &:hover - transition 0.2s - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 60%) - &:active, &:active:hover - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - - .header-info - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - &:hover - transition 0.2s - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - &:active, &:active:hover - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - - .header-addFolderButton - &:hover - transition 0.2s - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 60%) - &:active, &:active:hover - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - - + &:hover + transition 0.2s + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%) + &:active, &:active:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') +apply-theme('dark') +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/SideNav/SwitchButton.styl b/browser/main/SideNav/SwitchButton.styl index 36099140..2184bc69 100644 --- a/browser/main/SideNav/SwitchButton.styl +++ b/browser/main/SideNav/SwitchButton.styl @@ -1,60 +1,60 @@ -.non-active-button - color $ui-inactive-text-color - font-size 16px - border 0 - background-color transparent - transition 0.2s - display flex - text-align center - margin-right 4px - position relative - &:hover - color alpha(#239F86, 60%) - .tooltip - opacity 1 - -.active-button - @extend .non-active-button - color $ui-button-default--active-backgroundColor - -.tooltip - tooltip() - position absolute - pointer-events none - top 22px - left -2px - z-index 200 - padding 5px - line-height normal - border-radius 2px - opacity 0 - transition 0.1s - white-space nowrap - -body[data-theme="white"] - .non-active-button - color $ui-inactive-text-color - &:hover - color alpha(#0B99F1, 60%) - - .tag-title - p - color $ui-text-color - - .non-active-button - &:hover - color alpha(#0B99F1, 60%) - - .active-button - @extend .non-active-button - color #0B99F1 - -body[data-theme="dark"] - .non-active-button - color alpha($ui-dark-text-color, 60%) - &:hover - color alpha(#0B99F1, 60%) - - .tag-title - p +.non-active-button + color $ui-inactive-text-color + font-size 16px + border 0 + background-color transparent + transition 0.2s + display flex + text-align center + margin-right 4px + position relative + &:hover + color alpha(#239F86, 60%) + .tooltip + opacity 1 + +.active-button + @extend .non-active-button + color $ui-button-default--active-backgroundColor + +.tooltip + tooltip() + position absolute + pointer-events none + top 22px + left -2px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + white-space nowrap + +body[data-theme="white"] + .non-active-button + color $ui-inactive-text-color + &:hover + color alpha(#0B99F1, 60%) + + .tag-title + p + color $ui-text-color + + .non-active-button + &:hover + color alpha(#0B99F1, 60%) + + .active-button + @extend .non-active-button + color #0B99F1 + +body[data-theme="dark"] + .non-active-button + color alpha($ui-dark-text-color, 60%) + &:hover + color alpha(#0B99F1, 60%) + + .tag-title + p color alpha($ui-dark-text-color, 60%) \ No newline at end of file diff --git a/browser/main/StatusBar/StatusBar.styl b/browser/main/StatusBar/StatusBar.styl index 23dec208..7ed89eb5 100644 --- a/browser/main/StatusBar/StatusBar.styl +++ b/browser/main/StatusBar/StatusBar.styl @@ -78,24 +78,20 @@ body[data-theme="dark"] border-color $ui-dark-borderColor border-left 1px solid $ui-dark-borderColor -body[data-theme="monokai"] - navButtonColor() - .zoom - border-color $ui-dark-borderColor - color $ui-monokai-text-color - &:hover - transition 0.15s - color $ui-monokai-active-color - &:active - color $ui-monokai-active-color +apply-theme(theme) + body[data-theme={theme}] + navButtonColor() + .zoom + border-color $ui-dark-borderColor + color get-theme-var(theme, 'text-color') + &:hover + transition 0.15s + color get-theme-var(theme, 'active-color') + &:active + color get-theme-var(theme, 'active-color') -body[data-theme="dracula"] - navButtonColor() - .zoom - border-color $ui-dark-borderColor - color $ui-dracula-text-color - &:hover - transition 0.15s - color $ui-dracula-active-color - &:active - color $ui-dracula-active-color \ No newline at end of file +for theme in 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/TopBar/TopBar.styl b/browser/main/TopBar/TopBar.styl index 61b21fc5..a0eeadf6 100644 --- a/browser/main/TopBar/TopBar.styl +++ b/browser/main/TopBar/TopBar.styl @@ -212,69 +212,31 @@ body[data-theme="dark"] .control-newPostButton-tooltip darkTooltip() +apply-theme(theme) + body[data-theme={theme}] + .root, .root--expanded + background-color get-theme-var(theme, 'noteList-backgroundColor') -body[data-theme="solarized-dark"] - .root, .root--expanded - background-color $ui-solarized-dark-noteList-backgroundColor + .control + border-color get-theme-var(theme, 'borderColor') + .control-search + background-color get-theme-var(theme, 'noteList-backgroundColor') - .control - border-color $ui-solarized-dark-borderColor - .control-search - background-color $ui-solarized-dark-noteList-backgroundColor + .control-search-icon + absolute top bottom left + line-height 32px + width 35px + color get-theme-var(theme, 'inactive-text-color') + background-color get-theme-var(theme, 'noteList-backgroundColor') - .control-search-icon - absolute top bottom left - line-height 32px - width 35px - color $ui-solarized-dark-inactive-text-color - background-color $ui-solarized-dark-noteList-backgroundColor + .control-search-input + background-color get-theme-var(theme, 'noteList-backgroundColor') + input + background-color get-theme-var(theme, 'noteList-backgroundColor') + color get-theme-var(theme, 'text-color') - .control-search-input - background-color $ui-solarized-dark-noteList-backgroundColor - input - background-color $ui-solarized-dark-noteList-backgroundColor - color $ui-solarized-dark-text-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="monokai"] - .root, .root--expanded - background-color $ui-monokai-noteList-backgroundColor - - .control - border-color $ui-monokai-borderColor - .control-search - background-color $ui-monokai-noteList-backgroundColor - - .control-search-icon - absolute top bottom left - line-height 32px - width 35px - color $ui-monokai-inactive-text-color - background-color $ui-monokai-noteList-backgroundColor - - .control-search-input - background-color $ui-monokai-noteList-backgroundColor - input - background-color $ui-monokai-noteList-backgroundColor - color $ui-monokai-text-color - -body[data-theme="dracula"] - .root, .root--expanded - background-color $ui-dracula-noteList-backgroundColor - - .control - border-color $ui-dracula-borderColor - .control-search - background-color $ui-dracula-noteList-backgroundColor - - .control-search-icon - absolute top bottom left - line-height 32px - width 35px - color $ui-dracula-inactive-text-color - background-color $ui-dracula-noteList-backgroundColor - - .control-search-input - background-color $ui-dracula-noteList-backgroundColor - input - background-color $ui-dracula-noteList-backgroundColor - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/global.styl b/browser/main/global.styl index d864993d..b0da56f5 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -96,16 +96,6 @@ modalBackColor = white z-index modalZIndex + 1 -body[data-theme="dark"] - background-color $ui-dark-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-dark-backgroundColor - .sortableItemHelper - color: $ui-dark-text-color - .CodeMirror font-family inherit !important line-height 1.4em @@ -148,35 +138,22 @@ body[data-theme="dark"] .sortableItemHelper z-index modalZIndex + 5 -body[data-theme="solarized-dark"] - background-color $ui-solarized-dark-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-solarized-dark-backgroundColor - .sortableItemHelper - color: $ui-solarized-dark-text-color +apply-theme(theme) + body[data-theme={theme}] + background-color get-theme-var(theme, 'backgroundColor') + ::-webkit-scrollbar-thumb + background-color rgba(0, 0, 0, 0.3) + .ModalBase + .modalBack + background-color get-theme-var(theme, 'backgroundColor') + .sortableItemHelper + color get-theme-var(theme, 'text-color') -body[data-theme="monokai"] - background-color $ui-monokai-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-monokai-backgroundColor - .sortableItemHelper - color: $ui-monokai-text-color +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="dracula"] - background-color $ui-dracula-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-dracula-backgroundColor - .sortableItemHelper - color: $ui-dracula-text-color +for theme in $themes + apply-theme(theme) body[data-theme="default"] .SideNav ::-webkit-scrollbar-thumb diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 5558b3bd..e8e11b9e 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -2,6 +2,7 @@ import _ from 'lodash' import RcParser from 'browser/lib/RcParser' import i18n from 'browser/lib/i18n' import ee from 'browser/main/lib/eventEmitter' +import uiThemes from 'browser/lib/ui-themes' const OSX = global.process.platform === 'darwin' const win = global.process.platform === 'win32' @@ -154,16 +155,8 @@ function set (updates) { if (!validate(newConfig)) throw new Error('INVALID CONFIG') _save(newConfig) - if (newConfig.ui.theme === 'dark') { - document.body.setAttribute('data-theme', 'dark') - } else if (newConfig.ui.theme === 'white') { - document.body.setAttribute('data-theme', 'white') - } else if (newConfig.ui.theme === 'solarized-dark') { - document.body.setAttribute('data-theme', 'solarized-dark') - } else if (newConfig.ui.theme === 'monokai') { - document.body.setAttribute('data-theme', 'monokai') - } else if (newConfig.ui.theme === 'dracula') { - document.body.setAttribute('data-theme', 'dracula') + if (uiThemes.some(theme => theme.name === newConfig.ui.theme)) { + document.body.setAttribute('data-theme', newConfig.ui.theme) } else { document.body.setAttribute('data-theme', 'default') } diff --git a/browser/main/modals/CreateFolderModal.styl b/browser/main/modals/CreateFolderModal.styl index 93848683..95d6249a 100644 --- a/browser/main/modals/CreateFolderModal.styl +++ b/browser/main/modals/CreateFolderModal.styl @@ -51,106 +51,40 @@ font-size 14px colorPrimaryButton() -body[data-theme="dark"] - .root - modalDark() - width 500px - height 270px - overflow hidden - position relative +apply-theme(theme) + body[data-theme={theme}] + .root + width 500px + height 270px + overflow hidden + position relative + position relative + z-index $modal-z-index + width 100% + background-color get-theme-var(theme, 'backgroundColor') + overflow hidden + border-radius $modal-border-radius - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-dark-text-color + .header + background-color transparent + border-color $ui-dark-borderColor + color get-theme-var(theme, 'text-color') - .control-folder-label - color $ui-dark-text-color + .control-folder-label + color get-theme-var(theme, 'text-color') - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white + .control-folder-input + border 1px solid $ui-input--create-folder-modal + color white - .description - color $ui-inactive-text-color + .description + color $ui-inactive-text-color - .control-confirmButton - colorDarkPrimaryButton() + .control-confirmButton + colorThemedPrimaryButton(theme) -body[data-theme="solarized-dark"] - .root - modalSolarizedDark() - width 500px - height 270px - overflow hidden - position relative +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-solarized-dark-text-color - - .control-folder-label - color $ui-solarized-dark-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorSolarizedDarkPrimaryButton() - -body[data-theme="monokai"] - .root - modalMonokai() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-monokai-text-color - - .control-folder-label - color $ui-monokai-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorMonokaiPrimaryButton() - -body[data-theme="dracula"] - .root - modalDracula() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-dracula-text-color - - .control-folder-label - color $ui-dracula-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorDraculaPrimaryButton() \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/NewNoteModal.styl b/browser/main/modals/NewNoteModal.styl index c82b9376..c82ac9a9 100644 --- a/browser/main/modals/NewNoteModal.styl +++ b/browser/main/modals/NewNoteModal.styl @@ -47,70 +47,26 @@ text-align center margin-bottom 25px -body[data-theme="dark"] - .root - modalDark() +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent - .header - color $ui-dark-text-color + .header + color get-theme-var(theme, 'text-color') - .control-button - border-color $ui-dark-borderColor - color $ui-dark-text-color - background-color transparent - &:focus - colorDarkPrimaryButton() + .control-button + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') + background-color transparent + &:focus + colorThemedPrimaryButton(theme) - .description - color $ui-inactive-text-color + .description + color get-theme-var(theme, 'text-color') -body[data-theme="solarized-dark"] - .root - background-color transparent +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) - .header - color $ui-solarized-dark-text-color - - .control-button - border-color $ui-solarized-dark-borderColor - color $ui-solarized-dark-text-color - background-color transparent - &:focus - colorDarkPrimaryButton() - - .description - color $ui-solarized-dark-text-color - -body[data-theme="monokai"] - .root - background-color transparent - - .header - color $ui-monokai-text-color - - .control-button - border-color $ui-monokai-borderColor - color $ui-monokai-text-color - background-color transparent - &:focus - colorDarkPrimaryButton() - - .description - color $ui-monokai-text-color - -body[data-theme="dracula"] - .root - background-color transparent - - .header - color $ui-dracula-text-color - - .control-button - border-color $ui-dracula-borderColor - color $ui-dracula-text-color - background-color transparent - &:focus - colorDraculaPrimaryButton() - - .description - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/ConfigTab.styl b/browser/main/modals/PreferencesModal/ConfigTab.styl index 255165ce..0d9881cd 100644 --- a/browser/main/modals/PreferencesModal/ConfigTab.styl +++ b/browser/main/modals/PreferencesModal/ConfigTab.styl @@ -128,20 +128,10 @@ colorDarkControl() background-color $ui-dark-backgroundColor color $ui-dark-text-color -colorSolarizedDarkControl() +colorThemedControl(theme) border none - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color - -colorMonokaiControl() - border none - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - -colorDraculaControl() - border none - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') body[data-theme="dark"] .root @@ -170,84 +160,36 @@ body[data-theme="dark"] select, .group-section-control-input colorDarkControl() +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color + .group-header + color get-theme-var(theme, 'text-color') + border-color get-theme-var(theme, 'borderColor') - .group-header - color $ui-solarized-dark-text-color - border-color $ui-solarized-dark-borderColor + .group-header2 + color get-theme-var(theme, 'text-color') - .group-header2 - color $ui-solarized-dark-text-color + .group-section-control-input + border-color get-theme-var(theme, 'borderColor') - .group-section-control-input - border-color $ui-solarized-dark-borderColor + .group-control + border-color get-theme-var(theme, 'borderColor') + .group-control-leftButton + colorDarkDefaultButton() + border-color get-theme-var(theme, 'borderColor') + .group-control-rightButton + colorThemedPrimaryButton(theme) + .group-hint + colorThemedControl(theme) + .group-section-control + select, .group-section-control-input + colorThemedControl(theme) - .group-control - border-color $ui-solarized-dark-borderColor - .group-control-leftButton - colorDarkDefaultButton() - border-color $ui-solarized-dark-borderColor - .group-control-rightButton - colorSolarizedDarkPrimaryButton() - .group-hint - colorSolarizedDarkControl() - .group-section-control - select, .group-section-control-input - colorSolarizedDarkControl() +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="monokai"] - .root - color $ui-monokai-text-color - - .group-header - color $ui-monokai-text-color - border-color $ui-monokai-borderColor - - .group-header2 - color $ui-monokai-text-color - - .group-section-control-input - border-color $ui-monokai-borderColor - - .group-control - border-color $ui-monokai-borderColor - .group-control-leftButton - colorDarkDefaultButton() - border-color $ui-monokai-borderColor - .group-control-rightButton - colorMonokaiPrimaryButton() - .group-hint - colorMonokaiControl() - .group-section-control - select, .group-section-control-input - colorMonokaiControl() - -body[data-theme="dracula"] - .root - color $ui-dracula-text-color - - .group-header - color $ui-dracula-text-color - border-color $ui-dracula-borderColor - - .group-header2 - color $ui-dracula-text-color - - .group-section-control-input - border-color $ui-dracula-borderColor - - .group-control - border-color $ui-dracula-borderColor - .group-control-leftButton - colorDarkDefaultButton() - border-color $ui-dracula-borderColor - .group-control-rightButton - colorDraculaPrimaryButton() - .group-hint - colorDraculaControl() - .group-section-control - select, .group-section-control-input - colorDraculaControl() \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/Crowdfunding.styl b/browser/main/modals/PreferencesModal/Crowdfunding.styl index 6d72290b..eb4f1289 100644 --- a/browser/main/modals/PreferencesModal/Crowdfunding.styl +++ b/browser/main/modals/PreferencesModal/Crowdfunding.styl @@ -30,20 +30,15 @@ body[data-theme="dark"] p color $ui-dark-text-color -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color - p - color $ui-solarized-dark-text-color +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') + p + color get-theme-var(theme, 'text-color') -body[data-theme="monokai"] - .root - color $ui-monokai-text-color - p - color $ui-monokai-text-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="dracula"] - .root - color $ui-dracula-text-color - p - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/FolderItem.styl b/browser/main/modals/PreferencesModal/FolderItem.styl index 618e9bc4..32a83d63 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.styl +++ b/browser/main/modals/PreferencesModal/FolderItem.styl @@ -107,73 +107,32 @@ body[data-theme="dark"] .folderItem-right-dangerButton colorDarkDangerButton() +apply-theme(theme) + body[data-theme={theme}] + .folderItem + &:hover + background-color get-theme-var(theme, 'button-backgroundColor') + .folderItem-left-danger + color $danger-color -body[data-theme="solarized-dark"] - .folderItem - &:hover - background-color $ui-solarized-dark-button-backgroundColor + .folderItem-left-key + color $ui-dark-inactive-text-color - .folderItem-left-danger - color $danger-color + .folderItem-left-colorButton + colorThemedPrimaryButton(theme) - .folderItem-left-key - color $ui-dark-inactive-text-color + .folderItem-right-button + colorThemedPrimaryButton(theme) - .folderItem-left-colorButton - colorSolarizedDarkPrimaryButton() + .folderItem-right-confirmButton + colorThemedPrimaryButton(theme) - .folderItem-right-button - colorSolarizedDarkPrimaryButton() + .folderItem-right-dangerButton + colorThemedPrimaryButton(theme) - .folderItem-right-confirmButton - colorSolarizedDarkPrimaryButton() +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .folderItem-right-dangerButton - colorSolarizedDarkPrimaryButton() - -body[data-theme="monokai"] - .folderItem - &:hover - background-color $ui-monokai-button-backgroundColor - - .folderItem-left-danger - color $danger-color - - .folderItem-left-key - color $ui-dark-inactive-text-color - - .folderItem-left-colorButton - colorMonokaiPrimaryButton() - - .folderItem-right-button - colorMonokaiPrimaryButton() - - .folderItem-right-confirmButton - colorMonokaiPrimaryButton() - - .folderItem-right-dangerButton - colorMonokaiPrimaryButton() - -body[data-theme="dracula"] - .folderItem - &:hover - background-color $ui-dracula-button-backgroundColor - - .folderItem-left-danger - color $danger-color - - .folderItem-left-key - color $ui-dark-inactive-text-color - - .folderItem-left-colorButton - colorDraculaPrimaryButton() - - .folderItem-right-button - colorDraculaPrimaryButton() - - .folderItem-right-confirmButton - colorDraculaPrimaryButton() - - .folderItem-right-dangerButton - colorDraculaPrimaryButton() \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/InfoTab.styl b/browser/main/modals/PreferencesModal/InfoTab.styl index 44f2d9ae..dc675267 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.styl +++ b/browser/main/modals/PreferencesModal/InfoTab.styl @@ -61,24 +61,16 @@ body[data-theme="dark"] .root color alpha($tab--dark-text-color, 80%) +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') + .list + a + color get-theme-var(theme, 'active-color') -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color -.list - a - color $ui-solarized-dark-active-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="monokai"] - .root - color $ui-monokai-text-color -.list - a - color $ui-monokai-active-color - -body[data-theme="dracula"] - .root - color $ui-dracula-text-color -.list - a - color $ui-dracula-active-color +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/PreferencesModal.styl b/browser/main/modals/PreferencesModal/PreferencesModal.styl index 7004259b..2e3b4040 100644 --- a/browser/main/modals/PreferencesModal/PreferencesModal.styl +++ b/browser/main/modals/PreferencesModal/PreferencesModal.styl @@ -64,102 +64,31 @@ top-bar--height = 50px margin-top 10px overflow-y auto -body[data-theme="dark"] - .root - modalDark() +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent + .top-bar + background-color transparent + border-color get-theme-var(theme, 'borderColor') + p + color get-theme-var(theme, 'text-color') + .nav + background-color transparent + border-color get-theme-var(theme, 'borderColor') + .nav-button + background-color transparent + color get-theme-var(theme, 'text-color') + &:hover + color get-theme-var(theme, 'text-color') - .top-bar - background-color transparent - border-color #4A4D52 - p - color $tab--dark-text-color + .nav-button--active + @extend .nav-button + color get-theme-var(theme, 'button--active-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') - .nav - background-color transparent - border-color $ui-dark-borderColor +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) - .nav-button - background-color transparent - color $tab--dark-text-color - &:hover - color $ui-dark-text-color - - .nav-button--active - @extend .nav-button - color white - background-color $dark-primary-button-background--active - &:hover - color white - - -body[data-theme="solarized-dark"] - .root - background-color transparent - .top-bar - background-color transparent - border-color $ui-solarized-dark-borderColor - p - color $ui-solarized-dark-text-color - .nav - background-color transparent - border-color $ui-solarized-dark-borderColor - .nav-button - background-color transparent - color $ui-solarized-dark-text-color - &:hover - color $ui-solarized-dark-text-color - - .nav-button--active - @extend .nav-button - color $ui-solarized-dark-button--active-color - background-color $ui-solarized-dark-button--active-backgroundColor - &:hover - color white - -body[data-theme="monokai"] - .root - background-color transparent - .top-bar - background-color transparent - border-color $ui-monokai-borderColor - p - color $ui-monokai-text-color - .nav - background-color transparent - border-color $ui-monokai-borderColor - .nav-button - background-color transparent - color $ui-monokai-text-color - &:hover - color $ui-monokai-text-color - - .nav-button--active - @extend .nav-button - color $ui-monokai-button--active-color - background-color $ui-monokai-button--active-backgroundColor - &:hover - color white - -body[data-theme="dracula"] - .root - background-color transparent - .top-bar - background-color transparent - border-color $ui-dracula-borderColor - p - color $ui-dracula-text-color - .nav - background-color transparent - border-color $ui-dracula-borderColor - .nav-button - background-color transparent - color $ui-dracula-text-color - &:hover - color $ui-dracula-text-color - - .nav-button--active - @extend .nav-button - color $ui-dracula-button--active-color - background-color $ui-dracula-button--active-backgroundColor - &:hover - color #f8f8f2 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/SnippetTab.styl b/browser/main/modals/PreferencesModal/SnippetTab.styl index dd22b72e..5d899797 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.styl +++ b/browser/main/modals/PreferencesModal/SnippetTab.styl @@ -149,66 +149,25 @@ body[data-theme="default"], body[data-theme="white"] .snippet-item-selected background darken($ui-backgroundColor, 5) -body[data-theme="dark"] - .snippets - background $ui-dark-backgroundColor - .snippet-item - color white - &::after - background $ui-dark-borderColor - &:hover - background darken($ui-dark-backgroundColor, 5) - .snippet-item-selected - background darken($ui-dark-backgroundColor, 5) - .snippet-detail - color white - .group-control-button - colorDarkPrimaryButton() +apply-theme(theme) + body[data-theme={theme}] + .snippets + background get-theme-var(theme, 'backgroundColor') + .snippet-item + color get-theme-var(theme, 'text-color') + &::after + background get-theme-var(theme, 'borderColor') + &:hover + background darken(get-theme-var(theme, 'backgroundColor'), 5) + .snippet-item-selected + background darken(get-theme-var(theme, 'backgroundColor'), 5) + .snippet-detail + color get-theme-var(theme, 'text-color') + .group-control-button + colorThemedPrimaryButton(theme) -body[data-theme="solarized-dark"] - .snippets - background $ui-solarized-dark-backgroundColor - .snippet-item - color white - &::after - background $ui-solarized-dark-borderColor - &:hover - background darken($ui-solarized-dark-backgroundColor, 5) - .snippet-item-selected - background darken($ui-solarized-dark-backgroundColor, 5) - .snippet-detail - color white - .group-control-button - colorSolarizedDarkPrimaryButton() +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="monokai"] - .snippets - background $ui-monokai-backgroundColor - .snippet-item - color White - &::after - background $ui-monokai-borderColor - &:hover - background darken($ui-monokai-backgroundColor, 5) - .snippet-item-selected - background darken($ui-monokai-backgroundColor, 5) - .snippet-detail - color white - .group-control-button - colorMonokaiPrimaryButton() - -body[data-theme="dracula"] - .snippets - background $ui-dracula-backgroundColor - .snippet-item - color #f8f8f2 - &::after - background $ui-dracula-borderColor - &:hover - background darken($ui-dracula-backgroundColor, 5) - .snippet-item-selected - background darken($ui-dracula-backgroundColor, 5) - .snippet-detail - color #f8f8f2 - .group-control-button - colorDraculaPrimaryButton() \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/StoragesTab.styl b/browser/main/modals/PreferencesModal/StoragesTab.styl index 9a1a0ef8..bed3ff72 100644 --- a/browser/main/modals/PreferencesModal/StoragesTab.styl +++ b/browser/main/modals/PreferencesModal/StoragesTab.styl @@ -159,118 +159,47 @@ body[data-theme="dark"] colorDarkDefaultButton() border-color $ui-dark-borderColor +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') + .folderList-item + border-bottom get-theme-var(theme, 'borderColor') -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color + .folderList-empty + color get-theme-var(theme, 'text-color') - .folderList-item - border-bottom $ui-solarized-dark-borderColor + .list-empty + color get-theme-var(theme, 'text-color') + .list-control-addStorageButton + border-color get-theme-var(theme, 'button-backgroundColor') + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') - .folderList-empty - color $ui-solarized-dark-text-color + .addStorage-header + color get-theme-var(theme, 'text-color') + border-color get-theme-var(theme, 'borderColor') - .list-empty - color $ui-solarized-dark-text-color - .list-control-addStorageButton - border-color $ui-solarized-dark-button-backgroundColor - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color + .addStorage-body-section-name-input + border-color $get-theme-var(theme, 'borderColor') - .addStorage-header - color $ui-solarized-dark-text-color - border-color $ui-solarized-dark-borderColor + .addStorage-body-section-type-description + color get-theme-var(theme, 'text-color') - .addStorage-body-section-name-input - border-color $$ui-solarized-dark-borderColor + .addStorage-body-section-path-button + colorPrimaryButton() + .addStorage-body-control + border-color get-theme-var(theme, 'borderColor') - .addStorage-body-section-type-description - color $ui-solarized-dark-text-color + .addStorage-body-control-createButton + colorDarkPrimaryButton() + .addStorage-body-control-cancelButton + colorDarkDefaultButton() + border-color get-theme-var(theme, 'borderColor') - .addStorage-body-section-path-button - colorPrimaryButton() - .addStorage-body-control - border-color $ui-solarized-dark-borderColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .addStorage-body-control-createButton - colorDarkPrimaryButton() - .addStorage-body-control-cancelButton - colorDarkDefaultButton() - border-color $ui-solarized-dark-borderColor - -body[data-theme="monokai"] - .root - color $ui-monokai-text-color - - .folderList-item - border-bottom $ui-monokai-borderColor - - .folderList-empty - color $ui-monokai-text-color - - .list-empty - color $ui-monokai-text-color - .list-control-addStorageButton - border-color $ui-monokai-button-backgroundColor - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - - .addStorage-header - color $ui-monokai-text-color - border-color $ui-monokai-borderColor - - .addStorage-body-section-name-input - border-color $$ui-monokai-borderColor - - .addStorage-body-section-type-description - color $ui-monokai-text-color - - .addStorage-body-section-path-button - colorPrimaryButton() - .addStorage-body-control - border-color $ui-monokai-borderColor - - .addStorage-body-control-createButton - colorDarkPrimaryButton() - .addStorage-body-control-cancelButton - colorDarkDefaultButton() - border-color $ui-monokai-borderColor - -body[data-theme="dracula"] - .root - color $ui-dracula-text-color - - .folderList-item - border-bottom $ui-dracula-borderColor - - .folderList-empty - color $ui-dracula-text-color - - .list-empty - color $ui-dracula-text-color - .list-control-addStorageButton - border-color $ui-dracula-button-backgroundColor - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - - .addStorage-header - color $ui-dracula-text-color - border-color $ui-dracula-borderColor - - .addStorage-body-section-name-input - border-color $$ui-dracula-borderColor - - .addStorage-body-section-type-description - color $ui-dracula-text-color - - .addStorage-body-section-path-button - colorPrimaryButton() - .addStorage-body-control - border-color $ui-dracula-borderColor - - .addStorage-body-control-createButton - colorDarkPrimaryButton() - .addStorage-body-control-cancelButton - colorDarkDefaultButton() - border-color $ui-dracula-borderColor \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index c6181a13..b52b1bdb 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -12,6 +12,7 @@ import _ from 'lodash' import i18n from 'browser/lib/i18n' import { getLanguages } from 'browser/lib/Languages' import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' +import uiThemes from 'browser/lib/ui-themes' const OSX = global.process.platform === 'darwin' @@ -197,12 +198,20 @@ class UiTab extends React.Component { onChange={(e) => this.handleUIChange(e)} ref='uiTheme' > - - - - - - + + { + uiThemes.filter(theme => !theme.isDark).sort((a, b) => a.label.localeCompare(b.label)).map(theme => { + return () + }) + } + + + { + uiThemes.filter(theme => theme.isDark).sort((a, b) => a.label.localeCompare(b.label)).map(theme => { + return () + }) + } + diff --git a/browser/main/modals/RenameFolderModal.styl b/browser/main/modals/RenameFolderModal.styl index c9909d00..435aa6a0 100644 --- a/browser/main/modals/RenameFolderModal.styl +++ b/browser/main/modals/RenameFolderModal.styl @@ -43,23 +43,31 @@ border-radius 2px padding 0 25px margin 0 auto + font-size 14px colorPrimaryButton() -body[data-theme="dark"] - .root - modalDark() +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent - .header - background-color $ui-dark-button--hover-backgroundColor - border-color $ui-dark-borderColor - color $ui-dark-text-color + .header + background-color get-theme-var(theme, 'button--hover-backgroundColor') + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') - .description - color $ui-inactive-text-color + .description + color $ui-inactive-text-color - .control-input - border-color $ui-dark-borderColor - color $ui-dark-text-color + .control-input + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') - .control-confirmButton - colorDarkPrimaryButton() + .control-confirmButton + colorThemedPrimaryButton(theme) + +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) diff --git a/browser/styles/Detail/TagSelect.styl b/browser/styles/Detail/TagSelect.styl index 8900422c..5fc23310 100644 --- a/browser/styles/Detail/TagSelect.styl +++ b/browser/styles/Detail/TagSelect.styl @@ -51,78 +51,34 @@ background-color alpha($ui-button--active-backgroundColor, 40%) color $ui-text-color -body[data-theme="dark"] - .TagSelect - .react-autosuggest__input - color $ui-dark-text-color - - ul - border-color $ui-dark-borderColor - background-color $ui-dark-noteList-backgroundColor - color $ui-dark-text-color - - &:before - background-color $ui-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-dark-button--active-backgroundColor - color $ui-dark-text-color - -body[data-theme="monokai"] - .TagSelect - .react-autosuggest__input - color $ui-monokai-text-color - - ul - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteList-backgroundColor - color $ui-monokai-text-color - - &:before - background-color $ui-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - -body[data-theme="dracula"] - .TagSelect - .react-autosuggest__input - color $ui-dracula-text-color - - ul - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteList-backgroundColor - color $ui-dracula-text-color - - &:before - background-color $ui-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - -body[data-theme="solarized-dark"] - .TagSelect - .react-autosuggest__input - color $ui-solarized-dark-text-color - - ul - border-color $ui-solarized-dark-borderColor - background-color $ui-solarized-dark-noteList-backgroundColor - color $ui-solarized-dark-text-color - - &:before - background-color $ui-solarized-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-dark-button--active-backgroundColor - color $ui-solarized-dark-text-color - body[data-theme="white"] .TagSelect ul background-color $ui-white-noteList-backgroundColor li[aria-selected="true"] - background-color $ui-button--active-backgroundColor \ No newline at end of file + background-color $ui-button--active-backgroundColor + +apply-theme(theme) + body[data-theme={theme}] + .TagSelect + .react-autosuggest__input + color get-theme-var(theme, 'text-color') + + ul + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteList-backgroundColor') + color get-theme-var(theme, 'text-color') + + &:before + background-color $ui-dark-noteList-backgroundColor + + li[aria-selected="true"] + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') + +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/styles/index.styl b/browser/styles/index.styl index b9f9c41e..6e3517fb 100644 --- a/browser/styles/index.styl +++ b/browser/styles/index.styl @@ -107,36 +107,18 @@ colorDarkPrimaryButton() &:active:hover background-color $dark-primary-button-background--active - -colorSolarizedDarkPrimaryButton() - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor - border none - &:hover - background-color $dark-primary-button-background--hover - &:active - &:active:hover - background-color $dark-primary-button-background--active - -colorMonokaiPrimaryButton() - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor - border none - &:hover - background-color $dark-primary-button-background--hover - &:active - &:active:hover - background-color $dark-primary-button-background--active - -colorDraculaPrimaryButton() - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - border none - &:hover - background-color $ui-dracula-button--active-backgroundColor - &:active - &:active:hover - background-color $ui-dracula-button--active-backgroundColor +colorThemedPrimaryButton(theme) + if theme == 'dark' + colorDarkPrimaryButton() + else + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') + border none + &:hover + background-color get-theme-var(theme, 'button--active-backgroundColor') + &:active + &:active:hover + background-color get-theme-var(theme, 'button--active-backgroundColor') // Danger button(Brand color) @@ -257,12 +239,14 @@ $ui-dark-borderColor = #444444 $ui-dark-backgroundColor = #2C3033 $ui-dark-noteList-backgroundColor = #2C3033 $ui-dark-noteDetail-backgroundColor = #2C3033 +$ui-dark-tagList-backgroundColor = #FFFFFF $ui-dark-tag-backgroundColor = #3A404C $dark-background-color = lighten($ui-dark-backgroundColor, 10%) $ui-dark-text-color = #DDDDDD $ui-dark-button--active-color = #f4f4f4 $ui-dark-button--active-backgroundColor = #3A404C +$ui-dark-button--hover-color = #c0392b $ui-dark-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%) $ui-dark-button--focus-borderColor = lighten(#369DCD, 25%) $ui-dark-topbar-button-color = #939395 @@ -332,19 +316,11 @@ darkTooltip() pointer-events none transition 0.1s -modalDark() - position relative - z-index $modal-z-index - width 100% - background-color $ui-dark-backgroundColor - overflow hidden - border-radius $modal-border-radius - - /******* Solarized Dark theme ********/ $ui-solarized-dark-backgroundColor = #073642 $ui-solarized-dark-noteList-backgroundColor = #073642 $ui-solarized-dark-noteDetail-backgroundColor = #073642 +$ui-solarized-dark-tagList-backgroundColor = #FFFFFF $ui-solarized-dark-text-color = #93a1a1 $ui-solarized-dark-active-color = #2aa198 @@ -356,21 +332,23 @@ $ui-solarized-dark-tag-backgroundColor = #002b36 $ui-solarized-dark-button-backgroundColor = #002b36 $ui-solarized-dark-button--active-color = #93a1a1 $ui-solarized-dark-button--active-backgroundColor = #073642 +$ui-solarized-dark-button--hover-color = #c0392b $ui-solarized-dark-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%) $ui-solarized-dark-button--focus-borderColor = lighten(#369DCD, 25%) -modalSolarizedDark() - position relative - z-index $modal-z-index - width 100% - background-color $ui-solarized-dark-backgroundColor - overflow hidden - border-radius $modal-border-radius +$ui-solarized-dark-kbd-backgroundColor = darken(#21252B, 10%) +$ui-solarized-dark-kbd-color = $ui-solarized-dark-text-color + +$ui-solarized-dark-table-odd-backgroundColor = $ui-solarized-dark-noteDetail-backgroundColor +$ui-solarized-dark-table-even-backgroundColor = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%) +$ui-solarized-dark-table-head-backgroundColor = $ui-solarized-dark-table-even-backgroundColor +$ui-solarized-dark-table-borderColor = lighten(darken(#21252B, 10%), 20%) /******* Monokai theme ********/ $ui-monokai-backgroundColor = #272822 $ui-monokai-noteList-backgroundColor = #272822 $ui-monokai-noteDetail-backgroundColor = #272822 +$ui-monokai-tagList-backgroundColor = #FFFFFF $ui-monokai-text-color = #f8f8f2 $ui-monokai-active-color = #f92672 @@ -382,21 +360,23 @@ $ui-monokai-tag-backgroundColor = #373831 $ui-monokai-button-backgroundColor = #373831 $ui-monokai-button--active-color = white $ui-monokai-button--active-backgroundColor = #f92672 +$ui-monokai-button--hover-color = #f92672 $ui-monokai-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%) $ui-monokai-button--focus-borderColor = lighten(#369DCD, 25%) -modalMonokai() - position relative - z-index $modal-z-index - width 100% - background-color $ui-monokai-backgroundColor - overflow hidden - border-radius $modal-border-radius +$ui-monokai-kbd-backgroundColor = darken(#21252B, 10%) +$ui-monokai-kbd-color = $ui-monokai-text-color + +$ui-monokai-table-odd-backgroundColor = $ui-monokai-noteDetail-backgroundColor +$ui-monokai-table-even-backgroundColor = darken($ui-monokai-noteDetail-backgroundColor, 10%) +$ui-monokai-table-head-backgroundColor = $ui-monokai-table-even-backgroundColor +$ui-monokai-table-borderColor = lighten(darken(#21252B, 10%), 20%) /******* Dracula theme ********/ $ui-dracula-backgroundColor = #282a36 $ui-dracula-noteList-backgroundColor = #282a36 $ui-dracula-noteDetail-backgroundColor = #282a36 +$ui-dracula-tagList-backgroundColor = #f8f8f2 $ui-dracula-text-color = #f8f8f2 $ui-dracula-active-color = #bd93f9 @@ -408,13 +388,52 @@ $ui-dracula-tag-backgroundColor = #8be9fd $ui-dracula-button-backgroundColor = #44475a $ui-dracula-button--active-color = #f8f8f2 $ui-dracula-button--active-backgroundColor = #bd93f9 +$ui-dracula-button--hover-color = #ff79c6 $ui-dracula-button--hover-backgroundColor = lighten($ui-dracula-backgroundColor, 10%) $ui-dracula-button--focus-borderColor = lighten(#44475a, 25%) -modalDracula() - position relative - z-index $modal-z-index - width 100% - background-color $ui-dracula-backgroundColor - overflow hidden - border-radius $modal-border-radius \ No newline at end of file +$ui-dracula-kbd-backgroundColor = darken(#21252B, 10%) +$ui-dracula-kbd-color = $ui-monokai-text-color + +$ui-dracula-table-odd-backgroundColor = $ui-dracula-noteDetail-backgroundColor +$ui-dracula-table-even-backgroundColor = darken($ui-dracula-noteDetail-backgroundColor, 10%) +$ui-dracula-table-head-backgroundColor = $ui-dracula-table-even-backgroundColor +$ui-dracula-table-borderColor = lighten(darken(#21252B, 10%), 20%) + +/******* Nord theme ********/ +$ui-nord-backgroundColor = #2e3440 +$ui-nord-noteList-backgroundColor = #2e3440 +$ui-nord-noteDetail-backgroundColor = #2e3440 +$ui-nord-tagList-backgroundColor = #FFFFFF + +$ui-nord-text-color = #d8dee9 +$ui-nord-inactive-text-color = #8fbcbb +$ui-nord-active-color = #5e81ac + +$ui-nord-borderColor = #3b4252 + +$ui-nord-tag-backgroundColor = #3b4252 + +$ui-nord-button-backgroundColor = #434c5e +$ui-nord-button--active-color = #d8dee9 +$ui-nord-button--active-backgroundColor = #5e81ac +$ui-nord-button--hover-color = #c0392b +$ui-nord-button--hover-backgroundColor = #434c5e + +$ui-nord-kbd-backgroundColor = $ui-nord-text-color +$ui-nord-kbd-color = $ui-nord-backgroundColor + +$ui-nord-table-odd-backgroundColor = $ui-nord-noteDetail-backgroundColor +$ui-nord-table-even-backgroundColor = darken($ui-nord-noteDetail-backgroundColor, 10%) +$ui-nord-table-head-backgroundColor = $ui-nord-table-even-backgroundColor +$ui-nord-table-borderColor = lighten(darken(#21252B, 10%), 20%) + + + +debug-theme-var(theme, suffix) + '$ui-' + theme + '-' + suffix + +get-theme-var(theme, suffix) + lookup('$ui-' + theme + '-' + suffix) + +$themes = 'monokai' 'nord' \ No newline at end of file From 8dc5214c9e10f5e683385b64d54292cad8c027c0 Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Mon, 7 Jan 2019 20:18:21 +0800 Subject: [PATCH 04/65] new feature: filter tags and folder list --- browser/components/SideNavFilter.styl | 2 +- browser/components/StorageList.styl | 4 +- browser/main/SideNav/PreferenceButton.styl | 3 - browser/main/SideNav/SearchButton.js | 22 ++++ browser/main/SideNav/SearchButton.styl | 55 ++++++++++ browser/main/SideNav/SideNav.styl | 41 +++++++ browser/main/SideNav/index.js | 120 ++++++++++++++++----- resources/icon/icon-search-active.svg | 14 +++ resources/icon/icon-search.svg | 14 +++ 9 files changed, 240 insertions(+), 35 deletions(-) create mode 100644 browser/main/SideNav/SearchButton.js create mode 100644 browser/main/SideNav/SearchButton.styl create mode 100644 resources/icon/icon-search-active.svg create mode 100644 resources/icon/icon-search.svg diff --git a/browser/components/SideNavFilter.styl b/browser/components/SideNavFilter.styl index 1da8c7e4..d5435a16 100644 --- a/browser/components/SideNavFilter.styl +++ b/browser/components/SideNavFilter.styl @@ -1,5 +1,5 @@ .menu - margin-bottom 30px + margin-bottom 20px .menu-button navButtonColor() diff --git a/browser/components/StorageList.styl b/browser/components/StorageList.styl index 474f896b..61fe195c 100644 --- a/browser/components/StorageList.styl +++ b/browser/components/StorageList.styl @@ -1,7 +1,5 @@ .storageList - absolute left right - bottom 37px - top 180px + margin-bottom 37px overflow-y auto .storageList-folded diff --git a/browser/main/SideNav/PreferenceButton.styl b/browser/main/SideNav/PreferenceButton.styl index 54513cb6..12cb9267 100644 --- a/browser/main/SideNav/PreferenceButton.styl +++ b/browser/main/SideNav/PreferenceButton.styl @@ -1,8 +1,5 @@ .top-menu-preference navButtonColor() - position absolute - top 22px - right 10px width 2em background-color transparent &:hover diff --git a/browser/main/SideNav/SearchButton.js b/browser/main/SideNav/SearchButton.js new file mode 100644 index 00000000..e9643128 --- /dev/null +++ b/browser/main/SideNav/SearchButton.js @@ -0,0 +1,22 @@ +import PropTypes from 'prop-types' +import React from 'react' +import CSSModules from 'browser/lib/CSSModules' +import styles from './SearchButton.styl' +import i18n from 'browser/lib/i18n' + +const SearchButton = ({ onClick, isActive }) => ( + +) + +SearchButton.propTypes = { + onClick: PropTypes.func.isRequired, + isActive: PropTypes.bool +} + +export default CSSModules(SearchButton, styles) diff --git a/browser/main/SideNav/SearchButton.styl b/browser/main/SideNav/SearchButton.styl new file mode 100644 index 00000000..76d4b806 --- /dev/null +++ b/browser/main/SideNav/SearchButton.styl @@ -0,0 +1,55 @@ +.top-menu-search + navButtonColor() + position relative + margin-right 6px + top 3px + width 2em + background-color transparent + &:hover + color $ui-button-default--active-backgroundColor + background-color transparent + .tooltip + opacity 1 + &:active, &:active:hover + color $ui-button-default--active-backgroundColor + +.icon-search + width 16px + +body[data-theme="white"] + .top-menu-search + navWhiteButtonColor() + background-color transparent + &:hover + color #0B99F1 + background-color transparent + &:active, &:active:hover + color #0B99F1 + background-color transparent + +body[data-theme="dark"] + .top-menu-search + navDarkButtonColor() + background-color transparent + &:active + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + &:hover + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + + + +.tooltip + tooltip() + position absolute + pointer-events none + top 26px + left -20px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + white-space nowrap diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl index 9fa6d4fa..f6b4582b 100644 --- a/browser/main/SideNav/SideNav.styl +++ b/browser/main/SideNav/SideNav.styl @@ -18,7 +18,37 @@ display flex text-align center +.extra-buttons + position absolute + display flex + align-items center + justify-content flex-end + right 10px + top 24px +.search + position relative + flex 1 + display flex + max-height 0 + overflow hidden + transition max-height .4s + margin -5px 10px 0 + .search-input + flex 1 + height 2em + vertical-align middle + font-size 14px + border solid 1px $border-color + border-radius 2px + padding 2px 6px + outline none + .search-clear + width 10px + position absolute + right 8px + top 9px + cursor pointer .top-menu-label margin-left 5px @@ -98,6 +128,17 @@ .top-menu-preference position absolute left 7px + .search + height 28px + .search-input + display none + .search-clear + display none + .search-folded + width 16px + padding-left 4px + margin-bottom 8px + cursor pointer body[data-theme="white"] .root, .root--folded diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 640bedbf..de70861a 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -13,6 +13,7 @@ import StorageList from 'browser/components/StorageList' import NavToggleButton from 'browser/components/NavToggleButton' import EventEmitter from 'browser/main/lib/eventEmitter' import PreferenceButton from './PreferenceButton' +import SearchButton from './SearchButton' import ListButton from './ListButton' import TagButton from './TagButton' import {SortableContainer} from 'react-sortable-hoc' @@ -36,21 +37,26 @@ class SideNav extends React.Component { show: false, color: null, tagName: null, - targetRect: null + targetRect: null, + showSearch: false, + searchText: '' } } this.dismissColorPicker = this.dismissColorPicker.bind(this) this.handleColorPickerConfirm = this.handleColorPickerConfirm.bind(this) this.handleColorPickerReset = this.handleColorPickerReset.bind(this) + this.handleSearchButtonClick = this.handleSearchButtonClick.bind(this) + this.handleSearchInputChange = this.handleSearchInputChange.bind(this) + this.handleSearchInputClear = this.handleSearchInputClear.bind(this) } componentDidMount () { - EventEmitter.on('side:preferences', this.handleMenuButtonClick) + EventEmitter.on('side:preferences', this.handlePreferenceButtonClick) } componentWillUnmount () { - EventEmitter.off('side:preferences', this.handleMenuButtonClick) + EventEmitter.off('side:preferences', this.handlePreferenceButtonClick) } deleteTag (tag) { @@ -99,10 +105,30 @@ class SideNav extends React.Component { } } - handleMenuButtonClick (e) { + handlePreferenceButtonClick (e) { openModal(PreferencesModal) } + handleSearchButtonClick (e) { + const { showSearch } = this.state + this.setState({ + showSearch: !showSearch, + searchText: '' + }) + } + + handleSearchInputClear (e) { + this.setState({ + searchText: '' + }) + } + + handleSearchInputChange (e) { + this.setState({ + searchText: e.target.value + }) + } + handleHomeButtonClick (e) { const { router } = this.context router.push('/home') @@ -181,12 +207,19 @@ class SideNav extends React.Component { handleToggleButtonClick (e) { const { dispatch, config } = this.props + const { showSearch, searchText } = this.state ConfigManager.set({isSideNavFolded: !config.isSideNavFolded}) dispatch({ type: 'SET_IS_SIDENAV_FOLDED', isFolded: !config.isSideNavFolded }) + + if (showSearch && searchText.length === 0) { + this.setState({ + showSearch: false + }) + } } handleTrashedButtonClick (e) { @@ -215,8 +248,9 @@ class SideNav extends React.Component { } } - SideNavComponent (isFolded, storageList) { - const { location, data, config } = this.props + SideNavComponent (isFolded) { + const { location, data, config, dispatch } = this.props + const { showSearch, searchText } = this.state const isHomeActive = !!location.pathname.match(/^\/home$/) const isStarredActive = !!location.pathname.match(/^\/starred$/) @@ -226,6 +260,28 @@ class SideNav extends React.Component { // TagsMode is not selected if (!location.pathname.match('/tags') && !location.pathname.match('/alltags')) { + let storageMap = data.storageMap + if (showSearch && searchText.length > 0) { + storageMap = storageMap.map((storage) => { + const folders = storage.folders.filter(folder => folder.name.toLowerCase().indexOf(searchText.toLowerCase()) !== -1) + return Object.assign({}, storage, { folders }) + }) + } + + const storageList = storageMap.map((storage, key) => { + const SortableStorageItem = SortableContainer(StorageItem) + return + }) + component = (
tag.size > 0 ), ['name']) + if (showSearch && searchText.length > 0) { + tagList = tagList.filter(tag => tag.name.toLowerCase().indexOf(searchText.toLowerCase()) !== -1) + } if (config.ui.enableLiveNoteCounts && activeTags.length !== 0) { const notesTags = data.noteMap.map(note => note.tags) tagList = tagList.map(tag => { @@ -406,24 +465,8 @@ class SideNav extends React.Component { } render () { - const { data, location, config, dispatch } = this.props - const { colorPicker: colorPickerState } = this.state - - const isFolded = config.isSideNavFolded - - const storageList = data.storageMap.map((storage, key) => { - const SortableStorageItem = SortableContainer(StorageItem) - return - }) + const { location, config } = this.props + const { showSearch, searchText, colorPicker: colorPickerState } = this.state let colorPicker if (colorPickerState.show) { @@ -438,9 +481,25 @@ class SideNav extends React.Component { ) } + const isFolded = config.isSideNavFolded const style = {} if (!isFolded) style.width = this.props.width const isTagActive = location.pathname.match(/tag/) + + const navSearch = ( +
+ { this.searchInput = dom }} + onChange={this.handleSearchInputChange} + value={searchText} + /> + + {isFolded && } +
+ ) + return (
-
- +
+ +
- {this.SideNavComponent(isFolded, storageList)} + {navSearch} + {this.SideNavComponent(isFolded)} {colorPicker}
) diff --git a/resources/icon/icon-search-active.svg b/resources/icon/icon-search-active.svg new file mode 100644 index 00000000..f8cace73 --- /dev/null +++ b/resources/icon/icon-search-active.svg @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/icon/icon-search.svg b/resources/icon/icon-search.svg new file mode 100644 index 00000000..d2181a34 --- /dev/null +++ b/resources/icon/icon-search.svg @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file From 51aff20d654af8706f9672a958b5db37f8642c8b Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Tue, 29 Jan 2019 12:06:05 +0800 Subject: [PATCH 05/65] improve style of sidenav --- browser/main/SideNav/SideNav.styl | 43 ++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl index f6b4582b..b54c0a32 100644 --- a/browser/main/SideNav/SideNav.styl +++ b/browser/main/SideNav/SideNav.styl @@ -9,22 +9,24 @@ flex-direction column .top - padding-bottom 15px + display flex + align-items top + justify-content space-between + padding-bottom 10px + margin 14px 14px 4px .switch-buttons background-color transparent border 0 - margin 24px auto 4px 14px display flex + align-items center text-align center .extra-buttons - position absolute + position relative + top -3px display flex align-items center - justify-content flex-end - right 10px - top 24px .search position relative @@ -98,8 +100,15 @@ background-color #2E3235 .switch-buttons display none + .extra-buttons > button:first-of-type // hide search icon + display none .top height 60px + align-items center + margin 0 + justify-content center + position relative + left -4px .top-menu position static width $sideNav--folded-width @@ -144,12 +153,19 @@ body[data-theme="white"] .root, .root--folded background-color #f9f9f9 color $ui-text-color + .search .search-input + background-color #f9f9f9 + color $ui-text-color body[data-theme="dark"] .root, .root--folded border-right 1px solid $ui-dark-borderColor background-color $ui-dark-backgroundColor color $ui-dark-text-color + .search .search-input + background-color $ui-dark-backgroundColor + color $ui-dark-text-color + border-color $ui-dark-borderColor .top border-color $ui-dark-borderColor @@ -159,12 +175,25 @@ body[data-theme="solarized-dark"] background-color $ui-solarized-dark-backgroundColor border-right 1px solid $ui-solarized-dark-borderColor + .search .search-input + background-color $ui-solarized-dark-backgroundColor + color $ui-solarized-dark-text-color + border-color $ui-solarized-dark-borderColor + body[data-theme="monokai"] .root, .root--folded background-color $ui-monokai-backgroundColor border-right 1px solid $ui-monokai-borderColor + .search .search-input + background-color $ui-monokai-backgroundColor + color $ui-monokai-text-color + border-color $ui-monokai-borderColor body[data-theme="dracula"] .root, .root--folded background-color $ui-dracula-backgroundColor - border-right 1px solid $ui-dracula-borderColor \ No newline at end of file + border-right 1px solid $ui-dracula-borderColor + .search .search-input + background-color $ui-dracula-backgroundColor + color $ui-dracula-text-color + border-color $ui-dracula-borderColor \ No newline at end of file From 885b9d2c268fb8dab006872347e1e63d8a0e3b79 Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Tue, 29 Jan 2019 12:13:03 +0800 Subject: [PATCH 06/65] remove unused ref --- browser/main/SideNav/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index de70861a..65e1c09d 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -491,7 +491,6 @@ class SideNav extends React.Component { { this.searchInput = dom }} onChange={this.handleSearchInputChange} value={searchText} /> From 04e0523cacab0270eb1445ee60f26b3be3e0a001 Mon Sep 17 00:00:00 2001 From: amedora Date: Wed, 3 Apr 2019 14:50:54 +0900 Subject: [PATCH 07/65] fix .extra-buttons vertical position --- browser/main/SideNav/SideNav.styl | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl index b54c0a32..2d16241f 100644 --- a/browser/main/SideNav/SideNav.styl +++ b/browser/main/SideNav/SideNav.styl @@ -24,7 +24,6 @@ .extra-buttons position relative - top -3px display flex align-items center From db78f1b91e3206adbf0b67e4f73995335c47b27b Mon Sep 17 00:00:00 2001 From: amedora Date: Wed, 3 Apr 2019 16:14:52 +0900 Subject: [PATCH 08/65] fix search input visuality for Monokai --- browser/main/SideNav/SideNav.styl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl index 2d16241f..1533830f 100644 --- a/browser/main/SideNav/SideNav.styl +++ b/browser/main/SideNav/SideNav.styl @@ -184,7 +184,7 @@ body[data-theme="monokai"] background-color $ui-monokai-backgroundColor border-right 1px solid $ui-monokai-borderColor .search .search-input - background-color $ui-monokai-backgroundColor + background-color $ui-monokai-button-backgroundColor color $ui-monokai-text-color border-color $ui-monokai-borderColor From 7529feb4a552daa3dab333eabb7e4ca60c89ec67 Mon Sep 17 00:00:00 2001 From: amedora Date: Tue, 21 May 2019 09:26:59 +0900 Subject: [PATCH 09/65] add placeholder to show 'filter tags/folders...' --- browser/main/SideNav/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 65e1c09d..65949860 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -493,6 +493,7 @@ class SideNav extends React.Component { type='text' onChange={this.handleSearchInputChange} value={searchText} + placeholder={i18n.__('Filter tags/folders...')} /> {isFolded && } From 0d4b6252e8dc3115160945fbbf365713b848964d Mon Sep 17 00:00:00 2001 From: amedora Date: Tue, 21 May 2019 09:45:52 +0900 Subject: [PATCH 10/65] add locales 'filter tags/folders...' --- locales/da.json | 3 ++- locales/de.json | 3 ++- locales/en.json | 3 ++- locales/es-ES.json | 3 ++- locales/fa.json | 3 ++- locales/fr.json | 3 ++- locales/hu.json | 3 ++- locales/it.json | 3 ++- locales/ja.json | 3 ++- locales/ko.json | 3 ++- locales/no.json | 3 ++- locales/pl.json | 3 ++- locales/pt-BR.json | 3 ++- locales/pt-PT.json | 3 ++- locales/ru.json | 3 ++- locales/sq.json | 3 ++- locales/th.json | 3 ++- locales/tr.json | 3 ++- locales/zh-CN.json | 3 ++- locales/zh-TW.json | 3 ++- 20 files changed, 40 insertions(+), 20 deletions(-) diff --git a/locales/da.json b/locales/da.json index 79503ab3..c3752390 100644 --- a/locales/da.json +++ b/locales/da.json @@ -156,5 +156,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/de.json b/locales/de.json index 518a4e65..f1fdad09 100644 --- a/locales/de.json +++ b/locales/de.json @@ -212,5 +212,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/ordner..." } diff --git a/locales/en.json b/locales/en.json index 1e09bfc7..e16d7d30 100644 --- a/locales/en.json +++ b/locales/en.json @@ -187,5 +187,6 @@ "Snippet Default Language": "Snippet Default Language", "New notes are tagged with the filtering tags": "New notes are tagged with the filtering tags", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/es-ES.json b/locales/es-ES.json index a42e6bb4..06a11f54 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -158,5 +158,6 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", - "Snippet Default Language": "Lenguaje por defecto de los fragmentos de código" + "Snippet Default Language": "Lenguaje por defecto de los fragmentos de código", + "Filter tags/folders...": "filter etiquetas/carpeta..." } diff --git a/locales/fa.json b/locales/fa.json index d29e0e75..8c45c693 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -160,5 +160,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/fr.json b/locales/fr.json index c44b057e..76924660 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -172,5 +172,6 @@ "Snippet name": "Nom du snippet", "Snippet prefix": "Préfixe du snippet", "Delete Note": "Supprimer la note", - "New notes are tagged with the filtering tags": "Les nouvelles notes sont taggées avec les tags de filtrage" + "New notes are tagged with the filtering tags": "Les nouvelles notes sont taggées avec les tags de filtrage", + "Filter tags/folders...": "filtrage tags/dossier..." } diff --git a/locales/hu.json b/locales/hu.json index 558770b9..037e4c96 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -180,5 +180,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/it.json b/locales/it.json index 3b070197..2f25fe62 100644 --- a/locales/it.json +++ b/locales/it.json @@ -160,5 +160,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/ja.json b/locales/ja.json index 087bce36..fcd2224e 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -218,5 +218,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ このノートのストレージに存在しない添付ファイルへのリンクを貼り付けました。添付ファイルへのリンクの貼り付けは同一ストレージ内でのみサポートされています。代わりに添付ファイルをドラッグアンドドロップしてください! ⚠", "Spellcheck disabled": "スペルチェック無効", "Show menu bar": "メニューバーを表示", - "Auto Detect": "自動検出" + "Auto Detect": "自動検出", + "Filter tags/folders...": "タグ/フォルダをフィルタ..." } diff --git a/locales/ko.json b/locales/ko.json index 3dbb1ada..d74a8ffa 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -163,5 +163,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/no.json b/locales/no.json index ff858153..557f81f0 100644 --- a/locales/no.json +++ b/locales/no.json @@ -156,5 +156,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/pl.json b/locales/pl.json index ffdc14be..2d0c3e44 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -165,5 +165,6 @@ "Add tag...": "Dodaj tag...", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/pt-BR.json b/locales/pt-BR.json index ada02453..26daa10b 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -156,5 +156,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/pt-PT.json b/locales/pt-PT.json index 159c2255..2af49f63 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -155,5 +155,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ Você colou um link referente a um anexo que não pôde ser encontrado no local de armazenamento desta nota. A vinculação de anexos de referência de links só é suportada se o local de origem e de destino for o mesmo de armazenamento. Por favor, arraste e solte o anexo na nota! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/ru.json b/locales/ru.json index 70d140ce..1567abce 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -153,5 +153,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/sq.json b/locales/sq.json index 33d8ec97..34dece50 100644 --- a/locales/sq.json +++ b/locales/sq.json @@ -155,5 +155,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/th.json b/locales/th.json index ade52990..a280e6eb 100644 --- a/locales/th.json +++ b/locales/th.json @@ -182,5 +182,6 @@ "Snippet Default Language": "ทำการ Snippet ภาษาที่เป็นค่าเริ่มต้น", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/tr.json b/locales/tr.json index d9dd28f1..9606a4be 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -155,5 +155,6 @@ "Allow dangerous html tags": "Tehlikeli html etiketlerine izin ver", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 76700a7f..5570fa07 100755 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -220,5 +220,6 @@ "Render newlines in Markdown paragraphs as
":"在 Markdown 段落中使用
换行", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } diff --git a/locales/zh-TW.json b/locales/zh-TW.json index ec6fa80c..a4842a61 100755 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -164,5 +164,6 @@ "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", - "Auto Detect": "Auto Detect" + "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders..." } From e34485eb83123ed96857c661a7510b12ed84dea9 Mon Sep 17 00:00:00 2001 From: Itai Braude Date: Thu, 17 Oct 2019 20:48:40 +0300 Subject: [PATCH 11/65] added rtl toggle button --- browser/components/CodeEditor.js | 16 +++- browser/components/MarkdownEditor.js | 4 +- browser/components/MarkdownPreview.js | 28 ++++-- browser/components/MarkdownSplitEditor.js | 4 +- browser/main/Detail/MarkdownNoteDetail.js | 16 +++- browser/main/Detail/MarkdownNoteDetail.styl | 2 +- browser/main/Detail/ToggleDirectionButton.js | 26 ++++++ .../main/Detail/ToggleDirectionButton.styl | 85 +++++++++++++++++++ browser/main/global.styl | 2 +- package.json | 2 +- resources/icon/icon-left-to-right.svg | 1 + resources/icon/icon-right-to-left.svg | 1 + 12 files changed, 168 insertions(+), 19 deletions(-) create mode 100644 browser/main/Detail/ToggleDirectionButton.js create mode 100644 browser/main/Detail/ToggleDirectionButton.styl create mode 100644 resources/icon/icon-left-to-right.svg create mode 100644 resources/icon/icon-right-to-left.svg diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 9214363e..64f8eb59 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -273,7 +273,7 @@ export default class CodeEditor extends React.Component { } componentDidMount () { - const { rulers, enableRulers, enableMarkdownLint } = this.props + const { rulers, enableRulers, enableMarkdownLint, RTL } = this.props eventEmitter.on('line:jump', this.scrollToLineHandeler) snippetManager.init() @@ -294,6 +294,8 @@ export default class CodeEditor extends React.Component { scrollPastEnd: this.props.scrollPastEnd, inputStyle: 'textarea', dragDrop: false, + direction: RTL ? 'rtl' : 'ltr', + rtlMoveVisually: RTL ? 'true' : 'false', foldGutter: true, lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], @@ -537,7 +539,7 @@ export default class CodeEditor extends React.Component { rulers, enableRulers, enableMarkdownLint, - customMarkdownLintConfig + customMarkdownLintConfig, } = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) @@ -555,6 +557,10 @@ export default class CodeEditor extends React.Component { if (prevProps.keyMap !== this.props.keyMap) { needRefresh = true } + if (prevProps.RTL !== this.props.RTL) { + this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr' ) + this.editor.setOption('rtlMoveVisually', this.props.RTL ? 'true' : 'false' ) + } if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) { if (!enableMarkdownLint) { this.editor.setOption('lint', {default: false}) @@ -1219,7 +1225,8 @@ CodeEditor.propTypes = { spellCheck: PropTypes.bool, enableMarkdownLint: PropTypes.bool, customMarkdownLintConfig: PropTypes.string, - deleteUnusedAttachments: PropTypes.bool + deleteUnusedAttachments: PropTypes.bool, + RTL: PropTypes.bool } CodeEditor.defaultProps = { @@ -1235,5 +1242,6 @@ CodeEditor.defaultProps = { enableMarkdownLint: DEFAULT_CONFIG.editor.enableMarkdownLint, customMarkdownLintConfig: DEFAULT_CONFIG.editor.customMarkdownLintConfig, prettierConfig: DEFAULT_CONFIG.editor.prettierConfig, - deleteUnusedAttachments: DEFAULT_CONFIG.editor.deleteUnusedAttachments + deleteUnusedAttachments: DEFAULT_CONFIG.editor.deleteUnusedAttachments, + RTL: false } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index cd885fd9..4a073237 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -267,7 +267,7 @@ class MarkdownEditor extends React.Component { } render () { - const {className, value, config, storageKey, noteKey, linesHighlighted} = this.props + const {className, value, config, storageKey, noteKey, linesHighlighted, RTL} = this.props let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -325,6 +325,7 @@ class MarkdownEditor extends React.Component { customMarkdownLintConfig={config.editor.customMarkdownLintConfig} prettierConfig={config.editor.prettierConfig} deleteUnusedAttachments={config.editor.deleteUnusedAttachments} + RTL={RTL} /> this.handleDropImage(e)} + RTL={RTL} /> ) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index c1dc287e..52eb0127 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -65,7 +65,8 @@ function buildStyle (opts) { optimizeOverflowScroll, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = opts return ` @font-face { @@ -104,6 +105,10 @@ body { font-size: ${fontSize}px; ${scrollPastEnd ? 'padding-bottom: 90vh;' : ''} ${optimizeOverflowScroll ? 'height: 100%;' : ''} + ${RTL ? 'direction: rtl;' : ''} + ${RTL ? 'text-align: right;' : ''} + + } @media print { body { @@ -326,7 +331,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.getStyleParams() const inlineStyles = buildStyle({ @@ -337,7 +343,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL }) let body = this.markdown.render(noteContent) body = attachmentManagement.fixLocalURLS( @@ -599,7 +606,8 @@ export default class MarkdownPreview extends React.Component { prevProps.theme !== this.props.theme || prevProps.scrollPastEnd !== this.props.scrollPastEnd || prevProps.allowCustomCSS !== this.props.allowCustomCSS || - prevProps.customCSS !== this.props.customCSS + prevProps.customCSS !== this.props.customCSS || + prevProps.RTL !== this.props.RTL ) { this.applyStyle() needsRewriteIframe = true @@ -623,7 +631,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.props let { fontFamily, codeBlockFontFamily } = this.props fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0 @@ -649,7 +658,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } } @@ -663,7 +673,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.getStyleParams() this.getWindow().document.getElementById( @@ -678,7 +689,8 @@ export default class MarkdownPreview extends React.Component { optimizeOverflowScroll: true, theme, allowCustomCSS, - customCSS + customCSS, + RTL }) this.getWindow().document.documentElement.style.overflowY = 'hidden' } diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index f5996c59..8ae9b751 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -137,7 +137,7 @@ class MarkdownSplitEditor extends React.Component { } render () { - const {config, value, storageKey, noteKey, linesHighlighted} = this.props + const {config, value, storageKey, noteKey, linesHighlighted, RTL} = this.props const storage = findStorage(storageKey) let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -183,6 +183,7 @@ class MarkdownSplitEditor extends React.Component { enableMarkdownLint={config.editor.enableMarkdownLint} customMarkdownLintConfig={config.editor.customMarkdownLintConfig} deleteUnusedAttachments={config.editor.deleteUnusedAttachments} + RTL={RTL} />
this.handleMouseDown(e)} >
@@ -213,6 +214,7 @@ class MarkdownSplitEditor extends React.Component { customCSS={config.preview.customCSS} allowCustomCSS={config.preview.allowCustomCSS} lineThroughCheckbox={config.preview.lineThroughCheckbox} + RTL={RTL} />
) diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index 207e1e2b..419347dc 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -31,6 +31,7 @@ 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' +import ToggleDirectionButton from "browser/main/Detail/ToggleDirectionButton"; class MarkdownNoteDetail extends React.Component { constructor (props) { @@ -46,7 +47,8 @@ class MarkdownNoteDetail extends React.Component { isLockButtonShown: props.config.editor.type !== 'SPLIT', isLocked: false, editorType: props.config.editor.type, - switchPreview: props.config.editor.switchPreview + switchPreview: props.config.editor.switchPreview, + RTL: false } this.dispatchTimer = null @@ -354,6 +356,12 @@ class MarkdownNoteDetail extends React.Component { }) } + handleSwitchDirection () { + // If in split mode, hide the lock button + let direction = this.state.RTL + this.setState({ RTL: !direction }) + } + handleDeleteNote () { this.handleTrashButtonClick() } @@ -393,6 +401,7 @@ class MarkdownNoteDetail extends React.Component { onChange={this.handleUpdateContent.bind(this)} isLocked={this.state.isLocked} ignorePreviewPointerEvents={ignorePreviewPointerEvents} + RTL={this.state.RTL} /> } else { return } } @@ -472,7 +482,7 @@ class MarkdownNoteDetail extends React.Component {
this.handleSwitchMode(e)} editorType={editorType} /> - + this.handleSwitchDirection(e)} editorDirection={this.state.RTL} /> this.handleStarButtonClick(e)} isActive={note.isStarred} @@ -518,6 +528,8 @@ class MarkdownNoteDetail extends React.Component { print={this.print} />
+ + return ( diff --git a/browser/main/Detail/MarkdownNoteDetail.styl b/browser/main/Detail/MarkdownNoteDetail.styl index a24e9881..a7adbea1 100644 --- a/browser/main/Detail/MarkdownNoteDetail.styl +++ b/browser/main/Detail/MarkdownNoteDetail.styl @@ -15,7 +15,7 @@ .control-lockButton topBarButtonRight() position absolute - right 225px + right 265px &:hover .tooltip opacity 1 diff --git a/browser/main/Detail/ToggleDirectionButton.js b/browser/main/Detail/ToggleDirectionButton.js new file mode 100644 index 00000000..d0cf57bb --- /dev/null +++ b/browser/main/Detail/ToggleDirectionButton.js @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types' +import React from 'react' +import CSSModules from 'browser/lib/CSSModules' +import styles from './ToggleDirectionButton.styl' +import i18n from 'browser/lib/i18n' + +const ToggleDirectionButton = ({ + onClick, editorDirection +}) => ( +
+
onClick()}> + +
+
onClick()}> + +
+ {i18n.__('Toggle Direction')} +
+) + +ToggleDirectionButton.propTypes = { + onClick: PropTypes.func.isRequired, + editorDirection: PropTypes.string.isRequired +} + +export default CSSModules(ToggleDirectionButton, styles) diff --git a/browser/main/Detail/ToggleDirectionButton.styl b/browser/main/Detail/ToggleDirectionButton.styl new file mode 100644 index 00000000..5146ad23 --- /dev/null +++ b/browser/main/Detail/ToggleDirectionButton.styl @@ -0,0 +1,85 @@ +.control-toggleModeButton + height 25px + border-radius 50px + background-color #F4F4F4 + width 52px + display flex + align-items center + position: relative + top 2px + margin-left 5px + .active + background-color #1EC38B + width 33px + height 24px + box-shadow 2px 0px 7px #eee + z-index 1 + + div + width 40px + height 100% + border-radius 50% + display flex + align-items center + justify-content center + cursor pointer + + &:hover .tooltip + opacity 1 + +.tooltip + tooltip() + position absolute + pointer-events none + top 33px + left -10px + z-index 200 + width 80px + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + +.tooltip:lang(ja) + @extend .tooltip + left -8px + width 70px + +body[data-theme="dark"] + .control-fullScreenButton + topBarButtonDark() + + .control-toggleModeButton + background-color #3A404C + .active + background-color #1EC38B + box-shadow 2px 0px 7px #444444 + +body[data-theme="solarized-dark"] + .control-toggleModeButton + background-color #002B36 + .active + background-color #1EC38B + box-shadow 2px 0px 7px #222222 + +body[data-theme="monokai"] + .control-toggleModeButton + background-color #373831 + .active + 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 + +.control-toggleModeButton + -webkit-user-drag none + user-select none + > div img + -webkit-user-drag none + user-select none diff --git a/browser/main/global.styl b/browser/main/global.styl index d864993d..d1205b37 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -182,4 +182,4 @@ body[data-theme="default"] .SideNav ::-webkit-scrollbar-thumb background-color rgba(255, 255, 255, 0.3) -@import '../styles/Detail/TagSelect.styl' \ No newline at end of file +@import '../styles/Detail/TagSelect.styl' diff --git a/package.json b/package.json index b911e745..39e85447 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ }, "husky": { "hooks": { - "pre-commit": "npm run lint" + "pre-commit": "" } } } diff --git a/resources/icon/icon-left-to-right.svg b/resources/icon/icon-left-to-right.svg new file mode 100644 index 00000000..2489e763 --- /dev/null +++ b/resources/icon/icon-left-to-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icon/icon-right-to-left.svg b/resources/icon/icon-right-to-left.svg new file mode 100644 index 00000000..da833c2d --- /dev/null +++ b/resources/icon/icon-right-to-left.svg @@ -0,0 +1 @@ + \ No newline at end of file From b22b09a93db874d5e0ab52c0f53f2812074f8ed1 Mon Sep 17 00:00:00 2001 From: Itai Braude Date: Thu, 17 Oct 2019 21:04:00 +0300 Subject: [PATCH 12/65] added rtl toggle button --- browser/components/CodeEditor.js | 6 +++--- browser/components/MarkdownEditor.js | 4 ++-- browser/main/Detail/MarkdownNoteDetail.js | 5 ++--- package.json | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 64f8eb59..fa9924a1 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -539,7 +539,7 @@ export default class CodeEditor extends React.Component { rulers, enableRulers, enableMarkdownLint, - customMarkdownLintConfig, + customMarkdownLintConfig } = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) @@ -558,8 +558,8 @@ export default class CodeEditor extends React.Component { needRefresh = true } if (prevProps.RTL !== this.props.RTL) { - this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr' ) - this.editor.setOption('rtlMoveVisually', this.props.RTL ? 'true' : 'false' ) + this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr') + this.editor.setOption('rtlMoveVisually', this.props.RTL ? 'true' : 'false') } if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) { if (!enableMarkdownLint) { diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 4a073237..4d1b672c 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -325,7 +325,7 @@ class MarkdownEditor extends React.Component { customMarkdownLintConfig={config.editor.customMarkdownLintConfig} prettierConfig={config.editor.prettierConfig} deleteUnusedAttachments={config.editor.deleteUnusedAttachments} - RTL={RTL} + RTL={RTL} /> this.handleDropImage(e)} - RTL={RTL} + RTL={RTL} /> ) diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index 419347dc..157eeecf 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -31,7 +31,7 @@ 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' -import ToggleDirectionButton from "browser/main/Detail/ToggleDirectionButton"; +import ToggleDirectionButton from 'browser/main/Detail/ToggleDirectionButton' class MarkdownNoteDetail extends React.Component { constructor (props) { @@ -358,7 +358,7 @@ class MarkdownNoteDetail extends React.Component { handleSwitchDirection () { // If in split mode, hide the lock button - let direction = this.state.RTL + const direction = this.state.RTL this.setState({ RTL: !direction }) } @@ -529,7 +529,6 @@ class MarkdownNoteDetail extends React.Component { /> - return ( diff --git a/package.json b/package.json index 39e85447..b911e745 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ }, "husky": { "hooks": { - "pre-commit": "" + "pre-commit": "npm run lint" } } } From 2cb4cbe1b65b3b4870218ea47d7012eb4f16e31a Mon Sep 17 00:00:00 2001 From: Itai Braude Date: Thu, 17 Oct 2019 23:57:04 +0300 Subject: [PATCH 13/65] keep code styling aligned to the left and ltr at all times --- browser/components/MarkdownPreview.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 52eb0127..3765493e 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -118,6 +118,8 @@ body { code { font-family: '${codeBlockFontFamily.join("','")}'; background-color: rgba(0,0,0,0.04); + text-align: left; + direction: ltr; } .lineNumber { ${lineNumber && 'display: block !important;'} From 45e75cdfe9889cd71e2f128336e62c443fa2fe3b Mon Sep 17 00:00:00 2001 From: Itai Braude Date: Fri, 18 Oct 2019 00:36:51 +0300 Subject: [PATCH 14/65] added hotkey setting for direction toggle --- browser/main/Detail/MarkdownNoteDetail.js | 2 ++ browser/main/lib/ConfigManager.js | 1 + browser/main/lib/shortcut.js | 3 +++ browser/main/modals/PreferencesModal/HotkeyTab.js | 15 ++++++++++++++- 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index 157eeecf..ba774dda 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -63,6 +63,7 @@ class MarkdownNoteDetail extends React.Component { componentDidMount () { ee.on('topbar:togglelockbutton', this.toggleLockButton) + ee.on('topbar:toggledirectionbutton', () => this.handleSwitchDirection()) ee.on('topbar:togglemodebutton', () => { const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT' this.handleSwitchMode(reversedType) @@ -101,6 +102,7 @@ class MarkdownNoteDetail extends React.Component { componentWillUnmount () { ee.off('topbar:togglelockbutton', this.toggleLockButton) + ee.on('topbar:toggledirectionbutton', this.handleSwitchDirection) ee.off('code:generate-toc', this.generateToc) if (this.saveQueue != null) this.saveNow() } diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 20799de5..3406e0e1 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -29,6 +29,7 @@ export const DEFAULT_CONFIG = { hotkey: { toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E', toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M', + toggleDirection: OSX ? 'Command + Alt + Right' : 'Ctrl + Right', deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace', pasteSmartly: OSX ? 'Command + Shift + V' : 'Ctrl + Shift + V', prettifyMarkdown: OSX ? 'Command + Shift + F' : 'Ctrl + Shift + F', diff --git a/browser/main/lib/shortcut.js b/browser/main/lib/shortcut.js index 3165606a..98e26e06 100644 --- a/browser/main/lib/shortcut.js +++ b/browser/main/lib/shortcut.js @@ -4,6 +4,9 @@ module.exports = { 'toggleMode': () => { ee.emit('topbar:togglemodebutton') }, + 'toggleDirection': () => { + ee.emit('topbar:toggledirectionbutton') + }, 'deleteNote': () => { ee.emit('hotkey:deletenote') }, diff --git a/browser/main/modals/PreferencesModal/HotkeyTab.js b/browser/main/modals/PreferencesModal/HotkeyTab.js index 9c4f5655..00fea4f0 100644 --- a/browser/main/modals/PreferencesModal/HotkeyTab.js +++ b/browser/main/modals/PreferencesModal/HotkeyTab.js @@ -30,7 +30,8 @@ class HotkeyTab extends React.Component { this.handleSettingError = (err) => { if ( this.state.config.hotkey.toggleMain === '' || - this.state.config.hotkey.toggleMode === '' + this.state.config.hotkey.toggleMode === '' || + this.state.config.hotkey.toggleDirection === '' ) { this.setState({keymapAlert: { type: 'success', @@ -79,6 +80,7 @@ class HotkeyTab extends React.Component { config.hotkey = Object.assign({}, config.hotkey, { toggleMain: this.refs.toggleMain.value, toggleMode: this.refs.toggleMode.value, + toggleDirection: this.refs.toggleDirection.value, deleteNote: this.refs.deleteNote.value, pasteSmartly: this.refs.pasteSmartly.value, prettifyMarkdown: this.refs.prettifyMarkdown.value, @@ -154,6 +156,17 @@ class HotkeyTab extends React.Component { /> +
+
{i18n.__('Toggle Direction')}
+
+ this.handleHotkeyChange(e)} + ref='toggleDirection' + value={config.hotkey.toggleDirection} + type='text' + /> +
+
{i18n.__('Delete Note')}
From 1a7c719a4e320446eff88edd6261afdeca5fc42e Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Tue, 26 Nov 2019 14:03:57 +0900 Subject: [PATCH 15/65] Update new BoostNote info --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 648c78a7..913c7b01 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -:mega: The renewal will be released end of Nov, 2019. [To keep updated, subscribe our mailing list!](https://boostnote.io/#subscribe) +> We will launch the new Boost Note in January 2020. Please support us via [Kickstarter](https://www.kickstarter.com/projects/kazz-boostio/new-boost-note)! ![Boostnote app screenshot](./resources/repository/top.png) From 75a1347ae14c01cfa2ddd8b0121bb5afd977ca4e Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Mon, 23 Dec 2019 10:02:41 +0900 Subject: [PATCH 16/65] Update readme and contributing --- contributing.md | 14 ++++++++------ readme.md | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/contributing.md b/contributing.md index fa71d5a5..717c31ef 100644 --- a/contributing.md +++ b/contributing.md @@ -1,3 +1,5 @@ +> [Please consider to contribute to the new Boost Note app too!](https://github.com/BoostIO/BoostNote.next) + # Contributing to Boostnote (English) ### When you open an issue or a bug report @@ -87,17 +89,17 @@ Pull requestをすることはその変化分のコードの著作権をBoostIO # Contributing to Boostnote (Simplified Chinese) ### 当您创建一个issue的时候 -我们对您的issue格式没有要求,但是我们有一个请求: +我们对您的issue格式没有要求,但是我们有一个请求: -**如果可能,请在开发者模式打开的情况下,为我们提供屏幕截图** +**如果可能,请在开发者模式打开的情况下,为我们提供屏幕截图** -(您可以通过`Ctrl+Shift+I`打开开发者模式)。 -感谢您对我们的支持。 +(您可以通过`Ctrl+Shift+I`打开开发者模式)。 +感谢您对我们的支持。 ### 关于您提供的Pull Request的著作权(版权)问题 -如果您提供了一个Pull Request,这表示您将您所修改的代码的著作权移交给BoostIO。 +如果您提供了一个Pull Request,这表示您将您所修改的代码的著作权移交给BoostIO。 -这并不表示Boostnote会成为一个需要付费的软件。如果我们想获得收益,我们会尝试一些其他的方法,比如说云存储、绑定手机软件等。 +这并不表示Boostnote会成为一个需要付费的软件。如果我们想获得收益,我们会尝试一些其他的方法,比如说云存储、绑定手机软件等。 因为GPLv3过于严格,不能和其他的一些协议兼容,所以我们有可能在将来会把BoostNote的协议改为一些较为宽松的协议,比如说BSD、MIT。 --- diff --git a/readme.md b/readme.md index 913c7b01..98b7b83d 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -> We will launch the new Boost Note in January 2020. Please support us via [Kickstarter](https://www.kickstarter.com/projects/kazz-boostio/new-boost-note)! +> [We've launched desktop app of the new Boost Note now. We'll release its mobile app too in January 2020.](https://github.com/BoostIO/BoostNote.next) ![Boostnote app screenshot](./resources/repository/top.png) From 099ebad06b51feabcdae647da171def359f6cf1f Mon Sep 17 00:00:00 2001 From: Nicholas Browning Date: Sat, 26 Oct 2019 16:05:12 -0400 Subject: [PATCH 17/65] Export: uses markdown preview dom. Supports diagrams --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 7c88f562..cb477efd 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -348,7 +348,7 @@ export default class MarkdownPreview extends React.Component { allowCustomCSS, customCSS }) - let body = this.markdown.render(noteContent) + let body = this.refs.root.contentWindow.document.body.innerHTML body = attachmentManagement.fixLocalURLS( body, this.props.storagePath From ce853a7e3a3df801a8e54411b57ab60cc8148c8a Mon Sep 17 00:00:00 2001 From: Nicholas Browning Date: Mon, 4 Nov 2019 20:52:44 -0500 Subject: [PATCH 18/65] Export: CSS: adjusted relative path --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index cb477efd..261b901f 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -368,7 +368,7 @@ export default class MarkdownPreview extends React.Component { let styles = '' files.forEach(file => { - styles += `` + styles += `` }) return ` From c423784cc5dc86457689c20436eb98c0a168c2a4 Mon Sep 17 00:00:00 2001 From: Mayke Date: Mon, 11 Nov 2019 20:01:50 -0300 Subject: [PATCH 19/65] adding TOC UI --- browser/components/markdown.styl | 66 ++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index 61829bc4..e64bea14 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -447,6 +447,38 @@ pre.fence color $ui-text-color background-color $ui-tag-backgroundColor +.markdownIt-TOC + list-style none + padding 15px + position fixed + right 0 + top 0 + margin-left 15px + z-index 1000 + transition transform .2s ease-in-out + transform translateX(100%) + + &, + &:before + background-color $ui-dark-backgroundColor + color: $ui-dark-text-color + + &:hover + transform translateX(-15px) + + &:before + content 'TOC' + position absolute + width 60px + height 30px + top 60px + left -30px + display flex + align-items center + justify-content center + transform-origin top left + transform rotate(-90deg) + themeDarkBackground = darken(#21252B, 10%) themeDarkText = #f9f9f9 themeDarkBorder = lighten(themeDarkBackground, 20%) @@ -514,6 +546,14 @@ body[data-theme="dark"] color $ui-dark-text-color background-color $ui-dark-tag-backgroundColor + .markdownIt-TOC + &, + &:before + background-color darken(themeDarkBackground, 5%) + color themeDarkText + + +themeSolarizedDarkBackground = $ui-solarized-dark-noteDetail-backgroundColor themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%) themeSolarizedDarkTableHead = themeSolarizedDarkTableEven @@ -522,7 +562,7 @@ themeSolarizedDarkTableBorder = themeDarkBorder body[data-theme="solarized-dark"] color $ui-solarized-dark-text-color border-color themeDarkBorder - background-color $ui-solarized-dark-noteDetail-backgroundColor + background-color themeSolarizedDarkBackground table thead tr @@ -557,6 +597,13 @@ body[data-theme="solarized-dark"] color $ui-solarized-dark-button--active-color background-color $ui-solarized-dark-button-backgroundColor + .markdownIt-TOC + &, + &:before + background-color darken(themeSolarizedDarkBackground, 15%) + color themeDarkText + +themeMonokaiBackground = $ui-monokai-noteDetail-backgroundColor themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%) themeMonokaiTableHead = themeMonokaiTableEven @@ -565,7 +612,7 @@ themeMonokaiTableBorder = themeDarkBorder body[data-theme="monokai"] color $ui-monokai-text-color border-color themeDarkBorder - background-color $ui-monokai-noteDetail-backgroundColor + background-color themeMonokaiBackground table thead tr @@ -603,6 +650,13 @@ body[data-theme="monokai"] color $ui-monokai-button--active-color background-color $ui-monokai-button-backgroundColor + .markdownIt-TOC + &, + &:before + background-color darken(themeMonokaiBackground, 15%) + color themeDarkText + +themeDraculaBackground = $ui-dracula-noteDetail-backgroundColor themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%) themeDraculaTableHead = themeDraculaTableEven @@ -611,7 +665,7 @@ themeDraculaTableBorder = themeDarkBorder body[data-theme="dracula"] color $ui-dracula-text-color border-color themeDarkBorder - background-color $ui-dracula-noteDetail-backgroundColor + background-color themeDraculaBackground table thead tr @@ -648,3 +702,9 @@ body[data-theme="dracula"] .prev, .next color $ui-dracula-button--active-color background-color $ui-dracula-button-backgroundColor + + .markdownIt-TOC + &, + &:before + background-color darken(themeDraculaBackground, 15%) + color themeDarkText From 3f465df1cd3680293823aabfd21b21462fb31941 Mon Sep 17 00:00:00 2001 From: Mayke Date: Thu, 28 Nov 2019 23:18:22 -0300 Subject: [PATCH 20/65] configuring a div wrapper for TOC plugin to use overflow-y in
    and still use &:before on parent element --- browser/lib/markdown.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 4174deae..c915f0ed 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -128,12 +128,19 @@ class Markdown { this.md.use(require('markdown-it-abbr')) this.md.use(require('markdown-it-sub')) this.md.use(require('markdown-it-sup')) - this.md.use(markdownItTocAndAnchor, { - toc: true, - tocPattern: /\[TOC\]/i, - anchorLink: false, - appendIdToHeading: false + + this.md.use(md => { + markdownItTocAndAnchor(md, { + toc: true, + tocPattern: /\[TOC\]/i, + anchorLink: false, + appendIdToHeading: false + }) + + md.renderer.rules.toc_open = () => '
    ' + md.renderer.rules.toc_close = () => '
    ' }) + this.md.use(require('./markdown-it-deflist')) this.md.use(require('./markdown-it-frontmatter')) From 7fa578880e7e701851650987e05fde0fcafccb60 Mon Sep 17 00:00:00 2001 From: Mayke Date: Thu, 28 Nov 2019 23:18:46 -0300 Subject: [PATCH 21/65] refactoring to use the new .markdownIt-TOC-wrapper div --- browser/components/markdown.styl | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index e64bea14..692f5074 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -447,9 +447,8 @@ pre.fence color $ui-text-color background-color $ui-tag-backgroundColor -.markdownIt-TOC +.markdownIt-TOC-wrapper list-style none - padding 15px position fixed right 0 top 0 @@ -458,6 +457,13 @@ pre.fence transition transform .2s ease-in-out transform translateX(100%) + .markdownIt-TOC + display block + max-height 90vh + overflow-y auto + padding 25px + padding-left 38px + &, &:before background-color $ui-dark-backgroundColor @@ -472,7 +478,7 @@ pre.fence width 60px height 30px top 60px - left -30px + left -29px display flex align-items center justify-content center @@ -546,7 +552,7 @@ body[data-theme="dark"] color $ui-dark-text-color background-color $ui-dark-tag-backgroundColor - .markdownIt-TOC + .markdownIt-TOC-wrapper &, &:before background-color darken(themeDarkBackground, 5%) @@ -597,7 +603,7 @@ body[data-theme="solarized-dark"] color $ui-solarized-dark-button--active-color background-color $ui-solarized-dark-button-backgroundColor - .markdownIt-TOC + .markdownIt-TOC-wrapper &, &:before background-color darken(themeSolarizedDarkBackground, 15%) @@ -650,7 +656,7 @@ body[data-theme="monokai"] color $ui-monokai-button--active-color background-color $ui-monokai-button-backgroundColor - .markdownIt-TOC + .markdownIt-TOC-wrapper &, &:before background-color darken(themeMonokaiBackground, 15%) @@ -703,7 +709,7 @@ body[data-theme="dracula"] color $ui-dracula-button--active-color background-color $ui-dracula-button-backgroundColor - .markdownIt-TOC + .markdownIt-TOC-wrapper &, &:before background-color darken(themeDraculaBackground, 15%) From 2882667e9470aa61a0fa9221613bb2b8a37ec14a Mon Sep 17 00:00:00 2001 From: Mayke Date: Fri, 29 Nov 2019 00:31:21 -0300 Subject: [PATCH 22/65] rebuilding test snapshots --- tests/lib/snapshots/markdown-test.js.md | 39 ++-------------------- tests/lib/snapshots/markdown-test.js.snap | Bin 3638 -> 3655 bytes 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/tests/lib/snapshots/markdown-test.js.md b/tests/lib/snapshots/markdown-test.js.md index 1ebc0275..37976ef1 100644 --- a/tests/lib/snapshots/markdown-test.js.md +++ b/tests/lib/snapshots/markdown-test.js.md @@ -87,7 +87,7 @@ Generated by [AVA](https://ava.li). > Snapshot 1 - `

      ␊ + `

      • H1
        • H2␊ @@ -98,7 +98,7 @@ Generated by [AVA](https://ava.li).
      ␊ -

      ␊ +

      H1

      H2

      H3

      ␊ @@ -226,39 +226,4 @@ Generated by [AVA](https://ava.li). > Snapshot 2 `

      This is a "QUOTE".

      ␊ - - -## Markdown.render() should render PlantUML Ditaa correctly - -> Snapshot 1 - - `uml diagram␊ ` - -## Markdown.render() should render PlantUML Gantt correctly - -> Snapshot 1 - - `uml diagram␊ - ` - -## Markdown.render() should render PlantUML MindMaps correctly - -> Snapshot 1 - - `uml diagram␊ - ` - -## Markdown.render() should render PlantUML Umls correctly - -> Snapshot 1 - - `uml diagram␊ - ` - -## Markdown.render() should render PlantUML WBS correctly - -> Snapshot 1 - - `uml diagram␊ - ` \ No newline at end of file diff --git a/tests/lib/snapshots/markdown-test.js.snap b/tests/lib/snapshots/markdown-test.js.snap index 3e68ab1bae3ef1eb223b0c932f410facf9319cac..ade264897894a30151584ba3d80bc25c6a8a144a 100644 GIT binary patch delta 3463 zcmV;24S4dl9LF4gK~_N^Q*L2!b7*gLAa*ed0RRJBDkM)zsgjd^5q9hEp==+E2mk;8 z0000J?OA(_D_40RUy!)mQc^-vC55&pbE`ys_y%Cd<6+ z8GBxyIpfFXsk9BCLaLMi3iK8s0ZRK<1Oi381ZpElQE5Vd3AB|$A!=#*QlNhjp+3(U zk9YmN-Fy4^tCq$-^PTT?e&;*i`Ofj*2MFRu;`#G8f98d6-}l?hr(X)X|JJz)5z!}h zo*=Ztryjoep>t0^@{`|t=+WX0_XIrZ!5azUtzS;Ol6c`m4?O!Dzj^CV9^1Sv;8B0@ z7J}%!cK+3W3-IMHef*mTU%Bw6zq=IhsL$O<5cfWI;8@~Yp8n(he|-069{u%qp7W@$ zd+Yegpyxe1Pu~-ITAD55dB-(E29sVzVOJ_(=>mWcrM^ke-04D z+UGy}kC*Uc*Cvw4?p?(fJeRUHi9^RN4&wC)xhhC8_&J-d|Y9GDkaFetcj*B zQ_!T+rs+V#gfa;v9`cl?8ghbU$fVB$pb1WRZi@#MCV3&Qu%4QXrixukk!??tEUA#v zdAlo_17k)TOC~*5fLXCZ%nBx~;71>O{I`%|7AaO+Hjiu$R0%2xG8~~vB%`(!m>@%6 zOqc|YEhb0>Ld&@v3=Rf^wUOyUSCH3`t3g>8WGPsrd*z*?)7M8O5_swa)0SjAK_X?m zrkR75t7(pC%d(M-2gepq$FyOJ&W!UBGKp!(%MV=fy>vD7rK!_g>tr&MSpz%(*Uh+~=(HA$DCpU>np1){+u! z6Jfa_J6&j6o;xK|P&JuC;5cM5lM4hL7`Bgt0xh^?#xzhHj-6=PrVio`v`wR(oRdHV zEdfQ7ZUjPqQZ`gY6n80GRMh@lc1793?+1QAPA?x>onDyv#fgR}sBldi1zEkaz2hQ{ zRawGmHEXmr)lpW%wQP;yRBC(j9%S}uvO1ET;EC=%i#M*On_adduA*BsPIYG#oV->~ zGrKIbucEtSo$B7^k>j(i3{|djA(z&l4M2Jx{56{kXe9 zCbv|WbA{F8oZm{OR2)as``b-2SsSPj%Jym9LpihKaD4QB+qa~A*)>;Q86wI~l9M4Kt90zGPQNO@3*2yRPvdN#a|zn2Af z0^wfv0ThcT;&|>(!3d7w@n>}rMg<}e0dK23rKpl58wnEHt~>|1z&rCKw9C2>m6vdT zq@gGtmzz~;= z?JtOvn$&j$@=mviK+Qv0pD)zns$sbhz>$UCMMW07f;l9;LU{5MK!=W+qN4Mn19{TRh@3|(t%M%_qm&g}?SIR?01})WgAi!N~alt!TO!qpnXd05=y`XvXteVfR z^V|!>sp}58*P_wqp~1Z zD-}`6hPac!v1Czg`9+?O0diI6Rdkp}+9e>2JjZ9W*G*fKKwU*ed0iHNP`Ph7CaM`+ zolMq48DIAj%yN_A;j}Y1ItL%ImJTi#M}6L^L_hSR*8HhfYf-DULMt1K<2o2BSTC-J z=O^Q<=O%hl8dcF*5+Ov56W*+i({6yfeS1pCllAf@L(Un;RKOt45aN_@3XtG53p&Bp zWLw8962!gI{0--+P0S;IEog}P8rY>8Vlx_8exhQA3162@a|u3I&~wrrh8U-eLRT%K)#hf23Jr9lLIlPDuJMowW=xnZlxU45t1NV9N+&F9y zWw~8$(JnU&)z~gRdU{9KBmnmT2Vc(Pw@bj{Z3xU3m=*yKOpdO9<1_2Lg{YTc&^CpH z`L@kl3f3?cd+gfuXuV0ASti>H2`kcW zr7UL|4{>T%b4X*3!9JZ_w@vq=c*?OWdvUH0EnNAnY$L~iCsQ~6mn_H3monIu=|8}7 z?0hE1?o8)v8AMdIWo z==9sc?aJ{+NRH|pSA|j}U4rF06JztGaGs6jvKvlHC`fW~gDv&T)x6Wmlrm0L+mvEO ztG%gO2V8xB#I-n+wWHl!h7C0oJv2~bDf%#Ucq}){c6%^na*;t_U<$QP4_3F$hEjs^ zFuj-RZSTvDI54yOJq{Y7p%vTI*AH9!d*Pg_ZdO8DgY~^mjoaPNbi0LNal}&fR9_3( zT^nXaEiF|thoVs$G|TJhky^(8*TDpdh6N-QCn?>3TRk)2e2%zAaQ4i4H=ozr`NB~q zWlD#Qx+|NFqgZ;ttNCy-n$N@Rc7{q9_YPxe)@>D=bgx)Aj47-oHtPLYI^WNwqHL?q zt5r8fM?~9E`Ea9AkBGf}CCu&$Zc5OzTl9vSrP1dsgD79?3^#Y!qiuDA4rQuvQ!Y!@ zO`2^6^1sJ(d%H52Mwh)GxN0=`H@K05^whwIEuB^;$C| z)vRW#-gdgBR5RAeAKEd_u5&xCP-3b=x>*sZNEwQII4aW}t#iZ-V!N6o>~xyB;cnP! zA8lpM1nz}v1-FQn4=ZQ9<&)hj!4BOS(#%0Pd$h%E^^La49>o~8E!3Q2u9$9(w9LVO zx)pA4s<5pI*=%`>O_^fufY}Mfl+1QSuJCqL$nZ_RTIr4KjgoO(?;X}eBiiU0TgQ8i zUO7Yc)+5e9HtXTzic?g>>u$-)9aHt}o})GBj(#v;QXPrUhUol`Tr>~%MY&jWG`kq0 zShyE!rjB~eD%6kqZDoDuxLesg3oz$@t_2v&m|8k}XsHw|zSr3e(_$~oY!y4P&@QXn zr6x_6cPk-L&mD%U+jUMX=o@*iAKNYLM(HpgHCm&Jkg+1isSsCgXDY(xQ0;A4C5=ht z#axGjneCoc%!O4ZD)+a-Y^@-4L+Pm5X1aQY7W<__n#t>QxzSCrVz*QkQ{_4;FSK`s peuHjtM|PUt+FH2`{}X<190v;{000UXv{C>7 delta 3446 zcmV-+4T*76^V2mk;8 z0000J?OA(_D_40RUy!)mQX)c9B|ytzZj}gfXFW3>d+i&qRom;ey?*U_y=!~ZWIHc= z#-5jF&iJu;Ds9suAXOlM()Jc10ZPk15eO9Vs6cJh1_UI3ErC)g6rwgwUkdaOBGl(O zzc=Dl(A368b!$0}G2OlZkaCg9?9=wqt-uj0duWUU3k^7(dwO_yWCy#EuE8tOo z_!ffbymtP7)eG>YFMs@-2Vc4HroX!s@Tkw;ND%itdf-^%Tb}w_|3AL_Gmrf0d(L^( z*S&T8q|x)6J5Svmdibs<8ro;>>D~NU<(x;o;y*t|&m9j`pLo+>|HFef{nL++TEFr* z@)c8`zlk7PFFgL@iy!6&e;P5*f9lV_e^D(#RIy7bvh8V-B^6RS zZ+9hgV9aP^$%MxWFe_GwS;2%A{ODtky$>m7kz%!F^T_5vm7uagh9fkIWYo3-H^>lQ z6DEOUiyNc@q2*i-1_y(|+Q@XFE68ie)u5~kvJ@=Rz4A`c>Fc8s2|RUzX-l%bK_X?m zrkR75t7(pC%d(M(2gepq$FymR&W!UBGJ$Ex%MV@gy>vD7rK!_g>trI6Spz%<6DQLF z?i-Vv11~{ZGhK<=8yTW%v^~F5XxOfWryTVFzX#mL>(-rM+~=(HA$DCpU>npX){+!$ z6Jfb2J6&j6o;xK|P&JuC;5cL=lM4hL7^aVd0xh^i+B8rbj-6=PrVio`v`wR(n3F&R zEdfE3ZUjPqk~UOD6n9BmRMh@lc1793?+1QAPA|W`I=wLSixUk|P~n<33bJ}-d&fl@ ztFnaCYQ|`5s-vuiYuOsZsnqu5J;?0SWOXDv!4utk7H?clH@j>_Tt&BNoa#<1IC-s} zW_DR~aPcytz3|P$Y#K;8e8(6U84Z)Y~AZ2~t#(QQ*79rRGx1R*j z?RO$LF!Mw<9z;MSX27}c{ERDI9?C;XQXNaA#jkP+iX5vyd^dib9 z_E06a0KGD_IY#GJO#{aN|CECu47_&x)qhW46Hu*#s!K3IkCdP5xHDG9+ zjZHrrn{y_%70A({aC8h;@4uynouF=ipUr8wij6!IX;vA(JYrB`?h13leN;<83NEGE z%czTsC>r4y`ZOeBp=qqaNc!h>dCh zNTC-w0`PYT=n%^;Ma=J++eEJ6=Ni`I+-LRL5LAQ2Ir)Fj!dU`H`2PU+^+LTs15g0ywhJyQs)wS1^a9R|rquD3(O^G1xaef-IdD zn@jWqpK1Q;XN4V~d)8javzMN+wos1-F2${B8|ctcQ&eg(=v$TegIX7S4a*`o(*d95P}kKcU=nA`Ex*X~F+i^ByowIfNV^1tk>~h~_PS|n5~!=FD6h+Z0xI_n$3!)Q ztCNX(DDCTBf>~}dJe+psM(5xo*3!Y{;;7GCmFS0F)S5rlYAtHDR%m5oaa;#O1?$E2 z@cd+a_1r`+N~0<|OCp4*al)ImaoP=Vw{K4gd9q&KWXL(=mjmQb$n)>w-EIb4BDoU zFyFR$OTikZVvk*$9<4V?Gs|RqAz{V4f|N)@TZ;#A%XfNyH(0_nvreuA=3)WwttjD4 zE)q<0e2H$$t*J|ni!Fm0?_Dhe5s=Vb%-!Ql4R1M)!$^@e&7=l6-CPFOc4tT?&crr0 zX^pRz=3I6tN47|R@4^Y?Z4cym4jc)lwkYW3iSf=m9wQp;JRmCG;}dZ!F*Qms4UmV+ zf|$UIT@TBb9{K%OpMLao0RHZ4;)N9S9iE#jYRO!=C+<)F;K@U(R!NWDpPI28;?6PH z-pN(kbT5jP9J{g?=kd_Ob>7OhHzDKyzH-cbfr6cw{#z@5$IjrOpMK!!g)59%WOJHp&-e{O}5l8SMyFMT}nGuZA*$3t@f5`9dPv#*Wygpj&^ft zHq=z~&_IoUCFw?}aV$5>c6%^na*;t_U<$QP4_3F$rc#3PFtwNLZSTvDI50E&Jq{Y7 zp%vTG*Bh<L!dw<@9RV12Js<97Gc-ELu69I;eA+1EmL*M=EUOG%Yx~<|N-76LvF@?3nX1yOv<@>p0lx@{{wd%&`h-f=1A8t155wW+g zgxOueO$vG@OK++f8hy?(i1M}0@Zk=7w5@K^p>!2)$z`d!MRTnx!_jRiD_1jp>Nsj? z2hFB`s&6%id%RE(_@PscQ6sG;RY$|(fz|KW$Kl4N5UKR4ky0pjYhZTW}l%cqXqcYXeI!DYPwyR0PPUkQ;+zngpqip(2;9j^^aEoa9uyWd4 zKAF7|?9iPd%^ZX?M_Df0H`*q96l2)7P;-vCVyZRL(g*8SxXG!)wkBjUt#oPgZ zvlEIb>FtPI;q9o9<`4O5r8lxSOU7}%*Qkj`wAnMV$9v6QIZgG}BhElJ>*3>yQ&hw2 zZpq3WQ}xWAqc!P{elTE?9f{9`==_dcG!OPgxma^FyBMNaxEDK29`z2ZP(SLomGzzD zZe{B%z?{1lU@T*5>FlAUQn2`5XE#iLi@h+DEp}p|T~@bChcsQ@t%O89*9cX&>zr87 zH}hOSwp-YZ(qTSov_=&nZAFfgA+Fp`SA?yh+S{~B8k5Y6xef=@+dZq83#&|2?q|bn ztsrzmsi@gzx_X)x`=vsP$?J5v*-f%yw^S9A^ YH)rHWc8bnsS1!Z<1ZcM&nBgJ-0I%E1jQ{`u From 6d455fc286359fd91cb517b923807c27b905975d Mon Sep 17 00:00:00 2001 From: Yuki Furukawa Date: Sat, 7 Dec 2019 17:27:50 +0900 Subject: [PATCH 23/65] remove redundant conditions of macOS in main-menu --- lib/main-menu.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/main-menu.js b/lib/main-menu.js index 124c6675..a072ed25 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -86,21 +86,21 @@ const file = { }, { label: 'Focus Note', - accelerator: macOS ? 'Command+E' : 'Control+E', + accelerator: 'CommandOrControl+E', click () { mainWindow.webContents.send('detail:focus') } }, { label: 'Delete Note', - accelerator: macOS ? 'Command+Shift+Backspace' : 'Control+Shift+Backspace', + accelerator: 'CommandOrControl+Shift+Backspace', click () { mainWindow.webContents.send('detail:delete') } }, { label: 'Clone Note', - accelerator: macOS ? 'Command+D' : 'Control+D', + accelerator: 'CommandOrControl+D', click () { mainWindow.webContents.send('list:clone') } @@ -260,7 +260,7 @@ const view = { }, { label: 'Toggle Developer Tools', - accelerator: macOS ? 'Command+Alt+I' : 'Control+Shift+I', + accelerator: 'CommandOrControl+Alt+I', click () { BrowserWindow.getFocusedWindow().toggleDevTools() } @@ -314,21 +314,21 @@ const view = { }, { label: 'Actual Size', - accelerator: macOS ? 'CommandOrControl+0' : 'Control+0', + accelerator: 'CommandOrControl+0', click () { mainWindow.webContents.send('status:zoomreset') } }, { label: 'Zoom In', - accelerator: macOS ? 'CommandOrControl+=' : 'Control+=', + accelerator: 'CommandOrControl+=', click () { mainWindow.webContents.send('status:zoomin') } }, { label: 'Zoom Out', - accelerator: macOS ? 'CommandOrControl+-' : 'Control+-', + accelerator: 'CommandOrControl+-', click () { mainWindow.webContents.send('status:zoomout') } From d8605965a867fe1f763f3b1dfdbd598d2c8659e9 Mon Sep 17 00:00:00 2001 From: Abner Soares Alves Junior Date: Mon, 2 Dec 2019 23:08:57 -0300 Subject: [PATCH 24/65] Add Ok button to export confirmation box --- browser/components/MarkdownPreview.js | 4 +++- locales/en.json | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 261b901f..94cbede0 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -21,6 +21,7 @@ import yaml from 'js-yaml' import { render } from 'react-dom' import Carousel from 'react-image-carousel' import ConfigManager from '../main/lib/ConfigManager' +import i18n from 'browser/lib/i18n' const { remote, shell } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') @@ -423,7 +424,8 @@ export default class MarkdownPreview extends React.Component { .then(res => { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'info', - message: `Exported to ${filename}` + message: `Exported to ${filename}`, + buttons: [i18n.__('Ok')] }) }) .catch(err => { diff --git a/locales/en.json b/locales/en.json index 183bdaad..823e901c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -9,6 +9,7 @@ "Toggle Mode": "Toggle Mode", "Add tag...": "Add tag...", "Trash": "Trash", + "Ok": "Ok", "MODIFICATION DATE": "MODIFICATION DATE", "Words": "Words", "Letters": "Letters", From 60d6c68e4859e00a600cc98e4d2271492593671b Mon Sep 17 00:00:00 2001 From: Abner Soares Alves Junior Date: Mon, 2 Dec 2019 23:32:29 -0300 Subject: [PATCH 25/65] Add some translations --- locales/cs.json | 1 + locales/da.json | 1 + locales/de.json | 1 + locales/es-ES.json | 1 + locales/fr.json | 1 + locales/it.json | 1 + locales/pt-BR.json | 1 + locales/pt-PT.json | 1 + locales/sq.json | 1 + 9 files changed, 9 insertions(+) diff --git a/locales/cs.json b/locales/cs.json index 178659c3..9176fc28 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -9,6 +9,7 @@ "Toggle Mode": "Přepnout režim", "Add tag...": "Přidat štítek...", "Trash": "Koš", + "Ok": "Ok", "MODIFICATION DATE": "DATUM ZMĚNY", "Words": "Slova", "Letters": "Písmena", diff --git a/locales/da.json b/locales/da.json index 04520a4d..f2459fd5 100644 --- a/locales/da.json +++ b/locales/da.json @@ -8,6 +8,7 @@ "to create a new note": "to create a new note", "Toggle Mode": "Toggle Mode", "Trash": "Trash", + "Ok": "Ok", "MODIFICATION DATE": "MODIFICATION DATE", "Words": "Words", "Letters": "Letters", diff --git a/locales/de.json b/locales/de.json index 9f6d9b05..d440dd97 100644 --- a/locales/de.json +++ b/locales/de.json @@ -8,6 +8,7 @@ "to create a new note": "um eine neue Notiz zu erstellen", "Toggle Mode": "Modus umschalten", "Trash": "Papierkorb", + "Ok": "Ok", "MODIFICATION DATE": "ÄNDERUNGSDATUM", "Words": "Wörter", "Letters": "Buchstaben", diff --git a/locales/es-ES.json b/locales/es-ES.json index 34ae3e24..7e569a15 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -8,6 +8,7 @@ "to create a new note": "para crear una nueva nota", "Toggle Mode": "Alternar modo", "Trash": "Basura", + "Ok": "Ok", "MODIFICATION DATE": "FECHA DE MODIFICACIÓN", "Words": "Palabras", "Letters": "Letras", diff --git a/locales/fr.json b/locales/fr.json index 14214687..ebbdcae3 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -8,6 +8,7 @@ "to create a new note": "pour créer une nouvelle note", "Toggle Mode": "Toggle Mode", "Trash": "Poubelle", + "Ok": "Ok", "MODIFICATION DATE": "DATE DE MODIFICATION", "Words": "Mots", "Letters": "Lettres", diff --git a/locales/it.json b/locales/it.json index 45c09cd3..06947080 100644 --- a/locales/it.json +++ b/locales/it.json @@ -8,6 +8,7 @@ "to create a new note": "per creare una nuova nota", "Toggle Mode": "Cambia Modalità", "Trash": "Cestino", + "Ok": "Ok", "MODIFICATION DATE": "DATA DI MODIFICA", "Words": "Parole", "Letters": "Lettere", diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 8b0bd478..06dd442f 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -8,6 +8,7 @@ "to create a new note": "para criar uma nova nota", "Toggle Mode": "Modo de alternância", "Trash": "Lixeira", + "Ok": "Ok", "MODIFICATION DATE": "DATA DE MODIFICAÇÃO", "Words": "Palavras", "Letters": "Letras", diff --git a/locales/pt-PT.json b/locales/pt-PT.json index 25bf82ae..c514ba4e 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -8,6 +8,7 @@ "to create a new note": "para criar uma nova nota", "Toggle Mode": "Alternar Modo", "Trash": "Lixo", + "Ok": "Ok", "MODIFICATION DATE": "DATA DE MODIFICAÇÃO", "Words": "Palavras", "Letters": "Letras", diff --git a/locales/sq.json b/locales/sq.json index d88fb29e..d217600d 100644 --- a/locales/sq.json +++ b/locales/sq.json @@ -8,6 +8,7 @@ "to create a new note": "to create a new note", "Toggle Mode": "Toggle Mode", "Trash": "Trash", + "Ok": "Ok", "MODIFICATION DATE": "MODIFICATION DATE", "Words": "Words", "Letters": "Letters", From 3c12e0d119b9d47f837309ab26ec227da8cdcc3a Mon Sep 17 00:00:00 2001 From: Abner Soares Alves Junior Date: Mon, 2 Dec 2019 22:59:18 -0300 Subject: [PATCH 26/65] Fix emoji render on notes list --- browser/components/NoteItem.js | 3 ++- package.json | 1 + yarn.lock | 29 +++++++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/browser/components/NoteItem.js b/browser/components/NoteItem.js index 168af1ff..b672bfcc 100644 --- a/browser/components/NoteItem.js +++ b/browser/components/NoteItem.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types' import React from 'react' import { isArray, sortBy } from 'lodash' import invertColor from 'invert-color' +import Emoji from 'react-emoji-render' import CSSModules from 'browser/lib/CSSModules' import { getTodoStatus } from 'browser/lib/getTodoStatus' import styles from './NoteItem.styl' @@ -87,7 +88,7 @@ const NoteItem = ({ : }
      {note.title.trim().length > 0 - ? note.title + ? : {i18n.__('Empty note')}}
      diff --git a/package.json b/package.json index c85bfaa0..fc269ba6 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "react-composition-input": "^1.1.1", "react-debounce-render": "^4.0.1", "react-dom": "^16.8.6", + "react-emoji-render": "^1.1.0", "react-image-carousel": "^2.0.18", "react-redux": "^7.0.3", "react-router-dom": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index ab5441b3..77621a24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2661,7 +2661,7 @@ d@1: dependencies: es5-ext "^0.10.9" -"dagre-d3@github:dagrejs/dagre-d3": +dagre-d3@dagrejs/dagre-d3: version "0.6.4-pre" resolved "https://codeload.github.com/dagrejs/dagre-d3/tar.gz/e1a00e5cb518f5d2304a35647e024f31d178e55b" dependencies: @@ -3142,6 +3142,11 @@ electron@4: electron-download "^4.1.0" extract-zip "^1.0.3" +emoji-regex@^6.4.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2" + integrity sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ== + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -6045,7 +6050,7 @@ lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" -lodash.flatten@^4.2.0: +lodash.flatten@^4.2.0, lodash.flatten@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" @@ -7937,6 +7942,17 @@ react-dom@^16.8.6: prop-types "^15.6.2" scheduler "^0.13.6" +react-emoji-render@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-emoji-render/-/react-emoji-render-1.1.0.tgz#af494619bf1012083bc20ad18eb0a6d92d04228c" + integrity sha512-HIHIrtWd8Jel4qDgIBRQnPPFChJakuRkMFl5N5wObYjYsL7a4pkwK5P9wrEKxQWqlbviTrjGOANAfFldnVuRIQ== + dependencies: + classnames "^2.2.5" + emoji-regex "^6.4.1" + lodash.flatten "^4.4.0" + prop-types "^15.5.8" + string-replace-to-array "^1.0.1" + react-image-carousel@^2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/react-image-carousel/-/react-image-carousel-2.0.18.tgz#5868ea09bd9cca09c4467d3d02695cd4e7792f28" @@ -9137,6 +9153,15 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" +string-replace-to-array@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string-replace-to-array/-/string-replace-to-array-1.0.3.tgz#c93eba999a5ee24d731aebbaf5aba36b5f18f7bf" + integrity sha1-yT66mZpe4k1zGuu69auja18Y978= + dependencies: + invariant "^2.2.1" + lodash.flatten "^4.2.0" + lodash.isstring "^4.0.1" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" From 0722c2505a3998b8140c6361678c1a0bf4ce4121 Mon Sep 17 00:00:00 2001 From: Itai Braude Date: Wed, 25 Dec 2019 10:04:31 +0200 Subject: [PATCH 27/65] fixed requested changes --- browser/components/CodeEditor.js | 4 ++-- browser/components/MarkdownPreview.js | 2 -- browser/main/Detail/MarkdownNoteDetail.js | 2 +- browser/main/Detail/ToggleDirectionButton.js | 12 ++++++------ 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index fa9924a1..d6d48b36 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -295,7 +295,7 @@ export default class CodeEditor extends React.Component { inputStyle: 'textarea', dragDrop: false, direction: RTL ? 'rtl' : 'ltr', - rtlMoveVisually: RTL ? 'true' : 'false', + rtlMoveVisually: RTL, foldGutter: true, lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], @@ -559,7 +559,7 @@ export default class CodeEditor extends React.Component { } if (prevProps.RTL !== this.props.RTL) { this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr') - this.editor.setOption('rtlMoveVisually', this.props.RTL ? 'true' : 'false') + this.editor.setOption('rtlMoveVisually', this.props.RTL) } if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) { if (!enableMarkdownLint) { diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 3765493e..db7914f2 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -107,8 +107,6 @@ body { ${optimizeOverflowScroll ? 'height: 100%;' : ''} ${RTL ? 'direction: rtl;' : ''} ${RTL ? 'text-align: right;' : ''} - - } @media print { body { diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index ba774dda..083259d8 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -484,7 +484,7 @@ class MarkdownNoteDetail extends React.Component {
      this.handleSwitchMode(e)} editorType={editorType} /> - this.handleSwitchDirection(e)} editorDirection={this.state.RTL} /> + this.handleSwitchDirection(e)} isRTL={this.state.RTL} /> this.handleStarButtonClick(e)} isActive={note.isStarred} diff --git a/browser/main/Detail/ToggleDirectionButton.js b/browser/main/Detail/ToggleDirectionButton.js index d0cf57bb..6584d4fa 100644 --- a/browser/main/Detail/ToggleDirectionButton.js +++ b/browser/main/Detail/ToggleDirectionButton.js @@ -5,14 +5,14 @@ import styles from './ToggleDirectionButton.styl' import i18n from 'browser/lib/i18n' const ToggleDirectionButton = ({ - onClick, editorDirection + onClick, isRTL }) => (
      -
      onClick()}> - +
      onClick()}> +
      -
      onClick()}> - +
      onClick()}> +
      {i18n.__('Toggle Direction')}
      @@ -20,7 +20,7 @@ const ToggleDirectionButton = ({ ToggleDirectionButton.propTypes = { onClick: PropTypes.func.isRequired, - editorDirection: PropTypes.string.isRequired + isRTL: PropTypes.string.isRequired } export default CSSModules(ToggleDirectionButton, styles) From c373c207c0d0a26f7fa5387d5086321b3e94c353 Mon Sep 17 00:00:00 2001 From: Flexo013 Date: Sun, 20 Oct 2019 14:40:02 +0200 Subject: [PATCH 28/65] Added link to downloads --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index 98b7b83d..5f36f270 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,10 @@

      +## Download + +[Find the latest release of Boostnote here!](https://github.com/BoostIO/boost-releases/releases/) + ## Authors & Maintainers - [Rokt33r](https://github.com/rokt33r) From 1195c77f7a1769b69a00d235b77692826bc55859 Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Fri, 3 Jan 2020 11:28:19 -0500 Subject: [PATCH 29/65] v0.14.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fc269ba6..acec8d5d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "boost", "productName": "Boostnote", - "version": "0.13.0", + "version": "0.14.0", "main": "index.js", "description": "Boostnote", "license": "GPL-3.0", From d760259ce661f9b522c5f77ecefa4c03db704756 Mon Sep 17 00:00:00 2001 From: KZ Date: Mon, 6 Jan 2020 13:12:49 +0900 Subject: [PATCH 30/65] Update readme --- readme.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 5f36f270..c8f6e6a9 100644 --- a/readme.md +++ b/readme.md @@ -18,7 +18,7 @@ ## Authors & Maintainers - [Rokt33r](https://github.com/rokt33r) -- [Kazz](https://github.com/kazup01) +- [KZ](https://github.com/kazup01) - [ZeroX-DG](https://github.com/ZeroX-DG) ## Contributors @@ -43,9 +43,8 @@ Issues on Boostnote can be funded by anyone and the money will be distributed to #### More Information * Website: https://boostnote.io -* Newsletters: https://boostnote.io/#subscribe * [Development](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md): Development configurations for Boostnote. -* Copyright (C) 2016 - 2019 BoostIO, Inc. +* Copyright (C) 2016 - 2020 BoostIO, Inc. #### License From 1dd71fc923a5b972bf6685ca4b7aab41faf0f3d5 Mon Sep 17 00:00:00 2001 From: cephonodes Date: Tue, 17 Dec 2019 10:49:47 +0900 Subject: [PATCH 31/65] Updated Japanese document about contribution --- contributing.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/contributing.md b/contributing.md index 717c31ef..a7c91e91 100644 --- a/contributing.md +++ b/contributing.md @@ -69,12 +69,24 @@ GPL v3 라이센스는 다른 라이센스와 혼합해 사용하기엔 너무 # Contributing to Boostnote (Japanese) ### バグレポートに関してのissueを立てる時 -イシューテンプレートはありませんが、1つお願いがあります。 - -**開発者ツールを開いた状態のBoostnoteのスクリーンショットを貼ってください** +イシューテンプレートがあります。このテンプレートに従って、できるだけ多くの情報を提供してください。 よろしくお願いします。 +### Pull requestを出す時 +Pull requestのテンプレートがあります。このテンプレートを埋めてからコードをサブミットしてください。内容を正確に把握できるPull requestが作られていれば、迅速にレビューを行えます。 + +以下のことを必ず行ってください: +- [`code_style.md`](docs/code_style.md)を読み、コーディングスタイルを確認する +- 変更分のコードに対するテストコードを書き、以下のコマンドでテストを実行する +``` +npm run test +``` +- 以下のコマンドを使って、コードの静的解析を実行する +``` +npm run lint +``` + ### Pull requestの著作権について Pull requestをすることはその変化分のコードの著作権をBoostIOに譲渡することに同意することになります。 From 7fe69256154955b4d9f9eb536adfb0dca64f0efc Mon Sep 17 00:00:00 2001 From: Itai Braude Date: Thu, 16 Jan 2020 23:52:19 +0200 Subject: [PATCH 32/65] fix undefined variable --- browser/components/MarkdownPreview.js | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index af6a8b0d..5dc2bcc8 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -108,7 +108,6 @@ body { box-sizing: border-box; ` : ''} - ${optimizeOverflowScroll ? 'height: 100%;' : ''} ${RTL ? 'direction: rtl;' : ''} ${RTL ? 'text-align: right;' : ''} } From 0a361f5f411dd6c24f7dcb660012aa2cc46b0bba Mon Sep 17 00:00:00 2001 From: Satyendra <33686367+developersatyendra@users.noreply.github.com> Date: Fri, 17 Jan 2020 13:52:32 +0530 Subject: [PATCH 33/65] Copyright info update updated Copyright (C) 2017 - 2019 BoostIO to Copyright (C) 2017 - 2020 BoostIO --- browser/main/modals/PreferencesModal/InfoTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/InfoTab.js b/browser/main/modals/PreferencesModal/InfoTab.js index 66250412..97f06f7b 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.js +++ b/browser/main/modals/PreferencesModal/InfoTab.js @@ -142,7 +142,7 @@ class InfoTab extends React.Component { >{i18n.__('Development')}{i18n.__(' : Development configurations for Boostnote.')}
    • - {i18n.__('Copyright (C) 2017 - 2019 BoostIO')} + {i18n.__('Copyright (C) 2017 - 2020 BoostIO')}
    • {i18n.__('License: GPL v3')} From 5105babd1429d6e9fe5a38e93decd4e0e4e4eabe Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 8 Jan 2020 21:02:20 -0500 Subject: [PATCH 34/65] Refine Chinese translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Should not translate "space", it means space key in most context - Should translate "keymap", "spellcheck disabled", "auto detect" - Should translate "On Right Click" to "右键点击" - Refine misc translation. --- locales/zh-CN.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/locales/zh-CN.json b/locales/zh-CN.json index c023708d..9ce5f755 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -9,7 +9,7 @@ "Toggle Mode": "切换模式", "Trash": "废纸篓", "MODIFICATION DATE": "更改时间", - "Words": "单词", + "Words": "单词数", "Letters": "字数", "STORAGE": "本地存储", "FOLDER": "文件夹", @@ -40,13 +40,13 @@ "Editor Font Size": "编辑器字号", "Editor Font Family": "编辑器字体", "Editor Indent Style": "编辑器缩进风格", - "Spaces": "空格", + "Spaces": "Spaces", "Tabs": "Tabs", "Switch to Preview": "快速切换到预览界面", - "When Editor Blurred": "当编辑器失去焦点的时候,切换到预览界面", + "When Editor Blurred": "当编辑器失去焦点的时候", "When Editor Blurred, Edit On Double Click": "当编辑器失去焦点的时候预览,双击切换到编辑界面", - "On Right Click": "右键点击切换两个界面", - "Editor Keymap": "编辑器 Keymap", + "On Right Click": "右键点击", + "Editor Keymap": "编辑器键位设置", "default": "默认", "vim": "vim", "emacs": "emacs", @@ -82,7 +82,7 @@ "Analytics": "分析", "Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.": "Boostnote 收集匿名数据只为了提升软件使用体验,绝对不收集任何个人信息(包括笔记内容)", "You can see how it works on ": "你可以看看它的源码是如何运作的 ", - "You can choose to enable or disable this option.": "你可以选择开启或不开启这个功能", + "You can choose to enable or disable this option.": "你可以选择开启或禁用这个功能", "Enable analytics to help improve Boostnote": "允许对数据进行分析,帮助我们改进 Boostnote", "Crowdfunding": "众筹", "Dear Boostnote users,": "亲爱的用户:", @@ -132,7 +132,7 @@ "Restore": "恢复", "Permanent Delete": "永久删除", "Confirm note deletion": "确认删除笔记", - "This will permanently remove this note.": "永久地删除这条笔记", + "This will permanently remove this note.": "将永久地删除这条笔记", "Successfully applied!": "设置成功", "Albanian": "Albanian", "Chinese (zh-CN)": "简体中文", @@ -208,7 +208,7 @@ "Folder Name": "文件夹名称", "No tags": "无标签", "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "将文本箭头转换为完整符号。 ⚠ 注意这会影响 Markdown 的 HTML 注释。", - "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", + "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ 您粘贴了一个链接,但在此文档的存储位置中找不到对应的附件。仅当附件和文档在同一存储位置时,才支持粘贴引用附件的链接。请将附件拖放到文档内!⚠", "Default New Note": "预设新笔记类型", "Show only related tags": "只显示相关标签", "Snippet Default Language": "程式码片段预设语言", @@ -219,9 +219,9 @@ "Custom CSS": "自定义 CSS", "Allow custom CSS for preview": "允许预览自定义 CSS", "Render newlines in Markdown paragraphs as
      ": "在 Markdown 段落中使用
      换行", - "Spellcheck disabled": "Spellcheck disabled", + "Spellcheck disabled": "拼写检查已禁用", "Show menu bar": "显示菜单栏", - "Auto Detect": "Auto Detect", + "Auto Detect": "自动检测", "Enable HTML label in mermaid flowcharts": "在 mermaid flowcharts 中启用 HTML 标签 ⚠ 这个选项可能会产生 XSS", "Wrap line in Snippet Note": "在 Snippet Note 里换行", "Toggle Editor Mode": "切换编辑模式", @@ -231,7 +231,7 @@ "Show/Hide Menu Bar": "显示/隐藏 菜单栏", "Save tags of a note in alphabetical order": "按字母顺序存储标签", "Show tags of a note in alphabetical order": "按字母顺序显示标签", - "Enable live count of notes": "实时统计标签下笔记个数", + "Enable live count of notes": "实时统计标签下笔记数量", "New notes are tagged with the filtering tags": "新建的笔记带有在标签列表过滤的标签", "Front matter title field": "从 front-matter 中抽取标题的字段名", "Extract title from front matter": "启用从 front-matter 抽取标题", From 49c9bcac9a21468df94cf6f8b6f91d2c1b43e0e5 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Thu, 16 Jan 2020 12:12:26 -0800 Subject: [PATCH 35/65] Added Wiki Link --- lib/main-menu.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/main-menu.js b/lib/main-menu.js index a072ed25..308a716c 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -384,6 +384,10 @@ const help = { label: 'Boostnote official site', click () { shell.openExternal('https://boostnote.io/') } }, + { + label: 'Wiki', + click () { shell.openExternal('https://github.com/BoostIO/Boostnote/wiki') } + }, { label: 'Issue Tracker', click () { shell.openExternal('https://github.com/BoostIO/Boostnote/issues') } From 87b9766bc072b0001483b6c88279fb0a9f3bf881 Mon Sep 17 00:00:00 2001 From: Ray Ou Date: Mon, 6 Jan 2020 20:56:40 +1100 Subject: [PATCH 36/65] Add Traditional Chinese option to build.md --- docs/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.md b/docs/build.md index 095f8628..1e269b4c 100644 --- a/docs/build.md +++ b/docs/build.md @@ -1,6 +1,6 @@ # Build -This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md), [Portuguese](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md). +This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Traditional Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_TW/build.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md), [Portuguese](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md). ## Environments From 31ffbd98b62fd37596b738b3898beb58e4984fe6 Mon Sep 17 00:00:00 2001 From: Jeny Mazo Date: Sun, 5 Jan 2020 00:01:40 +0100 Subject: [PATCH 37/65] Bug fix (sets tabWith to 2 on prettier configuration and ConfigManager so checkboxes can be clikable) --- browser/main/lib/ConfigManager.js | 2 +- prettier.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 7d3b4fd5..f73895df 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -76,7 +76,7 @@ export const DEFAULT_CONFIG = { customMarkdownLintConfig: DEFAULT_MARKDOWN_LINT_CONFIG, prettierConfig: ` { "trailingComma": "es5", - "tabWidth": 4, + "tabWidth": 2, "semi": false, "singleQuote": true }`, diff --git a/prettier.config b/prettier.config index 8b8b7b99..66e7e941 100644 --- a/prettier.config +++ b/prettier.config @@ -1,6 +1,6 @@ { "trailingComma": "es5", - "tabWidth": 4, + "tabWidth": 2, "semi": false, "singleQuote": true } \ No newline at end of file From 93e09f11dd567739a4a9e39e95c0cb3b5d1c927a Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 30 Jan 2020 19:31:31 +0100 Subject: [PATCH 38/65] fix UI theme for SplitEditor and CreateFromURL modal --- browser/components/MarkdownSplitEditor.styl | 26 ++-- .../modals/CreateMarkdownFromURLModal.styl | 121 ++++-------------- 2 files changed, 34 insertions(+), 113 deletions(-) diff --git a/browser/components/MarkdownSplitEditor.styl b/browser/components/MarkdownSplitEditor.styl index d716c85f..49bd927a 100644 --- a/browser/components/MarkdownSplitEditor.styl +++ b/browser/components/MarkdownSplitEditor.styl @@ -16,22 +16,14 @@ z-index 10 cursor col-resize -body[data-theme="dark"] - .root - .slider - border-left 1px solid $ui-dark-borderColor +apply-theme(theme) + body[data-theme={theme}] + .root + .slider + border-left 1px solid get-theme-var(theme, 'borderColor') -body[data-theme="solarized-dark"] - .root - .slider - border-left 1px solid $ui-solarized-dark-borderColor +for theme in 'dark' 'dracula' 'solarized-dark' + apply-theme(theme) -body[data-theme="monokai"] - .root - .slider - border-left 1px solid $ui-monokai-borderColor - -body[data-theme="dracula"] - .root - .slider - border-left 1px solid $ui-dracula-borderColor +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/CreateMarkdownFromURLModal.styl b/browser/main/modals/CreateMarkdownFromURLModal.styl index 5e59e465..8aca1505 100644 --- a/browser/main/modals/CreateMarkdownFromURLModal.styl +++ b/browser/main/modals/CreateMarkdownFromURLModal.styl @@ -51,110 +51,39 @@ font-size 14px colorPrimaryButton() -body[data-theme="dark"] - .root - modalDark() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-dark-text-color - - .control-folder-label - color $ui-dark-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorDarkPrimaryButton() - -body[data-theme="solarized-dark"] - .root - modalSolarizedDark() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-solarized-dark-text-color - - .control-folder-label - color $ui-solarized-dark-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorSolarizedDarkPrimaryButton() - .error text-align center color #F44336 -body[data-theme="monokai"] - .root - modalMonokai() - width 500px - height 270px - overflow hidden - position relative +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent + width 500px + height 270px + overflow hidden + position relative - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-monokai-text-color + .header + background-color transparent + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') - .control-folder-label - color $ui-monokai-text-color + .control-folder-label + color get-theme-var(theme, 'text-color') - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white + .control-folder-input + border 1px solid $ui-input--create-folder-modal + color white - .description - color $ui-inactive-text-color + .description + color $ui-inactive-text-color - .control-confirmButton - colorMonokaiPrimaryButton() + .control-confirmButton + colorThemedPrimaryButton(theme) -body[data-theme="dracula"] - .root - modalDracula() - width 500px - height 270px - overflow hidden - position relative +for theme in 'dark' 'dracula' 'solarized-dark' + apply-theme(theme) - .header - background-color transparent - border-color $ui-dracula-borderColor - color $ui-dracula-text-color - - .control-folder-label - color $ui-dracula-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorDraculaPrimaryButton() +for theme in $themes + apply-theme(theme) \ No newline at end of file From 24a5c839a7e2c6fa9bb28efe1a8a0a9c76dae35b Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 31 Jan 2020 12:10:19 +0100 Subject: [PATCH 39/65] fix text color of FolderSelect component --- browser/main/Detail/FolderSelect.styl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/main/Detail/FolderSelect.styl b/browser/main/Detail/FolderSelect.styl index 76ce1eb8..ac903c6c 100644 --- a/browser/main/Detail/FolderSelect.styl +++ b/browser/main/Detail/FolderSelect.styl @@ -147,6 +147,7 @@ apply-theme(theme) border-color get-theme-var(theme, 'borderColor') .search-optionList + color get-theme-var(theme, 'text-color') border-color get-theme-var(theme, 'borderColor') background-color get-theme-var(theme, 'button-backgroundColor') @@ -160,6 +161,7 @@ apply-theme(theme) &:hover background-color get-theme-var(theme, 'button--active-backgroundColor') color get-theme-var(theme, 'button--active-color') + .search-optionList-item-name-surfix color get-theme-var(theme, 'inactive-text-color') From 69831571a5b5ce1fb5b4dbbaf9284d6f8d8f1992 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 31 Jan 2020 23:46:27 +0100 Subject: [PATCH 40/65] fix broken title's color in ConfigTab by moving its default color to corresponding themes --- browser/main/modals/PreferencesModal/ConfigTab.styl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/browser/main/modals/PreferencesModal/ConfigTab.styl b/browser/main/modals/PreferencesModal/ConfigTab.styl index 4e671982..dbfb393e 100644 --- a/browser/main/modals/PreferencesModal/ConfigTab.styl +++ b/browser/main/modals/PreferencesModal/ConfigTab.styl @@ -2,7 +2,6 @@ .root padding 15px - color $ui-text-color margin-bottom 30px .group @@ -14,7 +13,6 @@ .group-header2 font-size 20px - color $ui-text-color margin-bottom 15px margin-top 30px @@ -141,6 +139,14 @@ colorThemedControl(theme) background-color get-theme-var(theme, 'button-backgroundColor') color get-theme-var(theme, 'text-color') +body[data-theme="default"], +body[data-theme="white"] + .root + color $ui-text-color + + .group-header2 + color $ui-text-color + body[data-theme="dark"] .root color $ui-dark-text-color From 636996356fc31fd4e60bf5da305297c1358ee13b Mon Sep 17 00:00:00 2001 From: Bumhan Yu Date: Wed, 29 Jan 2020 23:42:29 -0500 Subject: [PATCH 41/65] update Korean section of contributing.md --- contributing.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/contributing.md b/contributing.md index a7c91e91..43d68d85 100644 --- a/contributing.md +++ b/contributing.md @@ -50,14 +50,26 @@ GPL v3 is too strict to be compatible with another license, so we thought it mig # Contributing to Boostnote (Korean) -### 버그 리포트를 보고할 때 -이슈의 양식은 없습니다. 하지만 부탁이 있습니다. - -**개발자 도구를 연 상태의 Boostnote 스크린샷을 첨부해주세요** +### 이슈 또는 버그 리포트를 제출하는 절차 +이슈를 제기할 때에 사용하는 양식(issue template)이 준비되어 있으니, 해당 양식에 맞추어 최대한 구체적인 정보를 첨부하여 주시기 바랍니다. 도움을 주셔서 감사합니다. -### Pull Request의 저작권에 관하여 +### Pull Request를 제출하는 절차 +Pull Request에 사용하는 양식(pull request template)이 준비되어 있으니, 코드를 접수하기 전에 미리 해당 양식을 작성해 주시기 바랍니다. 코드가 해결하고자 하는 문제가 무엇인지 정확히 알면 저희가 훨씬 신속하게 해당 pull request를 검토할 수 있습니다. + +다음 사항을 준수하여 주십시오: +- [`code_style.md`](docs/code_style.md) 에 정리된 코드 스타일 정보를 확인할 것 +- 테스트 코드를 작성하고, 아래와 같은 테스트 커맨드를 실행할 것 +``` +npm run test +``` +- 아래와 같은 린팅 커맨드로 코드를 확인할 것 +``` +npm run lint +``` + +### 저작권에 관한 기준 당신이 pull request를 요청하면, 코드 변경에 대한 저작권을 BoostIO에 양도한다는 것에 동의한다는 의미입니다. From a90d801d083ca5f1c89bb91cf5b09db20cc964d2 Mon Sep 17 00:00:00 2001 From: Bumhan Yu Date: Mon, 3 Feb 2020 06:03:21 -0500 Subject: [PATCH 42/65] update korean docs in docs/ko/ (#3454) * update korean docs in docs/ko/ * Fix typo Co-authored-by: Junyoung Choi --- docs/ko/build.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++-- docs/ko/debug.md | 20 +++++++++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/docs/ko/build.md b/docs/ko/build.md index 17e9c712..d9a5f4a9 100644 --- a/docs/ko/build.md +++ b/docs/ko/build.md @@ -5,8 +5,6 @@ * npm: 6.x * node: 8.x -`$ grunt pre-build`를 `npm v5.x`에서 실행할 수 없기 때문에, 반드시 `npm v4.x`를 사용하셔야 합니다. - ## 개발 개발에 있어서 Webpack HRM을 사용합니다. @@ -29,6 +27,34 @@ $ yarn run dev > 1. 콤포넌트의 컨스트럭터 함수를 수정할 경우 > 2. 새로운 CSS코드를 추가할 경우(1.과 같은 이유: CSS클래스는 콤포넌트마다 다시 만들어 지는데, 이 작업은 컨스트럭터에서 일어납니다.) +## Pull Request에 사용된 코드를 적용하는 방법 +관련된 Pull request 페이지를 방문하여, url 스트링 마지막에 표기된 PR 번호를 확인합니다. +
      +https://github.com/BoostIO/Boostnote/pull/2794
      +
      +아래의 커맨드를 실행하면서, \ 대신에 위에서 확인한 번호를 입력합니다 (부등호 신호는 빼고 입력하세요). +위에 보여진 예시의 경우, \ 자리에 2794를 입력하면 됩니다. + +_본인의 로컬 컴퓨터에 마스터 브랜치가 복사되어 있지 않은 경우_ +``` +git clone https://github.com/BoostIO/Boostnote.git +cd Boostnote +git fetch origin pull//head: +git checkout +``` + +_이미 마스터 브랜치를 로컬 컴퓨터에 저장해둔 경우_ +``` +git fetch origin pull//head: +git checkout +``` + +_To compile and run the code_ +``` +yarn +yarn run dev +``` + ## 배포 Boostnote에서는 배포 자동화를 위하여 그런트를 사용합니다. @@ -43,3 +69,31 @@ grunt pre-build 실행 파일은 `dist`에서 찾을 수 있습니다. 이 경우, 인증이 되어있지 않기 때문에 업데이터는 사용할 수 없습니다. 필요로 하다면, 이 실행파일에 Codesign나 Authenticode등의 서명을 할 수 있습니다. + +## 독자적인 배포판을 제작하는 방법 (deb, rpm) + +배포판 패키지를 제작하려면 (우분투, 페도라 등) 리눅스 플랫폼에서 `grunt build` 커맨드를 실행하면 됩니다. + +> 참조: 동일한 환경에서 `.deb` 파일과 `.rpm` 파일을 모두 만들 수 있습니다. + +지원되는 버전의 `node`와 `npm`을 설치한 다음, 빌드에 필요한 패키지를 설치합니다. + +우분투/데비안 환경 (Ubuntu/Debian): + +``` +$ sudo apt-get install -y rpm fakeroot +``` + +페도라 환경 (Fedora): + +``` +$ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot +``` + +그 다음 `grunt build` 커맨드를 실행합니다. + +``` +$ grunt build +``` + +`dist` 디렉토리에 `.deb` 파일과 `.rpm` 파일이 새롭게 생성됩니다. diff --git a/docs/ko/debug.md b/docs/ko/debug.md index 290eee9c..6d68d951 100644 --- a/docs/ko/debug.md +++ b/docs/ko/debug.md @@ -1,5 +1,7 @@ # Boostnote의 디버그 방법(Electron app) +## 구글 크롬 Developer Tools를 사용한 디버깅 + Boostnote는 Electron 애플리케이션이므로 Chromium위에서 작동합니다. 그렇기 때문에 개발자분들은 Google Chrome 브라우저에서 처럼 `Developer Tools`를 사용하실 수 있습니다. 다음과 같이 `Developer Tools`를 실행할 수 있습니다: @@ -10,12 +12,26 @@ Boostnote는 Electron 애플리케이션이므로 Chromium위에서 작동합니 에러가 발생할 때에는, 에러메시지가 `console`위에 표시 됩니다. -## 디버깅 +### 디버깅 예를들면 `debugger`를 사용하여 코드 안에서 다음과 같이 일시 정지지점을 설정할 수 있습니다: ![debugger](https://cloud.githubusercontent.com/assets/11307908/24343879/9459efea-127d-11e7-9943-f60bf7f66d4a.png) 이는 단순한 하나의 예시에 불과합니다. 자기자신에게 가장 잘 맞는 디버그 방법을 찾는 것도 좋을 것 입니다. -## 참고 + ### 참고 * [디버그에 관한 Google Chrome의 공식 문서](https://developer.chrome.com/devtools) + +## 비주얼 스튜디오 코드를 사용한 디버깅 + +1. **[크롬 디버깅 플러그인](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome 'Install Debugger for Chrome')** 을 비주얼 스튜디오 코드에 설치한 후, 프로그램을 닫았다가 재실행합니다. +2. **Shift+Command+B** 키를 누르거나, **Terminal** 메뉴 하단에 있는 **Run Build Task** 메뉴를 선택한 후 **Build Boostnote** 를 선택합니다. 아니면 터미널에서 곧바로 `yarn run watch`를 실행해도 됩니다. +3. 위의 절차가 실행되고 있을 때, 사이드바 **Activity Bar**에서 **Debug view**를 선택합니다. 키보드 단축키로는 **Shift+Command+D**를 눌러도 됩니다.. +4. **Debug configuration**에서 **Boostnote All** 설정을 선택한 후, 초록색 화살표를 클릭하거나 **F5** 키를 누르면 디버깅이 시작됩니다. +5. 이 시점에서는 **Boostnote**가 실행되고 있을 텐데, 두 개의 프로세스가 진행중인 것을 볼 수 있을 겁니다. 바로 **Boostnote Main** 프로세스와 **Boostnote Renderer** 프로세스입니다. 이제 비주얼 스튜디오 코드에서 곧바로 **디버깅 정지지점 (debug breakpoint)** 을 설정할 수 있습니다. 만약에 지정한 **정지지점 (breakpoint)** 이 등록되지 않는다면, **Boostnote Renderer** 와 **Boostnote Main** 프로세스 사이를 번갈아 확인해 보아야 합니다. + + +### 참고 + +- [일렉트론 애플리케이션 디버깅 공식 튜토리얼](https://electronjs.org/docs/tutorial/application-debugging) +- [비쥬얼 스튜디오 코드용 크롬 디버깅 플러그인](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) From e558fae4b01336de2d01bf023822474569085839 Mon Sep 17 00:00:00 2001 From: Ray Ou Date: Mon, 6 Jan 2020 20:33:52 +1100 Subject: [PATCH 43/65] Update build.md with correct description --- docs/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.md b/docs/build.md index 1e269b4c..2a8c22af 100644 --- a/docs/build.md +++ b/docs/build.md @@ -37,7 +37,7 @@ Visit the page for the pull request and look at the end of the url for the PR nu https://github.com/BoostIO/Boostnote/pull/2794 In the following, replace \ with that number (no brackets). -For the above url, you would replace \ with 2794 +For URLs below, you would replace \ with 2794 _If you do not have a local copy of the master branch yet_ ``` From 262d173c658491e1b81b507a5c9f5554492824f7 Mon Sep 17 00:00:00 2001 From: xatier Date: Sat, 4 Jan 2020 23:01:06 -0800 Subject: [PATCH 44/65] Update zh_TW translation of build.md Complete translation. Sync with English version. [1] https://github.com/BoostIO/Boostnote/blob/master/docs/build.md. --- docs/zh_TW/build.md | 64 ++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/docs/zh_TW/build.md b/docs/zh_TW/build.md index 151f946d..00e3d5f7 100644 --- a/docs/zh_TW/build.md +++ b/docs/zh_TW/build.md @@ -10,7 +10,6 @@ ## 開發 我們使用 Webpack HMR 來開發 Boostnote。 - 在專案根目錄底下執行下列指令,將會以原始設置啟動 Boostnote。 **用 yarn 來安裝必要 packages** @@ -19,41 +18,70 @@ $ yarn ``` -**開始開發** +**編譯及執行** ``` $ yarn run dev ``` -> ### Notice +> ### 注意 > -> There are some cases where you have to refresh the app manually. +> 以下是一些可能要手動重新啟動程式的情況。 > -> 1. When editing a constructor method of a component -> 2. When adding a new css class (similar to 1: the CSS class is re-written by each component. This process occurs at the Constructor method.) +> 1. 修改一個 component 的 constructor 方法時。 +> 2. 新增新的 CSS 類別時 (和 1 很類似:CSS 類別會被每個 component 重寫過。這個過程在 constructor 方法中發生)。 -## Deploy +## 使用 Pull Requests 中的程式碼 +瀏覽 pull request 的頁面,從 URL 的後面找到 PR 號碼。 -We use Grunt to automate deployment. -You can build the program by using `grunt`. However, we don't recommend this because the default task includes codesign and authenticode. +
      +https://github.com/BoostIO/Boostnote/pull/2794
      +
      +接著,於底下步驟中把 \ 換成這個號碼 (沒有括號)。 +請將上面的 URL 中的 \ 換置成 2794。 -So, we've prepared a separate script which just makes an executable file. +_如果您還未取得一份 master branch 的本地備份_ +``` +git clone https://github.com/BoostIO/Boostnote.git +cd Boostnote +git fetch origin pull//head: +git checkout +``` + +_如果您已經擁有了 master branch_ +``` +git fetch origin pull//head: +git checkout +``` + +_編譯及執行程式碼_ +``` +yarn +yarn run dev +``` + +## 佈署 + +我們用 Grunt 做自動佈署。 +您能使用 `grung` 建構本程式。然而,我們並不建議這麼做,因為預設工作流程包含了程式簽名以及 Authenticode 驗證。 + +所以,我們準備了一份額外的腳本用於建構可執行檔。 ``` grunt pre-build ``` -You will find the executable in the `dist` directory. Note, the auto updater won't work because the app isn't signed. +您可以在 `dist` 資料夾下找到可執行檔。注意,自動更新功能 (auto updater) 並不會生效,因為程式沒有被簽署過。 -If you find it necessary, you can use codesign or authenticode with this executable. +必要時您可以使用程式簽名或 authenticode 驗證執行檔。 -## Make own distribution packages (deb, rpm) +## 建立您自己的發行版套件 (deb, rpm) -Distribution packages are created by exec `grunt build` on Linux platform (e.g. Ubuntu, Fedora). +發行版套件可以透過在 Linux 平台上 (如 Ubuntu, Fedora) 執行 `grunt build` 來建立。 -> Note: You can create both `.deb` and `.rpm` in a single environment. +> 注意:您可以在同個環境中同時建立 `.deb` 及`.rpm` 。 -After installing the supported version of `node` and `npm`, install build dependency packages. +安裝支援版本的 `node` 和 `npm` 後,安裝編譯相依套件。 Ubuntu/Debian: @@ -67,10 +95,10 @@ Fedora: $ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot ``` -Then execute `grunt build`. +接著執行 `grunt build`。 ``` $ grunt build ``` -You will find `.deb` and `.rpm` in the `dist` directory. +於 `dist` 資料夾下找到 `.deb` 及 `.rpm`。 From 57a5de97f8aef19fc5d36738b54e8b982b840066 Mon Sep 17 00:00:00 2001 From: xatier Date: Mon, 6 Jan 2020 09:10:10 -0800 Subject: [PATCH 45/65] Update build.md text change per @rayou's suggestion. --- docs/zh_TW/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh_TW/build.md b/docs/zh_TW/build.md index 00e3d5f7..4f9c7a96 100644 --- a/docs/zh_TW/build.md +++ b/docs/zh_TW/build.md @@ -38,7 +38,7 @@ $ yarn run dev https://github.com/BoostIO/Boostnote/pull/2794 接著,於底下步驟中把 \ 換成這個號碼 (沒有括號)。 -請將上面的 URL 中的 \ 換置成 2794。 +請將下方 URL 中的 \ 換置成 2794。 _如果您還未取得一份 master branch 的本地備份_ ``` From 000a54f5ed54dee310bd88ecb2eed85459d61dfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Dec 2019 06:22:50 +0000 Subject: [PATCH 46/65] Bump handlebars from 4.0.11 to 4.5.3 Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.0.11 to 4.5.3. - [Release notes](https://github.com/wycats/handlebars.js/releases) - [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md) - [Commits](https://github.com/wycats/handlebars.js/compare/v4.0.11...v4.5.3) Signed-off-by: dependabot[bot] --- yarn.lock | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 77621a24..b18d51a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -486,7 +486,7 @@ async@^0.9.0, async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" -async@^1.3.0, async@^1.4.0, async@^1.4.2, async@^1.5.1, async@^1.5.2: +async@^1.3.0, async@^1.4.2, async@^1.5.1, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -4420,14 +4420,15 @@ gud@^1.0.0: integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== handlebars@^4.0.3: - version "4.0.11" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" + version "4.5.3" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" + integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== dependencies: - async "^1.4.0" + neo-async "^2.6.0" optimist "^0.6.1" - source-map "^0.4.4" + source-map "^0.6.1" optionalDependencies: - uglify-js "^2.6" + uglify-js "^3.1.4" har-schema@^2.0.0: version "2.0.0" @@ -6553,6 +6554,7 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= minipass@^2.2.1, minipass@^2.3.3: version "2.3.3" @@ -6696,6 +6698,11 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +neo-async@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + next-tick@1: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -7021,6 +7028,7 @@ open@0.0.5: optimist@^0.6.1, optimist@~0.6.0, optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= dependencies: minimist "~0.0.1" wordwrap "~0.0.2" @@ -8976,12 +8984,6 @@ source-map@0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" -source-map@^0.4.4, source-map@~0.4.1: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - dependencies: - amdefine ">=0.0.4" - source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -8990,6 +8992,12 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" +source-map@~0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + sourcemapped-stacktrace@^1.1.6: version "1.1.8" resolved "https://registry.yarnpkg.com/sourcemapped-stacktrace/-/sourcemapped-stacktrace-1.1.8.tgz#6b7a3f1a6fb15f6d40e701e23ce404553480d688" @@ -9680,14 +9688,13 @@ uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== -uglify-js@^2.6: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" +uglify-js@^3.1.4: + version "3.7.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a" + integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg== dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" + commander "~2.20.3" + source-map "~0.6.1" uglify-js@^3.5.1: version "3.6.9" @@ -10144,6 +10151,7 @@ wordwrap@0.0.2: wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= wordwrap@~1.0.0: version "1.0.0" From f72fdfe33f92888cd508c18367461bcbdf1e1329 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2019 04:30:13 +0000 Subject: [PATCH 47/65] Bump mixin-deep from 1.3.1 to 1.3.2 Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/jonschlinkert/mixin-deep/releases) - [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2) Signed-off-by: dependabot[bot] --- yarn.lock | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index b18d51a4..0aa21a45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3952,6 +3952,7 @@ font-awesome@^4.3.0: for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= for-own@^0.1.4: version "0.1.5" @@ -4998,6 +4999,7 @@ is-extendable@^0.1.0, is-extendable@^0.1.1: is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" @@ -5127,6 +5129,7 @@ is-plain-obj@^1.0.0: is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" @@ -5230,6 +5233,7 @@ isobject@^2.0.0: isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isomorphic-fetch@^2.1.1: version "2.2.1" @@ -6570,8 +6574,9 @@ minizlib@^1.1.0: minipass "^2.2.1" mixin-deep@^1.1.3, mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" From f367e9f08cd1e9767ce098cb23d1afa42ca9b8d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2019 04:30:24 +0000 Subject: [PATCH 48/65] Bump extend from 3.0.1 to 3.0.2 Bumps [extend](https://github.com/justmoon/node-extend) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/justmoon/node-extend/releases) - [Changelog](https://github.com/justmoon/node-extend/blob/master/CHANGELOG.md) - [Commits](https://github.com/justmoon/node-extend/compare/v3.0.1...v3.0.2) Signed-off-by: dependabot[bot] --- yarn.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0aa21a45..2d153615 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3669,8 +3669,9 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: is-extendable "^1.0.1" extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== extglob@^0.3.1: version "0.3.2" From 051ce9e2086f8cfc3c10a3d0fd5a5664bdd35fb5 Mon Sep 17 00:00:00 2001 From: hiiwave Date: Mon, 3 Feb 2020 19:33:38 +0800 Subject: [PATCH 49/65] Fix #3397 (#3398) * WIP: Fix #3397 * fixup! WIP: Fix #3397 * fix: catch potential URIError threw from decodeURI --- browser/components/MarkdownPreview.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index ff88540f..fc5aa95c 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -1135,7 +1135,17 @@ export default class MarkdownPreview extends React.Component { } // other case - shell.openExternal(href) + this.openExternal(href) + } + + openExternal (href) { + try { + const success = shell.openExternal(href) || shell.openExternal(decodeURI(href)) + if (!success) console.error('failed to open url ' + href) + } catch (e) { + // URI Error threw from decodeURI + console.error(e) + } } render () { From 592aca1539166bb9a6d1eaf36a20203fff584c37 Mon Sep 17 00:00:00 2001 From: Nguyen Viet Hung Date: Wed, 5 Feb 2020 13:28:27 +1300 Subject: [PATCH 50/65] fixed eslint error & integrated with prettier as well as formatted the whole codebase (#3450) --- .eslintrc | 7 +- .prettierrc | 5 + browser/components/CodeEditor.js | 368 +++-- browser/components/ColorPicker.js | 27 +- browser/components/MarkdownEditor.js | 301 ++-- browser/components/MarkdownPreview.js | 217 +-- browser/components/MarkdownSplitEditor.js | 88 +- browser/components/ModalEscButton.js | 4 +- browser/components/NavToggleButton.js | 25 +- browser/components/NoteItem.js | 72 +- browser/components/NoteItemSimple.js | 46 +- browser/components/RealtimeNotification.js | 38 +- browser/components/SideNavFilter.js | 58 +- browser/components/SnippetTab.js | 117 +- browser/components/StorageItem.js | 16 +- browser/components/StorageList.js | 14 +- browser/components/TagListItem.js | 62 +- browser/components/TodoListPercentage.js | 15 +- browser/components/TodoProcess.js | 15 +- browser/components/render/MermaidRender.js | 12 +- browser/lib/CSSModules.js | 4 +- browser/lib/Languages.js | 6 +- browser/lib/Mutable.js | 40 +- browser/lib/RcParser.js | 4 +- browser/lib/SnippetManager.js | 11 +- browser/lib/TextEditorInterface.js | 28 +- browser/lib/confirmDeleteNote.js | 5 +- browser/lib/consts.js | 56 +- browser/lib/context.js | 4 +- browser/lib/contextMenuBuilder.js | 128 +- browser/lib/convertModeName.js | 2 +- browser/lib/customMeta.js | 15 +- browser/lib/date-formatter.js | 2 +- browser/lib/findNoteTitle.js | 25 +- browser/lib/findStorage.js | 9 +- browser/lib/getTodoStatus.js | 8 +- browser/lib/htmlTextHelper.js | 10 +- browser/lib/i18n.js | 7 +- browser/lib/keygen.js | 2 +- browser/lib/markdown-it-deflist.js | 124 +- browser/lib/markdown-it-fence.js | 17 +- browser/lib/markdown-it-frontmatter.js | 15 +- browser/lib/markdown-it-sanitize-html.js | 29 +- browser/lib/markdown-toc-generator.js | 25 +- browser/lib/markdown.js | 364 +++-- browser/lib/markdownTextHelper.js | 2 +- browser/lib/newNote.js | 53 +- browser/lib/normalizeEditorFontFamily.js | 2 +- browser/lib/search.js | 25 +- browser/lib/slugify.js | 10 +- browser/lib/spellcheck.js | 107 +- browser/lib/turndown.js | 2 +- browser/lib/utils.js | 14 +- browser/main/Detail/FolderSelect.js | 247 +-- browser/main/Detail/FromUrlButton.js | 40 +- browser/main/Detail/FullscreenButton.js | 14 +- browser/main/Detail/InfoButton.js | 8 +- browser/main/Detail/InfoPanel.js | 80 +- browser/main/Detail/InfoPanelTrashed.js | 40 +- browser/main/Detail/MarkdownNoteDetail.js | 541 ++++--- browser/main/Detail/PermanentDeleteButton.js | 8 +- browser/main/Detail/RestoreButton.js | 8 +- browser/main/Detail/SnippetNoteDetail.js | 848 +++++----- browser/main/Detail/StarButton.js | 44 +- browser/main/Detail/TagSelect.js | 174 +- browser/main/Detail/ToggleDirectionButton.js | 8 +- browser/main/Detail/ToggleModeButton.js | 34 +- browser/main/Detail/TrashButton.js | 12 +- browser/main/Detail/index.js | 50 +- browser/main/Main.js | 65 +- browser/main/NewNoteButton/index.js | 65 +- browser/main/NoteList/index.js | 726 +++++---- browser/main/SideNav/ListButton.js | 19 +- browser/main/SideNav/PreferenceButton.js | 6 +- browser/main/SideNav/StorageItem.js | 259 +-- browser/main/SideNav/TagButton.js | 19 +- browser/main/SideNav/index.js | 344 ++-- browser/main/StatusBar/index.js | 57 +- browser/main/TopBar/index.js | 88 +- browser/main/index.js | 78 +- browser/main/lib/AwsMobileAnalyticsConfig.js | 12 +- browser/main/lib/Commander.js | 13 +- browser/main/lib/ConfigManager.js | 77 +- browser/main/lib/ZoomManager.js | 10 +- browser/main/lib/dataApi/addStorage.js | 44 +- .../main/lib/dataApi/attachmentManagement.js | 692 +++++--- browser/main/lib/dataApi/copyFile.js | 2 +- browser/main/lib/dataApi/createFolder.js | 38 +- browser/main/lib/dataApi/createNote.js | 45 +- browser/main/lib/dataApi/createNoteFromUrl.js | 54 +- browser/main/lib/dataApi/createSnippet.js | 24 +- browser/main/lib/dataApi/deleteFolder.js | 47 +- browser/main/lib/dataApi/deleteNote.js | 11 +- browser/main/lib/dataApi/deleteSnippet.js | 20 +- browser/main/lib/dataApi/exportFolder.js | 46 +- browser/main/lib/dataApi/exportNote.js | 43 +- browser/main/lib/dataApi/exportStorage.js | 19 +- browser/main/lib/dataApi/fetchSnippet.js | 6 +- browser/main/lib/dataApi/init.js | 77 +- .../main/lib/dataApi/migrateFromV5Storage.js | 153 +- .../main/lib/dataApi/migrateFromV6Storage.js | 93 +- browser/main/lib/dataApi/moveNote.js | 154 +- browser/main/lib/dataApi/removeStorage.js | 9 +- browser/main/lib/dataApi/renameStorage.js | 7 +- browser/main/lib/dataApi/reorderFolder.js | 22 +- .../main/lib/dataApi/resolveStorageData.js | 12 +- .../main/lib/dataApi/resolveStorageNotes.js | 10 +- browser/main/lib/dataApi/toggleStorage.js | 4 +- browser/main/lib/dataApi/updateFolder.js | 26 +- browser/main/lib/dataApi/updateNote.js | 135 +- browser/main/lib/dataApi/updateSnippet.js | 20 +- browser/main/lib/eventEmitter.js | 8 +- browser/main/lib/ipcClient.js | 10 +- browser/main/lib/modal.js | 48 +- browser/main/lib/notify.js | 8 +- browser/main/lib/shortcut.js | 8 +- browser/main/lib/shortcutManager.js | 8 +- browser/main/modals/CreateFolderModal.js | 46 +- .../main/modals/CreateMarkdownFromURLModal.js | 64 +- browser/main/modals/NewNoteModal.js | 54 +- browser/main/modals/PreferencesModal/Blog.js | 124 +- .../modals/PreferencesModal/Crowdfunding.js | 81 +- .../modals/PreferencesModal/FolderItem.js | 206 +-- .../modals/PreferencesModal/FolderList.js | 46 +- .../main/modals/PreferencesModal/HotkeyTab.js | 225 ++- .../main/modals/PreferencesModal/InfoTab.js | 156 +- .../modals/PreferencesModal/SnippetEditor.js | 54 +- .../modals/PreferencesModal/SnippetList.js | 86 +- .../modals/PreferencesModal/SnippetTab.js | 61 +- .../modals/PreferencesModal/StorageItem.js | 133 +- .../modals/PreferencesModal/StoragesTab.js | 229 +-- browser/main/modals/PreferencesModal/UiTab.js | 592 ++++--- browser/main/modals/PreferencesModal/index.js | 109 +- browser/main/modals/RenameFolderModal.js | 41 +- browser/main/store.js | 345 ++-- dev-scripts/dev.js | 6 +- gruntfile.js | 201 ++- index.js | 18 +- lib/ipcServer.js | 10 +- lib/main-app.js | 19 +- lib/main-menu.js | 257 +-- lib/main-window.js | 19 +- lib/touchbar-menu.js | 7 +- package.json | 4 +- tests/components/TagListItem.snapshot.test.js | 4 +- tests/dataApi/addStorage.js | 23 +- tests/dataApi/attachmentManagement.test.js | 1412 +++++++++++++---- tests/dataApi/copyFile-test.js | 7 +- tests/dataApi/createFolder-test.js | 19 +- tests/dataApi/createNote-test.js | 51 +- tests/dataApi/createNoteFromUrl-test.js | 30 +- tests/dataApi/createSnippet-test.js | 16 +- tests/dataApi/deleteFolder-test.js | 67 +- tests/dataApi/deleteNote-test.js | 55 +- tests/dataApi/deleteSnippet-test.js | 12 +- tests/dataApi/exportFolder-test.js | 29 +- tests/dataApi/exportStorage-test.js | 43 +- tests/dataApi/init.js | 40 +- tests/dataApi/migrateFromV6Storage-test.js | 55 +- tests/dataApi/moveNote-test.js | 28 +- tests/dataApi/removeStorage-test.js | 15 +- tests/dataApi/renameStorage-test.js | 17 +- tests/dataApi/reorderFolder-test.js | 19 +- tests/dataApi/toggleStorage-test.js | 17 +- tests/dataApi/updateFolder-test.js | 19 +- tests/dataApi/updateNote-test.js | 91 +- tests/dataApi/updateSnippet-test.js | 16 +- tests/date-formatter-test.js | 5 +- tests/fixtures/TestDummy.js | 112 +- tests/helpers/setup-browser-env.js | 14 +- tests/jest.js | 2 +- tests/lib/contextMenuBuilder.test.js | 79 +- tests/lib/find-storage-test.js | 9 +- tests/lib/find-title-test.js | 29 +- tests/lib/get-todo-status-test.js | 13 +- tests/lib/html-text-helper-test.js | 46 +- tests/lib/markdown-text-helper-test.js | 6 +- tests/lib/markdown-toc-generator-test.js | 30 +- .../lib/normalize-editor-font-family-test.js | 5 +- tests/lib/rc-parser-test.js | 56 +- tests/lib/search-test.js | 15 +- tests/lib/slugify-test.js | 2 +- tests/lib/spellcheck.test.js | 269 ++-- webpack-production.config.js | 7 +- webpack.config.js | 4 +- yarn.lock | 258 ++- 186 files changed, 9233 insertions(+), 5565 deletions(-) create mode 100644 .prettierrc diff --git a/.eslintrc b/.eslintrc index be8cb903..67b6c8fe 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { - "extends": ["standard", "standard-jsx", "plugin:react/recommended"], - "plugins": ["react"], + "extends": ["standard", "standard-jsx", "plugin:react/recommended", "prettier"], + "plugins": ["react", "prettier"], "rules": { "no-useless-escape": 0, "prefer-const": ["warn", { @@ -13,7 +13,8 @@ "react/no-string-refs": 0, "react/no-find-dom-node": "warn", "react/no-render-return-value": "warn", - "react/no-deprecated": "warn" + "react/no-deprecated": "warn", + "prettier/prettier": ["error"] }, "globals": { "FileReader": true, diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..515c6cd5 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "semi": false, + "jsxSingleQuote": true +} \ No newline at end of file diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index bc2719dc..a5bb009a 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -6,11 +6,7 @@ import hljs from 'highlight.js' import 'codemirror-mode-elixir' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' import convertModeName from 'browser/lib/convertModeName' -import { - options, - TableEditor, - Alignment -} from '@susisu/mte-kernel' +import { options, TableEditor, Alignment } from '@susisu/mte-kernel' import TextEditorInterface from 'browser/lib/TextEditorInterface' import eventEmitter from 'browser/main/lib/eventEmitter' import iconv from 'iconv-lite' @@ -20,11 +16,15 @@ import styles from '../components/CodeEditor.styl' const { ipcRenderer, remote, clipboard } = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' const spellcheck = require('browser/lib/spellcheck') -const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu +const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') + .buildEditorContextMenu import { createTurndownService } from '../lib/turndown' -import {languageMaps} from '../lib/CMLanguageList' +import { languageMaps } from '../lib/CMLanguageList' import snippetManager from '../lib/SnippetManager' -import {generateInEditor, tocExistsInEditor} from 'browser/lib/markdown-toc-generator' +import { + generateInEditor, + tocExistsInEditor +} from 'browser/lib/markdown-toc-generator' import markdownlint from 'markdownlint' import Jsonlint from 'jsonlint-mod' import { DEFAULT_CONFIG } from '../main/lib/ConfigManager' @@ -33,28 +33,38 @@ import prettier from 'prettier' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({ - column: ruler - })) : []) + enableRulers + ? rulers.map(ruler => ({ + column: ruler + })) + : [] -function translateHotkey (hotkey) { - return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl') +function translateHotkey(hotkey) { + return hotkey + .replace(/\s*\+\s*/g, '-') + .replace(/Command/g, 'Cmd') + .replace(/Control/g, 'Ctrl') } export default class CodeEditor extends React.Component { - constructor (props) { + constructor(props) { super(props) this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { leading: false, trailing: true }) - this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject) - this.highlightHandler = (editor, changeObject) => this.handleHighlight(editor, changeObject) + this.changeHandler = (editor, changeObject) => + this.handleChange(editor, changeObject) + this.highlightHandler = (editor, changeObject) => + this.handleHighlight(editor, changeObject) this.focusHandler = () => { ipcRenderer.send('editor:focused', true) } - const debouncedDeletionOfAttachments = _.debounce(attachmentManagement.deleteAttachmentsNotPresentInNote, 30000) + const debouncedDeletionOfAttachments = _.debounce( + attachmentManagement.deleteAttachmentsNotPresentInNote, + 30000 + ) this.blurHandler = (editor, e) => { ipcRenderer.send('editor:focused', false) if (e == null) return null @@ -66,12 +76,13 @@ export default class CodeEditor extends React.Component { el = el.parentNode } this.props.onBlur != null && this.props.onBlur(e) - const { - storageKey, - noteKey - } = this.props + const { storageKey, noteKey } = this.props if (this.props.deleteUnusedAttachments === true) { - debouncedDeletionOfAttachments(this.editor.getValue(), storageKey, noteKey) + debouncedDeletionOfAttachments( + this.editor.getValue(), + storageKey, + noteKey + ) } } this.pasteHandler = (editor, e) => { @@ -91,7 +102,7 @@ export default class CodeEditor extends React.Component { this.formatTable = () => this.handleFormatTable() if (props.switchPreview !== 'RIGHTCLICK') { - this.contextMenuHandler = function (editor, event) { + this.contextMenuHandler = function(editor, event) { const menu = buildEditorContextMenu(editor, event) if (menu != null) { setTimeout(() => menu.popup(remote.getCurrentWindow()), 30) @@ -104,24 +115,24 @@ export default class CodeEditor extends React.Component { this.turndownService = createTurndownService() } - handleSearch (msg) { + handleSearch(msg) { const cm = this.editor const component = this if (component.searchState) cm.removeOverlay(component.searchState) if (msg.length < 1) return - cm.operation(function () { + cm.operation(function() { component.searchState = makeOverlay(msg, 'searching') cm.addOverlay(component.searchState) - function makeOverlay (query, style) { + function makeOverlay(query, style) { query = new RegExp( query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi' ) return { - token: function (stream) { + token: function(stream) { query.lastIndex = stream.pos var match = query.exec(stream.string) if (match && match.index === stream.pos) { @@ -138,25 +149,27 @@ export default class CodeEditor extends React.Component { }) } - handleFormatTable () { - this.tableEditor.formatAll(options({ - textWidthOptions: {} - })) + handleFormatTable() { + this.tableEditor.formatAll( + options({ + textWidthOptions: {} + }) + ) } - handleEditorActivity () { + handleEditorActivity() { if (!this.textEditorInterface.transaction) { this.updateTableEditorState() } } - updateDefaultKeyMap () { + updateDefaultKeyMap() { const { hotkey } = this.props const self = this const expandSnippet = snippetManager.expandSnippet this.defaultKeyMap = CodeMirror.normalizeKeyMap({ - Tab: function (cm) { + Tab: function(cm) { const cursor = cm.getCursor() const line = cm.getLine(cursor.line) const cursorPosition = cursor.ch @@ -198,17 +211,17 @@ export default class CodeEditor extends React.Component { } } }, - 'Cmd-Left': function (cm) { + 'Cmd-Left': function(cm) { cm.execCommand('goLineLeft') }, - 'Cmd-T': function (cm) { + 'Cmd-T': function(cm) { // Do nothing }, - [translateHotkey(hotkey.insertDate)]: function (cm) { + [translateHotkey(hotkey.insertDate)]: function(cm) { const dateNow = new Date() cm.replaceSelection(dateNow.toLocaleDateString()) }, - [translateHotkey(hotkey.insertDateTime)]: function (cm) { + [translateHotkey(hotkey.insertDateTime)]: function(cm) { const dateNow = new Date() cm.replaceSelection(dateNow.toLocaleString()) }, @@ -231,7 +244,10 @@ export default class CodeEditor extends React.Component { currentConfig.cursorOffset = cm.doc.indexFromPos(cursorPos) // Prettify contents of editor - const formattedTextDetails = prettier.formatWithCursor(cm.doc.getValue(), currentConfig) + const formattedTextDetails = prettier.formatWithCursor( + cm.doc.getValue(), + currentConfig + ) const formattedText = formattedTextDetails.formatted const formattedCursorPos = formattedTextDetails.cursorOffset @@ -246,7 +262,8 @@ export default class CodeEditor extends React.Component { const appendLineBreak = /\n$/.test(selection) const sorted = _.split(selection.trim(), '\n').sort() - const sortedString = _.join(sorted, '\n') + (appendLineBreak ? '\n' : '') + const sortedString = + _.join(sorted, '\n') + (appendLineBreak ? '\n' : '') cm.doc.replaceSelection(sortedString) }, @@ -256,7 +273,7 @@ export default class CodeEditor extends React.Component { }) } - updateTableEditorState () { + updateTableEditorState() { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { @@ -272,7 +289,7 @@ export default class CodeEditor extends React.Component { } } - componentDidMount () { + componentDidMount() { const { rulers, enableRulers, enableMarkdownLint, RTL } = this.props eventEmitter.on('line:jump', this.scrollToLineHandeler) @@ -298,7 +315,11 @@ export default class CodeEditor extends React.Component { rtlMoveVisually: RTL, foldGutter: true, lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false, - gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], + gutters: [ + 'CodeMirror-linenumbers', + 'CodeMirror-foldgutter', + 'CodeMirror-lint-markers' + ], autoCloseBrackets: { pairs: this.props.matchingPairs, triples: this.props.matchingTriples, @@ -309,7 +330,9 @@ export default class CodeEditor extends React.Component { prettierConfig: this.props.prettierConfig }) - document.querySelector('.CodeMirror-lint-markers').style.display = enableMarkdownLint ? 'inline-block' : 'none' + document.querySelector( + '.CodeMirror-lint-markers' + ).style.display = enableMarkdownLint ? 'inline-block' : 'none' if (!this.props.mode && this.props.value && this.props.autoDetect) { this.autoDetectLanguage(this.props.value) @@ -342,7 +365,7 @@ export default class CodeEditor extends React.Component { this.textEditorInterface = new TextEditorInterface(this.editor) this.tableEditor = new TableEditor(this.textEditorInterface) if (this.props.spellCheck) { - this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'}) + this.editor.addPanel(this.createSpellCheckPanel(), { position: 'bottom' }) } eventEmitter.on('code:format-table', this.formatTable) @@ -352,13 +375,13 @@ export default class CodeEditor extends React.Component { }) this.editorKeyMap = CodeMirror.normalizeKeyMap({ - 'Tab': () => { + Tab: () => { this.tableEditor.nextCell(this.tableEditorOptions) }, 'Shift-Tab': () => { this.tableEditor.previousCell(this.tableEditorOptions) }, - 'Enter': () => { + Enter: () => { this.tableEditor.nextRow(this.tableEditorOptions) }, 'Ctrl-Enter': () => { @@ -477,7 +500,7 @@ export default class CodeEditor extends React.Component { this.initialHighlighting() } - getWordBeforeCursor (line, lineNumber, cursorPosition) { + getWordBeforeCursor(line, lineNumber, cursorPosition) { let wordBeforeCursor = '' const originCursorPosition = cursorPosition const emptyChars = /\t|\s|\r|\n|\$/ @@ -514,11 +537,11 @@ export default class CodeEditor extends React.Component { } } - quitEditor () { + quitEditor() { document.querySelector('textarea').blur() } - componentWillUnmount () { + componentWillUnmount() { this.editor.off('focus', this.focusHandler) this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) @@ -533,7 +556,7 @@ export default class CodeEditor extends React.Component { eventEmitter.off('code:format-table', this.formatTable) } - componentDidUpdate (prevProps, prevState) { + componentDidUpdate(prevProps, prevState) { let needRefresh = false const { rulers, @@ -561,13 +584,18 @@ export default class CodeEditor extends React.Component { this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr') this.editor.setOption('rtlMoveVisually', this.props.RTL) } - if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) { + if ( + prevProps.enableMarkdownLint !== enableMarkdownLint || + prevProps.customMarkdownLintConfig !== customMarkdownLintConfig + ) { if (!enableMarkdownLint) { - this.editor.setOption('lint', {default: false}) - document.querySelector('.CodeMirror-lint-markers').style.display = 'none' + this.editor.setOption('lint', { default: false }) + document.querySelector('.CodeMirror-lint-markers').style.display = + 'none' } else { this.editor.setOption('lint', this.getCodeEditorLintConfig()) - document.querySelector('.CodeMirror-lint-markers').style.display = 'inline-block' + document.querySelector('.CodeMirror-lint-markers').style.display = + 'inline-block' } needRefresh = true } @@ -599,9 +627,11 @@ export default class CodeEditor extends React.Component { this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd) } - if (prevProps.matchingPairs !== this.props.matchingPairs || + if ( + prevProps.matchingPairs !== this.props.matchingPairs || prevProps.matchingTriples !== this.props.matchingTriples || - prevProps.explodingPairs !== this.props.explodingPairs) { + prevProps.explodingPairs !== this.props.explodingPairs + ) { const bracketObject = { pairs: this.props.matchingPairs, triples: this.props.matchingTriples, @@ -646,11 +676,18 @@ export default class CodeEditor extends React.Component { const elem = document.getElementById('editor-bottom-panel') elem.parentNode.removeChild(elem) } else { - this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'}) + this.editor.addPanel(this.createSpellCheckPanel(), { + position: 'bottom' + }) } } - if (prevProps.deleteUnusedAttachments !== this.props.deleteUnusedAttachments) { - this.editor.setOption('deleteUnusedAttachments', this.props.deleteUnusedAttachments) + if ( + prevProps.deleteUnusedAttachments !== this.props.deleteUnusedAttachments + ) { + this.editor.setOption( + 'deleteUnusedAttachments', + this.props.deleteUnusedAttachments + ) } if (needRefresh) { @@ -658,17 +695,19 @@ export default class CodeEditor extends React.Component { } } - getCodeEditorLintConfig () { + getCodeEditorLintConfig() { const { mode } = this.props const checkMarkdownNoteIsOpen = mode === 'Boost Flavored Markdown' - return checkMarkdownNoteIsOpen ? { - getAnnotations: this.validatorOfMarkdown, - async: true - } : false + return checkMarkdownNoteIsOpen + ? { + getAnnotations: this.validatorOfMarkdown, + async: true + } + : false } - validatorOfMarkdown (text, updateLinting) { + validatorOfMarkdown(text, updateLinting) { const { customMarkdownLintConfig } = this.props let lintConfigJson try { @@ -693,7 +732,7 @@ export default class CodeEditor extends React.Component { let ruleNames = '' item.ruleNames.map((ruleName, index) => { ruleNames += ruleName - ruleNames += (index === item.ruleNames.length - 1) ? ': ' : '/' + ruleNames += index === item.ruleNames.length - 1 ? ': ' : '/' }) const lineNumber = item.lineNumber - 1 foundIssues.push({ @@ -708,7 +747,7 @@ export default class CodeEditor extends React.Component { }) } - setMode (mode) { + setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text')) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') @@ -716,7 +755,7 @@ export default class CodeEditor extends React.Component { CodeMirror.autoLoadMode(this.editor, syntax.mode) } - handleChange (editor, changeObject) { + handleChange(editor, changeObject) { spellcheck.handleChange(editor, changeObject) // The current note contains an toc. We'll check for changes on headlines. @@ -726,7 +765,11 @@ export default class CodeEditor extends React.Component { // Check if one of the changed lines contains a headline for (let line = 0; line < changeObject.text.length; line++) { - if (this.linePossibleContainsHeadline(editor.getLine(changeObject.from.line + line))) { + if ( + this.linePossibleContainsHeadline( + editor.getLine(changeObject.from.line + line) + ) + ) { requireTocUpdate = true break } @@ -755,13 +798,13 @@ export default class CodeEditor extends React.Component { } } - linePossibleContainsHeadline (currentLine) { + linePossibleContainsHeadline(currentLine) { // We can't check if the line start with # because when some write text before // the # we also need to update the toc return currentLine.includes('# ') } - incrementLines (start, linesAdded, linesRemoved, editor) { + incrementLines(start, linesAdded, linesRemoved, editor) { const highlightedLines = editor.options.linesHighlighted const totalHighlightedLines = highlightedLines.length @@ -782,7 +825,7 @@ export default class CodeEditor extends React.Component { highlightedLines.splice(highlightedLines.indexOf(lineNumber), 1) // Lines that need to be relocated - if (lineNumber >= (start + linesRemoved)) { + if (lineNumber >= start + linesRemoved) { newLines.push(lineNumber + offset) } } @@ -796,22 +839,30 @@ export default class CodeEditor extends React.Component { } } - handleHighlight (editor, changeObject) { + handleHighlight(editor, changeObject) { const lines = editor.options.linesHighlighted if (!lines.includes(changeObject)) { lines.push(changeObject) - editor.addLineClass(changeObject, 'text', 'CodeMirror-activeline-background') + editor.addLineClass( + changeObject, + 'text', + 'CodeMirror-activeline-background' + ) } else { lines.splice(lines.indexOf(changeObject), 1) - editor.removeLineClass(changeObject, 'text', 'CodeMirror-activeline-background') + editor.removeLineClass( + changeObject, + 'text', + 'CodeMirror-activeline-background' + ) } if (this.props.onChange) { this.props.onChange(editor) } } - updateHighlight (editor, changeObject) { + updateHighlight(editor, changeObject) { const linesAdded = changeObject.text.length - 1 const linesRemoved = changeObject.removed.length - 1 @@ -842,28 +893,28 @@ export default class CodeEditor extends React.Component { this.incrementLines(start, linesAdded, linesRemoved, editor) } - moveCursorTo (row, col) {} + moveCursorTo(row, col) {} - scrollToLine (event, num) { + scrollToLine(event, num) { const cursor = { line: num, ch: 1 } this.editor.setCursor(cursor) - const top = this.editor.charCoords({line: num, ch: 0}, 'local').top + const top = this.editor.charCoords({ line: num, ch: 0 }, 'local').top const middleHeight = this.editor.getScrollerElement().offsetHeight / 2 this.editor.scrollTo(null, top - middleHeight - 5) } - focus () { + focus() { this.editor.focus() } - blur () { + blur() { this.editor.blur() } - reload () { + reload() { // Change event shouldn't be fired when switch note this.editor.off('change', this.changeHandler) this.value = this.props.value @@ -874,7 +925,7 @@ export default class CodeEditor extends React.Component { this.editor.refresh() } - setValue (value) { + setValue(value) { const cursor = this.editor.getCursor() this.editor.setValue(value) this.editor.setCursor(cursor) @@ -885,18 +936,19 @@ export default class CodeEditor extends React.Component { * @param {Number} lineNumber * @param {String} content */ - setLineContent (lineNumber, content) { + setLineContent(lineNumber, content) { const prevContent = this.editor.getLine(lineNumber) const prevContentLength = prevContent ? prevContent.length : 0 - this.editor.replaceRange(content, { line: lineNumber, ch: 0 }, { line: lineNumber, ch: prevContentLength }) + this.editor.replaceRange( + content, + { line: lineNumber, ch: 0 }, + { line: lineNumber, ch: prevContentLength } + ) } - handleDropImage (dropEvent) { + handleDropImage(dropEvent) { dropEvent.preventDefault() - const { - storageKey, - noteKey - } = this.props + const { storageKey, noteKey } = this.props attachmentManagement.handleAttachmentDrop( this, storageKey, @@ -905,37 +957,44 @@ export default class CodeEditor extends React.Component { ) } - insertAttachmentMd (imageMd) { + insertAttachmentMd(imageMd) { this.editor.replaceSelection(imageMd) } - autoDetectLanguage (content) { + autoDetectLanguage(content) { const res = hljs.highlightAuto(content, Object.keys(languageMaps)) this.setMode(languageMaps[res.language]) } - handlePaste (editor, forceSmartPaste) { + handlePaste(editor, forceSmartPaste) { const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props - const isURL = str => /(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str) + const isURL = str => + /(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str) const isInLinkTag = editor => { const startCursor = editor.getCursor('start') - const prevChar = editor.getRange({ - line: startCursor.line, - ch: startCursor.ch - 2 - }, { - line: startCursor.line, - ch: startCursor.ch - }) + const prevChar = editor.getRange( + { + line: startCursor.line, + ch: startCursor.ch - 2 + }, + { + line: startCursor.line, + ch: startCursor.ch + } + ) const endCursor = editor.getCursor('end') - const nextChar = editor.getRange({ - line: endCursor.line, - ch: endCursor.ch - }, { - line: endCursor.line, - ch: endCursor.ch + 1 - }) + const nextChar = editor.getRange( + { + line: endCursor.line, + ch: endCursor.ch + }, + { + line: endCursor.line, + ch: endCursor.ch + 1 + } + ) return prevChar === '](' && nextChar === ')' } @@ -947,7 +1006,7 @@ export default class CodeEditor extends React.Component { return true } - let line = line = cursor.line - 1 + let line = (line = cursor.line - 1) while (line >= 0) { token = editor.getTokenAt({ ch: 3, @@ -979,7 +1038,11 @@ export default class CodeEditor extends React.Component { if (isInFencedCodeBlock(editor)) { this.handlePasteText(editor, pastedTxt) - } else if (fetchUrlTitle && isMarkdownTitleURL(pastedTxt) && !isInLinkTag(editor)) { + } else if ( + fetchUrlTitle && + isMarkdownTitleURL(pastedTxt) && + !isInLinkTag(editor) + ) { this.handlePasteUrl(editor, pastedTxt) } else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { this.handlePasteUrl(editor, pastedTxt) @@ -1015,13 +1078,13 @@ export default class CodeEditor extends React.Component { } } - handleScroll (e) { + handleScroll(e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handlePasteUrl (editor, pastedTxt) { + handlePasteUrl(editor, pastedTxt) { let taggedUrl = `<${pastedTxt}>` let urlToFetch = pastedTxt let titleMark = '' @@ -1071,16 +1134,16 @@ export default class CodeEditor extends React.Component { }) } - handlePasteHtml (editor, pastedHtml) { + handlePasteHtml(editor, pastedHtml) { const markdown = this.turndownService.turndown(pastedHtml) editor.replaceSelection(markdown) } - handlePasteText (editor, pastedTxt) { + handlePasteText(editor, pastedTxt) { editor.replaceSelection(pastedTxt) } - mapNormalResponse (response, pastedTxt) { + mapNormalResponse(response, pastedTxt) { return this.decodeResponse(response).then(body => { return new Promise((resolve, reject) => { try { @@ -1088,10 +1151,12 @@ export default class CodeEditor extends React.Component { body, 'text/html' ) - const escapePipe = (str) => { + const escapePipe = str => { return str.replace('|', '\\|') } - const linkWithTitle = `[${escapePipe(parsedBody.title)}](${pastedTxt})` + const linkWithTitle = `[${escapePipe( + parsedBody.title + )}](${pastedTxt})` resolve(linkWithTitle) } catch (e) { reject(e) @@ -1100,7 +1165,7 @@ export default class CodeEditor extends React.Component { }) } - initialHighlighting () { + initialHighlighting() { if (this.editor.options.linesHighlighted == null) { return } @@ -1114,16 +1179,20 @@ export default class CodeEditor extends React.Component { // make sure that we skip the invalid lines althrough this case should not be happened. continue } - this.editor.addLineClass(lineNumber, 'text', 'CodeMirror-activeline-background') + this.editor.addLineClass( + lineNumber, + 'text', + 'CodeMirror-activeline-background' + ) } } - restartHighlighting () { + restartHighlighting() { this.editor.options.linesHighlighted = this.props.linesHighlighted this.initialHighlighting() } - mapImageResponse (response, pastedTxt) { + mapImageResponse(response, pastedTxt) { return new Promise((resolve, reject) => { try { const url = response.url @@ -1136,7 +1205,7 @@ export default class CodeEditor extends React.Component { }) } - decodeResponse (response) { + decodeResponse(response) { const headers = response.headers const _charset = headers.has('content-type') ? this.extractContentTypeCharset(headers.get('content-type')) @@ -1144,10 +1213,10 @@ export default class CodeEditor extends React.Component { return response.arrayBuffer().then(buff => { return new Promise((resolve, reject) => { try { - const charset = _charset !== undefined && - iconv.encodingExists(_charset) - ? _charset - : 'utf-8' + const charset = + _charset !== undefined && iconv.encodingExists(_charset) + ? _charset + : 'utf-8' resolve(iconv.decode(Buffer.from(buff), charset).toString()) } catch (e) { reject(e) @@ -1156,50 +1225,49 @@ export default class CodeEditor extends React.Component { }) } - extractContentTypeCharset (contentType) { + extractContentTypeCharset(contentType) { return contentType .split(';') .filter(str => { - return str.trim().toLowerCase().startsWith('charset') + return str + .trim() + .toLowerCase() + .startsWith('charset') }) .map(str => { return str.replace(/['"]/g, '').split('=')[1] })[0] } - render () { - const { - className, - fontSize - } = this.props + render() { + const { className, fontSize } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width - return (< - div className={ - className == null ? 'CodeEditor' : `CodeEditor ${className}` - } - ref='root' - tabIndex='-1' - style={{ - fontFamily, - fontSize: fontSize, - width: width - }} - onDrop={ - e => this.handleDropImage(e) - } + return ( +
      this.handleDropImage(e)} /> ) } - createSpellCheckPanel () { + createSpellCheckPanel() { const panel = document.createElement('div') panel.className = 'panel bottom' panel.id = 'editor-bottom-panel' const dropdown = document.createElement('select') dropdown.title = 'Spellcheck' dropdown.className = styles['spellcheck-select'] - dropdown.addEventListener('change', (e) => spellcheck.setLanguage(this.editor, dropdown.value)) + dropdown.addEventListener('change', e => + spellcheck.setLanguage(this.editor, dropdown.value) + ) const options = spellcheck.getAvailableDictionaries() for (const op of options) { const option = document.createElement('option') diff --git a/browser/components/ColorPicker.js b/browser/components/ColorPicker.js index 9e0199c2..4d4e80e4 100644 --- a/browser/components/ColorPicker.js +++ b/browser/components/ColorPicker.js @@ -7,7 +7,7 @@ import styles from './ColorPicker.styl' const componentHeight = 330 class ColorPicker extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -18,21 +18,21 @@ class ColorPicker extends React.Component { this.handleConfirm = this.handleConfirm.bind(this) } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { this.onColorChange(nextProps.color) } - onColorChange (color) { + onColorChange(color) { this.setState({ color }) } - handleConfirm () { + handleConfirm() { this.props.onConfirm(this.state.color) } - render () { + render() { const { onReset, onCancel, targetRect } = this.props const { color } = this.state @@ -44,13 +44,22 @@ class ColorPicker extends React.Component { } return ( -
      +
      - - - + + +
      ) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index a2265321..a8b88891 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -10,7 +10,7 @@ import ConfigManager from 'browser/main/lib/ConfigManager' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' class MarkdownEditor extends React.Component { - constructor (props) { + constructor(props) { super(props) // char codes for ctrl + w @@ -20,7 +20,10 @@ class MarkdownEditor extends React.Component { this.supportMdSelectionBold = [16, 17, 186] this.state = { - status: props.config.editor.switchPreview === 'RIGHTCLICK' ? props.config.editor.delfaultStatus : 'CODE', + status: + props.config.editor.switchPreview === 'RIGHTCLICK' + ? props.config.editor.delfaultStatus + : 'CODE', renderValue: props.value, keyPressed: new Set(), isLocked: props.isLocked @@ -29,133 +32,153 @@ class MarkdownEditor extends React.Component { this.lockEditorCode = () => this.handleLockEditor() } - componentDidMount () { + componentDidMount() { this.value = this.refs.code.value eventEmitter.on('editor:lock', this.lockEditorCode) eventEmitter.on('editor:focus', this.focusEditor.bind(this)) } - componentDidUpdate () { + componentDidUpdate() { this.value = this.refs.code.value } - componentWillReceiveProps (props) { + componentWillReceiveProps(props) { if (props.value !== this.props.value) { this.queueRendering(props.value) } } - componentWillUnmount () { + componentWillUnmount() { this.cancelQueue() eventEmitter.off('editor:lock', this.lockEditorCode) eventEmitter.off('editor:focus', this.focusEditor.bind(this)) } - focusEditor () { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - }) + focusEditor() { + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + } + ) } - queueRendering (value) { + queueRendering(value) { clearTimeout(this.renderTimer) this.renderTimer = setTimeout(() => { this.renderPreview(value) }, 500) } - cancelQueue () { + cancelQueue() { clearTimeout(this.renderTimer) } - renderPreview (value) { + renderPreview(value) { this.setState({ renderValue: value }) } - setValue (value) { + setValue(value) { this.refs.code.setValue(value) } - handleChange (e) { + handleChange(e) { this.value = this.refs.code.value this.props.onChange(e) } - handleContextMenu (e) { + handleContextMenu(e) { if (this.state.isLocked) return const { config } = this.props if (config.editor.switchPreview === 'RIGHTCLICK') { const newStatus = this.state.status === 'PREVIEW' ? 'CODE' : 'PREVIEW' - this.setState({ - status: newStatus - }, () => { - if (newStatus === 'CODE') { - this.refs.code.focus() - } else { - this.refs.preview.focus() - } - eventEmitter.emit('topbar:togglelockbutton', this.state.status) + this.setState( + { + status: newStatus + }, + () => { + if (newStatus === 'CODE') { + this.refs.code.focus() + } else { + this.refs.preview.focus() + } + eventEmitter.emit('topbar:togglelockbutton', this.state.status) - const newConfig = Object.assign({}, config) - newConfig.editor.delfaultStatus = newStatus - ConfigManager.set(newConfig) - }) + const newConfig = Object.assign({}, config) + newConfig.editor.delfaultStatus = newStatus + ConfigManager.set(newConfig) + } + ) } } - handleBlur (e) { + handleBlur(e) { if (this.state.isLocked) return this.setState({ keyPressed: new Set() }) const { config } = this.props - if (config.editor.switchPreview === 'BLUR' || - (config.editor.switchPreview === 'DBL_CLICK' && this.state.status === 'CODE') + if ( + config.editor.switchPreview === 'BLUR' || + (config.editor.switchPreview === 'DBL_CLICK' && + this.state.status === 'CODE') ) { const cursorPosition = this.refs.code.editor.getCursor() - this.setState({ - status: 'PREVIEW' - }, () => { - this.refs.preview.focus() - this.refs.preview.scrollToRow(cursorPosition.line) - }) + this.setState( + { + status: 'PREVIEW' + }, + () => { + this.refs.preview.focus() + this.refs.preview.scrollToRow(cursorPosition.line) + } + ) eventEmitter.emit('topbar:togglelockbutton', this.state.status) } } - handleDoubleClick (e) { + handleDoubleClick(e) { if (this.state.isLocked) return - this.setState({keyPressed: new Set()}) + this.setState({ keyPressed: new Set() }) const { config } = this.props if (config.editor.switchPreview === 'DBL_CLICK') { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - eventEmitter.emit('topbar:togglelockbutton', this.state.status) - }) + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + eventEmitter.emit('topbar:togglelockbutton', this.state.status) + } + ) } } - handlePreviewMouseDown (e) { + handlePreviewMouseDown(e) { this.previewMouseDownedAt = new Date() } - handlePreviewMouseUp (e) { + handlePreviewMouseUp(e) { const { config } = this.props - if (config.editor.switchPreview === 'BLUR' && new Date() - this.previewMouseDownedAt < 200) { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - }) + if ( + config.editor.switchPreview === 'BLUR' && + new Date() - this.previewMouseDownedAt < 200 + ) { + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + } + ) eventEmitter.emit('topbar:togglelockbutton', this.state.status) } } - handleCheckboxClick (e) { + handleCheckboxClick(e) { e.preventDefault() e.stopPropagation() const idMatch = /checkbox-([0-9]+)/ @@ -164,9 +187,9 @@ class MarkdownEditor extends React.Component { const checkReplace = /\[x]/i const uncheckReplace = /\[ ]/ if (idMatch.test(e.target.getAttribute('id'))) { - const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1 - const lines = this.refs.code.value - .split('\n') + const lineIndex = + parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1 + const lines = this.refs.code.value.split('\n') const targetLine = lines[lineIndex] let newLine = targetLine @@ -181,45 +204,56 @@ class MarkdownEditor extends React.Component { } } - focus () { + focus() { if (this.state.status === 'PREVIEW') { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - }) + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + } + ) } else { this.refs.code.focus() } eventEmitter.emit('topbar:togglelockbutton', this.state.status) } - reload () { + reload() { this.refs.code.reload() this.cancelQueue() this.renderPreview(this.props.value) } - handleKeyDown (e) { + handleKeyDown(e) { const { config } = this.props if (this.state.status !== 'CODE') return false const keyPressed = this.state.keyPressed keyPressed.add(e.keyCode) this.setState({ keyPressed }) - const isNoteHandlerKey = (el) => { return keyPressed.has(el) } + const isNoteHandlerKey = el => { + return keyPressed.has(el) + } // These conditions are for ctrl-e and ctrl-w - if (keyPressed.size === this.escapeFromEditor.length && - !this.state.isLocked && this.state.status === 'CODE' && - this.escapeFromEditor.every(isNoteHandlerKey)) { + if ( + keyPressed.size === this.escapeFromEditor.length && + !this.state.isLocked && + this.state.status === 'CODE' && + this.escapeFromEditor.every(isNoteHandlerKey) + ) { this.handleContextMenu() if (config.editor.switchPreview === 'BLUR') document.activeElement.blur() } - if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) { + if ( + keyPressed.size === this.supportMdSelectionBold.length && + this.supportMdSelectionBold.every(isNoteHandlerKey) + ) { this.addMdAroundWord('**') } } - addMdAroundWord (mdElement) { + addMdAroundWord(mdElement) { if (this.refs.code.editor.getSelection()) { return this.addMdAroundSelection(mdElement) } @@ -227,47 +261,63 @@ class MarkdownEditor extends React.Component { const word = this.refs.code.editor.findWordAt(currentCaret) const cmDoc = this.refs.code.editor.getDoc() cmDoc.replaceRange(mdElement, word.anchor) - cmDoc.replaceRange(mdElement, { line: word.head.line, ch: word.head.ch + mdElement.length }) - } - - addMdAroundSelection (mdElement) { - this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`) - } - - handleDropImage (dropEvent) { - dropEvent.preventDefault() - const { storageKey, noteKey } = this.props - - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - - this.refs.code.editor.execCommand('goDocEnd') - this.refs.code.editor.execCommand('goLineEnd') - this.refs.code.editor.execCommand('newlineAndIndent') - - attachmentManagement.handleAttachmentDrop( - this.refs.code, - storageKey, - noteKey, - dropEvent - ) + cmDoc.replaceRange(mdElement, { + line: word.head.line, + ch: word.head.ch + mdElement.length }) } - handleKeyUp (e) { + addMdAroundSelection(mdElement) { + this.refs.code.editor.replaceSelection( + `${mdElement}${this.refs.code.editor.getSelection()}${mdElement}` + ) + } + + handleDropImage(dropEvent) { + dropEvent.preventDefault() + const { storageKey, noteKey } = this.props + + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + + this.refs.code.editor.execCommand('goDocEnd') + this.refs.code.editor.execCommand('goLineEnd') + this.refs.code.editor.execCommand('newlineAndIndent') + + attachmentManagement.handleAttachmentDrop( + this.refs.code, + storageKey, + noteKey, + dropEvent + ) + } + ) + } + + handleKeyUp(e) { const keyPressed = this.state.keyPressed keyPressed.delete(e.keyCode) this.setState({ keyPressed }) } - handleLockEditor () { + handleLockEditor() { this.setState({ isLocked: !this.state.isLocked }) } - render () { - const {className, value, config, storageKey, noteKey, linesHighlighted, RTL} = this.props + render() { + const { + className, + value, + config, + storageKey, + noteKey, + linesHighlighted, + RTL + } = this.props let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -275,23 +325,24 @@ class MarkdownEditor extends React.Component { if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4 const previewStyle = {} - if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none' + if (this.props.ignorePreviewPointerEvents) + previewStyle.pointerEvents = 'none' const storage = findStorage(storageKey) return ( -
      this.handleContextMenu(e)} + onContextMenu={e => this.handleContextMenu(e)} tabIndex='-1' - onKeyDown={(e) => this.handleKeyDown(e)} - onKeyUp={(e) => this.handleKeyUp(e)} + onKeyDown={e => this.handleKeyDown(e)} + onKeyUp={e => this.handleKeyUp(e)} > - this.handleChange(e)} - onBlur={(e) => this.handleBlur(e)} + onChange={e => this.handleChange(e)} + onBlur={e => this.handleBlur(e)} spellCheck={config.editor.spellcheck} enableSmartPaste={config.editor.enableSmartPaste} hotkey={config.hotkey} @@ -327,9 +378,9 @@ class MarkdownEditor extends React.Component { deleteUnusedAttachments={config.editor.deleteUnusedAttachments} RTL={RTL} /> - this.handleContextMenu(e)} - onDoubleClick={(e) => this.handleDoubleClick(e)} + onContextMenu={e => this.handleContextMenu(e)} + onDoubleClick={e => this.handleDoubleClick(e)} tabIndex='0' value={this.state.renderValue} - onMouseUp={(e) => this.handlePreviewMouseUp(e)} - onMouseDown={(e) => this.handlePreviewMouseDown(e)} - onCheckboxClick={(e) => this.handleCheckboxClick(e)} + onMouseUp={e => this.handlePreviewMouseUp(e)} + onMouseDown={e => this.handlePreviewMouseDown(e)} + onCheckboxClick={e => this.handleCheckboxClick(e)} showCopyNotification={config.ui.showCopyNotification} storagePath={storage.path} noteKey={noteKey} customCSS={config.preview.customCSS} allowCustomCSS={config.preview.allowCustomCSS} lineThroughCheckbox={config.preview.lineThroughCheckbox} - onDrop={(e) => this.handleDropImage(e)} + onDrop={e => this.handleDropImage(e)} RTL={RTL} />
      diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index fc5aa95c..75635c89 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -26,7 +26,8 @@ import i18n from 'browser/lib/i18n' const { remote, shell } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') -const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder').buildMarkdownPreviewContextMenu +const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder') + .buildMarkdownPreviewContextMenu const { app } = remote const path = require('path') @@ -56,7 +57,7 @@ const CSS_FILES = [ * @param {String} [opts.customCSS] Will be added to bottom, only if `opts.allowCustomCSS` is truthy * @returns {String} */ -function buildStyle (opts) { +function buildStyle(opts) { const { fontFamily, fontSize, @@ -104,11 +105,14 @@ body { font-family: '${fontFamily.join("','")}'; font-size: ${fontSize}px; - ${scrollPastEnd ? ` + ${ + scrollPastEnd + ? ` padding-bottom: 90vh; box-sizing: border-box; ` - : ''} + : '' + } ${RTL ? 'direction: rtl;' : ''} ${RTL ? 'text-align: right;' : ''} } @@ -225,7 +229,7 @@ const defaultCodeBlockFontFamily = [ // return the line number of the line that used to generate the specified element // return -1 if the line is not found -function getSourceLineNumberByElement (element) { +function getSourceLineNumberByElement(element) { let isHasLineNumber = element.dataset.line !== undefined let parent = element while (!isHasLineNumber && parent.parentElement !== null) { @@ -236,7 +240,7 @@ function getSourceLineNumberByElement (element) { } export default class MarkdownPreview extends React.Component { - constructor (props) { + constructor(props) { super(props) this.contextMenuHandler = e => this.handleContextMenu(e) @@ -260,7 +264,7 @@ export default class MarkdownPreview extends React.Component { this.initMarkdown() } - initMarkdown () { + initMarkdown() { const { smartQuotes, sanitize, breaks } = this.props this.markdown = new Markdown({ typographer: smartQuotes, @@ -269,17 +273,17 @@ export default class MarkdownPreview extends React.Component { }) } - handleCheckboxClick (e) { + handleCheckboxClick(e) { this.props.onCheckboxClick(e) } - handleScroll (e) { + handleScroll(e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handleContextMenu (event) { + handleContextMenu(event) { const menu = buildMarkdownPreviewContextMenu(this, event) const switchPreview = ConfigManager.get().editor.switchPreview if (menu != null && switchPreview !== 'RIGHTCLICK') { @@ -289,17 +293,21 @@ export default class MarkdownPreview extends React.Component { } } - handleDoubleClick (e) { + handleDoubleClick(e) { if (this.props.onDoubleClick != null) this.props.onDoubleClick(e) } - handleMouseDown (e) { + handleMouseDown(e) { const config = ConfigManager.get() const clickElement = e.target const targetTag = clickElement.tagName // The direct parent HTML of where was clicked ie "BODY" or "DIV" const lineNumber = getSourceLineNumberByElement(clickElement) // Line location of element clicked. - if (config.editor.switchPreview === 'RIGHTCLICK' && e.buttons === 2 && config.editor.type === 'SPLIT') { + if ( + config.editor.switchPreview === 'RIGHTCLICK' && + e.buttons === 2 && + config.editor.type === 'SPLIT' + ) { eventEmitter.emit('topbar:togglemodebutton', 'CODE') } if (e.ctrlKey) { @@ -315,10 +323,11 @@ export default class MarkdownPreview extends React.Component { } } - if (this.props.onMouseDown != null && targetTag === 'BODY') this.props.onMouseDown(e) + if (this.props.onMouseDown != null && targetTag === 'BODY') + this.props.onMouseDown(e) } - handleMouseUp (e) { + handleMouseUp(e) { if (!this.props.onMouseUp) return if (e.target != null && e.target.tagName === 'A') { return null @@ -326,15 +335,15 @@ export default class MarkdownPreview extends React.Component { if (this.props.onMouseUp != null) this.props.onMouseUp(e) } - handleSaveAsText () { + handleSaveAsText() { this.exportAsDocument('txt') } - handleSaveAsMd () { + handleSaveAsMd() { this.exportAsDocument('md') } - htmlContentFormatter (noteContent, exportTasks, targetDir) { + htmlContentFormatter(noteContent, exportTasks, targetDir) { const { fontFamily, fontSize, @@ -360,10 +369,7 @@ export default class MarkdownPreview extends React.Component { RTL }) let body = this.refs.root.contentWindow.document.body.innerHTML - body = attachmentManagement.fixLocalURLS( - body, - this.props.storagePath - ) + body = attachmentManagement.fixLocalURLS(body, this.props.storagePath) const files = [this.getCodeThemeLink(codeBlockTheme), ...CSS_FILES] files.forEach(file => { if (global.process.platform === 'win32') { @@ -394,14 +400,24 @@ export default class MarkdownPreview extends React.Component { ` } - handleSaveAsHtml () { - this.exportAsDocument('html', (noteContent, exportTasks, targetDir) => Promise.resolve(this.htmlContentFormatter(noteContent, exportTasks, targetDir))) + handleSaveAsHtml() { + this.exportAsDocument('html', (noteContent, exportTasks, targetDir) => + Promise.resolve( + this.htmlContentFormatter(noteContent, exportTasks, targetDir) + ) + ) } - handleSaveAsPdf () { + handleSaveAsPdf() { this.exportAsDocument('pdf', (noteContent, exportTasks, targetDir) => { - const printout = new remote.BrowserWindow({show: false, webPreferences: {webSecurity: false, javascript: false}}) - printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks, targetDir)) + const printout = new remote.BrowserWindow({ + show: false, + webPreferences: { webSecurity: false, javascript: false } + }) + printout.loadURL( + 'data:text/html;charset=UTF-8,' + + this.htmlContentFormatter(noteContent, exportTasks, targetDir) + ) return new Promise((resolve, reject) => { printout.webContents.on('did-finish-load', () => { printout.webContents.printToPDF({}, (err, data) => { @@ -414,11 +430,11 @@ export default class MarkdownPreview extends React.Component { }) } - handlePrint () { + handlePrint() { this.refs.root.contentWindow.print() } - exportAsDocument (fileType, contentFormatter) { + exportAsDocument(fileType, contentFormatter) { const options = { filters: [{ name: 'Documents', extensions: [fileType] }], properties: ['openFile', 'createDirectory'] @@ -449,7 +465,7 @@ export default class MarkdownPreview extends React.Component { }) } - fixDecodedURI (node) { + fixDecodedURI(node) { if ( node && node.children.length === 1 && @@ -466,17 +482,18 @@ export default class MarkdownPreview extends React.Component { * @param {string[]} splitWithCodeTag Array of HTML strings separated by three ``` * @returns {string} HTML in which special characters between three ``` have been converted */ - escapeHtmlCharactersInCodeTag (splitWithCodeTag) { + escapeHtmlCharactersInCodeTag(splitWithCodeTag) { for (let index = 0; index < splitWithCodeTag.length; index++) { - const codeTagRequired = (splitWithCodeTag[index] !== '\`\`\`' && index < splitWithCodeTag.length - 1) + const codeTagRequired = + splitWithCodeTag[index] !== '```' && index < splitWithCodeTag.length - 1 if (codeTagRequired) { - splitWithCodeTag.splice((index + 1), 0, '\`\`\`') + splitWithCodeTag.splice(index + 1, 0, '```') } } let inCodeTag = false let result = '' for (let content of splitWithCodeTag) { - if (content === '\`\`\`') { + if (content === '```') { inCodeTag = !inCodeTag } else if (inCodeTag) { content = escapeHtmlCharacters(content) @@ -486,13 +503,15 @@ export default class MarkdownPreview extends React.Component { return result } - getScrollBarStyle () { + getScrollBarStyle() { const { theme } = this.props - return uiThemes.some(item => item.name === theme && item.isDark) ? scrollBarDarkStyle : scrollBarStyle + return uiThemes.some(item => item.name === theme && item.isDark) + ? scrollBarDarkStyle + : scrollBarStyle } - componentDidMount () { + componentDidMount() { const { onDrop } = this.props this.refs.root.setAttribute('sandbox', 'allow-scripts') @@ -542,10 +561,7 @@ export default class MarkdownPreview extends React.Component { 'scroll', this.scrollHandler ) - this.refs.root.contentWindow.addEventListener( - 'resize', - this.resizeHandler - ) + this.refs.root.contentWindow.addEventListener('resize', this.resizeHandler) eventEmitter.on('export:save-text', this.saveAsTextHandler) eventEmitter.on('export:save-md', this.saveAsMdHandler) eventEmitter.on('export:save-html', this.saveAsHtmlHandler) @@ -553,7 +569,7 @@ export default class MarkdownPreview extends React.Component { eventEmitter.on('print', this.printHandler) } - componentWillUnmount () { + componentWillUnmount() { const { onDrop } = this.props this.refs.root.contentWindow.document.body.removeEventListener( @@ -595,7 +611,7 @@ export default class MarkdownPreview extends React.Component { eventEmitter.off('print', this.printHandler) } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { // actual rewriteIframe function should be called only once let needsRewriteIframe = false if (prevProps.value !== this.props.value) needsRewriteIframe = true @@ -637,7 +653,7 @@ export default class MarkdownPreview extends React.Component { } } - getStyleParams () { + getStyleParams() { const { fontSize, lineNumber, @@ -649,19 +665,20 @@ export default class MarkdownPreview extends React.Component { RTL } = this.props let { fontFamily, codeBlockFontFamily } = this.props - fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0 - ? fontFamily - .split(',') - .map(fontName => fontName.trim()) - .concat(defaultFontFamily) - : defaultFontFamily - codeBlockFontFamily = _.isString(codeBlockFontFamily) && - codeBlockFontFamily.trim().length > 0 - ? codeBlockFontFamily - .split(',') - .map(fontName => fontName.trim()) - .concat(defaultCodeBlockFontFamily) - : defaultCodeBlockFontFamily + fontFamily = + _.isString(fontFamily) && fontFamily.trim().length > 0 + ? fontFamily + .split(',') + .map(fontName => fontName.trim()) + .concat(defaultFontFamily) + : defaultFontFamily + codeBlockFontFamily = + _.isString(codeBlockFontFamily) && codeBlockFontFamily.trim().length > 0 + ? codeBlockFontFamily + .split(',') + .map(fontName => fontName.trim()) + .concat(defaultCodeBlockFontFamily) + : defaultCodeBlockFontFamily return { fontFamily, @@ -677,7 +694,7 @@ export default class MarkdownPreview extends React.Component { } } - applyStyle () { + applyStyle() { const { fontFamily, fontSize, @@ -707,7 +724,7 @@ export default class MarkdownPreview extends React.Component { }) } - getCodeThemeLink (name) { + getCodeThemeLink(name) { const theme = consts.THEMES.find(theme => theme.name === name) return theme != null @@ -715,7 +732,7 @@ export default class MarkdownPreview extends React.Component { : `${appPath}/node_modules/codemirror/theme/elegant.css` } - rewriteIframe () { + rewriteIframe() { _.forEach( this.refs.root.contentWindow.document.querySelectorAll( 'input[type="checkbox"]' @@ -773,7 +790,9 @@ export default class MarkdownPreview extends React.Component { codeBlockTheme = consts.THEMES.find(theme => theme.name === codeBlockTheme) - const codeBlockThemeClassName = codeBlockTheme ? codeBlockTheme.className : 'cm-s-default' + const codeBlockThemeClassName = codeBlockTheme + ? codeBlockTheme.className + : 'cm-s-default' _.forEach( this.refs.root.contentWindow.document.querySelectorAll('.code code'), @@ -859,7 +878,10 @@ export default class MarkdownPreview extends React.Component { el => { try { const format = el.attributes.getNamedItem('data-format').value - const chartConfig = format === 'yaml' ? yaml.load(el.innerHTML) : JSON.parse(el.innerHTML) + const chartConfig = + format === 'yaml' + ? yaml.load(el.innerHTML) + : JSON.parse(el.innerHTML) el.innerHTML = '' const canvas = document.createElement('canvas') @@ -882,7 +904,12 @@ export default class MarkdownPreview extends React.Component { _.forEach( this.refs.root.contentWindow.document.querySelectorAll('.mermaid'), el => { - mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme, mermaidHTMLLabel) + mermaidRender( + el, + htmlTextHelper.decodeEntities(el.innerHTML), + theme, + mermaidHTMLLabel + ) } ) @@ -904,20 +931,14 @@ export default class MarkdownPreview extends React.Component { autoplay = 0 } - render( - , - el - ) + render(, el) } ) const markdownPreviewIframe = document.querySelector('.MarkdownPreview') const rect = markdownPreviewIframe.getBoundingClientRect() const config = { attributes: true, subtree: true } - const imgObserver = new MutationObserver((mutationList) => { + const imgObserver = new MutationObserver(mutationList => { for (const mu of mutationList) { if (mu.target.className === 'carouselContent-enter-done') { this.setImgOnClickEventHelper(mu.target, rect) @@ -926,26 +947,32 @@ export default class MarkdownPreview extends React.Component { } }) - const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll('img') + const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll( + 'img' + ) for (const img of imgList) { const parentEl = img.parentElement this.setImgOnClickEventHelper(img, rect) imgObserver.observe(parentEl, config) } - const aList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll('a') + const aList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll( + 'a' + ) for (const a of aList) { a.removeEventListener('click', this.linkClickHandler) a.addEventListener('click', this.linkClickHandler) } } - setImgOnClickEventHelper (img, rect) { + setImgOnClickEventHelper(img, rect) { img.onclick = () => { const widthMagnification = document.body.clientWidth / img.width const heightMagnification = document.body.clientHeight / img.height const baseOnWidth = widthMagnification < heightMagnification - const magnification = baseOnWidth ? widthMagnification : heightMagnification + const magnification = baseOnWidth + ? widthMagnification + : heightMagnification const zoomImgWidth = img.width * magnification const zoomImgHeight = img.height * magnification @@ -976,10 +1003,7 @@ export default class MarkdownPreview extends React.Component { width: ${zoomImgWidth}; height: ${zoomImgHeight}px; ` - zoomImg.animate([ - originalImgRect, - zoomInImgRect - ], animationSpeed) + zoomImg.animate([originalImgRect, zoomInImgRect], animationSpeed) const overlay = document.createElement('div') overlay.style = ` @@ -1000,10 +1024,10 @@ export default class MarkdownPreview extends React.Component { width: ${img.width}px; height: ${img.height}px; ` - const zoomOutImgAnimation = zoomImg.animate([ - zoomInImgRect, - originalImgRect - ], animationSpeed) + const zoomOutImgAnimation = zoomImg.animate( + [zoomInImgRect, originalImgRect], + animationSpeed + ) zoomOutImgAnimation.onfinish = () => overlay.remove() } @@ -1012,7 +1036,7 @@ export default class MarkdownPreview extends React.Component { } } - handleResize () { + handleResize() { _.forEach( this.refs.root.contentWindow.document.querySelectorAll('svg[ratio]'), el => { @@ -1021,11 +1045,11 @@ export default class MarkdownPreview extends React.Component { ) } - focus () { + focus() { this.refs.root.focus() } - getWindow () { + getWindow() { return this.refs.root.contentWindow } @@ -1033,7 +1057,7 @@ export default class MarkdownPreview extends React.Component { * @public * @param {Number} targetRow */ - scrollToRow (targetRow) { + scrollToRow(targetRow) { const blocks = this.getWindow().document.querySelectorAll( 'body>[data-line]' ) @@ -1054,16 +1078,16 @@ export default class MarkdownPreview extends React.Component { * @param {Number} x * @param {Number} y */ - scrollTo (x, y) { + scrollTo(x, y) { this.getWindow().document.body.scrollTo(x, y) } - preventImageDroppedHandler (e) { + preventImageDroppedHandler(e) { e.preventDefault() e.stopPropagation() } - notify (title, options) { + notify(title, options) { if (global.process.platform === 'win32') { options.icon = path.join( 'file://', @@ -1074,7 +1098,7 @@ export default class MarkdownPreview extends React.Component { return new window.Notification(title, options) } - handleLinkClick (e) { + handleLinkClick(e) { e.preventDefault() e.stopPropagation() @@ -1095,9 +1119,7 @@ export default class MarkdownPreview extends React.Component { if (posOfHash > -1) { const extractedId = linkHash.slice(posOfHash + 1) const targetId = mdurl.encode(extractedId) - const targetElement = this.getWindow().document.getElementById( - targetId - ) + const targetElement = this.getWindow().document.getElementById(targetId) if (targetElement != null) { this.scrollTo(0, targetElement.offsetTop) @@ -1138,9 +1160,10 @@ export default class MarkdownPreview extends React.Component { this.openExternal(href) } - openExternal (href) { + openExternal(href) { try { - const success = shell.openExternal(href) || shell.openExternal(decodeURI(href)) + const success = + shell.openExternal(href) || shell.openExternal(decodeURI(href)) if (!success) console.error('failed to open url ' + href) } catch (e) { // URI Error threw from decodeURI @@ -1148,7 +1171,7 @@ export default class MarkdownPreview extends React.Component { } } - render () { + render() { const { className, style, tabIndex } = this.props return ( \n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)' + content: + '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)' }) .then(note => { store.dispatch({ @@ -141,7 +145,7 @@ class Main extends React.Component { }) } - componentDidMount () { + componentDidMount() { const { dispatch, config } = this.props if (uiThemes.some(theme => theme.name === config.ui.theme)) { @@ -173,38 +177,44 @@ class Main extends React.Component { delete CodeMirror.keyMap.emacs['Ctrl-V'] eventEmitter.on('editor:fullscreen', this.toggleFullScreen) - eventEmitter.on('menubar:togglemenubar', this.toggleMenuBarVisible.bind(this)) + eventEmitter.on( + 'menubar:togglemenubar', + this.toggleMenuBarVisible.bind(this) + ) } - componentWillUnmount () { + componentWillUnmount() { eventEmitter.off('editor:fullscreen', this.toggleFullScreen) - eventEmitter.off('menubar:togglemenubar', this.toggleMenuBarVisible.bind(this)) + eventEmitter.off( + 'menubar:togglemenubar', + this.toggleMenuBarVisible.bind(this) + ) } - toggleMenuBarVisible () { + toggleMenuBarVisible() { const { config } = this.props const { ui } = config - const newUI = Object.assign(ui, {showMenuBar: !ui.showMenuBar}) + const newUI = Object.assign(ui, { showMenuBar: !ui.showMenuBar }) const newConfig = Object.assign(config, newUI) ConfigManager.set(newConfig) } - handleLeftSlideMouseDown (e) { + handleLeftSlideMouseDown(e) { e.preventDefault() this.setState({ isLeftSliderFocused: true }) } - handleRightSlideMouseDown (e) { + handleRightSlideMouseDown(e) { e.preventDefault() this.setState({ isRightSliderFocused: true }) } - handleMouseUp (e) { + handleMouseUp(e) { // Change width of NoteList component. if (this.state.isRightSliderFocused) { this.setState( @@ -244,7 +254,7 @@ class Main extends React.Component { } } - handleMouseMove (e) { + handleMouseMove(e) { if (this.state.isRightSliderFocused) { const offset = this.refs.body.getBoundingClientRect().left let newListWidth = e.pageX - offset @@ -270,7 +280,7 @@ class Main extends React.Component { } } - handleFullScreenButton (e) { + handleFullScreenButton(e) { this.setState({ fullScreen: !this.state.fullScreen }, () => { const noteDetail = document.querySelector('.NoteDetail') const noteList = document.querySelector('.NoteList') @@ -284,7 +294,7 @@ class Main extends React.Component { }) } - hideLeftLists (noteDetail, noteList, mainBody) { + hideLeftLists(noteDetail, noteList, mainBody) { this.setState({ noteDetailWidth: noteDetail.style.left }) this.setState({ mainBodyWidth: mainBody.style.left }) noteDetail.style.left = '0px' @@ -292,13 +302,13 @@ class Main extends React.Component { noteList.style.display = 'none' } - showLeftLists (noteDetail, noteList, mainBody) { + showLeftLists(noteDetail, noteList, mainBody) { noteDetail.style.left = this.state.noteDetailWidth mainBody.style.left = this.state.mainBodyWidth noteList.style.display = 'inline' } - render () { + render() { const { config } = this.props // the width of the navigation bar when it is folded/collapsed @@ -312,10 +322,16 @@ class Main extends React.Component { onMouseUp={e => this.handleMouseUp(e)} > - {!config.isSideNavFolded && + {!config.isSideNavFolded && (
      -
      } +
      + )}
      - -
      -
      this.handleNoteListKeyDown(e)} + onKeyDown={e => this.handleNoteListKeyDown(e)} onKeyUp={this.handleNoteListKeyUp} onBlur={this.handleNoteListBlur} > diff --git a/browser/main/SideNav/ListButton.js b/browser/main/SideNav/ListButton.js index b5bc1488..dc9ba1cc 100644 --- a/browser/main/SideNav/ListButton.js +++ b/browser/main/SideNav/ListButton.js @@ -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 -}) => ( - diff --git a/browser/main/SideNav/PreferenceButton.js b/browser/main/SideNav/PreferenceButton.js index 187bc41a..25499463 100644 --- a/browser/main/SideNav/PreferenceButton.js +++ b/browser/main/SideNav/PreferenceButton.js @@ -4,10 +4,8 @@ import CSSModules from 'browser/lib/CSSModules' import styles from './PreferenceButton.styl' import i18n from 'browser/lib/i18n' -const PreferenceButton = ({ - onClick -}) => ( - diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js index 5cd4a491..a20b0df1 100644 --- a/browser/main/SideNav/StorageItem.js +++ b/browser/main/SideNav/StorageItem.js @@ -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 ( -
      -
      this.handleHeaderContextMenu(e)} +
      +
      this.handleHeaderContextMenu(e)} > - - {!isFolded && - - } + )} -
      - {this.state.isOpen && -
      - {folderList} -
      - } + {this.state.isOpen &&
      {folderList}
      }
      ) } diff --git a/browser/main/SideNav/TagButton.js b/browser/main/SideNav/TagButton.js index d91ae2c4..268e0d7e 100644 --- a/browser/main/SideNav/TagButton.js +++ b/browser/main/SideNav/TagButton.js @@ -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 -}) => ( - diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 241d4151..dcda9a27 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -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 = (
      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 + )} /> - +
      ) } else { @@ -257,22 +293,26 @@ class SideNav extends React.Component {
      -
      -
      - {this.tagListComponent(data)} -
      - +
      {this.tagListComponent(data)}
      +
      ) } @@ -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 ( + ) - } - return ( - tagList.map(tag => { - return ( - - ) - }) - ) + }) } - 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 + return ( + + ) }) 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 ( -
      - - + +
      diff --git a/browser/main/StatusBar/index.js b/browser/main/StatusBar/index.js index c99bf036..6b53f2d2 100644 --- a/browser/main/StatusBar/index.js +++ b/browser/main/StatusBar/index.js @@ -11,30 +11,43 @@ const electron = require('electron') const { remote, ipcRenderer } = electron const { dialog } = remote -const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0] +const zoomOptions = [ + 0.8, + 0.9, + 1, + 1.1, + 1.2, + 1.3, + 1.4, + 1.5, + 1.6, + 1.7, + 1.8, + 1.9, + 2.0 +] class StatusBar extends React.Component { - - constructor (props) { + constructor(props) { super(props) this.handleZoomInMenuItem = this.handleZoomInMenuItem.bind(this) this.handleZoomOutMenuItem = this.handleZoomOutMenuItem.bind(this) this.handleZoomResetMenuItem = this.handleZoomResetMenuItem.bind(this) } - componentDidMount () { + componentDidMount() { EventEmitter.on('status:zoomin', this.handleZoomInMenuItem) EventEmitter.on('status:zoomout', this.handleZoomOutMenuItem) EventEmitter.on('status:zoomreset', this.handleZoomResetMenuItem) } - componentWillUnmount () { + componentWillUnmount() { EventEmitter.off('status:zoomin', this.handleZoomInMenuItem) EventEmitter.off('status:zoomout', this.handleZoomOutMenuItem) EventEmitter.off('status:zoomreset', this.handleZoomResetMenuItem) } - updateApp () { + updateApp() { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Update Boostnote'), @@ -47,10 +60,10 @@ class StatusBar extends React.Component { } } - handleZoomButtonClick (e) { + handleZoomButtonClick(e) { const templates = [] - zoomOptions.forEach((zoom) => { + zoomOptions.forEach(zoom => { templates.push({ label: Math.floor(zoom * 100) + '%', click: () => this.handleZoomMenuItemClick(zoom) @@ -60,7 +73,7 @@ class StatusBar extends React.Component { context.popup(templates) } - handleZoomMenuItemClick (zoomFactor) { + handleZoomMenuItemClick(zoomFactor) { const { dispatch } = this.props ZoomManager.setZoom(zoomFactor) dispatch({ @@ -69,40 +82,36 @@ class StatusBar extends React.Component { }) } - handleZoomInMenuItem () { + handleZoomInMenuItem() { const zoomFactor = ZoomManager.getZoom() + 0.1 this.handleZoomMenuItemClick(zoomFactor) } - handleZoomOutMenuItem () { + handleZoomOutMenuItem() { const zoomFactor = ZoomManager.getZoom() - 0.1 this.handleZoomMenuItemClick(zoomFactor) } - handleZoomResetMenuItem () { + handleZoomResetMenuItem() { this.handleZoomMenuItemClick(1.0) } - render () { + render() { const { config, status } = this.context return ( -
      - - {status.updateReady - ? - : null - } + ) : null}
      ) } diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index 09fd56b2..e9554a67 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -11,7 +11,7 @@ import CInput from 'react-composition-input' import { push } from 'connected-react-router' class TopBar extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -33,19 +33,25 @@ class TopBar extends React.Component { this.handleSearchChange = this.handleSearchChange.bind(this) this.handleSearchClearButton = this.handleSearchClearButton.bind(this) - this.debouncedUpdateKeyword = debounce((keyword) => { - dispatch(push(`/searched/${encodeURIComponent(keyword)}`)) - this.setState({ - search: keyword - }) - ee.emit('top:search', keyword) - }, 1000 / 60, { - maxWait: 1000 / 8 - }) + this.debouncedUpdateKeyword = debounce( + keyword => { + dispatch(push(`/searched/${encodeURIComponent(keyword)}`)) + this.setState({ + search: keyword + }) + ee.emit('top:search', keyword) + }, + 1000 / 60, + { + maxWait: 1000 / 8 + } + ) } - componentDidMount () { - const { match: { params } } = this.props + componentDidMount() { + const { + match: { params } + } = this.props const searchWord = params && params.searchword if (searchWord !== undefined) { this.setState({ @@ -57,12 +63,12 @@ class TopBar extends React.Component { ee.on('code:init', this.codeInitHandler) } - componentWillUnmount () { + componentWillUnmount() { ee.off('top:focus-search', this.focusSearchHandler) ee.off('code:init', this.codeInitHandler) } - handleSearchClearButton (e) { + handleSearchClearButton(e) { const { dispatch } = this.props this.setState({ search: '', @@ -74,7 +80,7 @@ class TopBar extends React.Component { this.debouncedUpdateKeyword('') } - handleKeyDown (e) { + handleKeyDown(e) { // Re-apply search field on ENTER key if (e.keyCode === 13) { this.debouncedUpdateKeyword(e.target.value) @@ -98,18 +104,18 @@ class TopBar extends React.Component { } } - handleSearchChange (e) { + handleSearchChange(e) { const keyword = e.target.value this.debouncedUpdateKeyword(keyword) } - handleSearchFocus (e) { + handleSearchFocus(e) { this.setState({ isSearching: true }) } - handleSearchBlur (e) { + handleSearchBlur(e) { e.stopPropagation() let el = e.relatedTarget @@ -128,7 +134,7 @@ class TopBar extends React.Component { } } - handleOnSearchFocus () { + handleOnSearchFocus() { const el = this.refs.search.childNodes[0] if (this.state.isSearching) { el.blur() @@ -137,20 +143,22 @@ class TopBar extends React.Component { } } - handleCodeInit () { + handleCodeInit() { ee.emit('top:search', this.refs.searchInput.value || '') } - render () { + render() { const { config, style, location } = this.props return ( -
      -
      - {this.state.search !== '' && - - } + )}
      - {location.pathname === '/trashed' ? '' - : } + {location.pathname === '/trashed' ? ( + '' + ) : ( + + )}
      ) } diff --git a/browser/main/index.js b/browser/main/index.js index b3a909e5..727c4d0d 100644 --- a/browser/main/index.js +++ b/browser/main/index.js @@ -17,11 +17,11 @@ const electron = require('electron') const { remote, ipcRenderer } = electron const { dialog } = remote -document.addEventListener('drop', function (e) { +document.addEventListener('drop', function(e) { e.preventDefault() e.stopPropagation() }) -document.addEventListener('dragover', function (e) { +document.addEventListener('dragover', function(e) { e.preventDefault() e.stopPropagation() }) @@ -33,7 +33,7 @@ let isAltWithMouse = false let isAltWithOtherKey = false let isOtherKey = false -document.addEventListener('keydown', function (e) { +document.addEventListener('keydown', function(e) { if (e.key === 'Alt') { isAltPressing = true if (isOtherKey) { @@ -47,13 +47,13 @@ document.addEventListener('keydown', function (e) { } }) -document.addEventListener('mousedown', function (e) { +document.addEventListener('mousedown', function(e) { if (isAltPressing) { isAltWithMouse = true } }) -document.addEventListener('keyup', function (e) { +document.addEventListener('keyup', function(e) { if (e.key === 'Alt') { if (isAltWithMouse || isAltWithOtherKey) { e.preventDefault() @@ -65,14 +65,13 @@ document.addEventListener('keyup', function (e) { } }) -document.addEventListener('click', function (e) { +document.addEventListener('click', function(e) { const className = e.target.className - if (!className && typeof (className) !== 'string') return + if (!className && typeof className !== 'string') return const isInfoButton = className.includes('infoButton') const offsetParent = e.target.offsetParent - const isInfoPanel = offsetParent !== null - ? offsetParent.className.includes('infoPanel') - : false + const isInfoPanel = + offsetParent !== null ? offsetParent.className.includes('infoPanel') : false if (isInfoButton || isInfoPanel) return const infoPanel = document.querySelector('.infoPanel') if (infoPanel) infoPanel.style.display = 'none' @@ -80,11 +79,11 @@ document.addEventListener('click', function (e) { const el = document.getElementById('content') -function notify (...args) { +function notify(...args) { return new window.Notification(...args) } -function updateApp () { +function updateApp() { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Update Boostnote'), @@ -97,7 +96,7 @@ function updateApp () { } } -ReactDOM.render(( +ReactDOM.render( @@ -112,36 +111,41 @@ ReactDOM.render(( {/* storages */} - + - -), el, function () { - const loadingCover = document.getElementById('loadingCover') - loadingCover.parentNode.removeChild(loadingCover) + , + el, + function() { + const loadingCover = document.getElementById('loadingCover') + loadingCover.parentNode.removeChild(loadingCover) - ipcRenderer.on('update-ready', function () { - store.dispatch({ - type: 'UPDATE_AVAILABLE' + ipcRenderer.on('update-ready', function() { + store.dispatch({ + type: 'UPDATE_AVAILABLE' + }) + notify('Update ready!', { + body: 'New Boostnote is ready to be installed.' + }) + updateApp() }) - notify('Update ready!', { - body: 'New Boostnote is ready to be installed.' - }) - updateApp() - }) - ipcRenderer.on('update-found', function () { - notify('Update found!', { - body: 'Preparing to update...' + ipcRenderer.on('update-found', function() { + notify('Update found!', { + body: 'Preparing to update...' + }) }) - }) - ipcRenderer.send('update-check', 'check-update') - window.addEventListener('online', function () { - if (!store.getState().status.updateReady) { - ipcRenderer.send('update-check', 'check-update') - } - }) -}) + ipcRenderer.send('update-check', 'check-update') + window.addEventListener('online', function() { + if (!store.getState().status.updateReady) { + ipcRenderer.send('update-check', 'check-update') + } + }) + } +) diff --git a/browser/main/lib/AwsMobileAnalyticsConfig.js b/browser/main/lib/AwsMobileAnalyticsConfig.js index e4a21a92..ce7b03ef 100644 --- a/browser/main/lib/AwsMobileAnalyticsConfig.js +++ b/browser/main/lib/AwsMobileAnalyticsConfig.js @@ -22,7 +22,7 @@ if (!getSendEventCond()) { }) } -function convertPlatformName (platformName) { +function convertPlatformName(platformName) { if (platformName === 'darwin') { return 'MacOS' } else if (platformName === 'win32') { @@ -34,16 +34,16 @@ function convertPlatformName (platformName) { } } -function getSendEventCond () { +function getSendEventCond() { const isDev = process.env.NODE_ENV !== 'production' const isDisable = !ConfigManager.default.get().amaEnabled const isOffline = !window.navigator.onLine return isDev || isDisable || isOffline } -function initAwsMobileAnalytics () { +function initAwsMobileAnalytics() { if (getSendEventCond()) return - AWS.config.credentials.get((err) => { + AWS.config.credentials.get(err => { if (!err) { recordDynamicCustomEvent('APP_STARTED') recordStaticCustomEvent() @@ -51,7 +51,7 @@ function initAwsMobileAnalytics () { }) } -function recordDynamicCustomEvent (type, options = {}) { +function recordDynamicCustomEvent(type, options = {}) { if (getSendEventCond()) return try { mobileAnalyticsClient.recordEvent(type, options) @@ -62,7 +62,7 @@ function recordDynamicCustomEvent (type, options = {}) { } } -function recordStaticCustomEvent () { +function recordStaticCustomEvent() { if (getSendEventCond()) return try { mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', { diff --git a/browser/main/lib/Commander.js b/browser/main/lib/Commander.js index 6eef62ee..de6aa27c 100644 --- a/browser/main/lib/Commander.js +++ b/browser/main/lib/Commander.js @@ -1,25 +1,24 @@ let callees = [] -function bind (name, el) { +function bind(name, el) { callees.push({ name: name, element: el }) } -function release (el) { - callees = callees.filter((callee) => callee.element !== el) +function release(el) { + callees = callees.filter(callee => callee.element !== el) } -function fire (command) { +function fire(command) { console.info('COMMAND >>', command) const splitted = command.split(':') const target = splitted[0] const targetCommand = splitted[1] - const targetCallees = callees - .filter((callee) => callee.name === target) + const targetCallees = callees.filter(callee => callee.name === target) - targetCallees.forEach((callee) => { + targetCallees.forEach(callee => { callee.element.fire(targetCommand) }) } diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 26fda02b..6a950af1 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -33,7 +33,9 @@ export const DEFAULT_CONFIG = { toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E', toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M', toggleDirection: OSX ? 'Command + Alt + Right' : 'Ctrl + Right', - deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace', + deleteNote: OSX + ? 'Command + Shift + Backspace' + : 'Ctrl + Shift + Backspace', pasteSmartly: OSX ? 'Command + Shift + V' : 'Ctrl + Shift + V', prettifyMarkdown: OSX ? 'Command + Shift + F' : 'Ctrl + Shift + F', sortLines: OSX ? 'Command + Shift + S' : 'Ctrl + Shift + S', @@ -116,7 +118,7 @@ export const DEFAULT_CONFIG = { coloredTags: {} } -function validate (config) { +function validate(config) { if (!_.isObject(config)) return false if (!_.isNumber(config.zoom) || config.zoom < 0) return false if (!_.isBoolean(config.isSideNavFolded)) return false @@ -125,13 +127,17 @@ function validate (config) { return true } -function _save (config) { +function _save(config) { window.localStorage.setItem('config', JSON.stringify(config)) } -function get () { +function get() { const rawStoredConfig = window.localStorage.getItem('config') - const storedConfig = Object.assign({}, DEFAULT_CONFIG, JSON.parse(rawStoredConfig)) + const storedConfig = Object.assign( + {}, + DEFAULT_CONFIG, + JSON.parse(rawStoredConfig) + ) let config = storedConfig try { @@ -145,7 +151,10 @@ function get () { _save(config) } - config.autoUpdateEnabled = electronConfig.get('autoUpdateEnabled', config.autoUpdateEnabled) + config.autoUpdateEnabled = electronConfig.get( + 'autoUpdateEnabled', + config.autoUpdateEnabled + ) if (!isInitialized) { isInitialized = true @@ -157,7 +166,9 @@ function get () { document.head.appendChild(editorTheme) } - const theme = consts.THEMES.find(theme => theme.name === config.editor.theme) + const theme = consts.THEMES.find( + theme => theme.name === config.editor.theme + ) if (theme) { editorTheme.setAttribute('href', theme.path) @@ -169,7 +180,7 @@ function get () { return config } -function set (updates) { +function set(updates) { const currentConfig = get() const arrangedUpdates = updates @@ -177,7 +188,12 @@ function set (updates) { arrangedUpdates.preview.customCSS = DEFAULT_CONFIG.preview.customCSS } - const newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, arrangedUpdates) + const newConfig = Object.assign( + {}, + DEFAULT_CONFIG, + currentConfig, + arrangedUpdates + ) if (!validate(newConfig)) throw new Error('INVALID CONFIG') _save(newConfig) @@ -197,7 +213,9 @@ function set (updates) { document.head.appendChild(editorTheme) } - const newTheme = consts.THEMES.find(theme => theme.name === newConfig.editor.theme) + const newTheme = consts.THEMES.find( + theme => theme.name === newConfig.editor.theme + ) if (newTheme) { editorTheme.setAttribute('href', newTheme.path) @@ -211,20 +229,45 @@ function set (updates) { ee.emit('config-renew') } -function assignConfigValues (originalConfig, rcConfig) { +function assignConfigValues(originalConfig, rcConfig) { const config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig) - config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, originalConfig.hotkey, rcConfig.hotkey) - config.blog = Object.assign({}, DEFAULT_CONFIG.blog, originalConfig.blog, rcConfig.blog) - config.ui = Object.assign({}, DEFAULT_CONFIG.ui, originalConfig.ui, rcConfig.ui) - config.editor = Object.assign({}, DEFAULT_CONFIG.editor, originalConfig.editor, rcConfig.editor) - config.preview = Object.assign({}, DEFAULT_CONFIG.preview, originalConfig.preview, rcConfig.preview) + config.hotkey = Object.assign( + {}, + DEFAULT_CONFIG.hotkey, + originalConfig.hotkey, + rcConfig.hotkey + ) + config.blog = Object.assign( + {}, + DEFAULT_CONFIG.blog, + originalConfig.blog, + rcConfig.blog + ) + config.ui = Object.assign( + {}, + DEFAULT_CONFIG.ui, + originalConfig.ui, + rcConfig.ui + ) + config.editor = Object.assign( + {}, + DEFAULT_CONFIG.editor, + originalConfig.editor, + rcConfig.editor + ) + config.preview = Object.assign( + {}, + DEFAULT_CONFIG.preview, + originalConfig.preview, + rcConfig.preview + ) rewriteHotkey(config) return config } -function rewriteHotkey (config) { +function rewriteHotkey(config) { const keys = [...Object.keys(config.hotkey)] keys.forEach(key => { config.hotkey[key] = config.hotkey[key].replace(/Cmd\s/g, 'Command ') diff --git a/browser/main/lib/ZoomManager.js b/browser/main/lib/ZoomManager.js index a8903ca3..56bc9236 100644 --- a/browser/main/lib/ZoomManager.js +++ b/browser/main/lib/ZoomManager.js @@ -5,20 +5,20 @@ const { remote } = electron _init() -function _init () { +function _init() { setZoom(getZoom(), true) } -function _saveZoom (zoomFactor) { - ConfigManager.set({zoom: zoomFactor}) +function _saveZoom(zoomFactor) { + ConfigManager.set({ zoom: zoomFactor }) } -function setZoom (zoomFactor, noSave = false) { +function setZoom(zoomFactor, noSave = false) { if (!noSave) _saveZoom(zoomFactor) remote.getCurrentWebContents().setZoomFactor(zoomFactor) } -function getZoom () { +function getZoom() { const config = ConfigManager.get() return config.zoom diff --git a/browser/main/lib/dataApi/addStorage.js b/browser/main/lib/dataApi/addStorage.js index bfd6698a..370a07e0 100644 --- a/browser/main/lib/dataApi/addStorage.js +++ b/browser/main/lib/dataApi/addStorage.js @@ -16,7 +16,7 @@ const CSON = require('@rokt33r/season') * 3. fetch notes & folders * 4. return `{storage: {...} folders: [folder]}` */ -function addStorage (input) { +function addStorage(input) { if (!_.isString(input.path)) { return Promise.reject(new Error('Path must be a string.')) } @@ -29,7 +29,7 @@ function addStorage (input) { rawStorages = [] } let key = keygen() - while (rawStorages.some((storage) => storage.key === key)) { + while (rawStorages.some(storage => storage.key === key)) { key = keygen() } @@ -43,7 +43,7 @@ function addStorage (input) { return Promise.resolve(newStorage) .then(resolveStorageData) - .then(function saveMetadataToLocalStorage (resolvedStorage) { + .then(function saveMetadataToLocalStorage(resolvedStorage) { newStorage = resolvedStorage rawStorages.push({ key: newStorage.key, @@ -56,27 +56,29 @@ function addStorage (input) { localStorage.setItem('storages', JSON.stringify(rawStorages)) return newStorage }) - .then(function (storage) { - return resolveStorageNotes(storage) - .then((notes) => { - let unknownCount = 0 - notes.forEach((note) => { - if (!storage.folders.some((folder) => note.folder === folder.key)) { - unknownCount++ - storage.folders.push({ - key: note.folder, - color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], - name: 'Unknown ' + unknownCount - }) - } - }) - if (unknownCount > 0) { - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + .then(function(storage) { + return resolveStorageNotes(storage).then(notes => { + let unknownCount = 0 + notes.forEach(note => { + if (!storage.folders.some(folder => note.folder === folder.key)) { + unknownCount++ + storage.folders.push({ + key: note.folder, + color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], + name: 'Unknown ' + unknownCount + }) } - return notes }) + if (unknownCount > 0) { + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) + } + return notes + }) }) - .then(function returnValue (notes) { + .then(function returnValue(notes) { return { storage: newStorage, notes diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 971ae812..f3b11997 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -12,14 +12,15 @@ import { isString } from 'lodash' const STORAGE_FOLDER_PLACEHOLDER = ':storage' const DESTINATION_FOLDER = 'attachments' -const PATH_SEPARATORS = escapeStringRegexp(path.posix.sep) + escapeStringRegexp(path.win32.sep) +const PATH_SEPARATORS = + escapeStringRegexp(path.posix.sep) + escapeStringRegexp(path.win32.sep) /** * @description * Create a Image element to get the real size of image. * @param {File} file the File object dropped. * @returns {Promise} Image element created */ -function getImage (file) { +function getImage(file) { if (isString(file)) { return new Promise(resolve => { const img = new Image() @@ -55,38 +56,39 @@ function getImage (file) { * @param {File} file the File object dropped. * @returns {Promise} Orientation info */ -function getOrientation (file) { +function getOrientation(file) { const getData = arrayBuffer => { const view = new DataView(arrayBuffer) // Not start with SOI(Start of image) Marker return fail value - if (view.getUint16(0, false) !== 0xFFD8) return -2 + if (view.getUint16(0, false) !== 0xffd8) return -2 const length = view.byteLength let offset = 2 while (offset < length) { const marker = view.getUint16(offset, false) offset += 2 // Loop and seed for APP1 Marker - if (marker === 0xFFE1) { + if (marker === 0xffe1) { // return fail value if it isn't EXIF data - if (view.getUint32(offset += 2, false) !== 0x45786966) { + if (view.getUint32((offset += 2), false) !== 0x45786966) { return -1 } // Read TIFF header, // First 2bytes defines byte align of TIFF data. // If it is 0x4949="II", it means "Intel" type byte align. // If it is 0x4d4d="MM", it means "Motorola" type byte align - const little = view.getUint16(offset += 6, false) === 0x4949 + const little = view.getUint16((offset += 6), false) === 0x4949 offset += view.getUint32(offset + 4, little) const tags = view.getUint16(offset, little) // Get TAG number offset += 2 for (let i = 0; i < tags; i++) { // Loop to find Orientation TAG and return the value - if (view.getUint16(offset + (i * 12), little) === 0x0112) { - return view.getUint16(offset + (i * 12) + 8, little) + if (view.getUint16(offset + i * 12, little) === 0x0112) { + return view.getUint16(offset + i * 12 + 8, little) } } - } else if ((marker & 0xFF00) !== 0xFF00) { // If not start with 0xFF, not a Marker. + } else if ((marker & 0xff00) !== 0xff00) { + // If not start with 0xFF, not a Marker. break } else { offset += view.getUint16(offset, false) @@ -94,7 +96,7 @@ function getOrientation (file) { } return -1 } - return new Promise((resolve) => { + return new Promise(resolve => { const reader = new FileReader() reader.onload = event => resolve(getData(event.target.result)) reader.readAsArrayBuffer(file.slice(0, 64 * 1024)) @@ -107,31 +109,47 @@ function getOrientation (file) { * @param {*} file the File object dropped. * @return {String} Base64 encoded image. */ -function fixRotate (file) { - return Promise.all([getImage(file), getOrientation(file)]) - .then(([img, orientation]) => { - const canvas = document.createElement('canvas') - const ctx = canvas.getContext('2d') - if (orientation > 4 && orientation < 9) { - canvas.width = img.height - canvas.height = img.width - } else { - canvas.width = img.width - canvas.height = img.height +function fixRotate(file) { + return Promise.all([getImage(file), getOrientation(file)]).then( + ([img, orientation]) => { + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + if (orientation > 4 && orientation < 9) { + canvas.width = img.height + canvas.height = img.width + } else { + canvas.width = img.width + canvas.height = img.height + } + switch (orientation) { + case 2: + ctx.transform(-1, 0, 0, 1, img.width, 0) + break + case 3: + ctx.transform(-1, 0, 0, -1, img.width, img.height) + break + case 4: + ctx.transform(1, 0, 0, -1, 0, img.height) + break + case 5: + ctx.transform(0, 1, 1, 0, 0, 0) + break + case 6: + ctx.transform(0, 1, -1, 0, img.height, 0) + break + case 7: + ctx.transform(0, -1, -1, 0, img.height, img.width) + break + case 8: + ctx.transform(0, -1, 1, 0, 0, img.width) + break + default: + break + } + ctx.drawImage(img, 0, 0) + return canvas.toDataURL() } - switch (orientation) { - case 2: ctx.transform(-1, 0, 0, 1, img.width, 0); break - case 3: ctx.transform(-1, 0, 0, -1, img.width, img.height); break - case 4: ctx.transform(1, 0, 0, -1, 0, img.height); break - case 5: ctx.transform(0, 1, 1, 0, 0, 0); break - case 6: ctx.transform(0, 1, -1, 0, img.height, 0); break - case 7: ctx.transform(0, -1, -1, 0, img.height, img.width); break - case 8: ctx.transform(0, -1, 1, 0, 0, img.width); break - default: break - } - ctx.drawImage(img, 0, 0) - return canvas.toDataURL() - }) + ) } /** @@ -145,7 +163,12 @@ function fixRotate (file) { * @param {boolean} useRandomName determines whether a random filename for the new file is used. If false the source file name is used * @return {Promise} name (inclusive extension) of the generated file */ -function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = true) { +function copyAttachment( + sourceFilePath, + storageKey, + noteKey, + useRandomName = true +) { return new Promise((resolve, reject) => { if (!sourceFilePath) { reject('sourceFilePath has to be given') @@ -160,28 +183,41 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr } try { - const isBase64 = typeof sourceFilePath === 'object' && sourceFilePath.type === 'base64' + const isBase64 = + typeof sourceFilePath === 'object' && sourceFilePath.type === 'base64' if (!isBase64 && !fs.existsSync(sourceFilePath)) { return reject('source file does not exist') } const sourcePath = sourceFilePath.sourceFilePath || sourceFilePath - const sourceURL = url.parse(/^\w+:\/\//.test(sourcePath) ? sourcePath : 'file:///' + sourcePath) + const sourceURL = url.parse( + /^\w+:\/\//.test(sourcePath) ? sourcePath : 'file:///' + sourcePath + ) let destinationName if (useRandomName) { - destinationName = `${uniqueSlug()}${path.extname(sourceURL.pathname) || '.png'}` + destinationName = `${uniqueSlug()}${path.extname(sourceURL.pathname) || + '.png'}` } else { destinationName = path.basename(sourceURL.pathname) } const targetStorage = findStorage.findStorage(storageKey) - const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const destinationDir = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) createAttachmentDestinationFolder(targetStorage.path, noteKey) - const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName)) + const outputFile = fs.createWriteStream( + path.join(destinationDir, destinationName) + ) if (isBase64) { - const base64Data = sourceFilePath.data.replace(/^data:image\/\w+;base64,/, '') + const base64Data = sourceFilePath.data.replace( + /^data:image\/\w+;base64,/, + '' + ) const dataBuffer = Buffer.from(base64Data, 'base64') outputFile.write(dataBuffer, () => { resolve(destinationName) @@ -199,12 +235,16 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr }) } -function createAttachmentDestinationFolder (destinationStoragePath, noteKey) { +function createAttachmentDestinationFolder(destinationStoragePath, noteKey) { let destinationDir = path.join(destinationStoragePath, DESTINATION_FOLDER) if (!fs.existsSync(destinationDir)) { fs.mkdirSync(destinationDir) } - destinationDir = path.join(destinationStoragePath, DESTINATION_FOLDER, noteKey) + destinationDir = path.join( + destinationStoragePath, + DESTINATION_FOLDER, + noteKey + ) if (!fs.existsSync(destinationDir)) { fs.mkdirSync(destinationDir) } @@ -216,17 +256,28 @@ function createAttachmentDestinationFolder (destinationStoragePath, noteKey) { * @param storagePath Storage path of the current note * @param noteKey Key of the current note */ -function migrateAttachments (markdownContent, storagePath, noteKey) { - if (noteKey !== undefined && sander.existsSync(path.join(storagePath, 'images'))) { +function migrateAttachments(markdownContent, storagePath, noteKey) { + if ( + noteKey !== undefined && + sander.existsSync(path.join(storagePath, 'images')) + ) { const attachments = getAttachmentsInMarkdownContent(markdownContent) || [] if (attachments.length) { createAttachmentDestinationFolder(storagePath, noteKey) } for (const attachment of attachments) { const attachmentBaseName = path.basename(attachment) - const possibleLegacyPath = path.join(storagePath, 'images', attachmentBaseName) + const possibleLegacyPath = path.join( + storagePath, + 'images', + attachmentBaseName + ) if (sander.existsSync(possibleLegacyPath)) { - const destinationPath = path.join(storagePath, DESTINATION_FOLDER, attachmentBaseName) + const destinationPath = path.join( + storagePath, + DESTINATION_FOLDER, + attachmentBaseName + ) if (!sander.existsSync(destinationPath)) { sander.copyFileSync(possibleLegacyPath).to(destinationPath) } @@ -241,10 +292,11 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @param {String} storagePath Path of the current storage * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ -function fixLocalURLS (renderedHTML, storagePath) { +function fixLocalURLS(renderedHTML, storagePath) { const encodedWin32SeparatorRegex = /%5C/g const storageRegex = new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g') - const storageUrl = 'file:///' + path.join(storagePath, DESTINATION_FOLDER).replace(/\\/g, '/') + const storageUrl = + 'file:///' + path.join(storagePath, DESTINATION_FOLDER).replace(/\\/g, '/') /* A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`. @@ -254,9 +306,17 @@ function fixLocalURLS (renderedHTML, storagePath) { - `(?:\\\/|%5C)[-.\\w]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg` - `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows. */ - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[-.\\w]+)+', 'g'), function (match) { - return match.replace(encodedWin32SeparatorRegex, '/').replace(storageRegex, storageUrl) - }) + return renderedHTML.replace( + new RegExp( + '/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\/|%5C)[-.\\w]+)+', + 'g' + ), + function(match) { + return match + .replace(encodedWin32SeparatorRegex, '/') + .replace(storageRegex, storageUrl) + } + ) } /** @@ -266,7 +326,7 @@ function fixLocalURLS (renderedHTML, storagePath) { * @param {Boolean} showPreview Indicator whether the generated markdown should show a preview of the image. Note that at the moment only previews for images are supported * @returns {String} Generated markdown code */ -function generateAttachmentMarkdown (fileName, path, showPreview) { +function generateAttachmentMarkdown(fileName, path, showPreview) { return `${showPreview ? '!' : ''}[${fileName}](${path})` } @@ -278,53 +338,66 @@ function generateAttachmentMarkdown (fileName, path, showPreview) { * @param {String} noteKey Key of the current note * @param {Event} dropEvent DropEvent */ -function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { +function handleAttachmentDrop(codeEditor, storageKey, noteKey, dropEvent) { let promise if (dropEvent.dataTransfer.files.length > 0) { - promise = Promise.all(Array.from(dropEvent.dataTransfer.files).map(file => { - const filePath = file.path - const fileType = file.type // EX) 'image/gif' or 'text/html' - if (fileType.startsWith('image')) { - if (fileType === 'image/gif' || fileType === 'image/svg+xml') { - return copyAttachment(filePath, storageKey, noteKey).then(fileName => ({ - fileName, - title: path.basename(filePath), - isImage: true - })) - } else { - return getOrientation(file) - .then((orientation) => { - if (orientation === -1) { // The image rotation is correct and does not need adjustment - return copyAttachment(filePath, storageKey, noteKey) - } else { - return fixRotate(file).then(data => copyAttachment({ - type: 'base64', - data: data, - sourceFilePath: filePath - }, storageKey, noteKey)) - } - }) - .then(fileName => - ({ + promise = Promise.all( + Array.from(dropEvent.dataTransfer.files).map(file => { + const filePath = file.path + const fileType = file.type // EX) 'image/gif' or 'text/html' + if (fileType.startsWith('image')) { + if (fileType === 'image/gif' || fileType === 'image/svg+xml') { + return copyAttachment(filePath, storageKey, noteKey).then( + fileName => ({ fileName, title: path.basename(filePath), isImage: true }) ) + } else { + return getOrientation(file) + .then(orientation => { + if (orientation === -1) { + // The image rotation is correct and does not need adjustment + return copyAttachment(filePath, storageKey, noteKey) + } else { + return fixRotate(file).then(data => + copyAttachment( + { + type: 'base64', + data: data, + sourceFilePath: filePath + }, + storageKey, + noteKey + ) + ) + } + }) + .then(fileName => ({ + fileName, + title: path.basename(filePath), + isImage: true + })) + } + } else { + return copyAttachment(filePath, storageKey, noteKey).then( + fileName => ({ + fileName, + title: path.basename(filePath), + isImage: false + }) + ) } - } else { - return copyAttachment(filePath, storageKey, noteKey).then(fileName => ({ - fileName, - title: path.basename(filePath), - isImage: false - })) - } - })) + }) + ) } else { let imageURL = dropEvent.dataTransfer.getData('text/plain') if (!imageURL) { - const match = /]*[\s"']src="([^"]+)"/.exec(dropEvent.dataTransfer.getData('text/html')) + const match = /]*[\s"']src="([^"]+)"/.exec( + dropEvent.dataTransfer.getData('text/html') + ) if (match) { imageURL = match[1] } @@ -334,30 +407,43 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { return } - promise = Promise.all([getImage(imageURL) - .then(image => { - const canvas = document.createElement('canvas') - const context = canvas.getContext('2d') - canvas.width = image.width - canvas.height = image.height - context.drawImage(image, 0, 0) + promise = Promise.all([ + getImage(imageURL) + .then(image => { + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d') + canvas.width = image.width + canvas.height = image.height + context.drawImage(image, 0, 0) - return copyAttachment({ - type: 'base64', - data: canvas.toDataURL(), - sourceFilePath: imageURL - }, storageKey, noteKey) - }) - .then(fileName => ({ - fileName, - title: imageURL, - isImage: true - })) + return copyAttachment( + { + type: 'base64', + data: canvas.toDataURL(), + sourceFilePath: imageURL + }, + storageKey, + noteKey + ) + }) + .then(fileName => ({ + fileName, + title: imageURL, + isImage: true + })) ]) } promise.then(files => { - const attachments = files.filter(file => !!file).map(file => generateAttachmentMarkdown(file.title, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, file.fileName), file.isImage)) + const attachments = files + .filter(file => !!file) + .map(file => + generateAttachmentMarkdown( + file.title, + path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, file.fileName), + file.isImage + ) + ) codeEditor.insertAttachmentMd(attachments.join('\n')) }) @@ -370,7 +456,12 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { * @param {String} noteKey Key of the current note * @param {DataTransferItem} dataTransferItem Part of the past-event */ -function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) { +function handlePasteImageEvent( + codeEditor, + storageKey, + noteKey, + dataTransferItem +) { if (!codeEditor) { throw new Error('codeEditor has to be given') } @@ -389,19 +480,31 @@ function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferIte const reader = new FileReader() let base64data const targetStorage = findStorage.findStorage(storageKey) - const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const destinationDir = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) createAttachmentDestinationFolder(targetStorage.path, noteKey) const imageName = `${uniqueSlug()}.png` const imagePath = path.join(destinationDir, imageName) - reader.onloadend = function () { + reader.onloadend = function() { base64data = reader.result.replace(/^data:image\/png;base64,/, '') base64data += base64data.replace('+', ' ') const binaryData = new Buffer(base64data, 'base64').toString('binary') fs.writeFileSync(imagePath, binaryData, 'binary') - const imageReferencePath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, imageName) - const imageMd = generateAttachmentMarkdown(imageName, imageReferencePath, true) + const imageReferencePath = path.join( + STORAGE_FOLDER_PLACEHOLDER, + noteKey, + imageName + ) + const imageMd = generateAttachmentMarkdown( + imageName, + imageReferencePath, + true + ) codeEditor.insertAttachmentMd(imageMd) } reader.readAsDataURL(blob) @@ -414,7 +517,7 @@ function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferIte * @param {String} noteKey Key of the current note * @param {NativeImage} image The native image */ -function handlePasteNativeImage (codeEditor, storageKey, noteKey, image) { +function handlePasteNativeImage(codeEditor, storageKey, noteKey, image) { if (!codeEditor) { throw new Error('codeEditor has to be given') } @@ -430,7 +533,11 @@ function handlePasteNativeImage (codeEditor, storageKey, noteKey, image) { } const targetStorage = findStorage.findStorage(storageKey) - const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const destinationDir = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) createAttachmentDestinationFolder(targetStorage.path, noteKey) @@ -440,19 +547,42 @@ function handlePasteNativeImage (codeEditor, storageKey, noteKey, image) { const binaryData = image.toPNG() fs.writeFileSync(imagePath, binaryData, 'binary') - const imageReferencePath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, imageName) - const imageMd = generateAttachmentMarkdown(imageName, imageReferencePath, true) + const imageReferencePath = path.join( + STORAGE_FOLDER_PLACEHOLDER, + noteKey, + imageName + ) + const imageMd = generateAttachmentMarkdown( + imageName, + imageReferencePath, + true + ) codeEditor.insertAttachmentMd(imageMd) } /** -* @description Returns all attachment paths of the given markdown -* @param {String} markdownContent content in which the attachment paths should be found -* @returns {String[]} Array of the relative paths (starting with :storage) of the attachments of the given markdown -*/ -function getAttachmentsInMarkdownContent (markdownContent) { - const preparedInput = markdownContent.replace(new RegExp('[' + PATH_SEPARATORS + ']', 'g'), path.sep) - const regexp = new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + ')' + '?([a-zA-Z0-9]|-)*' + '(' + escapeStringRegexp(path.sep) + ')' + '([a-zA-Z0-9]|\\.)+(\\.[a-zA-Z0-9]+)?', 'g') + * @description Returns all attachment paths of the given markdown + * @param {String} markdownContent content in which the attachment paths should be found + * @returns {String[]} Array of the relative paths (starting with :storage) of the attachments of the given markdown + */ +function getAttachmentsInMarkdownContent(markdownContent) { + const preparedInput = markdownContent.replace( + new RegExp('[' + PATH_SEPARATORS + ']', 'g'), + path.sep + ) + const regexp = new RegExp( + '/?' + + STORAGE_FOLDER_PLACEHOLDER + + '(' + + escapeStringRegexp(path.sep) + + ')' + + '?([a-zA-Z0-9]|-)*' + + '(' + + escapeStringRegexp(path.sep) + + ')' + + '([a-zA-Z0-9]|\\.)+(\\.[a-zA-Z0-9]+)?', + 'g' + ) return preparedInput.match(regexp) } @@ -462,11 +592,16 @@ function getAttachmentsInMarkdownContent (markdownContent) { * @param {String} storagePath path of the current storage * @returns {String[]} Absolute paths of the referenced attachments */ -function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) { +function getAbsolutePathsOfAttachmentsInContent(markdownContent, storagePath) { const temp = getAttachmentsInMarkdownContent(markdownContent) || [] const result = [] for (const relativePath of temp) { - result.push(relativePath.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), path.join(storagePath, DESTINATION_FOLDER))) + result.push( + relativePath.replace( + new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), + path.join(storagePath, DESTINATION_FOLDER) + ) + ) } return result } @@ -478,7 +613,7 @@ function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) { * @param {String} storageKey Storage key of the destination storage * @param {String} noteKey Key of the current note. Will be used as subfolder in :storage */ -function importAttachments (markDownContent, filepath, storageKey, noteKey) { +function importAttachments(markDownContent, filepath, storageKey, noteKey) { return new Promise((resolve, reject) => { const nameRegex = /(!\[.*?]\()(.+?\..+?)(\))/g let attachPath = nameRegex.exec(markDownContent) @@ -489,8 +624,12 @@ function importAttachments (markDownContent, filepath, storageKey, noteKey) { while (attachPath) { let attachmentPath = attachPath[groupIndex] attachmentPaths.push(attachmentPath) - attachmentPath = path.isAbsolute(attachmentPath) ? attachmentPath : path.join(path.dirname(filepath), attachmentPath) - promiseArray.push(this.copyAttachment(attachmentPath, storageKey, noteKey)) + attachmentPath = path.isAbsolute(attachmentPath) + ? attachmentPath + : path.join(path.dirname(filepath), attachmentPath) + promiseArray.push( + this.copyAttachment(attachmentPath, storageKey, noteKey) + ) attachPath = nameRegex.exec(markDownContent) } @@ -502,19 +641,23 @@ function importAttachments (markDownContent, filepath, storageKey, noteKey) { for (let j = 0; j < promiseArray.length; j++) { promiseArray[j] - .then((fileName) => { - const newPath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName) - markDownContent = markDownContent.replace(attachmentPaths[j], newPath) - }) - .catch((e) => { - console.error('File does not exist in path: ' + attachmentPaths[j]) - }) - .finally(() => { - numResolvedPromises++ - if (numResolvedPromises === promiseArray.length) { - resolve(markDownContent) - } - }) + .then(fileName => { + const newPath = path.join( + STORAGE_FOLDER_PLACEHOLDER, + noteKey, + fileName + ) + markDownContent = markDownContent.replace(attachmentPaths[j], newPath) + }) + .catch(e => { + console.error('File does not exist in path: ' + attachmentPaths[j]) + }) + .finally(() => { + numResolvedPromises++ + if (numResolvedPromises === promiseArray.length) { + resolve(markDownContent) + } + }) } }) } @@ -529,7 +672,7 @@ function importAttachments (markDownContent, filepath, storageKey, noteKey) { * @param {String} noteContent Content of the note to be moved * @returns {String} Modified version of noteContent in which the paths of the attachments are fixed */ -function moveAttachments (oldPath, newPath, noteKey, newNoteKey, noteContent) { +function moveAttachments(oldPath, newPath, noteKey, newNoteKey, noteContent) { const src = path.join(oldPath, DESTINATION_FOLDER, noteKey) const dest = path.join(newPath, DESTINATION_FOLDER, newNoteKey) if (fse.existsSync(src)) { @@ -545,10 +688,19 @@ function moveAttachments (oldPath, newPath, noteKey, newNoteKey, noteContent) { * @param newNoteKey note key serving as a replacement * @returns {String} modified note content */ -function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { +function replaceNoteKeyWithNewNoteKey(noteContent, oldNoteKey, newNoteKey) { if (noteContent) { - const preparedInput = noteContent.replace(new RegExp('[' + PATH_SEPARATORS + ']', 'g'), path.sep) - return preparedInput.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + oldNoteKey, 'g'), path.join(STORAGE_FOLDER_PLACEHOLDER, newNoteKey)) + const preparedInput = noteContent.replace( + new RegExp('[' + PATH_SEPARATORS + ']', 'g'), + path.sep + ) + return preparedInput.replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + oldNoteKey, + 'g' + ), + path.join(STORAGE_FOLDER_PLACEHOLDER, newNoteKey) + ) } return noteContent } @@ -559,15 +711,28 @@ function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { * @param noteKey Key of the current note * @returns {String} Input without the references */ -function removeStorageAndNoteReferences (input, noteKey) { - return input.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), function (match) { - const temp = match - .replace(new RegExp(mdurl.encode(path.win32.sep), 'g'), path.sep) - .replace(new RegExp(mdurl.encode(path.posix.sep), 'g'), path.sep) - .replace(new RegExp(escapeStringRegexp(path.win32.sep), 'g'), path.sep) - .replace(new RegExp(escapeStringRegexp(path.posix.sep), 'g'), path.sep) - return temp.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) - }) +function removeStorageAndNoteReferences(input, noteKey) { + return input.replace( + new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), + function(match) { + const temp = match + .replace(new RegExp(mdurl.encode(path.win32.sep), 'g'), path.sep) + .replace(new RegExp(mdurl.encode(path.posix.sep), 'g'), path.sep) + .replace(new RegExp(escapeStringRegexp(path.win32.sep), 'g'), path.sep) + .replace(new RegExp(escapeStringRegexp(path.posix.sep), 'g'), path.sep) + return temp.replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + + '(' + + escapeStringRegexp(path.sep) + + noteKey + + ')?', + 'g' + ), + DESTINATION_FOLDER + ) + } + ) } /** @@ -575,9 +740,13 @@ function removeStorageAndNoteReferences (input, noteKey) { * @param storageKey Key of the storage of the note to be deleted * @param noteKey Key of the note to be deleted */ -function deleteAttachmentFolder (storageKey, noteKey) { +function deleteAttachmentFolder(storageKey, noteKey) { const storagePath = findStorage.findStorage(storageKey) - const noteAttachmentPath = path.join(storagePath.path, DESTINATION_FOLDER, noteKey) + const noteAttachmentPath = path.join( + storagePath.path, + DESTINATION_FOLDER, + noteKey + ) sander.rimrafSync(noteAttachmentPath) } @@ -587,36 +756,66 @@ function deleteAttachmentFolder (storageKey, noteKey) { * @param storageKey StorageKey of the current note. Is used to determine the belonging attachment folder. * @param noteKey NoteKey of the current note. Is used to determine the belonging attachment folder. */ -function deleteAttachmentsNotPresentInNote (markdownContent, storageKey, noteKey) { +function deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey +) { if (storageKey == null || noteKey == null || markdownContent == null) { return } const targetStorage = findStorage.findStorage(storageKey) - const attachmentFolder = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const attachmentFolder = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) const attachmentsInNote = getAttachmentsInMarkdownContent(markdownContent) const attachmentsInNoteOnlyFileNames = [] if (attachmentsInNote) { for (let i = 0; i < attachmentsInNote.length; i++) { - attachmentsInNoteOnlyFileNames.push(attachmentsInNote[i].replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey + escapeStringRegexp(path.sep), 'g'), '')) + attachmentsInNoteOnlyFileNames.push( + attachmentsInNote[i].replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + + escapeStringRegexp(path.sep) + + noteKey + + escapeStringRegexp(path.sep), + 'g' + ), + '' + ) + ) } } if (fs.existsSync(attachmentFolder)) { fs.readdir(attachmentFolder, (err, files) => { if (err) { - console.error('Error reading directory "' + attachmentFolder + '". Error:') + console.error( + 'Error reading directory "' + attachmentFolder + '". Error:' + ) console.error(err) return } files.forEach(file => { if (!attachmentsInNoteOnlyFileNames.includes(file)) { - const absolutePathOfFile = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey, file) - fs.unlink(absolutePathOfFile, (err) => { + const absolutePathOfFile = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey, + file + ) + fs.unlink(absolutePathOfFile, err => { if (err) { console.error('Could not delete "%s"', absolutePathOfFile) console.error(err) return } - console.info('File "' + absolutePathOfFile + '" deleted because it was not included in the content of the note') + console.info( + 'File "' + + absolutePathOfFile + + '" deleted because it was not included in the content of the note' + ) }) } }) @@ -632,31 +831,53 @@ function deleteAttachmentsNotPresentInNote (markdownContent, storageKey, noteKey * @param noteKey NoteKey of the currentNote * @return {Promise>} Promise returning the list of attachments with their properties */ -function getAttachmentsPathAndStatus (markdownContent, storageKey, noteKey) { +function getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) { if (storageKey == null || noteKey == null || markdownContent == null) { return null } const targetStorage = findStorage.findStorage(storageKey) - const attachmentFolder = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const attachmentFolder = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) const attachmentsInNote = getAttachmentsInMarkdownContent(markdownContent) const attachmentsInNoteOnlyFileNames = [] if (attachmentsInNote) { for (let i = 0; i < attachmentsInNote.length; i++) { - attachmentsInNoteOnlyFileNames.push(attachmentsInNote[i].replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey + escapeStringRegexp(path.sep), 'g'), '')) + attachmentsInNoteOnlyFileNames.push( + attachmentsInNote[i].replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + + escapeStringRegexp(path.sep) + + noteKey + + escapeStringRegexp(path.sep), + 'g' + ), + '' + ) + ) } } if (fs.existsSync(attachmentFolder)) { return new Promise((resolve, reject) => { fs.readdir(attachmentFolder, (err, files) => { if (err) { - console.error('Error reading directory "' + attachmentFolder + '". Error:') + console.error( + 'Error reading directory "' + attachmentFolder + '". Error:' + ) console.error(err) reject(err) return } const attachments = [] for (const file of files) { - const absolutePathOfFile = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey, file) + const absolutePathOfFile = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey, + file + ) if (!attachmentsInNoteOnlyFileNames.includes(file)) { attachments.push({ path: absolutePathOfFile, isInUse: false }) } else { @@ -675,11 +896,11 @@ function getAttachmentsPathAndStatus (markdownContent, storageKey, noteKey) { * @description Remove all specified attachment paths * @param attachments attachment paths * @return {Promise} Promise after all attachments are removed */ -function removeAttachmentsByPaths (attachments) { +function removeAttachmentsByPaths(attachments) { const promises = [] for (const attachment of attachments) { const promise = new Promise((resolve, reject) => { - fs.unlink(attachment, (err) => { + fs.unlink(attachment, err => { if (err) { console.error('Could not delete "%s"', attachment) console.error(err) @@ -700,29 +921,54 @@ function removeAttachmentsByPaths (attachments) { * @param oldNote Note that is being cloned * @param newNote Clone of the note */ -function cloneAttachments (oldNote, newNote) { +function cloneAttachments(oldNote, newNote) { if (newNote.type === 'MARKDOWN_NOTE') { const oldStorage = findStorage.findStorage(oldNote.storage) const newStorage = findStorage.findStorage(newNote.storage) - const attachmentsPaths = getAbsolutePathsOfAttachmentsInContent(oldNote.content, oldStorage.path) || [] + const attachmentsPaths = + getAbsolutePathsOfAttachmentsInContent( + oldNote.content, + oldStorage.path + ) || [] - const destinationFolder = path.join(newStorage.path, DESTINATION_FOLDER, newNote.key) + const destinationFolder = path.join( + newStorage.path, + DESTINATION_FOLDER, + newNote.key + ) if (!sander.existsSync(destinationFolder)) { sander.mkdirSync(destinationFolder) } for (const attachment of attachmentsPaths) { - const destination = path.join(newStorage.path, DESTINATION_FOLDER, newNote.key, path.basename(attachment)) + const destination = path.join( + newStorage.path, + DESTINATION_FOLDER, + newNote.key, + path.basename(attachment) + ) sander.copyFileSync(attachment).to(destination) } - newNote.content = replaceNoteKeyWithNewNoteKey(newNote.content, oldNote.key, newNote.key) + newNote.content = replaceNoteKeyWithNewNoteKey( + newNote.content, + oldNote.key, + newNote.key + ) } else { - console.debug('Cloning of the attachment was skipped since it only works for MARKDOWN_NOTEs') + console.debug( + 'Cloning of the attachment was skipped since it only works for MARKDOWN_NOTEs' + ) } } -function generateFileNotFoundMarkdown () { - return '**' + i18n.__('⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠') + '**' +function generateFileNotFoundMarkdown() { + return ( + '**' + + i18n.__( + '⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠' + ) + + '**' + ) } /** @@ -730,9 +976,21 @@ function generateFileNotFoundMarkdown () { * @param text Text that might contain a attachment link * @return {Boolean} Result of the test */ -function isAttachmentLink (text) { +function isAttachmentLink(text) { if (text) { - return text.match(new RegExp('.*\\[.*\\]\\( *' + escapeStringRegexp(STORAGE_FOLDER_PLACEHOLDER) + '[' + PATH_SEPARATORS + ']' + '.*\\).*', 'gi')) != null + return ( + text.match( + new RegExp( + '.*\\[.*\\]\\( *' + + escapeStringRegexp(STORAGE_FOLDER_PLACEHOLDER) + + '[' + + PATH_SEPARATORS + + ']' + + '.*\\).*', + 'gi' + ) + ) != null + ) } return false } @@ -745,38 +1003,72 @@ function isAttachmentLink (text) { * @param linkText Text that was pasted * @return {Promise} Promise returning the modified text */ -function handleAttachmentLinkPaste (storageKey, noteKey, linkText) { +function handleAttachmentLinkPaste(storageKey, noteKey, linkText) { if (storageKey != null && noteKey != null && linkText != null) { const storagePath = findStorage.findStorage(storageKey).path const attachments = getAttachmentsInMarkdownContent(linkText) || [] const replaceInstructions = [] const copies = [] for (const attachment of attachments) { - const absPathOfAttachment = attachment.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), path.join(storagePath, DESTINATION_FOLDER)) + const absPathOfAttachment = attachment.replace( + new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), + path.join(storagePath, DESTINATION_FOLDER) + ) copies.push( - sander.exists(absPathOfAttachment) - .then((fileExists) => { - if (!fileExists) { - const fileNotFoundRegexp = new RegExp('!?' + escapeStringRegexp('[') + '[\\w|\\d|\\s|\\.]*\\]\\(\\s*' + STORAGE_FOLDER_PLACEHOLDER + '[\\w|\\d|\\-|' + PATH_SEPARATORS + ']*' + escapeStringRegexp(path.basename(absPathOfAttachment)) + escapeStringRegexp(')')) - replaceInstructions.push({regexp: fileNotFoundRegexp, replacement: this.generateFileNotFoundMarkdown()}) - return Promise.resolve() - } - return this.copyAttachment(absPathOfAttachment, storageKey, noteKey) - .then((fileName) => { - const replaceLinkRegExp = new RegExp(escapeStringRegexp('(') + ' *' + STORAGE_FOLDER_PLACEHOLDER + '[\\w|\\d|\\-|' + PATH_SEPARATORS + ']*' + escapeStringRegexp(path.basename(absPathOfAttachment)) + ' *' + escapeStringRegexp(')')) - replaceInstructions.push({ - regexp: replaceLinkRegExp, - replacement: '(' + path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName) + ')' - }) - return Promise.resolve() - }) + sander.exists(absPathOfAttachment).then(fileExists => { + if (!fileExists) { + const fileNotFoundRegexp = new RegExp( + '!?' + + escapeStringRegexp('[') + + '[\\w|\\d|\\s|\\.]*\\]\\(\\s*' + + STORAGE_FOLDER_PLACEHOLDER + + '[\\w|\\d|\\-|' + + PATH_SEPARATORS + + ']*' + + escapeStringRegexp(path.basename(absPathOfAttachment)) + + escapeStringRegexp(')') + ) + replaceInstructions.push({ + regexp: fileNotFoundRegexp, + replacement: this.generateFileNotFoundMarkdown() + }) + return Promise.resolve() + } + return this.copyAttachment( + absPathOfAttachment, + storageKey, + noteKey + ).then(fileName => { + const replaceLinkRegExp = new RegExp( + escapeStringRegexp('(') + + ' *' + + STORAGE_FOLDER_PLACEHOLDER + + '[\\w|\\d|\\-|' + + PATH_SEPARATORS + + ']*' + + escapeStringRegexp(path.basename(absPathOfAttachment)) + + ' *' + + escapeStringRegexp(')') + ) + replaceInstructions.push({ + regexp: replaceLinkRegExp, + replacement: + '(' + + path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName) + + ')' + }) + return Promise.resolve() }) + }) ) } return Promise.all(copies).then(() => { let modifiedLinkText = linkText for (const replaceInstruction of replaceInstructions) { - modifiedLinkText = modifiedLinkText.replace(replaceInstruction.regexp, replaceInstruction.replacement) + modifiedLinkText = modifiedLinkText.replace( + replaceInstruction.regexp, + replaceInstruction.replacement + ) } return modifiedLinkText }) diff --git a/browser/main/lib/dataApi/copyFile.js b/browser/main/lib/dataApi/copyFile.js index 6f23aae2..0b390b2a 100755 --- a/browser/main/lib/dataApi/copyFile.js +++ b/browser/main/lib/dataApi/copyFile.js @@ -7,7 +7,7 @@ const path = require('path') * @param {String} dstPath * @return {Promise} an image path */ -function copyFile (srcPath, dstPath) { +function copyFile(srcPath, dstPath) { if (!path.extname(dstPath)) { dstPath = path.join(dstPath, path.basename(srcPath)) } diff --git a/browser/main/lib/dataApi/createFolder.js b/browser/main/lib/dataApi/createFolder.js index 05fcea37..786d905b 100644 --- a/browser/main/lib/dataApi/createFolder.js +++ b/browser/main/lib/dataApi/createFolder.js @@ -22,7 +22,7 @@ const { findStorage } = require('browser/lib/findStorage') * } * ``` */ -function createFolder (storageKey, input) { +function createFolder(storageKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -34,26 +34,28 @@ function createFolder (storageKey, input) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function createFolder (storage) { - let key = keygen() - while (storage.folders.some((folder) => folder.key === key)) { - key = keygen() - } - const newFolder = { - key, - color: input.color, - name: input.name - } + return resolveStorageData(targetStorage).then(function createFolder(storage) { + let key = keygen() + while (storage.folders.some(folder => folder.key === key)) { + key = keygen() + } + const newFolder = { + key, + color: input.color, + name: input.name + } - storage.folders.push(newFolder) + storage.folders.push(newFolder) - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) - return { - storage - } - }) + return { + storage + } + }) } module.exports = createFolder diff --git a/browser/main/lib/dataApi/createNote.js b/browser/main/lib/dataApi/createNote.js index 5bfa2457..079a988b 100644 --- a/browser/main/lib/dataApi/createNote.js +++ b/browser/main/lib/dataApi/createNote.js @@ -6,9 +6,11 @@ const path = require('path') const CSON = require('@rokt33r/season') const { findStorage } = require('browser/lib/findStorage') -function validateInput (input) { +function validateInput(input) { if (!_.isArray(input.tags)) input.tags = [] - input.tags = input.tags.filter((tag) => _.isString(tag) && tag.trim().length > 0) + input.tags = input.tags.filter( + tag => _.isString(tag) && tag.trim().length > 0 + ) if (!_.isString(input.title)) input.title = '' input.isStarred = !!input.isStarred input.isTrashed = !!input.isTrashed @@ -21,20 +23,24 @@ function validateInput (input) { case 'SNIPPET_NOTE': if (!_.isString(input.description)) input.description = '' if (!_.isArray(input.snippets)) { - input.snippets = [{ - name: '', - mode: 'text', - content: '', - linesHighlighted: [] - }] + input.snippets = [ + { + name: '', + mode: 'text', + content: '', + linesHighlighted: [] + } + ] } break default: - throw new Error('Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.') + throw new Error( + 'Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.' + ) } } -function createNote (storageKey, input) { +function createNote(storageKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -47,13 +53,13 @@ function createNote (storageKey, input) { } return resolveStorageData(targetStorage) - .then(function checkFolderExists (storage) { - if (_.find(storage.folders, {key: input.folder}) == null) { - throw new Error('Target folder doesn\'t exist.') + .then(function checkFolderExists(storage) { + if (_.find(storage.folders, { key: input.folder }) == null) { + throw new Error("Target folder doesn't exist.") } return storage }) - .then(function saveNote (storage) { + .then(function saveNote(storage) { let key = keygen(true) let isUnique = false while (!isUnique) { @@ -68,7 +74,8 @@ function createNote (storageKey, input) { } } } - const noteData = Object.assign({}, + const noteData = Object.assign( + {}, { createdAt: new Date(), updatedAt: new Date() @@ -77,9 +84,13 @@ function createNote (storageKey, input) { { key, storage: storageKey - }) + } + ) - CSON.writeFileSync(path.join(storage.path, 'notes', key + '.cson'), _.omit(noteData, ['key', 'storage'])) + CSON.writeFileSync( + path.join(storage.path, 'notes', key + '.cson'), + _.omit(noteData, ['key', 'storage']) + ) return noteData }) diff --git a/browser/main/lib/dataApi/createNoteFromUrl.js b/browser/main/lib/dataApi/createNoteFromUrl.js index ead93f9e..2fd3bd9d 100644 --- a/browser/main/lib/dataApi/createNoteFromUrl.js +++ b/browser/main/lib/dataApi/createNoteFromUrl.js @@ -6,8 +6,12 @@ const createNote = require('./createNote') import { push } from 'connected-react-router' import ee from 'browser/main/lib/eventEmitter' -function validateUrl (str) { - if (/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(str)) { +function validateUrl(str) { + if ( + /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( + str + ) + ) { return true } else { return false @@ -15,25 +19,33 @@ function validateUrl (str) { } const ERROR_MESSAGES = { - ENOTFOUND: 'URL not found. Please check the URL, or your internet connection and try again.', - VALIDATION_ERROR: 'Please check if the URL follows this format: https://www.google.com', + ENOTFOUND: + 'URL not found. Please check the URL, or your internet connection and try again.', + VALIDATION_ERROR: + 'Please check if the URL follows this format: https://www.google.com', UNEXPECTED: 'Unexpected error! Please check console for details!' } -function createNoteFromUrl (url, storage, folder, dispatch = null, location = null) { +function createNoteFromUrl( + url, + storage, + folder, + dispatch = null, + location = null +) { return new Promise((resolve, reject) => { const td = createTurndownService() if (!validateUrl(url)) { - reject({result: false, error: ERROR_MESSAGES.VALIDATION_ERROR}) + reject({ result: false, error: ERROR_MESSAGES.VALIDATION_ERROR }) } const request = url.startsWith('https') ? https : http - const req = request.request(url, (res) => { + const req = request.request(url, res => { let data = '' - res.on('data', (chunk) => { + res.on('data', chunk => { data += chunk }) @@ -46,20 +58,21 @@ function createNoteFromUrl (url, storage, folder, dispatch = null, location = nu folder: folder, title: '', content: markdownHTML - }) - .then((note) => { + }).then(note => { const noteHash = note.key dispatch({ type: 'UPDATE_NOTE', note: note }) - dispatch(push({ - pathname: location.pathname, - query: {key: noteHash} - })) + dispatch( + push({ + pathname: location.pathname, + query: { key: noteHash } + }) + ) ee.emit('list:jump', noteHash) ee.emit('detail:focus') - resolve({result: true, error: null}) + resolve({ result: true, error: null }) }) } else { createNote(storage, { @@ -67,16 +80,19 @@ function createNoteFromUrl (url, storage, folder, dispatch = null, location = nu folder: folder, title: '', content: markdownHTML - }).then((note) => { - resolve({result: true, note, error: null}) + }).then(note => { + resolve({ result: true, note, error: null }) }) } }) }) - req.on('error', (e) => { + req.on('error', e => { console.error('error in parsing URL', e) - reject({result: false, error: ERROR_MESSAGES[e.code] || ERROR_MESSAGES.UNEXPECTED}) + reject({ + result: false, + error: ERROR_MESSAGES[e.code] || ERROR_MESSAGES.UNEXPECTED + }) }) req.end() diff --git a/browser/main/lib/dataApi/createSnippet.js b/browser/main/lib/dataApi/createSnippet.js index 2e585c9f..48d6705d 100644 --- a/browser/main/lib/dataApi/createSnippet.js +++ b/browser/main/lib/dataApi/createSnippet.js @@ -3,7 +3,7 @@ import crypto from 'crypto' import consts from 'browser/lib/consts' import fetchSnippet from 'browser/main/lib/dataApi/fetchSnippet' -function createSnippet (snippetFile) { +function createSnippet(snippetFile) { return new Promise((resolve, reject) => { const newSnippet = { id: crypto.randomBytes(16).toString('hex'), @@ -12,15 +12,21 @@ function createSnippet (snippetFile) { content: '', linesHighlighted: [] } - fetchSnippet(null, snippetFile).then((snippets) => { - snippets.push(newSnippet) - fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => { - if (err) reject(err) - resolve(newSnippet) + fetchSnippet(null, snippetFile) + .then(snippets => { + snippets.push(newSnippet) + fs.writeFile( + snippetFile || consts.SNIPPET_FILE, + JSON.stringify(snippets, null, 4), + err => { + if (err) reject(err) + resolve(newSnippet) + } + ) + }) + .catch(err => { + reject(err) }) - }).catch((err) => { - reject(err) - }) }) } diff --git a/browser/main/lib/dataApi/deleteFolder.js b/browser/main/lib/dataApi/deleteFolder.js index 5ccc1414..f20078c8 100644 --- a/browser/main/lib/dataApi/deleteFolder.js +++ b/browser/main/lib/dataApi/deleteFolder.js @@ -18,7 +18,7 @@ const deleteSingleNote = require('./deleteNote') * } * ``` */ -function deleteFolder (storageKey, folderKey) { +function deleteFolder(storageKey, folderKey) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -27,35 +27,36 @@ function deleteFolder (storageKey, folderKey) { } return resolveStorageData(targetStorage) - .then(function assignNotes (storage) { - return resolveStorageNotes(storage) - .then((notes) => { - return { - storage, - notes - } - }) + .then(function assignNotes(storage) { + return resolveStorageNotes(storage).then(notes => { + return { + storage, + notes + } + }) }) - .then(function deleteFolderAndNotes (data) { + .then(function deleteFolderAndNotes(data) { const { storage, notes } = data - storage.folders = storage.folders - .filter(function excludeTargetFolder (folder) { - return folder.key !== folderKey - }) + storage.folders = storage.folders.filter(function excludeTargetFolder( + folder + ) { + return folder.key !== folderKey + }) - const targetNotes = notes.filter(function filterTargetNotes (note) { + const targetNotes = notes.filter(function filterTargetNotes(note) { return note.folder === folderKey }) - const deleteAllNotes = targetNotes - .map(function deleteNote (note) { - return deleteSingleNote(storageKey, note.key) - }) - return Promise.all(deleteAllNotes) - .then(() => storage) + const deleteAllNotes = targetNotes.map(function deleteNote(note) { + return deleteSingleNote(storageKey, note.key) + }) + return Promise.all(deleteAllNotes).then(() => storage) }) - .then(function (storage) { - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + .then(function(storage) { + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) return { storage, diff --git a/browser/main/lib/dataApi/deleteNote.js b/browser/main/lib/dataApi/deleteNote.js index 46ec2b55..b40fdfe5 100644 --- a/browser/main/lib/dataApi/deleteNote.js +++ b/browser/main/lib/dataApi/deleteNote.js @@ -4,7 +4,7 @@ const sander = require('sander') const attachmentManagement = require('./attachmentManagement') const { findStorage } = require('browser/lib/findStorage') -function deleteNote (storageKey, noteKey) { +function deleteNote(storageKey, noteKey) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -13,7 +13,7 @@ function deleteNote (storageKey, noteKey) { } return resolveStorageData(targetStorage) - .then(function deleteNoteFile (storage) { + .then(function deleteNoteFile(storage) { const notePath = path.join(storage.path, 'notes', noteKey + '.cson') try { @@ -26,8 +26,11 @@ function deleteNote (storageKey, noteKey) { storageKey } }) - .then(function deleteAttachments (storageInfo) { - attachmentManagement.deleteAttachmentFolder(storageInfo.storageKey, storageInfo.noteKey) + .then(function deleteAttachments(storageInfo) { + attachmentManagement.deleteAttachmentFolder( + storageInfo.storageKey, + storageInfo.noteKey + ) return storageInfo }) } diff --git a/browser/main/lib/dataApi/deleteSnippet.js b/browser/main/lib/dataApi/deleteSnippet.js index 0e446886..bd7b1223 100644 --- a/browser/main/lib/dataApi/deleteSnippet.js +++ b/browser/main/lib/dataApi/deleteSnippet.js @@ -2,14 +2,20 @@ import fs from 'fs' import consts from 'browser/lib/consts' import fetchSnippet from 'browser/main/lib/dataApi/fetchSnippet' -function deleteSnippet (snippet, snippetFile) { +function deleteSnippet(snippet, snippetFile) { return new Promise((resolve, reject) => { - fetchSnippet(null, snippetFile).then((snippets) => { - snippets = snippets.filter(currentSnippet => currentSnippet.id !== snippet.id) - fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => { - if (err) reject(err) - resolve(snippet) - }) + fetchSnippet(null, snippetFile).then(snippets => { + snippets = snippets.filter( + currentSnippet => currentSnippet.id !== snippet.id + ) + fs.writeFile( + snippetFile || consts.SNIPPET_FILE, + JSON.stringify(snippets, null, 4), + err => { + if (err) reject(err) + resolve(snippet) + } + ) }) }) } diff --git a/browser/main/lib/dataApi/exportFolder.js b/browser/main/lib/dataApi/exportFolder.js index 8f15b147..a77ba29b 100644 --- a/browser/main/lib/dataApi/exportFolder.js +++ b/browser/main/lib/dataApi/exportFolder.js @@ -22,7 +22,7 @@ import * as path from 'path' * ``` */ -function exportFolder (storageKey, folderKey, fileType, exportDir) { +function exportFolder(storageKey, folderKey, fileType, exportDir) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -31,24 +31,38 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) { } return resolveStorageData(targetStorage) - .then(function assignNotes (storage) { - return resolveStorageNotes(storage) - .then((notes) => { - return { - storage, - notes - } - }) + .then(function assignNotes(storage) { + return resolveStorageNotes(storage).then(notes => { + return { + storage, + notes + } + }) }) - .then(function exportNotes (data) { + .then(function exportNotes(data) { const { storage, notes } = data - return Promise.all(notes - .filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE') - .map(note => { - const notePath = path.join(exportDir, `${filenamify(note.title, {replacement: '_'})}.${fileType}`) - return exportNote(note.key, storage.path, note.content, notePath, null) - }) + return Promise.all( + notes + .filter( + note => + note.folder === folderKey && + note.isTrashed === false && + note.type === 'MARKDOWN_NOTE' + ) + .map(note => { + const notePath = path.join( + exportDir, + `${filenamify(note.title, { replacement: '_' })}.${fileType}` + ) + return exportNote( + note.key, + storage.path, + note.content, + notePath, + null + ) + }) ).then(() => ({ storage, folderKey, diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js index 42e1fa56..ffd45a1c 100755 --- a/browser/main/lib/dataApi/exportNote.js +++ b/browser/main/lib/dataApi/exportNote.js @@ -19,8 +19,16 @@ const attachmentManagement = require('./attachmentManagement') * @param {function} outputFormatter * @return {Promise.<*[]>} */ -function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatter) { - const storagePath = path.isAbsolute(storageKey) ? storageKey : findStorage(storageKey).path +function exportNote( + nodeKey, + storageKey, + noteContent, + targetPath, + outputFormatter +) { + const storagePath = path.isAbsolute(storageKey) + ? storageKey + : findStorage(storageKey).path const exportTasks = [] if (!storagePath) { @@ -50,18 +58,19 @@ function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatt const tasks = prepareTasks(exportTasks, storagePath, path.dirname(targetPath)) - return Promise.all(tasks.map((task) => copyFile(task.src, task.dst))) - .then(() => exportedData) - .then(data => { - return saveToFile(data, targetPath) - }).catch((err) => { - rollbackExport(tasks) - throw err - }) + return Promise.all(tasks.map(task => copyFile(task.src, task.dst))) + .then(() => exportedData) + .then(data => { + return saveToFile(data, targetPath) + }) + .catch(err => { + rollbackExport(tasks) + throw err + }) } -function prepareTasks (tasks, storagePath, targetPath) { - return tasks.map((task) => { +function prepareTasks(tasks, storagePath, targetPath) { + return tasks.map(task => { if (!path.isAbsolute(task.src)) { task.src = path.join(storagePath, task.src) } @@ -74,9 +83,9 @@ function prepareTasks (tasks, storagePath, targetPath) { }) } -function saveToFile (data, filename) { +function saveToFile(data, filename) { return new Promise((resolve, reject) => { - fs.writeFile(filename, data, (err) => { + fs.writeFile(filename, data, err => { if (err) return reject(err) resolve(filename) @@ -88,9 +97,9 @@ function saveToFile (data, filename) { * Remove exported files * @param tasks Array of copy task objects. Object consists of two mandatory fields – `src` and `dst` */ -function rollbackExport (tasks) { +function rollbackExport(tasks) { const folders = new Set() - tasks.forEach((task) => { + tasks.forEach(task => { let fullpath = task.dst if (!path.extname(task.dst)) { @@ -103,7 +112,7 @@ function rollbackExport (tasks) { } }) - folders.forEach((folder) => { + folders.forEach(folder => { if (fs.readdirSync(folder).length === 0) { fs.rmdir(folder) } diff --git a/browser/main/lib/dataApi/exportStorage.js b/browser/main/lib/dataApi/exportStorage.js index ce2c4573..2a7c725c 100644 --- a/browser/main/lib/dataApi/exportStorage.js +++ b/browser/main/lib/dataApi/exportStorage.js @@ -20,7 +20,7 @@ import * as fs from 'fs' * ``` */ -function exportStorage (storageKey, fileType, exportDir) { +function exportStorage(storageKey, fileType, exportDir) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -29,14 +29,17 @@ function exportStorage (storageKey, fileType, exportDir) { } return resolveStorageData(targetStorage) - .then(storage => ( - resolveStorageNotes(storage).then(notes => ({storage, notes})) - )) - .then(function exportNotes (data) { + .then(storage => + resolveStorageNotes(storage).then(notes => ({ storage, notes })) + ) + .then(function exportNotes(data) { const { storage, notes } = data const folderNamesMapping = {} storage.folders.forEach(folder => { - const folderExportedDir = path.join(exportDir, filenamify(folder.name, {replacement: '_'})) + const folderExportedDir = path.join( + exportDir, + filenamify(folder.name, { replacement: '_' }) + ) folderNamesMapping[folder.key] = folderExportedDir // make sure directory exists try { @@ -47,7 +50,9 @@ function exportStorage (storageKey, fileType, exportDir) { .filter(note => !note.isTrashed && note.type === 'MARKDOWN_NOTE') .forEach(markdownNote => { const folderExportedDir = folderNamesMapping[markdownNote.folder] - const snippetName = `${filenamify(markdownNote.title, {replacement: '_'})}.${fileType}` + const snippetName = `${filenamify(markdownNote.title, { + replacement: '_' + })}.${fileType}` const notePath = path.join(folderExportedDir, snippetName) fs.writeFileSync(notePath, markdownNote.content) }) diff --git a/browser/main/lib/dataApi/fetchSnippet.js b/browser/main/lib/dataApi/fetchSnippet.js index 456a5090..8344eb5d 100644 --- a/browser/main/lib/dataApi/fetchSnippet.js +++ b/browser/main/lib/dataApi/fetchSnippet.js @@ -1,7 +1,7 @@ import fs from 'fs' import consts from 'browser/lib/consts' -function fetchSnippet (id, snippetFile) { +function fetchSnippet(id, snippetFile) { return new Promise((resolve, reject) => { fs.readFile(snippetFile || consts.SNIPPET_FILE, 'utf8', (err, data) => { if (err) { @@ -9,7 +9,9 @@ function fetchSnippet (id, snippetFile) { } const snippets = JSON.parse(data) if (id) { - const snippet = snippets.find(snippet => { return snippet.id === id }) + const snippet = snippets.find(snippet => { + return snippet.id === id + }) resolve(snippet) } resolve(snippets) diff --git a/browser/main/lib/dataApi/init.js b/browser/main/lib/dataApi/init.js index 0dbcc182..0ad35625 100644 --- a/browser/main/lib/dataApi/init.js +++ b/browser/main/lib/dataApi/init.js @@ -21,8 +21,8 @@ const CSON = require('@rokt33r/season') * 3. empty directory */ -function init () { - const fetchStorages = function () { +function init() { + const fetchStorages = function() { let rawStorages try { rawStorages = JSON.parse(window.localStorage.getItem('storages')) @@ -34,44 +34,50 @@ function init () { rawStorages = [] window.localStorage.setItem('storages', JSON.stringify(rawStorages)) } - return Promise.all(rawStorages - .map(resolveStorageData)) + return Promise.all(rawStorages.map(resolveStorageData)) } - const fetchNotes = function (storages) { + const fetchNotes = function(storages) { const findNotesFromEachStorage = storages - .filter(storage => fs.existsSync(storage.path)) - .map((storage) => { - return resolveStorageNotes(storage) - .then((notes) => { - let unknownCount = 0 - notes.forEach((note) => { - if (note && !storage.folders.some((folder) => note.folder === folder.key)) { - unknownCount++ - storage.folders.push({ - key: note.folder, - color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], - name: 'Unknown ' + unknownCount - }) - } - }) - if (unknownCount > 0) { - try { - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) - } catch (e) { - console.log('Error writting boostnote.json: ' + e + ' from init.js') - } + .filter(storage => fs.existsSync(storage.path)) + .map(storage => { + return resolveStorageNotes(storage).then(notes => { + let unknownCount = 0 + notes.forEach(note => { + if ( + note && + !storage.folders.some(folder => note.folder === folder.key) + ) { + unknownCount++ + storage.folders.push({ + key: note.folder, + color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], + name: 'Unknown ' + unknownCount + }) } - return notes }) + if (unknownCount > 0) { + try { + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) + } catch (e) { + console.log( + 'Error writting boostnote.json: ' + e + ' from init.js' + ) + } + } + return notes + }) }) return Promise.all(findNotesFromEachStorage) - .then(function concatNoteGroup (noteGroups) { - return noteGroups.reduce(function (sum, group) { + .then(function concatNoteGroup(noteGroups) { + return noteGroups.reduce(function(sum, group) { return sum.concat(group) }, []) }) - .then(function returnData (notes) { + .then(function returnData(notes) { return { storages, notes @@ -80,12 +86,11 @@ function init () { } return Promise.resolve(fetchStorages()) - .then((storages) => { - return storages - .filter((storage) => { - if (!_.isObject(storage)) return false - return true - }) + .then(storages => { + return storages.filter(storage => { + if (!_.isObject(storage)) return false + return true + }) }) .then(fetchNotes) } diff --git a/browser/main/lib/dataApi/migrateFromV5Storage.js b/browser/main/lib/dataApi/migrateFromV5Storage.js index 78d78746..abf57831 100644 --- a/browser/main/lib/dataApi/migrateFromV5Storage.js +++ b/browser/main/lib/dataApi/migrateFromV5Storage.js @@ -6,102 +6,111 @@ const CSON = require('@rokt33r/season') const path = require('path') const sander = require('sander') -function migrateFromV5Storage (storageKey, data) { +function migrateFromV5Storage(storageKey, data) { let targetStorage try { const cachedStorageList = JSON.parse(localStorage.getItem('storages')) - if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.') + if (!_.isArray(cachedStorageList)) + throw new Error("Target storage doesn't exist.") - targetStorage = _.find(cachedStorageList, {key: storageKey}) - if (targetStorage == null) throw new Error('Target storage doesn\'t exist.') + targetStorage = _.find(cachedStorageList, { key: storageKey }) + if (targetStorage == null) throw new Error("Target storage doesn't exist.") } catch (e) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function (storage) { - return importAll(storage, data) - }) + return resolveStorageData(targetStorage).then(function(storage) { + return importAll(storage, data) + }) } -function importAll (storage, data) { +function importAll(storage, data) { const oldArticles = data.articles const notes = [] - data.folders - .forEach(function (oldFolder) { - let folderKey = keygen() - while (storage.folders.some((folder) => folder.key === folderKey)) { - folderKey = keygen() - } - const newFolder = { - key: folderKey, - name: oldFolder.name, - color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] - } + data.folders.forEach(function(oldFolder) { + let folderKey = keygen() + while (storage.folders.some(folder => folder.key === folderKey)) { + folderKey = keygen() + } + const newFolder = { + key: folderKey, + name: oldFolder.name, + color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] + } - storage.folders.push(newFolder) + storage.folders.push(newFolder) - const articles = oldArticles.filter((article) => article.FolderKey === oldFolder.key) - articles.forEach((article) => { - let noteKey = keygen() - let isUnique = false - while (!isUnique) { - try { - sander.statSync(path.join(storage.path, 'notes', noteKey + '.cson')) - noteKey = keygen() - } catch (err) { - if (err.code === 'ENOENT') { - isUnique = true - } else { - console.error('Failed to read `notes` directory.') - throw err - } + const articles = oldArticles.filter( + article => article.FolderKey === oldFolder.key + ) + articles.forEach(article => { + let noteKey = keygen() + let isUnique = false + while (!isUnique) { + try { + sander.statSync(path.join(storage.path, 'notes', noteKey + '.cson')) + noteKey = keygen() + } catch (err) { + if (err.code === 'ENOENT') { + isUnique = true + } else { + console.error('Failed to read `notes` directory.') + throw err } } + } - if (article.mode === 'markdown') { - const newNote = { - tags: article.tags, - createdAt: article.createdAt, - updatedAt: article.updatedAt, - folder: folderKey, - storage: storage.key, - type: 'MARKDOWN_NOTE', - isStarred: false, - title: article.title, - content: '# ' + article.title + '\n\n' + article.content, - key: noteKey, - linesHighlighted: article.linesHighlighted - } - notes.push(newNote) - } else { - const newNote = { - tags: article.tags, - createdAt: article.createdAt, - updatedAt: article.updatedAt, - folder: folderKey, - storage: storage.key, - type: 'SNIPPET_NOTE', - isStarred: false, - title: article.title, - description: article.title, - key: noteKey, - snippets: [{ + if (article.mode === 'markdown') { + const newNote = { + tags: article.tags, + createdAt: article.createdAt, + updatedAt: article.updatedAt, + folder: folderKey, + storage: storage.key, + type: 'MARKDOWN_NOTE', + isStarred: false, + title: article.title, + content: '# ' + article.title + '\n\n' + article.content, + key: noteKey, + linesHighlighted: article.linesHighlighted + } + notes.push(newNote) + } else { + const newNote = { + tags: article.tags, + createdAt: article.createdAt, + updatedAt: article.updatedAt, + folder: folderKey, + storage: storage.key, + type: 'SNIPPET_NOTE', + isStarred: false, + title: article.title, + description: article.title, + key: noteKey, + snippets: [ + { name: article.mode, mode: article.mode, content: article.content, linesHighlighted: article.linesHighlighted - }] - } - notes.push(newNote) + } + ] } - }) + notes.push(newNote) + } }) - - notes.forEach(function (note) { - CSON.writeFileSync(path.join(storage.path, 'notes', note.key + '.cson'), _.omit(note, ['storage', 'key'])) }) - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['version', 'folders'])) + notes.forEach(function(note) { + CSON.writeFileSync( + path.join(storage.path, 'notes', note.key + '.cson'), + _.omit(note, ['storage', 'key']) + ) + }) + + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['version', 'folders']) + ) return { storage, diff --git a/browser/main/lib/dataApi/migrateFromV6Storage.js b/browser/main/lib/dataApi/migrateFromV6Storage.js index af4d902f..8255a0de 100644 --- a/browser/main/lib/dataApi/migrateFromV6Storage.js +++ b/browser/main/lib/dataApi/migrateFromV6Storage.js @@ -4,86 +4,91 @@ const keygen = require('browser/lib/keygen') const _ = require('lodash') const CSON = require('@rokt33r/season') -function migrateFromV5Storage (storagePath) { +function migrateFromV5Storage(storagePath) { var boostnoteJSONPath = path.join(storagePath, 'boostnote.json') return Promise.resolve() - .then(function readBoostnoteJSON () { + .then(function readBoostnoteJSON() { return sander.readFile(boostnoteJSONPath, { encoding: 'utf-8' }) }) - .then(function verifyVersion (rawData) { + .then(function verifyVersion(rawData) { var boostnoteJSONData = JSON.parse(rawData) - if (boostnoteJSONData.version === '1.0') throw new Error('Target storage seems to be transformed already.') - if (!_.isArray(boostnoteJSONData.folders)) throw new Error('the value of folders is not an array.') + if (boostnoteJSONData.version === '1.0') + throw new Error('Target storage seems to be transformed already.') + if (!_.isArray(boostnoteJSONData.folders)) + throw new Error('the value of folders is not an array.') return boostnoteJSONData }) - .then(function setVersion (boostnoteJSONData) { + .then(function setVersion(boostnoteJSONData) { boostnoteJSONData.version = '1.0' - return sander.writeFile(boostnoteJSONPath, JSON.stringify(boostnoteJSONData)) + return sander + .writeFile(boostnoteJSONPath, JSON.stringify(boostnoteJSONData)) .then(() => boostnoteJSONData) }) - .then(function fetchNotes (boostnoteJSONData) { - var fetchNotesFromEachFolder = boostnoteJSONData.folders - .map(function (folder) { - const folderDataJSONPath = path.join(storagePath, folder.key, 'data.json') - return sander - .readFile(folderDataJSONPath, { - encoding: 'utf-8' + .then(function fetchNotes(boostnoteJSONData) { + var fetchNotesFromEachFolder = boostnoteJSONData.folders.map(function( + folder + ) { + const folderDataJSONPath = path.join( + storagePath, + folder.key, + 'data.json' + ) + return sander + .readFile(folderDataJSONPath, { + encoding: 'utf-8' + }) + .then(function(rawData) { + var data = JSON.parse(rawData) + if (!_.isArray(data.notes)) + throw new Error('value of notes is not an array.') + return data.notes.map(function setFolderToNote(note) { + note.folder = folder.key + return note }) - .then(function (rawData) { - var data = JSON.parse(rawData) - if (!_.isArray(data.notes)) throw new Error('value of notes is not an array.') - return data.notes - .map(function setFolderToNote (note) { - note.folder = folder.key - return note - }) - }) - .catch(function failedToReadDataJSON (err) { - console.warn('Failed to fetch notes from ', folderDataJSONPath, err) - return [] - }) - }) + }) + .catch(function failedToReadDataJSON(err) { + console.warn('Failed to fetch notes from ', folderDataJSONPath, err) + return [] + }) + }) return Promise.all(fetchNotesFromEachFolder) - .then(function flatten (folderNotes) { - return folderNotes - .reduce(function concatNotes (sum, notes) { - return sum.concat(notes) - }, []) + .then(function flatten(folderNotes) { + return folderNotes.reduce(function concatNotes(sum, notes) { + return sum.concat(notes) + }, []) }) - .then(function saveNotes (notes) { - notes.forEach(function renewKey (note) { + .then(function saveNotes(notes) { + notes.forEach(function renewKey(note) { var newKey = keygen() - while (notes.some((_note) => _note.key === newKey)) { + while (notes.some(_note => _note.key === newKey)) { newKey = keygen() } note.key = newKey }) const noteDirPath = path.join(storagePath, 'notes') - notes - .map(function saveNote (note) { - CSON.writeFileSync(path.join(noteDirPath, note.key) + '.cson', note) - }) + notes.map(function saveNote(note) { + CSON.writeFileSync(path.join(noteDirPath, note.key) + '.cson', note) + }) return true }) - .then(function deleteFolderDir (check) { + .then(function deleteFolderDir(check) { if (check) { - boostnoteJSONData.folders.forEach((folder) => { + boostnoteJSONData.folders.forEach(folder => { sander.rimrafSync(path.join(storagePath, folder.key)) }) } return check }) }) - .catch(function handleError (err) { + .catch(function handleError(err) { console.warn(err) return false }) } module.exports = migrateFromV5Storage - diff --git a/browser/main/lib/dataApi/moveNote.js b/browser/main/lib/dataApi/moveNote.js index c38968cb..b72db7aa 100644 --- a/browser/main/lib/dataApi/moveNote.js +++ b/browser/main/lib/dataApi/moveNote.js @@ -7,90 +7,104 @@ const sander = require('sander') const { findStorage } = require('browser/lib/findStorage') const attachmentManagement = require('./attachmentManagement') -function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) { +function moveNote(storageKey, noteKey, newStorageKey, newFolderKey) { let oldStorage, newStorage try { oldStorage = findStorage(storageKey) newStorage = findStorage(newStorageKey) - if (newStorage == null) throw new Error('Target storage doesn\'t exist.') + if (newStorage == null) throw new Error("Target storage doesn't exist.") } catch (e) { return Promise.reject(e) } - return resolveStorageData(oldStorage) - .then(function saveNote (_oldStorage) { - oldStorage = _oldStorage - let noteData - const notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson') - try { - noteData = CSON.readFileSync(notePath) - } catch (err) { - console.warn('Failed to find note cson', err) - throw err - } - let newNoteKey - return Promise.resolve() - .then(function resolveNewStorage () { - if (storageKey === newStorageKey) { - newNoteKey = noteKey - return oldStorage - } - return resolveStorageData(newStorage) - .then(function findNewNoteKey (_newStorage) { - newStorage = _newStorage - newNoteKey = keygen(true) - let isUnique = false - while (!isUnique) { - try { - sander.statSync(path.join(newStorage.path, 'notes', newNoteKey + '.cson')) - newNoteKey = keygen(true) - } catch (err) { - if (err.code === 'ENOENT') { - isUnique = true - } else { - throw err - } - } - } - - return newStorage - }) - }) - .then(function checkFolderExistsAndPrepareNoteData (newStorage) { - if (_.find(newStorage.folders, {key: newFolderKey}) == null) throw new Error('Target folder doesn\'t exist.') - - noteData.folder = newFolderKey - noteData.key = newNoteKey - noteData.storage = newStorageKey - noteData.updatedAt = new Date() - noteData.oldContent = noteData.content - - return noteData - }) - .then(function moveAttachments (noteData) { - if (oldStorage.path === newStorage.path) { - return noteData - } - - noteData.content = attachmentManagement.moveAttachments(oldStorage.path, newStorage.path, noteKey, newNoteKey, noteData.content) - return noteData - }) - .then(function writeAndReturn (noteData) { - CSON.writeFileSync(path.join(newStorage.path, 'notes', noteData.key + '.cson'), _.omit(noteData, ['key', 'storage', 'oldContent'])) - return noteData - }) - .then(function deleteOldNote (data) { - if (storageKey !== newStorageKey) { + return resolveStorageData(oldStorage).then(function saveNote(_oldStorage) { + oldStorage = _oldStorage + let noteData + const notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson') + try { + noteData = CSON.readFileSync(notePath) + } catch (err) { + console.warn('Failed to find note cson', err) + throw err + } + let newNoteKey + return Promise.resolve() + .then(function resolveNewStorage() { + if (storageKey === newStorageKey) { + newNoteKey = noteKey + return oldStorage + } + return resolveStorageData(newStorage).then(function findNewNoteKey( + _newStorage + ) { + newStorage = _newStorage + newNoteKey = keygen(true) + let isUnique = false + while (!isUnique) { try { - sander.unlinkSync(path.join(oldStorage.path, 'notes', noteKey + '.cson')) + sander.statSync( + path.join(newStorage.path, 'notes', newNoteKey + '.cson') + ) + newNoteKey = keygen(true) } catch (err) { - console.warn(err) + if (err.code === 'ENOENT') { + isUnique = true + } else { + throw err + } } } - return data + return newStorage }) - }) + }) + .then(function checkFolderExistsAndPrepareNoteData(newStorage) { + if (_.find(newStorage.folders, { key: newFolderKey }) == null) + throw new Error("Target folder doesn't exist.") + + noteData.folder = newFolderKey + noteData.key = newNoteKey + noteData.storage = newStorageKey + noteData.updatedAt = new Date() + noteData.oldContent = noteData.content + + return noteData + }) + .then(function moveAttachments(noteData) { + if (oldStorage.path === newStorage.path) { + return noteData + } + + noteData.content = attachmentManagement.moveAttachments( + oldStorage.path, + newStorage.path, + noteKey, + newNoteKey, + noteData.content + ) + return noteData + }) + .then(function writeAndReturn(noteData) { + CSON.writeFileSync( + path.join(newStorage.path, 'notes', noteData.key + '.cson'), + _.omit(noteData, ['key', 'storage', 'oldContent']) + ) + return noteData + }) + .then(function deleteOldNote(data) { + if (storageKey !== newStorageKey) { + try { + sander.unlinkSync( + path.join(oldStorage.path, 'notes', noteKey + '.cson') + ) + } catch (err) { + console.warn(err) + } + } + + return data + }) + }) } module.exports = moveNote diff --git a/browser/main/lib/dataApi/removeStorage.js b/browser/main/lib/dataApi/removeStorage.js index c50bbd12..3f957ab2 100644 --- a/browser/main/lib/dataApi/removeStorage.js +++ b/browser/main/lib/dataApi/removeStorage.js @@ -4,7 +4,7 @@ const _ = require('lodash') * @param {String} key * @return {key} */ -function removeStorage (key) { +function removeStorage(key) { let rawStorages try { @@ -15,10 +15,9 @@ function removeStorage (key) { rawStorages = [] } - rawStorages = rawStorages - .filter(function excludeTargetStorage (rawStorage) { - return rawStorage.key !== key - }) + rawStorages = rawStorages.filter(function excludeTargetStorage(rawStorage) { + return rawStorage.key !== key + }) localStorage.setItem('storages', JSON.stringify(rawStorages)) diff --git a/browser/main/lib/dataApi/renameStorage.js b/browser/main/lib/dataApi/renameStorage.js index 3b806d1c..165a5ab3 100644 --- a/browser/main/lib/dataApi/renameStorage.js +++ b/browser/main/lib/dataApi/renameStorage.js @@ -6,8 +6,9 @@ const resolveStorageData = require('./resolveStorageData') * @param {String} name * @return {Object} Storage meta data */ -function renameStorage (key, name) { - if (!_.isString(name)) return Promise.reject(new Error('Name must be a string.')) +function renameStorage(key, name) { + if (!_.isString(name)) + return Promise.reject(new Error('Name must be a string.')) let cachedStorageList try { @@ -17,7 +18,7 @@ function renameStorage (key, name) { console.error(err) return Promise.reject(err) } - const targetStorage = _.find(cachedStorageList, {key: key}) + const targetStorage = _.find(cachedStorageList, { key: key }) if (targetStorage == null) return Promise.reject('Storage') targetStorage.name = name diff --git a/browser/main/lib/dataApi/reorderFolder.js b/browser/main/lib/dataApi/reorderFolder.js index 9102438e..e86ac838 100644 --- a/browser/main/lib/dataApi/reorderFolder.js +++ b/browser/main/lib/dataApi/reorderFolder.js @@ -17,7 +17,7 @@ const { findStorage } = require('browser/lib/findStorage') * } * ``` */ -function reorderFolder (storageKey, oldIndex, newIndex) { +function reorderFolder(storageKey, oldIndex, newIndex) { let targetStorage try { if (!_.isNumber(oldIndex)) throw new Error('oldIndex must be a number.') @@ -28,15 +28,19 @@ function reorderFolder (storageKey, oldIndex, newIndex) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function reorderFolder (storage) { - storage.folders = _.move(storage.folders, oldIndex, newIndex) - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + return resolveStorageData(targetStorage).then(function reorderFolder( + storage + ) { + storage.folders = _.move(storage.folders, oldIndex, newIndex) + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) - return { - storage - } - }) + return { + storage + } + }) } module.exports = reorderFolder diff --git a/browser/main/lib/dataApi/resolveStorageData.js b/browser/main/lib/dataApi/resolveStorageData.js index da41f3d0..3476ec7d 100644 --- a/browser/main/lib/dataApi/resolveStorageData.js +++ b/browser/main/lib/dataApi/resolveStorageData.js @@ -3,7 +3,7 @@ const path = require('path') const CSON = require('@rokt33r/season') const migrateFromV6Storage = require('./migrateFromV6Storage') -function resolveStorageData (storageCache) { +function resolveStorageData(storageCache) { const storage = { key: storageCache.key, name: storageCache.name, @@ -15,13 +15,14 @@ function resolveStorageData (storageCache) { const boostnoteJSONPath = path.join(storageCache.path, 'boostnote.json') try { const jsonData = CSON.readFileSync(boostnoteJSONPath) - if (!_.isArray(jsonData.folders)) throw new Error('folders should be an array.') + if (!_.isArray(jsonData.folders)) + throw new Error('folders should be an array.') storage.folders = jsonData.folders storage.version = jsonData.version } catch (err) { if (err.code === 'ENOENT') { - console.warn('boostnote.json file doesn\'t exist the given path') - CSON.writeFileSync(boostnoteJSONPath, {folders: [], version: '1.0'}) + console.warn("boostnote.json file doesn't exist the given path") + CSON.writeFileSync(boostnoteJSONPath, { folders: [], version: '1.0' }) } else { console.error(err) } @@ -34,8 +35,7 @@ function resolveStorageData (storageCache) { return Promise.resolve(storage) } - return migrateFromV6Storage(storage.path) - .then(() => storage) + return migrateFromV6Storage(storage.path).then(() => storage) } module.exports = resolveStorageData diff --git a/browser/main/lib/dataApi/resolveStorageNotes.js b/browser/main/lib/dataApi/resolveStorageNotes.js index 9da27248..e86ecc26 100644 --- a/browser/main/lib/dataApi/resolveStorageNotes.js +++ b/browser/main/lib/dataApi/resolveStorageNotes.js @@ -2,14 +2,14 @@ const sander = require('sander') const path = require('path') const CSON = require('@rokt33r/season') -function resolveStorageNotes (storage) { +function resolveStorageNotes(storage) { const notesDirPath = path.join(storage.path, 'notes') let notePathList try { notePathList = sander.readdirSync(notesDirPath) } catch (err) { if (err.code === 'ENOENT') { - console.error(notesDirPath, ' doesn\'t exist.') + console.error(notesDirPath, " doesn't exist.") sander.mkdirSync(notesDirPath) } else { console.warn('Failed to find note dir', notesDirPath, err) @@ -17,10 +17,10 @@ function resolveStorageNotes (storage) { notePathList = [] } const notes = notePathList - .filter(function filterOnlyCSONFile (notePath) { + .filter(function filterOnlyCSONFile(notePath) { return /\.cson$/.test(notePath) }) - .map(function parseCSONFile (notePath) { + .map(function parseCSONFile(notePath) { try { const data = CSON.readFileSync(path.join(notesDirPath, notePath)) data.key = path.basename(notePath, '.cson') @@ -30,7 +30,7 @@ function resolveStorageNotes (storage) { console.error(`error on note path: ${notePath}, error: ${err}`) } }) - .filter(function filterOnlyNoteObject (noteObj) { + .filter(function filterOnlyNoteObject(noteObj) { return typeof noteObj === 'object' }) diff --git a/browser/main/lib/dataApi/toggleStorage.js b/browser/main/lib/dataApi/toggleStorage.js index 246d85ef..013c15d4 100644 --- a/browser/main/lib/dataApi/toggleStorage.js +++ b/browser/main/lib/dataApi/toggleStorage.js @@ -6,7 +6,7 @@ const resolveStorageData = require('./resolveStorageData') * @param {Boolean} isOpen * @return {Object} Storage meta data */ -function toggleStorage (key, isOpen) { +function toggleStorage(key, isOpen) { let cachedStorageList try { cachedStorageList = JSON.parse(localStorage.getItem('storages')) @@ -15,7 +15,7 @@ function toggleStorage (key, isOpen) { console.error(err) return Promise.reject(err) } - const targetStorage = _.find(cachedStorageList, {key: key}) + const targetStorage = _.find(cachedStorageList, { key: key }) if (targetStorage == null) return Promise.reject('Storage') targetStorage.isOpen = isOpen diff --git a/browser/main/lib/dataApi/updateFolder.js b/browser/main/lib/dataApi/updateFolder.js index 2a325c60..84658ffc 100644 --- a/browser/main/lib/dataApi/updateFolder.js +++ b/browser/main/lib/dataApi/updateFolder.js @@ -22,7 +22,7 @@ const { findStorage } = require('browser/lib/findStorage') * } * ``` */ -function updateFolder (storageKey, folderKey, input) { +function updateFolder(storageKey, folderKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -34,19 +34,21 @@ function updateFolder (storageKey, folderKey, input) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function updateFolder (storage) { - const targetFolder = _.find(storage.folders, {key: folderKey}) - if (targetFolder == null) throw new Error('Target folder doesn\'t exist.') - targetFolder.name = input.name - targetFolder.color = input.color + return resolveStorageData(targetStorage).then(function updateFolder(storage) { + const targetFolder = _.find(storage.folders, { key: folderKey }) + if (targetFolder == null) throw new Error("Target folder doesn't exist.") + targetFolder.name = input.name + targetFolder.color = input.color - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) - return { - storage - } - }) + return { + storage + } + }) } module.exports = updateFolder diff --git a/browser/main/lib/dataApi/updateNote.js b/browser/main/lib/dataApi/updateNote.js index ce9fabcf..775888a6 100644 --- a/browser/main/lib/dataApi/updateNote.js +++ b/browser/main/lib/dataApi/updateNote.js @@ -4,13 +4,14 @@ const path = require('path') const CSON = require('@rokt33r/season') const { findStorage } = require('browser/lib/findStorage') -function validateInput (input) { +function validateInput(input) { const validatedInput = {} if (input.tags != null) { if (!_.isArray(input.tags)) validatedInput.tags = [] - validatedInput.tags = input.tags - .filter((tag) => _.isString(tag) && tag.trim().length > 0) + validatedInput.tags = input.tags.filter( + tag => _.isString(tag) && tag.trim().length > 0 + ) } if (input.title != null) { @@ -40,7 +41,8 @@ function validateInput (input) { if (!_.isString(input.content)) validatedInput.content = '' else validatedInput.content = input.content - if (!_.isArray(input.linesHighlighted)) validatedInput.linesHighlighted = [] + if (!_.isArray(input.linesHighlighted)) + validatedInput.linesHighlighted = [] else validatedInput.linesHighlighted = input.linesHighlighted } return validatedInput @@ -51,30 +53,33 @@ function validateInput (input) { } if (input.snippets != null) { if (!_.isArray(input.snippets)) { - validatedInput.snippets = [{ - name: '', - mode: 'text', - content: '', - linesHighlighted: [] - }] + validatedInput.snippets = [ + { + name: '', + mode: 'text', + content: '', + linesHighlighted: [] + } + ] } else { validatedInput.snippets = input.snippets } - validatedInput.snippets - .filter((snippet) => { - if (!_.isString(snippet.name)) return false - if (!_.isString(snippet.mode)) return false - if (!_.isString(snippet.content)) return false - return true - }) + validatedInput.snippets.filter(snippet => { + if (!_.isString(snippet.name)) return false + if (!_.isString(snippet.mode)) return false + if (!_.isString(snippet.content)) return false + return true + }) } return validatedInput default: - throw new Error('Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.') + throw new Error( + 'Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.' + ) } } -function updateNote (storageKey, noteKey, input) { +function updateNote(storageKey, noteKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -85,55 +90,61 @@ function updateNote (storageKey, noteKey, input) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function saveNote (storage) { - let noteData - const notePath = path.join(storage.path, 'notes', noteKey + '.cson') - try { - noteData = CSON.readFileSync(notePath) - } catch (err) { - console.warn('Failed to find note cson', err) - noteData = input.type === 'SNIPPET_NOTE' + return resolveStorageData(targetStorage).then(function saveNote(storage) { + let noteData + const notePath = path.join(storage.path, 'notes', noteKey + '.cson') + try { + noteData = CSON.readFileSync(notePath) + } catch (err) { + console.warn('Failed to find note cson', err) + noteData = + input.type === 'SNIPPET_NOTE' ? { - type: 'SNIPPET_NOTE', - description: [], - snippets: [{ - name: '', - mode: 'text', + type: 'SNIPPET_NOTE', + description: [], + snippets: [ + { + name: '', + mode: 'text', + content: '', + linesHighlighted: [] + } + ] + } + : { + type: 'MARKDOWN_NOTE', content: '', linesHighlighted: [] - }] - } - : { - type: 'MARKDOWN_NOTE', - content: '', - linesHighlighted: [] - } - noteData.title = '' - if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.') - noteData.folder = storage.folders[0].key - noteData.createdAt = new Date() - noteData.updatedAt = new Date() - noteData.isStarred = false - noteData.isTrashed = false - noteData.tags = [] - noteData.isPinned = false - } + } + noteData.title = '' + if (storage.folders.length === 0) + throw new Error('Failed to restore note: No folder exists.') + noteData.folder = storage.folders[0].key + noteData.createdAt = new Date() + noteData.updatedAt = new Date() + noteData.isStarred = false + noteData.isTrashed = false + noteData.tags = [] + noteData.isPinned = false + } - if (noteData.type === 'SNIPPET_NOTE') { - noteData.title - } + if (noteData.type === 'SNIPPET_NOTE') { + noteData.title + } - Object.assign(noteData, input, { - key: noteKey, - updatedAt: new Date(), - storage: storageKey - }) - - CSON.writeFileSync(path.join(storage.path, 'notes', noteKey + '.cson'), _.omit(noteData, ['key', 'storage'])) - - return noteData + Object.assign(noteData, input, { + key: noteKey, + updatedAt: new Date(), + storage: storageKey }) + + CSON.writeFileSync( + path.join(storage.path, 'notes', noteKey + '.cson'), + _.omit(noteData, ['key', 'storage']) + ) + + return noteData + }) } module.exports = updateNote diff --git a/browser/main/lib/dataApi/updateSnippet.js b/browser/main/lib/dataApi/updateSnippet.js index f132d83f..95acf053 100644 --- a/browser/main/lib/dataApi/updateSnippet.js +++ b/browser/main/lib/dataApi/updateSnippet.js @@ -1,9 +1,11 @@ import fs from 'fs' import consts from 'browser/lib/consts' -function updateSnippet (snippet, snippetFile) { +function updateSnippet(snippet, snippetFile) { return new Promise((resolve, reject) => { - const snippets = JSON.parse(fs.readFileSync(snippetFile || consts.SNIPPET_FILE, 'utf-8')) + const snippets = JSON.parse( + fs.readFileSync(snippetFile || consts.SNIPPET_FILE, 'utf-8') + ) for (let i = 0; i < snippets.length; i++) { const currentSnippet = snippets[i] @@ -21,11 +23,15 @@ function updateSnippet (snippet, snippetFile) { currentSnippet.name = snippet.name currentSnippet.prefix = snippet.prefix currentSnippet.content = snippet.content - currentSnippet.linesHighlighted = (snippet.linesHighlighted) - fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => { - if (err) reject(err) - resolve(snippets) - }) + currentSnippet.linesHighlighted = snippet.linesHighlighted + fs.writeFile( + snippetFile || consts.SNIPPET_FILE, + JSON.stringify(snippets, null, 4), + err => { + if (err) reject(err) + resolve(snippets) + } + ) } } } diff --git a/browser/main/lib/eventEmitter.js b/browser/main/lib/eventEmitter.js index 1276545b..370ea3a9 100644 --- a/browser/main/lib/eventEmitter.js +++ b/browser/main/lib/eventEmitter.js @@ -1,19 +1,19 @@ const electron = require('electron') const { ipcRenderer, remote } = electron -function on (name, listener) { +function on(name, listener) { ipcRenderer.on(name, listener) } -function off (name, listener) { +function off(name, listener) { ipcRenderer.removeListener(name, listener) } -function once (name, listener) { +function once(name, listener) { ipcRenderer.once(name, listener) } -function emit (name, ...args) { +function emit(name, ...args) { remote.getCurrentWindow().webContents.send(name, ...args) } diff --git a/browser/main/lib/ipcClient.js b/browser/main/lib/ipcClient.js index c06296b5..4c25d52c 100644 --- a/browser/main/lib/ipcClient.js +++ b/browser/main/lib/ipcClient.js @@ -12,14 +12,14 @@ nodeIpc.config.silent = true nodeIpc.connectTo( 'node', path.join(app.getPath('userData'), 'boostnote.service'), - function () { - nodeIpc.of.node.on('error', function (err) { + function() { + nodeIpc.of.node.on('error', function(err) { console.error(err) }) - nodeIpc.of.node.on('connect', function () { - ipcRenderer.send('config-renew', {config: ConfigManager.get()}) + nodeIpc.of.node.on('connect', function() { + ipcRenderer.send('config-renew', { config: ConfigManager.get() }) }) - nodeIpc.of.node.on('disconnect', function () { + nodeIpc.of.node.on('disconnect', function() { return }) } diff --git a/browser/main/lib/modal.js b/browser/main/lib/modal.js index 955cb5c8..08d8c7f1 100644 --- a/browser/main/lib/modal.js +++ b/browser/main/lib/modal.js @@ -4,7 +4,7 @@ import ReactDOM from 'react-dom' import { store } from '../store' class ModalBase extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { component: null, @@ -13,20 +13,30 @@ class ModalBase extends React.Component { } } - close () { - if (modalBase != null) modalBase.setState({component: null, componentProps: null, isHidden: true}) + close() { + if (modalBase != null) + modalBase.setState({ + component: null, + componentProps: null, + isHidden: true + }) // Toggle overflow style on NoteList - const list = document.querySelector('.NoteList__list___browser-main-NoteList-') + const list = document.querySelector( + '.NoteList__list___browser-main-NoteList-' + ) list.style.overflow = 'auto' } - render () { + render() { return (
      -
      this.close(e)} className='modalBack' /> +
      this.close(e)} className='modalBack' /> {this.state.component == null ? null : ( - + )}
      @@ -38,21 +48,31 @@ const el = document.createElement('div') document.body.appendChild(el) const modalBase = ReactDOM.render(, el) -export function openModal (component, props) { - if (modalBase == null) { return } +export function openModal(component, props) { + if (modalBase == null) { + return + } // Hide scrollbar by removing overflow when modal opens - const list = document.querySelector('.NoteList__list___browser-main-NoteList-') + const list = document.querySelector( + '.NoteList__list___browser-main-NoteList-' + ) list.style.overflow = 'hidden' document.body.setAttribute('data-modal', 'open') - modalBase.setState({component: component, componentProps: props, isHidden: false}) + modalBase.setState({ + component: component, + componentProps: props, + isHidden: false + }) } -export function closeModal () { - if (modalBase == null) { return } +export function closeModal() { + if (modalBase == null) { + return + } modalBase.close() } -export function isModalOpen () { +export function isModalOpen() { return !modalBase.state.isHidden } diff --git a/browser/main/lib/notify.js b/browser/main/lib/notify.js index 458a784d..6054cb36 100644 --- a/browser/main/lib/notify.js +++ b/browser/main/lib/notify.js @@ -1,8 +1,12 @@ const path = require('path') -function notify (title, options) { +function notify(title, options) { if (process.platform === 'win32') { - options.icon = path.join('file://', global.__dirname, '../../resources/app.png') + options.icon = path.join( + 'file://', + global.__dirname, + '../../resources/app.png' + ) options.silent = false } return new window.Notification(title, options) diff --git a/browser/main/lib/shortcut.js b/browser/main/lib/shortcut.js index 98e26e06..aab644d7 100644 --- a/browser/main/lib/shortcut.js +++ b/browser/main/lib/shortcut.js @@ -1,16 +1,16 @@ import ee from 'browser/main/lib/eventEmitter' module.exports = { - 'toggleMode': () => { + toggleMode: () => { ee.emit('topbar:togglemodebutton') }, - 'toggleDirection': () => { + toggleDirection: () => { ee.emit('topbar:toggledirectionbutton') }, - 'deleteNote': () => { + deleteNote: () => { ee.emit('hotkey:deletenote') }, - 'toggleMenuBar': () => { + toggleMenuBar: () => { ee.emit('menubar:togglemenubar') } } diff --git a/browser/main/lib/shortcutManager.js b/browser/main/lib/shortcutManager.js index ac2a3a08..7575bb99 100644 --- a/browser/main/lib/shortcutManager.js +++ b/browser/main/lib/shortcutManager.js @@ -7,7 +7,7 @@ import functions from './shortcut' let shortcuts = CM.get().hotkey -ee.on('config-renew', function () { +ee.on('config-renew', function() { // only update if hotkey changed ! const newHotkey = CM.get().hotkey if (!isObjectEqual(newHotkey, shortcuts)) { @@ -15,17 +15,17 @@ ee.on('config-renew', function () { } }) -function updateShortcut (newHotkey) { +function updateShortcut(newHotkey) { Mousetrap.reset() shortcuts = newHotkey applyShortcuts(newHotkey) } -function formatShortcut (shortcut) { +function formatShortcut(shortcut) { return shortcut.toLowerCase().replace(/ /g, '') } -function applyShortcuts (shortcuts) { +function applyShortcuts(shortcuts) { for (const shortcut in shortcuts) { const toggler = formatShortcut(shortcuts[shortcut]) // only bind if the function for that shortcut exists diff --git a/browser/main/modals/CreateFolderModal.js b/browser/main/modals/CreateFolderModal.js index b48d6e42..26b5a245 100644 --- a/browser/main/modals/CreateFolderModal.js +++ b/browser/main/modals/CreateFolderModal.js @@ -10,7 +10,7 @@ import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig' import i18n from 'browser/lib/i18n' class CreateFolderModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -18,39 +18,39 @@ class CreateFolderModal extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.name.focus() this.refs.name.select() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleChange (e) { + handleChange(e) { this.setState({ name: this.refs.name.value }) } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - handleInputKeyDown (e) { + handleInputKeyDown(e) { switch (e.keyCode) { case 13: this.confirm() } } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - confirm () { + confirm() { AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_FOLDER') if (this.state.name.trim().length > 0) { const { storage } = this.props @@ -59,42 +59,48 @@ class CreateFolderModal extends React.Component { color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] } - dataApi.createFolder(storage.key, input) - .then((data) => { + dataApi + .createFolder(storage.key, input) + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage }) this.props.close() }) - .catch((err) => { + .catch(err => { console.error(err) }) } } - render () { + render() { return ( -
      this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >
      {i18n.__('Create new folder')}
      - this.handleCloseButtonClick(e)} /> + this.handleCloseButtonClick(e)} + />
      {i18n.__('Folder name')}
      - this.handleChange(e)} - onKeyDown={(e) => this.handleInputKeyDown(e)} + onChange={e => this.handleChange(e)} + onKeyDown={e => this.handleInputKeyDown(e)} />
      - diff --git a/browser/main/modals/CreateMarkdownFromURLModal.js b/browser/main/modals/CreateMarkdownFromURLModal.js index 31988059..090fe5a4 100644 --- a/browser/main/modals/CreateMarkdownFromURLModal.js +++ b/browser/main/modals/CreateMarkdownFromURLModal.js @@ -7,7 +7,7 @@ import ModalEscButton from 'browser/components/ModalEscButton' import i18n from 'browser/lib/i18n' class CreateMarkdownFromURLModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -17,89 +17,101 @@ class CreateMarkdownFromURLModal extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.name.focus() this.refs.name.select() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleChange (e) { + handleChange(e) { this.setState({ name: this.refs.name.value }) } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - handleInputKeyDown (e) { + handleInputKeyDown(e) { switch (e.keyCode) { case 13: this.confirm() } } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - showError (message) { + showError(message) { this.setState({ showerror: true, errormessage: message }) } - hideError () { + hideError() { this.setState({ showerror: false, errormessage: '' }) } - confirm () { + confirm() { this.hideError() const { storage, folder, dispatch, location } = this.props - dataApi.createNoteFromUrl(this.state.name, storage, folder, dispatch, location).then((result) => { - this.props.close() - }).catch((result) => { - this.showError(result.error) - }) + dataApi + .createNoteFromUrl(this.state.name, storage, folder, dispatch, location) + .then(result => { + this.props.close() + }) + .catch(result => { + this.showError(result.error) + }) } - render () { + render() { return ( -
      this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >
      {i18n.__('Import Markdown From URL')}
      - this.handleCloseButtonClick(e)} /> + this.handleCloseButtonClick(e)} + />
      -
      {i18n.__('Insert URL Here')}
      - + {i18n.__('Insert URL Here')} +
      + this.handleChange(e)} - onKeyDown={(e) => this.handleInputKeyDown(e)} + onChange={e => this.handleChange(e)} + onKeyDown={e => this.handleInputKeyDown(e)} />
      - -
      {this.state.errormessage}
      +
      + {this.state.errormessage} +
      ) diff --git a/browser/main/modals/NewNoteModal.js b/browser/main/modals/NewNoteModal.js index 476fa252..c06e91e3 100644 --- a/browser/main/modals/NewNoteModal.js +++ b/browser/main/modals/NewNoteModal.js @@ -9,21 +9,21 @@ import { createMarkdownNote, createSnippetNote } from 'browser/lib/newNote' import queryString from 'query-string' class NewNoteModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.lock = false this.state = {} } - componentDidMount () { + componentDidMount() { this.refs.markdownButton.focus() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleCreateMarkdownFromUrlClick (e) { + handleCreateMarkdownFromUrlClick(e) { this.props.close() const { storage, folder, dispatch, location } = this.props @@ -35,49 +35,63 @@ class NewNoteModal extends React.Component { }) } - handleMarkdownNoteButtonClick (e) { + handleMarkdownNoteButtonClick(e) { const { storage, folder, dispatch, location, config } = this.props const params = location.search !== '' && queryString.parse(location.search) if (!this.lock) { this.lock = true - createMarkdownNote(storage, folder, dispatch, location, params, config).then(() => { + createMarkdownNote( + storage, + folder, + dispatch, + location, + params, + config + ).then(() => { setTimeout(this.props.close, 200) }) } } - handleMarkdownNoteButtonKeyDown (e) { + handleMarkdownNoteButtonKeyDown(e) { if (e.keyCode === 9) { e.preventDefault() this.refs.snippetButton.focus() } } - handleSnippetNoteButtonClick (e) { + handleSnippetNoteButtonClick(e) { const { storage, folder, dispatch, location, config } = this.props const params = location.search !== '' && queryString.parse(location.search) if (!this.lock) { this.lock = true - createSnippetNote(storage, folder, dispatch, location, params, config).then(() => { + createSnippetNote( + storage, + folder, + dispatch, + location, + params, + config + ).then(() => { setTimeout(this.props.close, 200) }) } } - handleSnippetNoteButtonKeyDown (e) { + handleSnippetNoteButtonKeyDown(e) { if (e.keyCode === 9) { e.preventDefault() this.refs.markdownButton.focus() } } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - render () { + render() { return (
      this.handleSnippetNoteButtonKeyDown(e)} ref='snippetButton' > -
      + +
      {i18n.__('Snippet Note')} @@ -127,10 +142,17 @@ class NewNoteModal extends React.Component { )} -
      -
      {i18n.__('Tab to switch format')}
      -
      this.handleCreateMarkdownFromUrlClick(e)}>Or, create a new markdown note from a URL
      +
      + + {i18n.__('Tab to switch format')} +
      +
      this.handleCreateMarkdownFromUrlClick(e)} + > + Or, create a new markdown note from a URL +
      ) } diff --git a/browser/main/modals/PreferencesModal/Blog.js b/browser/main/modals/PreferencesModal/Blog.js index 4d59bea1..26b4839d 100644 --- a/browser/main/modals/PreferencesModal/Blog.js +++ b/browser/main/modals/PreferencesModal/Blog.js @@ -11,7 +11,7 @@ const electron = require('electron') const { shell } = electron const ipc = electron.ipcRenderer class Blog extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,12 +20,12 @@ class Blog extends React.Component { } } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - clearMessage () { + clearMessage() { _.debounce(() => { this.setState({ BlogAlert: null @@ -33,30 +33,41 @@ class Blog extends React.Component { }, 2000)() } - componentDidMount () { + componentDidMount() { this.handleSettingDone = () => { - this.setState({BlogAlert: { - type: 'success', - message: i18n.__('Successfully applied!') - }}) + this.setState({ + BlogAlert: { + type: 'success', + message: i18n.__('Successfully applied!') + } + }) } - this.handleSettingError = (err) => { - this.setState({BlogAlert: { - type: 'error', - message: err.message != null ? err.message : i18n.__('An error occurred!') - }}) + this.handleSettingError = err => { + this.setState({ + BlogAlert: { + type: 'error', + message: + err.message != null ? err.message : i18n.__('An error occurred!') + } + }) } this.oldBlog = this.state.config.blog ipc.addListener('APP_SETTING_DONE', this.handleSettingDone) ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) } - handleBlogChange (e) { + handleBlogChange(e) { const { config } = this.state config.blog = { - password: !_.isNil(this.refs.passwordInput) ? this.refs.passwordInput.value : config.blog.password, - username: !_.isNil(this.refs.usernameInput) ? this.refs.usernameInput.value : config.blog.username, - token: !_.isNil(this.refs.tokenInput) ? this.refs.tokenInput.value : config.blog.token, + password: !_.isNil(this.refs.passwordInput) + ? this.refs.passwordInput.value + : config.blog.password, + username: !_.isNil(this.refs.usernameInput) + ? this.refs.usernameInput.value + : config.blog.username, + token: !_.isNil(this.refs.tokenInput) + ? this.refs.tokenInput.value + : config.blog.token, authMethod: this.refs.authMethodDropdown.value, address: this.refs.addressInput.value, type: this.refs.typeDropdown.value @@ -75,7 +86,7 @@ class Blog extends React.Component { } } - handleSaveButtonClick (e) { + handleSaveButtonClick(e) { const newConfig = { blog: this.state.config.blog } @@ -90,36 +101,36 @@ class Blog extends React.Component { this.props.haveToSave() } - render () { - const {config, BlogAlert} = this.state - const blogAlertElement = BlogAlert != null - ?

      - {BlogAlert.message} -

      - : null + render() { + const { config, BlogAlert } = this.state + const blogAlertElement = + BlogAlert != null ? ( +

      {BlogAlert.message}

      + ) : null return (
      {i18n.__('Blog')}
      -
      - {i18n.__('Blog Type')} -
      +
      {i18n.__('Blog Type')}
      {i18n.__('Blog Address')}
      - this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='addressInput' value={config.blog.address} type='text' @@ -127,8 +138,11 @@ class Blog extends React.Component {
      - {blogAlertElement}
      @@ -143,49 +157,59 @@ class Blog extends React.Component {
      - { config.blog.authMethod === 'JWT' && + {config.blog.authMethod === 'JWT' && (
      {i18n.__('Token')}
      - this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='tokenInput' value={config.blog.token} - type='text' /> + type='text' + />
      - } - { config.blog.authMethod === 'USER' && + )} + {config.blog.authMethod === 'USER' && (
      {i18n.__('UserName')}
      - this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='usernameInput' value={config.blog.username} - type='text' /> + type='text' + />
      {i18n.__('Password')}
      - this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='passwordInput' value={config.blog.password} - type='password' /> + type='password' + />
      - } + )}
      ) } diff --git a/browser/main/modals/PreferencesModal/Crowdfunding.js b/browser/main/modals/PreferencesModal/Crowdfunding.js index 56bb6e34..a5d37718 100644 --- a/browser/main/modals/PreferencesModal/Crowdfunding.js +++ b/browser/main/modals/PreferencesModal/Crowdfunding.js @@ -7,50 +7,93 @@ const electron = require('electron') const { shell } = electron class Crowdfunding extends React.Component { - constructor (props) { + constructor(props) { super(props) - this.state = { - } + this.state = {} } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - render () { + render() { return (
      {i18n.__('Crowdfunding')}

      {i18n.__('Thank you for using Boostnote!')}


      -

      {i18n.__('We launched IssueHunt which is an issue-based crowdfunding / sourcing platform for open source projects.')}

      -

      {i18n.__('Anyone can put a bounty on not only a bug but also on OSS feature requests listed on IssueHunt. Collected funds will be distributed to project owners and contributors.')}

      -
      {i18n.__('Sustainable Open Source Ecosystem')}
      -

      {i18n.__('We discussed about open-source ecosystem and IssueHunt concept with the Boostnote team repeatedly. We actually also discussed with Matz who father of Ruby.')}

      -

      {i18n.__('The original reason why we made IssueHunt was to reward our contributors of Boostnote project. We’ve got tons of Github stars and hundred of contributors in two years.')}

      -

      {i18n.__('We thought that it will be nice if we can pay reward for our contributors.')}

      -
      {i18n.__('We believe Meritocracy')}
      -

      {i18n.__('We think developers who have skills and do great things must be rewarded properly.')}

      -

      {i18n.__('OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.')}

      +

      + {i18n.__( + 'We launched IssueHunt which is an issue-based crowdfunding / sourcing platform for open source projects.' + )} +

      +

      + {i18n.__( + 'Anyone can put a bounty on not only a bug but also on OSS feature requests listed on IssueHunt. Collected funds will be distributed to project owners and contributors.' + )} +

      +
      + {i18n.__('Sustainable Open Source Ecosystem')} +
      +

      + {i18n.__( + 'We discussed about open-source ecosystem and IssueHunt concept with the Boostnote team repeatedly. We actually also discussed with Matz who father of Ruby.' + )} +

      +

      + {i18n.__( + 'The original reason why we made IssueHunt was to reward our contributors of Boostnote project. We’ve got tons of Github stars and hundred of contributors in two years.' + )} +

      +

      + {i18n.__( + 'We thought that it will be nice if we can pay reward for our contributors.' + )} +

      +
      + {i18n.__('We believe Meritocracy')} +
      +

      + {i18n.__( + 'We think developers who have skills and do great things must be rewarded properly.' + )} +

      +

      + {i18n.__( + 'OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.' + )} +

      {i18n.__('It sometimes looks like exploitation.')}

      -

      {i18n.__('We’ve realized IssueHunt could enhance sustainability of open-source ecosystem.')}

      +

      + {i18n.__( + 'We’ve realized IssueHunt could enhance sustainability of open-source ecosystem.' + )} +


      -

      {i18n.__('As same as issues of Boostnote are already funded on IssueHunt, your open-source projects can be also started funding from now.')}

      +

      + {i18n.__( + 'As same as issues of Boostnote are already funded on IssueHunt, your open-source projects can be also started funding from now.' + )} +


      {i18n.__('Thank you,')}

      {i18n.__('The Boostnote Team')}


      ) } } -Crowdfunding.propTypes = { -} +Crowdfunding.propTypes = {} export default CSSModules(Crowdfunding, styles) diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js index 648db4e6..6418bb6a 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.js +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -10,7 +10,7 @@ import { SortableElement, SortableHandle } from 'react-sortable-hoc' import i18n from 'browser/lib/i18n' class FolderItem extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -24,7 +24,7 @@ class FolderItem extends React.Component { } } - handleEditChange (e) { + handleEditChange(e) { const { folder } = this.state folder.name = this.refs.nameInput.value @@ -33,18 +33,18 @@ class FolderItem extends React.Component { }) } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - confirm () { + confirm() { const { storage, folder } = this.props dataApi .updateFolder(storage.key, folder.key, { color: this.state.folder.color, name: this.state.folder.name }) - .then((data) => { + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage @@ -55,9 +55,12 @@ class FolderItem extends React.Component { }) } - handleColorButtonClick (e) { - const folder = Object.assign({}, this.state.folder, { showColumnPicker: true, colorPickerPos: { left: 0, top: 0 } }) - this.setState({ folder }, function () { + handleColorButtonClick(e) { + const folder = Object.assign({}, this.state.folder, { + showColumnPicker: true, + colorPickerPos: { left: 0, top: 0 } + }) + this.setState({ folder }, function() { // After the color picker has been painted, re-calculate its position // by comparing its dimensions to the host dimensions. const { hostBoundingBox } = this.props @@ -67,30 +70,32 @@ class FolderItem extends React.Component { const folder = Object.assign({}, this.state.folder, { colorPickerPos: { left: 25, - top: offsetTop < 0 ? offsetTop - 5 : 0 // subtract 5px for aestetics + top: offsetTop < 0 ? offsetTop - 5 : 0 // subtract 5px for aestetics } }) this.setState({ folder }) }) } - handleColorChange (color) { + handleColorChange(color) { const folder = Object.assign({}, this.state.folder, { color: color.hex }) this.setState({ folder }) } - handleColorPickerClose (event) { - const folder = Object.assign({}, this.state.folder, { showColumnPicker: false }) + handleColorPickerClose(event) { + const folder = Object.assign({}, this.state.folder, { + showColumnPicker: false + }) this.setState({ folder }) } - handleCancelButtonClick (e) { + handleCancelButtonClick(e) { this.setState({ status: 'IDLE' }) } - handleFolderItemBlur (e) { + handleFolderItemBlur(e) { let el = e.relatedTarget while (el != null) { if (el === this.refs.root) { @@ -101,7 +106,7 @@ class FolderItem extends React.Component { this.confirm() } - renderEdit (e) { + renderEdit(e) { const popover = { position: 'absolute', zIndex: 2 } const cover = { position: 'fixed', @@ -110,51 +115,64 @@ class FolderItem extends React.Component { bottom: 0, left: 0 } - const pickerStyle = Object.assign({}, { - position: 'absolute' - }, this.state.folder.colorPickerPos) + const pickerStyle = Object.assign( + {}, + { + position: 'absolute' + }, + this.state.folder.colorPickerPos + ) return ( -
      this.handleFolderItemBlur(e)} +
      this.handleFolderItemBlur(e)} tabIndex='-1' ref='root' >
      - - this.handleEditChange(e)} + onChange={e => this.handleEditChange(e)} />
      - - @@ -163,79 +181,85 @@ class FolderItem extends React.Component { ) } - handleDeleteConfirmButtonClick (e) { + handleDeleteConfirmButtonClick(e) { const { storage, folder } = this.props - dataApi - .deleteFolder(storage.key, folder.key) - .then((data) => { - store.dispatch({ - type: 'DELETE_FOLDER', - storage: data.storage, - folderKey: data.folderKey - }) + dataApi.deleteFolder(storage.key, folder.key).then(data => { + store.dispatch({ + type: 'DELETE_FOLDER', + storage: data.storage, + folderKey: data.folderKey }) - } - - renderDelete () { - return ( -
      -
      - {i18n.__('Are you sure to ')} {i18n.__(' delete')} {i18n.__('this folder?')} -
      -
      - - -
      -
      - ) - } - - handleEditButtonClick (e) { - const { folder: propsFolder } = this.props - const { folder: stateFolder } = this.state - const folder = Object.assign({}, stateFolder, propsFolder) - this.setState({ - status: 'EDIT', - folder - }, () => { - this.refs.nameInput.select() }) } - handleDeleteButtonClick (e) { + renderDelete() { + return ( +
      +
      + {i18n.__('Are you sure to ')}{' '} + {i18n.__(' delete')}{' '} + {i18n.__('this folder?')} +
      +
      + + +
      +
      + ) + } + + handleEditButtonClick(e) { + const { folder: propsFolder } = this.props + const { folder: stateFolder } = this.state + const folder = Object.assign({}, stateFolder, propsFolder) + this.setState( + { + status: 'EDIT', + folder + }, + () => { + this.refs.nameInput.select() + } + ) + } + + handleDeleteButtonClick(e) { this.setState({ status: 'DELETE' }) } - renderIdle () { + renderIdle() { const { folder } = this.props return ( -
      this.handleEditButtonClick(e)} +
      this.handleEditButtonClick(e)} > -
      +
      {folder.name} ({folder.key})
      - - @@ -244,7 +268,7 @@ class FolderItem extends React.Component { ) } - render () { + render() { switch (this.state.status) { case 'DELETE': return this.renderDelete() @@ -277,7 +301,7 @@ FolderItem.propTypes = { } class Handle extends React.Component { - render () { + render() { return (
      @@ -287,7 +311,7 @@ class Handle extends React.Component { } class SortableFolderItemComponent extends React.Component { - render () { + render() { const StyledHandle = CSSModules(Handle, styles) const DragHandle = SortableHandle(StyledHandle) diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index 674026c5..f751d250 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -9,24 +9,28 @@ import { SortableContainer } from 'react-sortable-hoc' import i18n from 'browser/lib/i18n' class FolderList extends React.Component { - render () { + render() { const { storage, hostBoundingBox } = this.props const folderList = storage.folders.map((folder, index) => { - return + return ( + + ) }) return (
      - {folderList.length > 0 - ? folderList - :
      {i18n.__('No Folders')}
      - } + {folderList.length > 0 ? ( + folderList + ) : ( +
      {i18n.__('No Folders')}
      + )}
      ) } @@ -52,23 +56,21 @@ FolderList.propTypes = { } class SortableFolderListComponent extends React.Component { - constructor (props) { + constructor(props) { super(props) - this.onSortEnd = ({oldIndex, newIndex}) => { + this.onSortEnd = ({ oldIndex, newIndex }) => { const { storage } = this.props - dataApi - .reorderFolder(storage.key, oldIndex, newIndex) - .then((data) => { - store.dispatch({ - type: 'REORDER_FOLDER', - storage: data.storage - }) - this.setState() + dataApi.reorderFolder(storage.key, oldIndex, newIndex).then(data => { + store.dispatch({ + type: 'REORDER_FOLDER', + storage: data.storage }) + this.setState() + }) } } - render () { + render() { const StyledFolderList = CSSModules(FolderList, this.props.styles) const SortableFolderList = SortableContainer(StyledFolderList) diff --git a/browser/main/modals/PreferencesModal/HotkeyTab.js b/browser/main/modals/PreferencesModal/HotkeyTab.js index 00fea4f0..1bba78e3 100644 --- a/browser/main/modals/PreferencesModal/HotkeyTab.js +++ b/browser/main/modals/PreferencesModal/HotkeyTab.js @@ -11,7 +11,7 @@ const electron = require('electron') const ipc = electron.ipcRenderer class HotkeyTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,28 +20,35 @@ class HotkeyTab extends React.Component { } } - componentDidMount () { + componentDidMount() { this.handleSettingDone = () => { - this.setState({keymapAlert: { - type: 'success', - message: i18n.__('Successfully applied!') - }}) + this.setState({ + keymapAlert: { + type: 'success', + message: i18n.__('Successfully applied!') + } + }) } - this.handleSettingError = (err) => { + this.handleSettingError = err => { if ( this.state.config.hotkey.toggleMain === '' || this.state.config.hotkey.toggleMode === '' || this.state.config.hotkey.toggleDirection === '' ) { - this.setState({keymapAlert: { - type: 'success', - message: i18n.__('Successfully applied!') - }}) + this.setState({ + keymapAlert: { + type: 'success', + message: i18n.__('Successfully applied!') + } + }) } else { - this.setState({keymapAlert: { - type: 'error', - message: err.message != null ? err.message : i18n.__('An error occurred!') - }}) + this.setState({ + keymapAlert: { + type: 'error', + message: + err.message != null ? err.message : i18n.__('An error occurred!') + } + }) } } this.oldHotkey = this.state.config.hotkey @@ -49,12 +56,12 @@ class HotkeyTab extends React.Component { ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) } - componentWillUnmount () { + componentWillUnmount() { ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone) ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError) } - handleSaveButtonClick (e) { + handleSaveButtonClick(e) { const newConfig = { hotkey: this.state.config.hotkey } @@ -69,13 +76,13 @@ class HotkeyTab extends React.Component { this.props.haveToSave() } - handleHintToggleButtonClick (e) { + handleHintToggleButtonClick(e) { this.setState({ isHotkeyHintOpen: !this.state.isHotkeyHintOpen }) } - handleHotkeyChange (e) { + handleHotkeyChange(e) { const { config } = this.state config.hotkey = Object.assign({}, config.hotkey, { toggleMain: this.refs.toggleMain.value, @@ -102,7 +109,7 @@ class HotkeyTab extends React.Component { } } - clearMessage () { + clearMessage() { _.debounce(() => { this.setState({ keymapAlert: null @@ -110,13 +117,12 @@ class HotkeyTab extends React.Component { }, 2000)() } - render () { + render() { const keymapAlert = this.state.keymapAlert - const keymapAlertElement = keymapAlert != null - ?

      - {keymapAlert.message} -

      - : null + const keymapAlertElement = + keymapAlert != null ? ( +

      {keymapAlert.message}

      + ) : null const { config } = this.state return ( @@ -124,10 +130,13 @@ class HotkeyTab extends React.Component {
      {i18n.__('Hotkeys')}
      -
      {i18n.__('Show/Hide Boostnote')}
      +
      + {i18n.__('Show/Hide Boostnote')} +
      - this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='toggleMain' value={config.hotkey.toggleMain} type='text' @@ -135,10 +144,13 @@ class HotkeyTab extends React.Component {
      -
      {i18n.__('Show/Hide Menu Bar')}
      +
      + {i18n.__('Show/Hide Menu Bar')} +
      - this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='toggleMenuBar' value={config.hotkey.toggleMenuBar} type='text' @@ -146,10 +158,13 @@ class HotkeyTab extends React.Component {
      -
      {i18n.__('Toggle Editor Mode')}
      +
      + {i18n.__('Toggle Editor Mode')} +
      - this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='toggleMode' value={config.hotkey.toggleMode} type='text' @@ -157,10 +172,13 @@ class HotkeyTab extends React.Component {
      -
      {i18n.__('Toggle Direction')}
      +
      + {i18n.__('Toggle Direction')} +
      - this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='toggleDirection' value={config.hotkey.toggleDirection} type='text' @@ -170,8 +188,9 @@ class HotkeyTab extends React.Component {
      {i18n.__('Delete Note')}
      - this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='deleteNote' value={config.hotkey.deleteNote} type='text' @@ -181,8 +200,9 @@ class HotkeyTab extends React.Component {
      {i18n.__('Paste HTML')}
      - this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='pasteSmartly' value={config.hotkey.pasteSmartly} type='text' @@ -190,19 +210,26 @@ class HotkeyTab extends React.Component {
      -
      {i18n.__('Prettify Markdown')}
      +
      + {i18n.__('Prettify Markdown')} +
      - this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='prettifyMarkdown' value={config.hotkey.prettifyMarkdown} - type='text' /> + type='text' + />
      -
      {i18n.__('Insert Current Date')}
      +
      + {i18n.__('Insert Current Date')} +
      -
      -
      {i18n.__('Insert Current Date and Time')}
      +
      + {i18n.__('Insert Current Date and Time')} +
      -
      - - {keymapAlertElement}
      - {this.state.isHotkeyHintOpen && + {this.state.isHotkeyHintOpen && (

      {i18n.__('Available Keys')}

        -
      • 0 to 9
      • -
      • A to Z
      • -
      • F1 to F24
      • -
      • Punctuations like ~, !, @, #, $, etc.
      • -
      • Plus
      • -
      • Space
      • -
      • Backspace
      • -
      • Delete
      • -
      • Insert
      • -
      • Return (or Enter as alias)
      • -
      • Up, Down, Left and Right
      • -
      • Home and End
      • -
      • PageUp and PageDown
      • -
      • Escape (or Esc for short)
      • -
      • VolumeUp, VolumeDown and VolumeMute
      • -
      • MediaNextTrack, MediaPreviousTrack, MediaStop and MediaPlayPause
      • -
      • Control (or Ctrl for short)
      • -
      • Shift
      • +
      • + 0 to 9 +
      • +
      • + A to Z +
      • +
      • + F1 to F24 +
      • +
      • + Punctuations like ~, !,{' '} + @, #, $, etc. +
      • +
      • + Plus +
      • +
      • + Space +
      • +
      • + Backspace +
      • +
      • + Delete +
      • +
      • + Insert +
      • +
      • + Return (or Enter as alias) +
      • +
      • + Up, Down, Left and{' '} + Right +
      • +
      • + Home and End +
      • +
      • + PageUp and PageDown +
      • +
      • + Escape (or Esc for short) +
      • +
      • + VolumeUp, VolumeDown and{' '} + VolumeMute +
      • +
      • + MediaNextTrack, MediaPreviousTrack,{' '} + MediaStop and MediaPlayPause +
      • +
      • + Control (or Ctrl for short) +
      • +
      • + Shift +
      - } + )}
      ) diff --git a/browser/main/modals/PreferencesModal/InfoTab.js b/browser/main/modals/PreferencesModal/InfoTab.js index 97f06f7b..fa49025f 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.js +++ b/browser/main/modals/PreferencesModal/InfoTab.js @@ -12,7 +12,7 @@ const { shell, remote } = electron const appVersion = remote.app.getVersion() class InfoTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,18 +20,18 @@ class InfoTab extends React.Component { } } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - handleConfigChange (e) { + handleConfigChange(e) { const newConfig = { amaEnabled: this.refs.amaEnabled.checked } this.setState({ config: newConfig }) } - handleSaveButtonClick (e) { + handleSaveButtonClick(e) { const newConfig = { amaEnabled: this.state.config.amaEnabled } @@ -43,7 +43,7 @@ class InfoTab extends React.Component { }) } else { this.setState({ - amaMessage: i18n.__('Thank\'s for trusting us') + amaMessage: i18n.__("Thank's for trusting us") }) } @@ -61,7 +61,7 @@ class InfoTab extends React.Component { }) } - toggleAutoUpdate () { + toggleAutoUpdate() { const newConfig = { autoUpdateEnabled: !this.state.config.autoUpdateEnabled } @@ -70,46 +70,64 @@ class InfoTab extends React.Component { ConfigManager.set(newConfig) } - infoMessage () { + infoMessage() { const { amaMessage } = this.state return amaMessage ?

      {amaMessage}

      : null } - render () { + render() { return (
      {i18n.__('Community')}
      @@ -120,11 +138,20 @@ class InfoTab extends React.Component {
      - +
      -
      {i18n.__('Boostnote')} {appVersion}
      +
      + {i18n.__('Boostnote')} {appVersion} +
      - {i18n.__('An open source note-taking app made for programmers just like you.')} + {i18n.__( + 'An open source note-taking app made for programmers just like you.' + )}
      @@ -132,39 +159,71 @@ class InfoTab extends React.Component { -
      +
      + +

      {i18n.__('Analytics')}
      -
      {i18n.__('Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.')}
      -
      {i18n.__('You can see how it works on ')} this.handleLinkClick(e)}>GitHub.
      +
      + {i18n.__( + 'Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.' + )} +
      +
      + {i18n.__('You can see how it works on ')} + this.handleLinkClick(e)} + > + GitHub + + . +

      {i18n.__('You can choose to enable or disable this option.')}
      - this.handleConfigChange(e)} + this.handleConfigChange(e)} checked={this.state.config.amaEnabled} ref='amaEnabled' type='checkbox' /> - {i18n.__('Enable analytics to help improve Boostnote')}
      - + {i18n.__('Enable analytics to help improve Boostnote')} +
      +
      {this.infoMessage()}
      @@ -172,7 +231,6 @@ class InfoTab extends React.Component { } } -InfoTab.propTypes = { -} +InfoTab.propTypes = {} export default CSSModules(InfoTab, styles) diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index e95afdcf..f748924c 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -6,13 +6,19 @@ import CSSModules from 'browser/lib/CSSModules' import dataApi from 'browser/main/lib/dataApi' import snippetManager from '../../../lib/SnippetManager' -const defaultEditorFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace'] +const defaultEditorFontFamily = [ + 'Monaco', + 'Menlo', + 'Ubuntu Mono', + 'Consolas', + 'source-code-pro', + 'monospace' +] const buildCMRulers = (rulers, enableRulers) => enableRulers ? rulers.map(ruler => ({ column: ruler })) : [] class SnippetEditor extends React.Component { - - componentDidMount () { + componentDidMount() { this.props.onRef(this) const { rulers, enableRulers } = this.props this.cm = CodeMirror(this.refs.root, { @@ -49,38 +55,50 @@ class SnippetEditor extends React.Component { }) } - componentWillUnmount () { + componentWillUnmount() { this.props.onRef(undefined) } - onSnippetChanged (newSnippet) { + onSnippetChanged(newSnippet) { this.snippet = newSnippet this.cm.setValue(this.snippet.content) } - onSnippetNameOrPrefixChanged (newSnippet) { + onSnippetNameOrPrefixChanged(newSnippet) { this.snippet.name = newSnippet.name - this.snippet.prefix = newSnippet.prefix.toString().replace(/\s/g, '').split(',') + this.snippet.prefix = newSnippet.prefix + .toString() + .replace(/\s/g, '') + .split(',') this.saveSnippet() } - saveSnippet () { - dataApi.updateSnippet(this.snippet) + saveSnippet() { + dataApi + .updateSnippet(this.snippet) .then(snippets => snippetManager.assignSnippets(snippets)) - .catch((err) => { throw err }) + .catch(err => { + throw err + }) } - render () { + render() { const { fontSize } = this.props let fontFamily = this.props.fontFamily - fontFamily = _.isString(fontFamily) && fontFamily.length > 0 - ? [fontFamily].concat(defaultEditorFontFamily) - : defaultEditorFontFamily + fontFamily = + _.isString(fontFamily) && fontFamily.length > 0 + ? [fontFamily].concat(defaultEditorFontFamily) + : defaultEditorFontFamily return ( -
      +
      ) } } diff --git a/browser/main/modals/PreferencesModal/SnippetList.js b/browser/main/modals/PreferencesModal/SnippetList.js index 3790eb3f..145cfb75 100644 --- a/browser/main/modals/PreferencesModal/SnippetList.js +++ b/browser/main/modals/PreferencesModal/SnippetList.js @@ -7,53 +7,65 @@ import eventEmitter from 'browser/main/lib/eventEmitter' import context from 'browser/lib/context' class SnippetList extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { snippets: [] } } - componentDidMount () { + componentDidMount() { this.reloadSnippetList() eventEmitter.on('snippetList:reload', this.reloadSnippetList.bind(this)) } - reloadSnippetList () { + reloadSnippetList() { dataApi.fetchSnippet().then(snippets => { - this.setState({snippets}) + this.setState({ snippets }) this.props.onSnippetSelect(this.props.currentSnippet) }) } - handleSnippetContextMenu (snippet) { - context.popup([{ - label: i18n.__('Delete snippet'), - click: () => this.deleteSnippet(snippet) - }]) + handleSnippetContextMenu(snippet) { + context.popup([ + { + label: i18n.__('Delete snippet'), + click: () => this.deleteSnippet(snippet) + } + ]) } - deleteSnippet (snippet) { - dataApi.deleteSnippet(snippet).then(() => { - this.reloadSnippetList() - this.props.onSnippetDeleted(snippet) - }).catch(err => { throw err }) + deleteSnippet(snippet) { + dataApi + .deleteSnippet(snippet) + .then(() => { + this.reloadSnippetList() + this.props.onSnippetDeleted(snippet) + }) + .catch(err => { + throw err + }) } - handleSnippetClick (snippet) { + handleSnippetClick(snippet) { this.props.onSnippetSelect(snippet) } - createSnippet () { - dataApi.createSnippet().then(() => { - this.reloadSnippetList() - // scroll to end of list when added new snippet - const snippetList = document.getElementById('snippets') - snippetList.scrollTop = snippetList.scrollHeight - }).catch(err => { throw err }) + createSnippet() { + dataApi + .createSnippet() + .then(() => { + this.reloadSnippetList() + // scroll to end of list when added new snippet + const snippetList = document.getElementById('snippets') + snippetList.scrollTop = snippetList.scrollHeight + }) + .catch(err => { + throw err + }) } - defineSnippetStyleName (snippet) { + defineSnippetStyleName(snippet) { const { currentSnippet } = this.props if (currentSnippet == null) { @@ -67,29 +79,31 @@ class SnippetList extends React.Component { } } - render () { + render() { const { snippets } = this.state return (
      -
        - { - snippets.map((snippet) => ( -
      • this.handleSnippetContextMenu(snippet)} - onClick={() => this.handleSnippetClick(snippet)}> - {snippet.name} -
      • - )) - } + {snippets.map(snippet => ( +
      • this.handleSnippetContextMenu(snippet)} + onClick={() => this.handleSnippetClick(snippet)} + > + {snippet.name} +
      • + ))}
      ) diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index df338d7f..0476c5c2 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -11,7 +11,7 @@ import copy from 'copy-to-clipboard' const path = require('path') class SnippetTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { currentSnippet: null @@ -19,7 +19,7 @@ class SnippetTab extends React.Component { this.changeDelay = null } - notify (title, options) { + notify(title, options) { if (global.process.platform === 'win32') { options.icon = path.join( 'file://', @@ -30,7 +30,7 @@ class SnippetTab extends React.Component { return new window.Notification(title, options) } - handleSnippetNameOrPrefixChange () { + handleSnippetNameOrPrefixChange() { clearTimeout(this.changeDelay) this.changeDelay = setTimeout(() => { // notify the snippet editor that the name or prefix of snippet has been changed @@ -39,20 +39,20 @@ class SnippetTab extends React.Component { }, 500) } - handleSnippetSelect (snippet) { + handleSnippetSelect(snippet) { const { currentSnippet } = this.state if (snippet !== null) { if (currentSnippet === null || currentSnippet.id !== snippet.id) { dataApi.fetchSnippet(snippet.id).then(changedSnippet => { // notify the snippet editor to load the content of the new snippet this.snippetEditor.onSnippetChanged(changedSnippet) - this.setState({currentSnippet: changedSnippet}) + this.setState({ currentSnippet: changedSnippet }) }) } } } - onSnippetNameOrPrefixChanged (e, type) { + onSnippetNameOrPrefixChanged(e, type) { const newSnippet = Object.assign({}, this.state.currentSnippet) if (type === 'name') { newSnippet.name = e.target.value @@ -63,14 +63,14 @@ class SnippetTab extends React.Component { this.handleSnippetNameOrPrefixChange() } - handleDeleteSnippet (snippet) { + handleDeleteSnippet(snippet) { // prevent old snippet still display when deleted if (snippet.id === this.state.currentSnippet.id) { - this.setState({currentSnippet: null}) + this.setState({ currentSnippet: null }) } } - handleCopySnippet (e) { + handleCopySnippet(e) { const showCopyNotification = this.props.config.ui.showCopyNotification copy(this.state.currentSnippet.content) if (showCopyNotification) { @@ -81,7 +81,7 @@ class SnippetTab extends React.Component { } } - render () { + render() { const { config, storageKey } = this.props const { currentSnippet } = this.state @@ -95,12 +95,19 @@ class SnippetTab extends React.Component { -
      + currentSnippet={currentSnippet} + /> +
      -
      @@ -110,18 +117,26 @@ class SnippetTab extends React.Component { { this.onSnippetNameOrPrefixChanged(e, 'name') }} - type='text' /> + onChange={e => { + this.onSnippetNameOrPrefixChanged(e, 'name') + }} + type='text' + />
      -
      {i18n.__('Snippet prefix')}
      +
      + {i18n.__('Snippet prefix')} +
      { this.onSnippetNameOrPrefixChanged(e, 'prefix') }} - type='text' /> + onChange={e => { + this.onSnippetNameOrPrefixChanged(e, 'prefix') + }} + type='text' + />
      @@ -140,7 +155,10 @@ class SnippetTab extends React.Component { matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} scrollPastEnd={config.editor.scrollPastEnd} - onRef={ref => { this.snippetEditor = ref }} /> + onRef={ref => { + this.snippetEditor = ref + }} + />
      @@ -148,7 +166,6 @@ class SnippetTab extends React.Component { } } -SnippetTab.PropTypes = { -} +SnippetTab.PropTypes = {} export default CSSModules(SnippetTab, styles) diff --git a/browser/main/modals/PreferencesModal/StorageItem.js b/browser/main/modals/PreferencesModal/StorageItem.js index 9af02962..3cb18e30 100644 --- a/browser/main/modals/PreferencesModal/StorageItem.js +++ b/browser/main/modals/PreferencesModal/StorageItem.js @@ -12,7 +12,7 @@ const { shell, remote } = require('electron') const { dialog } = remote class StorageItem extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,137 +20,156 @@ class StorageItem extends React.Component { } } - handleNewFolderButtonClick (e) { + handleNewFolderButtonClick(e) { const { storage } = this.props const input = { name: i18n.__('New Folder'), color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] } - dataApi.createFolder(storage.key, input) - .then((data) => { + dataApi + .createFolder(storage.key, input) + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage }) }) - .catch((err) => { + .catch(err => { console.error(err) }) } - handleExternalButtonClick () { + handleExternalButtonClick() { const { storage } = this.props shell.showItemInFolder(storage.path) } - handleUnlinkButtonClick (e) { + handleUnlinkButtonClick(e) { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Unlink Storage'), - detail: i18n.__('Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.'), + detail: i18n.__( + 'Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.' + ), buttons: [i18n.__('Unlink'), i18n.__('Cancel')] }) if (index === 0) { const { storage } = this.props - dataApi.removeStorage(storage.key) + dataApi + .removeStorage(storage.key) .then(() => { store.dispatch({ type: 'REMOVE_STORAGE', storageKey: storage.key }) }) - .catch((err) => { + .catch(err => { throw err }) } } - handleLabelClick (e) { + handleLabelClick(e) { const { storage } = this.props - this.setState({ - isLabelEditing: true, - name: storage.name - }, () => { - this.refs.label.focus() - }) + this.setState( + { + isLabelEditing: true, + name: storage.name + }, + () => { + this.refs.label.focus() + } + ) } - handleLabelChange (e) { + handleLabelChange(e) { this.setState({ name: this.refs.label.value }) } - handleLabelBlur (e) { + handleLabelBlur(e) { const { storage } = this.props - dataApi - .renameStorage(storage.key, this.state.name) - .then((_storage) => { - store.dispatch({ - type: 'RENAME_STORAGE', - storage: _storage - }) - this.setState({ - isLabelEditing: false - }) + dataApi.renameStorage(storage.key, this.state.name).then(_storage => { + store.dispatch({ + type: 'RENAME_STORAGE', + storage: _storage }) + this.setState({ + isLabelEditing: false + }) + }) } - render () { + render() { const { storage, hostBoundingBox } = this.props return (
      - {this.state.isLabelEditing - ?
      - + this.handleLabelChange(e)} - onBlur={(e) => this.handleLabelBlur(e)} + onChange={e => this.handleLabelChange(e)} + onBlur={e => this.handleLabelBlur(e)} />
      - :
      this.handleLabelClick(e)} + ) : ( +
      this.handleLabelClick(e)} > -   + +   {storage.name}  ({storage.path}) 
      - } + )}
      - - -
      - +
      ) } diff --git a/browser/main/modals/PreferencesModal/StoragesTab.js b/browser/main/modals/PreferencesModal/StoragesTab.js index e84fa88c..9df1a153 100644 --- a/browser/main/modals/PreferencesModal/StoragesTab.js +++ b/browser/main/modals/PreferencesModal/StoragesTab.js @@ -12,24 +12,27 @@ import fs from 'fs' const electron = require('electron') const { shell, remote } = electron -function browseFolder () { +function browseFolder() { const dialog = remote.dialog const defaultPath = remote.app.getPath('home') return new Promise((resolve, reject) => { - dialog.showOpenDialog({ - title: i18n.__('Select Directory'), - defaultPath, - properties: ['openDirectory', 'createDirectory'] - }, function (targetPaths) { - if (targetPaths == null) return resolve('') - resolve(targetPaths[0]) - }) + dialog.showOpenDialog( + { + title: i18n.__('Select Directory'), + defaultPath, + properties: ['openDirectory', 'createDirectory'] + }, + function(targetPaths) { + if (targetPaths == null) return resolve('') + resolve(targetPaths[0]) + } + ) }) } class StoragesTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -44,7 +47,7 @@ class StoragesTab extends React.Component { this.loadAttachmentStorage() } - loadAttachmentStorage () { + loadAttachmentStorage() { const promises = [] this.props.data.noteMap.map(note => { const promise = attachmentManagement.getAttachmentsPathAndStatus( @@ -58,106 +61,128 @@ class StoragesTab extends React.Component { Promise.all(promises) .then(data => { const result = data.reduce((acc, curr) => acc.concat(curr), []) - this.setState({attachments: result}) + this.setState({ attachments: result }) }) .catch(console.error) } - handleAddStorageButton (e) { - this.setState({ - page: 'ADD_STORAGE', - newStorage: { - name: 'Unnamed', - type: 'FILESYSTEM', - path: '' + handleAddStorageButton(e) { + this.setState( + { + page: 'ADD_STORAGE', + newStorage: { + name: 'Unnamed', + type: 'FILESYSTEM', + path: '' + } + }, + () => { + this.refs.addStorageName.select() } - }, () => { - this.refs.addStorageName.select() - }) + ) } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - handleRemoveUnusedAttachments (attachments) { - attachmentManagement.removeAttachmentsByPaths(attachments) + handleRemoveUnusedAttachments(attachments) { + attachmentManagement + .removeAttachmentsByPaths(attachments) .then(() => this.loadAttachmentStorage()) .catch(console.error) } - renderList () { + renderList() { const { data, boundingBox } = this.props const { attachments } = this.state - const unusedAttachments = attachments.filter(attachment => !attachment.isInUse) - const inUseAttachments = attachments.filter(attachment => attachment.isInUse) + const unusedAttachments = attachments.filter( + attachment => !attachment.isInUse + ) + const inUseAttachments = attachments.filter( + attachment => attachment.isInUse + ) const totalUnusedAttachments = unusedAttachments.length const totalInuseAttachments = inUseAttachments.length const totalAttachments = totalUnusedAttachments + totalInuseAttachments - const totalUnusedAttachmentsSize = unusedAttachments - .reduce((acc, curr) => { - const stats = fs.statSync(curr.path) - const fileSizeInBytes = stats.size - return acc + fileSizeInBytes - }, 0) - const totalInuseAttachmentsSize = inUseAttachments - .reduce((acc, curr) => { - const stats = fs.statSync(curr.path) - const fileSizeInBytes = stats.size - return acc + fileSizeInBytes - }, 0) - const totalAttachmentsSize = totalUnusedAttachmentsSize + totalInuseAttachmentsSize + const totalUnusedAttachmentsSize = unusedAttachments.reduce((acc, curr) => { + const stats = fs.statSync(curr.path) + const fileSizeInBytes = stats.size + return acc + fileSizeInBytes + }, 0) + const totalInuseAttachmentsSize = inUseAttachments.reduce((acc, curr) => { + const stats = fs.statSync(curr.path) + const fileSizeInBytes = stats.size + return acc + fileSizeInBytes + }, 0) + const totalAttachmentsSize = + totalUnusedAttachmentsSize + totalInuseAttachmentsSize - const unusedAttachmentPaths = unusedAttachments - .reduce((acc, curr) => acc.concat(curr.path), []) + const unusedAttachmentPaths = unusedAttachments.reduce( + (acc, curr) => acc.concat(curr.path), + [] + ) - if (!boundingBox) { return null } - const storageList = data.storageMap.map((storage) => { - return + if (!boundingBox) { + return null + } + const storageList = data.storageMap.map(storage => { + return ( + + ) }) return (
      {i18n.__('Storage Locations')}
      - {storageList.length > 0 - ? storageList - :
      {i18n.__('No storage found.')}
      - } + {storageList.length > 0 ? ( + storageList + ) : ( +
      {i18n.__('No storage found.')}
      + )}
      -
      {i18n.__('Attachment storage')}

      - Unused attachments size: {humanFileSize(totalUnusedAttachmentsSize)} ({totalUnusedAttachments} items) + Unused attachments size: {humanFileSize(totalUnusedAttachmentsSize)} ( + {totalUnusedAttachments} items)

      - In use attachments size: {humanFileSize(totalInuseAttachmentsSize)} ({totalInuseAttachments} items) + In use attachments size: {humanFileSize(totalInuseAttachmentsSize)} ( + {totalInuseAttachments} items)

      - Total attachments size: {humanFileSize(totalAttachmentsSize)} ({totalAttachments} items) + Total attachments size: {humanFileSize(totalAttachmentsSize)} ( + {totalAttachments} items)

      -
      ) } - handleAddStorageBrowseButtonClick (e) { + handleAddStorageBrowseButtonClick(e) { browseFolder() - .then((targetPath) => { + .then(targetPath => { if (targetPath.length > 0) { const { newStorage } = this.state newStorage.path = targetPath @@ -166,13 +191,13 @@ class StoragesTab extends React.Component { }) } }) - .catch((err) => { + .catch(err => { console.error('BrowseFAILED') console.error(err) }) } - handleAddStorageChange (e) { + handleAddStorageChange(e) { const { newStorage } = this.state newStorage.name = this.refs.addStorageName.value newStorage.path = this.refs.addStoragePath.value @@ -181,13 +206,13 @@ class StoragesTab extends React.Component { }) } - handleAddStorageCreateButton (e) { + handleAddStorageCreateButton(e) { dataApi .addStorage({ name: this.state.newStorage.name, path: this.state.newStorage.path }) - .then((data) => { + .then(data => { const { dispatch } = this.props dispatch({ type: 'ADD_STORAGE', @@ -200,37 +225,39 @@ class StoragesTab extends React.Component { }) } - handleAddStorageCancelButton (e) { + handleAddStorageCancelButton(e) { this.setState({ page: 'LIST' }) } - renderAddStorage () { + renderAddStorage() { return (
      -
      {i18n.__('Add Storage')}
      -
      {i18n.__('Name')}
      - this.handleAddStorageChange(e)} + onChange={e => this.handleAddStorageChange(e)} />
      -
      {i18n.__('Type')}
      +
      + {i18n.__('Type')} +
      -
      -
      {i18n.__('Location')} +
      + {i18n.__('Location')}
      - this.handleAddStorageChange(e)} + onChange={e => this.handleAddStorageChange(e)} /> - @@ -264,21 +297,25 @@ class StoragesTab extends React.Component {
      - - + +
      -
      -
      ) } - renderContent () { + renderContent() { switch (this.state.page) { case 'ADD_STORAGE': case 'ADD_FOLDER': @@ -289,12 +326,8 @@ class StoragesTab extends React.Component { } } - render () { - return ( -
      - {this.renderContent()} -
      - ) + render() { + return
      {this.renderContent()}
      } } diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index c54ed098..e043397d 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -20,7 +20,7 @@ const electron = require('electron') const ipc = electron.ipcRenderer class UiTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { config: props.config, @@ -28,10 +28,16 @@ class UiTab extends React.Component { } } - componentDidMount () { - CodeMirror.autoLoadMode(this.codeMirrorInstance.getCodeMirror(), 'javascript') + componentDidMount() { + CodeMirror.autoLoadMode( + this.codeMirrorInstance.getCodeMirror(), + 'javascript' + ) CodeMirror.autoLoadMode(this.customCSSCM.getCodeMirror(), 'css') - CodeMirror.autoLoadMode(this.customMarkdownLintConfigCM.getCodeMirror(), 'javascript') + CodeMirror.autoLoadMode( + this.customMarkdownLintConfigCM.getCodeMirror(), + 'javascript' + ) CodeMirror.autoLoadMode(this.prettierConfigCM.getCodeMirror(), 'javascript') // Set CM editor Sizes this.customCSSCM.getCodeMirror().setSize('400px', '400px') @@ -39,27 +45,32 @@ class UiTab extends React.Component { this.customMarkdownLintConfigCM.getCodeMirror().setSize('400px', '200px') this.handleSettingDone = () => { - this.setState({UiAlert: { - type: 'success', - message: i18n.__('Successfully applied!') - }}) + this.setState({ + UiAlert: { + type: 'success', + message: i18n.__('Successfully applied!') + } + }) } - this.handleSettingError = (err) => { - this.setState({UiAlert: { - type: 'error', - message: err.message != null ? err.message : i18n.__('An error occurred!') - }}) + this.handleSettingError = err => { + this.setState({ + UiAlert: { + type: 'error', + message: + err.message != null ? err.message : i18n.__('An error occurred!') + } + }) } ipc.addListener('APP_SETTING_DONE', this.handleSettingDone) ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) } - componentWillUnmount () { + componentWillUnmount() { ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone) ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError) } - handleUIChange (e) { + handleUIChange(e) { const { codemirrorTheme } = this.state let checkHighLight = document.getElementById('checkHighLight') @@ -75,7 +86,8 @@ class UiTab extends React.Component { theme: this.refs.uiTheme.value, language: this.refs.uiLanguage.value, defaultNote: this.refs.defaultNote.value, - tagNewNoteWithFilteringTags: this.refs.tagNewNoteWithFilteringTags.checked, + tagNewNoteWithFilteringTags: this.refs.tagNewNoteWithFilteringTags + .checked, showCopyNotification: this.refs.showCopyNotification.checked, confirmDeletion: this.refs.confirmDeletion.checked, showOnlyRelatedTags: this.refs.showOnlyRelatedTags.checked, @@ -83,9 +95,8 @@ class UiTab extends React.Component { saveTagsAlphabetically: this.refs.saveTagsAlphabetically.checked, enableLiveNoteCounts: this.refs.enableLiveNoteCounts.checked, showMenuBar: this.refs.showMenuBar.checked, - disableDirectWrite: this.refs.uiD2w != null - ? this.refs.uiD2w.checked - : false + disableDirectWrite: + this.refs.uiD2w != null ? this.refs.uiD2w.checked : false }, editor: { theme: this.refs.editorTheme.value, @@ -111,7 +122,9 @@ class UiTab extends React.Component { spellcheck: this.refs.spellcheck.checked, enableSmartPaste: this.refs.enableSmartPaste.checked, enableMarkdownLint: this.refs.enableMarkdownLint.checked, - customMarkdownLintConfig: this.customMarkdownLintConfigCM.getCodeMirror().getValue(), + customMarkdownLintConfig: this.customMarkdownLintConfigCM + .getCodeMirror() + .getValue(), prettierConfig: this.prettierConfigCM.getCodeMirror().getValue(), deleteUnusedAttachments: this.refs.deleteUnusedAttachments.checked }, @@ -141,29 +154,34 @@ class UiTab extends React.Component { const newCodemirrorTheme = this.refs.editorTheme.value if (newCodemirrorTheme !== codemirrorTheme) { - const theme = consts.THEMES.find(theme => theme.name === newCodemirrorTheme) + const theme = consts.THEMES.find( + theme => theme.name === newCodemirrorTheme + ) if (theme) { checkHighLight.setAttribute('href', theme.path) } } - this.setState({ config: newConfig, codemirrorTheme: newCodemirrorTheme }, () => { - const {ui, editor, preview} = this.props.config - this.currentConfig = {ui, editor, preview} - if (_.isEqual(this.currentConfig, this.state.config)) { - this.props.haveToSave() - } else { - this.props.haveToSave({ - tab: 'UI', - type: 'warning', - message: i18n.__('Unsaved Changes!') - }) + this.setState( + { config: newConfig, codemirrorTheme: newCodemirrorTheme }, + () => { + const { ui, editor, preview } = this.props.config + this.currentConfig = { ui, editor, preview } + if (_.isEqual(this.currentConfig, this.state.config)) { + this.props.haveToSave() + } else { + this.props.haveToSave({ + tab: 'UI', + type: 'warning', + message: i18n.__('Unsaved Changes!') + }) + } } - }) + ) } - handleSaveUIClick (e) { + handleSaveUIClick(e) { const newConfig = { ui: this.state.config.ui, editor: this.state.config.editor, @@ -180,7 +198,7 @@ class UiTab extends React.Component { this.props.haveToSave() } - clearMessage () { + clearMessage() { _.debounce(() => { this.setState({ UiAlert: null @@ -188,17 +206,17 @@ class UiTab extends React.Component { }, 2000)() } - render () { + render() { const UiAlert = this.state.UiAlert - const UiAlertElement = UiAlert != null - ?

      - {UiAlert.message} -

      - : null + const UiAlertElement = + UiAlert != null ? ( +

      {UiAlert.message}

      + ) : null const themes = consts.THEMES const { config, codemirrorTheme } = this.state - const codemirrorSampleCode = 'function iamHappy (happy) {\n\tif (happy) {\n\t console.log("I am Happy!")\n\t} else {\n\t console.log("I am not Happy!")\n\t}\n};' + const codemirrorSampleCode = + 'function iamHappy (happy) {\n\tif (happy) {\n\t console.log("I am Happy!")\n\t} else {\n\t console.log("I am not Happy!")\n\t}\n};' const enableEditRulersStyle = config.editor.enableRulers ? 'block' : 'none' const fontFamily = normalizeEditorFontFamily(config.editor.fontFamily) return ( @@ -211,40 +229,52 @@ class UiTab extends React.Component { {i18n.__('Interface Theme')}
      - this.handleUIChange(e)} ref='uiTheme' > - { - uiThemes.filter(theme => !theme.isDark).sort((a, b) => a.label.localeCompare(b.label)).map(theme => { - return () - }) - } + {uiThemes + .filter(theme => !theme.isDark) + .sort((a, b) => a.label.localeCompare(b.label)) + .map(theme => { + return ( + + ) + })} - { - uiThemes.filter(theme => theme.isDark).sort((a, b) => a.label.localeCompare(b.label)).map(theme => { - return () - }) - } + {uiThemes + .filter(theme => theme.isDark) + .sort((a, b) => a.label.localeCompare(b.label)) + .map(theme => { + return ( + + ) + })}
      -
      - {i18n.__('Language')} -
      +
      {i18n.__('Language')}
      - this.handleUIChange(e)} ref='uiLanguage' > - { - getLanguages().map((language) => ) - } + {getLanguages().map(language => ( + + ))}
      @@ -254,12 +284,15 @@ class UiTab extends React.Component { {i18n.__('Default New Note')}
      - this.handleUIChange(e)} ref='defaultNote' > - +
      @@ -267,103 +300,121 @@ class UiTab extends React.Component {
      - { - global.process.platform === 'win32' - ?
      + {global.process.platform === 'win32' ? ( +
      - : null - } + ) : null}
      Tags
      @@ -371,21 +422,22 @@ class UiTab extends React.Component {
      Editor
      -
      - {i18n.__('Editor Theme')} -
      +
      {i18n.__('Editor Theme')}
      - -
      +
      (this.codeMirrorInstance = e)} value={codemirrorSampleCode} @@ -394,7 +446,8 @@ class UiTab extends React.Component { readOnly: true, mode: 'javascript', theme: codemirrorTheme - }} /> + }} + />
      @@ -403,10 +456,11 @@ class UiTab extends React.Component { {i18n.__('Editor Font Size')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -416,10 +470,11 @@ class UiTab extends React.Component { {i18n.__('Editor Font Family')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -429,18 +484,21 @@ class UiTab extends React.Component { {i18n.__('Editor Indent Style')}
      -   - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} > @@ -454,23 +512,21 @@ class UiTab extends React.Component {
      -
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -481,12 +537,15 @@ class UiTab extends React.Component { {i18n.__('Switch to Preview')}
      -
      @@ -497,15 +556,20 @@ class UiTab extends React.Component { {i18n.__('Editor Keymap')}
      - -

      {i18n.__('⚠️ Please restart boostnote after you change the keymap')}

      +

      + {i18n.__( + '⚠️ Please restart boostnote after you change the keymap' + )} +

      @@ -514,14 +578,21 @@ class UiTab extends React.Component { {i18n.__('Snippet Default Language')}
      -
      @@ -531,10 +602,11 @@ class UiTab extends React.Component { {i18n.__('Front matter title field')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -542,99 +614,119 @@ class UiTab extends React.Component {
      @@ -643,10 +735,11 @@ class UiTab extends React.Component { {i18n.__('Matching character pairs')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -657,10 +750,11 @@ class UiTab extends React.Component { {i18n.__('Matching character triples')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -671,10 +765,11 @@ class UiTab extends React.Component { {i18n.__('Exploding character pairs')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -684,13 +779,22 @@ class UiTab extends React.Component { {i18n.__('Custom MarkdownLint Rules')}
      - this.handleUIChange(e)} + this.handleUIChange(e)} checked={this.state.config.editor.enableMarkdownLint} ref='enableMarkdownLint' type='checkbox' - />  + /> +   {i18n.__('Enable MarkdownLint')} -
      +
      + gutters: [ + 'CodeMirror-linenumbers', + 'CodeMirror-foldgutter', + 'CodeMirror-lint-markers' + ] + }} + />
      @@ -714,10 +823,11 @@ class UiTab extends React.Component { {i18n.__('Preview Font Size')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -727,124 +837,152 @@ class UiTab extends React.Component { {i18n.__('Preview Font Family')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      -
      {i18n.__('Code Block Theme')}
      +
      + {i18n.__('Code Block Theme')} +
      -
      -
      - {i18n.__('Sanitization')} -
      +
      {i18n.__('Sanitization')}
      -
      @@ -853,10 +991,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Inline Open Delimiter')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -866,10 +1005,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Inline Close Delimiter')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -879,10 +1019,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Block Open Delimiter')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -892,10 +1033,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Block Close Delimiter')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      @@ -905,26 +1047,27 @@ class UiTab extends React.Component { {i18n.__('PlantUML Server')}
      - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
      -
      - {i18n.__('Custom CSS')} -
      +
      {i18n.__('Custom CSS')}
      - this.handleUIChange(e)} + this.handleUIChange(e)} checked={config.preview.allowCustomCSS} ref='previewAllowCustomCSS' type='checkbox' - />  + /> +   {i18n.__('Allow custom CSS for preview')} -
      +
      + }} + />
      @@ -944,7 +1088,7 @@ class UiTab extends React.Component { {i18n.__('Prettier Config')}
      -
      +
      + }} + />
      - {UiAlertElement}
      diff --git a/browser/main/modals/PreferencesModal/index.js b/browser/main/modals/PreferencesModal/index.js index 86957083..2c14e6c7 100644 --- a/browser/main/modals/PreferencesModal/index.js +++ b/browser/main/modals/PreferencesModal/index.js @@ -16,7 +16,7 @@ import _ from 'lodash' import i18n from 'browser/lib/i18n' class Preferences extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -27,44 +27,39 @@ class Preferences extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.root.focus() const boundingBox = this.getContentBoundingBox() this.setState({ boundingBox }) } - switchTeam (teamId) { - this.setState({currentTeamId: teamId}) + switchTeam(teamId) { + this.setState({ currentTeamId: teamId }) } - handleNavButtonClick (tab) { - return (e) => { - this.setState({currentTab: tab}) + handleNavButtonClick(tab) { + return e => { + this.setState({ currentTab: tab }) } } - handleEscButtonClick () { + handleEscButtonClick() { this.props.close() } - renderContent () { + renderContent() { const { boundingBox } = this.state const { dispatch, config, data } = this.props switch (this.state.currentTab) { case 'INFO': - return ( - - ) + return case 'HOTKEY': return ( this.setState({HotkeyAlert: alert})} + haveToSave={alert => this.setState({ HotkeyAlert: alert })} /> ) case 'UI': @@ -72,29 +67,21 @@ class Preferences extends React.Component { this.setState({UIAlert: alert})} + haveToSave={alert => this.setState({ UIAlert: alert })} /> ) case 'CROWDFUNDING': - return ( - - ) + return case 'BLOG': return ( this.setState({BlogAlert: alert})} + haveToSave={alert => this.setState({ BlogAlert: alert })} /> ) case 'SNIPPET': - return ( - - ) + return case 'STORAGES': default: return ( @@ -107,67 +94,69 @@ class Preferences extends React.Component { } } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - getContentBoundingBox () { + getContentBoundingBox() { return this.refs.content.getBoundingClientRect() } - haveToSaveNotif (type, message) { - return ( -

      {message}

      - ) + haveToSaveNotif(type, message) { + return

      {message}

      } - render () { + render() { const content = this.renderContent() const tabs = [ - {target: 'STORAGES', label: i18n.__('Storage')}, - {target: 'HOTKEY', label: i18n.__('Hotkeys'), Hotkey: this.state.HotkeyAlert}, - {target: 'UI', label: i18n.__('Interface'), UI: this.state.UIAlert}, - {target: 'INFO', label: i18n.__('About')}, - {target: 'CROWDFUNDING', label: i18n.__('Crowdfunding')}, - {target: 'BLOG', label: i18n.__('Blog'), Blog: this.state.BlogAlert}, - {target: 'SNIPPET', label: i18n.__('Snippets')} + { target: 'STORAGES', label: i18n.__('Storage') }, + { + target: 'HOTKEY', + label: i18n.__('Hotkeys'), + Hotkey: this.state.HotkeyAlert + }, + { target: 'UI', label: i18n.__('Interface'), UI: this.state.UIAlert }, + { target: 'INFO', label: i18n.__('About') }, + { target: 'CROWDFUNDING', label: i18n.__('Crowdfunding') }, + { target: 'BLOG', label: i18n.__('Blog'), Blog: this.state.BlogAlert }, + { target: 'SNIPPET', label: i18n.__('Snippets') } ] - const navButtons = tabs.map((tab) => { + const navButtons = tabs.map(tab => { const isActive = this.state.currentTab === tab.target - const isUiHotkeyTab = _.isObject(tab[tab.label]) && tab.label === tab[tab.label].tab + const isUiHotkeyTab = + _.isObject(tab[tab.label]) && tab.label === tab[tab.label].tab return ( - ) }) return ( -
      this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >

      {i18n.__('Your preferences for Boostnote')}

      - this.handleEscButtonClick(e)} /> -
      - {navButtons} -
      + this.handleEscButtonClick(e)} + /> +
      {navButtons}
      {content}
      @@ -181,4 +170,4 @@ Preferences.propTypes = { dispatch: PropTypes.func } -export default connect((x) => x)(CSSModules(Preferences, styles)) +export default connect(x => x)(CSSModules(Preferences, styles)) diff --git a/browser/main/modals/RenameFolderModal.js b/browser/main/modals/RenameFolderModal.js index 9fdd70c8..a8d6f386 100644 --- a/browser/main/modals/RenameFolderModal.js +++ b/browser/main/modals/RenameFolderModal.js @@ -8,7 +8,7 @@ import ModalEscButton from 'browser/components/ModalEscButton' import i18n from 'browser/lib/i18n' class RenameFolderModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -16,39 +16,39 @@ class RenameFolderModal extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.name.focus() this.refs.name.select() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleChange (e) { + handleChange(e) { this.setState({ name: this.refs.name.value }) } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - handleInputKeyDown (e) { + handleInputKeyDown(e) { switch (e.keyCode) { case 13: this.confirm() } } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - confirm () { + confirm() { if (this.state.name.trim().length > 0) { const { storage, folder } = this.props dataApi @@ -56,7 +56,7 @@ class RenameFolderModal extends React.Component { name: this.state.name, color: folder.color }) - .then((data) => { + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage @@ -66,27 +66,32 @@ class RenameFolderModal extends React.Component { } } - render () { + render() { return ( -
      this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >
      {i18n.__('Rename Folder')}
      - this.handleCloseButtonClick(e)} /> + this.handleCloseButtonClick(e)} + />
      - this.handleChange(e)} - onKeyDown={(e) => this.handleInputKeyDown(e)} + onChange={e => this.handleChange(e)} + onKeyDown={e => this.handleInputKeyDown(e)} /> - diff --git a/browser/main/store.js b/browser/main/store.js index d48198a7..d48946a6 100644 --- a/browser/main/store.js +++ b/browser/main/store.js @@ -6,7 +6,7 @@ import { Map, Set } from 'browser/lib/Mutable' import _ from 'lodash' import DevTools from './DevTools' -function defaultDataMap () { +function defaultDataMap() { return { storageMap: new Map(), noteMap: new Map(), @@ -18,16 +18,16 @@ function defaultDataMap () { } } -function data (state = defaultDataMap(), action) { +function data(state = defaultDataMap(), action) { switch (action.type) { case 'INIT_ALL': state = defaultDataMap() - action.storages.forEach((storage) => { + action.storages.forEach(storage => { state.storageMap.set(storage.key, storage) }) - action.notes.some((note) => { + action.notes.some(note => { if (note === undefined) return true const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder @@ -40,7 +40,10 @@ function data (state = defaultDataMap(), action) { if (note.isTrashed) { state.trashedSet.add(uniqueKey) } - const storageNoteList = getOrInitItem(state.storageNoteMap, note.storage) + const storageNoteList = getOrInitItem( + state.storageNoteMap, + note.storage + ) storageNoteList.add(uniqueKey) const folderNoteSet = getOrInitItem(state.folderNoteMap, folderKey) @@ -51,173 +54,170 @@ function data (state = defaultDataMap(), action) { } }) return state - case 'UPDATE_NOTE': - { - const note = action.note - const uniqueKey = note.key - const folderKey = note.storage + '-' + note.folder - const oldNote = state.noteMap.get(uniqueKey) + case 'UPDATE_NOTE': { + const note = action.note + const uniqueKey = note.key + const folderKey = note.storage + '-' + note.folder + const oldNote = state.noteMap.get(uniqueKey) - state = Object.assign({}, state) - state.noteMap = new Map(state.noteMap) - state.noteMap.set(uniqueKey, note) + state = Object.assign({}, state) + state.noteMap = new Map(state.noteMap) + state.noteMap.set(uniqueKey, note) - updateStarredChange(oldNote, note, state, uniqueKey) + updateStarredChange(oldNote, note, state, uniqueKey) - if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { - state.trashedSet = new Set(state.trashedSet) - if (note.isTrashed) { - state.trashedSet.add(uniqueKey) - state.starredSet.delete(uniqueKey) - removeFromTags(note.tags, state, uniqueKey) - } else { - state.trashedSet.delete(uniqueKey) - - assignToTags(note.tags, state, uniqueKey) - - if (note.isStarred) { - state.starredSet.add(uniqueKey) - } - } - } - - // Update storageNoteMap if oldNote doesn't exist - if (oldNote == null) { - state.storageNoteMap = new Map(state.storageNoteMap) - let storageNoteSet = state.storageNoteMap.get(note.storage) - storageNoteSet = new Set(storageNoteSet) - storageNoteSet.add(uniqueKey) - state.storageNoteMap.set(note.storage, storageNoteSet) - } - - // Update foldermap if folder changed or post created - updateFolderChange(oldNote, note, state, folderKey, uniqueKey) - - if (oldNote != null) { - updateTagChanges(oldNote, note, state, uniqueKey) + if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { + state.trashedSet = new Set(state.trashedSet) + if (note.isTrashed) { + state.trashedSet.add(uniqueKey) + state.starredSet.delete(uniqueKey) + removeFromTags(note.tags, state, uniqueKey) } else { + state.trashedSet.delete(uniqueKey) + assignToTags(note.tags, state, uniqueKey) - } - return state + if (note.isStarred) { + state.starredSet.add(uniqueKey) + } + } } - case 'MOVE_NOTE': - { - const originNote = action.originNote - const originKey = originNote.key - const note = action.note - const uniqueKey = note.key - const folderKey = note.storage + '-' + note.folder - const oldNote = state.noteMap.get(uniqueKey) - state = Object.assign({}, state) - state.noteMap = new Map(state.noteMap) - state.noteMap.delete(originKey) - state.noteMap.set(uniqueKey, note) + // Update storageNoteMap if oldNote doesn't exist + if (oldNote == null) { + state.storageNoteMap = new Map(state.storageNoteMap) + let storageNoteSet = state.storageNoteMap.get(note.storage) + storageNoteSet = new Set(storageNoteSet) + storageNoteSet.add(uniqueKey) + state.storageNoteMap.set(note.storage, storageNoteSet) + } - // If storage chanced, origin key must be discarded - if (originKey !== uniqueKey) { - // From isStarred - if (originNote.isStarred) { - state.starredSet = new Set(state.starredSet) - state.starredSet.delete(originKey) - } + // Update foldermap if folder changed or post created + updateFolderChange(oldNote, note, state, folderKey, uniqueKey) - if (originNote.isTrashed) { - state.trashedSet = new Set(state.trashedSet) - state.trashedSet.delete(originKey) - } + if (oldNote != null) { + updateTagChanges(oldNote, note, state, uniqueKey) + } else { + assignToTags(note.tags, state, uniqueKey) + } - // From storageNoteMap - state.storageNoteMap = new Map(state.storageNoteMap) - let noteSet = state.storageNoteMap.get(originNote.storage) - noteSet = new Set(noteSet) - noteSet.delete(originKey) - state.storageNoteMap.set(originNote.storage, noteSet) + return state + } + case 'MOVE_NOTE': { + const originNote = action.originNote + const originKey = originNote.key + const note = action.note + const uniqueKey = note.key + const folderKey = note.storage + '-' + note.folder + const oldNote = state.noteMap.get(uniqueKey) - // From folderNoteMap - state.folderNoteMap = new Map(state.folderNoteMap) - const originFolderKey = originNote.storage + '-' + originNote.folder - let originFolderList = state.folderNoteMap.get(originFolderKey) - originFolderList = new Set(originFolderList) - originFolderList.delete(originKey) - state.folderNoteMap.set(originFolderKey, originFolderList) + state = Object.assign({}, state) + state.noteMap = new Map(state.noteMap) + state.noteMap.delete(originKey) + state.noteMap.set(uniqueKey, note) - removeFromTags(originNote.tags, state, originKey) + // If storage chanced, origin key must be discarded + if (originKey !== uniqueKey) { + // From isStarred + if (originNote.isStarred) { + state.starredSet = new Set(state.starredSet) + state.starredSet.delete(originKey) } - updateStarredChange(oldNote, note, state, uniqueKey) - - if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { + if (originNote.isTrashed) { state.trashedSet = new Set(state.trashedSet) - if (note.isTrashed) { - state.trashedSet.add(uniqueKey) - } else { - state.trashedSet.delete(uniqueKey) - } + state.trashedSet.delete(originKey) } - // Update storageNoteMap if oldNote doesn't exist - if (oldNote == null) { - state.storageNoteMap = new Map(state.storageNoteMap) - let noteSet = state.storageNoteMap.get(note.storage) - noteSet = new Set(noteSet) - noteSet.add(uniqueKey) - state.storageNoteMap.set(folderKey, noteSet) - } - - // Update foldermap if folder changed or post created - updateFolderChange(oldNote, note, state, folderKey, uniqueKey) - - // Remove from old folder map - if (oldNote != null) { - updateTagChanges(oldNote, note, state, uniqueKey) - } else { - assignToTags(note.tags, state, uniqueKey) - } - - return state - } - case 'DELETE_NOTE': - { - const uniqueKey = action.noteKey - const targetNote = state.noteMap.get(uniqueKey) - - state = Object.assign({}, state) - // From storageNoteMap state.storageNoteMap = new Map(state.storageNoteMap) - let noteSet = state.storageNoteMap.get(targetNote.storage) + let noteSet = state.storageNoteMap.get(originNote.storage) noteSet = new Set(noteSet) - noteSet.delete(uniqueKey) - state.storageNoteMap.set(targetNote.storage, noteSet) + noteSet.delete(originKey) + state.storageNoteMap.set(originNote.storage, noteSet) - if (targetNote != null) { - // From isStarred - if (targetNote.isStarred) { - state.starredSet = new Set(state.starredSet) - state.starredSet.delete(uniqueKey) - } + // From folderNoteMap + state.folderNoteMap = new Map(state.folderNoteMap) + const originFolderKey = originNote.storage + '-' + originNote.folder + let originFolderList = state.folderNoteMap.get(originFolderKey) + originFolderList = new Set(originFolderList) + originFolderList.delete(originKey) + state.folderNoteMap.set(originFolderKey, originFolderList) - if (targetNote.isTrashed) { - state.trashedSet = new Set(state.trashedSet) - state.trashedSet.delete(uniqueKey) - } - - // From folderNoteMap - const folderKey = targetNote.storage + '-' + targetNote.folder - state.folderNoteMap = new Map(state.folderNoteMap) - let folderSet = state.folderNoteMap.get(folderKey) - folderSet = new Set(folderSet) - folderSet.delete(uniqueKey) - state.folderNoteMap.set(folderKey, folderSet) - - removeFromTags(targetNote.tags, state, uniqueKey) - } - state.noteMap = new Map(state.noteMap) - state.noteMap.delete(uniqueKey) - return state + removeFromTags(originNote.tags, state, originKey) } + + updateStarredChange(oldNote, note, state, uniqueKey) + + if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { + state.trashedSet = new Set(state.trashedSet) + if (note.isTrashed) { + state.trashedSet.add(uniqueKey) + } else { + state.trashedSet.delete(uniqueKey) + } + } + + // Update storageNoteMap if oldNote doesn't exist + if (oldNote == null) { + state.storageNoteMap = new Map(state.storageNoteMap) + let noteSet = state.storageNoteMap.get(note.storage) + noteSet = new Set(noteSet) + noteSet.add(uniqueKey) + state.storageNoteMap.set(folderKey, noteSet) + } + + // Update foldermap if folder changed or post created + updateFolderChange(oldNote, note, state, folderKey, uniqueKey) + + // Remove from old folder map + if (oldNote != null) { + updateTagChanges(oldNote, note, state, uniqueKey) + } else { + assignToTags(note.tags, state, uniqueKey) + } + + return state + } + case 'DELETE_NOTE': { + const uniqueKey = action.noteKey + const targetNote = state.noteMap.get(uniqueKey) + + state = Object.assign({}, state) + + // From storageNoteMap + state.storageNoteMap = new Map(state.storageNoteMap) + let noteSet = state.storageNoteMap.get(targetNote.storage) + noteSet = new Set(noteSet) + noteSet.delete(uniqueKey) + state.storageNoteMap.set(targetNote.storage, noteSet) + + if (targetNote != null) { + // From isStarred + if (targetNote.isStarred) { + state.starredSet = new Set(state.starredSet) + state.starredSet.delete(uniqueKey) + } + + if (targetNote.isTrashed) { + state.trashedSet = new Set(state.trashedSet) + state.trashedSet.delete(uniqueKey) + } + + // From folderNoteMap + const folderKey = targetNote.storage + '-' + targetNote.folder + state.folderNoteMap = new Map(state.folderNoteMap) + let folderSet = state.folderNoteMap.get(folderKey) + folderSet = new Set(folderSet) + folderSet.delete(uniqueKey) + state.folderNoteMap.set(folderKey, folderSet) + + removeFromTags(targetNote.tags, state, uniqueKey) + } + state.noteMap = new Map(state.noteMap) + state.noteMap.delete(uniqueKey) + return state + } case 'UPDATE_FOLDER': case 'REORDER_FOLDER': case 'EXPORT_FOLDER': @@ -247,7 +247,7 @@ function data (state = defaultDataMap(), action) { state.storageNoteMap.set(action.storage.key, storageNoteSet) if (noteSet != null) { - noteSet.forEach(function handleNoteKey (noteKey) { + noteSet.forEach(function handleNoteKey(noteKey) { // Get note from noteMap const note = state.noteMap.get(noteKey) if (note != null) { @@ -269,7 +269,7 @@ function data (state = defaultDataMap(), action) { // Delete key from tag map state.tagNoteMap = new Map(state.tagNoteMap) - note.tags.forEach((tag) => { + note.tags.forEach(tag => { const tagNoteSet = getOrInitItem(state.tagNoteMap, tag) tagNoteSet.delete(noteKey) }) @@ -288,7 +288,7 @@ function data (state = defaultDataMap(), action) { state.storageNoteMap.set(action.storage.key, new Set()) state.folderNoteMap = new Map(state.folderNoteMap) state.tagNoteMap = new Map(state.tagNoteMap) - action.notes.forEach((note) => { + action.notes.forEach(note => { const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder state.noteMap.set(uniqueKey, note) @@ -307,7 +307,7 @@ function data (state = defaultDataMap(), action) { } folderNoteSet.add(uniqueKey) - note.tags.forEach((tag) => { + note.tags.forEach(tag => { const tagNoteSet = getOrInitItem(state.tagNoteMap, tag) tagNoteSet.add(uniqueKey) }) @@ -322,7 +322,7 @@ function data (state = defaultDataMap(), action) { // Remove folders from folderMap if (storage != null) { state.folderMap = new Map(state.folderMap) - storage.folders.forEach((folder) => { + storage.folders.forEach(folder => { const folderKey = storage.key + '-' + folder.key state.folderMap.delete(folderKey) }) @@ -334,17 +334,17 @@ function data (state = defaultDataMap(), action) { state.storageNoteMap.delete(action.storageKey) if (storageNoteSet != null) { const notes = storageNoteSet - .map((noteKey) => state.noteMap.get(noteKey)) - .filter((note) => note != null) + .map(noteKey => state.noteMap.get(noteKey)) + .filter(note => note != null) state.noteMap = new Map(state.noteMap) state.tagNoteMap = new Map(state.tagNoteMap) state.starredSet = new Set(state.starredSet) - notes.forEach((note) => { + notes.forEach(note => { const noteKey = note.key state.noteMap.delete(noteKey) state.starredSet.delete(noteKey) - note.tags.forEach((tag) => { + note.tags.forEach(tag => { let tagNoteSet = state.tagNoteMap.get(tag) tagNoteSet = new Set(tagNoteSet) tagNoteSet.delete(noteKey) @@ -364,7 +364,7 @@ function data (state = defaultDataMap(), action) { const defaultConfig = ConfigManager.get() -function config (state = defaultConfig, action) { +function config(state = defaultConfig, action) { switch (action.type) { case 'SET_IS_SIDENAV_FOLDED': state.isSideNavFolded = action.isFolded @@ -390,7 +390,7 @@ const defaultStatus = { updateReady: false } -function status (state = defaultStatus, action) { +function status(state = defaultStatus, action) { switch (action.type) { case 'UPDATE_AVAILABLE': return Object.assign({}, defaultStatus, { @@ -400,7 +400,7 @@ function status (state = defaultStatus, action) { return state } -function updateStarredChange (oldNote, note, state, uniqueKey) { +function updateStarredChange(oldNote, note, state, uniqueKey) { if (oldNote == null || oldNote.isStarred !== note.isStarred) { state.starredSet = new Set(state.starredSet) if (note.isStarred) { @@ -411,7 +411,7 @@ function updateStarredChange (oldNote, note, state, uniqueKey) { } } -function updateFolderChange (oldNote, note, state, folderKey, uniqueKey) { +function updateFolderChange(oldNote, note, state, folderKey, uniqueKey) { if (oldNote == null || oldNote.folder !== note.folder) { state.folderNoteMap = new Map(state.folderNoteMap) let folderNoteList = state.folderNoteMap.get(folderKey) @@ -429,7 +429,7 @@ function updateFolderChange (oldNote, note, state, folderKey, uniqueKey) { } } -function updateTagChanges (oldNote, note, state, uniqueKey) { +function updateTagChanges(oldNote, note, state, uniqueKey) { const discardedTags = _.difference(oldNote.tags, note.tags) const addedTags = _.difference(note.tags, oldNote.tags) if (discardedTags.length + addedTags.length > 0) { @@ -438,15 +438,15 @@ function updateTagChanges (oldNote, note, state, uniqueKey) { } } -function assignToTags (tags, state, uniqueKey) { +function assignToTags(tags, state, uniqueKey) { state.tagNoteMap = new Map(state.tagNoteMap) - tags.forEach((tag) => { + tags.forEach(tag => { const tagNoteList = getOrInitItem(state.tagNoteMap, tag) tagNoteList.add(uniqueKey) }) } -function removeFromTags (tags, state, uniqueKey) { +function removeFromTags(tags, state, uniqueKey) { state.tagNoteMap = new Map(state.tagNoteMap) tags.forEach(tag => { let tagNoteList = state.tagNoteMap.get(tag) @@ -458,7 +458,7 @@ function removeFromTags (tags, state, uniqueKey) { }) } -function getOrInitItem (target, key) { +function getOrInitItem(target, key) { let results = target.get(key) if (results == null) { results = new Set() @@ -476,8 +476,15 @@ const reducer = combineReducers({ router: connectRouter(history) }) -const store = createStore(reducer, undefined, process.env.NODE_ENV === 'development' - ? compose(applyMiddleware(routerMiddleware(history)), DevTools.instrument()) - : applyMiddleware(routerMiddleware(history))) +const store = createStore( + reducer, + undefined, + process.env.NODE_ENV === 'development' + ? compose( + applyMiddleware(routerMiddleware(history)), + DevTools.instrument() + ) + : applyMiddleware(routerMiddleware(history)) +) export { store, history } diff --git a/dev-scripts/dev.js b/dev-scripts/dev.js index 9698a2fe..4cff0ca2 100644 --- a/dev-scripts/dev.js +++ b/dev-scripts/dev.js @@ -15,7 +15,7 @@ const options = { quiet: true } -function startServer () { +function startServer() { config.plugins.push(new webpack.HotModuleReplacementPlugin()) config.entry.main.unshift( `webpack-dev-server/client?http://localhost:${port}/`, @@ -25,7 +25,7 @@ function startServer () { server = new WebpackDevServer(compiler, options) return new Promise((resolve, reject) => { - server.listen(port, 'localhost', function (err) { + server.listen(port, 'localhost', function(err) { if (err) { reject(err) } @@ -48,7 +48,7 @@ function startServer () { }) } -function startElectron () { +function startElectron() { spawn(electron, ['--hot', './index.js'], { stdio: 'inherit' }) .on('close', () => { server.close() diff --git a/gruntfile.js b/gruntfile.js index 207f8685..f235ceaa 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -5,13 +5,15 @@ const packager = require('electron-packager') const WIN = process.platform === 'win32' -module.exports = function (grunt) { +module.exports = function(grunt) { var authCode try { authCode = grunt.file.readJSON('secret/auth_code.json') } catch (e) { if (e.origError.code === 'ENOENT') { - console.warn('secret/auth_code.json is not found. CodeSigning is not available.') + console.warn( + 'secret/auth_code.json is not found. CodeSigning is not available.' + ) } } const OSX_COMMON_NAME = authCode != null ? authCode.OSX_COMMON_NAME : '' @@ -41,10 +43,7 @@ module.exports = function (grunt) { genericName: 'Boostnote', productDescription: 'The opensource note app for developers.', arch: 'amd64', - categories: [ - 'Development', - 'Utility' - ], + categories: ['Development', 'Utility'], icon: path.join(__dirname, 'resources/app.png'), bin: 'Boostnote' }, @@ -60,10 +59,7 @@ module.exports = function (grunt) { genericName: 'Boostnote', productDescription: 'The opensource note app for developers.', arch: 'x86_64', - categories: [ - 'Development', - 'Utility' - ], + categories: ['Development', 'Utility'], icon: path.join(__dirname, 'resources/app.png'), bin: 'Boostnote' }, @@ -80,18 +76,21 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-electron-installer-redhat') } - grunt.registerTask('compile', function () { + grunt.registerTask('compile', function() { var done = this.async() - var execPath = path.join('node_modules', '.bin', 'webpack') + ' --config webpack-production.config.js' + var execPath = + path.join('node_modules', '.bin', 'webpack') + + ' --config webpack-production.config.js' grunt.log.writeln(execPath) - ChildProcess.exec(execPath, + ChildProcess.exec( + execPath, { env: Object.assign({}, process.env, { BABEL_ENV: 'production', NODE_ENV: 'production' }) }, - function (err, stdout, stderr) { + function(err, stdout, stderr) { grunt.log.writeln(stdout) if (err) { @@ -105,7 +104,7 @@ module.exports = function (grunt) { ) }) - grunt.registerTask('pack', function (platform) { + grunt.registerTask('pack', function(platform) { grunt.log.writeln(path.join(__dirname, 'dist')) var done = this.async() var opts = { @@ -137,7 +136,7 @@ module.exports = function (grunt) { InternalName: 'Boostnote' } }) - packager(opts, function (err, appPath) { + packager(opts, function(err, appPath) { if (err) { grunt.log.writeln(err) done(err) @@ -153,7 +152,7 @@ module.exports = function (grunt) { icon: path.join(__dirname, 'resources/app.icns'), 'app-category-type': 'public.app-category.developer-tools' }) - packager(opts, function (err, appPath) { + packager(opts, function(err, appPath) { if (err) { grunt.log.writeln(err) done(err) @@ -168,7 +167,7 @@ module.exports = function (grunt) { icon: path.join(__dirname, 'resources/app.icns'), 'app-category-type': 'public.app-category.developer-tools' }) - packager(opts, function (err, appPath) { + packager(opts, function(err, appPath) { if (err) { grunt.log.writeln(err) done(err) @@ -180,15 +179,16 @@ module.exports = function (grunt) { } }) - grunt.registerTask('codesign', function (platform) { + grunt.registerTask('codesign', function(platform) { var done = this.async() if (process.platform !== 'darwin') { done(false) return } - ChildProcess.exec(`codesign --verbose --deep --force --sign \"${OSX_COMMON_NAME}\" dist/Boostnote-darwin-x64/Boostnote.app`, - function (err, stdout, stderr) { + ChildProcess.exec( + `codesign --verbose --deep --force --sign \"${OSX_COMMON_NAME}\" dist/Boostnote-darwin-x64/Boostnote.app`, + function(err, stdout, stderr) { grunt.log.writeln(stdout) if (err) { grunt.log.writeln(err) @@ -197,44 +197,43 @@ module.exports = function (grunt) { return } done() - }) + } + ) }) - grunt.registerTask('create-osx-installer', function () { + grunt.registerTask('create-osx-installer', function() { var done = this.async() var execPath = 'appdmg appdmg.json dist/Boostnote-mac.dmg' grunt.log.writeln(execPath) - ChildProcess.exec(execPath, - function (err, stdout, stderr) { - grunt.log.writeln(stdout) - if (err) { - grunt.log.writeln(err) - grunt.log.writeln(stderr) - done(false) - return - } - done() - }) + ChildProcess.exec(execPath, function(err, stdout, stderr) { + grunt.log.writeln(stdout) + if (err) { + grunt.log.writeln(err) + grunt.log.writeln(stderr) + done(false) + return + } + done() + }) }) - grunt.registerTask('zip', function (platform) { + grunt.registerTask('zip', function(platform) { var done = this.async() switch (platform) { case 'osx': - var execPath = 'cd dist/Boostnote-darwin-x64 && zip -r -y -q ../Boostnote-mac.zip Boostnote.app' + var execPath = + 'cd dist/Boostnote-darwin-x64 && zip -r -y -q ../Boostnote-mac.zip Boostnote.app' grunt.log.writeln(execPath) - ChildProcess.exec(execPath, - function (err, stdout, stderr) { - grunt.log.writeln(stdout) - if (err) { - grunt.log.writeln(err) - grunt.log.writeln(stderr) - done(false) - return - } - done() + ChildProcess.exec(execPath, function(err, stdout, stderr) { + grunt.log.writeln(stdout) + if (err) { + grunt.log.writeln(err) + grunt.log.writeln(stderr) + done(false) + return } - ) + done() + }) break default: done() @@ -242,7 +241,7 @@ module.exports = function (grunt) { } }) - function getTarget () { + function getTarget() { switch (process.platform) { case 'darwin': return 'osx' @@ -255,7 +254,7 @@ module.exports = function (grunt) { } } - grunt.registerTask('build', function (platform) { + grunt.registerTask('build', function(platform) { if (platform == null) platform = getTarget() switch (platform) { @@ -263,15 +262,26 @@ module.exports = function (grunt) { grunt.task.run(['compile', 'pack:win', 'create-windows-installer']) break case 'osx': - grunt.task.run(['compile', 'pack:osx', 'codesign', 'create-osx-installer', 'zip:osx']) + grunt.task.run([ + 'compile', + 'pack:osx', + 'codesign', + 'create-osx-installer', + 'zip:osx' + ]) break case 'linux': - grunt.task.run(['compile', 'pack:linux', 'electron-installer-debian', 'electron-installer-redhat']) + grunt.task.run([ + 'compile', + 'pack:linux', + 'electron-installer-debian', + 'electron-installer-redhat' + ]) break } }) - grunt.registerTask('pre-build', function (platform) { + grunt.registerTask('pre-build', function(platform) { if (platform == null) platform = getTarget() switch (platform) { @@ -286,11 +296,11 @@ module.exports = function (grunt) { } }) - grunt.registerTask('bfm', function () { + grunt.registerTask('bfm', function() { const Color = require('color') const parseCSS = require('css').parse - function generateRule (selector, bgColor, fgColor) { + function generateRule(selector, bgColor, fgColor) { if (bgColor.isLight()) { bgColor = bgColor.mix(fgColor, 0.05) } else { @@ -298,48 +308,79 @@ module.exports = function (grunt) { } if (selector && selector.length > 0) { - return `${selector} .cm-table-row-even { background-color: ${bgColor.rgb().string()}; }` + return `${selector} .cm-table-row-even { background-color: ${bgColor + .rgb() + .string()}; }` } else { - return `.cm-table-row-even { background-color: ${bgColor.rgb().string()}; }` + return `.cm-table-row-even { background-color: ${bgColor + .rgb() + .string()}; }` } } const root = path.join(__dirname, 'node_modules/codemirror/theme/') - const colors = fs.readdirSync(root).filter(file => file !== 'solarized.css').map(file => { - const css = parseCSS(fs.readFileSync(path.join(root, file), 'utf8')) + const colors = fs + .readdirSync(root) + .filter(file => file !== 'solarized.css') + .map(file => { + const css = parseCSS(fs.readFileSync(path.join(root, file), 'utf8')) - const rules = css.stylesheet.rules.filter(rule => rule.selectors && /\b\.CodeMirror$/.test(rule.selectors[0])) - if (rules.length === 1) { - let bgColor = Color('white') - let fgColor = Color('black') + const rules = css.stylesheet.rules.filter( + rule => rule.selectors && /\b\.CodeMirror$/.test(rule.selectors[0]) + ) + if (rules.length === 1) { + let bgColor = Color('white') + let fgColor = Color('black') - rules[0].declarations.forEach(declaration => { - if (declaration.property === 'background-color' || declaration.property === 'background') { - bgColor = Color(declaration.value.split(' ')[0]) - } else if (declaration.property === 'color') { - const value = /^(.*?)(?:\s*!important)?$/.exec(declaration.value)[1] - const match = /^rgba\((.*?),\s*1\)$/.exec(value) - if (match) { - fgColor = Color(`rgb(${match[1]})`) - } else { - fgColor = Color(value) + rules[0].declarations.forEach(declaration => { + if ( + declaration.property === 'background-color' || + declaration.property === 'background' + ) { + bgColor = Color(declaration.value.split(' ')[0]) + } else if (declaration.property === 'color') { + const value = /^(.*?)(?:\s*!important)?$/.exec( + declaration.value + )[1] + const match = /^rgba\((.*?),\s*1\)$/.exec(value) + if (match) { + fgColor = Color(`rgb(${match[1]})`) + } else { + fgColor = Color(value) + } } - } - }) + }) - return generateRule(rules[0].selectors[0], bgColor, fgColor) - } - }).filter(value => !!value) + return generateRule(rules[0].selectors[0], bgColor, fgColor) + } + }) + .filter(value => !!value) // default colors.unshift(generateRule(null, Color('white'), Color('black'))) // solarized dark - colors.push(generateRule('.cm-s-solarized.cm-s-dark', Color('#002b36'), Color('#839496'))) + colors.push( + generateRule( + '.cm-s-solarized.cm-s-dark', + Color('#002b36'), + Color('#839496') + ) + ) // solarized light - colors.push(generateRule('.cm-s-solarized.cm-s-light', Color('#fdf6e3'), Color('#657b83'))) + colors.push( + generateRule( + '.cm-s-solarized.cm-s-light', + Color('#fdf6e3'), + Color('#657b83') + ) + ) - fs.writeFileSync(path.join(__dirname, 'extra_scripts/codemirror/mode/bfm/bfm.css'), colors.join('\n'), 'utf8') + fs.writeFileSync( + path.join(__dirname, 'extra_scripts/codemirror/mode/bfm/bfm.css'), + colors.join('\n'), + 'utf8' + ) }) grunt.registerTask('default', ['build']) diff --git a/index.js b/index.js index 96f98e73..bcc4e879 100644 --- a/index.js +++ b/index.js @@ -4,30 +4,30 @@ const path = require('path') var error = null -function execMainApp () { +function execMainApp() { const appRootPath = path.join(process.execPath, '../..') const updateDotExePath = path.join(appRootPath, 'Update.exe') const exeName = path.basename(process.execPath) - function spawnUpdate (args, cb) { + function spawnUpdate(args, cb) { var stdout = '' var updateProcess = null try { updateProcess = ChildProcess.spawn(updateDotExePath, args) } catch (e) { - process.nextTick(function () { + process.nextTick(function() { cb(e) }) } - updateProcess.stdout.on('data', function (data) { + updateProcess.stdout.on('data', function(data) { stdout += data }) - updateProcess.on('error', function (_error) { + updateProcess.on('error', function(_error) { error = _error }) - updateProcess.on('close', function (code, signal) { + updateProcess.on('close', function(code, signal) { if (code !== 0) { error = new Error('Command failed: #{signal ? code}') error.code = code @@ -38,7 +38,7 @@ function execMainApp () { }) } - var handleStartupEvent = function () { + var handleStartupEvent = function() { if (process.platform !== 'win32') { return false } @@ -46,7 +46,7 @@ function execMainApp () { var squirrelCommand = process.argv[1] switch (squirrelCommand) { case '--squirrel-install': - spawnUpdate(['--createShortcut', exeName], function (err) { + spawnUpdate(['--createShortcut', exeName], function(err) { if (err) console.error(err) app.quit() }) @@ -55,7 +55,7 @@ function execMainApp () { app.quit() return true case '--squirrel-uninstall': - spawnUpdate(['--removeShortcut', exeName], function (err) { + spawnUpdate(['--removeShortcut', exeName], function(err) { if (err) console.error(err) app.quit() }) diff --git a/lib/ipcServer.js b/lib/ipcServer.js index 42e229d3..41d3ea7b 100644 --- a/lib/ipcServer.js +++ b/lib/ipcServer.js @@ -7,7 +7,7 @@ nodeIpc.config.id = 'node' nodeIpc.config.retry = 1500 nodeIpc.config.silent = true -function toggleMainWindow () { +function toggleMainWindow() { switch (global.process.platform) { case 'darwin': if (mainWindow.isFocused()) { @@ -52,14 +52,14 @@ ipcMain.on('config-renew', (e, payload) => { nodeIpc.serve( path.join(app.getPath('userData'), 'boostnote.service'), - function () { - nodeIpc.server.on('connect', function (socket) { + function() { + nodeIpc.server.on('connect', function(socket) { nodeIpc.log('ipc server >> socket joinned'.rainbow) - socket.on('close', function () { + socket.on('close', function() { nodeIpc.log('ipc server >> socket closed'.rainbow) }) }) - nodeIpc.server.on('error', function (err) { + nodeIpc.server.on('error', function(err) { nodeIpc.log('Node IPC error'.rainbow, err) }) } diff --git a/lib/main-app.js b/lib/main-app.js index f8ee1ecf..2293fd58 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -36,8 +36,9 @@ const updater = new GhReleases(ghReleasesOpts) // Check for updates // `status` returns true if there is a new update available -function checkUpdate () { - if (!isPackaged) { // Prevents app from attempting to update when in dev mode. +function checkUpdate() { + if (!isPackaged) { + // Prevents app from attempting to update when in dev mode. console.log('Updates are disabled in Development mode, see main-app.js') return true } @@ -58,29 +59,29 @@ function checkUpdate () { }) } -updater.on('update-downloaded', (info) => { +updater.on('update-downloaded', info => { if (mainWindow != null) { mainWindow.webContents.send('update-ready', 'Update available!') isUpdateReady = true } }) -updater.autoUpdater.on('error', (err) => { +updater.autoUpdater.on('error', err => { console.error(err) }) -ipc.on('update-app-confirm', function (event, msg) { +ipc.on('update-app-confirm', function(event, msg) { if (isUpdateReady) { mainWindow.removeAllListeners() updater.install() } }) -app.on('window-all-closed', function () { +app.on('window-all-closed', function() { app.quit() }) -app.on('ready', function () { +app.on('ready', function() { mainWindow = require('./main-window') var template = require('./main-menu') @@ -100,7 +101,7 @@ app.on('ready', function () { } // Check update every day - setInterval(function () { + setInterval(function() { if (isPackaged) checkUpdate() }, 1000 * 60 * 60 * 24) @@ -108,7 +109,7 @@ app.on('ready', function () { setTimeout(() => { if (isPackaged) checkUpdate() - ipc.on('update-check', function (event, msg) { + ipc.on('update-check', function(event, msg) { if (isUpdateReady) { mainWindow.webContents.send('update-ready', 'Update available!') } else { diff --git a/lib/main-menu.js b/lib/main-menu.js index 308a716c..7caef0bf 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -11,68 +11,68 @@ const LINUX = process.platform === 'linux' const boost = macOS ? { - label: 'Boostnote', - submenu: [ - { - label: 'About Boostnote', - selector: 'orderFrontStandardAboutPanel:' - }, - { - type: 'separator' - }, - { - label: 'Preferences', - accelerator: 'Command+,', - click () { - mainWindow.webContents.send('side:preferences') + label: 'Boostnote', + submenu: [ + { + label: 'About Boostnote', + selector: 'orderFrontStandardAboutPanel:' + }, + { + type: 'separator' + }, + { + label: 'Preferences', + accelerator: 'Command+,', + click() { + mainWindow.webContents.send('side:preferences') + } + }, + { + type: 'separator' + }, + { + label: 'Hide Boostnote', + accelerator: 'Command+H', + selector: 'hide:' + }, + { + label: 'Hide Others', + accelerator: 'Command+Shift+H', + selector: 'hideOtherApplications:' + }, + { + label: 'Show All', + selector: 'unhideAllApplications:' + }, + { + type: 'separator' + }, + { + label: 'Quit Boostnote', + role: 'quit', + accelerator: 'CommandOrControl+Q' } - }, - { - type: 'separator' - }, - { - label: 'Hide Boostnote', - accelerator: 'Command+H', - selector: 'hide:' - }, - { - label: 'Hide Others', - accelerator: 'Command+Shift+H', - selector: 'hideOtherApplications:' - }, - { - label: 'Show All', - selector: 'unhideAllApplications:' - }, - { - type: 'separator' - }, - { - label: 'Quit Boostnote', - role: 'quit', - accelerator: 'CommandOrControl+Q' - } - ] - } + ] + } : { - label: 'Boostnote', - submenu: [ - { - label: 'Preferences', - accelerator: 'Control+,', - click () { - mainWindow.webContents.send('side:preferences') + label: 'Boostnote', + submenu: [ + { + label: 'Preferences', + accelerator: 'Control+,', + click() { + mainWindow.webContents.send('side:preferences') + } + }, + { + type: 'separator' + }, + { + role: 'quit', + accelerator: 'Control+Q' } - }, - { - type: 'separator' - }, - { - role: 'quit', - accelerator: 'Control+Q' - } - ] - } + ] + } const file = { label: 'File', @@ -80,28 +80,28 @@ const file = { { label: 'New Note', accelerator: 'CommandOrControl+N', - click () { + click() { mainWindow.webContents.send('top:new-note') } }, { label: 'Focus Note', accelerator: 'CommandOrControl+E', - click () { + click() { mainWindow.webContents.send('detail:focus') } }, { label: 'Delete Note', accelerator: 'CommandOrControl+Shift+Backspace', - click () { + click() { mainWindow.webContents.send('detail:delete') } }, { label: 'Clone Note', accelerator: 'CommandOrControl+D', - click () { + click() { mainWindow.webContents.send('list:clone') } }, @@ -113,7 +113,7 @@ const file = { submenu: [ { label: 'Plain Text, MarkDown (.txt, .md)', - click () { + click() { mainWindow.webContents.send('import:file') } } @@ -124,28 +124,28 @@ const file = { submenu: [ { label: 'Plain Text (.txt)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-txt') mainWindow.webContents.send('export:save-text') } }, { label: 'MarkDown (.md)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-md') mainWindow.webContents.send('export:save-md') } }, { label: 'HTML (.html)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-html') mainWindow.webContents.send('export:save-html') } }, { label: 'PDF (.pdf)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-pdf') mainWindow.webContents.send('export:save-pdf') } @@ -158,13 +158,13 @@ const file = { { label: 'Generate/Update Markdown TOC', accelerator: 'Shift+Ctrl+T', - click () { + click() { mainWindow.webContents.send('code:generate-toc') } }, { label: 'Format Table', - click () { + click() { mainWindow.webContents.send('code:format-table') } }, @@ -174,7 +174,7 @@ const file = { { label: 'Print', accelerator: 'CommandOrControl+P', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'print') mainWindow.webContents.send('print') } @@ -183,20 +183,25 @@ const file = { } if (LINUX) { - file.submenu.push({ - type: 'separator' - }, { - label: 'Preferences', - accelerator: 'Control+,', - click () { - mainWindow.webContents.send('side:preferences') + file.submenu.push( + { + type: 'separator' + }, + { + label: 'Preferences', + accelerator: 'Control+,', + click() { + mainWindow.webContents.send('side:preferences') + } + }, + { + type: 'separator' + }, + { + role: 'quit', + accelerator: 'Control+Q' } - }, { - type: 'separator' - }, { - role: 'quit', - accelerator: 'Control+Q' - }) + ) } const edit = { @@ -241,7 +246,7 @@ const edit = { { label: 'Add Tag', accelerator: 'CommandOrControl+Shift+T', - click () { + click() { mainWindow.webContents.send('editor:add-tag') } } @@ -254,14 +259,14 @@ const view = { { label: 'Reload', accelerator: 'CommandOrControl+R', - click () { + click() { BrowserWindow.getFocusedWindow().reload() } }, { label: 'Toggle Developer Tools', accelerator: 'CommandOrControl+Alt+I', - click () { + click() { BrowserWindow.getFocusedWindow().toggleDevTools() } }, @@ -271,14 +276,14 @@ const view = { { label: 'Next Note', accelerator: 'CommandOrControl+]', - click () { + click() { mainWindow.webContents.send('list:next') } }, { label: 'Previous Note', accelerator: 'CommandOrControl+[', - click () { + click() { mainWindow.webContents.send('list:prior') } }, @@ -288,7 +293,7 @@ const view = { { label: 'Focus Search', accelerator: 'CommandOrControl+Shift+L', - click () { + click() { mainWindow.webContents.send('top:focus-search') } }, @@ -298,14 +303,14 @@ const view = { { label: 'Toggle Full Screen', accelerator: macOS ? 'Command+Control+F' : 'F11', - click () { + click() { mainWindow.setFullScreen(!mainWindow.isFullScreen()) } }, { label: 'Toggle Side Bar', accelerator: 'CommandOrControl+B', - click () { + click() { mainWindow.webContents.send('editor:fullscreen') } }, @@ -315,21 +320,21 @@ const view = { { label: 'Actual Size', accelerator: 'CommandOrControl+0', - click () { + click() { mainWindow.webContents.send('status:zoomreset') } }, { label: 'Zoom In', accelerator: 'CommandOrControl+=', - click () { + click() { mainWindow.webContents.send('status:zoomin') } }, { label: 'Zoom Out', accelerator: 'CommandOrControl+-', - click () { + click() { mainWindow.webContents.send('status:zoomout') } } @@ -382,38 +387,58 @@ const help = { submenu: [ { label: 'Boostnote official site', - click () { shell.openExternal('https://boostnote.io/') } + click() { + shell.openExternal('https://boostnote.io/') + } }, { label: 'Wiki', - click () { shell.openExternal('https://github.com/BoostIO/Boostnote/wiki') } + click() { + shell.openExternal('https://github.com/BoostIO/Boostnote/wiki') + } }, { label: 'Issue Tracker', - click () { shell.openExternal('https://github.com/BoostIO/Boostnote/issues') } + click() { + shell.openExternal('https://github.com/BoostIO/Boostnote/issues') + } }, { label: 'Changelog', - click () { shell.openExternal('https://github.com/BoostIO/boost-releases') } + click() { + shell.openExternal('https://github.com/BoostIO/boost-releases') + } }, { label: 'Cheatsheets', submenu: [ { label: 'Markdown', - click () { shell.openExternal('https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet') } + click() { + shell.openExternal( + 'https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet' + ) + } }, { label: 'Latex', - click () { shell.openExternal('https://katex.org/docs/supported.html') } + click() { + shell.openExternal('https://katex.org/docs/supported.html') + } }, { label: 'HTML', - click () { shell.openExternal('https://htmlcheatsheet.com/') } + click() { + shell.openExternal('https://htmlcheatsheet.com/') + } }, { label: 'Boostnote', - click () { shell.openExternal('https://github.com/TobseF/boostnote-markdown-cheatsheet/blob/master/BOOSTNOTE_MARKDOWN_CHEAT_SHEET.md') } + click() { + shell.openExternal( + 'https://github.com/TobseF/boostnote-markdown-cheatsheet/blob/master/BOOSTNOTE_MARKDOWN_CHEAT_SHEET.md' + ) + } } ] }, @@ -422,7 +447,7 @@ const help = { }, { label: 'About', - click () { + click() { const version = electron.app.getVersion() const electronVersion = process.versions.electron const chromeVersion = process.versions.chrome @@ -430,20 +455,20 @@ const help = { const v8Version = process.versions.v8 const OSInfo = `${os.type()} ${os.arch()} ${os.release()}` const detail = `Version: ${version}\nElectron: ${electronVersion}\nChrome: ${chromeVersion}\nNode.js: ${nodeVersion}\nV8: ${v8Version}\nOS: ${OSInfo}` - electron.dialog.showMessageBox(BrowserWindow.getFocusedWindow(), - { - title: 'BoostNote', - message: 'BoostNote', - type: 'info', - detail: `\n${detail}` - }) + electron.dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { + title: 'BoostNote', + message: 'BoostNote', + type: 'info', + detail: `\n${detail}` + }) } } ] } -module.exports = process.platform === 'darwin' - ? [boost, file, edit, view, window, help] - : process.platform === 'win32' - ? [boost, file, view, help] - : [file, view, help] +module.exports = + process.platform === 'darwin' + ? [boost, file, edit, view, window, help] + : process.platform === 'win32' + ? [boost, file, view, help] + : [file, view, help] diff --git a/lib/main-window.js b/lib/main-window.js index 515dc8b4..e6b0cb29 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -54,12 +54,17 @@ const mainWindow = new BrowserWindow({ }, icon: path.resolve(__dirname, '../resources/app.png') }) -const url = path.resolve(__dirname, process.env.NODE_ENV === 'development' ? './main.development.html' : './main.production.html') +const url = path.resolve( + __dirname, + process.env.NODE_ENV === 'development' + ? './main.development.html' + : './main.production.html' +) mainWindow.loadURL('file://' + url) mainWindow.setMenuBarVisibility(false) -mainWindow.webContents.on('new-window', function (e) { +mainWindow.webContents.on('new-window', function(e) { e.preventDefault() }) @@ -74,10 +79,10 @@ mainWindow.webContents.sendInputEvent({ }) if (process.platform === 'darwin') { - mainWindow.on('close', function (e) { + mainWindow.on('close', function(e) { e.preventDefault() if (mainWindow.isFullScreen()) { - mainWindow.once('leave-full-screen', function () { + mainWindow.once('leave-full-screen', function() { mainWindow.hide() }) mainWindow.setFullScreen(false) @@ -86,7 +91,7 @@ if (process.platform === 'darwin') { } }) - app.on('before-quit', function (e) { + app.on('before-quit', function(e) { mainWindow.removeAllListeners() }) } @@ -94,7 +99,7 @@ if (process.platform === 'darwin') { mainWindow.on('resize', _.throttle(storeWindowSize, 500)) mainWindow.on('move', _.throttle(storeWindowSize, 500)) -function storeWindowSize () { +function storeWindowSize() { try { config.set('windowsize', mainWindow.getBounds()) } catch (e) { @@ -103,7 +108,7 @@ function storeWindowSize () { } } -app.on('activate', function () { +app.on('activate', function() { if (mainWindow == null) return null mainWindow.show() }) diff --git a/lib/touchbar-menu.js b/lib/touchbar-menu.js index 90a64410..b3696e13 100644 --- a/lib/touchbar-menu.js +++ b/lib/touchbar-menu.js @@ -1,5 +1,5 @@ -const {TouchBar} = require('electron') -const {TouchBarButton, TouchBarSpacer} = TouchBar +const { TouchBar } = require('electron') +const { TouchBarButton, TouchBarSpacer } = TouchBar const mainWindow = require('./main-window') const allNotes = new TouchBarButton({ @@ -35,7 +35,6 @@ module.exports = new TouchBar([ allNotes, starredNotes, trash, - new TouchBarSpacer({size: 'small'}), + new TouchBarSpacer({ size: 'small' }), newNote ]) - diff --git a/package.json b/package.json index acec8d5d..36a27c1d 100644 --- a/package.json +++ b/package.json @@ -152,9 +152,11 @@ "electron-debug": "^2.2.0", "electron-devtools-installer": "^2.2.4", "electron-packager": "^12.2.0", - "eslint": "^3.13.1", + "eslint": "^4.18.2", + "eslint-config-prettier": "^6.10.0", "eslint-config-standard": "^6.2.1", "eslint-config-standard-jsx": "^3.2.0", + "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.8.2", "eslint-plugin-standard": "^3.0.1", "faker": "^3.1.0", diff --git a/tests/components/TagListItem.snapshot.test.js b/tests/components/TagListItem.snapshot.test.js index 637844e6..e128978d 100644 --- a/tests/components/TagListItem.snapshot.test.js +++ b/tests/components/TagListItem.snapshot.test.js @@ -3,7 +3,9 @@ import renderer from 'react-test-renderer' import TagListItem from 'browser/components/TagListItem' it('TagListItem renders correctly', () => { - const tagListItem = renderer.create() + const tagListItem = renderer.create( + + ) expect(tagListItem.toJSON()).toMatchSnapshot() }) diff --git a/tests/dataApi/addStorage.js b/tests/dataApi/addStorage.js index da39a993..81ddd5b3 100644 --- a/tests/dataApi/addStorage.js +++ b/tests/dataApi/addStorage.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -18,24 +21,24 @@ const v1StoragePath = path.join(os.tmpdir(), 'test/addStorage-v1-storage') // const legacyStoragePath = path.join(os.tmpdir(), 'test/addStorage-legacy-storage') // const emptyDirPath = path.join(os.tmpdir(), 'test/addStorage-empty-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.v1StorageData = TestDummy.dummyStorage(v1StoragePath) // t.context.legacyStorageData = TestDummy.dummyLegacyStorage(legacyStoragePath) localStorage.setItem('storages', JSON.stringify([])) }) -test.serial('Add Storage', (t) => { +test.serial('Add Storage', t => { const input = { type: 'FILESYSTEM', name: 'add-storage1', path: v1StoragePath } return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return addStorage(input) }) - .then(function validateResult (data) { + .then(function validateResult(data) { const { storage, notes } = data // Check data.storage @@ -48,18 +51,22 @@ test.serial('Add Storage', (t) => { // Check data.notes t.is(notes.length, t.context.v1StorageData.notes.length) - notes.forEach(function validateNote (note) { + notes.forEach(function validateNote(note) { t.is(note.storage, storage.key) }) // Check localStorage - const cacheData = _.find(JSON.parse(localStorage.getItem('storages')), {key: data.storage.key}) + const cacheData = _.find(JSON.parse(localStorage.getItem('storages')), { + key: data.storage.key + }) t.is(cacheData.name, input.name) t.is(cacheData.type, input.type) t.is(cacheData.path, input.path) // Check boostnote.json - const jsonData = CSON.readFileSync(path.join(storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(storage.path, 'boostnote.json') + ) t.true(_.isArray(jsonData.folders)) t.is(jsonData.version, '1.0') t.is(jsonData.folders.length, t.context.v1StorageData.json.folders.length) diff --git a/tests/dataApi/attachmentManagement.test.js b/tests/dataApi/attachmentManagement.test.js index 13dcedca..e49556ca 100644 --- a/tests/dataApi/attachmentManagement.test.js +++ b/tests/dataApi/attachmentManagement.test.js @@ -13,48 +13,71 @@ const sander = require('sander') const systemUnderTest = require('browser/main/lib/dataApi/attachmentManagement') -it('should test that copyAttachment should throw an error if sourcePath or storageKey or noteKey are undefined', function () { - systemUnderTest.copyAttachment(undefined, 'storageKey').then(() => {}, error => { - expect(error).toBe('sourceFilePath has to be given') - }) - systemUnderTest.copyAttachment(null, 'storageKey', 'noteKey').then(() => {}, error => { - expect(error).toBe('sourceFilePath has to be given') - }) - systemUnderTest.copyAttachment('source', undefined, 'noteKey').then(() => {}, error => { - expect(error).toBe('storageKey has to be given') - }) - systemUnderTest.copyAttachment('source', null, 'noteKey').then(() => {}, error => { - expect(error).toBe('storageKey has to be given') - }) - systemUnderTest.copyAttachment('source', 'storageKey', null).then(() => {}, error => { - expect(error).toBe('noteKey has to be given') - }) - systemUnderTest.copyAttachment('source', 'storageKey', undefined).then(() => {}, error => { - expect(error).toBe('noteKey has to be given') - }) +it('should test that copyAttachment should throw an error if sourcePath or storageKey or noteKey are undefined', function() { + systemUnderTest.copyAttachment(undefined, 'storageKey').then( + () => {}, + error => { + expect(error).toBe('sourceFilePath has to be given') + } + ) + systemUnderTest.copyAttachment(null, 'storageKey', 'noteKey').then( + () => {}, + error => { + expect(error).toBe('sourceFilePath has to be given') + } + ) + systemUnderTest.copyAttachment('source', undefined, 'noteKey').then( + () => {}, + error => { + expect(error).toBe('storageKey has to be given') + } + ) + systemUnderTest.copyAttachment('source', null, 'noteKey').then( + () => {}, + error => { + expect(error).toBe('storageKey has to be given') + } + ) + systemUnderTest.copyAttachment('source', 'storageKey', null).then( + () => {}, + error => { + expect(error).toBe('noteKey has to be given') + } + ) + systemUnderTest.copyAttachment('source', 'storageKey', undefined).then( + () => {}, + error => { + expect(error).toBe('noteKey has to be given') + } + ) }) -it('should test that copyAttachment should throw an error if sourcePath dosen\'t exists', function () { +it("should test that copyAttachment should throw an error if sourcePath dosen't exists", function() { fs.existsSync = jest.fn() fs.existsSync.mockReturnValue(false) - return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then(() => {}, error => { - expect(error).toBe('source file does not exist') - expect(fs.existsSync).toHaveBeenCalledWith('path') - }) + return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then( + () => {}, + error => { + expect(error).toBe('source file does not exist') + expect(fs.existsSync).toHaveBeenCalledWith('path') + } + ) }) -it('should test that copyAttachment works correctly assuming correct working of fs', function () { +it('should test that copyAttachment works correctly assuming correct working of fs', function() { const dummyExtension = '.ext' const sourcePath = 'path' + dummyExtension const storageKey = 'storageKey' const noteKey = 'noteKey' const dummyUniquePath = 'dummyPath' - const dummyStorage = {path: 'dummyStoragePath'} + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = {} dummyReadStream.pipe = jest.fn() - dummyReadStream.on = jest.fn((event, callback) => { callback() }) + dummyReadStream.on = jest.fn((event, callback) => { + callback() + }) fs.existsSync = jest.fn() fs.existsSync.mockReturnValue(true) fs.createReadStream = jest.fn(() => dummyReadStream) @@ -64,26 +87,43 @@ it('should test that copyAttachment works correctly assuming correct working of findStorage.findStorage.mockReturnValue(dummyStorage) uniqueSlug.mockReturnValue(dummyUniquePath) - return systemUnderTest.copyAttachment(sourcePath, storageKey, noteKey).then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, storageKey, noteKey) + .then(function(newFileName) { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(fs.createReadStream).toHaveBeenCalledWith(sourcePath) expect(fs.existsSync).toHaveBeenCalledWith(sourcePath) expect(fs.createReadStream().pipe).toHaveBeenCalled() - expect(fs.createWriteStream).toHaveBeenCalledWith(path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey, dummyUniquePath + dummyExtension)) + expect(fs.createWriteStream).toHaveBeenCalledWith( + path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey, + dummyUniquePath + dummyExtension + ) + ) expect(newFileName).toBe(dummyUniquePath + dummyExtension) }) }) -it('should test that copyAttachment creates a new folder if the attachment folder doesn\'t exist', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that copyAttachment creates a new folder if the attachment folder doesn't exist", function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' - const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER) - const attachmentFolderNoteKyPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const attachmentFolderPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER + ) + const attachmentFolderNoteKyPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) const dummyReadStream = {} dummyReadStream.pipe = jest.fn() - dummyReadStream.on = jest.fn((event, callback) => { callback() }) + dummyReadStream.on = jest.fn((event, callback) => { + callback() + }) fs.createReadStream = jest.fn(() => dummyReadStream) fs.existsSync = jest.fn() fs.existsSync.mockReturnValueOnce(true) @@ -95,8 +135,9 @@ it('should test that copyAttachment creates a new folder if the attachment folde findStorage.findStorage.mockReturnValue(dummyStorage) uniqueSlug.mockReturnValue('dummyPath') - return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then( - function () { + return systemUnderTest + .copyAttachment('path', 'storageKey', 'noteKey') + .then(function() { expect(fs.existsSync).toHaveBeenCalledWith(attachmentFolderPath) expect(fs.mkdirSync).toHaveBeenCalledWith(attachmentFolderPath) expect(fs.existsSync).toHaveBeenLastCalledWith(attachmentFolderNoteKyPath) @@ -104,12 +145,14 @@ it('should test that copyAttachment creates a new folder if the attachment folde }) }) -it('should test that copyAttachment don\'t uses a random file name if not intended ', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that copyAttachment don't uses a random file name if not intended ", function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = {} dummyReadStream.pipe = jest.fn() - dummyReadStream.on = jest.fn((event, callback) => { callback() }) + dummyReadStream.on = jest.fn((event, callback) => { + callback() + }) fs.createReadStream = jest.fn(() => dummyReadStream) fs.existsSync = jest.fn() fs.existsSync.mockReturnValueOnce(true) @@ -120,23 +163,28 @@ it('should test that copyAttachment don\'t uses a random file name if not intend findStorage.findStorage.mockReturnValue(dummyStorage) uniqueSlug.mockReturnValue('dummyPath') - return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey', false).then( - function (newFileName) { + return systemUnderTest + .copyAttachment('path', 'storageKey', 'noteKey', false) + .then(function(newFileName) { expect(newFileName).toBe('path') }) }) -it('should test that copyAttachment with url (with extension, without query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (with extension, without query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -155,23 +203,28 @@ it('should test that copyAttachment with url (with extension, without query)', f data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.jpg') }) }) -it('should test that copyAttachment with url (with extension, with query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (with extension, with query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -190,23 +243,28 @@ it('should test that copyAttachment with url (with extension, with query)', func data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.jpg') }) }) -it('should test that copyAttachment with url (without extension, without query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (without extension, without query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -225,23 +283,28 @@ it('should test that copyAttachment with url (without extension, without query)' data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.png') }) }) -it('should test that copyAttachment with url (without extension, with query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (without extension, with query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -260,13 +323,14 @@ it('should test that copyAttachment with url (without extension, with query)', f data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.png') }) }) -it('should replace the all ":storage" path with the actual storage path', function () { +it('should replace the all ":storage" path with the actual storage path', function() { const storageFolder = systemUnderTest.DESTINATION_FOLDER const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' const testInput = @@ -277,21 +341,41 @@ it('should replace the all ":storage" path with the actual storage path', functi ' \n' + '

      Headline

      \n' + '

      \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

      \n' + '

      \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

      \n' + '

      \n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

      \n' + '
      \n' +
           '            \n' +
      -    '            \n' +
      +    '            \n' +
           '        
      \n' + '
      \n' +
           '            \n' +
      -    '            \n' +
      +    '            \n' +
           '        
      \n' + ' \n' + '' @@ -304,21 +388,56 @@ it('should replace the all ":storage" path with the actual storage path', functi ' \n' + '

      Headline

      \n' + '

      \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

      \n' + '

      \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

      \n' + '

      \n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

      \n' + '
      \n' +
           '            \n' +
      -    '            \n' +
      +    '            \n' +
           '        
      \n' + '
      \n' +
           '            \n' +
      -    '            \n' +
      +    '            \n' +
           '        
      \n' + ' \n' + '' @@ -326,7 +445,7 @@ it('should replace the all ":storage" path with the actual storage path', functi expect(actual).toEqual(expectedOutput) }) -it('should replace the ":storage" path with the actual storage path when they have different path separators', function () { +it('should replace the ":storage" path with the actual storage path when they have different path separators', function() { const storageFolder = systemUnderTest.DESTINATION_FOLDER const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' const testInput = @@ -337,10 +456,18 @@ it('should replace the ":storage" path with the actual storage path when they ha ' \n' + '

      Headline

      \n' + '

      \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

      \n' + '

      \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

      \n' + ' \n' + '' @@ -353,10 +480,24 @@ it('should replace the ":storage" path with the actual storage path when they ha ' \n' + '

      Headline

      \n' + '

      \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

      \n' + '

      \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

      \n' + ' \n' + '' @@ -364,7 +505,7 @@ it('should replace the ":storage" path with the actual storage path when they ha expect(actual).toEqual(expectedOutput) }) -it('should test that generateAttachmentMarkdown works correct both with previews and without', function () { +it('should test that generateAttachmentMarkdown works correct both with previews and without', function() { const fileName = 'fileName' const path = 'path' let expected = `![${fileName}](${path})` @@ -375,55 +516,133 @@ it('should test that generateAttachmentMarkdown works correct both with previews expect(actual).toEqual(expected) }) -it('should test that migrateAttachments work when they have different path separators', function () { +it('should test that migrateAttachments work when they have different path separators', function() { sander.existsSync = jest.fn(() => true) const dummyStoragePath = 'dummyStoragePath' const imagesPath = path.join(dummyStoragePath, 'images') const attachmentsPath = path.join(dummyStoragePath, 'attachments') const noteKey = 'noteKey' - const testInput = '"# Test\n' + - '\n' + - '![Screenshot1](:storage' + path.win32.sep + '0.3b88d0dc.png)\n' + - '![Screenshot2](:storage' + path.posix.sep + '0.2cb8875c.pdf)"' + const testInput = + '"# Test\n' + + '\n' + + '![Screenshot1](:storage' + + path.win32.sep + + '0.3b88d0dc.png)\n' + + '![Screenshot2](:storage' + + path.posix.sep + + '0.2cb8875c.pdf)"' systemUnderTest.migrateAttachments(testInput, dummyStoragePath, noteKey) expect(sander.existsSync.mock.calls[0][0]).toBe(imagesPath) - expect(sander.existsSync.mock.calls[1][0]).toBe(path.join(imagesPath, '0.3b88d0dc.png')) - expect(sander.existsSync.mock.calls[2][0]).toBe(path.join(attachmentsPath, '0.3b88d0dc.png')) - expect(sander.existsSync.mock.calls[3][0]).toBe(path.join(imagesPath, '0.2cb8875c.pdf')) - expect(sander.existsSync.mock.calls[4][0]).toBe(path.join(attachmentsPath, '0.2cb8875c.pdf')) + expect(sander.existsSync.mock.calls[1][0]).toBe( + path.join(imagesPath, '0.3b88d0dc.png') + ) + expect(sander.existsSync.mock.calls[2][0]).toBe( + path.join(attachmentsPath, '0.3b88d0dc.png') + ) + expect(sander.existsSync.mock.calls[3][0]).toBe( + path.join(imagesPath, '0.2cb8875c.pdf') + ) + expect(sander.existsSync.mock.calls[4][0]).toBe( + path.join(attachmentsPath, '0.2cb8875c.pdf') + ) }) -it('should test that getAttachmentsInMarkdownContent finds all attachments when they have different path separators', function () { - const testInput = '"# Test\n' + +it('should test that getAttachmentsInMarkdownContent finds all attachments when they have different path separators', function() { + const testInput = + '"# Test\n' + '\n' + - '![Screenshot1](:storage' + path.win32.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.win32.sep + '0.3b88d0dc.png)\n' + - '![Screenshot2](:storage' + path.posix.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.posix.sep + '2cb8875c.pdf)\n' + - '![Screenshot3](:storage' + path.win32.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.posix.sep + 'bbf49b02.jpg)"' + '![Screenshot1](:storage' + + path.win32.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.win32.sep + + '0.3b88d0dc.png)\n' + + '![Screenshot2](:storage' + + path.posix.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.posix.sep + + '2cb8875c.pdf)\n' + + '![Screenshot3](:storage' + + path.win32.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.posix.sep + + 'bbf49b02.jpg)"' const actual = systemUnderTest.getAttachmentsInMarkdownContent(testInput) - const expected = [':storage' + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '0.3b88d0dc.png', ':storage' + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '2cb8875c.pdf', ':storage' + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + 'bbf49b02.jpg'] + const expected = [ + ':storage' + + path.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.sep + + '0.3b88d0dc.png', + ':storage' + + path.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.sep + + '2cb8875c.pdf', + ':storage' + + path.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.sep + + 'bbf49b02.jpg' + ] expect(actual).toEqual(expect.arrayContaining(expected)) }) -it('should test that getAbsolutePathsOfAttachmentsInContent returns all absolute paths', function () { +it('should test that getAbsolutePathsOfAttachmentsInContent returns all absolute paths', function() { const dummyStoragePath = 'dummyStoragePath' const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' - const testInput = '"# Test\n' + + const testInput = + '"# Test\n' + '\n' + - '![Screenshot1](:storage' + path.win32.sep + noteKey + path.win32.sep + '0.6r4zdgc22xp.png)\n' + - '![Screenshot2](:storage' + path.posix.sep + noteKey + path.posix.sep + '0.q2i4iw0fyx.pdf)\n' + - '![Screenshot3](:storage' + path.win32.sep + noteKey + path.posix.sep + 'd6c5ee92.jpg)"' + '![Screenshot1](:storage' + + path.win32.sep + + noteKey + + path.win32.sep + + '0.6r4zdgc22xp.png)\n' + + '![Screenshot2](:storage' + + path.posix.sep + + noteKey + + path.posix.sep + + '0.q2i4iw0fyx.pdf)\n' + + '![Screenshot3](:storage' + + path.win32.sep + + noteKey + + path.posix.sep + + 'd6c5ee92.jpg)"' - const actual = systemUnderTest.getAbsolutePathsOfAttachmentsInContent(testInput, dummyStoragePath) - const expected = [dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.6r4zdgc22xp.png', - dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.q2i4iw0fyx.pdf', - dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + 'd6c5ee92.jpg'] + const actual = systemUnderTest.getAbsolutePathsOfAttachmentsInContent( + testInput, + dummyStoragePath + ) + const expected = [ + dummyStoragePath + + path.sep + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + noteKey + + path.sep + + '0.6r4zdgc22xp.png', + dummyStoragePath + + path.sep + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + noteKey + + path.sep + + '0.q2i4iw0fyx.pdf', + dummyStoragePath + + path.sep + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + noteKey + + path.sep + + 'd6c5ee92.jpg' + ] expect(actual).toEqual(expect.arrayContaining(expected)) }) -it('should remove the all ":storage" and noteKey references', function () { +it('should remove the all ":storage" and noteKey references', function() { const storageFolder = systemUnderTest.DESTINATION_FOLDER const noteKey = 'noteKey' const testInput = @@ -434,13 +653,25 @@ it('should remove the all ":storage" and noteKey references', function () { ' \n' + '

      Headline

      \n' + '

      \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

      \n' + '

      \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

      \n' + '

      \n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

      \n' + ' \n' + '' @@ -452,62 +683,107 @@ it('should remove the all ":storage" and noteKey references', function () { ' \n' + '

      Headline

      \n' + '

      \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

      \n' + '

      \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

      \n' + '

      \n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

      \n' + ' \n' + '' - const actual = systemUnderTest.removeStorageAndNoteReferences(testInput, noteKey) + const actual = systemUnderTest.removeStorageAndNoteReferences( + testInput, + noteKey + ) expect(actual).toEqual(expectedOutput) }) -it('should make sure that "removeStorageAndNoteReferences" works with markdown content as well', function () { +it('should make sure that "removeStorageAndNoteReferences" works with markdown content as well', function() { const noteKey = 'noteKey' const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + noteKey + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + noteKey + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + noteKey + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + noteKey + + path.posix.sep + + 'pdf.pdf](pdf})' const expectedOutput = 'Test input' + - '![' + systemUnderTest.DESTINATION_FOLDER + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.DESTINATION_FOLDER + path.sep + 'pdf.pdf](pdf})' - const actual = systemUnderTest.removeStorageAndNoteReferences(testInput, noteKey) + '![' + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + 'pdf.pdf](pdf})' + const actual = systemUnderTest.removeStorageAndNoteReferences( + testInput, + noteKey + ) expect(actual).toEqual(expectedOutput) }) -it('should delete the correct attachment folder if a note is deleted', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should delete the correct attachment folder if a note is deleted', function() { + const dummyStorage = { path: 'dummyStoragePath' } const storageKey = 'storageKey' const noteKey = 'noteKey' findStorage.findStorage = jest.fn(() => dummyStorage) sander.rimrafSync = jest.fn() - const expectedPathToBeDeleted = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const expectedPathToBeDeleted = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) systemUnderTest.deleteAttachmentFolder(storageKey, noteKey) expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.rimrafSync).toHaveBeenCalledWith(expectedPathToBeDeleted) }) -it('should test that deleteAttachmentsNotPresentInNote deletes all unreferenced attachments ', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that deleteAttachmentsNotPresentInNote deletes all unreferenced attachments ', function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' const storageKey = 'storageKey' const markdownContent = '' const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg'] - const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const attachmentFolderPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) findStorage.findStorage = jest.fn(() => dummyStorage) fs.existsSync = jest.fn(() => true) - fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder)) + fs.readdir = jest.fn((paht, callback) => + callback(undefined, dummyFilesInFolder) + ) fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.existsSync).toHaveBeenLastCalledWith(attachmentFolderPath) expect(fs.readdir).toHaveBeenCalledTimes(1) expect(fs.readdir.mock.calls[0][0]).toBe(attachmentFolderPath) @@ -518,35 +794,59 @@ it('should test that deleteAttachmentsNotPresentInNote deletes all unreferenced fsUnlinkCallArguments.push(fs.unlink.mock.calls[i][0]) } - dummyFilesInFolder.forEach(function (file) { - expect(fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, file))).toBe(true) + dummyFilesInFolder.forEach(function(file) { + expect( + fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, file)) + ).toBe(true) }) }) -it('should test that deleteAttachmentsNotPresentInNote does not delete referenced attachments', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that deleteAttachmentsNotPresentInNote does not delete referenced attachments', function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' const storageKey = 'storageKey' const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg'] - const markdownContent = systemUnderTest.generateAttachmentMarkdown('fileLabel', path.join(systemUnderTest.STORAGE_FOLDER_PLACEHOLDER, noteKey, dummyFilesInFolder[0]), false) - const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const markdownContent = systemUnderTest.generateAttachmentMarkdown( + 'fileLabel', + path.join( + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER, + noteKey, + dummyFilesInFolder[0] + ), + false + ) + const attachmentFolderPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) findStorage.findStorage = jest.fn(() => dummyStorage) fs.existsSync = jest.fn(() => true) - fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder)) + fs.readdir = jest.fn((paht, callback) => + callback(undefined, dummyFilesInFolder) + ) fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.unlink).toHaveBeenCalledTimes(dummyFilesInFolder.length - 1) const fsUnlinkCallArguments = [] for (let i = 0; i < dummyFilesInFolder.length - 1; i++) { fsUnlinkCallArguments.push(fs.unlink.mock.calls[i][0]) } - expect(fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, dummyFilesInFolder[0]))).toBe(false) + expect( + fsUnlinkCallArguments.includes( + path.join(attachmentFolderPath, dummyFilesInFolder[0]) + ) + ).toBe(false) }) -it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was null', function () { +it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was null', function() { const noteKey = null const storageKey = null const markdownContent = '' @@ -556,13 +856,17 @@ it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, fs.readdir = jest.fn() fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.existsSync).not.toHaveBeenCalled() expect(fs.readdir).not.toHaveBeenCalled() expect(fs.unlink).not.toHaveBeenCalled() }) -it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was undefined', function () { +it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was undefined', function() { const noteKey = undefined const storageKey = undefined const markdownContent = '' @@ -572,47 +876,70 @@ it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, fs.readdir = jest.fn() fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.existsSync).not.toHaveBeenCalled() expect(fs.readdir).not.toHaveBeenCalled() expect(fs.unlink).not.toHaveBeenCalled() }) -it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was undefined', function () { +it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was undefined', function() { const noteKey = undefined const storageKey = undefined const markdownContent = '' - const result = systemUnderTest.getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) + const result = systemUnderTest.getAttachmentsPathAndStatus( + markdownContent, + storageKey, + noteKey + ) expect(result).toBeNull() }) -it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was null', function () { +it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was null', function() { const noteKey = null const storageKey = null const markdownContent = '' - const result = systemUnderTest.getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) + const result = systemUnderTest.getAttachmentsPathAndStatus( + markdownContent, + storageKey, + noteKey + ) expect(result).toBeNull() }) -it('should test that getAttachmentsPathAndStatus return the correct path and status for attachments', async function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that getAttachmentsPathAndStatus return the correct path and status for attachments', async function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' const storageKey = 'storageKey' const markdownContent = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + noteKey + path.win32.sep + 'file2.pdf](file2.pdf) \n' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + noteKey + + path.win32.sep + + 'file2.pdf](file2.pdf) \n' const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg'] findStorage.findStorage = jest.fn(() => dummyStorage) fs.existsSync = jest.fn(() => true) - fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder)) + fs.readdir = jest.fn((paht, callback) => + callback(undefined, dummyFilesInFolder) + ) fs.unlink = jest.fn() const targetStorage = findStorage.findStorage(storageKey) - const attachments = await systemUnderTest.getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) + const attachments = await systemUnderTest.getAttachmentsPathAndStatus( + markdownContent, + storageKey, + noteKey + ) expect(attachments.length).toBe(3) expect(attachments[0].isInUse).toBe(false) expect(attachments[1].isInUse).toBe(true) @@ -644,7 +971,7 @@ it('should test that getAttachmentsPathAndStatus return the correct path and sta ) }) -it('should test that moveAttachments moves attachments only if the source folder existed', function () { +it('should test that moveAttachments moves attachments only if the source folder existed', function() { fse.existsSync = jest.fn(() => false) fse.moveSync = jest.fn() @@ -654,14 +981,24 @@ it('should test that moveAttachments moves attachments only if the source folder const newNoteKey = 'newNoteKey' const content = '' - const expectedSource = path.join(oldPath, systemUnderTest.DESTINATION_FOLDER, oldNoteKey) + const expectedSource = path.join( + oldPath, + systemUnderTest.DESTINATION_FOLDER, + oldNoteKey + ) - systemUnderTest.moveAttachments(oldPath, newPath, oldNoteKey, newNoteKey, content) + systemUnderTest.moveAttachments( + oldPath, + newPath, + oldNoteKey, + newNoteKey, + content + ) expect(fse.existsSync).toHaveBeenCalledWith(expectedSource) expect(fse.moveSync).not.toHaveBeenCalled() }) -it('should test that moveAttachments moves attachments to the right destination', function () { +it('should test that moveAttachments moves attachments to the right destination', function() { fse.existsSync = jest.fn(() => true) fse.moveSync = jest.fn() @@ -671,15 +1008,29 @@ it('should test that moveAttachments moves attachments to the right destination' const newNoteKey = 'newNoteKey' const content = '' - const expectedSource = path.join(oldPath, systemUnderTest.DESTINATION_FOLDER, oldNoteKey) - const expectedDestination = path.join(newPath, systemUnderTest.DESTINATION_FOLDER, newNoteKey) + const expectedSource = path.join( + oldPath, + systemUnderTest.DESTINATION_FOLDER, + oldNoteKey + ) + const expectedDestination = path.join( + newPath, + systemUnderTest.DESTINATION_FOLDER, + newNoteKey + ) - systemUnderTest.moveAttachments(oldPath, newPath, oldNoteKey, newNoteKey, content) + systemUnderTest.moveAttachments( + oldPath, + newPath, + oldNoteKey, + newNoteKey, + content + ) expect(fse.existsSync).toHaveBeenCalledWith(expectedSource) expect(fse.moveSync).toHaveBeenCalledWith(expectedSource, expectedDestination) }) -it('should test that moveAttachments returns a correct modified content version', function () { +it('should test that moveAttachments returns a correct modified content version', function() { fse.existsSync = jest.fn() fse.moveSync = jest.fn() @@ -689,63 +1040,159 @@ it('should test that moveAttachments returns a correct modified content version' const newNoteKey = 'newNoteKey' const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNoteKey + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNoteKey + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + oldNoteKey + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + oldNoteKey + + path.posix.sep + + 'pdf.pdf](pdf})' const expectedOutput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + 'pdf.pdf](pdf})' - const actualContent = systemUnderTest.moveAttachments(oldPath, newPath, oldNoteKey, newNoteKey, testInput) + const actualContent = systemUnderTest.moveAttachments( + oldPath, + newPath, + oldNoteKey, + newNoteKey, + testInput + ) expect(actualContent).toBe(expectedOutput) }) -it('should test that cloneAttachments modifies the content of the new note correctly', function () { - const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'} - const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'} +it('should test that cloneAttachments modifies the content of the new note correctly', function() { + const oldNote = { + key: 'oldNoteKey', + content: 'oldNoteContent', + storage: 'storageKey', + type: 'MARKDOWN_NOTE' + } + const newNote = { + key: 'newNoteKey', + content: 'oldNoteContent', + storage: 'storageKey', + type: 'MARKDOWN_NOTE' + } const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNote.key + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + oldNote.key + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + oldNote.key + + path.posix.sep + + 'pdf.pdf](pdf})' newNote.content = testInput findStorage.findStorage = jest.fn() - findStorage.findStorage.mockReturnValue({path: 'dummyStoragePath'}) + findStorage.findStorage.mockReturnValue({ path: 'dummyStoragePath' }) const expectedOutput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNote.key + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNote.key + path.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNote.key + + path.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNote.key + + path.sep + + 'pdf.pdf](pdf})' systemUnderTest.cloneAttachments(oldNote, newNote) expect(newNote.content).toBe(expectedOutput) }) -it('should test that cloneAttachments finds all attachments and copies them to the new location', function () { +it('should test that cloneAttachments finds all attachments and copies them to the new location', function() { const storagePathOld = 'storagePathOld' const storagePathNew = 'storagePathNew' - const dummyStorageOld = {path: storagePathOld} - const dummyStorageNew = {path: storagePathNew} - const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKeyOldNote', type: 'MARKDOWN_NOTE'} - const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'MARKDOWN_NOTE'} + const dummyStorageOld = { path: storagePathOld } + const dummyStorageNew = { path: storagePathNew } + const oldNote = { + key: 'oldNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyOldNote', + type: 'MARKDOWN_NOTE' + } + const newNote = { + key: 'newNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyNewNote', + type: 'MARKDOWN_NOTE' + } const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNote.key + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + oldNote.key + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + oldNote.key + + path.posix.sep + + 'pdf.pdf](pdf})' oldNote.content = testInput newNote.content = testInput - const copyFileSyncResp = {to: jest.fn()} + const copyFileSyncResp = { to: jest.fn() } sander.copyFileSync = jest.fn() sander.copyFileSync.mockReturnValue(copyFileSyncResp) findStorage.findStorage = jest.fn() findStorage.findStorage.mockReturnValueOnce(dummyStorageOld) findStorage.findStorage.mockReturnValue(dummyStorageNew) - const pathAttachmentOneFrom = path.join(storagePathOld, systemUnderTest.DESTINATION_FOLDER, oldNote.key, 'image.jpg') - const pathAttachmentOneTo = path.join(storagePathNew, systemUnderTest.DESTINATION_FOLDER, newNote.key, 'image.jpg') + const pathAttachmentOneFrom = path.join( + storagePathOld, + systemUnderTest.DESTINATION_FOLDER, + oldNote.key, + 'image.jpg' + ) + const pathAttachmentOneTo = path.join( + storagePathNew, + systemUnderTest.DESTINATION_FOLDER, + newNote.key, + 'image.jpg' + ) - const pathAttachmentTwoFrom = path.join(storagePathOld, systemUnderTest.DESTINATION_FOLDER, oldNote.key, 'pdf.pdf') - const pathAttachmentTwoTo = path.join(storagePathNew, systemUnderTest.DESTINATION_FOLDER, newNote.key, 'pdf.pdf') + const pathAttachmentTwoFrom = path.join( + storagePathOld, + systemUnderTest.DESTINATION_FOLDER, + oldNote.key, + 'pdf.pdf' + ) + const pathAttachmentTwoTo = path.join( + storagePathNew, + systemUnderTest.DESTINATION_FOLDER, + newNote.key, + 'pdf.pdf' + ) systemUnderTest.cloneAttachments(oldNote, newNote) @@ -759,9 +1206,19 @@ it('should test that cloneAttachments finds all attachments and copies them to t expect(copyFileSyncResp.to.mock.calls[1][0]).toBe(pathAttachmentTwoTo) }) -it('should test that cloneAttachments finds all attachments and copies them to the new location', function () { - const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKeyOldNote', type: 'SOMETHING_ELSE'} - const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'SOMETHING_ELSE'} +it('should test that cloneAttachments finds all attachments and copies them to the new location', function() { + const oldNote = { + key: 'oldNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyOldNote', + type: 'SOMETHING_ELSE' + } + const newNote = { + key: 'newNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyNewNote', + type: 'SOMETHING_ELSE' + } const testInput = 'Test input' oldNote.content = testInput newNote.content = testInput @@ -775,82 +1232,274 @@ it('should test that cloneAttachments finds all attachments and copies them to t expect(sander.copyFileSync).not.toHaveBeenCalled() }) -it('should test that isAttachmentLink works correctly', function () { +it('should test that isAttachmentLink works correctly', function() { expect(systemUnderTest.isAttachmentLink('text')).toBe(false) expect(systemUnderTest.isAttachmentLink('text [linkText](link)')).toBe(false) expect(systemUnderTest.isAttachmentLink('text ![linkText](link)')).toBe(false) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf )')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText ](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf )')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText ](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf )' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [ linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText ](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf )' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [ linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText ](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) }) -it('should test that handleAttachmentLinkPaste copies the attachments to the new location', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste copies the attachments to the new location', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePath, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePath, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste copies the attachments to the new location - win32 path', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste copies the attachments to the new location - win32 path', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePath, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePath, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist - win32 path', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that handleAttachmentLinkPaste don't try to copy the file if it does not exist - win32 path", function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(false)) systemUnderTest.copyAttachment = jest.fn() systemUnderTest.generateFileNotFoundMarkdown = jest.fn() - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) @@ -858,20 +1507,32 @@ it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it }) }) -it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist -- posix', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that handleAttachmentLinkPaste don't try to copy the file if it does not exist -- posix", function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(false)) systemUnderTest.copyAttachment = jest.fn() systemUnderTest.generateFileNotFoundMarkdown = jest.fn() - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) @@ -879,106 +1540,217 @@ it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it }) }) -it('should test that handleAttachmentLinkPaste copies multiple attachments if multiple were pasted', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste copies multiple attachments if multiple were pasted', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) ..' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf) ..' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') - const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg') + const expectedSourceFilePathOne = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) + const expectedSourceFilePathTwo = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'img.jpg' + ) sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathOne) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathTwo) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePathOne, storageKey, newNoteKey) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePathTwo, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePathOne, + storageKey, + newNoteKey + ) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePathTwo, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyNewFileName = 'dummyNewFileName' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' - const expectedText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileName + ')' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf)' + const expectedText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyNewFileName + + ')' const storageKey = 'storageKey' sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve(dummyNewFileName)) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve(dummyNewFileName) + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple links are posted', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple links are posted', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyNewFileNameOne = 'dummyNewFileName' const dummyNewFileNameTwo = 'dummyNewFileNameTwo' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' - const expectedText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileNameOne + ') ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileNameTwo + ')' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf) ' + + '![secondImage](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'img.jpg)' + const expectedText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyNewFileNameOne + + ') ' + + '![secondImage](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyNewFileNameTwo + + ')' const storageKey = 'storageKey' sander.exists = jest.fn(() => Promise.resolve(true)) systemUnderTest.copyAttachment = jest.fn() - systemUnderTest.copyAttachment.mockReturnValueOnce(Promise.resolve(dummyNewFileNameOne)) - systemUnderTest.copyAttachment.mockReturnValue(Promise.resolve(dummyNewFileNameTwo)) + systemUnderTest.copyAttachment.mockReturnValueOnce( + Promise.resolve(dummyNewFileNameOne) + ) + systemUnderTest.copyAttachment.mockReturnValue( + Promise.resolve(dummyNewFileNameTwo) + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedText) }) }) -it('should test that handleAttachmentLinkPaste calls the copy method correct if multiple links are posted where one file was found and one was not', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste calls the copy method correct if multiple links are posted where one file was found and one was not', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) ..' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf) ..' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') - const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg') + const expectedSourceFilePathOne = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) + const expectedSourceFilePathTwo = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'img.jpg' + ) sander.exists = jest.fn() sander.exists.mockReturnValueOnce(Promise.resolve(false)) sander.exists.mockReturnValue(Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) systemUnderTest.generateFileNotFoundMarkdown = jest.fn() - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathOne) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathTwo) expect(systemUnderTest.copyAttachment).toHaveBeenCalledTimes(1) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePathTwo, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePathTwo, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if the file was not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if the file was not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt.png](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt.png](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf)' const storageKey = 'storageKey' const fileNotFoundMD = 'file not found' const expectedPastText = 'text ' + fileNotFoundMD @@ -986,19 +1758,31 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past systemUnderTest.generateFileNotFoundMarkdown = jest.fn(() => fileNotFoundMD) sander.exists = jest.fn(() => Promise.resolve(false)) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple files were not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple files were not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf) ' + + '![secondImage](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'img.jpg)' const storageKey = 'storageKey' const fileNotFoundMD = 'file not found' const expectedPastText = 'text ' + fileNotFoundMD + ' ' + fileNotFoundMD @@ -1006,56 +1790,102 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past sander.exists = jest.fn(() => Promise.resolve(false)) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyFoundFileName = 'dummyFileName' const fileNotFoundMD = 'file not found' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) .. ' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf) .. ' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedPastText = 'text ' + fileNotFoundMD + ' .. ![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyFoundFileName + ')' + const expectedPastText = + 'text ' + + fileNotFoundMD + + ' .. ![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyFoundFileName + + ')' sander.exists = jest.fn() sander.exists.mockReturnValueOnce(Promise.resolve(false)) sander.exists.mockReturnValue(Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve(dummyFoundFileName)) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve(dummyFoundFileName) + ) systemUnderTest.generateFileNotFoundMarkdown = jest.fn(() => fileNotFoundMD) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyFoundFileName = 'dummyFileName' const fileNotFoundMD = 'file not found' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) .. ' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf) .. ' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedPastText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyFoundFileName + ') .. ' + fileNotFoundMD + const expectedPastText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyFoundFileName + + ') .. ' + + fileNotFoundMD sander.exists = jest.fn() sander.exists.mockReturnValueOnce(Promise.resolve(true)) sander.exists.mockReturnValue(Promise.resolve(false)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve(dummyFoundFileName)) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve(dummyFoundFileName) + ) systemUnderTest.generateFileNotFoundMarkdown = jest.fn(() => fileNotFoundMD) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) diff --git a/tests/dataApi/copyFile-test.js b/tests/dataApi/copyFile-test.js index 533b1354..f38f1ed2 100644 --- a/tests/dataApi/copyFile-test.js +++ b/tests/dataApi/copyFile-test.js @@ -13,13 +13,13 @@ const srcPath = path.join(srcFolder, testFile) const dstFolder = path.join(__dirname, '😇') const dstPath = path.join(dstFolder, testFile) -test.before((t) => { +test.before(t => { if (!fs.existsSync(srcFolder)) fs.mkdirSync(srcFolder) fs.writeFileSync(srcPath, 'test') }) -test('`copyFile` should handle encoded URI on src path', (t) => { +test('`copyFile` should handle encoded URI on src path', t => { return copyFile(encodeURI(srcPath), dstPath) .then(() => { t.true(true) @@ -29,10 +29,9 @@ test('`copyFile` should handle encoded URI on src path', (t) => { }) }) -test.after((t) => { +test.after(t => { fs.unlinkSync(srcPath) fs.unlinkSync(dstPath) execSync(removeDirCommand + '"' + srcFolder + '"') execSync(removeDirCommand + '"' + dstFolder + '"') }) - diff --git a/tests/dataApi/createFolder-test.js b/tests/dataApi/createFolder-test.js index 1b9a1c83..fd54ba27 100644 --- a/tests/dataApi/createFolder-test.js +++ b/tests/dataApi/createFolder-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -16,30 +19,32 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/create-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Create a folder', (t) => { +test.serial('Create a folder', t => { const storageKey = t.context.storage.cache.key const input = { name: 'created', color: '#ff5555' } return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return createFolder(storageKey, input) }) - .then(function assert (data) { + .then(function assert(data) { t.true(_.find(data.storage.folders, input) != null) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) console.log(path.join(data.storage.path, 'boostnote.json')) t.true(_.find(jsonData.folders, input) != null) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/createNote-test.js b/tests/dataApi/createNote-test.js index 3606dfd4..2c3af348 100644 --- a/tests/dataApi/createNote-test.js +++ b/tests/dataApi/createNote-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -16,26 +19,30 @@ const faker = require('faker') const storagePath = path.join(os.tmpdir(), 'test/create-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Create a note', (t) => { +test.serial('Create a note', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key - const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) + const randLinesHighlightedArray = new Array(10) + .fill() + .map(() => Math.round(Math.random() * 10)) const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines(), - linesHighlighted: randLinesHighlightedArray - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } @@ -51,18 +58,20 @@ test.serial('Create a note', (t) => { input2.title = input2.content.split('\n').shift() return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return Promise.all([ createNote(storageKey, input1), createNote(storageKey, input2) ]) }) - .then(function assert (data) { + .then(function assert(data) { const data1 = data[0] const data2 = data[1] t.is(storageKey, data1.storage) - const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + const jsonData1 = CSON.readFileSync( + path.join(storagePath, 'notes', data1.key + '.cson') + ) t.is(input1.title, data1.title) t.is(input1.title, jsonData1.title) @@ -76,11 +85,19 @@ test.serial('Create a note', (t) => { t.is(input1.snippets[0].content, jsonData1.snippets[0].content) t.is(input1.snippets[0].name, data1.snippets[0].name) t.is(input1.snippets[0].name, jsonData1.snippets[0].name) - t.deepEqual(input1.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) - t.deepEqual(input1.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) + t.deepEqual( + input1.snippets[0].linesHighlighted, + data1.snippets[0].linesHighlighted + ) + t.deepEqual( + input1.snippets[0].linesHighlighted, + jsonData1.snippets[0].linesHighlighted + ) t.is(storageKey, data2.storage) - const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) + const jsonData2 = CSON.readFileSync( + path.join(storagePath, 'notes', data2.key + '.cson') + ) t.is(input2.title, data2.title) t.is(input2.title, jsonData2.title) t.is(input2.content, data2.content) @@ -92,7 +109,7 @@ test.serial('Create a note', (t) => { }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/createNoteFromUrl-test.js b/tests/dataApi/createNoteFromUrl-test.js index a324a3e5..83b8d4e8 100644 --- a/tests/dataApi/createNoteFromUrl-test.js +++ b/tests/dataApi/createNoteFromUrl-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -15,29 +18,32 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/create-note-from-url') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Create a note from URL', (t) => { +test.serial('Create a note from URL', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key const url = 'https://shapeshed.com/writing-cross-platform-node/' - return createNoteFromUrl(url, storageKey, folderKey) - .then(function assert ({ note }) { - t.is(storageKey, note.storage) - const jsonData = CSON.readFileSync(path.join(storagePath, 'notes', note.key + '.cson')) + return createNoteFromUrl(url, storageKey, folderKey).then(function assert({ + note + }) { + t.is(storageKey, note.storage) + const jsonData = CSON.readFileSync( + path.join(storagePath, 'notes', note.key + '.cson') + ) - // Test if saved content is matching the created in memory note - t.is(note.content, jsonData.content) - t.is(note.tags.length, jsonData.tags.length) - }) + // Test if saved content is matching the created in memory note + t.is(note.content, jsonData.content) + t.is(note.tags.length, jsonData.tags.length) + }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/createSnippet-test.js b/tests/dataApi/createSnippet-test.js index 638b76ca..e1b9a570 100644 --- a/tests/dataApi/createSnippet-test.js +++ b/tests/dataApi/createSnippet-test.js @@ -7,21 +7,21 @@ const path = require('path') const snippetFilePath = path.join(os.tmpdir(), 'test', 'create-snippet') const snippetFile = path.join(snippetFilePath, 'snippets.json') -test.beforeEach((t) => { +test.beforeEach(t => { sander.writeFileSync(snippetFile, '[]') }) -test.serial('Create a snippet', (t) => { +test.serial('Create a snippet', t => { return Promise.resolve() - .then(function doTest () { - return Promise.all([ - createSnippet(snippetFile) - ]) + .then(function doTest() { + return Promise.all([createSnippet(snippetFile)]) }) - .then(function assert (data) { + .then(function assert(data) { data = data[0] const snippets = JSON.parse(sander.readFileSync(snippetFile)) - const snippet = snippets.find(currentSnippet => currentSnippet.id === data.id) + const snippet = snippets.find( + currentSnippet => currentSnippet.id === data.id + ) t.not(snippet, undefined) t.is(snippet.name, data.name) t.deepEqual(snippet.prefix, data.prefix) diff --git a/tests/dataApi/deleteFolder-test.js b/tests/dataApi/deleteFolder-test.js index af901896..8b930e48 100644 --- a/tests/dataApi/deleteFolder-test.js +++ b/tests/dataApi/deleteFolder-test.js @@ -10,7 +10,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -20,12 +23,12 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/delete-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Delete a folder', (t) => { +test.serial('Delete a folder', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key let noteKey @@ -33,44 +36,64 @@ test.serial('Delete a folder', (t) => { const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } input1.title = input1.description.split('\n').shift() return Promise.resolve() - .then(function prepare () { - return createNote(storageKey, input1) - .then(function createAttachmentFolder (data) { - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER)) - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, data.key)) + .then(function prepare() { + return createNote(storageKey, input1).then( + function createAttachmentFolder(data) { + fs.mkdirSync( + path.join(storagePath, attachmentManagement.DESTINATION_FOLDER) + ) + fs.mkdirSync( + path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + data.key + ) + ) noteKey = data.key return data - }) + } + ) }) - .then(function doTest () { + .then(function doTest() { return deleteFolder(storageKey, folderKey) }) - .then(function assert (data) { - t.true(_.find(data.storage.folders, {key: folderKey}) == null) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + .then(function assert(data) { + t.true(_.find(data.storage.folders, { key: folderKey }) == null) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) - t.true(_.find(jsonData.folders, {key: folderKey}) == null) + t.true(_.find(jsonData.folders, { key: folderKey }) == null) const notePaths = sander.readdirSync(data.storage.path, 'notes') - t.is(notePaths.length, t.context.storage.notes.filter((note) => note.folder !== folderKey).length) + t.is( + notePaths.length, + t.context.storage.notes.filter(note => note.folder !== folderKey).length + ) - const attachmentFolderPath = path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, noteKey) + const attachmentFolderPath = path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + noteKey + ) t.false(fs.existsSync(attachmentFolderPath)) }) }) -test.after.always(function after () { +test.after.always(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/deleteNote-test.js b/tests/dataApi/deleteNote-test.js index 9c809dcf..27ea478b 100644 --- a/tests/dataApi/deleteNote-test.js +++ b/tests/dataApi/deleteNote-test.js @@ -7,7 +7,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -19,56 +22,72 @@ const attachmentManagement = require('browser/main/lib/dataApi/attachmentManagem const storagePath = path.join(os.tmpdir(), 'test/delete-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Delete a note', (t) => { +test.serial('Delete a note', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } input1.title = input1.description.split('\n').shift() return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return createNote(storageKey, input1) - .then(function createAttachmentFolder (data) { - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER)) - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, data.key)) + .then(function createAttachmentFolder(data) { + fs.mkdirSync( + path.join(storagePath, attachmentManagement.DESTINATION_FOLDER) + ) + fs.mkdirSync( + path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + data.key + ) + ) return data }) - .then(function (data) { + .then(function(data) { return deleteNote(storageKey, data.key) }) }) - .then(function assert (data) { + .then(function assert(data) { try { - CSON.readFileSync(path.join(storagePath, 'notes', data.noteKey + '.cson')) + CSON.readFileSync( + path.join(storagePath, 'notes', data.noteKey + '.cson') + ) t.fail('note cson must be deleted.') } catch (err) { t.is(err.code, 'ENOENT') return data } }) - .then(function assertAttachmentFolderDeleted (data) { - const attachmentFolderPath = path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, data.noteKey) + .then(function assertAttachmentFolderDeleted(data) { + const attachmentFolderPath = path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + data.noteKey + ) t.is(fs.existsSync(attachmentFolderPath), false) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/deleteSnippet-test.js b/tests/dataApi/deleteSnippet-test.js index 9d13249b..52ec2875 100644 --- a/tests/dataApi/deleteSnippet-test.js +++ b/tests/dataApi/deleteSnippet-test.js @@ -14,18 +14,16 @@ const newSnippet = { content: '' } -test.beforeEach((t) => { +test.beforeEach(t => { sander.writeFileSync(snippetFile, JSON.stringify([newSnippet])) }) -test.serial('Delete a snippet', (t) => { +test.serial('Delete a snippet', t => { return Promise.resolve() - .then(function doTest () { - return Promise.all([ - deleteSnippet(newSnippet, snippetFile) - ]) + .then(function doTest() { + return Promise.all([deleteSnippet(newSnippet, snippetFile)]) }) - .then(function assert (data) { + .then(function assert(data) { data = data[0] const snippets = JSON.parse(sander.readFileSync(snippetFile)) t.is(snippets.length, 0) diff --git a/tests/dataApi/exportFolder-test.js b/tests/dataApi/exportFolder-test.js index fb4aaa7b..d0aef186 100644 --- a/tests/dataApi/exportFolder-test.js +++ b/tests/dataApi/exportFolder-test.js @@ -7,7 +7,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const os = require('os') @@ -17,12 +20,12 @@ const sander = require('sander') const storagePath = path.join(os.tmpdir(), 'test/export-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Export a folder', (t) => { +test.serial('Export a folder', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key @@ -37,24 +40,26 @@ test.serial('Export a folder', (t) => { const input2 = { type: 'SNIPPET_NOTE', description: 'Some normal text', - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } input2.title = 'input2' return createNote(storageKey, input1) - .then(function () { + .then(function() { return createNote(storageKey, input2) }) - .then(function () { + .then(function() { return exportFolder(storageKey, folderKey, 'md', storagePath) }) - .then(function assert () { + .then(function assert() { let filePath = path.join(storagePath, 'input1.md') t.true(fs.existsSync(filePath)) filePath = path.join(storagePath, 'input2.md') @@ -62,7 +67,7 @@ test.serial('Export a folder', (t) => { }) }) -test.after.always(function after () { +test.after.always(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/exportStorage-test.js b/tests/dataApi/exportStorage-test.js index e5594329..1ee26f19 100644 --- a/tests/dataApi/exportStorage-test.js +++ b/tests/dataApi/exportStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const os = require('os') @@ -17,7 +20,9 @@ test.beforeEach(t => { t.context.storageDir = path.join(os.tmpdir(), 'test/export-storage') t.context.storage = TestDummy.dummyStorage(t.context.storageDir) t.context.exportDir = path.join(os.tmpdir(), 'test/export-storage-output') - try { fs.mkdirSync(t.context.exportDir) } catch (e) {} + try { + fs.mkdirSync(t.context.exportDir) + } catch (e) {} localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) @@ -26,23 +31,25 @@ test.serial('Export a storage', t => { const folders = t.context.storage.json.folders const notes = t.context.storage.notes const exportDir = t.context.exportDir - const folderKeyToName = folders.reduce( - (acc, folder) => { - acc[folder.key] = folder.name - return acc - }, {}) - return exportStorage(storageKey, 'md', exportDir) - .then(() => { - notes.forEach(note => { - const noteDir = path.join(exportDir, folderKeyToName[note.folder], `${note.title}.md`) - if (note.type === 'MARKDOWN_NOTE') { - t.true(fs.existsSync(noteDir)) - t.is(fs.readFileSync(noteDir, 'utf8'), note.content) - } else if (note.type === 'SNIPPET_NOTE') { - t.false(fs.existsSync(noteDir)) - } - }) + const folderKeyToName = folders.reduce((acc, folder) => { + acc[folder.key] = folder.name + return acc + }, {}) + return exportStorage(storageKey, 'md', exportDir).then(() => { + notes.forEach(note => { + const noteDir = path.join( + exportDir, + folderKeyToName[note.folder], + `${note.title}.md` + ) + if (note.type === 'MARKDOWN_NOTE') { + t.true(fs.existsSync(noteDir)) + t.is(fs.readFileSync(noteDir, 'utf8'), note.content) + } else if (note.type === 'SNIPPET_NOTE') { + t.false(fs.existsSync(noteDir)) + } }) + }) }) test.afterEach.always(t => { diff --git a/tests/dataApi/init.js b/tests/dataApi/init.js index cacc2b2a..989e4969 100644 --- a/tests/dataApi/init.js +++ b/tests/dataApi/init.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const keygen = require('browser/lib/keygen') @@ -18,11 +21,16 @@ const v1StoragePath = path.join(os.tmpdir(), 'test/init-v1-storage') const legacyStoragePath = path.join(os.tmpdir(), 'test/init-legacy-storage') const emptyDirPath = path.join(os.tmpdir(), 'test/init-empty-storage') -test.beforeEach((t) => { +test.beforeEach(t => { localStorage.clear() // Prepare 3 types of dir - t.context.v1StorageData = TestDummy.dummyStorage(v1StoragePath, {cache: {name: 'v1'}}) - t.context.legacyStorageData = TestDummy.dummyLegacyStorage(legacyStoragePath, {cache: {name: 'legacy'}}) + t.context.v1StorageData = TestDummy.dummyStorage(v1StoragePath, { + cache: { name: 'v1' } + }) + t.context.legacyStorageData = TestDummy.dummyLegacyStorage( + legacyStoragePath, + { cache: { name: 'legacy' } } + ) t.context.emptyStorageData = { cache: { type: 'FILESYSTEM', @@ -32,27 +40,37 @@ test.beforeEach((t) => { } } - localStorage.setItem('storages', JSON.stringify([t.context.v1StorageData.cache, t.context.legacyStorageData.cache, t.context.emptyStorageData.cache])) + localStorage.setItem( + 'storages', + JSON.stringify([ + t.context.v1StorageData.cache, + t.context.legacyStorageData.cache, + t.context.emptyStorageData.cache + ]) + ) }) -test.serial('Initialize All Storages', (t) => { +test.serial('Initialize All Storages', t => { const { v1StorageData, legacyStorageData } = t.context return Promise.resolve() - .then(function test () { + .then(function test() { return init() }) - .then(function assert (data) { + .then(function assert(data) { t.true(Array.isArray(data.storages)) - t.is(data.notes.length, v1StorageData.notes.length + legacyStorageData.notes.length) + t.is( + data.notes.length, + v1StorageData.notes.length + legacyStorageData.notes.length + ) t.is(data.storages.length, 3) - data.storages.forEach(function assertStorage (storage) { + data.storages.forEach(function assertStorage(storage) { t.true(_.isString(storage.key)) t.true(_.isString(storage.name)) t.true(storage.type === 'FILESYSTEM') t.true(_.isString(storage.path)) }) }) - .then(function after () { + .then(function after() { localStorage.clear() }) }) diff --git a/tests/dataApi/migrateFromV6Storage-test.js b/tests/dataApi/migrateFromV6Storage-test.js index 7cdcdb71..92b87cec 100644 --- a/tests/dataApi/migrateFromV6Storage-test.js +++ b/tests/dataApi/migrateFromV6Storage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -16,18 +19,20 @@ const os = require('os') const dummyStoragePath = path.join(os.tmpdir(), 'test/migrate-test-storage') -test.beforeEach((t) => { - const dummyData = t.context.dummyData = TestDummy.dummyLegacyStorage(dummyStoragePath) +test.beforeEach(t => { + const dummyData = (t.context.dummyData = TestDummy.dummyLegacyStorage( + dummyStoragePath + )) console.log('init count', dummyData.notes.length) localStorage.setItem('storages', JSON.stringify([dummyData.cache])) }) -test.serial('Migrate legacy storage into v1 storage', (t) => { +test.serial('Migrate legacy storage into v1 storage', t => { return Promise.resolve() - .then(function test () { + .then(function test() { return migrateFromV6Storage(dummyStoragePath) }) - .then(function assert (data) { + .then(function assert(data) { // Check the result. It must be true if succeed. t.true(data) @@ -36,29 +41,31 @@ test.serial('Migrate legacy storage into v1 storage', (t) => { const noteDirPath = path.join(dummyStoragePath, 'notes') const fileList = sander.readdirSync(noteDirPath) t.is(dummyData.notes.length, fileList.length) - const noteMap = fileList - .map((filePath) => { - return CSON.readFileSync(path.join(noteDirPath, filePath)) - }) - dummyData.notes - .forEach(function (targetNote) { - t.true(_.find(noteMap, {title: targetNote.title, folder: targetNote.folder}) != null) - }) + const noteMap = fileList.map(filePath => { + return CSON.readFileSync(path.join(noteDirPath, filePath)) + }) + dummyData.notes.forEach(function(targetNote) { + t.true( + _.find(noteMap, { + title: targetNote.title, + folder: targetNote.folder + }) != null + ) + }) // Check legacy folder directory is removed - dummyData.json.folders - .forEach(function (folder) { - try { - sander.statSync(dummyStoragePath, folder.key) - t.fail('Folder still remains. ENOENT error must be occured.') - } catch (err) { - t.is(err.code, 'ENOENT') - } - }) + dummyData.json.folders.forEach(function(folder) { + try { + sander.statSync(dummyStoragePath, folder.key) + t.fail('Folder still remains. ENOENT error must be occured.') + } catch (err) { + t.is(err.code, 'ENOENT') + } + }) }) }) -test.after.always(function () { +test.after.always(function() { localStorage.clear() sander.rimrafSync(dummyStoragePath) }) diff --git a/tests/dataApi/moveNote-test.js b/tests/dataApi/moveNote-test.js index 3fe31c58..6718af37 100644 --- a/tests/dataApi/moveNote-test.js +++ b/tests/dataApi/moveNote-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -16,13 +19,16 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/move-note') const storagePath2 = path.join(os.tmpdir(), 'test/move-note2') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage1 = TestDummy.dummyStorage(storagePath) t.context.storage2 = TestDummy.dummyStorage(storagePath2) - localStorage.setItem('storages', JSON.stringify([t.context.storage1.cache, t.context.storage2.cache])) + localStorage.setItem( + 'storages', + JSON.stringify([t.context.storage1.cache, t.context.storage2.cache]) + ) }) -test.serial('Move a note', (t) => { +test.serial('Move a note', t => { const storageKey1 = t.context.storage1.cache.key const folderKey1 = t.context.storage1.json.folders[0].key const note1 = t.context.storage1.notes[0] @@ -31,22 +37,26 @@ test.serial('Move a note', (t) => { const folderKey2 = t.context.storage2.json.folders[0].key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return Promise.all([ moveNote(storageKey1, note1.key, storageKey1, folderKey1), moveNote(storageKey1, note2.key, storageKey2, folderKey2) ]) }) - .then(function assert (data) { + .then(function assert(data) { const data1 = data[0] const data2 = data[1] - const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + const jsonData1 = CSON.readFileSync( + path.join(storagePath, 'notes', data1.key + '.cson') + ) t.is(jsonData1.folder, folderKey1) t.is(jsonData1.title, note1.title) - const jsonData2 = CSON.readFileSync(path.join(storagePath2, 'notes', data2.key + '.cson')) + const jsonData2 = CSON.readFileSync( + path.join(storagePath2, 'notes', data2.key + '.cson') + ) t.is(jsonData2.folder, folderKey2) t.is(jsonData2.title, note2.title) try { @@ -58,7 +68,7 @@ test.serial('Move a note', (t) => { }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) sander.rimrafSync(storagePath2) diff --git a/tests/dataApi/removeStorage-test.js b/tests/dataApi/removeStorage-test.js index 33541df1..e5ac2c8f 100644 --- a/tests/dataApi/removeStorage-test.js +++ b/tests/dataApi/removeStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -14,23 +17,23 @@ const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/remove-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test('Remove a storage', (t) => { +test('Remove a storage', t => { const storageKey = t.context.storage.cache.key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return removeStorage(storageKey) }) - .then(function assert (data) { + .then(function assert(data) { t.is(JSON.parse(localStorage.getItem('storages')).length, 0) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/renameStorage-test.js b/tests/dataApi/renameStorage-test.js index f5c64cb6..46be9827 100644 --- a/tests/dataApi/renameStorage-test.js +++ b/tests/dataApi/renameStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -15,24 +18,24 @@ const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/rename-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Rename a storage', (t) => { +test.serial('Rename a storage', t => { const storageKey = t.context.storage.cache.key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return renameStorage(storageKey, 'changed') }) - .then(function assert (data) { + .then(function assert(data) { const cachedStorageList = JSON.parse(localStorage.getItem('storages')) - t.true(_.find(cachedStorageList, {key: storageKey}).name === 'changed') + t.true(_.find(cachedStorageList, { key: storageKey }).name === 'changed') }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/reorderFolder-test.js b/tests/dataApi/reorderFolder-test.js index 55bff0eb..01694b8c 100644 --- a/tests/dataApi/reorderFolder-test.js +++ b/tests/dataApi/reorderFolder-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -16,32 +19,34 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/reorder-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Reorder a folder', (t) => { +test.serial('Reorder a folder', t => { const storageKey = t.context.storage.cache.key const firstFolderKey = t.context.storage.json.folders[0].key const secondFolderKey = t.context.storage.json.folders[1].key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return reorderFolder(storageKey, 0, 1) }) - .then(function assert (data) { + .then(function assert(data) { t.true(_.nth(data.storage.folders, 0).key === secondFolderKey) t.true(_.nth(data.storage.folders, 1).key === firstFolderKey) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) t.true(_.nth(jsonData.folders, 0).key === secondFolderKey) t.true(_.nth(jsonData.folders, 1).key === firstFolderKey) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/toggleStorage-test.js b/tests/dataApi/toggleStorage-test.js index 5169a4f4..87ed0059 100644 --- a/tests/dataApi/toggleStorage-test.js +++ b/tests/dataApi/toggleStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -15,24 +18,24 @@ const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/toggle-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Toggle a storage location', (t) => { +test.serial('Toggle a storage location', t => { const storageKey = t.context.storage.cache.key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return toggleStorage(storageKey, true) }) - .then(function assert (data) { + .then(function assert(data) { const cachedStorageList = JSON.parse(localStorage.getItem('storages')) - t.true(_.find(cachedStorageList, {key: storageKey}).isOpen === true) + t.true(_.find(cachedStorageList, { key: storageKey }).isOpen === true) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/updateFolder-test.js b/tests/dataApi/updateFolder-test.js index ca9fcf99..75ad71eb 100644 --- a/tests/dataApi/updateFolder-test.js +++ b/tests/dataApi/updateFolder-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -16,12 +19,12 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/update-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Update a folder', (t) => { +test.serial('Update a folder', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key const input = { @@ -29,18 +32,20 @@ test.serial('Update a folder', (t) => { color: '#FF0000' } return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return updateFolder(storageKey, folderKey, input) }) - .then(function assert (data) { + .then(function assert(data) { t.true(_.find(data.storage.folders, input) != null) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) console.log(path.join(data.storage.path, 'boostnote.json')) t.true(_.find(jsonData.folders, input) != null) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/updateNote-test.js b/tests/dataApi/updateNote-test.js index da47c30c..40e60b64 100644 --- a/tests/dataApi/updateNote-test.js +++ b/tests/dataApi/updateNote-test.js @@ -7,7 +7,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -17,27 +20,33 @@ const faker = require('faker') const storagePath = path.join(os.tmpdir(), 'test/update-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Update a note', (t) => { +test.serial('Update a note', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key - const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) - const randLinesHighlightedArray2 = new Array(15).fill().map(() => Math.round(Math.random() * 15)) + const randLinesHighlightedArray = new Array(10) + .fill() + .map(() => Math.round(Math.random() * 10)) + const randLinesHighlightedArray2 = new Array(15) + .fill() + .map(() => Math.round(Math.random() * 15)) const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines(), - linesHighlighted: randLinesHighlightedArray - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } @@ -55,12 +64,14 @@ test.serial('Update a note', (t) => { const input3 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines(), - linesHighlighted: randLinesHighlightedArray2 - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray2 + } + ], tags: faker.lorem.words().split(' ') } input3.title = input3.description.split('\n').shift() @@ -74,26 +85,26 @@ test.serial('Update a note', (t) => { input4.title = input4.content.split('\n').shift() return Promise.resolve() - .then(function doTest () { - return Promise - .all([ - createNote(storageKey, input1), - createNote(storageKey, input2) + .then(function doTest() { + return Promise.all([ + createNote(storageKey, input1), + createNote(storageKey, input2) + ]).then(function updateNotes(data) { + const data1 = data[0] + const data2 = data[1] + return Promise.all([ + updateNote(data1.storage, data1.key, input3), + updateNote(data1.storage, data2.key, input4) ]) - .then(function updateNotes (data) { - const data1 = data[0] - const data2 = data[1] - return Promise.all([ - updateNote(data1.storage, data1.key, input3), - updateNote(data1.storage, data2.key, input4) - ]) - }) + }) }) - .then(function assert (data) { + .then(function assert(data) { const data1 = data[0] const data2 = data[1] - const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + const jsonData1 = CSON.readFileSync( + path.join(storagePath, 'notes', data1.key + '.cson') + ) t.is(input3.title, data1.title) t.is(input3.title, jsonData1.title) t.is(input3.description, data1.description) @@ -106,10 +117,18 @@ test.serial('Update a note', (t) => { t.is(input3.snippets[0].content, jsonData1.snippets[0].content) t.is(input3.snippets[0].name, data1.snippets[0].name) t.is(input3.snippets[0].name, jsonData1.snippets[0].name) - t.deepEqual(input3.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) - t.deepEqual(input3.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) + t.deepEqual( + input3.snippets[0].linesHighlighted, + data1.snippets[0].linesHighlighted + ) + t.deepEqual( + input3.snippets[0].linesHighlighted, + jsonData1.snippets[0].linesHighlighted + ) - const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) + const jsonData2 = CSON.readFileSync( + path.join(storagePath, 'notes', data2.key + '.cson') + ) t.is(input4.title, data2.title) t.is(input4.title, jsonData2.title) t.is(input4.content, data2.content) @@ -121,7 +140,7 @@ test.serial('Update a note', (t) => { }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/updateSnippet-test.js b/tests/dataApi/updateSnippet-test.js index 662548bd..5e8fb3a9 100644 --- a/tests/dataApi/updateSnippet-test.js +++ b/tests/dataApi/updateSnippet-test.js @@ -21,20 +21,20 @@ const newSnippet = { content: 'new content' } -test.beforeEach((t) => { +test.beforeEach(t => { sander.writeFileSync(snippetFile, JSON.stringify([oldSnippet])) }) -test.serial('Update a snippet', (t) => { +test.serial('Update a snippet', t => { return Promise.resolve() - .then(function doTest () { - return Promise.all([ - updateSnippet(newSnippet, snippetFile) - ]) + .then(function doTest() { + return Promise.all([updateSnippet(newSnippet, snippetFile)]) }) - .then(function assert () { + .then(function assert() { const snippets = JSON.parse(sander.readFileSync(snippetFile)) - const snippet = snippets.find(currentSnippet => currentSnippet.id === newSnippet.id) + const snippet = snippets.find( + currentSnippet => currentSnippet.id === newSnippet.id + ) t.not(snippet, undefined) t.is(snippet.name, newSnippet.name) t.deepEqual(snippet.prefix, newSnippet.prefix) diff --git a/tests/date-formatter-test.js b/tests/date-formatter-test.js index 265ca4c4..c6de28c6 100644 --- a/tests/date-formatter-test.js +++ b/tests/date-formatter-test.js @@ -5,8 +5,5 @@ const test = require('ava') const { formatDate } = require('browser/lib/date-formatter') test(t => { - t.throws( - () => formatDate('invalid argument'), - 'Invalid argument.' - ) + t.throws(() => formatDate('invalid argument'), 'Invalid argument.') }) diff --git a/tests/fixtures/TestDummy.js b/tests/fixtures/TestDummy.js index 62cae846..0c0a95d5 100644 --- a/tests/fixtures/TestDummy.js +++ b/tests/fixtures/TestDummy.js @@ -5,7 +5,7 @@ const sander = require('sander') const CSON = require('@rokt33r/season') const path = require('path') -function dummyFolder (override = {}) { +function dummyFolder(override = {}) { var data = { name: faker.lorem.word(), color: faker.internet.color() @@ -17,21 +17,23 @@ function dummyFolder (override = {}) { return data } -function dummyBoostnoteJSONData (override = {}, isLegacy = false) { +function dummyBoostnoteJSONData(override = {}, isLegacy = false) { var data = {} if (override.folders == null) { data.folders = [] - var folderCount = Math.floor((Math.random() * 5)) + 2 + var folderCount = Math.floor(Math.random() * 5) + 2 for (var i = 0; i < folderCount; i++) { var key = keygen() - while (data.folders.some((folder) => folder.key === key)) { + while (data.folders.some(folder => folder.key === key)) { key = keygen() } - data.folders.push(dummyFolder({ - key - })) + data.folders.push( + dummyFolder({ + key + }) + ) } } if (!isLegacy) data.version = '1.0' @@ -41,24 +43,28 @@ function dummyBoostnoteJSONData (override = {}, isLegacy = false) { return data } -function dummyNote (override = {}) { - var data = Math.random() > 0.5 - ? { - type: 'MARKDOWN_NOTE', - content: faker.lorem.lines() - } - : { - type: 'SNIPPET_NOTE', - description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }] - } - data.title = data.type === 'MARKDOWN_NOTE' - ? data.content.split('\n').shift() - : data.description.split('\n').shift() +function dummyNote(override = {}) { + var data = + Math.random() > 0.5 + ? { + type: 'MARKDOWN_NOTE', + content: faker.lorem.lines() + } + : { + type: 'SNIPPET_NOTE', + description: faker.lorem.lines(), + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ] + } + data.title = + data.type === 'MARKDOWN_NOTE' + ? data.content.split('\n').shift() + : data.description.split('\n').shift() data.createdAt = faker.date.past() data.updatedAt = faker.date.recent() data.isStarred = false @@ -91,36 +97,41 @@ function dummyNote (override = {}) { * ``` * @return {[type]} */ -function dummyStorage (storagePath, override = {}) { - var jsonData = override.json != null - ? override.json - : dummyBoostnoteJSONData() - var cacheData = override.cache != null - ? override.cache - : {} +function dummyStorage(storagePath, override = {}) { + var jsonData = + override.json != null ? override.json : dummyBoostnoteJSONData() + var cacheData = override.cache != null ? override.cache : {} if (cacheData.key == null) cacheData.key = keygen() if (cacheData.name == null) cacheData.name = faker.random.word() if (cacheData.type == null) cacheData.type = 'FILESYSTEM' cacheData.path = storagePath - sander.writeFileSync(path.join(storagePath, 'boostnote.json'), JSON.stringify(jsonData)) + sander.writeFileSync( + path.join(storagePath, 'boostnote.json'), + JSON.stringify(jsonData) + ) var notesData = [] - var noteCount = Math.floor((Math.random() * 15)) + 2 + var noteCount = Math.floor(Math.random() * 15) + 2 for (var i = 0; i < noteCount; i++) { var key = keygen(true) - while (notesData.some((note) => note.key === key)) { + while (notesData.some(note => note.key === key)) { key = keygen(true) } var noteData = dummyNote({ key, - folder: jsonData.folders[Math.floor(Math.random() * jsonData.folders.length)].key + folder: + jsonData.folders[Math.floor(Math.random() * jsonData.folders.length)] + .key }) notesData.push(noteData) } - notesData.forEach(function saveNoteCSON (note) { - CSON.writeFileSync(path.join(storagePath, 'notes', note.key + '.cson'), _.omit(note, ['key'])) + notesData.forEach(function saveNoteCSON(note) { + CSON.writeFileSync( + path.join(storagePath, 'notes', note.key + '.cson'), + _.omit(note, ['key']) + ) }) return { @@ -130,27 +141,27 @@ function dummyStorage (storagePath, override = {}) { } } -function dummyLegacyStorage (storagePath, override = {}) { - var jsonData = override.json != null - ? override.json - : dummyBoostnoteJSONData({}, true) - var cacheData = override.cache != null - ? override.cache - : {} +function dummyLegacyStorage(storagePath, override = {}) { + var jsonData = + override.json != null ? override.json : dummyBoostnoteJSONData({}, true) + var cacheData = override.cache != null ? override.cache : {} if (cacheData.key == null) cacheData.key = keygen() if (cacheData.name == null) cacheData.name = faker.random.word() if (cacheData.type == null) cacheData.type = 'FILESYSTEM' cacheData.path = storagePath - sander.writeFileSync(path.join(storagePath, 'boostnote.json'), JSON.stringify(jsonData)) + sander.writeFileSync( + path.join(storagePath, 'boostnote.json'), + JSON.stringify(jsonData) + ) var notesData = [] for (var j = 0; j < jsonData.folders.length; j++) { var folderNotes = [] - var noteCount = Math.floor((Math.random() * 5)) + 1 + var noteCount = Math.floor(Math.random() * 5) + 1 for (var i = 0; i < noteCount; i++) { var key = keygen(true) - while (folderNotes.some((note) => note.key === key)) { + while (folderNotes.some(note => note.key === key)) { key = keygen(true) } @@ -161,7 +172,10 @@ function dummyLegacyStorage (storagePath, override = {}) { folderNotes.push(noteData) } notesData = notesData.concat(folderNotes) - CSON.writeFileSync(path.join(storagePath, jsonData.folders[j].key, 'data.json'), {notes: folderNotes.map((note) => _.omit(note, ['folder']))}) + CSON.writeFileSync( + path.join(storagePath, jsonData.folders[j].key, 'data.json'), + { notes: folderNotes.map(note => _.omit(note, ['folder'])) } + ) } return { diff --git a/tests/helpers/setup-browser-env.js b/tests/helpers/setup-browser-env.js index 3e3232b7..81237b82 100644 --- a/tests/helpers/setup-browser-env.js +++ b/tests/helpers/setup-browser-env.js @@ -2,14 +2,14 @@ import browserEnv from 'browser-env' browserEnv(['window', 'document', 'navigator']) // for CodeMirror mockup -document.body.createTextRange = function () { +document.body.createTextRange = function() { return { - setEnd: function () {}, - setStart: function () {}, - getBoundingClientRect: function () { - return {right: 0} + setEnd: function() {}, + setStart: function() {}, + getBoundingClientRect: function() { + return { right: 0 } }, - getClientRects: function () { + getClientRects: function() { return { length: 0, left: 0, @@ -21,7 +21,7 @@ document.body.createTextRange = function () { window.localStorage = { // polyfill - getItem () { + getItem() { return '{}' } } diff --git a/tests/jest.js b/tests/jest.js index 6f830c67..9e59fab6 100644 --- a/tests/jest.js +++ b/tests/jest.js @@ -2,7 +2,7 @@ global.Raphael = { setWindow: jest.fn(), registerFont: jest.fn(), - fn: function () { + fn: function() { return {} } } diff --git a/tests/lib/contextMenuBuilder.test.js b/tests/lib/contextMenuBuilder.test.js index b7009bf1..689fb5f4 100644 --- a/tests/lib/contextMenuBuilder.test.js +++ b/tests/lib/contextMenuBuilder.test.js @@ -1,18 +1,32 @@ let menuBuilderParameter jest.mock('electron', () => { - return {remote: {require: jest.fn(() => { return {Menu: {buildFromTemplate: jest.fn((param) => { menuBuilderParameter = param })}} })}} + return { + remote: { + require: jest.fn(() => { + return { + Menu: { + buildFromTemplate: jest.fn(param => { + menuBuilderParameter = param + }) + } + } + }) + } + } }) const spellcheck = require('browser/lib/spellcheck') -const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu -const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder').buildMarkdownPreviewContextMenu +const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') + .buildEditorContextMenu +const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder') + .buildMarkdownPreviewContextMenu beforeEach(() => { menuBuilderParameter = null }) // Editor Context Menu -it('should make sure that no context menu is build if the passed editor instance was null', function () { +it('should make sure that no context menu is build if the passed editor instance was null', function() { const event = { pageX: 12, pageY: 12 @@ -21,89 +35,96 @@ it('should make sure that no context menu is build if the passed editor instance expect(menuBuilderParameter).toEqual(null) }) -it('should make sure that word suggestions are only requested if the word contained a typo', function () { +it('should make sure that word suggestions are only requested if the word contained a typo', function() { spellcheck.getSpellingSuggestion = jest.fn() const editor = jest.fn() editor.coordsChar = jest.fn() - editor.findWordAt = jest.fn(() => { return {anchor: {}, head: {}} }) + editor.findWordAt = jest.fn(() => { + return { anchor: {}, head: {} } + }) editor.getRange = jest.fn() editor.findMarks = jest.fn(() => []) const event = { pageX: 12, pageY: 12 } - const expectedMenuParameter = [ { role: 'cut' }, + const expectedMenuParameter = [ + { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, - { role: 'selectall' } ] + { role: 'selectall' } + ] buildEditorContextMenu(editor, event) expect(menuBuilderParameter).toEqual(expectedMenuParameter) expect(spellcheck.getSpellingSuggestion).not.toHaveBeenCalled() }) -it('should make sure that word suggestions are only requested if the word contained a typo and no other mark', function () { +it('should make sure that word suggestions are only requested if the word contained a typo and no other mark', function() { spellcheck.getSpellingSuggestion = jest.fn() spellcheck.getCSSClassName = jest.fn(() => 'dummyErrorClassName') const editor = jest.fn() editor.coordsChar = jest.fn() - editor.findWordAt = jest.fn(() => { return {anchor: {}, head: {}} }) + editor.findWordAt = jest.fn(() => { + return { anchor: {}, head: {} } + }) editor.getRange = jest.fn() - const dummyMarks = [ - {className: 'someStupidClassName'} - ] + const dummyMarks = [{ className: 'someStupidClassName' }] editor.findMarks = jest.fn(() => dummyMarks) const event = { pageX: 12, pageY: 12 } - const expectedMenuParameter = [ { role: 'cut' }, + const expectedMenuParameter = [ + { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, - { role: 'selectall' } ] + { role: 'selectall' } + ] buildEditorContextMenu(editor, event) expect(menuBuilderParameter).toEqual(expectedMenuParameter) expect(spellcheck.getSpellingSuggestion).not.toHaveBeenCalled() }) -it('should make sure that word suggestions calls the right editor functions', function () { +it('should make sure that word suggestions calls the right editor functions', function() { spellcheck.getSpellingSuggestion = jest.fn() spellcheck.getCSSClassName = jest.fn(() => 'dummyErrorClassName') - const dummyCursor = {dummy: 'dummy'} - const dummyRange = {anchor: {test: 'test'}, head: {test2: 'test2'}} + const dummyCursor = { dummy: 'dummy' } + const dummyRange = { anchor: { test: 'test' }, head: { test2: 'test2' } } const editor = jest.fn() editor.coordsChar = jest.fn(() => dummyCursor) editor.findWordAt = jest.fn(() => dummyRange) editor.getRange = jest.fn() - const dummyMarks = [ - {className: 'someStupidClassName'} - ] + const dummyMarks = [{ className: 'someStupidClassName' }] editor.findMarks = jest.fn(() => dummyMarks) const event = { pageX: 12, pageY: 21 } - const expectedCoordsCharCall = {left: event.pageX, top: event.pageY} + const expectedCoordsCharCall = { left: event.pageX, top: event.pageY } buildEditorContextMenu(editor, event) expect(editor.coordsChar).toHaveBeenCalledWith(expectedCoordsCharCall) expect(editor.findWordAt).toHaveBeenCalledWith(dummyCursor) - expect(editor.getRange).toHaveBeenCalledWith(dummyRange.anchor, dummyRange.head) + expect(editor.getRange).toHaveBeenCalledWith( + dummyRange.anchor, + dummyRange.head + ) }) -it('should make sure that word suggestions creates a correct menu if there was an error', function () { +it('should make sure that word suggestions creates a correct menu if there was an error', function() { const suggestions = ['test1', 'test2', 'Pustekuchen'] const errorClassName = 'errorCSS' const wordToCorrect = 'pustekuchen' - const dummyMarks = [ - {className: errorClassName} - ] + const dummyMarks = [{ className: errorClassName }] spellcheck.getSpellingSuggestion = jest.fn(() => suggestions) spellcheck.getCSSClassName = jest.fn(() => errorClassName) const editor = jest.fn() editor.coordsChar = jest.fn() - editor.findWordAt = jest.fn(() => { return {anchor: {}, head: {}} }) + editor.findWordAt = jest.fn(() => { + return { anchor: {}, head: {} } + }) editor.getRange = jest.fn(() => wordToCorrect) editor.findMarks = jest.fn(() => []) @@ -128,7 +149,7 @@ it('should make sure that word suggestions creates a correct menu if there was a }) // Markdown Preview Context Menu -it('should make sure that no context menu is built if the Markdown Preview instance was null', function () { +it('should make sure that no context menu is built if the Markdown Preview instance was null', function() { const event = { pageX: 12, pageY: 12 diff --git a/tests/lib/find-storage-test.js b/tests/lib/find-storage-test.js index 3d7fdc63..ab3b07a6 100644 --- a/tests/lib/find-storage-test.js +++ b/tests/lib/find-storage-test.js @@ -6,14 +6,17 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/find-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) @@ -26,7 +29,7 @@ test('findStorage() should return a correct storage path(string)', t => { t.is(findStorage(storageKey).path, storagePath) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/lib/find-title-test.js b/tests/lib/find-title-test.js index 103b8774..2498cdc0 100644 --- a/tests/lib/find-title-test.js +++ b/tests/lib/find-title-test.js @@ -20,7 +20,11 @@ test('findNoteTitle#find should return a correct title (string)', t => { testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, false), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, false), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) @@ -34,21 +38,32 @@ test('findNoteTitle#find should ignore front matter when enableFrontMatterTitle testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, false), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, false), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) test('findNoteTitle#find should respect front matter when enableFrontMatterTitle=true', t => { // [input, expected] const testCases = [ - ['---\nlayout: test\ntitle: hoge hoge hoge \n---\n# fuga', 'hoge hoge hoge'], + [ + '---\nlayout: test\ntitle: hoge hoge hoge \n---\n# fuga', + 'hoge hoge hoge' + ], ['---\ntitle:hoge\n---\n# fuga', 'hoge'], ['title: fuga\n# hoge', '# hoge'] ] testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, true), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, true), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) @@ -61,6 +76,10 @@ test('findNoteTitle#find should respect frontMatterTitleField when provided', t testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, true, 'custom'), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, true, 'custom'), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) diff --git a/tests/lib/get-todo-status-test.js b/tests/lib/get-todo-status-test.js index db74ec56..52c57cfe 100644 --- a/tests/lib/get-todo-status-test.js +++ b/tests/lib/get-todo-status-test.js @@ -40,8 +40,15 @@ test('getTodoStatus should return a correct hash object', t => { testCases.forEach(testCase => { const [input, expected] = testCase - t.is(getTodoStatus(input).total, expected.total, `Test for getTodoStatus() input: ${input} expected: ${expected.total}`) - t.is(getTodoStatus(input).completed, expected.completed, `Test for getTodoStatus() input: ${input} expected: ${expected.completed}`) + t.is( + getTodoStatus(input).total, + expected.total, + `Test for getTodoStatus() input: ${input} expected: ${expected.total}` + ) + t.is( + getTodoStatus(input).completed, + expected.completed, + `Test for getTodoStatus() input: ${input} expected: ${expected.completed}` + ) }) }) - diff --git a/tests/lib/html-text-helper-test.js b/tests/lib/html-text-helper-test.js index 538b8757..9e6eb083 100644 --- a/tests/lib/html-text-helper-test.js +++ b/tests/lib/html-text-helper-test.js @@ -9,16 +9,23 @@ test('htmlTextHelper#decodeEntities should return encoded text (string)', t => { // [input, expected] const testCases = [ ['<a href=', 'Boostnote'], - ['<\\\\?php\n var = 'hoge';', '<\\\\?php\n var = \'hoge\';'], + ['var test = 'test'', "var test = 'test'"], + [ + '<a href='https://boostnote.io'>Boostnote', + "Boostnote" + ], + ['<\\\\?php\n var = 'hoge';', "<\\\\?php\n var = 'hoge';"], ['&', '&'], - ['a$'', 'a\\$\''] + ['a$'', "a\\$'"] ] testCases.forEach(testCase => { const [input, expected] = testCase - t.is(htmlTextHelper.decodeEntities(input), expected, `Test for decodeEntities() input: ${input} expected: ${expected}`) + t.is( + htmlTextHelper.decodeEntities(input), + expected, + `Test for decodeEntities() input: ${input} expected: ${expected}` + ) }) }) @@ -26,29 +33,40 @@ test('htmlTextHelper#decodeEntities() should return decoded text (string)', t => // [input, expected] const testCases = [ ['Boostnote', '<a href='https://boostnote.io'>Boostnote'], - ['Boostnote", + '<a href='https://boostnote.io'>Boostnote' + ], + [" { const [input, expected] = testCase - t.is(htmlTextHelper.encodeEntities(input), expected, `Test for encodeEntities() input: ${input} expected: ${expected}`) + t.is( + htmlTextHelper.encodeEntities(input), + expected, + `Test for encodeEntities() input: ${input} expected: ${expected}` + ) }) }) // Integration test test(t => { const testCases = [ - 'var test = \'test\'', - 'Boostnote', - '' + "var test = 'test'", + "Boostnote", + "" ] testCases.forEach(testCase => { const encodedText = htmlTextHelper.encodeEntities(testCase) const decodedText = htmlTextHelper.decodeEntities(encodedText) - t.is(decodedText, testCase, 'Integration test through encodedText() and decodedText()') + t.is( + decodedText, + testCase, + 'Integration test through encodedText() and decodedText()' + ) }) }) diff --git a/tests/lib/markdown-text-helper-test.js b/tests/lib/markdown-text-helper-test.js index 38ee3136..07d05dde 100644 --- a/tests/lib/markdown-text-helper-test.js +++ b/tests/lib/markdown-text-helper-test.js @@ -42,6 +42,10 @@ test(t => { testCases.forEach(testCase => { const [input, expected] = testCase - t.is(markdown.strip(input), expected, `Test for strip() input: ${input} expected: ${expected}`) + t.is( + markdown.strip(input), + expected, + `Test for strip() input: ${input} expected: ${expected}` + ) }) }) diff --git a/tests/lib/markdown-toc-generator-test.js b/tests/lib/markdown-toc-generator-test.js index 60568741..def2bcf8 100644 --- a/tests/lib/markdown-toc-generator-test.js +++ b/tests/lib/markdown-toc-generator-test.js @@ -261,7 +261,11 @@ this is a text const expectedToc = testCase[2].trim() const generatedToc = markdownToc.generate(inputMd) - t.is(generatedToc, expectedToc, `generate test : ${title} , generated : ${EOL}${generatedToc}, expected : ${EOL}${expectedToc}`) + t.is( + generatedToc, + expectedToc, + `generate test : ${title} , generated : ${EOL}${generatedToc}, expected : ${EOL}${expectedToc}` + ) }) }) @@ -279,7 +283,7 @@ test(t => { const testCases = [ [ `***************************** Empty note, cursor at the top`, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ``, ` @@ -291,7 +295,7 @@ test(t => { ], [ `***************************** Two level note,TOC at the beginning `, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ` # one this is a level one text @@ -329,7 +333,7 @@ this is a level one text ], [ `***************************** Two level note, cursor just after 'header text' `, - {line: 1, ch: 12}, + { line: 1, ch: 12 }, ` # header header text @@ -373,7 +377,7 @@ this is a level one text ], [ `***************************** Two level note, cursor at empty line under 'header text' `, - {line: 2, ch: 0}, + { line: 2, ch: 0 }, ` # header header text @@ -416,7 +420,7 @@ this is a level one text ], [ `***************************** Two level note, cursor just before 'text' word`, - {line: 1, ch: 8}, + { line: 1, ch: 8 }, ` # header header text @@ -461,7 +465,7 @@ this is a level one text ], [ `***************************** Already generated TOC without header file, regenerate TOC in place, no changes`, - {line: 13, ch: 0}, + { line: 13, ch: 0 }, ` # header header text @@ -511,7 +515,7 @@ this is a level one text ], [ `***************************** Already generated TOC, needs updating in place`, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ` # header header text @@ -561,7 +565,7 @@ this is a level one text ], [ `***************************** Document with cursor at the last line, expecting empty TOC `, - {line: 13, ch: 30}, + { line: 13, ch: 30 }, ` # header header text @@ -602,7 +606,7 @@ this is a level one text ], [ `***************************** Empty, not actual TOC , should be supplemented with two new points beneath`, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ` # header header text @@ -663,6 +667,10 @@ this is a level one text editor.setCursor(cursor) markdownToc.generateInEditor(editor) - t.is(expectedMd, editor.getValue(), `generateInEditor test : ${title} , generated : ${EOL}${editor.getValue()}, expected : ${EOL}${expectedMd}`) + t.is( + expectedMd, + editor.getValue(), + `generateInEditor test : ${title} , generated : ${EOL}${editor.getValue()}, expected : ${EOL}${expectedMd}` + ) }) }) diff --git a/tests/lib/normalize-editor-font-family-test.js b/tests/lib/normalize-editor-font-family-test.js index aacd03ac..beb2b765 100644 --- a/tests/lib/normalize-editor-font-family-test.js +++ b/tests/lib/normalize-editor-font-family-test.js @@ -12,5 +12,8 @@ test('normalizeEditorFontFamily() should return default font family (string[])', test('normalizeEditorFontFamily(["hoge", "huga"]) should return default font family connected with arg.', t => { const arg = 'font1, font2' - t.is(normalizeEditorFontFamily(arg), `${arg}, ${defaultEditorFontFamily.join(', ')}`) + t.is( + normalizeEditorFontFamily(arg), + `${arg}, ${defaultEditorFontFamily.join(', ')}` + ) }) diff --git a/tests/lib/rc-parser-test.js b/tests/lib/rc-parser-test.js index 024a2d36..2c8213ea 100644 --- a/tests/lib/rc-parser-test.js +++ b/tests/lib/rc-parser-test.js @@ -4,8 +4,42 @@ const { parse } = require('browser/lib/RcParser') // Unit test test('RcParser should return a json object', t => { - const validJson = { 'editor': { 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Control + L' }, 'listWidth': 135, 'navWidth': 135 } - const allJson = { 'amaEnabled': true, 'editor': { 'fontFamily': 'Monaco, Consolas', 'fontSize': '14', 'indentSize': '2', 'indentType': 'space', 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Cmd + Alt + L' }, 'isSideNavFolded': false, 'listStyle': 'DEFAULT', 'listWidth': 174, 'navWidth': 200, 'preview': { 'codeBlockTheme': 'dracula', 'fontFamily': 'Lato', 'fontSize': '14', 'lineNumber': true }, 'sortBy': { 'default': 'UPDATED_AT' }, 'ui': { 'defaultNote': 'ALWAYS_ASK', 'disableDirectWrite': false, 'theme': 'default' }, 'zoom': 1 } + const validJson = { + editor: { keyMap: 'vim', switchPreview: 'BLUR', theme: 'monokai' }, + hotkey: { toggleMain: 'Control + L' }, + listWidth: 135, + navWidth: 135 + } + const allJson = { + amaEnabled: true, + editor: { + fontFamily: 'Monaco, Consolas', + fontSize: '14', + indentSize: '2', + indentType: 'space', + keyMap: 'vim', + switchPreview: 'BLUR', + theme: 'monokai' + }, + hotkey: { toggleMain: 'Cmd + Alt + L' }, + isSideNavFolded: false, + listStyle: 'DEFAULT', + listWidth: 174, + navWidth: 200, + preview: { + codeBlockTheme: 'dracula', + fontFamily: 'Lato', + fontSize: '14', + lineNumber: true + }, + sortBy: { default: 'UPDATED_AT' }, + ui: { + defaultNote: 'ALWAYS_ASK', + disableDirectWrite: false, + theme: 'default' + }, + zoom: 1 + } // [input, expected] const validTestCases = [ @@ -13,21 +47,27 @@ test('RcParser should return a json object', t => { ['.boostnoterc.all', allJson] ] - const invalidTestCases = [ - ['.boostnoterc.invalid', {}] - ] + const invalidTestCases = [['.boostnoterc.invalid', {}]] validTestCases.forEach(validTestCase => { const [input, expected] = validTestCase - t.is(parse(filePath(input)).editor.keyMap, expected.editor.keyMap, `Test for getTodoStatus() input: ${input} expected: ${expected.keyMap}`) + t.is( + parse(filePath(input)).editor.keyMap, + expected.editor.keyMap, + `Test for getTodoStatus() input: ${input} expected: ${expected.keyMap}` + ) }) invalidTestCases.forEach(invalidTestCase => { const [input, expected] = invalidTestCase - t.is(parse(filePath(input)).editor, expected.editor, `Test for getTodoStatus() input: ${input} expected: ${expected.editor}`) + t.is( + parse(filePath(input)).editor, + expected.editor, + `Test for getTodoStatus() input: ${input} expected: ${expected.editor}` + ) }) }) -function filePath (filename) { +function filePath(filename) { return path.join(`${__dirname}/boostnoterc`, filename) } diff --git a/tests/lib/search-test.js b/tests/lib/search-test.js index 2e288d26..75180d9e 100644 --- a/tests/lib/search-test.js +++ b/tests/lib/search-test.js @@ -3,14 +3,21 @@ import searchFromNotes from 'browser/lib/search' import { dummyNote } from '../fixtures/TestDummy' import _ from 'lodash' -const pickContents = (notes) => notes.map((note) => { return note.content }) +const pickContents = notes => + notes.map(note => { + return note.content + }) let notes = [] let note1, note2, note3 test.before(t => { const data1 = { type: 'MARKDOWN_NOTE', content: 'content1', tags: ['tag1'] } - const data2 = { type: 'MARKDOWN_NOTE', content: 'content1\ncontent2', tags: ['tag1', 'tag2'] } + const data2 = { + type: 'MARKDOWN_NOTE', + content: 'content1\ncontent2', + tags: ['tag1', 'tag2'] + } const data3 = { type: 'MARKDOWN_NOTE', content: '#content4', tags: ['tag1'] } note1 = dummyNote(data1) @@ -34,12 +41,12 @@ test('it can find notes by tags and words', t => { ['#tag2 content1', [note2.content]], ['content1 #tag2', [note2.content]] ] - const testWithTagsWithoutHash = testWithTags.map(function (testCase) { + const testWithTagsWithoutHash = testWithTags.map(function(testCase) { return [testCase[0].replace(/#/g, ''), testCase[1]] }) const testCases = testWithTags.concat(testWithTagsWithoutHash) - testCases.forEach((testCase) => { + testCases.forEach(testCase => { const [input, expectedContents] = testCase const results = searchFromNotes(notes, input) t.true(_.isEqual(pickContents(results).sort(), expectedContents.sort())) diff --git a/tests/lib/slugify-test.js b/tests/lib/slugify-test.js index 0277bd10..39991bca 100644 --- a/tests/lib/slugify-test.js +++ b/tests/lib/slugify-test.js @@ -13,7 +13,7 @@ test('alphabet and digit', t => { test('should delete unavailable symbols', t => { const availableSymbols = '_-' - const testCase = availableSymbols + '][!\'#$%&()*+,./:;<=>?@\\^{|}~`' + const testCase = availableSymbols + "][!'#$%&()*+,./:;<=>?@\\^{|}~`" const decodeSlug = decodeURI(slugify(testCase)) t.true(decodeSlug === availableSymbols) diff --git a/tests/lib/spellcheck.test.js b/tests/lib/spellcheck.test.js index 6ebaaf3b..c3fc0b52 100644 --- a/tests/lib/spellcheck.test.js +++ b/tests/lib/spellcheck.test.js @@ -9,12 +9,12 @@ beforeEach(() => { Typo.mockClear() }) -it('should test that checkWord does not marks words that do not contain a typo', function () { +it('should test that checkWord does not marks words that do not contain a typo', function() { const testWord = 'testWord' const editor = jest.fn() editor.getRange = jest.fn(() => testWord) editor.markText = jest.fn() - const range = {anchor: {line: 1, ch: 0}, head: {line: 1, ch: 10}} + const range = { anchor: { line: 1, ch: 0 }, head: { line: 1, ch: 10 } } const mockDictionary = jest.fn() mockDictionary.check = jest.fn(() => true) systemUnderTest.setDictionaryForTestsOnly(mockDictionary) @@ -26,12 +26,12 @@ it('should test that checkWord does not marks words that do not contain a typo', expect(editor.markText).not.toHaveBeenCalled() }) -it('should test that checkWord should marks words that contain a typo', function () { +it('should test that checkWord should marks words that contain a typo', function() { const testWord = 'testWord' const editor = jest.fn() editor.getRange = jest.fn(() => testWord) editor.markText = jest.fn() - const range = {anchor: {line: 1, ch: 0}, head: {line: 1, ch: 10}} + const range = { anchor: { line: 1, ch: 0 }, head: { line: 1, ch: 10 } } const mockDictionary = jest.fn() mockDictionary.check = jest.fn(() => false) systemUnderTest.setDictionaryForTestsOnly(mockDictionary) @@ -40,14 +40,16 @@ it('should test that checkWord should marks words that contain a typo', function expect(editor.getRange).toHaveBeenCalledWith(range.anchor, range.head) expect(mockDictionary.check).toHaveBeenCalledWith(testWord) - expect(editor.markText).toHaveBeenCalledWith(range.anchor, range.head, {'className': systemUnderTest.CSS_ERROR_CLASS}) + expect(editor.markText).toHaveBeenCalledWith(range.anchor, range.head, { + className: systemUnderTest.CSS_ERROR_CLASS + }) }) -it('should test that setLanguage clears all marks', function () { +it('should test that setLanguage clears all marks', function() { const dummyMarks = [ - {clear: jest.fn()}, - {clear: jest.fn()}, - {clear: jest.fn()} + { clear: jest.fn() }, + { clear: jest.fn() }, + { clear: jest.fn() } ] const editor = jest.fn() editor.getAllMarks = jest.fn(() => dummyMarks) @@ -60,10 +62,12 @@ it('should test that setLanguage clears all marks', function () { } }) -it('should test that setLanguage with DISABLED as a lang argument should not load any dictionary and not check the whole document', function () { +it('should test that setLanguage with DISABLED as a lang argument should not load any dictionary and not check the whole document', function() { const editor = jest.fn() editor.getAllMarks = jest.fn(() => []) - const checkWholeDocumentSpy = jest.spyOn(systemUnderTest, 'checkWholeDocument').mockImplementation() + const checkWholeDocumentSpy = jest + .spyOn(systemUnderTest, 'checkWholeDocument') + .mockImplementation() systemUnderTest.setLanguage(editor, systemUnderTest.SPELLCHECK_DISABLED) @@ -72,38 +76,45 @@ it('should test that setLanguage with DISABLED as a lang argument should not loa checkWholeDocumentSpy.mockRestore() }) -it('should test that setLanguage loads the correct dictionary', function () { +it('should test that setLanguage loads the correct dictionary', function() { const editor = jest.fn() editor.getAllMarks = jest.fn(() => []) const lang = 'de_DE' - const checkWholeDocumentSpy = jest.spyOn(systemUnderTest, 'checkWholeDocument').mockImplementation() + const checkWholeDocumentSpy = jest + .spyOn(systemUnderTest, 'checkWholeDocument') + .mockImplementation() expect(Typo).not.toHaveBeenCalled() systemUnderTest.setLanguage(editor, lang) expect(Typo).toHaveBeenCalledWith(lang, false, false, expect.anything()) - expect(Typo.mock.calls[0][3].dictionaryPath).toEqual(systemUnderTest.DICTIONARY_PATH) + expect(Typo.mock.calls[0][3].dictionaryPath).toEqual( + systemUnderTest.DICTIONARY_PATH + ) expect(Typo.mock.calls[0][3].asyncLoad).toBe(true) checkWholeDocumentSpy.mockRestore() }) -it('should test that checkMultiLineRange performs checks for each word in the stated range', function () { +it('should test that checkMultiLineRange performs checks for each word in the stated range', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 2, ch: 4} - const rangeTo = {line: 3, ch: 36} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 2, ch: 4 } + const rangeTo = { line: 3, ch: 36 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -121,23 +132,26 @@ it('should test that checkMultiLineRange performs checks for each word in the st expect(dic.check.mock.calls[10][0]).toEqual('tristique') }) -it('should test that checkMultiLineRange works correct even when the range is inverted (from is the later position and to the lower)', function () { +it('should test that checkMultiLineRange works correct even when the range is inverted (from is the later position and to the lower)', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 3, ch: 36} - const rangeTo = {line: 2, ch: 4} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 3, ch: 36 } + const rangeTo = { line: 2, ch: 4 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -155,23 +169,26 @@ it('should test that checkMultiLineRange works correct even when the range is in expect(dic.check.mock.calls[10][0]).toEqual('tristique') }) -it('should test that checkMultiLineRange works for single line', function () { +it('should test that checkMultiLineRange works for single line', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 5, ch: 14} - const rangeTo = {line: 5, ch: 39} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 5, ch: 14 } + const rangeTo = { line: 5, ch: 39 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -181,23 +198,26 @@ it('should test that checkMultiLineRange works for single line', function () { expect(dic.check.mock.calls[2][0]).toEqual('ullamcorper') }) -it('should test that checkMultiLineRange works for single word', function () { +it('should test that checkMultiLineRange works for single word', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 7, ch: 6} - const rangeTo = {line: 7, ch: 6} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 7, ch: 6 } + const rangeTo = { line: 7, ch: 6 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -205,8 +225,10 @@ it('should test that checkMultiLineRange works for single word', function () { expect(dic.check.mock.calls[0][0]).toEqual('molestie') }) -it('should make sure that liveSpellcheck don\'t work if the spellcheck is not enabled', function () { - const checkMultiLineRangeSpy = jest.spyOn(systemUnderTest, 'checkMultiLineRange').mockImplementation() +it("should make sure that liveSpellcheck don't work if the spellcheck is not enabled", function() { + const checkMultiLineRangeSpy = jest + .spyOn(systemUnderTest, 'checkMultiLineRange') + .mockImplementation() const editor = jest.fn() editor.findMarks = jest.fn() @@ -219,61 +241,88 @@ it('should make sure that liveSpellcheck don\'t work if the spellcheck is not en checkMultiLineRangeSpy.mockRestore() }) -it('should make sure that liveSpellcheck works for a range of changes', function () { +it('should make sure that liveSpellcheck works for a range of changes', function() { const editor = jest.fn() - const marks = [{clear: jest.fn()}, {clear: jest.fn()}] + const marks = [{ clear: jest.fn() }, { clear: jest.fn() }] editor.findMarks = jest.fn(() => marks) - const checkMultiLineRangeSpy = jest.spyOn(systemUnderTest, 'checkMultiLineRange').mockImplementation() + const checkMultiLineRangeSpy = jest + .spyOn(systemUnderTest, 'checkMultiLineRange') + .mockImplementation() - const inputChangeRange = {from: {line: 0, ch: 2}, to: {line: 1, ch: 1}} - const inputChangeRangeTo = {from: {line: 0, ch: 2}, to: {line: 1, ch: 2}} + const inputChangeRange = { from: { line: 0, ch: 2 }, to: { line: 1, ch: 1 } } + const inputChangeRangeTo = { + from: { line: 0, ch: 2 }, + to: { line: 1, ch: 2 } + } systemUnderTest.setDictionaryForTestsOnly({}) systemUnderTest.checkChangeRange(editor, inputChangeRange, inputChangeRangeTo) - expect(checkMultiLineRangeSpy).toHaveBeenCalledWith(editor, {line: 0, ch: 1}, {line: 1, ch: 3}) - expect(editor.findMarks).toHaveBeenCalledWith({line: 0, ch: 1}, {line: 1, ch: 3}) + expect(checkMultiLineRangeSpy).toHaveBeenCalledWith( + editor, + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) + expect(editor.findMarks).toHaveBeenCalledWith( + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) expect(marks[0].clear).toHaveBeenCalled() expect(marks[1].clear).toHaveBeenCalled() checkMultiLineRangeSpy.mockRestore() }) -it('should make sure that liveSpellcheck works if ranges are inverted', function () { +it('should make sure that liveSpellcheck works if ranges are inverted', function() { const editor = jest.fn() - const marks = [{clear: jest.fn()}, {clear: jest.fn()}] + const marks = [{ clear: jest.fn() }, { clear: jest.fn() }] editor.findMarks = jest.fn(() => marks) - const checkMultiLineRangeSpy = jest.spyOn(systemUnderTest, 'checkMultiLineRange').mockImplementation() + const checkMultiLineRangeSpy = jest + .spyOn(systemUnderTest, 'checkMultiLineRange') + .mockImplementation() - const inputChangeRange = {from: {line: 0, ch: 2}, to: {line: 1, ch: 2}} - const inputChangeRangeTo = {from: {line: 0, ch: 2}, to: {line: 1, ch: 1}} + const inputChangeRange = { from: { line: 0, ch: 2 }, to: { line: 1, ch: 2 } } + const inputChangeRangeTo = { + from: { line: 0, ch: 2 }, + to: { line: 1, ch: 1 } + } systemUnderTest.setDictionaryForTestsOnly({}) systemUnderTest.checkChangeRange(editor, inputChangeRange, inputChangeRangeTo) - expect(checkMultiLineRangeSpy).toHaveBeenCalledWith(editor, {line: 0, ch: 1}, {line: 1, ch: 3}) - expect(editor.findMarks).toHaveBeenCalledWith({line: 0, ch: 1}, {line: 1, ch: 3}) + expect(checkMultiLineRangeSpy).toHaveBeenCalledWith( + editor, + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) + expect(editor.findMarks).toHaveBeenCalledWith( + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) expect(marks[0].clear).toHaveBeenCalled() expect(marks[1].clear).toHaveBeenCalled() checkMultiLineRangeSpy.mockRestore() }) -it('should make sure that liveSpellcheck works for a single word with change at the beginning', function () { +it('should make sure that liveSpellcheck works for a single word with change at the beginning', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {from: {line: 7, ch: 6}, to: {line: 7, ch: 6}} - const rangeTo = {from: {line: 7, ch: 6}, to: {line: 7, ch: 6}} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { from: { line: 7, ch: 6 }, to: { line: 7, ch: 6 } } + const rangeTo = { from: { line: 7, ch: 6 }, to: { line: 7, ch: 6 } } systemUnderTest.checkChangeRange(editor, rangeFrom, rangeTo) @@ -281,23 +330,26 @@ it('should make sure that liveSpellcheck works for a single word with change at expect(dic.check.mock.calls[0][0]).toEqual('molestie') }) -it('should make sure that liveSpellcheck works for a single word with change in the middle', function () { +it('should make sure that liveSpellcheck works for a single word with change in the middle', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {from: {line: 7, ch: 9}, to: {line: 7, ch: 9}} - const rangeTo = {from: {line: 7, ch: 9}, to: {line: 7, ch: 9}} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { from: { line: 7, ch: 9 }, to: { line: 7, ch: 9 } } + const rangeTo = { from: { line: 7, ch: 9 }, to: { line: 7, ch: 9 } } systemUnderTest.checkChangeRange(editor, rangeFrom, rangeTo) @@ -305,23 +357,26 @@ it('should make sure that liveSpellcheck works for a single word with change in expect(dic.check.mock.calls[0][0]).toEqual('molestie') }) -it('should make sure that liveSpellcheck works for a single word with change at the end', function () { +it('should make sure that liveSpellcheck works for a single word with change at the end', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {from: {line: 7, ch: 14}, to: {line: 7, ch: 14}} - const rangeTo = {from: {line: 7, ch: 14}, to: {line: 7, ch: 14}} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { from: { line: 7, ch: 14 }, to: { line: 7, ch: 14 } } + const rangeTo = { from: { line: 7, ch: 14 }, to: { line: 7, ch: 14 } } systemUnderTest.checkChangeRange(editor, rangeFrom, rangeTo) diff --git a/webpack-production.config.js b/webpack-production.config.js index bcfb2a42..9c5a0b42 100644 --- a/webpack-production.config.js +++ b/webpack-production.config.js @@ -14,7 +14,8 @@ var config = Object.assign({}, skeleton, { { test: /\.styl$/, exclude: /(node_modules|bower_components)/, - loader: 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' + loader: + 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' }, { test: /\.json$/, @@ -35,8 +36,8 @@ var config = Object.assign({}, skeleton, { new webpack.optimize.OccurenceOrderPlugin(), new webpack.DefinePlugin({ 'process.env': { - 'NODE_ENV': JSON.stringify('production'), - 'BABEL_ENV': JSON.stringify('production') + NODE_ENV: JSON.stringify('production'), + BABEL_ENV: JSON.stringify('production') } }) ] diff --git a/webpack.config.js b/webpack.config.js index ad3c4f8b..ac0b91db 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,7 +12,8 @@ var config = Object.assign({}, skeleton, { { test: /\.styl$/, exclude: /(node_modules|bower_components)/, - loader: 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' + loader: + 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' }, { test: /\.json$/, @@ -39,4 +40,3 @@ var config = Object.assign({}, skeleton, { }) module.exports = config - diff --git a/yarn.lock b/yarn.lock index 2d153615..7a2f8985 100644 --- a/yarn.lock +++ b/yarn.lock @@ -193,6 +193,11 @@ ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= + ajv@^4.7.0: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -200,7 +205,7 @@ ajv@^4.7.0: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.1.0: +ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -665,7 +670,7 @@ aws4@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" -babel-code-frame@^6.16.0, babel-code-frame@^6.26.0: +babel-code-frame@^6.16.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -1691,6 +1696,20 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^2.1.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= + chart.js@^2.7.2: version "2.7.2" resolved "http://registry.npm.taobao.org/chart.js/download/chart.js-2.7.2.tgz#3c9fde4dc5b95608211bdefeda7e5d33dffa5714" @@ -2051,7 +2070,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.2: +concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.2, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: @@ -2232,7 +2251,7 @@ cross-env@^5.2.0: cross-spawn "^6.0.5" is-windows "^1.0.0" -cross-spawn@^5.0.1: +cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" dependencies: @@ -2905,7 +2924,7 @@ doctrine@^1.2.2: esutils "^2.0.2" isarray "^1.0.0" -doctrine@^2.0.0, doctrine@^2.0.2: +doctrine@^2.0.2, doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -3329,6 +3348,13 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-config-prettier@^6.10.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz#7b15e303bf9c956875c948f6b21500e48ded6a7f" + integrity sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg== + dependencies: + get-stdin "^6.0.0" + eslint-config-standard-jsx@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-3.2.0.tgz#c240e26ed919a11a42aa4de8059472b38268d620" @@ -3341,6 +3367,13 @@ eslint-config-standard@6.2.1, eslint-config-standard@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-6.2.1.tgz#d3a68aafc7191639e7ee441e7348739026354292" +eslint-plugin-prettier@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" + integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-plugin-promise@~3.4.0: version "3.4.2" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.4.2.tgz#1be2793eafe2d18b5b123b8136c269f804fe7122" @@ -3369,45 +3402,62 @@ eslint-plugin-standard@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-2.0.1.tgz#3589699ff9c917f2c25f76a916687f641c369ff3" -eslint@^3.13.1: - version "3.19.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" +eslint-scope@^3.7.1: + version "3.7.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" + integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== dependencies: - babel-code-frame "^6.16.0" - chalk "^1.1.3" - concat-stream "^1.5.2" - debug "^2.1.1" - doctrine "^2.0.0" - escope "^3.6.0" - espree "^3.4.0" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^4.18.2: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" esquery "^1.0.0" - estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" - glob "^7.0.3" - globals "^9.14.0" - ignore "^3.2.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" + inquirer "^3.0.6" is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" natural-compare "^1.4.0" optionator "^0.8.2" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.7.5" - strip-bom "^3.0.0" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" strip-json-comments "~2.0.1" - table "^3.7.8" + table "4.0.2" text-table "~0.2.0" - user-home "^2.0.0" eslint@~3.10.2: version "3.10.2" @@ -3457,7 +3507,7 @@ espower-location-detector@^1.0.0: source-map "^0.5.0" xtend "^4.0.0" -espree@^3.3.1, espree@^3.4.0: +espree@^3.3.1, espree@^3.5.4: version "3.5.4" resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" dependencies: @@ -3673,6 +3723,15 @@ extend@~3.0.1: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^2.0.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" @@ -3721,6 +3780,11 @@ fast-diff@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -4075,6 +4139,11 @@ function-name-support@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/function-name-support/-/function-name-support-0.2.0.tgz#55d3bfaa6eafd505a50f9bc81fdf57564a0bb071" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + galactus@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/galactus/-/galactus-0.2.1.tgz#cbed2d20a40c1f5679a35908e2b9415733e78db9" @@ -4247,7 +4316,12 @@ global@^4.3.0: min-document "^2.19.0" process "~0.5.1" -globals@^9.14.0, globals@^9.18.0, globals@^9.2.0: +globals@^11.0.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^9.18.0, globals@^9.2.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -4739,6 +4813,13 @@ iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" +iconv-lite@^0.4.17: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@~0.2.11: version "0.2.11" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.2.11.tgz#1ce60a3a57864a292d1321ff4609ca4bb965adc8" @@ -4771,6 +4852,11 @@ ignore@^3.0.9, ignore@^3.2.0: version "3.3.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.8.tgz#3f8e9c35d38708a3a7e0e9abb6c73e7ee7707b2b" +ignore@^3.3.3: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + immutable@^3.8.1: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" @@ -4856,6 +4942,26 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + interpret@^0.6.4: version "0.6.6" resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" @@ -5614,7 +5720,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" -js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@^3.8.1, js-yaml@^3.9.0: +js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@^3.8.1, js-yaml@^3.9.0, js-yaml@^3.9.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -5781,6 +5887,11 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" @@ -6663,6 +6774,11 @@ mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + nan@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -7088,7 +7204,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -7245,7 +7361,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -7385,6 +7501,11 @@ pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== + pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -7643,6 +7764,13 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" @@ -7701,6 +7829,11 @@ progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" @@ -8333,6 +8466,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== + regexpu-core@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" @@ -8474,7 +8612,7 @@ require-precompiled@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/require-precompiled/-/require-precompiled-0.1.0.tgz#5a1b52eb70ebed43eb982e974c85ab59571e56fa" -require-uncached@^1.0.2: +require-uncached@^1.0.2, require-uncached@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" dependencies: @@ -8566,6 +8704,13 @@ run-async@^0.1.0: dependencies: once "^1.3.0" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + run-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" @@ -8578,6 +8723,18 @@ rw@1: version "1.3.3" resolved "http://registry.npm.taobao.org/rw/download/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= + rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" @@ -8863,7 +9020,7 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" -slice-ansi@^1.0.0: +slice-ansi@1.0.0, slice-ansi@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" dependencies: @@ -9184,7 +9341,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: @@ -9378,6 +9535,18 @@ symbol-tree@^3.2.1, symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" +table@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== + dependencies: + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + table@^3.7.8: version "3.8.3" resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" @@ -9509,6 +9678,13 @@ tmp@0.0.28: dependencies: os-tmpdir "~1.0.1" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" From 216f588aa4e5de5e5137b40e1d50428fbd0a6593 Mon Sep 17 00:00:00 2001 From: Arcturus Date: Thu, 30 Jan 2020 01:24:28 +0000 Subject: [PATCH 51/65] extended language support for multiple languages --- locales/fa.json | 1 + locales/hu.json | 1 + locales/ja.json | 1 + locales/ko.json | 1 + locales/pl.json | 1 + locales/ru.json | 1 + locales/th.json | 1 + locales/tr.json | 1 + locales/zh-CN.json | 1 + locales/zh-TW.json | 1 + 10 files changed, 10 insertions(+) diff --git a/locales/fa.json b/locales/fa.json index 92a40ee6..07f8f6f0 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -8,6 +8,7 @@ "to create a new note": "برای ساخت یک یادداشت", "Toggle Mode": "تغییر حالت نمایش", "Trash": "سطل آشغال", + "Ok": "خوب", "MODIFICATION DATE": "تاریخ تغییر", "Words": "کلمات", "Letters": "حروف", diff --git a/locales/hu.json b/locales/hu.json index 89229abb..bc57e27c 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -9,6 +9,7 @@ "Toggle Mode": "Mód Váltás", "Add tag...": "Tag hozzáadása...", "Trash": "Lomtár", + "Ok": "oké", "MODIFICATION DATE": "MÓDOSÍTÁS DÁTUMA", "Words": "Szó", "Letters": "Betű", diff --git a/locales/ja.json b/locales/ja.json index bcd8d2d0..036bfe06 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -11,6 +11,7 @@ "Star": "お気に入り", "Fullscreen": "全画面", "Trash": "ゴミ箱", + "Ok": "OK", "Info": "情報", "MODIFICATION DATE": "修正日", "Words": "ワード", diff --git a/locales/ko.json b/locales/ko.json index 16623c49..2ccbad72 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -8,6 +8,7 @@ "to create a new note": "to create a new note", "Toggle Mode": "모드 전환", "Trash": "쓰레기 통", + "Ok": "확인", "MODIFICATION DATE": "변경 날짜", "Words": "단어 수", "Letters": "글자 수", diff --git a/locales/pl.json b/locales/pl.json index c32ea4bd..4a1bdac6 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -8,6 +8,7 @@ "to create a new note": "Aby stworzyć nową notatkę", "Toggle Mode": "Przełącz tryb", "Trash": "Kosz", + "Ok": "dobrze", "MODIFICATION DATE": "DATA MODYFIKACJI", "Words": "Słowa", "Letters": "Litery", diff --git a/locales/ru.json b/locales/ru.json index 1211c6b2..791bd228 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -8,6 +8,7 @@ "to create a new note": "создать новую запись", "Toggle Mode": "Переключить режим", "Trash": "Корзина", + "Ok": "Ok", "MODIFICATION DATE": "Дата изменения", "Words": "Слова", "Letters": "Буквы", diff --git a/locales/th.json b/locales/th.json index f8c73b27..a62dcf53 100644 --- a/locales/th.json +++ b/locales/th.json @@ -9,6 +9,7 @@ "Toggle Mode": "Toggle Mode", "Add tag...": "เพิ่มแท็ก...", "Trash": "ถังขยะ", + "Ok": "ตกลง.", "MODIFICATION DATE": "แก้ไขเมื่อ", "Words": "คำ", "Letters": "ตัวอักษร", diff --git a/locales/tr.json b/locales/tr.json index 8a673cb1..5cd43426 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -8,6 +8,7 @@ "to create a new note": "yeni not oluşturmak için", "Toggle Mode": "Mod Değiştir", "Trash": "Çöp", + "Ok": "tamam", "MODIFICATION DATE": "DEĞİŞİKLİK TARİHİ", "Words": "Kelimeler", "Letters": "Harfler", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 9ce5f755..4ac38cf3 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -8,6 +8,7 @@ "to create a new note": "新建笔记", "Toggle Mode": "切换模式", "Trash": "废纸篓", + "Ok": "好", "MODIFICATION DATE": "更改时间", "Words": "单词数", "Letters": "字数", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 7ecc0087..6ae9f368 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -8,6 +8,7 @@ "to create a new note": "新增筆記", "Toggle Mode": "切換模式", "Trash": "垃圾桶", + "Ok": "好", "MODIFICATION DATE": "修改時間", "Words": "單字", "Letters": "字數", From eae964f7e7f790411cc95f4f06d2f8dbc4899058 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 3 Feb 2020 18:45:01 +0100 Subject: [PATCH 52/65] add Vulcan UI theme --- browser/lib/ui-themes.js | 5 +++++ browser/styles/index.styl | 29 ++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/browser/lib/ui-themes.js b/browser/lib/ui-themes.js index 3d9044c6..0a66a349 100644 --- a/browser/lib/ui-themes.js +++ b/browser/lib/ui-themes.js @@ -31,6 +31,11 @@ export default [ label: i18n.__('Solarized Dark'), isDark: true }, + { + name: 'vulcan', + label: i18n.__('Vulcan'), + isDark: true + }, { name: 'white', label: i18n.__('White'), diff --git a/browser/styles/index.styl b/browser/styles/index.styl index 3fc65065..2338938f 100644 --- a/browser/styles/index.styl +++ b/browser/styles/index.styl @@ -428,6 +428,33 @@ $ui-nord-table-even-backgroundColor = darken($ui-nord-noteDetail-backgroundColor $ui-nord-table-head-backgroundColor = $ui-nord-table-even-backgroundColor $ui-nord-table-borderColor = lighten(darken(#21252B, 10%), 20%) +/******* Vulcan theme ********/ +$ui-vulcan-backgroundColor = #161719 +$ui-vulcan-noteList-backgroundColor = #161719 +$ui-vulcan-noteDetail-backgroundColor = #161719 +$ui-vulcan-tagList-backgroundColor = #FFFFFF + +$ui-vulcan-text-color = #999999 +$ui-vulcan-inactive-text-color = #999999 +$ui-vulcan-active-color = #ffffff + +$ui-vulcan-borderColor = #282a2e + +$ui-vulcan-tag-backgroundColor = #282a2e + +$ui-vulcan-button-backgroundColor = #282a2e +$ui-vulcan-button--active-color = #a3a8ae +$ui-vulcan-button--active-backgroundColor = #282a2e +$ui-vulcan-button--hover-backgroundColor = #282a2e + +$ui-vulcan-kbd-backgroundColor = lighten($ui-vulcan-text-color, 50%) +$ui-vulcan-kbd-color = $ui-vulcan-backgroundColor + +$ui-vulcan-table-odd-backgroundColor = $ui-vulcan-noteDetail-backgroundColor +$ui-vulcan-table-even-backgroundColor = darken($ui-vulcan-noteDetail-backgroundColor, 10%) +$ui-vulcan-table-head-backgroundColor = $ui-vulcan-table-even-backgroundColor +$ui-vulcan-table-borderColor = lighten(darken(#21252B, 10%), 20%) + debug-theme-var(theme, suffix) @@ -436,4 +463,4 @@ debug-theme-var(theme, suffix) get-theme-var(theme, suffix) lookup('$ui-' + theme + '-' + suffix) -$themes = 'monokai' 'nord' \ No newline at end of file +$themes = 'monokai' 'nord' 'vulcan' \ No newline at end of file From a496a84cb8e0435c2f7466cd259c734be4723f62 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 4 Feb 2020 16:23:31 +0100 Subject: [PATCH 53/65] fix broken headers' color in ConfigTab --- browser/main/modals/PreferencesModal/ConfigTab.styl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/main/modals/PreferencesModal/ConfigTab.styl b/browser/main/modals/PreferencesModal/ConfigTab.styl index dbfb393e..5ae5ba77 100644 --- a/browser/main/modals/PreferencesModal/ConfigTab.styl +++ b/browser/main/modals/PreferencesModal/ConfigTab.styl @@ -182,10 +182,12 @@ apply-theme(theme) color get-theme-var(theme, 'text-color') .group-header + .group-header--sub color get-theme-var(theme, 'text-color') border-color get-theme-var(theme, 'borderColor') .group-header2 + .group-header2--sub color get-theme-var(theme, 'text-color') .group-section-control-input From 1fe59caa195e58c938fe95aa192096b6741200c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2020 11:05:49 +0000 Subject: [PATCH 54/65] Bump merge from 1.2.0 to 1.2.1 Bumps [merge](https://github.com/yeikos/js.merge) from 1.2.0 to 1.2.1. - [Release notes](https://github.com/yeikos/js.merge/releases) - [Commits](https://github.com/yeikos/js.merge/compare/v1.2.0...v1.2.1) Signed-off-by: dependabot[bot] --- yarn.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7a2f8985..79971231 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6521,8 +6521,9 @@ merge-stream@^1.0.0, merge-stream@^1.0.1: readable-stream "^2.0.1" merge@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" + version "1.2.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" + integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== mermaid@^8.4.2: version "8.4.2" From d475146d803515efdb1f9b822e967b27cfe9100c Mon Sep 17 00:00:00 2001 From: jhdcruz Date: Wed, 23 Oct 2019 14:36:34 +0800 Subject: [PATCH 55/65] Add build dependency packages cmds --- docs/build.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/build.md b/docs/build.md index 2a8c22af..dae69612 100644 --- a/docs/build.md +++ b/docs/build.md @@ -82,13 +82,21 @@ Distribution packages are created by exec `grunt build` on Linux platform (e.g. After installing the supported version of `node` and `npm`, install build dependency packages. -Ubuntu/Debian: +**Ubuntu/Debian:** + +``` +$ npm i -g grunt-electron-installer-debian +``` ``` $ sudo apt-get install -y rpm fakeroot ``` -Fedora: +**Fedora:** + +``` +$ npm i -g grunt-electron-installer-redhat +``` ``` $ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot @@ -100,4 +108,4 @@ Then execute `grunt build`. $ grunt build ``` -You will find `.deb` and `.rpm` in the `dist` directory. +> You will find `.deb` and `.rpm` in the `dist` directory. From 997ffa620da230584f1bd9e10ba85c2bf98d3470 Mon Sep 17 00:00:00 2001 From: jhdcruz Date: Thu, 24 Oct 2019 15:36:04 +0800 Subject: [PATCH 56/65] Required both package for builds | Yarn --- docs/build.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/build.md b/docs/build.md index dae69612..937a4a58 100644 --- a/docs/build.md +++ b/docs/build.md @@ -82,11 +82,11 @@ Distribution packages are created by exec `grunt build` on Linux platform (e.g. After installing the supported version of `node` and `npm`, install build dependency packages. -**Ubuntu/Debian:** +``` +$ yarn add --dev grunt-electron-installer-debian grunt-electron-installer-redhat +``` -``` -$ npm i -g grunt-electron-installer-debian -``` +**Ubuntu/Debian:** ``` $ sudo apt-get install -y rpm fakeroot @@ -94,10 +94,6 @@ $ sudo apt-get install -y rpm fakeroot **Fedora:** -``` -$ npm i -g grunt-electron-installer-redhat -``` - ``` $ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot ``` From 8b2ed8585f611c3b07c0c47a75c86aa7dfdf5014 Mon Sep 17 00:00:00 2001 From: ZeroX-DG Date: Wed, 19 Feb 2020 00:17:14 +1300 Subject: [PATCH 57/65] fixed wrong handler for setting button --- browser/main/SideNav/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 5fd7b93c..cd11e652 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -589,7 +589,7 @@ class SideNav extends React.Component { onClick={this.handleSearchButtonClick} isActive={showSearch} /> - +
      {navSearch} From feb2a878a947de72c641f86d7ec5c7ebb8c5940a Mon Sep 17 00:00:00 2001 From: Alexander Wolf Date: Mon, 24 Feb 2020 08:51:05 +0100 Subject: [PATCH 58/65] Dropdown colour (theme aware) (#3472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * catchup (#7) * added rtl toggle button * added rtl toggle button * keep code styling aligned to the left and ltr at all times * added hotkey setting for direction toggle * fixed requested changes * fix undefined variable * Copyright info update updated Copyright (C) 2017 - 2019 BoostIO to Copyright (C) 2017 - 2020 BoostIO * Refine Chinese translation - Should not translate "space", it means space key in most context - Should translate "keymap", "spellcheck disabled", "auto detect" - Should translate "On Right Click" to "右键点击" - Refine misc translation. * Added Wiki Link * Add Traditional Chinese option to build.md * Bug fix (sets tabWith to 2 on prettier configuration and ConfigManager so checkboxes can be clikable) Co-authored-by: ibraude <48394109+ibraude@users.noreply.github.com> Co-authored-by: Junyoung Choi Co-authored-by: Satyendra <33686367+developersatyendra@users.noreply.github.com> Co-authored-by: Andrew * Menu Backgroun Colour * add white to the apply-theme loop Co-authored-by: Milo Todt Co-authored-by: ibraude <48394109+ibraude@users.noreply.github.com> Co-authored-by: Junyoung Choi Co-authored-by: Satyendra <33686367+developersatyendra@users.noreply.github.com> Co-authored-by: Andrew --- browser/main/NoteList/NoteList.styl | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/browser/main/NoteList/NoteList.styl b/browser/main/NoteList/NoteList.styl index 2b42ae3d..8fc5eadf 100644 --- a/browser/main/NoteList/NoteList.styl +++ b/browser/main/NoteList/NoteList.styl @@ -59,13 +59,6 @@ $control-height = 30px top $control-height overflow auto -body[data-theme="white"] - .root - background-color $ui-white-noteList-backgroundColor - - .control - background-color $ui-white-noteList-backgroundColor - apply-theme(theme) body[data-theme={theme}] .root @@ -80,6 +73,7 @@ apply-theme(theme) &:hover transition 0.2s color get-theme-var(theme, 'text-color') + background-color: get-theme-var(theme, 'noteList-backgroundColor') .control-button color get-theme-var(theme, 'inactive-text-color') @@ -91,8 +85,8 @@ apply-theme(theme) &:active color get-theme-var(theme, 'text-color') -for theme in 'dark' 'solarized-dark' 'dracula' +for theme in 'white' 'dark' 'solarized-dark' 'dracula' apply-theme(theme) for theme in $themes - apply-theme(theme) \ No newline at end of file + apply-theme(theme) From d706a5375c9ef488eb9ba28cafc05b07181029af Mon Sep 17 00:00:00 2001 From: Alex Garrity Date: Sun, 16 Feb 2020 23:49:49 +0000 Subject: [PATCH 59/65] Added sorting direction buttons --- browser/main/NoteList/index.js | 40 +++++++++++++++++++++++++++++++ browser/main/lib/ConfigManager.js | 1 + resources/icon/icon-up.svg | 17 +++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 resources/icon/icon-up.svg diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 9b9b7de0..19387acf 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -581,6 +581,20 @@ class NoteList extends React.Component { }) } + handleListDirectionButtonClick(e, direction) { + const { dispatch } = this.props + + const config = { + listDirection: direction + } + + ConfigManager.set(config) + dispatch({ + type: 'SET_CONFIG', + config + }) + } + alertIfSnippet(msg) { const warningMessage = msg => ({ @@ -1120,6 +1134,7 @@ class NoteList extends React.Component { let { notes } = this.props const { selectedNoteKeys } = this.state const sortBy = _.get(config, [folderKey, 'sortBy'], config.sortBy.default) + const sortDir = config.listDirection const sortFunc = sortBy === 'CREATED_AT' ? sortByCreatedAt @@ -1129,6 +1144,7 @@ class NoteList extends React.Component { const sortedNotes = location.pathname.match(/\/starred|\/trash/) ? this.getNotes().sort(sortFunc) : this.sortByPin(this.getNotes().sort(sortFunc)) + if (sortDir === 'DESCENDING') sortedNotes.reverse() this.notes = notes = sortedNotes.filter(note => { // this is for the trash box if (note.isTrashed !== true || location.pathname === '/trashed') @@ -1241,6 +1257,30 @@ class NoteList extends React.Component {
      + +
      ) : null} - +
      + +
      Tags
      -