From 931a93af4e715035fa2600632bcb80dd26ce02f0 Mon Sep 17 00:00:00 2001 From: Juan Tejada Date: Thu, 3 Dec 2015 11:52:51 -0800 Subject: [PATCH] feat(print): Add functionality to print currently focused thread Summary: - Adds button inside the message list to print the thread - Adds cmdctrl-p binding to print thread - Adds new action and new internal_package to listen to this action. - Creates a standalone browser window with current thread html, and removes all collapsed messsages from the print view Test Plan: - Manual Reviewers: evan, bengotow Reviewed By: bengotow Differential Revision: https://phab.nylas.com/D2310 --- .../message-list/lib/message-list.cjsx | 10 ++- .../print/assets/nylas-print-logo.png | Bin 0 -> 17206 bytes internal_packages/print/lib/main.es6 | 14 ++++ internal_packages/print/lib/print-window.es6 | 68 ++++++++++++++++ internal_packages/print/lib/printer.es6 | 42 ++++++++++ internal_packages/print/package.json | 11 +++ .../print/static/print-styles.css | 74 ++++++++++++++++++ internal_packages/print/static/print.js | 41 ++++++++++ keymaps/base.cson | 1 + menus/darwin.cson | 2 + menus/linux.cson | 6 +- menus/win32.cson | 2 + src/flux/actions.coffee | 13 ++- static/images/message-list/print@2x.png | Bin 0 -> 1550 bytes 14 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 internal_packages/print/assets/nylas-print-logo.png create mode 100644 internal_packages/print/lib/main.es6 create mode 100644 internal_packages/print/lib/print-window.es6 create mode 100644 internal_packages/print/lib/printer.es6 create mode 100644 internal_packages/print/package.json create mode 100644 internal_packages/print/static/print-styles.css create mode 100644 internal_packages/print/static/print.js create mode 100644 static/images/message-list/print@2x.png diff --git a/internal_packages/message-list/lib/message-list.cjsx b/internal_packages/message-list/lib/message-list.cjsx index 7617dfb73..5c9e1dde6 100755 --- a/internal_packages/message-list/lib/message-list.cjsx +++ b/internal_packages/message-list/lib/message-list.cjsx @@ -92,6 +92,7 @@ class MessageList extends React.Component 'application:reply': => @_createReplyOrUpdateExistingDraft('reply') 'application:reply-all': => @_createReplyOrUpdateExistingDraft('reply-all') 'application:forward': => @_onForward() + 'application:print-thread': => @_onPrintThread() 'core:messages-page-up': => @_onScrollByPage(-1) 'core:messages-page-down': => @_onScrollByPage(1) @@ -230,7 +231,10 @@ class MessageList extends React.Component "Collapse All"
- + +
+
+
@@ -265,6 +269,10 @@ class MessageList extends React.Component _onToggleAllMessagesExpanded: -> Actions.toggleAllMessagesExpanded() + _onPrintThread: => + node = React.findDOMNode(@) + Actions.printThread(@state.currentThread, node.innerHTML) + _onRemoveLabel: (label) => task = new ChangeLabelsTask(thread: @state.currentThread, labelsToRemove: [label]) Actions.queueTask(task) diff --git a/internal_packages/print/assets/nylas-print-logo.png b/internal_packages/print/assets/nylas-print-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d8829efa5376d84aa920e923d29728375a56fd5a GIT binary patch literal 17206 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 { + return (`
  • ${part.name} <${part.email}>
  • `); + }).join(''); + + const content = (` + + + + ${styleTags} + + + + + ${htmlContent} + + + + + `); + + this.tmpFile = path.join(app.getPath('temp'), 'print.html'); + this.browserWin = new BrowserWindow({ + width: 800, + height: 600, + title: `Print - ${subject}`, + webPreferences: { + nodeIntegration: false, + }, + }); + fs.writeFileSync(this.tmpFile, content); + } + + /** + * Load our temp html file. Once the file is loaded it will run print.js, and + * that script will pop out the print dialog. + */ + load() { + this.browserWin.loadURL(`file://${this.tmpFile}`); + } +} diff --git a/internal_packages/print/lib/printer.es6 b/internal_packages/print/lib/printer.es6 new file mode 100644 index 000000000..b15bee38d --- /dev/null +++ b/internal_packages/print/lib/printer.es6 @@ -0,0 +1,42 @@ +import {AccountStore, Actions} from 'nylas-exports'; +import PrintWindow from './print-window'; + +class Printer { + + constructor() { + this.unsub = Actions.printThread.listen(this._printThread); + } + + _printThread(thread, htmlContent) { + if (!thread) throw new Error('Printing: No thread active!'); + + // Get the tag present in the document + const styleTag = document.getElementsByTagName('nylas-styles')[0]; + // These iframes should correspond to the message iframes when a thread is + // focused + const iframes = document.getElementsByTagName('iframe'); + // Grab the html inside the iframes + const messagesHtml = [].slice.call(iframes).map((iframe)=> { + return iframe.contentDocument.documentElement.innerHTML; + }); + + const win = new PrintWindow({ + subject: thread.subject, + account: { + name: AccountStore.current().name, + email: AccountStore.current().emailAddress, + }, + participants: thread.participants, + styleTags: styleTag.innerHTML, + htmlContent, + printMessages: JSON.stringify(messagesHtml), + }); + win.load(); + } + + deactivate() { + this.unsub(); + } +} + +export default Printer; diff --git a/internal_packages/print/package.json b/internal_packages/print/package.json new file mode 100644 index 000000000..ad620ac4e --- /dev/null +++ b/internal_packages/print/package.json @@ -0,0 +1,11 @@ +{ + "name": "print", + "version": "0.1.0", + "main": "./lib/main", + "description": "Print", + "license": "GPLv3", + "private": true, + "engines": { + "nylas": "*" + } +} diff --git a/internal_packages/print/static/print-styles.css b/internal_packages/print/static/print-styles.css new file mode 100644 index 000000000..defa45b40 --- /dev/null +++ b/internal_packages/print/static/print-styles.css @@ -0,0 +1,74 @@ +body { + overflow: auto !important; +} +#print-header { + padding: 15px 20px 0 20px; +} +#print-header img { + zoom: 0.5; +} +#print-header .logo-wrapper { + display: flex; + align-items: center; + font-family: "Nylas-Pro", "Helvetica", "Lucidia Grande", sans-serif !important; +} +#print-header h1 { + font-size: 1.5em !important; + font-family: "Nylas-Pro", "Helvetica", "Lucidia Grande", sans-serif !important; +} +#print-header .account { + margin-left: auto; + font-size: 0.8em !important; +} +#print-header .participant { + font-size: 0.7em; + font-family: "Nylas-Pro", "Helvetica", "Lucidia Grande", sans-serif !important; +} + +/* Elements to hide */ +.message-subject-wrap { + display: none !important; +} +.minified-bundle,.headers,.scrollbar-track,.message-icons-wrap,.header-toggle-control { + display: none !important; +} +.message-actions-wrap { + display: none; +} +.collapsed.message-item-wrap,.draft.message-item-wrap { + display: none !important; +} +.message-item-area>div { + display: none !important; +} +.quoted-text-control, .footer-reply-area-wrap { + display: none; +} + +@media only print { + body,#message-list,.message-item-wrap,.message-item-white-wrap, + .message-item-area,.inbox-html-wrapper { + display: block !important; + width: auto !important; + height: auto !important; + overflow: visible !important; + } + #message-list { + min-height: initial; + } + #print-header { + padding: 0; + } + #print-header .account { + font-size: 0.7em; + } + .message-item-wrap { + display: block; + } + .message-item-area>span { + page-break-before: avoid; + } + .message-item-area>header { + page-break-after: avoid; + } +} diff --git a/internal_packages/print/static/print.js b/internal_packages/print/static/print.js new file mode 100644 index 000000000..441706d84 --- /dev/null +++ b/internal_packages/print/static/print.js @@ -0,0 +1,41 @@ +(function() { + function rebuildMessages(messageNodes, messages) { + // Simply insert the message html inside the appropriate node + for (var idx = 0; idx < messageNodes.length; idx++) { + var msgNode = messageNodes[idx]; + var msgHtml = messages[idx]; + msgNode.innerHTML = msgHtml; + } + } + + function removeClassFromNodes(nodeList, className) { + for (var idx = 0; idx < nodeList.length; idx++) { + var node = nodeList[idx]; + var re = new RegExp('\\b' + className + '\\b', 'g'); + node.className = node.className.replace(re, ''); + } + } + + function removeScrollClasses() { + var scrollRegions = document.querySelectorAll('.scroll-region'); + var scrollContents = document.querySelectorAll('.scroll-region-content'); + var scrollContentInners = document.querySelectorAll('.scroll-region-content-inner'); + removeClassFromNodes(scrollRegions, 'scroll-region'); + removeClassFromNodes(scrollContents, 'scroll-region-content'); + removeClassFromNodes(scrollContentInners, 'scroll-region-content-inner'); + } + + function print() { + window.print(); + // Close this print window after selecting to print + // This is really hackish but appears to be the only working solution + setTimeout(window.close, 500); + } + + var messageNodes = document.querySelectorAll('.message-item-area>span'); + + removeScrollClasses(); + rebuildMessages(messageNodes, window.printMessages); + // Give it a few ms before poppint out the print dialog + setTimeout(print, 50); +})(); diff --git a/keymaps/base.cson b/keymaps/base.cson index 938a4ee0d..63463b1d7 100644 --- a/keymaps/base.cson +++ b/keymaps/base.cson @@ -41,6 +41,7 @@ ### Core application commands. ### 'cmdctrl-q' : 'application:quit' 'cmdctrl-w' : 'window:close' + 'cmdctrl-p' : 'application:print-thread' ### Universal N1 commands. ### 'enter' : 'core:focus-item' diff --git a/menus/darwin.cson b/menus/darwin.cson index 2e1ed531c..de3c4ff79 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -28,6 +28,8 @@ { label: 'New Message', command: 'application:new-message' } { type: 'separator' } { label: 'Close Window', command: 'window:close' } + { type: 'separator' } + { label: 'Print Current Thread', command: 'application:print-thread' } ] } diff --git a/menus/linux.cson b/menus/linux.cson index f3fa471a9..d785297eb 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -4,9 +4,11 @@ submenu: [ { label: '&New Message', command: 'application:new-message' } { type: 'separator' } - { label: 'Preferences', command: 'application:open-preferences' } { label: 'Add Account...', command: 'application:add-account' } { label: 'Clos&e Window', command: 'window:close' } + { type: 'separator' } + { label: 'Print Current Thread', command: 'application:print-thread' } + { type: 'separator' } { label: 'Quit', command: 'application:quit' } ] } @@ -21,6 +23,8 @@ { label: 'C&opy', command: 'core:copy' } { label: '&Paste', command: 'core:paste' } { label: 'Select &All', command: 'core:select-all' } + { type: 'separator' } + { label: 'Preferences', command: 'application:open-preferences' } ] } diff --git a/menus/win32.cson b/menus/win32.cson index cd1d563c9..79b7ac020 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -55,6 +55,8 @@ { type: 'separator' } { label: 'Preferences', command: 'application:open-preferences' } { type: 'separator' } + { label: 'Print Current Thread', command: 'application:print-thread' } + { type: 'separator' } { label: 'E&xit', command: 'application:quit' } ] diff --git a/src/flux/actions.coffee b/src/flux/actions.coffee index 525ac77e1..a8ca0818e 100644 --- a/src/flux/actions.coffee +++ b/src/flux/actions.coffee @@ -231,12 +231,23 @@ class Actions *Scope: Window* ``` - message = Actions.toggleAllMessagesExpanded() ``` ### @toggleAllMessagesExpanded: ActionScopeWindow + ### + Public: Print the currently selected thread. + + *Scope: Window* + + ``` + thread = + Actions.printThread(thread) + ``` + ### + @printThread: ActionScopeWindow + ### Public: Create a new reply to the provided threadId and messageId and populate it with the body provided. diff --git a/static/images/message-list/print@2x.png b/static/images/message-list/print@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d557b1797b192a4ca9bd9e48d1fc541bd321a88a GIT binary patch literal 1550 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a5n0T@z%2~Ij105p zNH8!kUd;@ND2ed(u}aR*)k{ptPfFFR$SnY>W?-j zfNYSkzLEl1NlCV?QiN}Sf^&XRs)DJWiJpOy9hZWFf=y9MnpKdC8&o@xXRDM^Qc_^0 zuU}qXu2*iXmtT~wZ)j<0sc&GUZ)BtkRH0j3nOBlnp_^B%3^4>|j!SBBa#3bMNoIbY z0?6FNr2NtnTO}osMQ{LdXGvxn!lt}psJDO~)CbAv8|oS8!_5Y2wE>A*Ip^mV6r~my z1I@QHv;nI`5kuGmQSV=rnF@3%$USiN$YSW~12XbU@`0gNl$q?ASCU!;v<#}-2x=Q{ z6;QKm^g$j+3J6FjgGGVCY{zAz505fCE~Xm|z!bu~*we)^#DjP1H17_-M2Uvp-v;l5 zf`hnDrm9Y#G(qU}%ncV*&YsGeb|P(AfSHcRln4*aRL3wAO;4#qVVgEy*b#rcG|lGx z&-2yK{!BM4R=Zbq?q}ZnufO+w{$ILy)zZF&GYzDEty<3Ao3U8@UT9YDS>7H`@wJa@ zWz-rSzbwy>OMO%kmz3M%`k$FO_XEeF<16+gi)(2o@Xl$zZXno|mbdJFg58z_Nf!!# zEU;7%bkTNRpYZU&m5ylbizW@vt~mYBIBk(%D>L`x%ZA+VD?461P~Q>zD!S3=q>_fT z?)!*mirT(5qOgoa{gQ?frz?&JUss_dHwZk(O9Jl!61+LSHTz8tIrxlppX}J18{1`{?8b+Paz~w7c7Ude7OZsm=_`~&0 z%QB5p+qstu7)n;Sy!77t(7tp*@K@eDbt=V8--JB){8GNmtb1_hzrg13U&_hW?^++V z_E*o~;rT4MH1_r?w;9-e8vSa!C+B{}Te`(Jf8n4}M0syZQe zAo_&L{6aH}rZ&IQcMJEhe1E7phlzhict~fi?&%xO3n~;Q6{+lf)PLG?>f}sop)+re zHGFnk{YPu>zn8{#8oxX?UE4U*OIe3OzcOsnmbtE8^-Dg99<5xk-b%K5!mdPqlg6dr z8lKKo?Y?Xo*W6Xb_BLAfwvd_ct6j-b)3-iQf2;iCXHKK@21yxvp7{^*7s*ck;JxBq zYz2q=L!OdDB^5leKi9la-_*T4eq-te{%wu(FIy%E{5x=6=>31;56Vxz?qmo*czLhz zI!Cp_9XhRDdHVuy&S&I((BC4yU-ny5_+g`GM}3wbjC{dRdgW?c_STgZ=Xv+&%RK*n zML0t>sorGO!ZHi~b!%1Ge#`I#$$qWXUii4z^Vp$6quJFvF1+bIr|?Ff>3q}UzNfD0 zX^Z!!ziip{Z<@ORUwYM68R?yY(N_KoyG=JR-&(-F=*l_Q)K{7E&B_mUq>ANc76)`H zlx=oO*1!B+_TIbwpa0GCV{Kk_#8TGk9&6Z2=`B}TynaY{%yrznL8tcu&$M$o(+|0R z*plVrY_&q<_z?!4dz0VJ-Rcy6V1LlV#its&r1)<79gdeiy>!x(=P%P1t!wOlzJNK; z*<)s|_0&f*s)c6mJf9Zja%HCGT literal 0 HcmV?d00001