Mailspring/docs/DraftStoreExtension.html
2015-10-03 12:39:26 -07:00

363 lines
15 KiB
HTML

---
layout: docs
title: DraftStoreExtension
edit_url: "https://github.com/nylas/N1/blob/master/src/flux/stores/draft-store-extension.coffee"
---
<h2>Summary</h2>
<div class="markdown-from-sourecode">
<p><p>DraftStoreExtension is an abstract base class. To create DraftStoreExtensions
that enhance the composer experience, you should subclass <a href='draftstoreextension.html'>DraftStoreExtension</a> and
implement the class methods your plugin needs.</p>
<p>To register your extension with the DraftStore, call <a href='DraftStore.html#registerExtension'>DraftStore::registerExtension</a>.
When your package is being unloaded, you <em>must</em> call the corresponding
<a href='DraftStore.html#unregisterExtension'>DraftStore::unregisterExtension</a> to unhook your extension.</p>
<pre><code class="lang-coffee">activate: -&gt;
DraftStore.<span class="hljs-function"><span class="hljs-title">registerExtension</span><span class="hljs-params">(MyExtension)</span></span>
...
deactivate: -&gt;
DraftStore.<span class="hljs-function"><span class="hljs-title">unregisterExtension</span><span class="hljs-params">(MyExtension)</span></span>
</code></pre>
<p>Your DraftStoreExtension subclass should be stateless. The user may have multiple drafts
open at any time, and the methods of your DraftStoreExtension may be called for different
drafts at any time. You should not expect that the session you receive in
<a href='#finalizeSessionBeforeSending'>finalizeSessionBeforeSending</a> is for the same draft you previously received in
<a href='#warningsForSending'>warningsForSending</a>, etc.</p>
<p>The DraftStoreExtension API does not currently expose any asynchronous or <a href='https://github.com/petkaantonov/bluebird/blob/master/API.md'>Promise</a>-based APIs.
This will likely change in the future. If you have a use-case for a Draft Store extension that
is not possible with the current API, please let us know.</p>
</p>
</div>
<ul>
</ul>
<h3>Class Methods</h3>
<h4 id=warningsForSending class="function-name">
warningsForSending(<span class="args"><span class="arg">draft</span></span>) <a href="#warningsForSending" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>Inspect the draft, and return any warnings that need to be displayed before
the draft is sent. Warnings should be string phrases, such as &quot;without an attachment&quot;
that fit into a message of the form: &quot;Send #{phase1} and #{phase2}?&quot;</p>
</p>
</div>
<strong>Parameters</strong>
<table class="arguments">
<tr>
<th>Argument</th>
<th>Description</th>
</tr>
<tr>
<td style="width:15%;">
<em>draft</em>
</td>
<td class="markdown-from-sourecode">
<p>A fully populated <a href='message.html'>Message</a> object that is about to be sent.</p>
</td>
</tr>
</table>
<strong>Returns</strong>
<table class="arguments">
<tr>
<th>Return Values</th>
</tr>
<tr><td class="markdown-from-sourecode"><p>Returns a list of warning strings, or an empty array if no warnings need to be displayed.</p>
</td></tr>
</table>
<h4 id=composerToolbar class="function-name">
composerToolbar(<span class="args"><span class="arg">mutator</span><span class="arg">tooltip</span><span class="arg">iconUrl</span></span>) <a href="#composerToolbar" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>declare an icon to be displayed in the composer&#39;s toolbar (where
bold, italic, underline, etc are).</p>
<p>You must declare the following properties:</p>
</p>
</div>
<strong>Parameters</strong>
<table class="arguments">
<tr>
<th>Argument</th>
<th>Description</th>
</tr>
<tr>
<td style="width:15%;">
<em>mutator</em>
</td>
<td class="markdown-from-sourecode">
<p>A function that&#39;s called when your toolbar button is clicked. This mutator function will be passed as its only argument the <code>dom</code>. The <code>dom</code> is the full {DOM} object of the current composer. You may mutate this in place. We don&#39;t care about the mutator&#39;s return value.</p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>tooltip</em>
</td>
<td class="markdown-from-sourecode">
<p>A one or two word description of what your icon does</p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>iconUrl</em>
</td>
<td class="markdown-from-sourecode">
<p>The url of your icon. It should be in the <code>nylas://</code> scheme. For example: <code>nylas://your-package-name/assets/my-icon@2x.png</code>. Note, we will downsample your image by 2x (for Retina screens), so make sure it&#39;s twice the resolution. The icon should be black and white. We will directly pass the <code>url</code> prop of a <a href='retinaimg.html'>RetinaImg</a></p>
</td>
</tr>
</table>
<h4 id=prepareNewDraft class="function-name">
prepareNewDraft(<span class="args"></span>) <a href="#prepareNewDraft" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>Override prepareNewDraft to modify a brand new draft before it is displayed
in a composer. This is one of the only places in the application where it&#39;s safe
to modify the draft object you&#39;re given directly to add participants to the draft,
add a signature, etc.</p>
<p>By default, new drafts are considered <code>pristine</code>. If the user leaves the composer
without making any changes, the draft is discarded. If your extension populates
the draft in a way that makes it &quot;populated&quot; in a valuable way, you should set
<code>draft.pristine = false</code> so the draft saves, even if no further changes are made.</p>
</p>
</div>
<h4 id=finalizeSessionBeforeSending class="function-name">
finalizeSessionBeforeSending(<span class="args"><span class="arg">session</span></span>) <a href="#finalizeSessionBeforeSending" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>Override finalizeSessionBeforeSending in your DraftStoreExtension subclass to transform
the <a href='draftstoreproxy.html'>DraftStoreProxy</a> editing session just before the draft is sent. This method
gives you an opportunity to make any final substitutions or changes after any
<a href='#warningsForSending'>warningsForSending</a> have been displayed.</p>
<p>Example:</p>
<pre><code class="lang-coffee"><span class="hljs-comment"># Remove any &lt;code&gt; tags found in the draft body</span>
<span class="hljs-attribute">finalizeSessionBeforeSending</span>: <span class="hljs-function"><span class="hljs-params">(session)</span> -&gt;</span>
body = session.draft().body
clean = body.replace(<span class="hljs-regexp">/&lt;\/?code[^&gt;]*&gt;/g</span>, <span class="hljs-string">''</span>)
<span class="hljs-keyword">if</span> body != clean
session.changes.add(<span class="hljs-attribute">body</span>: clean)
</code></pre>
</p>
</div>
<strong>Parameters</strong>
<table class="arguments">
<tr>
<th>Argument</th>
<th>Description</th>
</tr>
<tr>
<td style="width:15%;">
<em>session</em>
</td>
<td class="markdown-from-sourecode">
<p>A <a href='draftstoreproxy.html'>DraftStoreProxy</a> for the draft.</p>
</td>
</tr>
</table>
<h4 id=onMouseUp class="function-name">
onMouseUp(<span class="args"><span class="arg">editableNode</span><span class="arg">range</span><span class="arg">event</span></span>) <a href="#onMouseUp" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>Override onMouseUp in your DraftStoreExtension subclass to
listen for mouse up events sent to the composer&#39;s body text area. This
hook provides the contenteditable DOM Node itself, allowing you to
adjust selection ranges and change content as necessary.</p>
</p>
</div>
<strong>Parameters</strong>
<table class="arguments">
<tr>
<th>Argument</th>
<th>Description</th>
</tr>
<tr>
<td style="width:15%;">
<em>editableNode</em>
</td>
<td class="markdown-from-sourecode">
<p>The composer&#39;s contenteditable <a href='https://developer.mozilla.org/en-US/docs/Web/API/Node'>Node</a> that received the event.</p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>range</em>
</td>
<td class="markdown-from-sourecode">
<p>The currently selected <a href='https://developer.mozilla.org/en-US/docs/Web/API/Range'>Range</a> in the <code>editableNode</code></p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>event</em>
</td>
<td class="markdown-from-sourecode">
<p>The mouse up event.</p>
</td>
</tr>
</table>
<h4 id=onFocusPrevious class="function-name">
onFocusPrevious(<span class="args"><span class="arg">editableNode</span><span class="arg">range</span><span class="arg">event</span></span>) <a href="#onFocusPrevious" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>Called when the user presses <code>Shift-Tab</code> while focused on the composer&#39;s body field.
Override onFocusPrevious in your DraftStoreExtension to adjust the selection or perform
other actions. If your package implements Shift-Tab behavior in a particular scenario, you
should prevent the default behavior of Shift-Tab via <code>event.preventDefault()</code>.</p>
</p>
</div>
<strong>Parameters</strong>
<table class="arguments">
<tr>
<th>Argument</th>
<th>Description</th>
</tr>
<tr>
<td style="width:15%;">
<em>editableNode</em>
</td>
<td class="markdown-from-sourecode">
<p>The composer&#39;s contenteditable <a href='https://developer.mozilla.org/en-US/docs/Web/API/Node'>Node</a> that received the event.</p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>range</em>
</td>
<td class="markdown-from-sourecode">
<p>The currently selected <a href='https://developer.mozilla.org/en-US/docs/Web/API/Range'>Range</a> in the <code>editableNode</code></p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>event</em>
</td>
<td class="markdown-from-sourecode">
<p>The mouse up event.</p>
</td>
</tr>
</table>
<h4 id=onFocusNext class="function-name">
onFocusNext(<span class="args"><span class="arg">editableNode</span><span class="arg">range</span><span class="arg">event</span></span>) <a href="#onFocusNext" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>Called when the user presses <code>Tab</code> while focused on the composer&#39;s body field.
Override onFocusPrevious in your DraftStoreExtension to adjust the selection or perform
other actions. If your package implements Tab behavior in a particular scenario, you
should prevent the default behavior of Tab via <code>event.preventDefault()</code>.</p>
</p>
</div>
<strong>Parameters</strong>
<table class="arguments">
<tr>
<th>Argument</th>
<th>Description</th>
</tr>
<tr>
<td style="width:15%;">
<em>editableNode</em>
</td>
<td class="markdown-from-sourecode">
<p>The composer&#39;s contenteditable <a href='https://developer.mozilla.org/en-US/docs/Web/API/Node'>Node</a> that received the event.</p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>range</em>
</td>
<td class="markdown-from-sourecode">
<p>The currently selected <a href='https://developer.mozilla.org/en-US/docs/Web/API/Range'>Range</a> in the <code>editableNode</code></p>
</td>
</tr>
<tr>
<td style="width:15%;">
<em>event</em>
</td>
<td class="markdown-from-sourecode">
<p>The mouse up event.</p>
</td>
</tr>
</table>
<h4 id=onInput class="function-name">
onInput(<span class="args"></span>) <a href="#onInput" class="link"></a>
</h4>
<div class="function-description markdown-from-sourecode">
<p><p>Override onInput in your DraftStoreExtension subclass to
implement custom behavior as the user types in the composer&#39;s
contenteditable body field.</p>
<p>As the first argument you are passed the entire DOM object of the
composer. You may mutate this object and edit it in place.</p>
<p>Example:</p>
<p>The Nylas <code>templates</code> package uses this method to see if the user has populated a
<code>&lt;code&gt;</code> tag placed in the body and change it&#39;s CSS class to reflect that it is no
longer empty.</p>
<pre><code class="lang-coffee">onInput: (editableNode, event) -&gt;
selection = document.<span class="hljs-function"><span class="hljs-title">getSelection</span><span class="hljs-params">()</span></span>
isWithinNode = (node) -&gt;
test = selection<span class="hljs-class">.baseNode</span>
while test isnt editableNode
return true <span class="hljs-keyword">if</span> test is node
test = test<span class="hljs-class">.parentNode</span>
return false
codeTags = editableNode.<span class="hljs-function"><span class="hljs-title">querySelectorAll</span><span class="hljs-params">(<span class="hljs-string">'code.var.empty'</span>)</span></span>
<span class="hljs-keyword">for</span> codeTag <span class="hljs-keyword">in</span> codeTags
<span class="hljs-keyword">if</span> selection.<span class="hljs-function"><span class="hljs-title">containsNode</span><span class="hljs-params">(codeTag)</span></span> or <span class="hljs-function"><span class="hljs-title">isWithinNode</span><span class="hljs-params">(codeTag)</span></span>
codeTag<span class="hljs-class">.classList</span><span class="hljs-class">.remove</span>(<span class="hljs-string">'empty'</span>)
</code></pre>
</p>
</div>