diff --git a/internal_packages/composer/lib/composer-view.jsx b/internal_packages/composer/lib/composer-view.jsx index 1e4ba1eb2..b846aad94 100644 --- a/internal_packages/composer/lib/composer-view.jsx +++ b/internal_packages/composer/lib/composer-view.jsx @@ -1,6 +1,6 @@ -import _ from 'underscore'; -import React from 'react'; -import ReactDOM from 'react-dom'; +import _ from 'underscore' +import React from 'react' +import ReactDOM from 'react-dom' import {remote} from 'electron' import { @@ -9,8 +9,7 @@ import { DraftStore, DraftHelpers, FileDownloadStore, - QuotedHTMLTransformer, -} from 'nylas-exports'; +} from 'nylas-exports' import { DropZone, @@ -21,17 +20,17 @@ import { KeyCommandsRegion, OverlaidComponents, InjectedComponentSet, -} from 'nylas-component-kit'; +} from 'nylas-component-kit' -import FileUpload from './file-upload'; -import ImageFileUpload from './image-file-upload'; +import FileUpload from './file-upload' +import ImageFileUpload from './image-file-upload' -import ComposerEditor from './composer-editor'; -import ComposerHeader from './composer-header'; -import SendActionButton from './send-action-button'; +import ComposerEditor from './composer-editor' +import ComposerHeader from './composer-header' +import SendActionButton from './send-action-button' import ActionBarPlugins from './action-bar-plugins' -import Fields from './fields'; +import Fields from './fields' // The ComposerView is a unique React component because it (currently) is a // singleton. Normally, the React way to do things would be to re-render the @@ -55,6 +54,7 @@ export default class ComposerView extends React.Component { super(props) this.state = { showQuotedText: DraftHelpers.isForwardedMessage(props.draft), + showQuotedTextControl: DraftHelpers.shouldAppendQuotedText(props.draft), } } @@ -69,9 +69,11 @@ export default class ComposerView extends React.Component { this._teardownForProps(); this._setupForProps(nextProps); } - if (DraftHelpers.isForwardedMessage(this.props.draft) !== DraftHelpers.isForwardedMessage(nextProps.draft)) { + if (DraftHelpers.isForwardedMessage(this.props.draft) !== DraftHelpers.isForwardedMessage(nextProps.draft) || + DraftHelpers.shouldAppendQuotedText(this.props.draft) !== DraftHelpers.shouldAppendQuotedText(nextProps.draft)) { this.setState({ showQuotedText: DraftHelpers.isForwardedMessage(nextProps.draft), + showQuotedTextControl: DraftHelpers.shouldAppendQuotedText(nextProps.draft), }); } } @@ -118,6 +120,7 @@ export default class ComposerView extends React.Component { _setupForProps({draft, session}) { this.setState({ showQuotedText: DraftHelpers.isForwardedMessage(draft), + showQuotedTextControl: DraftHelpers.shouldAppendQuotedText(draft), }); // TODO: This is a dirty hack to save selection state into the undo/redo @@ -203,7 +206,7 @@ export default class ComposerView extends React.Component { _renderEditor() { const exposedProps = { - body: this._removeQuotedText(this.props.draft.body), + body: this.props.draft.body, draftClientId: this.props.draft.clientId, parentActions: { getComposerBoundingRect: this._getComposerBoundingRect, @@ -239,20 +242,10 @@ export default class ComposerView extends React.Component { return ReactDOM.findDOMNode(this.refs.composerWrap).getBoundingClientRect() } - _removeQuotedText = (html) => { - const {showQuotedText} = this.state; - return showQuotedText ? html : QuotedHTMLTransformer.removeQuotedHTML(html); - } - - _showQuotedText = (html) => { - const {showQuotedText} = this.state; - return showQuotedText ? html : QuotedHTMLTransformer.appendQuotedHTML(html, this.props.draft.body); - } - _renderQuotedTextControl() { - if (QuotedHTMLTransformer.hasQuotedHTML(this.props.draft.body)) { + if (this.state.showQuotedTextControl) { return ( - + ••• { - this.setState({showQuotedText: !this.state.showQuotedText}); + _onExpandQuotedText = () => { + this.setState({ + showQuotedText: true, + showQuotedTextControl: false, + }, () => { + DraftHelpers.appendQuotedTextToDraft(this.props.draft) + .then((draftWithQuotedText) => { + this.props.session.changes.add({ + body: `${draftWithQuotedText.body}
`, + }) + }) + }) } _onRemoveQuotedText = (event) => { event.stopPropagation() const {session, draft} = this.props session.changes.add({ - body: QuotedHTMLTransformer.removeQuotedHTML(draft.body), + body: `${draft.body}
`, + }) + this.setState({ + showQuotedText: false, + showQuotedTextControl: false, }) - this.setState({showQuotedText: false}) } _renderFooterRegions() { @@ -508,7 +514,7 @@ export default class ComposerView extends React.Component { } _onBodyChanged = (event) => { - this.props.session.changes.add({body: this._showQuotedText(event.target.value)}); + this.props.session.changes.add({body: event.target.value}); return; } diff --git a/internal_packages/keybase/lib/decrypt-button.cjsx b/internal_packages/keybase/lib/decrypt-button.cjsx index 22a115a98..e11f60ae9 100755 --- a/internal_packages/keybase/lib/decrypt-button.cjsx +++ b/internal_packages/keybase/lib/decrypt-button.cjsx @@ -43,7 +43,7 @@ class DecryptMessageButton extends React.Component Actions.openPopover( @_openPassphrasePopover(popoverTarget, @decryptPopoverDone)}/>, + callback={=> @_openPassphrasePopover(popoverTarget, @decryptPopoverDone)}/>, {originRect: popoverTarget, direction: 'down'} ) else @@ -59,7 +59,7 @@ class DecryptMessageButton extends React.Component Actions.openPopover( @_openPassphrasePopover(popoverTarget, @decryptAttachmentsPopoverDone)}/>, + callback={=> @_openPassphrasePopover(popoverTarget, @decryptAttachmentsPopoverDone)}/>, {originRect: popoverTarget, direction: 'down'} ) else @@ -81,9 +81,9 @@ class DecryptMessageButton extends React.Component _openPassphrasePopover: (target, callback) => Actions.openPopover( - , - {originRect: target, direction: 'down'} - ) + , + {originRect: target, direction: 'down'} + ) _noPrivateKeys: => numKeys = 0 diff --git a/internal_packages/keybase/lib/passphrase-popover.cjsx b/internal_packages/keybase/lib/passphrase-popover.cjsx index 89e67c2a8..423f6edad 100644 --- a/internal_packages/keybase/lib/passphrase-popover.cjsx +++ b/internal_packages/keybase/lib/passphrase-popover.cjsx @@ -15,7 +15,7 @@ class PassphrasePopover extends React.Component mounted: true } - componentDidMount: -> + componentDidMount: -> @_mounted = true componentWillUnmount: -> @@ -42,7 +42,7 @@ class PassphrasePopover extends React.Component if event.keyCode == 13 @_validatePassphrase() - _validatePassphrase: () => + _validatePassphrase: => passphrase = @state.passphrase for emailIndex of @props.addresses email = @props.addresses[emailIndex] diff --git a/internal_packages/keybase/lib/private-key-popover.cjsx b/internal_packages/keybase/lib/private-key-popover.cjsx index 930e023f8..a53f84b8d 100644 --- a/internal_packages/keybase/lib/private-key-popover.cjsx +++ b/internal_packages/keybase/lib/private-key-popover.cjsx @@ -37,7 +37,7 @@ class PrivateKeyPopover extends React.Component {if (@state.import or @state.paste) and !@state.validKeyBody and @state.keyBody != "" then errorBar} {if @state.import or @state.paste then keyArea}
-
+
{saveButton}
diff --git a/src/flux/stores/draft-factory.coffee b/src/flux/stores/draft-factory.coffee index 1cbca0c5e..54e44d521 100644 --- a/src/flux/stores/draft-factory.coffee +++ b/src/flux/stores/draft-factory.coffee @@ -109,19 +109,9 @@ class DraftFactory cc: cc, from: [@_fromContactForReply(message)], threadId: thread.id, - accountId: message.accountId + accountId: message.accountId, replyToMessageId: message.id, - body: """ -

-
-
- #{DOMUtils.escapeHTMLCharacters(message.replyAttributionLine())} -
-
- #{body} -
-
""" + body: "" ) createDraftForForward: ({thread, message}) => @@ -141,7 +131,9 @@ class DraftFactory threadId: thread.id, accountId: message.accountId, body: """ -

+

+
+
---------- Forwarded message ---------

#{fields.join('
')} diff --git a/src/flux/stores/draft-helpers.es6 b/src/flux/stores/draft-helpers.es6 index b7c09711c..47c8d6d11 100644 --- a/src/flux/stores/draft-helpers.es6 +++ b/src/flux/stores/draft-helpers.es6 @@ -1,8 +1,10 @@ import _ from 'underscore' import Actions from '../actions' import DatabaseStore from './database-store' +import Message from '../models/message' import * as ExtensionRegistry from '../../extension-registry' import SyncbackDraftFilesTask from '../tasks/syncback-draft-files-task' +import DOMUtils from '../../dom-utils' import QuotedHTMLTransformer from '../../services/quoted-html-transformer' @@ -32,6 +34,10 @@ export function isForwardedMessage({body, subject} = {}) { return bodyForwarded || bodyFwd || subjectFwd } +export function shouldAppendQuotedText({body = ''} = {}) { + return !body.includes('
') +} + export function messageMentionsAttachment({body} = {}) { if (body == null) { throw new Error('DraftHelpers::messageMentionsAttachment - Message has no body loaded') } let cleaned = QuotedHTMLTransformer.removeQuotedHTML(body.toLowerCase().trim()); @@ -48,6 +54,25 @@ export function queueDraftFileUploads(draft) { } } +export function appendQuotedTextToDraft(draft) { + return DatabaseStore.find(Message, draft.replyToMessageId) + .include(Message.attributes.body) + .then((prevMessage) => { + const quotedText = ` +
+
+ ${DOMUtils.escapeHTMLCharacters(prevMessage.replyAttributionLine())} +
+
+ ${prevMessage.body} +
+
` + draft.body = draft.body + quotedText + return Promise.resolve(draft) + }) +} + export function applyExtensionTransformsToDraft(draft) { let latestTransformed = draft const extensions = ExtensionRegistry.Composer.extensions() @@ -88,9 +113,15 @@ export function applyExtensionTransformsToDraft(draft) { export function prepareDraftForSyncback(session) { return session.ensureCorrectAccount({noSyncback: true}) .then(() => applyExtensionTransformsToDraft(session.draft())) - .then((transformed) => ( - DatabaseStore.inTransaction((t) => t.persistModel(transformed)) - .then(() => Promise.resolve(queueDraftFileUploads(transformed))) - .thenReturn(transformed) + .then((transformed) => { + if (!shouldAppendQuotedText(transformed)) { + return Promise.resolve(transformed) + } + return appendQuotedTextToDraft(transformed) + }) + .then((draft) => ( + DatabaseStore.inTransaction((t) => t.persistModel(draft)) + .then(() => Promise.resolve(queueDraftFileUploads(draft))) + .thenReturn(draft) )) }