message: Tell widgets to terminate gracefully in AppTile:_endWidgetActions
  If the widget fails to terminate in two seconds, proceed with disposing
  the iframe nevertheless.
  This allows e.g. Jitsi to hangup the conference when minimizing the
  widget, instead of abrupt disconnect that can leave ghost users behind.
diff: @@ -340,8 +340,14 @@ export default class AppTile extends React.Component {
  /**
  * Ends all widget interaction, such as cancelling calls and disabling webcams.
  * @private
  + * @returns {Promise<*>} Resolves when the widget is terminated, or timeout passed.
  */
  _endWidgetActions() {
  + const timeout = 2000;
  + const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout));
  + const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id);
  +
  + return Promise.race([messaging.terminate(), timeoutPromise]).finally(() => {
  // HACK: This is a really dirty way to ensure that Jitsi cleans up
  // its hold on the webcam. Without this, the widget holds a media
  // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351
  @@ -357,6 +363,7 @@ export default class AppTile extends React.Component {
  // Delete the widget from the persisted store for good measure.
  PersistedElement.destroyElement(this._persistKey);
  + });
  }
  /* If user has permission to modify widgets, delete the widget,
  @@ -380,8 +387,7 @@ export default class AppTile extends React.Component {
  }
  this.setState({deleting: true});
  - this._endWidgetActions();
  -
  + this._endWidgetActions().then(() => {
  WidgetUtils.setRoomWidget(
  this.props.room.roomId,
  this.props.app.id,
  @@ -396,6 +402,7 @@ export default class AppTile extends React.Component {
  }).finally(() => {
  this.setState({deleting: false});
  });
  + });
  },
  });
  }
  @@ -545,14 +552,19 @@ export default class AppTile extends React.Component {
  if (this.props.userWidget) {
  this._onMinimiseClick();
  } else {
  + let promise;
  if (this.props.show) {
  // if we were being shown, end the widget as we're about to be minimized.
  - this._endWidgetActions();
  + promise = this._endWidgetActions();
  + } else {
  + promise = Promise.resolve();
  }
  + promise.then(() => {
  dis.dispatch({
  action: 'appsDrawer',
  show: !this.props.show,
  });
  + });
  }
  }
  