1
0
mirror of https://github.com/BoostIo/Boostnote synced 2026-01-07 05:59:20 +00:00

Merge branch 'master' into date-iso8601

This commit is contained in:
Junyoung Choi
2020-07-20 19:42:08 +09:00
13 changed files with 420 additions and 47 deletions

View File

@@ -21,6 +21,8 @@ const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
import { createTurndownService } from '../lib/turndown'
import { languageMaps } from '../lib/CMLanguageList'
import snippetManager from '../lib/SnippetManager'
import { findStorage } from 'browser/lib/findStorage'
import { sendWakatimeHeartBeat } from 'browser/lib/wakatime-plugin'
import {
generateInEditor,
tocExistsInEditor
@@ -113,6 +115,16 @@ export default class CodeEditor extends React.Component {
this.editorActivityHandler = () => this.handleEditorActivity()
this.turndownService = createTurndownService()
// wakatime
const { storageKey, noteKey } = this.props
const storage = findStorage(storageKey)
if (storage)
sendWakatimeHeartBeat(storage.path, noteKey, storage.name, {
isWrite: false,
hasFileChanges: false,
isFileChange: true
})
}
handleSearch(msg) {
@@ -801,9 +813,23 @@ export default class CodeEditor extends React.Component {
this.updateHighlight(editor, changeObject)
this.value = editor.getValue()
const { storageKey, noteKey } = this.props
const storage = findStorage(storageKey)
if (this.props.onChange) {
this.props.onChange(editor)
}
const isWrite = !!this.props.onChange
const hasFileChanges = isWrite
if (storage) {
sendWakatimeHeartBeat(storage.path, noteKey, storage.name, {
isWrite,
hasFileChanges,
isFileChange: false
})
}
}
linePossibleContainsHeadline(currentLine) {
@@ -931,6 +957,16 @@ export default class CodeEditor extends React.Component {
this.restartHighlighting()
this.editor.on('change', this.changeHandler)
this.editor.refresh()
// wakatime
const { storageKey, noteKey } = this.props
const storage = findStorage(storageKey)
if (storage)
sendWakatimeHeartBeat(storage.path, noteKey, storage.name, {
isWrite: false,
hasFileChanges: false,
isFileChange: true
})
}
setValue(value) {

View File

@@ -1192,7 +1192,10 @@ class MarkdownPreview extends React.Component {
e.preventDefault()
e.stopPropagation()
const rawHref = e.target.getAttribute('href')
const el = e.target.closest('a[href]')
if (!el) return
const rawHref = el.getAttribute('href')
const { dispatch } = this.props
if (!rawHref) return // not checked href because parser will create file://... string for [empty link]()

View File

@@ -1,4 +1,4 @@
import mermaidAPI from 'mermaid'
import mermaidAPI from 'mermaid/dist/mermaid.min.js'
import uiThemes from 'browser/lib/ui-themes'
// fixes bad styling in the mermaid dark theme
@@ -61,7 +61,6 @@ function render(element, content, theme, enableHTMLLabel) {
el.setAttribute('ratio', ratio)
el.setAttribute('height', el.parentNode.clientWidth / ratio)
console.log(el)
}
})
} catch (e) {

View File

@@ -0,0 +1,49 @@
import config from 'browser/main/lib/ConfigManager'
const exec = require('child_process').exec
const path = require('path')
let lastHeartbeat = 0
function sendWakatimeHeartBeat(
storagePath,
noteKey,
storageName,
{ isWrite, hasFileChanges, isFileChange }
) {
if (
config.get().wakatime.isActive &&
!!config.get().wakatime.key &&
(new Date().getTime() - lastHeartbeat > 120000 || isFileChange)
) {
const notePath = path.join(storagePath, 'notes', noteKey + '.cson')
if (!isWrite && !hasFileChanges && !isFileChange) {
return
}
lastHeartbeat = new Date()
const wakatimeKey = config.get().wakatime.key
if (wakatimeKey) {
exec(
`wakatime --file ${notePath} --project '${storageName}' --key ${wakatimeKey} --plugin Boostnote-wakatime`,
(error, stdOut, stdErr) => {
if (error) {
console.log(error)
lastHeartbeat = 0
} else {
console.log(
'wakatime',
'isWrite',
isWrite,
'hasChanges',
hasFileChanges,
'isFileChange',
isFileChange
)
}
}
)
}
}
}
export { sendWakatimeHeartBeat }

View File

@@ -871,6 +871,8 @@ class SnippetNoteDetail extends React.Component {
hotkey={config.hotkey}
autoDetect={autoDetect}
dateFormatISO8601={config.editor.dateFormatISO8601}
storageKey={storageKey}
noteKey={note.key}
/>
)}
</div>

View File

@@ -139,7 +139,10 @@ export const DEFAULT_CONFIG = {
username: '',
password: ''
},
coloredTags: {}
coloredTags: {},
wakatime: {
key: null
}
}
function validate(config) {
@@ -255,6 +258,12 @@ function assignConfigValues(originalConfig, rcConfig) {
originalConfig.hotkey,
rcConfig.hotkey
)
config.wakatime = Object.assign(
{},
DEFAULT_CONFIG.wakatime,
originalConfig.wakatime,
rcConfig.wakatime
)
config.blog = Object.assign(
{},
DEFAULT_CONFIG.blog,

View File

@@ -1,4 +1,5 @@
import ConfigManager from 'browser/main/lib/ConfigManager'
import uiThemes from 'browser/lib/ui-themes'
const saveChanges = newConfig => {
ConfigManager.set(newConfig)
@@ -40,14 +41,7 @@ const chooseTheme = config => {
}
const applyTheme = theme => {
const supportedThemes = [
'dark',
'white',
'solarized-dark',
'monokai',
'dracula'
]
if (supportedThemes.indexOf(theme) !== -1) {
if (uiThemes.some(item => item.name === theme)) {
document.body.setAttribute('data-theme', theme)
if (document.body.querySelector('.MarkdownPreview')) {
document.body

View File

@@ -0,0 +1,207 @@
import PropTypes from 'prop-types'
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 _ from 'lodash'
import i18n from 'browser/lib/i18n'
import { sync as commandExists } from 'command-exists'
const electron = require('electron')
const ipc = electron.ipcRenderer
const { remote } = electron
const { dialog } = remote
class PluginsTab extends React.Component {
constructor(props) {
super(props)
this.state = {
config: props.config
}
}
componentDidMount() {
this.handleSettingDone = () => {
this.setState({
pluginsAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}
})
}
this.handleSettingError = err => {
this.setState({
pluginsAlert: {
type: 'error',
message:
err.message != null ? err.message : i18n.__('An error occurred!')
}
})
}
this.oldWakatimeConfig = this.state.config.wakatime
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
}
componentWillUnmount() {
ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError)
}
checkWakatimePluginRequirement() {
const { wakatime } = this.state.config
if (wakatime.isActive && !commandExists('wakatime')) {
this.setState({
wakatimePluginAlert: {
type: i18n.__('Warning'),
message: i18n.__('Missing wakatime cli')
}
})
const alertConfig = {
type: 'warning',
message: i18n.__('Missing Wakatime CLI'),
detail: i18n.__(
`Please install Wakatime CLI to use Wakatime tracker feature.`
),
buttons: [i18n.__('OK')]
}
dialog.showMessageBox(remote.getCurrentWindow(), alertConfig)
} else {
this.setState({
wakatimePluginAlert: null
})
}
}
handleSaveButtonClick(e) {
const newConfig = {
wakatime: {
isActive: this.state.config.wakatime.isActive,
key: this.state.config.wakatime.key
}
}
ConfigManager.set(newConfig)
store.dispatch({
type: 'SET_CONFIG',
config: newConfig
})
this.clearMessage()
this.props.haveToSave()
this.checkWakatimePluginRequirement()
}
handleIsWakatimePluginActiveChange(e) {
const { config } = this.state
config.wakatime.isActive = !config.wakatime.isActive
this.setState({
config
})
if (_.isEqual(this.oldWakatimeConfig.isActive, config.wakatime.isActive)) {
this.props.haveToSave()
} else {
this.props.haveToSave({
tab: 'Plugins',
type: 'warning',
message: i18n.__('Unsaved Changes!')
})
}
}
handleWakatimeKeyChange(e) {
const { config } = this.state
config.wakatime = {
isActive: true,
key: this.refs.wakatimeKey.value
}
this.setState({
config
})
if (_.isEqual(this.oldWakatimeConfig.key, config.wakatime.key)) {
this.props.haveToSave()
} else {
this.props.haveToSave({
tab: 'Plugins',
type: 'warning',
message: i18n.__('Unsaved Changes!')
})
}
}
clearMessage() {
_.debounce(() => {
this.setState({
pluginsAlert: null
})
}, 2000)()
}
render() {
const pluginsAlert = this.state.pluginsAlert
const pluginsAlertElement =
pluginsAlert != null ? (
<p className={`alert ${pluginsAlert.type}`}>{pluginsAlert.message}</p>
) : null
const wakatimeAlert = this.state.wakatimePluginAlert
const wakatimePluginAlertElement =
wakatimeAlert != null ? (
<p className={`alert ${wakatimeAlert.type}`}>{wakatimeAlert.message}</p>
) : null
const { config } = this.state
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>{i18n.__('Plugins')}</div>
<div styleName='group-header2'>{i18n.__('Wakatime')}</div>
<div styleName='group-checkBoxSection'>
<label>
<input
onChange={e => this.handleIsWakatimePluginActiveChange(e)}
checked={config.wakatime.isActive}
ref='wakatimeIsActive'
type='checkbox'
/>
&nbsp;
{i18n.__('Enable Wakatime')}
</label>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Wakatime key')}</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
onChange={e => this.handleWakatimeKeyChange(e)}
disabled={!config.wakatime.isActive}
ref='wakatimeKey'
value={config.wakatime.key}
type='text'
/>
{wakatimePluginAlertElement}
</div>
</div>
<div styleName='group-control'>
<button
styleName='group-control-rightButton'
onClick={e => this.handleSaveButtonClick(e)}
>
{i18n.__('Save')}
</button>
{pluginsAlertElement}
</div>
</div>
</div>
)
}
}
PluginsTab.propTypes = {
dispatch: PropTypes.func,
haveToSave: PropTypes.func
}
export default CSSModules(PluginsTab, styles)

View File

@@ -7,6 +7,7 @@ import InfoTab from './InfoTab'
import Crowdfunding from './Crowdfunding'
import StoragesTab from './StoragesTab'
import SnippetTab from './SnippetTab'
import PluginsTab from './PluginsTab'
import Blog from './Blog'
import ModalEscButton from 'browser/components/ModalEscButton'
import CSSModules from 'browser/lib/CSSModules'
@@ -82,6 +83,14 @@ class Preferences extends React.Component {
)
case 'SNIPPET':
return <SnippetTab dispatch={dispatch} config={config} data={data} />
case 'PLUGINS':
return (
<PluginsTab
dispatch={dispatch}
config={config}
haveToSave={alert => this.setState({ PluginsAlert: alert })}
/>
)
case 'STORAGES':
default:
return (
@@ -122,7 +131,8 @@ class Preferences extends React.Component {
{ 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: 'SNIPPET', label: i18n.__('Snippets') },
{ target: 'PLUGINS', label: i18n.__('Plugins') }
]
const navButtons = tabs.map(tab => {