fix(tracking): fix link tracking and read receipt plugins

Fix broken links in link tracking and read receipts

Fix bug in email frame where it wouldn't adjust the height even when
content changed

MessageItem bodies automatically clear the MessageBodyProcessor cache when
the message contents (including metadata) change.

Remove unused Account stuff from nylas-observables

Plugins appIds properly read if there's an environment set
This commit is contained in:
Evan Morikawa 2016-02-24 12:24:19 -08:00
parent 90b9570f91
commit 9dd7a9600a
13 changed files with 47 additions and 37 deletions

View file

@ -29,6 +29,7 @@ export default class LinkTrackingComposerExtension extends ComposerExtension {
// loop through all <a href> elements, replace with redirect links and save mappings
draftBody.unquoted = draftBody.unquoted.replace(RegExpUtils.linkTagRegex(), (match, prefix, url, suffix, content, closingTag) => {
const encoded = encodeURIComponent(url);
// the links param is an index of the link array.
const redirectUrl = `${PLUGIN_URL}/link/${draft.accountId}/${messageUid}/${links.length}?redirect=${encoded}`;
links.push({url: url, click_count: 0, click_data: [], redirect_url: redirectUrl});
return prefix + redirectUrl + suffix + content + closingTag;

View file

@ -1,9 +1,9 @@
import {MessageViewExtension, RegExpUtils} from 'nylas-exports'
import plugin from '../package.json'
import {PLUGIN_ID} from './link-tracking-constants'
export default class LinkTrackingMessageExtension extends MessageViewExtension {
static formatMessageBody({message}) {
const metadata = message.metadataForPluginId(plugin.appId) || {};
const metadata = message.metadataForPluginId(PLUGIN_ID) || {};
if ((metadata.links || []).length === 0) { return }
const links = {}
for (const link of metadata.links) {

View file

@ -31,7 +31,7 @@ function afterDraftSend({draftClientId}) {
body: JSON.stringify(data),
}).then( ([response, responseBody]) => {
if (response.statusCode !== 200) {
throw new Error();
throw new Error(`Link Tracking server error ${response.statusCode} at ${serverUrl}: ${responseBody}`);
}
return responseBody;
}).catch(error => {

View file

@ -1,7 +1,7 @@
React = require 'react'
_ = require "underscore"
{EventedIFrame} = require 'nylas-component-kit'
{QuotedHTMLTransformer} = require 'nylas-exports'
{Utils, QuotedHTMLTransformer} = require 'nylas-exports'
EmailFrameStylesStore = require './email-frame-styles-store'
@ -11,9 +11,6 @@ class EmailFrame extends React.Component
@propTypes:
content: React.PropTypes.string.isRequired
constructor: (@props) ->
@_lastComputedHeight = 0
render: =>
<EventedIFrame ref="iframe" seamless="seamless" onResize={@_setFrameHeight}/>
@ -29,14 +26,12 @@ class EmailFrame extends React.Component
componentDidUpdate: =>
@_writeContent()
shouldComponentUpdate: (newProps, newState) =>
# Turns out, React is not able to tell if props.children has changed,
# so whenever the message list updates each email-frame is repopulated,
# often with the exact same content. To avoid unnecessary calls to
# _writeContent, we do a quick check for deep equality.
!_.isEqual(newProps, @props)
shouldComponentUpdate: (nextProps, nextState) =>
not Utils.isEqualReact(nextProps, @props) or
not Utils.isEqualReact(nextState, @state)
_writeContent: =>
@_lastComputedHeight = 0
domNode = React.findDOMNode(@)
doc = domNode.contentDocument
return unless doc

View file

@ -22,11 +22,20 @@ class MessageItemBody extends React.Component
processedBody: undefined
componentWillMount: =>
@_unsub = MessageBodyProcessor.processAndSubscribe(@props.message, @_onBodyChanged)
@_prepareBody(@props)
componentWillReceiveProps: (newProps) =>
shouldComponentUpdate: (nextProps, nextState) ->
not Utils.isEqualReact(nextProps, @props) or
not Utils.isEqualReact(nextState, @state)
componentWillUpdate: (nextProps, nextState) =>
if not Utils.isEqualReact(nextProps.message, @props.message)
@_prepareBody(nextProps)
_prepareBody: (props) ->
MessageBodyProcessor.resetCache(props.message)
@_unsub?()
@_unsub = MessageBodyProcessor.processAndSubscribe(newProps.message, @_onBodyChanged)
@_unsub = MessageBodyProcessor.processAndSubscribe(props.message, @_onBodyChanged)
componentWillUnmount: =>
@_unsub?()

View file

@ -106,7 +106,7 @@ describe "MessageItem", ->
snippet: "snippet one..."
subject: "Subject One"
threadId: "thread_12345"
accountId: TEST_ACCOUNT_ID
accountId: window.TEST_ACCOUNT_ID
# Generate the test component. Should be called after @message is configured
# for the test, since MessageItem assumes attributes of the message will not

View file

@ -2,7 +2,6 @@ import {DraftStore, React, Actions, NylasAPI, DatabaseStore, Message, Rx} from '
import {RetinaImg} from 'nylas-component-kit'
import {PLUGIN_ID, PLUGIN_NAME} from './open-tracking-constants'
export default class OpenTrackingButton extends React.Component {
static displayName = 'OpenTrackingButton';

View file

@ -1,6 +1,6 @@
import {React} from 'nylas-exports'
import {RetinaImg} from 'nylas-component-kit'
import plugin from '../package.json'
import {PLUGIN_ID} from './open-tracking-constants'
export default class OpenTrackingMessageStatus extends React.Component {
static displayName = "OpenTrackingMessageStatus";
@ -15,7 +15,7 @@ export default class OpenTrackingMessageStatus extends React.Component {
}
_getStateFromMessage(message) {
const metadata = message.metadataForPluginId(plugin.appId);
const metadata = message.metadataForPluginId(PLUGIN_ID);
if (!metadata) {
return {hasMetadata: false, opened: false}
}

View file

@ -19,8 +19,6 @@
font-weight: bold;
}
.open-tracking-icon {
height: 10px;
margin: -9px 0 0 4px;
}
.read-receipt-message-status {

View file

@ -109,6 +109,7 @@ window.TEST_ACCOUNT_CLIENT_ID = "local-test-account-client-id"
window.TEST_ACCOUNT_ID = "test-account-server-id"
window.TEST_ACCOUNT_EMAIL = "tester@nylas.com"
window.TEST_ACCOUNT_NAME = "Nylas Test"
window.TEST_PLUGIN_ID = "test-plugin-id-123"
beforeEach ->
NylasEnv.testOrganizationUnit = null

View file

@ -11,13 +11,17 @@ class MessageBodyProcessor
@_subscriptions = []
@resetCache()
resetCache: ->
# Store an object for recently processed items. Put the item reference into
# both data structures so we can access it in O(1) and also delete in O(1)
@_recentlyProcessedA = []
@_recentlyProcessedD = {}
for {message, callback} in @_subscriptions
callback(@process(message))
resetCache: (msg) ->
if msg
key = @_key(msg)
delete @_recentlyProcessedD[key]
else
# Store an object for recently processed items. Put the item reference into
# both data structures so we can access it in O(1) and also delete in O(1)
@_recentlyProcessedA = []
@_recentlyProcessedD = {}
for {message, callback} in @_subscriptions
callback(@process(message))
# It's far safer to key off the hash of the body then the [id, version]
# pair. This is because it's theoretically possible for the body to

View file

@ -2,13 +2,8 @@ Rx = require 'rx-lite'
_ = require 'underscore'
Category = require '../flux/models/category'
QuerySubscriptionPool = require '../flux/models/query-subscription-pool'
AccountStore = require '../flux/stores/account-store'
DatabaseStore = require '../flux/stores/database-store'
AccountOperators = {}
AccountObservables = {}
CategoryOperators =
sort: ->
obs = @.map (categories) ->
@ -60,10 +55,8 @@ CategoryObservables =
CategoryObservables.forAccount(account).sort()
.categoryFilter (cat) -> cat.isHiddenCategory()
module.exports =
Categories: CategoryObservables
Accounts: AccountObservables
# Attach a few global helpers

View file

@ -75,7 +75,17 @@ class Package
@metadata ?= Package.loadMetadata(@path)
@bundledPackage = Package.isBundledPackagePath(@path)
@name = @metadata?.name ? path.basename(@path)
@pluginAppId = @metadata.appId ? null
if @metadata.appId
if _.isString @metadata.appId
@pluginAppId = @metadata.appId ? null
else if _.isObject @metadata.appId
@pluginAppId = @metadata.appId[NylasEnv.config.get('env')] ? null
else
@pluginAppId = null
else
@pluginAppId = null
@displayName = @metadata?.displayName || @name
ModuleCache.add(@path, @metadata)
@reset()