mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-21 07:46:06 +08:00
fix(link/open tracking): Remove intermediate metadata states, extra db lookup, dead code
This commit is contained in:
parent
274131863f
commit
fe27fb161f
|
@ -29,7 +29,7 @@ export default class LinkTrackingButton extends React.Component {
|
|||
iconName="icon-composer-linktracking.png"
|
||||
pluginId={PLUGIN_ID}
|
||||
pluginName={PLUGIN_NAME}
|
||||
metadataKey="tracked"
|
||||
metadataEnabledValue={{"tracked": true}}
|
||||
stickyToggle
|
||||
errorMessage={this._errorMessage}
|
||||
draftClientId={this.props.draftClientId} />
|
||||
|
|
|
@ -42,6 +42,7 @@ export default class LinkTrackingComposerExtension extends ComposerExtension {
|
|||
// save the link info to draft metadata
|
||||
metadata.uid = messageUid;
|
||||
metadata.links = links;
|
||||
|
||||
Actions.setMetadata(draft, PLUGIN_ID, metadata);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
import {React} from 'nylas-exports'
|
||||
import {RetinaImg} from 'nylas-component-kit'
|
||||
import {PLUGIN_ID} from './link-tracking-constants'
|
||||
|
||||
const sum = (array, extractFn) => array.reduce( (a, b) => a + extractFn(b), 0 );
|
||||
|
||||
|
||||
export default class LinkTrackingIcon extends React.Component {
|
||||
|
||||
static displayName = 'LinkTrackingIcon';
|
||||
|
||||
static propTypes = {
|
||||
thread: React.PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = this._getStateFromThread(props.thread);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
this.setState(this._getStateFromThread(newProps.thread));
|
||||
}
|
||||
|
||||
_getStateFromThread(thread) {
|
||||
const messages = thread.metadata;
|
||||
// Pull a list of metadata for all messages
|
||||
const metadataObjs = messages.map(msg => msg.metadataForPluginId(PLUGIN_ID)).filter(meta => meta);
|
||||
if (metadataObjs.length) {
|
||||
// If there's metadata, return the total number of link clicks in the most recent metadata
|
||||
const mostRecentMetadata = metadataObjs.pop();
|
||||
return {
|
||||
clicks: sum(mostRecentMetadata.links || [], link => link.click_count || 0),
|
||||
};
|
||||
}
|
||||
return {clicks: null};
|
||||
}
|
||||
|
||||
|
||||
_renderIcon = () => {
|
||||
return this.state.clicks == null ? "" : this._getIcon(this.state.clicks);
|
||||
};
|
||||
|
||||
_getIcon(clicks) {
|
||||
return (<span>
|
||||
<RetinaImg
|
||||
className={clicks > 0 ? "clicked" : ""}
|
||||
name="icon-composer-linktracking.png"
|
||||
mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<span className="link-click-count">{clicks > 0 ? clicks : ""}</span>
|
||||
</span>)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<div className="link-tracking-icon">
|
||||
{this._renderIcon()}
|
||||
</div>)
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import {React} from 'nylas-exports'
|
||||
import {PLUGIN_ID} from './link-tracking-constants'
|
||||
|
||||
|
||||
export default class LinkTrackingPanel extends React.Component {
|
||||
static displayName = 'LinkTrackingPanel';
|
||||
|
||||
static propTypes = {
|
||||
message: React.PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = this._getStateFromMessage(props.message)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
this.setState(this._getStateFromMessage(newProps.message));
|
||||
}
|
||||
|
||||
_getStateFromMessage(message) {
|
||||
const metadata = message.metadataForPluginId(PLUGIN_ID);
|
||||
return metadata ? {links: metadata.links} : {};
|
||||
}
|
||||
|
||||
_renderContents() {
|
||||
return this.state.links.map(link => {
|
||||
return (<tr className="link-info">
|
||||
<td className="link-url">{link.url}</td>
|
||||
<td className="link-count">{link.click_count + " clicks"}</td>
|
||||
</tr>)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.links) {
|
||||
return (<div className="link-tracking-panel">
|
||||
<h4>Link Tracking Enabled</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
{this._renderContents()}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>);
|
||||
}
|
||||
return <div></div>;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
import request from 'request';
|
||||
import {ComponentRegistry, DatabaseStore, Message, ExtensionRegistry, Actions} from 'nylas-exports';
|
||||
import {ComponentRegistry, ExtensionRegistry, Actions} from 'nylas-exports';
|
||||
import LinkTrackingButton from './link-tracking-button';
|
||||
// TODO what's up with these components?
|
||||
// import LinkTrackingIcon from './link-tracking-icon';
|
||||
// import LinkTrackingPanel from './link-tracking-panel';
|
||||
import LinkTrackingComposerExtension from './link-tracking-composer-extension';
|
||||
import LinkTrackingMessageExtension from './link-tracking-message-extension';
|
||||
import {PLUGIN_ID, PLUGIN_URL} from './link-tracking-constants';
|
||||
|
@ -11,41 +8,36 @@ import {PLUGIN_ID, PLUGIN_URL} from './link-tracking-constants';
|
|||
const post = Promise.promisify(request.post, {multiArgs: true});
|
||||
|
||||
|
||||
function afterDraftSend({draftClientId}) {
|
||||
function afterDraftSend({message}) {
|
||||
// only run this handler in the main window
|
||||
if (!NylasEnv.isMainWindow()) return;
|
||||
|
||||
// query for the message
|
||||
DatabaseStore.findBy(Message, {clientId: draftClientId}).then((message) => {
|
||||
// grab message metadata, if any
|
||||
const metadata = message.metadataForPluginId(PLUGIN_ID);
|
||||
// get the uid from the metadata, if present
|
||||
if (metadata) {
|
||||
const uid = metadata.uid;
|
||||
// grab message metadata, if any
|
||||
const metadata = message.metadataForPluginId(PLUGIN_ID);
|
||||
if (metadata) {
|
||||
// get the uid from the metadata, if present
|
||||
const uid = metadata.uid;
|
||||
|
||||
// post the uid and message id pair to the plugin server
|
||||
const data = {uid: uid, message_id: message.id};
|
||||
const serverUrl = `${PLUGIN_URL}/plugins/register-message`;
|
||||
return post({
|
||||
url: serverUrl,
|
||||
body: JSON.stringify(data),
|
||||
}).then( ([response, responseBody]) => {
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(`Link Tracking server error ${response.statusCode} at ${serverUrl}: ${responseBody}`);
|
||||
}
|
||||
return responseBody;
|
||||
}).catch(error => {
|
||||
NylasEnv.showErrorDialog("There was a problem contacting the Link Tracking server! This message will not have link tracking");
|
||||
Promise.reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
// post the uid and message id pair to the plugin server
|
||||
const data = {uid: uid, message_id: message.id};
|
||||
const serverUrl = `${PLUGIN_URL}/plugins/register-message`;
|
||||
|
||||
post({
|
||||
url: serverUrl,
|
||||
body: JSON.stringify(data),
|
||||
}).then( ([response, responseBody]) => {
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(`Link Tracking server error ${response.statusCode} at ${serverUrl}: ${responseBody}`);
|
||||
}
|
||||
}).catch(error => {
|
||||
NylasEnv.showErrorDialog("There was a problem contacting the Link Tracking server! This message will not have link tracking");
|
||||
Promise.reject(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function activate() {
|
||||
ComponentRegistry.register(LinkTrackingButton, {role: 'Composer:ActionButton'});
|
||||
// ComponentRegistry.register(LinkTrackingIcon, {role: 'ThreadListIcon'});
|
||||
// ComponentRegistry.register(LinkTrackingPanel, {role: 'message:BodyHeader'});
|
||||
ExtensionRegistry.Composer.register(LinkTrackingComposerExtension);
|
||||
ExtensionRegistry.MessageView.register(LinkTrackingMessageExtension);
|
||||
this._unlistenSendDraftSuccess = Actions.sendDraftSuccess.listen(afterDraftSend);
|
||||
|
@ -55,8 +47,6 @@ export function serialize() {}
|
|||
|
||||
export function deactivate() {
|
||||
ComponentRegistry.unregister(LinkTrackingButton);
|
||||
// ComponentRegistry.unregister(LinkTrackingIcon);
|
||||
// ComponentRegistry.unregister(LinkTrackingPanel);
|
||||
ExtensionRegistry.Composer.unregister(LinkTrackingComposerExtension);
|
||||
ExtensionRegistry.MessageView.unregister(LinkTrackingMessageExtension);
|
||||
this._unlistenSendDraftSuccess()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import request from 'request';
|
||||
import {ComponentRegistry, ExtensionRegistry, DatabaseStore, Message, Actions} from 'nylas-exports';
|
||||
import {ComponentRegistry, ExtensionRegistry, Actions} from 'nylas-exports';
|
||||
import OpenTrackingButton from './open-tracking-button';
|
||||
import OpenTrackingIcon from './open-tracking-icon';
|
||||
import OpenTrackingMessageStatus from './open-tracking-message-status';
|
||||
|
@ -9,39 +9,33 @@ import {PLUGIN_ID, PLUGIN_URL} from './open-tracking-constants'
|
|||
const post = Promise.promisify(request.post, {multiArgs: true});
|
||||
|
||||
|
||||
function afterDraftSend({draftClientId}) {
|
||||
function afterDraftSend({message}) {
|
||||
// only run this handler in the main window
|
||||
if (!NylasEnv.isMainWindow()) return;
|
||||
|
||||
// query for the message
|
||||
DatabaseStore.findBy(Message, {clientId: draftClientId}).then((message) => {
|
||||
// grab message metadata, if any
|
||||
const metadata = message.metadataForPluginId(PLUGIN_ID);
|
||||
// grab message metadata, if any
|
||||
const metadata = message.metadataForPluginId(PLUGIN_ID);
|
||||
|
||||
// get the uid from the metadata, if present
|
||||
if (metadata) {
|
||||
const uid = metadata.uid;
|
||||
// get the uid from the metadata, if present
|
||||
if (metadata) {
|
||||
const uid = metadata.uid;
|
||||
|
||||
// set metadata against the message
|
||||
Actions.setMetadata(message, PLUGIN_ID, {open_count: 0, open_data: []});
|
||||
// post the uid and message id pair to the plugin server
|
||||
const data = {uid: uid, message_id: message.id, thread_id: 1};
|
||||
const serverUrl = `${PLUGIN_URL}/plugins/register-message`;
|
||||
|
||||
// post the uid and message id pair to the plugin server
|
||||
const data = {uid: uid, message_id: message.id, thread_id: 1};
|
||||
const serverUrl = `${PLUGIN_URL}/plugins/register-message`;
|
||||
return post({
|
||||
url: serverUrl,
|
||||
body: JSON.stringify(data),
|
||||
}).then(([response, responseBody]) => {
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error();
|
||||
}
|
||||
return responseBody;
|
||||
}).catch(error => {
|
||||
NylasEnv.showErrorDialog("There was a problem contacting the Open Tracking server! This message will not have open tracking :(");
|
||||
Promise.reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
post({
|
||||
url: serverUrl,
|
||||
body: JSON.stringify(data),
|
||||
}).then(([response, responseBody]) => {
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(responseBody);
|
||||
}
|
||||
}).catch(error => {
|
||||
NylasEnv.showErrorDialog(`There was a problem saving your open tracking settings. This message will not have open tracking. ${error.message}`);
|
||||
Promise.reject(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function activate() {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import {React, APIError, NylasAPI} from 'nylas-exports'
|
||||
import {MetadataComposerToggleButton} from 'nylas-component-kit'
|
||||
import {PLUGIN_ID, PLUGIN_NAME} from './open-tracking-constants'
|
||||
import uuid from 'node-uuid';
|
||||
|
||||
export default class OpenTrackingButton extends React.Component {
|
||||
static displayName = 'OpenTrackingButton';
|
||||
|
@ -23,13 +24,19 @@ export default class OpenTrackingButton extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const enabledValue = {
|
||||
uid: uuid.v4().replace(/-/g, ""),
|
||||
open_count: 0,
|
||||
open_data: [],
|
||||
};
|
||||
|
||||
return (
|
||||
<MetadataComposerToggleButton
|
||||
title={this._title}
|
||||
iconUrl="nylas://open-tracking/assets/icon-composer-eye@2x.png"
|
||||
pluginId={PLUGIN_ID}
|
||||
pluginName={PLUGIN_NAME}
|
||||
metadataKey="tracked"
|
||||
metadataEnabledValue={enabledValue}
|
||||
stickyToggle
|
||||
errorMessage={this._errorMessage}
|
||||
draftClientId={this.props.draftClientId} />
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import uuid from 'node-uuid';
|
||||
import {ComposerExtension, Actions, QuotedHTMLTransformer} from 'nylas-exports';
|
||||
import {ComposerExtension, QuotedHTMLTransformer} from 'nylas-exports';
|
||||
import {PLUGIN_ID, PLUGIN_URL} from './open-tracking-constants';
|
||||
|
||||
|
||||
|
@ -17,11 +16,8 @@ export default class OpenTrackingComposerExtension extends ComposerExtension {
|
|||
// grab message metadata, if any
|
||||
const metadata = draft.metadataForPluginId(PLUGIN_ID);
|
||||
if (metadata) {
|
||||
// generate a UID
|
||||
const uid = uuid.v4().replace(/-/g, "");
|
||||
|
||||
// insert a tracking pixel <img> into the message
|
||||
const serverUrl = `${PLUGIN_URL}/open/${draft.accountId}/${uid}`;
|
||||
const serverUrl = `${PLUGIN_URL}/open/${draft.accountId}/${metadata.uid}`;
|
||||
const img = `<img width="0" height="0" style="border:0; width:0; height:0;" src="${serverUrl}">`;
|
||||
const draftBody = new DraftBody(draft);
|
||||
draftBody.unquoted = draftBody.unquoted + "<br>" + img;
|
||||
|
@ -29,10 +25,6 @@ export default class OpenTrackingComposerExtension extends ComposerExtension {
|
|||
// save the draft
|
||||
session.changes.add({body: draftBody.body});
|
||||
session.changes.commit();
|
||||
|
||||
// save the uid to draft metadata
|
||||
metadata.uid = uid;
|
||||
Actions.setMetadata(draft, PLUGIN_ID, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,7 +115,8 @@ describe "SendDraftTask", ->
|
|||
waitsForPromise =>
|
||||
@task.performRemote().then =>
|
||||
args = Actions.sendDraftSuccess.calls[0].args[0]
|
||||
expect(args.draftClientId).toBe @draft.clientId
|
||||
expect(args.message instanceof Message).toBe(true)
|
||||
expect(args.messageClientId).toBe(@draft.clientId)
|
||||
|
||||
it "should play a sound", ->
|
||||
spyOn(NylasEnv.config, "get").andReturn true
|
||||
|
|
|
@ -52,6 +52,7 @@ class ExtendedSelection
|
|||
selectFromTo: (from, to) ->
|
||||
fromNode = @findNodeAt(from)
|
||||
toNode = @findNodeAt(to)
|
||||
return unless fromNode and toNode
|
||||
@setBaseAndExtent(fromNode, 0, toNode, (toNode.length ? 0))
|
||||
|
||||
selectFromToWithIndex: (from, fromIndex, to, toIndex) ->
|
||||
|
@ -59,6 +60,7 @@ class ExtendedSelection
|
|||
toNode = @findNodeAt(to)
|
||||
if (not _.isNumber(fromIndex)) or (not _.isNumber(toIndex))
|
||||
throw @_errBadUsage()
|
||||
return unless fromNode and toNode
|
||||
@setBaseAndExtent(fromNode, fromIndex, toNode, toIndex)
|
||||
|
||||
exportSelection: -> new ExportedSelection(@rawSelection, @scopeNode)
|
||||
|
|
|
@ -12,7 +12,7 @@ export default class MetadataComposerToggleButton extends React.Component {
|
|||
iconName: React.PropTypes.string,
|
||||
pluginId: React.PropTypes.string.isRequired,
|
||||
pluginName: React.PropTypes.string.isRequired,
|
||||
metadataKey: React.PropTypes.string.isRequired,
|
||||
metadataEnabledValue: React.PropTypes.object.isRequired,
|
||||
stickyToggle: React.PropTypes.bool,
|
||||
errorMessage: React.PropTypes.func.isRequired,
|
||||
draftClientId: React.PropTypes.string.isRequired,
|
||||
|
@ -55,21 +55,19 @@ export default class MetadataComposerToggleButton extends React.Component {
|
|||
if (!metadata) {
|
||||
if (!this.state.isSetup) {
|
||||
if (this._isDefaultOn()) {
|
||||
this._setMetadataValueTo(true)
|
||||
this._setEnabled(true)
|
||||
}
|
||||
this.setState({isSetup: true})
|
||||
}
|
||||
} else {
|
||||
this.setState({enabled: metadata.tracked, isSetup: true});
|
||||
this.setState({enabled: true, isSetup: true});
|
||||
}
|
||||
};
|
||||
|
||||
_setMetadataValueTo(enabled) {
|
||||
const newValue = {}
|
||||
newValue[this.props.metadataKey] = enabled
|
||||
_setEnabled(enabled) {
|
||||
const metadataValue = enabled ? this.props.metadataEnabledValue : null;
|
||||
this.setState({enabled, pending: true});
|
||||
const metadataValue = enabled ? newValue : null
|
||||
// write metadata into the draft to indicate tracked state
|
||||
|
||||
return DraftStore.sessionForClientId(this.props.draftClientId).then((session)=> {
|
||||
const draft = session.draft();
|
||||
|
||||
|
@ -108,7 +106,7 @@ export default class MetadataComposerToggleButton extends React.Component {
|
|||
if (this.props.stickyToggle) {
|
||||
NylasEnv.config.set(this._configKey(), !this.state.enabled)
|
||||
}
|
||||
this._setMetadataValueTo(!this.state.enabled)
|
||||
this._setEnabled(!this.state.enabled)
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -199,7 +199,7 @@ class SendDraftTask extends Task
|
|||
Actions.queueTask(task)
|
||||
)
|
||||
|
||||
Actions.sendDraftSuccess draftClientId: @message.clientId
|
||||
Actions.sendDraftSuccess(message: @message, messageClientId: @message.clientId)
|
||||
|
||||
# Play the sending sound
|
||||
if NylasEnv.config.get("core.sending.sounds")
|
||||
|
|
Loading…
Reference in a new issue