Skip to content

Commit d97e62f

Browse files
committed
Import note from url with markdown
1 parent 0d296c3 commit d97e62f

11 files changed

+17990
-21
lines changed

browser/main/Detail/FromUrlButton.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import PropTypes from 'prop-types'
2+
import React from 'react'
3+
import CSSModules from 'browser/lib/CSSModules'
4+
import styles from './FromUrlButton.styl'
5+
import _ from 'lodash'
6+
import i18n from 'browser/lib/i18n'
7+
8+
class FromUrlButton extends React.Component {
9+
constructor (props) {
10+
super(props)
11+
12+
this.state = {
13+
isActive: false
14+
}
15+
}
16+
17+
handleMouseDown (e) {
18+
this.setState({
19+
isActive: true
20+
})
21+
}
22+
23+
handleMouseUp (e) {
24+
this.setState({
25+
isActive: false
26+
})
27+
}
28+
29+
handleMouseLeave (e) {
30+
this.setState({
31+
isActive: false
32+
})
33+
}
34+
35+
render () {
36+
const { className } = this.props
37+
38+
return (
39+
<button className={_.isString(className)
40+
? 'FromUrlButton ' + className
41+
: 'FromUrlButton'
42+
}
43+
styleName={this.state.isActive || this.props.isActive
44+
? 'root--active'
45+
: 'root'
46+
}
47+
onMouseDown={(e) => this.handleMouseDown(e)}
48+
onMouseUp={(e) => this.handleMouseUp(e)}
49+
onMouseLeave={(e) => this.handleMouseLeave(e)}
50+
onClick={this.props.onClick}>
51+
<img styleName='icon'
52+
src={this.state.isActive || this.props.isActive
53+
? '../resources/icon/icon-external.svg'
54+
: '../resources/icon/icon-external.svg'
55+
}
56+
/>
57+
<span styleName='tooltip'>{i18n.__('Convert URL to Markdown')}</span>
58+
</button>
59+
)
60+
}
61+
}
62+
63+
FromUrlButton.propTypes = {
64+
isActive: PropTypes.bool,
65+
onClick: PropTypes.func,
66+
className: PropTypes.string
67+
}
68+
69+
export default CSSModules(FromUrlButton, styles)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
.root
2+
top 45px
3+
topBarButtonRight()
4+
&:hover
5+
transition 0.2s
6+
color alpha($ui-favorite-star-button-color, 0.6)
7+
&:hover .tooltip
8+
opacity 1
9+
10+
.tooltip
11+
tooltip()
12+
position absolute
13+
pointer-events none
14+
top 50px
15+
right 125px
16+
width 90px
17+
z-index 200
18+
padding 5px
19+
line-height normal
20+
border-radius 2px
21+
opacity 0
22+
transition 0.1s
23+
24+
.root--active
25+
@extend .root
26+
transition 0.15s
27+
color $ui-favorite-star-button-color
28+
&:hover
29+
transition 0.2s
30+
color alpha($ui-favorite-star-button-color, 0.6)
31+
32+
.icon
33+
transition transform 0.15s
34+
height 13px
35+
36+
body[data-theme="dark"]
37+
.root
38+
topBarButtonDark()
39+
&:hover
40+
transition 0.2s
41+
color alpha($ui-favorite-star-button-color, 0.6)

browser/main/Detail/MarkdownNoteDetail.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ class MarkdownNoteDetail extends React.Component {
368368
</div>
369369
<div styleName='info-right'>
370370
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />
371+
371372
<StarButton
372373
onClick={(e) => this.handleStarButtonClick(e)}
373374
isActive={note.isStarred}

browser/main/Detail/StarButton.styl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ body[data-theme="dark"]
3737
topBarButtonDark()
3838
&:hover
3939
transition 0.2s
40-
color alpha($ui-favorite-star-button-color, 0.6)
40+
color alpha($ui-favorite-star-button-color, 0.6)
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import PropTypes from 'prop-types'
2+
import React from 'react'
3+
import CSSModules from 'browser/lib/CSSModules'
4+
import styles from './CreateMarkdownFromURLModal.styl'
5+
import dataApi from 'browser/main/lib/dataApi'
6+
import store from 'browser/main/store'
7+
import consts from 'browser/lib/consts'
8+
import ModalEscButton from 'browser/components/ModalEscButton'
9+
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
10+
import i18n from 'browser/lib/i18n'
11+
const http = require('http')
12+
const https = require('https')
13+
const TurndownService = require('turndown')
14+
import { hashHistory } from 'react-router'
15+
import ee from 'browser/main/lib/eventEmitter'
16+
17+
class CreateMarkdownFromURLModal extends React.Component {
18+
constructor (props) {
19+
super(props)
20+
let td = new TurndownService();
21+
22+
this.state = {
23+
name: '',
24+
showerror: false,
25+
errormessage: '',
26+
turndownService: td
27+
}
28+
29+
}
30+
31+
componentDidMount () {
32+
this.refs.name.focus()
33+
this.refs.name.select()
34+
}
35+
36+
handleCloseButtonClick (e) {
37+
this.props.close()
38+
}
39+
40+
handleChange (e) {
41+
this.setState({
42+
name: this.refs.name.value
43+
})
44+
}
45+
46+
handleKeyDown (e) {
47+
if (e.keyCode === 27) {
48+
this.props.close()
49+
}
50+
}
51+
52+
handleInputKeyDown (e) {
53+
switch (e.keyCode) {
54+
case 13:
55+
this.confirm()
56+
}
57+
}
58+
59+
handleConfirmButtonClick (e) {
60+
this.confirm()
61+
}
62+
63+
showError(message) {
64+
this.setState({
65+
showerror: true,
66+
errormessage: message
67+
});
68+
}
69+
70+
hideError() {
71+
this.setState({
72+
showerror: false,
73+
errormessage: ''
74+
})
75+
}
76+
77+
validateUrl(str) {
78+
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)) {
79+
return true;
80+
} else {
81+
return false;
82+
}
83+
}
84+
85+
confirm () {
86+
if(this.validateUrl(this.state.name)) {
87+
this.hideError()
88+
let url = this.state.name;
89+
let request = http;
90+
if(url.includes('https'))
91+
request = https;
92+
let req = request.request(url, (res) => {
93+
let data = '';
94+
res.on('data', (chunk) => {
95+
data += chunk
96+
})
97+
res.on('end', () => {
98+
console.log("receiving data", data);
99+
100+
let html = document.createElement('html');
101+
html.innerHTML = data;
102+
103+
let scripts = html.getElementsByTagName('script');
104+
for(let i = scripts.length - 1; i >= 0; i--) {
105+
scripts[i].parentNode.removeChild(scripts[i]);
106+
}
107+
108+
let body = html.getElementsByTagName('body')[0].innerHTML;
109+
let markdownHTML = this.state.turndownService.turndown(body);
110+
console.log('markdown', markdownHTML);
111+
html.innerHTML = '';
112+
const { storage, folder, dispatch, location } = this.props
113+
114+
dataApi
115+
.createNote(storage, {
116+
type: 'MARKDOWN_NOTE',
117+
folder: folder,
118+
title: '',
119+
content: markdownHTML
120+
})
121+
.then((note) => {
122+
const noteHash = note.key
123+
dispatch({
124+
type: 'UPDATE_NOTE',
125+
note: note
126+
})
127+
hashHistory.push({
128+
pathname: location.pathname,
129+
query: {key: noteHash}
130+
})
131+
ee.emit('list:jump', noteHash)
132+
ee.emit('detail:focus')
133+
this.props.close()
134+
})
135+
})
136+
});
137+
req.on('error', (e) => {
138+
console.log("Error in request", e.message);
139+
});
140+
req.end();
141+
} else {
142+
this.showError("Please check your URL is in correct format. (Example, 'https://google.com')")
143+
}
144+
}
145+
146+
render () {
147+
return (
148+
<div styleName='root'
149+
tabIndex='-1'
150+
onKeyDown={(e) => this.handleKeyDown(e)}
151+
>
152+
<div styleName='header'>
153+
<div styleName='title'>{i18n.__('Import Markdown From URL')}</div>
154+
</div>
155+
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
156+
<div styleName='control'>
157+
<div styleName='control-folder'>
158+
<div styleName='control-folder-label'>{i18n.__('Insert URL Here')}</div>
159+
<input styleName='control-folder-input'
160+
ref='name'
161+
value={this.state.name}
162+
onChange={(e) => this.handleChange(e)}
163+
onKeyDown={(e) => this.handleInputKeyDown(e)}
164+
/>
165+
</div>
166+
<button styleName='control-confirmButton'
167+
onClick={(e) => this.handleConfirmButtonClick(e)}
168+
>
169+
{i18n.__('Import')}
170+
</button>
171+
<div className="error" styleName="error">{this.state.errormessage}</div>
172+
</div>
173+
</div>
174+
)
175+
}
176+
}
177+
178+
CreateMarkdownFromURLModal.propTypes = {
179+
storage: PropTypes.shape({
180+
key: PropTypes.string
181+
})
182+
}
183+
184+
export default CSSModules(CreateMarkdownFromURLModal, styles)

0 commit comments

Comments
 (0)