diff --git a/.babelrc b/.babelrc index 270349d2..3a366286 100644 --- a/.babelrc +++ b/.babelrc @@ -7,7 +7,7 @@ "test": { "presets": ["env" ,"react", "es2015"], "plugins": [ - [ "babel-plugin-webpack-alias", { "config": "${PWD}/webpack.config.js" } ] + [ "babel-plugin-webpack-alias", { "config": "/webpack.config.js" } ] ] } } diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..4ec9b5bf --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://issuehunt.io/r/BoostIo/Boostnote diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 4893b342..1abd15a9 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -26,6 +26,8 @@ import {languageMaps} from '../lib/CMLanguageList' import snippetManager from '../lib/SnippetManager' 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' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' @@ -38,38 +40,6 @@ function translateHotkey (hotkey) { return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl') } -const validatorOfMarkdown = (text, updateLinting) => { - const lintOptions = { - 'strings': { - 'content': text - } - } - - return markdownlint(lintOptions, (err, result) => { - if (!err) { - const foundIssues = [] - result.content.map(item => { - let ruleNames = '' - item.ruleNames.map((ruleName, index) => { - ruleNames += ruleName - if (index === item.ruleNames.length - 1) { - ruleNames += ': ' - } else { - ruleNames += '/' - } - }) - foundIssues.push({ - from: CodeMirror.Pos(item.lineNumber, 0), - to: CodeMirror.Pos(item.lineNumber, 1), - message: ruleNames + item.ruleDescription, - severity: 'warning' - }) - }) - updateLinting(foundIssues) - } - }) -} - export default class CodeEditor extends React.Component { constructor (props) { super(props) @@ -116,6 +86,8 @@ export default class CodeEditor extends React.Component { this.searchHandler = (e, msg) => this.handleSearch(msg) this.searchState = null this.scrollToLineHandeler = this.scrollToLine.bind(this) + this.getCodeEditorLintConfig = this.getCodeEditorLintConfig.bind(this) + this.validatorOfMarkdown = this.validatorOfMarkdown.bind(this) this.formatTable = () => this.handleFormatTable() @@ -283,13 +255,12 @@ export default class CodeEditor extends React.Component { } componentDidMount () { - const { rulers, enableRulers } = this.props + const { rulers, enableRulers, enableMarkdownLint } = this.props eventEmitter.on('line:jump', this.scrollToLineHandeler) snippetManager.init() this.updateDefaultKeyMap() - const checkMarkdownNoteIsOpening = this.props.mode === 'Boost Flavored Markdown' this.value = this.props.value this.editor = CodeMirror(this.refs.root, { rulers: buildCMRulers(rulers, enableRulers), @@ -306,10 +277,7 @@ export default class CodeEditor extends React.Component { inputStyle: 'textarea', dragDrop: false, foldGutter: true, - lint: checkMarkdownNoteIsOpening ? { - 'getAnnotations': validatorOfMarkdown, - 'async': true - } : false, + lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], autoCloseBrackets: { pairs: this.props.matchingPairs, @@ -320,6 +288,8 @@ export default class CodeEditor extends React.Component { extraKeys: this.defaultKeyMap }) + 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) } else { @@ -546,7 +516,9 @@ export default class CodeEditor extends React.Component { let needRefresh = false const { rulers, - enableRulers + enableRulers, + enableMarkdownLint, + customMarkdownLintConfig } = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) @@ -564,6 +536,16 @@ export default class CodeEditor extends React.Component { if (prevProps.keyMap !== this.props.keyMap) { needRefresh = true } + if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) { + if (!enableMarkdownLint) { + 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' + } + needRefresh = true + } if ( prevProps.enableRulers !== enableRulers || @@ -644,6 +626,56 @@ export default class CodeEditor extends React.Component { } } + getCodeEditorLintConfig () { + const { mode } = this.props + const checkMarkdownNoteIsOpen = mode === 'Boost Flavored Markdown' + + return checkMarkdownNoteIsOpen ? { + 'getAnnotations': this.validatorOfMarkdown, + 'async': true + } : false + } + + validatorOfMarkdown (text, updateLinting) { + const { customMarkdownLintConfig } = this.props + let lintConfigJson + try { + Jsonlint.parse(customMarkdownLintConfig) + lintConfigJson = JSON.parse(customMarkdownLintConfig) + } catch (err) { + eventEmitter.emit('APP_SETTING_ERROR') + return + } + const lintOptions = { + 'strings': { + 'content': text + }, + 'config': lintConfigJson + } + + return markdownlint(lintOptions, (err, result) => { + if (!err) { + const foundIssues = [] + const splitText = text.split('\n') + result.content.map(item => { + let ruleNames = '' + item.ruleNames.map((ruleName, index) => { + ruleNames += ruleName + ruleNames += (index === item.ruleNames.length - 1) ? ': ' : '/' + }) + const lineNumber = item.lineNumber - 1 + foundIssues.push({ + from: CodeMirror.Pos(lineNumber, 0), + to: CodeMirror.Pos(lineNumber, splitText[lineNumber].length), + message: ruleNames + item.ruleDescription, + severity: 'warning' + }) + }) + updateLinting(foundIssues) + } + }) + } + setMode (mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text')) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') @@ -1105,13 +1137,11 @@ export default class CodeEditor extends React.Component { } ref='root' tabIndex='-1' - style={ - { + style={{ fontFamily, fontSize: fontSize, width: width - } - } + }} onDrop={ e => this.handleDropImage(e) } @@ -1149,7 +1179,9 @@ CodeEditor.propTypes = { onChange: PropTypes.func, readOnly: PropTypes.bool, autoDetect: PropTypes.bool, - spellCheck: PropTypes.bool + spellCheck: PropTypes.bool, + enableMarkdownLint: PropTypes.bool, + customMarkdownLintConfig: PropTypes.string } CodeEditor.defaultProps = { @@ -1161,5 +1193,7 @@ CodeEditor.defaultProps = { indentSize: 4, indentType: 'space', autoDetect: false, - spellCheck: false + spellCheck: false, + enableMarkdownLint: DEFAULT_CONFIG.editor.enableMarkdownLint, + customMarkdownLintConfig: DEFAULT_CONFIG.editor.customMarkdownLintConfig } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 593f7d99..526ecb39 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -319,6 +319,8 @@ class MarkdownEditor extends React.Component { enableSmartPaste={config.editor.enableSmartPaste} hotkey={config.hotkey} switchPreview={config.editor.switchPreview} + enableMarkdownLint={config.editor.enableMarkdownLint} + customMarkdownLintConfig={config.editor.customMarkdownLintConfig} />
this.handleMouseDown(e)} >
diff --git a/browser/lib/CSSModules.js b/browser/lib/CSSModules.js index 181274f4..691b44d2 100644 --- a/browser/lib/CSSModules.js +++ b/browser/lib/CSSModules.js @@ -1,5 +1,5 @@ import CSSModules from 'react-css-modules' export default function (component, styles) { - return CSSModules(component, styles, {errorWhenNotFound: false}) + return CSSModules(component, styles, {handleNotFoundStyleName: 'log'}) } diff --git a/browser/lib/Languages.js b/browser/lib/Languages.js index 8c3747a9..435747a9 100644 --- a/browser/lib/Languages.js +++ b/browser/lib/Languages.js @@ -62,10 +62,12 @@ const languages = [ { name: 'Spanish', locale: 'es-ES' - }, { + }, + { name: 'Turkish', locale: 'tr' - }, { + }, + { name: 'Thai', locale: 'th' } @@ -82,4 +84,3 @@ module.exports = { return languages } } - diff --git a/browser/lib/newNote.js b/browser/lib/newNote.js index d8ef196f..4eec24f1 100644 --- a/browser/lib/newNote.js +++ b/browser/lib/newNote.js @@ -1,7 +1,8 @@ -import { hashHistory } from 'react-router' import dataApi from 'browser/main/lib/dataApi' import ee from 'browser/main/lib/eventEmitter' import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig' +import queryString from 'query-string' +import { push } from 'connected-react-router' export function createMarkdownNote (storage, folder, dispatch, location, params, config) { AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_MARKDOWN') @@ -28,10 +29,10 @@ export function createMarkdownNote (storage, folder, dispatch, location, params, note: note }) - hashHistory.push({ + dispatch(push({ pathname: location.pathname, - query: { key: noteHash } - }) + search: queryString.stringify({ key: noteHash }) + })) ee.emit('list:jump', noteHash) ee.emit('detail:focus') }) @@ -70,10 +71,10 @@ export function createSnippetNote (storage, folder, dispatch, location, params, type: 'UPDATE_NOTE', note: note }) - hashHistory.push({ + dispatch(push({ pathname: location.pathname, - query: { key: noteHash } - }) + search: queryString.stringify({ key: noteHash }) + })) ee.emit('list:jump', noteHash) ee.emit('detail:focus') }) diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index cf3be072..06fb91a5 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -9,7 +9,6 @@ import StarButton from './StarButton' import TagSelect from './TagSelect' import FolderSelect from './FolderSelect' import dataApi from 'browser/main/lib/dataApi' -import { hashHistory } from 'react-router' import ee from 'browser/main/lib/eventEmitter' import markdown from 'browser/lib/markdownTextHelper' import StatusBar from '../StatusBar' @@ -30,6 +29,8 @@ import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus' import striptags from 'striptags' import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote' import markdownToc from 'browser/lib/markdown-toc-generator' +import queryString from 'query-string' +import { replace } from 'connected-react-router' class MarkdownNoteDetail extends React.Component { constructor (props) { @@ -140,6 +141,7 @@ class MarkdownNoteDetail extends React.Component { } handleFolderChange (e) { + const { dispatch } = this.props const { note } = this.state const value = this.refs.folder.value const splitted = value.split('-') @@ -159,12 +161,12 @@ class MarkdownNoteDetail extends React.Component { originNote: note, note: newNote }) - hashHistory.replace({ + dispatch(replace({ pathname: location.pathname, - query: { + search: queryString.stringify({ key: newNote.key - } - }) + }) + })) this.setState({ isMovingNote: false }) @@ -491,7 +493,7 @@ class MarkdownNoteDetail extends React.Component { button, div + -webkit-user-drag none + user-select none + > img, span + -webkit-user-drag none + user-select none diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 80378793..7503addb 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -8,7 +8,6 @@ import StarButton from './StarButton' import TagSelect from './TagSelect' import FolderSelect from './FolderSelect' import dataApi from 'browser/main/lib/dataApi' -import {hashHistory} from 'react-router' import ee from 'browser/main/lib/eventEmitter' import CodeMirror from 'codemirror' import 'codemirror-mode-elixir' @@ -18,7 +17,6 @@ import context from 'browser/lib/context' import ConfigManager from 'browser/main/lib/ConfigManager' import _ from 'lodash' import {findNoteTitle} from 'browser/lib/findNoteTitle' -import convertModeName from 'browser/lib/convertModeName' import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig' import FullscreenButton from './FullscreenButton' import TrashButton from './TrashButton' @@ -31,6 +29,8 @@ import { formatDate } from 'browser/lib/date-formatter' import i18n from 'browser/lib/i18n' import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote' import markdownToc from 'browser/lib/markdown-toc-generator' +import queryString from 'query-string' +import { replace } from 'connected-react-router' const electron = require('electron') const { remote } = electron @@ -166,12 +166,12 @@ class SnippetNoteDetail extends React.Component { originNote: note, note: newNote }) - hashHistory.replace({ + dispatch(replace({ pathname: location.pathname, - query: { + search: queryString.stringify({ key: newNote.key - } - }) + }) + })) this.setState({ isMovingNote: false }) @@ -814,7 +814,7 @@ class SnippetNoteDetail extends React.Component { note) const trashedNotes = data.trashedSet.toJS().map(uniqueKey => data.noteMap.get(uniqueKey)) let displayedNotes = allNotes diff --git a/browser/main/DevTools/index.dev.js b/browser/main/DevTools/index.dev.js new file mode 100644 index 00000000..77d3eccd --- /dev/null +++ b/browser/main/DevTools/index.dev.js @@ -0,0 +1,16 @@ +import React from 'react' +import { createDevTools } from 'redux-devtools' +import LogMonitor from 'redux-devtools-log-monitor' +import DockMonitor from 'redux-devtools-dock-monitor' + +const DevTools = createDevTools( + + + +) + +export default DevTools diff --git a/browser/main/DevTools/index.js b/browser/main/DevTools/index.js new file mode 100644 index 00000000..93d666a2 --- /dev/null +++ b/browser/main/DevTools/index.js @@ -0,0 +1,8 @@ +/* eslint-disable no-undef */ +if (process.env.NODE_ENV === 'production') { + // eslint-disable-next-line global-require + module.exports = require('./index.prod').default +} else { + // eslint-disable-next-line global-require + module.exports = require('./index.dev').default +} diff --git a/browser/main/DevTools/index.prod.js b/browser/main/DevTools/index.prod.js new file mode 100644 index 00000000..762cae2c --- /dev/null +++ b/browser/main/DevTools/index.prod.js @@ -0,0 +1,6 @@ +import React from 'react' + +const DevTools = () =>
+DevTools.instrument = () => {} + +export default DevTools diff --git a/browser/main/Main.js b/browser/main/Main.js index 26fc8377..30bf8e8a 100644 --- a/browser/main/Main.js +++ b/browser/main/Main.js @@ -12,11 +12,11 @@ import _ from 'lodash' import ConfigManager from 'browser/main/lib/ConfigManager' import mobileAnalytics from 'browser/main/lib/AwsMobileAnalyticsConfig' import eventEmitter from 'browser/main/lib/eventEmitter' -import { hashHistory } from 'react-router' -import store from 'browser/main/store' +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 { push } from 'connected-react-router' const path = require('path') const electron = require('electron') const { remote } = electron @@ -132,7 +132,7 @@ class Main extends React.Component { .then(() => data.storage) }) .then(storage => { - hashHistory.push('/storages/' + storage.key) + store.dispatch(push('/storages/' + storage.key)) }) .catch(err => { throw err @@ -311,7 +311,7 @@ class Main extends React.Component { onMouseUp={e => this.handleMouseUp(e)} > {!config.isSideNavFolded && @@ -341,7 +341,7 @@ class Main extends React.Component { 'dispatch', 'config', 'data', - 'params', + 'match', 'location' ])} /> @@ -351,7 +351,7 @@ class Main extends React.Component { 'dispatch', 'data', 'config', - 'params', + 'match', 'location' ])} /> @@ -373,7 +373,7 @@ class Main extends React.Component { 'dispatch', 'data', 'config', - 'params', + 'match', 'location' ])} ignorePreviewPointerEvents={this.state.isRightSliderFocused} diff --git a/browser/main/NewNoteButton/index.js b/browser/main/NewNoteButton/index.js index c34443be..115d9530 100644 --- a/browser/main/NewNoteButton/index.js +++ b/browser/main/NewNoteButton/index.js @@ -21,23 +21,20 @@ class NewNoteButton extends React.Component { this.state = { } - this.newNoteHandler = () => { - this.handleNewNoteButtonClick() - } + this.handleNewNoteButtonClick = this.handleNewNoteButtonClick.bind(this) } componentDidMount () { - eventEmitter.on('top:new-note', this.newNoteHandler) + eventEmitter.on('top:new-note', this.handleNewNoteButtonClick) } componentWillUnmount () { - eventEmitter.off('top:new-note', this.newNoteHandler) + eventEmitter.off('top:new-note', this.handleNewNoteButtonClick) } handleNewNoteButtonClick (e) { - const { location, params, dispatch, config } = this.props + const { location, dispatch, match: { params }, config } = this.props const { storage, folder } = this.resolveTargetFolder() - if (config.ui.defaultNote === 'MARKDOWN_NOTE') { createMarkdownNote(storage.key, folder.key, dispatch, location, params, config) } else if (config.ui.defaultNote === 'SNIPPET_NOTE') { @@ -55,9 +52,8 @@ class NewNoteButton extends React.Component { } resolveTargetFolder () { - const { data, params } = this.props + const { data, match: { params } } = this.props let storage = data.storageMap.get(params.storageKey) - // Find first storage if (storage == null) { for (const kv of data.storageMap) { @@ -93,7 +89,7 @@ class NewNoteButton extends React.Component { >
diff --git a/browser/main/index.js b/browser/main/index.js index 6e8bdcc5..b3a909e5 100644 --- a/browser/main/index.js +++ b/browser/main/index.js @@ -1,11 +1,13 @@ import { Provider } from 'react-redux' import Main from './Main' -import store from './store' -import React from 'react' +import { store, history } from './store' +import React, { Fragment } from 'react' import ReactDOM from 'react-dom' require('!!style!css!stylus?sourceMap!./global.styl') -import { Router, Route, IndexRoute, IndexRedirect, hashHistory } from 'react-router' -import { syncHistoryWithStore } from 'react-router-redux' +import { Route, Switch, Redirect } from 'react-router-dom' +import { ConnectedRouter } from 'connected-react-router' +import DevTools from './DevTools' + require('./lib/ipcClient') require('../lib/customMeta') import i18n from 'browser/lib/i18n' @@ -77,7 +79,6 @@ document.addEventListener('click', function (e) { }) const el = document.getElementById('content') -const history = syncHistoryWithStore(hashHistory, store) function notify (...args) { return new window.Notification(...args) @@ -98,29 +99,24 @@ function updateApp () { ReactDOM.render(( - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + {/* storages */} + + + + + + + ), el, function () { const loadingCover = document.getElementById('loadingCover') diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index f20b3d88..bea019fa 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -11,6 +11,10 @@ const consts = require('browser/lib/consts') let isInitialized = false +const DEFAULT_MARKDOWN_LINT_CONFIG = `{ + "default": true +}` + export const DEFAULT_CONFIG = { zoom: 1, isSideNavFolded: false, @@ -59,7 +63,9 @@ export const DEFAULT_CONFIG = { enableFrontMatterTitle: true, frontMatterTitleField: 'title', spellcheck: false, - enableSmartPaste: false + enableSmartPaste: false, + enableMarkdownLint: false, + customMarkdownLintConfig: DEFAULT_MARKDOWN_LINT_CONFIG }, preview: { fontSize: '14', diff --git a/browser/main/lib/modal.js b/browser/main/lib/modal.js index 7a7a9c8c..955cb5c8 100644 --- a/browser/main/lib/modal.js +++ b/browser/main/lib/modal.js @@ -1,7 +1,7 @@ import React from 'react' import { Provider } from 'react-redux' import ReactDOM from 'react-dom' -import store from '../store' +import { store } from '../store' class ModalBase extends React.Component { constructor (props) { diff --git a/browser/main/modals/CreateFolderModal.js b/browser/main/modals/CreateFolderModal.js index b061b0f3..b48d6e42 100644 --- a/browser/main/modals/CreateFolderModal.js +++ b/browser/main/modals/CreateFolderModal.js @@ -3,7 +3,7 @@ import React from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './CreateFolderModal.styl' import dataApi from 'browser/main/lib/dataApi' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import consts from 'browser/lib/consts' import ModalEscButton from 'browser/components/ModalEscButton' import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig' diff --git a/browser/main/modals/NewNoteModal.js b/browser/main/modals/NewNoteModal.js index 41c174cb..a17a36cd 100644 --- a/browser/main/modals/NewNoteModal.js +++ b/browser/main/modals/NewNoteModal.js @@ -4,6 +4,7 @@ import styles from './NewNoteModal.styl' import ModalEscButton from 'browser/components/ModalEscButton' import i18n from 'browser/lib/i18n' import { createMarkdownNote, createSnippetNote } from 'browser/lib/newNote' +import queryString from 'query-string' class NewNoteModal extends React.Component { constructor (props) { @@ -21,7 +22,8 @@ class NewNoteModal extends React.Component { } handleMarkdownNoteButtonClick (e) { - const { storage, folder, dispatch, location, params, config } = this.props + 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(() => { @@ -38,7 +40,8 @@ class NewNoteModal extends React.Component { } handleSnippetNoteButtonClick (e) { - const { storage, folder, dispatch, location, params, config } = this.props + 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(() => { diff --git a/browser/main/modals/PreferencesModal/Blog.js b/browser/main/modals/PreferencesModal/Blog.js index 2c93fb29..4d59bea1 100644 --- a/browser/main/modals/PreferencesModal/Blog.js +++ b/browser/main/modals/PreferencesModal/Blog.js @@ -2,7 +2,7 @@ import React from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './ConfigTab.styl' import ConfigManager from 'browser/main/lib/ConfigManager' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import PropTypes from 'prop-types' import _ from 'lodash' import i18n from 'browser/lib/i18n' diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js index dc9082b9..e6bd1e37 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.js +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -4,7 +4,7 @@ import CSSModules from 'browser/lib/CSSModules' import ReactDOM from 'react-dom' import styles from './FolderItem.styl' import dataApi from 'browser/main/lib/dataApi' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import { SketchPicker } from 'react-color' import { SortableElement, SortableHandle } from 'react-sortable-hoc' import i18n from 'browser/lib/i18n' diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index e7cc6f94..02f5cee9 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -3,7 +3,7 @@ import React from 'react' import CSSModules from 'browser/lib/CSSModules' import dataApi from 'browser/main/lib/dataApi' import styles from './FolderList.styl' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import FolderItem from './FolderItem' import { SortableContainer } from 'react-sortable-hoc' import i18n from 'browser/lib/i18n' diff --git a/browser/main/modals/PreferencesModal/HotkeyTab.js b/browser/main/modals/PreferencesModal/HotkeyTab.js index 218a68f6..713f6a65 100644 --- a/browser/main/modals/PreferencesModal/HotkeyTab.js +++ b/browser/main/modals/PreferencesModal/HotkeyTab.js @@ -3,7 +3,7 @@ import React from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './ConfigTab.styl' import ConfigManager from 'browser/main/lib/ConfigManager' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import _ from 'lodash' import i18n from 'browser/lib/i18n' diff --git a/browser/main/modals/PreferencesModal/InfoTab.js b/browser/main/modals/PreferencesModal/InfoTab.js index 8682f62d..71e99da9 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.js +++ b/browser/main/modals/PreferencesModal/InfoTab.js @@ -2,7 +2,7 @@ import React from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './InfoTab.styl' import ConfigManager from 'browser/main/lib/ConfigManager' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig' import _ from 'lodash' import i18n from 'browser/lib/i18n' diff --git a/browser/main/modals/PreferencesModal/StorageItem.js b/browser/main/modals/PreferencesModal/StorageItem.js index 3a2b075c..9af02962 100644 --- a/browser/main/modals/PreferencesModal/StorageItem.js +++ b/browser/main/modals/PreferencesModal/StorageItem.js @@ -4,7 +4,7 @@ import CSSModules from 'browser/lib/CSSModules' import styles from './StorageItem.styl' import consts from 'browser/lib/consts' import dataApi from 'browser/main/lib/dataApi' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import FolderList from './FolderList' import i18n from 'browser/lib/i18n' diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 994ca3d3..f74dbda5 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -3,7 +3,7 @@ import React from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './ConfigTab.styl' import ConfigManager from 'browser/main/lib/ConfigManager' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import consts from 'browser/lib/consts' import ReactCodeMirror from 'react-codemirror' import CodeMirror from 'codemirror' @@ -30,7 +30,9 @@ class UiTab extends React.Component { componentDidMount () { CodeMirror.autoLoadMode(this.codeMirrorInstance.getCodeMirror(), 'javascript') CodeMirror.autoLoadMode(this.customCSSCM.getCodeMirror(), 'css') + CodeMirror.autoLoadMode(this.customMarkdownLintConfigCM.getCodeMirror(), 'javascript') this.customCSSCM.getCodeMirror().setSize('400px', '400px') + this.customMarkdownLintConfigCM.getCodeMirror().setSize('400px', '200px') this.handleSettingDone = () => { this.setState({UiAlert: { type: 'success', @@ -101,7 +103,9 @@ class UiTab extends React.Component { matchingTriples: this.refs.matchingTriples.value, explodingPairs: this.refs.explodingPairs.value, spellcheck: this.refs.spellcheck.checked, - enableSmartPaste: this.refs.enableSmartPaste.checked + enableSmartPaste: this.refs.enableSmartPaste.checked, + enableMarkdownLint: this.refs.enableMarkdownLint.checked, + customMarkdownLintConfig: this.customMarkdownLintConfigCM.getCodeMirror().getValue() }, preview: { fontSize: this.refs.previewFontSize.value, @@ -637,6 +641,34 @@ class UiTab extends React.Component { />
+
+
+ {i18n.__('Custom MarkdownLint Rules')} +
+
+ this.handleUIChange(e)} + checked={this.state.config.editor.enableMarkdownLint} + ref='enableMarkdownLint' + type='checkbox' + />  + {i18n.__('Enable MarkdownLint')} +
+ this.handleUIChange(e)} + ref={e => (this.customMarkdownLintConfigCM = e)} + value={config.editor.customMarkdownLintConfig} + options={{ + lineNumbers: true, + mode: 'application/json', + theme: codemirrorTheme, + lint: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'] + }} /> +
+
+
{i18n.__('Preview')}
diff --git a/browser/main/modals/RenameFolderModal.js b/browser/main/modals/RenameFolderModal.js index edbcee67..9fdd70c8 100644 --- a/browser/main/modals/RenameFolderModal.js +++ b/browser/main/modals/RenameFolderModal.js @@ -3,7 +3,7 @@ import React from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './RenameFolderModal.styl' import dataApi from 'browser/main/lib/dataApi' -import store from 'browser/main/store' +import { store } from 'browser/main/store' import ModalEscButton from 'browser/components/ModalEscButton' import i18n from 'browser/lib/i18n' diff --git a/browser/main/store.js b/browser/main/store.js index 5edc115f..c708c3ad 100644 --- a/browser/main/store.js +++ b/browser/main/store.js @@ -1,8 +1,10 @@ -import { combineReducers, createStore } from 'redux' -import { routerReducer } from 'react-router-redux' +import { combineReducers, createStore, compose, applyMiddleware } from 'redux' +import { connectRouter, routerMiddleware } from 'connected-react-router' +import { createHashHistory as createHistory } from 'history' import ConfigManager from 'browser/main/lib/ConfigManager' import { Map, Set } from 'browser/lib/Mutable' import _ from 'lodash' +import DevTools from './DevTools' function defaultDataMap () { return { @@ -465,13 +467,16 @@ function getOrInitItem (target, key) { return results } +const history = createHistory() + const reducer = combineReducers({ data, config, status, - routing: routerReducer + router: connectRouter(history) }) -const store = createStore(reducer) +const store = createStore(reducer, undefined, compose( + applyMiddleware(routerMiddleware(history)), DevTools.instrument())) -export default store +export { store, history } diff --git a/docs/code_style.md b/docs/code_style.md index d8f458d7..c0416216 100644 --- a/docs/code_style.md +++ b/docs/code_style.md @@ -79,4 +79,11 @@ class MyComponent extends React.Component { // code goes here... } } -``` \ No newline at end of file +``` + +## React Hooks +Existing code will be kept class-based and will only be changed to functional components with hooks if it improves readability or makes things more reusable. + +For new components it's OK to use hooks with functional components but don't mix hooks & class-based components within a feature - just for code style / readability reasons. + +Read more about hooks in the [React hooks introduction](https://reactjs.org/docs/hooks-intro.html). diff --git a/lib/main-window.js b/lib/main-window.js index 418fd442..e650cb92 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -6,6 +6,33 @@ const Config = require('electron-config') const config = new Config() const _ = require('lodash') +// set up some chrome extensions +if (process.env.NODE_ENV === 'development') { + const { + default: installExtension, + REACT_DEVELOPER_TOOLS, + REACT_PERF + } = require('electron-devtools-installer') + + require('electron-debug')({ showDevTools: false }) + + const ChromeLens = { + // ID of the extension (https://chrome.google.com/webstore/detail/chromelens/idikgljglpfilbhaboonnpnnincjhjkd) + id: 'idikgljglpfilbhaboonnpnnincjhjkd', + electron: '>=1.2.1' + } + + const extensions = [REACT_DEVELOPER_TOOLS, REACT_PERF, ChromeLens] + + for (const extension of extensions) { + try { + installExtension(extension) + } catch (e) { + console.error(`[ELECTRON] Extension installation failed`, e) + } + } +} + const windowSize = config.get('windowsize') || { x: null, y: null, @@ -27,8 +54,7 @@ const mainWindow = new BrowserWindow({ }, icon: path.resolve(__dirname, '../resources/app.png') }) - -const url = path.resolve(__dirname, './main.html') +const url = path.resolve(__dirname, process.env.NODE_ENV === 'production' ? './main.production.html' : './main.development.html') mainWindow.loadURL('file://' + url) mainWindow.setMenuBarVisibility(false) diff --git a/lib/main.development.html b/lib/main.development.html new file mode 100644 index 00000000..38e2cea9 --- /dev/null +++ b/lib/main.development.html @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + Boostnote + + + + + +
+ +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/main.html b/lib/main.production.html similarity index 95% rename from lib/main.html rename to lib/main.production.html index 87a88e2d..ffd9eec3 100644 --- a/lib/main.html +++ b/lib/main.production.html @@ -10,7 +10,6 @@ - Boostnote @@ -126,8 +125,6 @@ - - - - + +