diff --git a/internal_packages/composer/lib/composer-header.jsx b/internal_packages/composer/lib/composer-header.jsx
index 59e0f97f1..5c1f84d69 100644
--- a/internal_packages/composer/lib/composer-header.jsx
+++ b/internal_packages/composer/lib/composer-header.jsx
@@ -27,6 +27,7 @@ export default class ComposerHeader extends React.Component {
static propTypes = {
draft: React.PropTypes.object.isRequired,
session: React.PropTypes.object.isRequired,
+ initiallyFocused: React.PropTypes.bool,
}
static contextTypes = {
@@ -35,12 +36,12 @@ export default class ComposerHeader extends React.Component {
constructor(props = {}) {
super(props)
- this.state = this._initialStateForDraft(this.props.draft);
+ this.state = this._initialStateForDraft(this.props.draft, props);
}
componentWillReceiveProps(nextProps) {
if (this.props.session !== nextProps.session) {
- this.setState(this._initialStateForDraft(nextProps.draft));
+ this.setState(this._initialStateForDraft(nextProps.draft, nextProps));
} else {
this._ensureFilledFieldsEnabled(nextProps.draft);
}
@@ -80,7 +81,7 @@ export default class ComposerHeader extends React.Component {
}
}
- _initialStateForDraft(draft) {
+ _initialStateForDraft(draft, props) {
const enabledFields = [Fields.To];
if (!_.isEmpty(draft.cc)) {
enabledFields.push(Fields.Cc);
@@ -94,8 +95,8 @@ export default class ComposerHeader extends React.Component {
}
return {
- enabledFields: enabledFields,
- participantsFocused: false,
+ enabledFields,
+ participantsFocused: props.initiallyFocused,
};
}
diff --git a/internal_packages/composer/lib/composer-view.jsx b/internal_packages/composer/lib/composer-view.jsx
index 9bc4d1465..de2841f0f 100644
--- a/internal_packages/composer/lib/composer-view.jsx
+++ b/internal_packages/composer/lib/composer-view.jsx
@@ -145,6 +145,7 @@ export default class ComposerView extends React.Component {
ref="header"
draft={this.props.draft}
session={this.props.session}
+ initiallyFocused={this.props.draft.to.length === 0}
/>
{
+ componentWillUnmount() {
+ if (this._usub) {this._usub()}
+ }
+
+ componentDidUpdate() {
+ this.refs.composer.focus()
+ }
+
+ _onDraftReady = () => {
this.refs.composer.focus().then(() => {
NylasEnv.displayWindow();
+
if (this.state.errorMessage) {
this._showInitialErrorDialog(this.state.errorMessage);
}
- // Give the composer some time to render before hitting another wall
- // of javascript.
- window.setTimeout(() => {
- NylasEnv.getCurrentWindow().updateLoadSettings({
- windowType: "composer",
+ // This will start loading the rest of the composer's plugins. This
+ // may take a while (hundreds of ms) depending on how many plugins
+ // you have installed. For some reason it takes two frames to
+ // reliably get the basic composer (Send button, etc) painted
+ // properly.
+ window.requestAnimationFrame(() => {
+ window.requestAnimationFrame(() => {
+ NylasEnv.getCurrentWindow().updateLoadSettings({
+ windowType: "composer",
+ })
})
-
- // The call to updateLoadSettings will start loading the remaining
- // packages. Once those packages load it'll cause a change in the
- // root Sheet-level InjectedComponentSet, which will cause
- // everything to re-render losing our focus. We have to manually
- // refocus it but defer it so the event loop of the package
- // activation happens first.
- window.setTimeout(() => {
- this.refs.composer.focus()
- }, 32)
- }, 32)
+ })
});
}
@@ -65,7 +69,7 @@ class ComposerWithWindowProps extends React.Component {
return (
diff --git a/src/component-registry.coffee b/src/component-registry.coffee
index a057ba89c..68e4aa8ac 100644
--- a/src/component-registry.coffee
+++ b/src/component-registry.coffee
@@ -160,7 +160,19 @@ class ComponentRegistry
return [].concat(results)
- triggerDebounced: _.debounce(( -> @trigger(@)), 1)
+ # We debounce because a single plugin may activate many components in
+ # their `activate` methods. Furthermore, when the window loads several
+ # plugins may load in sequence. Plugin loading takes a while (dozens of
+ # ms) since javascript is being read and `require` trees are being
+ # traversed.
+ #
+ # Triggering the ComponentRegistry is fairly expensive since many very
+ # high-level components (like the ) listen and re-render when
+ # this triggers.
+ #
+ # We set the debouce interval to 2 "frames" (33ms) to balance
+ # responsiveness and efficient batching.
+ triggerDebounced: _.debounce(( -> @trigger(@)), 33)
_removeDeprecatedRoles: (displayName, roles) ->
newRoles = _.clone(roles)