feat(composer): better composer plugin loading

This commit is contained in:
Evan Morikawa 2016-04-29 15:57:45 -07:00
parent 31fb621d6b
commit ebba673671
4 changed files with 131 additions and 16 deletions

View file

@ -0,0 +1,78 @@
import React from 'react'
import classnames from 'classnames'
import {ComponentRegistry} from 'nylas-exports'
import {InjectedComponentSet} from 'nylas-component-kit'
export default class ActionBarPlugins extends React.Component {
static displayName = "ActionBarPlugins";
static ROLE = "Composer:ActionButton"
static propTypes = {
draft: React.PropTypes.object,
session: React.PropTypes.session,
}
constructor(props) {
super(props);
this.state = this._getStateFromStores()
}
componentDidMount() {
this._usub = ComponentRegistry.listen(this._onComponentsChange)
}
componentWillUnmount() {
this._usub();
}
_onComponentsChange = () => {
if (this._getPluginsLength() > 0) {
// The `InjectedComponentSet` also listens to the ComponentRegistry.
// Since we can't guarantee the order the listeners are fired in and
// we want to make sure we add the class after the injected component
// set has rendered, put the call in this requestAnimationFrame
//
// It also takes 2 frames to reliably get all of the icons painted.
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
this.setState(this._getStateFromStores())
})
})
}
}
_getPluginsLength() {
const role = ActionBarPlugins.ROLE
return ComponentRegistry.findComponentsMatching({role}).length;
}
_getStateFromStores() {
return {
pluginsLoaded: this._getPluginsLength() > 0,
}
}
render() {
const className = classnames({
"action-bar-animation-wrap": true,
"plugins-loaded": this.state.pluginsLoaded,
});
return (
<span className={className}>
<div className="action-bar-cover"></div>
<InjectedComponentSet
className="composer-action-bar-plugins"
matching={{role: ActionBarPlugins.ROLE}}
exposedProps={{
draft: this.props.draft,
threadId: this.props.draft.threadId,
draftClientId: this.props.draft.clientId,
session: this.props.session,
}}
/>
</span>
)
}
}

View file

@ -27,8 +27,9 @@ import FileUpload from './file-upload';
import ImageFileUpload from './image-file-upload';
import ComposerEditor from './composer-editor';
import SendActionButton from './send-action-button';
import ComposerHeader from './composer-header';
import SendActionButton from './send-action-button';
import ActionBarPlugins from './action-bar-plugins'
import Fields from './fields';
@ -329,16 +330,7 @@ export default class ComposerView extends React.Component {
_renderActionsRegion() {
return (
<div className="composer-action-bar-content">
<InjectedComponentSet
className="composer-action-bar-plugins"
matching={{role: "Composer:ActionButton"}}
exposedProps={{
draft: this.props.draft,
threadId: this.props.draft.threadId,
draftClientId: this.props.draft.clientId,
session: this.props.session,
}}
/>
<ActionBarPlugins draft={this.props.draft} session={this.props.session} />
<button
tabIndex={-1}

View file

@ -23,6 +23,13 @@ body.platform-win32 {
}
}
.action-bar-cover-gen() {
.action-bar-cover {
background-image: -webkit-linear-gradient(left, fade(@action-bar-bg, 0) 0%,
@action-bar-bg 10%);
}
}
.composer-inner-wrap {
position: relative;
height: 100%;
@ -53,14 +60,17 @@ body.platform-win32 {
}
.composer-action-bar-wrap {
@action-bar-bg: @background-off-primary;
position: relative;
width: 100%;
background: @background-off-primary;
background: @action-bar-bg;
border-top: 1px solid darken(@background-off-primary, 7%);
box-shadow: inset 0 2px 1px rgba(0,0,0,0.03);
border-bottom: 0;
border-radius: @border-radius-base;
.action-bar-cover-gen;
// Buttons in the composer footer
.btn.btn-toolbar:not(.btn-emphasis) {
background: transparent;
@ -101,6 +111,35 @@ body.platform-win32 {
flex-wrap: wrap;
}
}
.action-bar-animation-wrap {
position: relative;
overflow: hidden;
.composer-action-bar-plugins {
opacity: 0;
transition: opacity 30ms;
}
.action-bar-cover {
transition: left 200ms ease-out;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
}
&.plugins-loaded {
.composer-action-bar-plugins {
opacity: 1;
}
.action-bar-cover {
left: 100%;
}
}
}
}
.composer-content-wrap {
@ -293,9 +332,11 @@ body.platform-win32 {
.composer-inner-wrap {
.composer-action-bar-wrap {
background: darken(@background-primary, 1%);
@action-bar-bg: darken(@background-primary, 1%);
background: @action-bar-bg;
border-top: 1px solid darken(@background-primary, 8%);
box-shadow: inset 0 1px 2px rgba(0,0,0,0.03);
.action-bar-cover-gen;
}
.composer-action-bar-content {
@ -329,7 +370,9 @@ body.platform-win32 {
background: linear-gradient(to right, fade(@blurred-primary-color, 0%) 0%, fade(@blurred-primary-color, 100%) 40%);
}
.composer-action-bar-wrap {
background: @blurred-off-primary-color;
@action-bar-bg: @blurred-off-primary-color;
background: @action-bar-bg;
.action-bar-cover-gen;
}
}
.message-item-white-wrap.composer-outer-wrap.focused {
@ -339,7 +382,9 @@ body.platform-win32 {
background: linear-gradient(to right, fade(@background-primary, 0%) 0%, fade(@background-primary, 100%) 40%);
}
.composer-action-bar-wrap {
background: @background-off-primary;
@action-bar-bg: @background-off-primary;
background: @action-bar-bg;
.action-bar-cover-gen;
}
}
}

View file

@ -39,7 +39,7 @@ class DraftChangeSet
clearTimeout(@_timer)
@_timer = null
add: (changes, {doesNotAffectPristine}) =>
add: (changes, {doesNotAffectPristine}={}) =>
@_pending = _.extend(@_pending, changes)
@_pending['pristine'] = false unless doesNotAffectPristine
@_onAltered()