Skip to content

Commit

Permalink
Merge pull request #3099 from AWolf81/html-to-md
Browse files Browse the repository at this point in the history
Html to md feature
  • Loading branch information
Rokt33r authored Sep 2, 2019
2 parents 1332b33 + ec47ee8 commit 68b3077
Show file tree
Hide file tree
Showing 15 changed files with 590 additions and 12 deletions.
4 changes: 2 additions & 2 deletions browser/components/CodeEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ 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
import TurndownService from 'turndown'
import { createTurndownService } from '../lib/turndown'
import {languageMaps} from '../lib/CMLanguageList'
import snippetManager from '../lib/SnippetManager'
import {generateInEditor, tocExistsInEditor} from 'browser/lib/markdown-toc-generator'
Expand Down Expand Up @@ -101,7 +101,7 @@ export default class CodeEditor extends React.Component {

this.editorActivityHandler = () => this.handleEditorActivity()

this.turndownService = new TurndownService()
this.turndownService = createTurndownService()
}

handleSearch (msg) {
Expand Down
9 changes: 9 additions & 0 deletions browser/lib/turndown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const TurndownService = require('turndown')
const { gfm } = require('turndown-plugin-gfm')

export const createTurndownService = function () {
const turndown = new TurndownService()
turndown.use(gfm)
turndown.remove('script')
return turndown
}
69 changes: 69 additions & 0 deletions browser/main/Detail/FromUrlButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './FromUrlButton.styl'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'

class FromUrlButton extends React.Component {
constructor (props) {
super(props)

this.state = {
isActive: false
}
}

handleMouseDown (e) {
this.setState({
isActive: true
})
}

handleMouseUp (e) {
this.setState({
isActive: false
})
}

handleMouseLeave (e) {
this.setState({
isActive: false
})
}

render () {
const { className } = this.props

return (
<button className={_.isString(className)
? 'FromUrlButton ' + className
: 'FromUrlButton'
}
styleName={this.state.isActive || this.props.isActive
? 'root--active'
: 'root'
}
onMouseDown={(e) => this.handleMouseDown(e)}
onMouseUp={(e) => this.handleMouseUp(e)}
onMouseLeave={(e) => this.handleMouseLeave(e)}
onClick={this.props.onClick}>
<img styleName='icon'
src={this.state.isActive || this.props.isActive
? '../resources/icon/icon-external.svg'
: '../resources/icon/icon-external.svg'
}
/>
<span styleName='tooltip'>{i18n.__('Convert URL to Markdown')}</span>
</button>
)
}
}

FromUrlButton.propTypes = {
isActive: PropTypes.bool,
onClick: PropTypes.func,
className: PropTypes.string
}

export default CSSModules(FromUrlButton, styles)
41 changes: 41 additions & 0 deletions browser/main/Detail/FromUrlButton.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.root
top 45px
topBarButtonRight()
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
&:hover .tooltip
opacity 1

.tooltip
tooltip()
position absolute
pointer-events none
top 50px
right 125px
width 90px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s

.root--active
@extend .root
transition 0.15s
color $ui-favorite-star-button-color
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)

.icon
transition transform 0.15s
height 13px

body[data-theme="dark"]
.root
topBarButtonDark()
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
1 change: 1 addition & 0 deletions browser/main/Detail/MarkdownNoteDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ class MarkdownNoteDetail extends React.Component {
</div>
<div styleName='info-right'>
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />

<StarButton
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
Expand Down
2 changes: 1 addition & 1 deletion browser/main/Detail/StarButton.styl
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ body[data-theme="dark"]
topBarButtonDark()
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
color alpha($ui-favorite-star-button-color, 0.6)
79 changes: 79 additions & 0 deletions browser/main/lib/dataApi/createNoteFromUrl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const http = require('http')
const https = require('https')
const { createTurndownService } = require('../../../lib/turndown')
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)) {
return true
} else {
return false
}
}

function createNoteFromUrl (url, storage, folder, dispatch = null, location = null) {
return new Promise((resolve, reject) => {
const td = createTurndownService()

if (!validateUrl(url)) {
reject({result: false, error: 'Please check your URL is in correct format. (Example, https://www.google.com)'})
}

const request = url.startsWith('https') ? https : http

const req = request.request(url, (res) => {
let data = ''

res.on('data', (chunk) => {
data += chunk
})

res.on('end', () => {
const markdownHTML = td.turndown(data)

if (dispatch !== null) {
createNote(storage, {
type: 'MARKDOWN_NOTE',
folder: folder,
title: '',
content: markdownHTML
})
.then((note) => {
const noteHash = note.key
dispatch({
type: 'UPDATE_NOTE',
note: note
})
dispatch(push({
pathname: location.pathname,
query: {key: noteHash}
}))
ee.emit('list:jump', noteHash)
ee.emit('detail:focus')
resolve({result: true, error: null})
})
} else {
createNote(storage, {
type: 'MARKDOWN_NOTE',
folder: folder,
title: '',
content: markdownHTML
}).then((note) => {
resolve({result: true, note, error: null})
})
}
})
})

req.on('error', (e) => {
console.error('error in parsing URL', e)
reject({result: false, error: e})
})
req.end()
})
}

module.exports = createNoteFromUrl
1 change: 1 addition & 0 deletions browser/main/lib/dataApi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const dataApi = {
exportFolder: require('./exportFolder'),
exportStorage: require('./exportStorage'),
createNote: require('./createNote'),
createNoteFromUrl: require('./createNoteFromUrl'),
updateNote: require('./updateNote'),
deleteNote: require('./deleteNote'),
moveNote: require('./moveNote'),
Expand Down
118 changes: 118 additions & 0 deletions browser/main/modals/CreateMarkdownFromURLModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './CreateMarkdownFromURLModal.styl'
import dataApi from 'browser/main/lib/dataApi'
import ModalEscButton from 'browser/components/ModalEscButton'
import i18n from 'browser/lib/i18n'

class CreateMarkdownFromURLModal extends React.Component {
constructor (props) {
super(props)

this.state = {
name: '',
showerror: false,
errormessage: ''
}
}

componentDidMount () {
this.refs.name.focus()
this.refs.name.select()
}

handleCloseButtonClick (e) {
this.props.close()
}

handleChange (e) {
this.setState({
name: this.refs.name.value
})
}

handleKeyDown (e) {
if (e.keyCode === 27) {
this.props.close()
}
}

handleInputKeyDown (e) {
switch (e.keyCode) {
case 13:
this.confirm()
}
}

handleConfirmButtonClick (e) {
this.confirm()
}

showError (message) {
this.setState({
showerror: true,
errormessage: message
})
}

hideError () {
this.setState({
showerror: false,
errormessage: ''
})
}

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)
})
}

render () {
return (
<div styleName='root'
tabIndex='-1'
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='header'>
<div styleName='title'>{i18n.__('Import Markdown From URL')}</div>
</div>
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
<div styleName='control'>
<div styleName='control-folder'>
<div styleName='control-folder-label'>{i18n.__('Insert URL Here')}</div>
<input styleName='control-folder-input'
ref='name'
value={this.state.name}
onChange={(e) => this.handleChange(e)}
onKeyDown={(e) => this.handleInputKeyDown(e)}
/>
</div>
<button styleName='control-confirmButton'
onClick={(e) => this.handleConfirmButtonClick(e)}
>
{i18n.__('Import')}
</button>
<div className='error' styleName='error'>{this.state.errormessage}</div>
</div>
</div>
)
}
}

CreateMarkdownFromURLModal.propTypes = {
storage: PropTypes.string,
folder: PropTypes.string,
dispatch: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string
})
}

export default CSSModules(CreateMarkdownFromURLModal, styles)
Loading

0 comments on commit 68b3077

Please sign in to comment.