Skip to content

Commit

Permalink
fix(composer): fix flashing and focus losing in the composer
Browse files Browse the repository at this point in the history
  • Loading branch information
emorikawa committed Apr 29, 2016
1 parent e62bebb commit dec047b
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 25 deletions.
11 changes: 6 additions & 5 deletions internal_packages/composer/lib/composer-header.jsx
Expand Up @@ -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 = {
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -94,8 +95,8 @@ export default class ComposerHeader extends React.Component {
}

return {
enabledFields: enabledFields,
participantsFocused: false,
enabledFields,
participantsFocused: props.initiallyFocused,
};
}

Expand Down
1 change: 1 addition & 0 deletions internal_packages/composer/lib/composer-view.jsx
Expand Up @@ -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}
/>
<div
className="compose-body"
Expand Down
42 changes: 23 additions & 19 deletions internal_packages/composer/lib/main.es6
Expand Up @@ -24,7 +24,7 @@ class ComposerWithWindowProps extends React.Component {
super(props);

// We'll now always have windowProps by the time we construct this.
const windowProps = NylasEnv.getWindowProps()
const windowProps = NylasEnv.getWindowProps();
const {draftJSON, draftClientId} = windowProps;
if (!draftJSON) {
throw new Error("Initialize popout composer windows with valid draftJSON")
Expand All @@ -34,38 +34,42 @@ class ComposerWithWindowProps extends React.Component {
this.state = windowProps
}

onDraftReady = () => {
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)
})
});
}

render() {
return (
<ComposerViewForDraftClientId
ref="composer"
onDraftReady={this.onDraftReady}
onDraftReady={this._onDraftReady}
draftClientId={this.state.draftClientId}
className="composer-full-window"
/>
Expand Down
14 changes: 13 additions & 1 deletion src/component-registry.coffee
Expand Up @@ -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 <Sheet />) 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)
Expand Down

0 comments on commit dec047b

Please sign in to comment.