message: Better handling for widgets that fail to load
diff: @@ -47,9 +47,14 @@ export default class AppTile extends React.Component {
  // The key used for PersistedElement
  this._persistKey = getPersistKey(this.props.app.id);
  + try {
  this._sgWidget = new StopGapWidget(this.props);
  this._sgWidget.on("preparing", this._onWidgetPrepared);
  this._sgWidget.on("ready", this._onWidgetReady);
  + } catch (e) {
  + console.log("Failed to construct widget", e);
  + this._sgWidget = null;
  + }
  this.iframe = null; // ref to the iframe (callback style)
  this.state = this._getNewState(props);
  @@ -97,7 +102,7 @@ export default class AppTile extends React.Component {
  // Force the widget to be non-persistent (able to be deleted/forgotten)
  ActiveWidgetStore.destroyPersistentWidget(this.props.app.id);
  PersistedElement.destroyElement(this._persistKey);
  - this._sgWidget.stop();
  + if (this._sgWidget) this._sgWidget.stop();
  }
  this.setState({ hasPermissionToLoad });
  @@ -117,7 +122,7 @@ export default class AppTile extends React.Component {
  componentDidMount() {
  // Only fetch IM token on mount if we're showing and have permission to load
  - if (this.state.hasPermissionToLoad) {
  + if (this._sgWidget && this.state.hasPermissionToLoad) {
  this._startWidget();
  }
  @@ -146,10 +151,15 @@ export default class AppTile extends React.Component {
  if (this._sgWidget) {
  this._sgWidget.stop();
  }
  + try {
  this._sgWidget = new StopGapWidget(newProps);
  this._sgWidget.on("preparing", this._onWidgetPrepared);
  this._sgWidget.on("ready", this._onWidgetReady);
  this._startWidget();
  + } catch (e) {
  + console.log("Failed to construct widget", e);
  + this._sgWidget = null;
  + }
  }
  _startWidget() {
  @@ -161,7 +171,7 @@ export default class AppTile extends React.Component {
  _iframeRefChange = (ref) => {
  this.iframe = ref;
  if (ref) {
  - this._sgWidget.start(ref);
  + if (this._sgWidget) this._sgWidget.start(ref);
  } else {
  this._resetWidget(this.props);
  }
  @@ -209,7 +219,7 @@ export default class AppTile extends React.Component {
  // Delete the widget from the persisted store for good measure.
  PersistedElement.destroyElement(this._persistKey);
  - this._sgWidget.stop({forceDestroy: true});
  + if (this._sgWidget) this._sgWidget.stop({forceDestroy: true});
  }
  _onWidgetPrepared = () => {
  @@ -340,7 +350,13 @@ export default class AppTile extends React.Component {
  <Spinner message={_t("Loading...")} />
  </div>
  );
  - if (!this.state.hasPermissionToLoad) {
  + if (this._sgWidget === null) {
  + appTileBody = (
  + <div className={appTileBodyClass} style={appTileBodyStyles}>
  + <AppWarning errorMsg="Error loading Widget" />
  + </div>
  + );
  + } else if (!this.state.hasPermissionToLoad) {
  // only possible for room widgets, can assert this.props.room here
  const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
  appTileBody = (
  