From 1c5869c8670ad9a6d29bc03b4f63f55e1adcd5d1 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Wed, 24 Feb 2016 14:45:47 -0800 Subject: [PATCH] feat(tracking): sticky tracking state and consolidate component --- .../lib/link-tracking-button.jsx | 63 +++------ .../lib/open-tracking-button.jsx | 63 +++------ .../metadata-composer-toggle-button.jsx | 123 ++++++++++++++++++ src/global/nylas-component-kit.coffee | 1 + 4 files changed, 160 insertions(+), 90 deletions(-) create mode 100644 src/components/metadata-composer-toggle-button.jsx diff --git a/internal_packages/link-tracking/lib/link-tracking-button.jsx b/internal_packages/link-tracking/lib/link-tracking-button.jsx index 7712e668c..bb9444ee5 100644 --- a/internal_packages/link-tracking/lib/link-tracking-button.jsx +++ b/internal_packages/link-tracking/lib/link-tracking-button.jsx @@ -1,8 +1,8 @@ -import {DraftStore, React, Actions, NylasAPI, DatabaseStore, Message, Rx} from 'nylas-exports' -import {RetinaImg} from 'nylas-component-kit' +// import {DraftStore, React, Actions, NylasAPI, DatabaseStore, Message, Rx} from 'nylas-exports' +import {React} from 'nylas-exports' +import {MetadataComposerToggleButton} from 'nylas-component-kit' import {PLUGIN_ID, PLUGIN_NAME} from './link-tracking-constants' - export default class LinkTrackingButton extends React.Component { static displayName = 'LinkTrackingButton'; @@ -10,55 +10,26 @@ export default class LinkTrackingButton extends React.Component { draftClientId: React.PropTypes.string.isRequired, }; - constructor(props) { - super(props); - this.state = {enabled: false}; + _title(enabled) { + const dir = enabled ? "Disable" : "Enable"; + return `${dir} link tracking` } - componentDidMount() { - const query = DatabaseStore.findBy(Message, {clientId: this.props.draftClientId}); - this._subscription = Rx.Observable.fromQuery(query).subscribe(this.setStateFromDraft); + _errorMessage(error) { + return `Sorry, we were unable to save your link tracking settings. ${error.message}` } - componentWillUnmount() { - this._subscription.dispose(); - } - - setStateFromDraft = (draft)=> { - if (!draft) return; - const metadata = draft.metadataForPluginId(PLUGIN_ID); - this.setState({enabled: metadata ? metadata.tracked : false}); - }; - - _onClick = ()=> { - const currentlyEnabled = this.state.enabled; - - // write metadata into the draft to indicate tracked state - DraftStore.sessionForClientId(this.props.draftClientId).then((session) => { - const draft = session.draft(); - - NylasAPI.authPlugin(PLUGIN_ID, PLUGIN_NAME, draft.accountId) - .then(() => { - Actions.setMetadata(draft, PLUGIN_ID, currentlyEnabled ? null : {tracked: true}); - }) - .catch((error)=> { - NylasEnv.reportError(error); - NylasEnv.showErrorDialog(`Sorry, we were unable to save your link tracking settings. ${error.message}`); - }); - }); - }; - render() { - const title = this.state.enabled ? "Disable" : "Enable" return ( - + ) } } diff --git a/internal_packages/open-tracking/lib/open-tracking-button.jsx b/internal_packages/open-tracking/lib/open-tracking-button.jsx index 10bf00935..34e24c14f 100644 --- a/internal_packages/open-tracking/lib/open-tracking-button.jsx +++ b/internal_packages/open-tracking/lib/open-tracking-button.jsx @@ -1,60 +1,35 @@ -import {DraftStore, React, Actions, NylasAPI, DatabaseStore, Message, Rx} from 'nylas-exports' -import {RetinaImg} from 'nylas-component-kit' +// import {DraftStore, React, Actions, NylasAPI, DatabaseStore, Message, Rx} from 'nylas-exports' +import {React} from 'nylas-exports' +import {MetadataComposerToggleButton} from 'nylas-component-kit' import {PLUGIN_ID, PLUGIN_NAME} from './open-tracking-constants' export default class OpenTrackingButton extends React.Component { - static displayName = 'OpenTrackingButton'; static propTypes = { draftClientId: React.PropTypes.string.isRequired, }; - constructor(props) { - super(props); - this.state = {enabled: false}; + _title(enabled) { + const dir = enabled ? "Disable" : "Enable"; + return `${dir} read receipts` } - componentDidMount() { - const query = DatabaseStore.findBy(Message, {clientId: this.props.draftClientId}); - this._subscription = Rx.Observable.fromQuery(query).subscribe(this.setStateFromDraft) + _errorMessage(error) { + return `Sorry, we were unable to save your read receipt settings. ${error.message}` } - componentWillUnmount() { - this._subscription.dispose(); - } - - setStateFromDraft = (draft)=> { - if (!draft) return; - const metadata = draft.metadataForPluginId(PLUGIN_ID); - this.setState({enabled: metadata ? metadata.tracked : false}); - }; - - _onClick=()=> { - const currentlyEnabled = this.state.enabled; - - // write metadata into the draft to indicate tracked state - DraftStore.sessionForClientId(this.props.draftClientId).then((session)=> { - const draft = session.draft(); - - NylasAPI.authPlugin(PLUGIN_ID, PLUGIN_NAME, draft.accountId) - .then(() => { - Actions.setMetadata(draft, PLUGIN_ID, currentlyEnabled ? null : {tracked: true}); - }) - .catch((error)=> { - NylasEnv.reportError(error); - NylasEnv.showErrorDialog(`Sorry, we were unable to save your read receipt settings. ${error.message}`); - }); - }); - }; - render() { - const title = this.state.enabled ? "Disable" : "Enable"; - return () + return ( + + ) } - } diff --git a/src/components/metadata-composer-toggle-button.jsx b/src/components/metadata-composer-toggle-button.jsx new file mode 100644 index 000000000..8d937c650 --- /dev/null +++ b/src/components/metadata-composer-toggle-button.jsx @@ -0,0 +1,123 @@ +import {DraftStore, React, Actions, NylasAPI, DatabaseStore, Message, Rx} from 'nylas-exports' +import {RetinaImg} from 'nylas-component-kit' +import classnames from 'classnames' + +export default class MetadataComposerToggleButton extends React.Component { + + static displayName = 'MetadataComposerToggleButton'; + + static propTypes = { + title: React.PropTypes.func.isRequired, + iconUrl: React.PropTypes.string, + iconName: React.PropTypes.string, + pluginId: React.PropTypes.string.isRequired, + pluginName: React.PropTypes.string.isRequired, + metadataKey: React.PropTypes.string.isRequired, + stickyToggle: React.PropTypes.bool, + errorMessage: React.PropTypes.func.isRequired, + draftClientId: React.PropTypes.string.isRequired, + }; + + static defaultProps = { + stickyToggle: false, + } + + constructor(props) { + super(props); + this.state = { + enabled: false, + isSetup: false, + }; + } + + componentDidMount() { + this._mounted = true; + const query = DatabaseStore.findBy(Message, {clientId: this.props.draftClientId}); + this._subscription = Rx.Observable.fromQuery(query).subscribe(this._onDraftChange) + } + + componentWillUnmount() { + this._mounted = false + this._subscription.dispose(); + } + + _configKey() { + return `plugins.${this.props.pluginId}.defaultOn` + } + + _isDefaultOn() { + return NylasEnv.config.get(this._configKey()) + } + + _onDraftChange = (draft)=> { + if (!this._mounted || !draft) { return; } + const metadata = draft.metadataForPluginId(this.props.pluginId); + if (!metadata) { + if (!this.state.isSetup) { + if (this._isDefaultOn()) { + this._setMetadataValueTo(true) + } + this.setState({isSetup: true}) + } + } else { + this.setState({enabled: metadata.tracked, isSetup: true}); + } + }; + + _setMetadataValueTo(enabled) { + const newValue = {} + newValue[this.props.metadataKey] = enabled + 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(); + + return NylasAPI.authPlugin(this.props.pluginId, this.props.pluginName, draft.accountId) + .then(() => { + Actions.setMetadata(draft, this.props.pluginId, metadataValue); + }) + .catch((error) => { + this.setState({enabled: false}); + NylasEnv.reportError(error); + NylasEnv.showErrorDialog(this.props.errorMessage(error)); + }) + }).finally(() => { + this.setState({pending: false}) + }); + } + + _onClick = () => { + // Toggle. + if (this.state.pending) { return; } + if (this.props.stickyToggle) { + NylasEnv.config.set(this._configKey(), !this.state.enabled) + } + this._setMetadataValueTo(!this.state.enabled) + }; + + render() { + const title = this.props.title(this.state.enabled) + + const className = classnames({ + "btn": true, + "btn-toolbar": true, + "btn-pending": this.state.pending, + "btn-enabled": this.state.enabled, + }); + + const attrs = {} + if (this.props.iconUrl) { + attrs.url = this.props.iconUrl + } else if (this.props.iconName) { + attrs.name = this.props.iconName + } + + return ( + + ); + } + +} diff --git a/src/global/nylas-component-kit.coffee b/src/global/nylas-component-kit.coffee index 52fd65552..4ef18a29f 100644 --- a/src/global/nylas-component-kit.coffee +++ b/src/global/nylas-component-kit.coffee @@ -32,6 +32,7 @@ class NylasComponentKit @load "MultiselectActionBar", 'multiselect-action-bar' @load "InjectedComponentSet", 'injected-component-set' @load "TimeoutTransitionGroup", 'timeout-transition-group' + @load "MetadataComposerToggleButton", 'metadata-composer-toggle-button' @load "ConfigPropContainer", "config-prop-container" @load "DisclosureTriangle", "disclosure-triangle" @load "EditableList", "editable-list"