Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit 4c99429

Browse files
committed
Merge branch 'master' into fix-title-decoding
2 parents 6439810 + 96fbd75 commit 4c99429

20 files changed

Lines changed: 585 additions & 107 deletions

File tree

browser/components/CodeEditor.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { findStorage } from 'browser/lib/findStorage'
99
import fs from 'fs'
1010
import eventEmitter from 'browser/main/lib/eventEmitter'
1111
import iconv from 'iconv-lite'
12+
const { ipcRenderer } = require('electron')
1213

1314
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
1415

@@ -33,8 +34,13 @@ export default class CodeEditor extends React.Component {
3334
constructor (props) {
3435
super(props)
3536

37+
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
3638
this.changeHandler = (e) => this.handleChange(e)
39+
this.focusHandler = () => {
40+
ipcRenderer.send('editor:focused', true)
41+
}
3742
this.blurHandler = (editor, e) => {
43+
ipcRenderer.send('editor:focused', false)
3844
if (e == null) return null
3945
let el = e.relatedTarget
4046
while (el != null) {
@@ -82,7 +88,6 @@ export default class CodeEditor extends React.Component {
8288
}
8389
}
8490
})
85-
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
8691
}
8792

8893
componentDidMount () {
@@ -140,6 +145,7 @@ export default class CodeEditor extends React.Component {
140145

141146
this.setMode(this.props.mode)
142147

148+
this.editor.on('focus', this.focusHandler)
143149
this.editor.on('blur', this.blurHandler)
144150
this.editor.on('change', this.changeHandler)
145151
this.editor.on('paste', this.pasteHandler)
@@ -163,6 +169,7 @@ export default class CodeEditor extends React.Component {
163169
}
164170

165171
componentWillUnmount () {
172+
this.editor.off('focus', this.focusHandler)
166173
this.editor.off('blur', this.blurHandler)
167174
this.editor.off('change', this.changeHandler)
168175
this.editor.off('paste', this.pasteHandler)

browser/components/NoteItem.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ NoteItem.propTypes = {
123123
title: PropTypes.string.isrequired,
124124
tags: PropTypes.array,
125125
isStarred: PropTypes.bool.isRequired,
126-
isTrashed: PropTypes.bool.isRequired
126+
isTrashed: PropTypes.bool.isRequired,
127+
blog: {
128+
blogLink: PropTypes.string,
129+
blogId: PropTypes.number
130+
}
127131
}),
128132
handleNoteClick: PropTypes.func.isRequired,
129133
handleNoteContextMenu: PropTypes.func.isRequired,

browser/main/Detail/SnippetNoteDetail.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ class SnippetNoteDetail extends React.Component {
343343

344344
handleKeyDown (e) {
345345
switch (e.keyCode) {
346+
// tab key
346347
case 9:
347348
if (e.ctrlKey && !e.shiftKey) {
348349
e.preventDefault()
@@ -355,6 +356,7 @@ class SnippetNoteDetail extends React.Component {
355356
this.focusEditor()
356357
}
357358
break
359+
// L key
358360
case 76:
359361
{
360362
const isSuper = global.process.platform === 'darwin'
@@ -366,6 +368,7 @@ class SnippetNoteDetail extends React.Component {
366368
}
367369
}
368370
break
371+
// T key
369372
case 84:
370373
{
371374
const isSuper = global.process.platform === 'darwin'

browser/main/NoteList/index.js

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ import searchFromNotes from 'browser/lib/search'
1313
import fs from 'fs'
1414
import path from 'path'
1515
import { hashHistory } from 'react-router'
16+
import copy from 'copy-to-clipboard'
1617
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
18+
import markdown from '../../lib/markdown'
1719

1820
const { remote } = require('electron')
1921
const { Menu, MenuItem, dialog } = remote
22+
const WP_POST_PATH = '/wp/v2/posts'
2023

2124
function sortByCreatedAt (a, b) {
2225
return new Date(b.createdAt) - new Date(a.createdAt)
@@ -70,6 +73,7 @@ class NoteList extends React.Component {
7073
this.getNoteFolder = this.getNoteFolder.bind(this)
7174
this.getViewType = this.getViewType.bind(this)
7275
this.restoreNote = this.restoreNote.bind(this)
76+
this.copyNoteLink = this.copyNoteLink.bind(this)
7377

7478
// TODO: not Selected noteKeys but SelectedNote(for reusing)
7579
this.state = {
@@ -257,27 +261,38 @@ class NoteList extends React.Component {
257261
handleNoteListKeyDown (e) {
258262
if (e.metaKey || e.ctrlKey) return true
259263

264+
// A key
260265
if (e.keyCode === 65 && !e.shiftKey) {
261266
e.preventDefault()
262267
ee.emit('top:new-note')
263268
}
264269

270+
// D key
265271
if (e.keyCode === 68) {
266272
e.preventDefault()
267273
this.deleteNote()
268274
}
269275

276+
// E key
270277
if (e.keyCode === 69) {
271278
e.preventDefault()
272279
ee.emit('detail:focus')
273280
}
274281

275-
if (e.keyCode === 38) {
282+
// F or S key
283+
if (e.keyCode === 70 || e.keyCode === 83) {
284+
e.preventDefault()
285+
ee.emit('top:focus-search')
286+
}
287+
288+
// UP or K key
289+
if (e.keyCode === 38 || e.keyCode === 75) {
276290
e.preventDefault()
277291
this.selectPriorNote()
278292
}
279293

280-
if (e.keyCode === 40) {
294+
// DOWN or J key
295+
if (e.keyCode === 40 || e.keyCode === 74) {
281296
e.preventDefault()
282297
this.selectNextNote()
283298
}
@@ -458,6 +473,10 @@ class NoteList extends React.Component {
458473
const deleteLabel = 'Delete Note'
459474
const cloneNote = 'Clone Note'
460475
const restoreNote = 'Restore Note'
476+
const copyNoteLink = 'Copy Note Link'
477+
const publishLabel = 'Publish Blog'
478+
const updateLabel = 'Update Blog'
479+
const openBlogLabel = 'Open Blog'
461480

462481
const menu = new Menu()
463482
if (!location.pathname.match(/\/starred|\/trash/)) {
@@ -482,6 +501,28 @@ class NoteList extends React.Component {
482501
label: cloneNote,
483502
click: this.cloneNote.bind(this)
484503
}))
504+
menu.append(new MenuItem({
505+
label: copyNoteLink,
506+
click: this.copyNoteLink(note)
507+
}))
508+
if (note.type === 'MARKDOWN_NOTE') {
509+
if (note.blog && note.blog.blogLink && note.blog.blogId) {
510+
menu.append(new MenuItem({
511+
label: updateLabel,
512+
click: this.publishMarkdown.bind(this)
513+
}))
514+
menu.append(new MenuItem({
515+
label: openBlogLabel,
516+
click: () => this.openBlog.bind(this)(note)
517+
}))
518+
} else {
519+
menu.append(new MenuItem({
520+
label: publishLabel,
521+
click: this.publishMarkdown.bind(this)
522+
}))
523+
}
524+
}
525+
485526
menu.popup()
486527
}
487528

@@ -630,6 +671,117 @@ class NoteList extends React.Component {
630671
})
631672
}
632673

674+
copyNoteLink (note) {
675+
const noteLink = `[${note.title}](${note.storage}-${note.key})`
676+
return copy(noteLink)
677+
}
678+
679+
save (note) {
680+
const { dispatch } = this.props
681+
dataApi
682+
.updateNote(note.storage, note.key, note)
683+
.then((note) => {
684+
dispatch({
685+
type: 'UPDATE_NOTE',
686+
note: note
687+
})
688+
})
689+
}
690+
691+
publishMarkdown () {
692+
if (this.pendingPublish) {
693+
clearTimeout(this.pendingPublish)
694+
}
695+
this.pendingPublish = setTimeout(() => {
696+
this.publishMarkdownNow()
697+
}, 1000)
698+
}
699+
700+
publishMarkdownNow () {
701+
const {selectedNoteKeys} = this.state
702+
const notes = this.notes.map((note) => Object.assign({}, note))
703+
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
704+
const firstNote = selectedNotes[0]
705+
const config = ConfigManager.get()
706+
const {address, token, authMethod, username, password} = config.blog
707+
let authToken = ''
708+
if (authMethod === 'USER') {
709+
authToken = `Basic ${window.btoa(`${username}:${password}`)}`
710+
} else {
711+
authToken = `Bearer ${token}`
712+
}
713+
const contentToRender = firstNote.content.replace(`# ${firstNote.title}`, '')
714+
var data = {
715+
title: firstNote.title,
716+
content: markdown.render(contentToRender),
717+
status: 'publish'
718+
}
719+
720+
let url = ''
721+
let method = ''
722+
if (firstNote.blog && firstNote.blog.blogId) {
723+
url = `${address}${WP_POST_PATH}/${firstNote.blog.blogId}`
724+
method = 'PUT'
725+
} else {
726+
url = `${address}${WP_POST_PATH}`
727+
method = 'POST'
728+
}
729+
// eslint-disable-next-line no-undef
730+
fetch(url, {
731+
method: method,
732+
body: JSON.stringify(data),
733+
headers: {
734+
'Authorization': authToken,
735+
'Content-Type': 'application/json'
736+
}
737+
}).then(res => res.json())
738+
.then(response => {
739+
if (_.isNil(response.link) || _.isNil(response.id)) {
740+
return Promise.reject()
741+
}
742+
firstNote.blog = {
743+
blogLink: response.link,
744+
blogId: response.id
745+
}
746+
this.save(firstNote)
747+
this.confirmPublish(firstNote)
748+
})
749+
.catch((error) => {
750+
console.error(error)
751+
this.confirmPublishError()
752+
})
753+
}
754+
755+
confirmPublishError () {
756+
const { remote } = electron
757+
const { dialog } = remote
758+
const alertError = {
759+
type: 'warning',
760+
message: 'Publish Failed',
761+
detail: 'Check and update your blog setting and try again.',
762+
buttons: ['Confirm']
763+
}
764+
dialog.showMessageBox(remote.getCurrentWindow(), alertError)
765+
}
766+
767+
confirmPublish (note) {
768+
const buttonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
769+
type: 'warning',
770+
message: 'Publish Succeeded',
771+
detail: `${note.title} is published at ${note.blog.blogLink}`,
772+
buttons: ['Confirm', 'Open Blog']
773+
})
774+
775+
if (buttonIndex === 1) {
776+
this.openBlog(note)
777+
}
778+
}
779+
780+
openBlog (note) {
781+
const { shell } = electron
782+
shell.openExternal(note.blog.blogLink)
783+
}
784+
633785
importFromFile () {
634786
const options = {
635787
filters: [

browser/main/SideNav/StorageItem.js

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -191,33 +191,16 @@ class StorageItem extends React.Component {
191191
dropNote (storage, folder, dispatch, location, noteData) {
192192
noteData = noteData.filter((note) => folder.key !== note.folder)
193193
if (noteData.length === 0) return
194-
const newNoteData = noteData.map((note) => Object.assign({}, note, {storage: storage, folder: folder.key}))
195194

196195
Promise.all(
197-
newNoteData.map((note) => dataApi.createNote(storage.key, note))
196+
noteData.map((note) => dataApi.moveNote(note.storage, note.key, storage.key, folder.key))
198197
)
199198
.then((createdNoteData) => {
200-
createdNoteData.forEach((note) => {
199+
createdNoteData.forEach((newNote) => {
201200
dispatch({
202-
type: 'UPDATE_NOTE',
203-
note: note
204-
})
205-
})
206-
})
207-
.catch((err) => {
208-
console.error(`error on create notes: ${err}`)
209-
})
210-
.then(() => {
211-
return Promise.all(
212-
noteData.map((note) => dataApi.deleteNote(note.storage, note.key))
213-
)
214-
})
215-
.then((deletedNoteData) => {
216-
deletedNoteData.forEach((note) => {
217-
dispatch({
218-
type: 'DELETE_NOTE',
219-
storageKey: note.storageKey,
220-
noteKey: note.noteKey
201+
type: 'MOVE_NOTE',
202+
originNote: noteData.find((note) => note.content === newNote.content),
203+
note: newNote
221204
})
222205
})
223206
})

browser/main/StatusBar/StatusBar.styl

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,19 @@
2121
color white
2222

2323
.zoom
24-
display none
25-
// navButtonColor()
26-
// color rgba(0,0,0,.54)
27-
// height 20px
28-
// display flex
29-
// padding 0
30-
// align-items center
31-
// background-color transparent
32-
// &:hover
33-
// color $ui-active-color
34-
// &:active
35-
// color $ui-active-color
36-
// span
37-
// margin-left 5px
24+
navButtonColor()
25+
color rgba(0,0,0,.54)
26+
height 20px
27+
display flex
28+
padding 0
29+
align-items center
30+
background-color transparent
31+
&:hover
32+
color $ui-active-color
33+
&:active
34+
color $ui-active-color
35+
span
36+
margin-left 5px
3837

3938
.update
4039
navButtonColor()

0 commit comments

Comments
 (0)