1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-25 23:51:51 +00:00

Merge branch 'text-expansion-support' of https://github.com/ZeroX-DG/Boostnote into text-expansion-support

This commit is contained in:
Nguyễn Việt Hưng
2018-04-27 11:17:17 +07:00
42 changed files with 1122 additions and 347 deletions

View File

@@ -3,10 +3,7 @@ import React from 'react'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import path from 'path'
import copyImage from 'browser/main/lib/dataApi/copyImage'
import { findStorage } from 'browser/lib/findStorage'
import fs from 'fs'
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
import eventEmitter from 'browser/main/lib/eventEmitter'
import iconv from 'iconv-lite'
import crypto from 'crypto'
@@ -349,23 +346,13 @@ export default class CodeEditor extends React.Component {
this.editor.setCursor(cursor)
}
handleDropImage (e) {
e.preventDefault()
const ValidImageTypes = ['image/gif', 'image/jpeg', 'image/png']
const file = e.dataTransfer.files[0]
const filePath = file.path
const filename = path.basename(filePath)
const fileType = file['type']
copyImage(filePath, this.props.storageKey).then((imagePath) => {
var showPreview = ValidImageTypes.indexOf(fileType) > 0
const imageMd = `${showPreview ? '!' : ''}[${filename}](${path.join('/:storage', imagePath)})`
this.insertImageMd(imageMd)
})
handleDropImage (dropEvent) {
dropEvent.preventDefault()
const {storageKey, noteKey} = this.props
attachmentManagement.handleAttachmentDrop(this, storageKey, noteKey, dropEvent)
}
insertImageMd (imageMd) {
insertAttachmentMd (imageMd) {
this.editor.replaceSelection(imageMd)
}
@@ -391,24 +378,8 @@ export default class CodeEditor extends React.Component {
return prevChar === '](' && nextChar === ')'
}
if (dataTransferItem.type.match('image')) {
const blob = dataTransferItem.getAsFile()
const reader = new FileReader()
let base64data
reader.readAsDataURL(blob)
reader.onloadend = () => {
base64data = reader.result.replace(/^data:image\/png;base64,/, '')
base64data += base64data.replace('+', ' ')
const binaryData = new Buffer(base64data, 'base64').toString('binary')
const imageName = Math.random().toString(36).slice(-16)
const storagePath = findStorage(this.props.storageKey).path
const imageDir = path.join(storagePath, 'images')
if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir)
const imagePath = path.join(imageDir, `${imageName}.png`)
fs.writeFile(imagePath, binaryData, 'binary')
const imageMd = `![${imageName}](${path.join('/:storage', `${imageName}.png`)})`
this.insertImageMd(imageMd)
}
const {storageKey, noteKey} = this.props
attachmentManagement.handlePastImageEvent(this, storageKey, noteKey, dataTransferItem)
} else if (this.props.fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) {
this.handlePasteUrl(e, editor, pastedTxt)
}

View File

@@ -5,7 +5,7 @@ import styles from './MarkdownEditor.styl'
import CodeEditor from 'browser/components/CodeEditor'
import MarkdownPreview from 'browser/components/MarkdownPreview'
import eventEmitter from 'browser/main/lib/eventEmitter'
import {findStorage} from 'browser/lib/findStorage'
import { findStorage } from 'browser/lib/findStorage'
class MarkdownEditor extends React.Component {
constructor (props) {
@@ -223,7 +223,7 @@ class MarkdownEditor extends React.Component {
}
render () {
const { className, value, config, storageKey } = this.props
const {className, value, config, storageKey, noteKey} = this.props
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
@@ -263,6 +263,7 @@ class MarkdownEditor extends React.Component {
displayLineNumbers={config.editor.displayLineNumbers}
scrollPastEnd={config.editor.scrollPastEnd}
storageKey={storageKey}
noteKey={noteKey}
fetchUrlTitle={config.editor.fetchUrlTitle}
onChange={(e) => this.handleChange(e)}
onBlur={(e) => this.handleBlur(e)}

View File

@@ -13,9 +13,11 @@ import htmlTextHelper from 'browser/lib/htmlTextHelper'
import copy from 'copy-to-clipboard'
import mdurl from 'mdurl'
import exportNote from 'browser/main/lib/dataApi/exportNote'
import {escapeHtmlCharacters} from 'browser/lib/utils'
import { escapeHtmlCharacters } from 'browser/lib/utils'
const { remote } = require('electron')
const attachmentManagement = require('../main/lib/dataApi/attachmentManagement')
const { app } = remote
const path = require('path')
const dialog = remote.dialog
@@ -391,13 +393,11 @@ export default class MarkdownPreview extends React.Component {
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
})
}
this.refs.root.contentWindow.document.body.innerHTML = this.markdown.render(value)
let renderedHTML = this.markdown.render(value)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(renderedHTML, storagePath)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el)
el.href = this.markdown.normalizeLinkText(el.href)
if (!/\/:storage/.test(el.href)) return
el.href = `file:///${this.markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.href)))}`
el.addEventListener('click', this.anchorClickHandler)
})
@@ -409,12 +409,6 @@ export default class MarkdownPreview extends React.Component {
el.addEventListener('click', this.linkClickHandler)
})
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => {
el.src = this.markdown.normalizeLinkText(el.src)
if (!/\/:storage/.test(el.src)) return
el.src = `file:///${this.markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}`
})
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
? codeBlockTheme
: 'default'
@@ -523,6 +517,15 @@ export default class MarkdownPreview extends React.Component {
}
handlelinkClick (e) {
e.preventDefault()
e.stopPropagation()
const href = e.target.href
if (href.match(/^http/i)) {
shell.openExternal(href)
return
}
const noteHash = e.target.href.split('/').pop()
// this will match the new uuid v4 hash and the old hash
// e.g.

View File

@@ -88,7 +88,7 @@ class MarkdownSplitEditor extends React.Component {
}
render () {
const { config, value, storageKey } = this.props
const {config, value, storageKey, noteKey} = this.props
const storage = findStorage(storageKey)
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
@@ -115,6 +115,7 @@ class MarkdownSplitEditor extends React.Component {
scrollPastEnd={config.editor.scrollPastEnd}
fetchUrlTitle={config.editor.fetchUrlTitle}
storageKey={storageKey}
noteKey={noteKey}
onChange={this.handleOnChange.bind(this)}
onScroll={this.handleScroll.bind(this)}
/>

View File

@@ -51,7 +51,7 @@ const SideNavFilter = ({
</button>
<button styleName={isTrashedActive ? 'menu-button-trash--active' : 'menu-button'}
onClick={handleTrashedButtonClick}
onClick={handleTrashedButtonClick} onContextMenu={handleFilterButtonContextMenu}
>
<div styleName='iconWrap'>
<img src={isTrashedActive
@@ -60,7 +60,7 @@ const SideNavFilter = ({
}
/>
</div>
<span onContextMenu={handleFilterButtonContextMenu} styleName='menu-button-label'>{i18n.__('Trash')}</span>
<span styleName='menu-button-label'>{i18n.__('Trash')}</span>
<span styleName='counters'>{counterDelNote}</span>
</button>

View File

@@ -9,16 +9,26 @@ import CSSModules from 'browser/lib/CSSModules'
/**
* @param {string} name
* @param {Function} handleClickTagListItem
* @param {Function} handleClickNarrowToTag
* @param {bool} isActive
* @param {bool} isRelated
*/
const TagListItem = ({name, handleClickTagListItem, isActive, count}) => (
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
<span styleName='tagList-item-name'>
{`# ${name}`}
<span styleName='tagList-item-count'> {count}</span>
</span>
</button>
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, isActive, isRelated, count}) => (
<div styleName='tagList-itemContainer'>
{isRelated
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
<i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} />
</button>
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
}
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
<span styleName='tagList-item-name'>
{`# ${name}`}
<span styleName='tagList-item-count'>{count}</span>
</span>
</button>
</div>
)
TagListItem.propTypes = {

View File

@@ -1,5 +1,9 @@
.tagList-itemContainer
display flex
.tagList-item
display flex
flex 1
width 100%
height 26px
background-color transparent
@@ -20,9 +24,16 @@
color $ui-button-default-color
background-color $ui-button-default--active-backgroundColor
.tagList-itemNarrow
composes tagList-item
flex none
width 20px
padding 0 4px
.tagList-item-active
background-color $ui-button-default--active-backgroundColor
display flex
flex 1
width 100%
height 26px
padding 0
@@ -36,10 +47,16 @@
background-color alpha($ui-button-default--active-backgroundColor, 60%)
transition 0.2s
.tagList-itemNarrow-active
composes tagList-item-active
flex none
width 20px
padding 0 4px
.tagList-item-name
display block
flex 1
padding 0 15px
padding 0 8px 0 4px
height 26px
line-height 26px
border-width 0 0 0 2px
@@ -49,7 +66,10 @@
text-overflow ellipsis
.tagList-item-count
padding 0 3px
float right
line-height 26px
padding-right 15px
font-size 13px
body[data-theme="white"]
.tagList-item

75
browser/lib/Languages.js Normal file
View File

@@ -0,0 +1,75 @@
const languages = [
{
name: 'Albanian',
locale: 'sq'
},
{
name: 'Chinese (zh-CN)',
locale: 'zh-CN'
},
{
name: 'Chinese (zh-TW)',
locale: 'zh-TW'
},
{
name: 'Danish',
locale: 'da'
},
{
name: 'English',
locale: 'en'
},
{
name: 'French',
locale: 'fr'
},
{
name: 'German',
locale: 'de'
},
{
name: 'Hungarian',
locale: 'hu'
},
{
name: 'Japanese',
locale: 'ja'
},
{
name: 'Korean',
locale: 'ko'
},
{
name: 'Norwegian',
locale: 'no'
},
{
name: 'Polish',
locale: 'pl'
},
{
name: 'Portuguese',
locale: 'pt'
},
{
name: 'Russian',
locale: 'ru'
},
{
name: 'Spanish',
locale: 'es-ES'
}
]
module.exports = {
getLocales () {
return languages.reduce(function (localeList, locale) {
localeList.push(locale.locale)
return localeList
}, [])
},
getLanguages () {
return languages
}
}

View File

@@ -1,11 +1,12 @@
const path = require('path')
const { remote } = require('electron')
const { app } = remote
const { getLocales } = require('./Languages.js')
// load package for localization
const i18n = new (require('i18n-2'))({
// setup some locales - other locales default to the first locale
locales: [ 'da', 'de', 'en', 'es-ES', 'fr', 'hu', 'ja', 'ko', 'pl', 'pt-BR', 'pt-PT', 'ru', 'sq', 'zh-CN', 'zh-TW' ],
locales: getLocales(),
extension: '.json',
directory: process.env.NODE_ENV === 'production'
? path.join(app.getAppPath(), './locales')

View File

@@ -5,7 +5,7 @@ import math from '@rokt33r/markdown-it-math'
import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import katex from 'katex'
import {lastFindInArray} from './utils'
import { lastFindInArray } from './utils'
function createGutter (str, firstLineNumber) {
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
@@ -234,10 +234,6 @@ class Markdown {
if (!_.isString(content)) content = ''
return this.md.render(content)
}
normalizeLinkText (linkText) {
return this.md.normalizeLinkText(linkText)
}
}
export default Markdown

View File

@@ -289,6 +289,7 @@ class MarkdownNoteDetail extends React.Component {
config={config}
value={note.content}
storageKey={note.storage}
noteKey={note.key}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
@@ -298,6 +299,7 @@ class MarkdownNoteDetail extends React.Component {
config={config}
value={note.content}
storageKey={note.storage}
noteKey={note.key}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>

View File

@@ -15,6 +15,7 @@ import eventEmitter from 'browser/main/lib/eventEmitter'
import { hashHistory } from 'react-router'
import store from 'browser/main/store'
import i18n from 'browser/lib/i18n'
import { getLocales } from 'browser/lib/Languages'
const path = require('path')
const electron = require('electron')
const { remote } = electron
@@ -152,24 +153,7 @@ class Main extends React.Component {
document.body.setAttribute('data-theme', 'default')
}
const supportedLanguages = [
'sq',
'zh-CN',
'zh-TW',
'da',
'fr',
'de',
'hu',
'ja',
'ko',
'no',
'pl',
'pt',
'ru',
'es-ES'
]
if (supportedLanguages.indexOf(config.ui.language) !== -1) {
if (getLocales().indexOf(config.ui.language) !== -1) {
i18n.setLocale(config.ui.language)
} else {
i18n.setLocale('en')

View File

@@ -282,8 +282,8 @@ class NoteList extends React.Component {
ee.emit('detail:focus')
}
// F or S key
if (e.keyCode === 70 || e.keyCode === 83) {
// L or S key
if (e.keyCode === 76 || e.keyCode === 83) {
e.preventDefault()
ee.emit('top:focus-search')
}
@@ -343,11 +343,10 @@ class NoteList extends React.Component {
}
if (location.pathname.match(/\/tags/)) {
const listOfTags = params.tagname.split(' ')
return data.noteMap.map(note => {
return note
}).filter(note => {
return note.tags.includes(params.tagname)
})
}).filter(note => listOfTags.every(tag => note.tags.includes(tag)))
}
return this.getContextNotes()
@@ -916,7 +915,7 @@ class NoteList extends React.Component {
if (note.isTrashed !== true || location.pathname === '/trashed') return true
})
moment.locale('en', {
moment.updateLocale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',

View File

@@ -145,20 +145,27 @@ class SideNav extends React.Component {
tagListComponent () {
const { data, location, config } = this.props
const relatedTags = this.getRelatedTags(this.getActiveTags(location.pathname), data.noteMap)
let tagList = _.sortBy(data.tagNoteMap.map(
(tag, name) => ({name, size: tag.size})),
['name']
)
(tag, name) => ({ name, size: tag.size, related: relatedTags.has(name) })
), ['name'])
if (config.sortTagsBy === 'COUNTER') {
tagList = _.sortBy(tagList, item => (0 - item.size))
}
if (config.ui.showOnlyRelatedTags && (relatedTags.size > 0)) {
tagList = tagList.filter(
tag => tag.related
)
}
return (
tagList.map(tag => {
return (
<TagListItem
name={tag.name}
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
isActive={this.getTagActive(location.pathname, tag.name)}
isRelated={tag.related}
key={tag.name}
count={tag.size}
/>
@@ -167,10 +174,30 @@ class SideNav extends React.Component {
)
}
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))
)
let relatedTags = new Set()
relatedNotes.forEach(note => note.tags.map(tag => relatedTags.add(tag)))
return relatedTags
}
getTagActive (path, tag) {
return this.getActiveTags(path).includes(tag)
}
getActiveTags (path) {
const pathSegments = path.split('/')
const pathTag = pathSegments[pathSegments.length - 1]
return pathTag === tag
const tags = pathSegments[pathSegments.length - 1]
return (tags === 'alltags')
? []
: tags.split(' ')
}
handleClickTagListItem (name) {
@@ -192,6 +219,19 @@ class SideNav extends React.Component {
})
}
handleClickNarrowToTag (tag) {
const { router } = this.context
const { location } = this.props
let listOfTags = this.getActiveTags(location.pathname)
const indexOfTag = listOfTags.indexOf(tag)
if (indexOfTag > -1) {
listOfTags.splice(indexOfTag, 1)
} else {
listOfTags.push(tag)
}
router.push(`/tags/${listOfTags.join(' ')}`)
}
emptyTrash (entries) {
const { dispatch } = this.props
const deletionPromises = entries.map((note) => {

View File

@@ -0,0 +1,164 @@
const uniqueSlug = require('unique-slug')
const fs = require('fs')
const path = require('path')
const findStorage = require('browser/lib/findStorage')
const mdurl = require('mdurl')
const STORAGE_FOLDER_PLACEHOLDER = ':storage'
const DESTINATION_FOLDER = 'attachments'
/**
* @description
* Copies a copy of an attachment to the storage folder specified by the given key and return the generated attachment name.
* Renames the file to match a unique file name.
*
* @param {String} sourceFilePath The source path of the attachment to be copied
* @param {String} storageKey Storage key of the destination storage
* @param {String} noteKey Key of the current note. Will be used as subfolder in :storage
* @param {boolean} useRandomName determines whether a random filename for the new file is used. If false the source file name is used
* @return {Promise<String>} name (inclusive extension) of the generated file
*/
function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = true) {
return new Promise((resolve, reject) => {
if (!sourceFilePath) {
reject('sourceFilePath has to be given')
}
if (!storageKey) {
reject('storageKey has to be given')
}
if (!noteKey) {
reject('noteKey has to be given')
}
try {
if (!fs.existsSync(sourceFilePath)) {
reject('source file does not exist')
}
const targetStorage = findStorage.findStorage(storageKey)
const inputFile = fs.createReadStream(sourceFilePath)
let destinationName
if (useRandomName) {
destinationName = `${uniqueSlug()}${path.extname(sourceFilePath)}`
} else {
destinationName = path.basename(sourceFilePath)
}
const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
createAttachmentDestinationFolder(targetStorage.path, noteKey)
const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName))
inputFile.pipe(outputFile)
resolve(destinationName)
} catch (e) {
return reject(e)
}
})
}
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)
if (!fs.existsSync(destinationDir)) {
fs.mkdirSync(destinationDir)
}
}
/**
* @description Fixes the URLs embedded in the generated HTML so that they again refer actual local files.
* @param {String} renderedHTML HTML in that the links should be fixed
* @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) {
return renderedHTML.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
}
/**
* @description Generates the markdown code for a given attachment
* @param {String} fileName Name of the attachment
* @param {String} path Path of the attachment
* @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) {
return `${showPreview ? '!' : ''}[${fileName}](${path})`
}
/**
* @description Handles the drop-event of a file. Includes the necessary markdown code and copies the file to the corresponding storage folder.
* The method calls {CodeEditor#insertAttachmentMd()} to include the generated markdown at the needed place!
* @param {CodeEditor} codeEditor Markdown editor. Its insertAttachmentMd() method will be called to include the markdown code
* @param {String} storageKey Key of the current storage
* @param {String} noteKey Key of the current note
* @param {Event} dropEvent DropEvent
*/
function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) {
const file = dropEvent.dataTransfer.files[0]
const filePath = file.path
const originalFileName = path.basename(filePath)
const fileType = file['type']
copyAttachment(filePath, storageKey, noteKey).then((fileName) => {
let showPreview = fileType.startsWith('image')
let imageMd = generateAttachmentMarkdown(originalFileName, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName), showPreview)
codeEditor.insertAttachmentMd(imageMd)
})
}
/**
* @description Creates a new file in the storage folder belonging to the current note and inserts the correct markdown code
* @param {CodeEditor} codeEditor Markdown editor. Its insertAttachmentMd() method will be called to include the markdown code
* @param {String} storageKey Key of the current storage
* @param {String} noteKey Key of the current note
* @param {DataTransferItem} dataTransferItem Part of the past-event
*/
function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) {
if (!codeEditor) {
throw new Error('codeEditor has to be given')
}
if (!storageKey) {
throw new Error('storageKey has to be given')
}
if (!noteKey) {
throw new Error('noteKey has to be given')
}
if (!dataTransferItem) {
throw new Error('dataTransferItem has to be given')
}
const blob = dataTransferItem.getAsFile()
const reader = new FileReader()
let base64data
const targetStorage = findStorage.findStorage(storageKey)
const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
createAttachmentDestinationFolder(targetStorage.path, noteKey)
let imageName = `${uniqueSlug()}.png`
const imagePath = path.join(destinationDir, imageName)
reader.onloadend = function () {
base64data = reader.result.replace(/^data:image\/png;base64,/, '')
base64data += base64data.replace('+', ' ')
const binaryData = new Buffer(base64data, 'base64').toString('binary')
fs.writeFile(imagePath, binaryData, 'binary')
let imageMd = generateAttachmentMarkdown(imageName, imagePath, true)
codeEditor.insertAttachmentMd(imageMd)
}
reader.readAsDataURL(blob)
}
module.exports = {
copyAttachment,
fixLocalURLS,
generateAttachmentMarkdown,
handleAttachmentDrop,
handlePastImageEvent,
STORAGE_FOLDER_PLACEHOLDER,
DESTINATION_FOLDER
}

View File

@@ -2,6 +2,8 @@ const fs = require('fs')
const path = require('path')
const { findStorage } = require('browser/lib/findStorage')
// TODO: ehhc: delete this
/**
* @description Copy an image and return the path.
* @param {String} filePath
@@ -21,8 +23,12 @@ function copyImage (filePath, storageKey, rename = true) {
const imageDir = path.join(targetStorage.path, 'images')
if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir)
const outputImage = fs.createWriteStream(path.join(imageDir, basename))
outputImage.on('error', reject)
inputImage.on('error', reject)
inputImage.on('end', () => {
resolve(basename)
})
inputImage.pipe(outputImage)
resolve(basename)
} catch (e) {
return reject(e)
}

View File

@@ -1,11 +1,12 @@
import copyFile from 'browser/main/lib/dataApi/copyFile'
import {findStorage} from 'browser/lib/findStorage'
import { findStorage } from 'browser/lib/findStorage'
import filenamify from 'filenamify'
const fs = require('fs')
const path = require('path')
const LOCAL_STORED_REGEX = /!\[(.*?)]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
// TODO: ehhc: check this -> attachmentManagement
const IMAGES_FOLDER_NAME = 'images'
/**

View File

@@ -68,6 +68,8 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
return noteData
})
.then(function moveImages (noteData) {
if (oldStorage.path === newStorage.path) return noteData
const searchImagesRegex = /!\[.*?]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
let match = searchImagesRegex.exec(noteData.content)
@@ -75,6 +77,7 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
while (match != null) {
const [, filename] = match
const oldPath = path.join(oldStorage.path, 'images', filename)
// TODO: ehhc: attachmentManagement
moveTasks.push(
copyImage(oldPath, noteData.storage, false)
.then(() => {

View File

@@ -10,6 +10,7 @@ import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
import { getLanguages } from 'browser/lib/Languages'
const OSX = global.process.platform === 'darwin'
@@ -65,6 +66,7 @@ class UiTab extends React.Component {
language: this.refs.uiLanguage.value,
showCopyNotification: this.refs.showCopyNotification.checked,
confirmDeletion: this.refs.confirmDeletion.checked,
showOnlyRelatedTags: this.refs.showOnlyRelatedTags.checked,
disableDirectWrite: this.refs.uiD2w != null
? this.refs.uiD2w.checked
: false
@@ -182,22 +184,9 @@ class UiTab extends React.Component {
onChange={(e) => this.handleUIChange(e)}
ref='uiLanguage'
>
<option value='sq'>{i18n.__('Albanian')}</option>
<option value='zh-CN'>{i18n.__('Chinese (zh-CN)')}</option>
<option value='zh-TW'>{i18n.__('Chinese (zh-TW)')}</option>
<option value='da'>{i18n.__('Danish')}</option>
<option value='en'>{i18n.__('English')}</option>
<option value='fr'>{i18n.__('French')}</option>
<option value='de'>{i18n.__('German')}</option>
<option value='hu'>{i18n.__('Hungarian')}</option>
<option value='ja'>{i18n.__('Japanese')}</option>
<option value='ko'>{i18n.__('Korean')}</option>
<option value='no'>{i18n.__('Norwegian')}</option>
<option value='pl'>{i18n.__('Polish')}</option>
<option value='pt-BR'>{i18n.__('Portuguese (Brazil)')}</option>
<option value='pt-PT'>{i18n.__('Portuguese (Portugal)')}</option>
<option value='ru'>{i18n.__('Russian')}</option>
<option value='es-ES'>{i18n.__('Spanish')}</option>
{
getLanguages().map((language) => <option value={language.locale} key={language.locale}>{i18n.__(language.name)}</option>)
}
</select>
</div>
</div>
@@ -222,6 +211,16 @@ class UiTab extends React.Component {
{i18n.__('Show a confirmation dialog when deleting notes')}
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.showOnlyRelatedTags}
ref='showOnlyRelatedTags'
type='checkbox'
/>&nbsp;
{i18n.__('Show only related tags')}
</label>
</div>
{
global.process.platform === 'win32'
? <div styleName='group-checkBoxSection'>