Allow Modal to be used with async-loaded components

Add Modal.createDialogAsync, which can be used to display asynchronously-loaded
React components. Also make EncryptedEventDialog use it as a handy
demonstration.
This commit is contained in:
Richard van der Hoff 2017-01-16 17:01:26 +00:00
parent 1d5112db5d
commit ac22803ba0
4 changed files with 74 additions and 6 deletions

View file

@ -19,6 +19,53 @@ limitations under the License.
var React = require('react');
var ReactDOM = require('react-dom');
import sdk from './index';
/**
* Wrap an asynchronous loader function with a react component which shows a
* spinner until the real component loads.
*/
const AsyncWrapper = React.createClass({
propTypes: {
/** A function which takes a 'callback' argument which it will call
* with the real component once it loads.
*/
loader: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
component: null,
}
},
componentWillMount: function() {
this._unmounted = false;
this.props.loader((e) => {
if (this._unmounted) {
return;
}
this.setState({component: e});
});
},
componentWillUnmount: function() {
this._unmounted = true;
},
render: function() {
const {loader, ...otherProps} = this.props;
if (this.state.component) {
const Component = this.state.component;
return <Component {...otherProps} />;
} else {
// show a spinner until the component is loaded.
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
}
},
});
module.exports = {
DialogContainerId: "mx_Dialog_Container",
@ -36,8 +83,30 @@ module.exports = {
},
createDialog: function (Element, props, className) {
var self = this;
return this.createDialogAsync((cb) => {cb(Element)}, props, className);
},
/**
* Open a modal view.
*
* This can be used to display a react component which is loaded as an asynchronous
* webpack component. To do this, set 'loader' as:
*
* (cb) => {
* require(['<module>'], cb);
* }
*
* @param {Function} loader a function which takes a 'callback' argument,
* which it should call with a React component which will be displayed as
* the modal view.
*
* @param {Object} props properties to pass to the displayed
* component. (We will also pass an 'onFinished' property.)
*
* @param {String} className CSS class to apply to the modal wrapper
*/
createDialogAsync: function (loader, props, className) {
var self = this;
// never call this via modal.close() from onFinished() otherwise it will loop
var closeDialog = function() {
if (props && props.onFinished) props.onFinished.apply(null, arguments);
@ -49,7 +118,7 @@ module.exports = {
var dialog = (
<div className={"mx_Dialog_wrapper " + className}>
<div className="mx_Dialog">
<Element {...props} onFinished={closeDialog}/>
<AsyncWrapper loader={loader} {...props} onFinished={closeDialog}/>
</div>
<div className="mx_Dialog_background" onClick={ closeDialog.bind(this, false) }></div>
</div>

View file

@ -75,8 +75,6 @@ import views$dialogs$ChatInviteDialog from './components/views/dialogs/ChatInvit
views$dialogs$ChatInviteDialog && (module.exports.components['views.dialogs.ChatInviteDialog'] = views$dialogs$ChatInviteDialog);
import views$dialogs$DeactivateAccountDialog from './components/views/dialogs/DeactivateAccountDialog';
views$dialogs$DeactivateAccountDialog && (module.exports.components['views.dialogs.DeactivateAccountDialog'] = views$dialogs$DeactivateAccountDialog);
import views$dialogs$EncryptedEventDialog from './components/views/dialogs/EncryptedEventDialog';
views$dialogs$EncryptedEventDialog && (module.exports.components['views.dialogs.EncryptedEventDialog'] = views$dialogs$EncryptedEventDialog);
import views$dialogs$ErrorDialog from './components/views/dialogs/ErrorDialog';
views$dialogs$ErrorDialog && (module.exports.components['views.dialogs.ErrorDialog'] = views$dialogs$ErrorDialog);
import views$dialogs$InteractiveAuthDialog from './components/views/dialogs/InteractiveAuthDialog';

View file

@ -366,10 +366,11 @@ module.exports = WithMatrixClient(React.createClass({
},
onCryptoClicked: function(e) {
var EncryptedEventDialog = sdk.getComponent("dialogs.EncryptedEventDialog");
var event = this.props.mxEvent;
Modal.createDialog(EncryptedEventDialog, {
Modal.createDialogAsync((cb) => {
require(['../../../async-components/views/dialogs/EncryptedEventDialog'], cb)
}, {
event: event,
});
},