@@ -13,10 +13,13 @@ import searchFromNotes from 'browser/lib/search'
1313import fs from 'fs'
1414import path from 'path'
1515import { hashHistory } from 'react-router'
16+ import copy from 'copy-to-clipboard'
1617import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
18+ import markdown from '../../lib/markdown'
1719
1820const { remote } = require ( 'electron' )
1921const { Menu, MenuItem, dialog } = remote
22+ const WP_POST_PATH = '/wp/v2/posts'
2023
2124function 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 ( / \/ s t a r r e d | \/ t r a s h / ) ) {
@@ -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 : [
0 commit comments