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.

Signed-off-by: Pauli Virtanen <pav@iki.fi>
This commit is contained in:
Pauli Virtanen 2020-04-18 14:53:48 +03:00
parent 1f2bf0485e
commit 4fac781051

View file

@ -340,8 +340,14 @@ export default class AppTile extends React.Component {
/** /**
* Ends all widget interaction, such as cancelling calls and disabling webcams. * Ends all widget interaction, such as cancelling calls and disabling webcams.
* @private * @private
* @returns {Promise<*>} Resolves when the widget is terminated, or timeout passed.
*/ */
_endWidgetActions() { _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 // 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 // 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 // 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. // Delete the widget from the persisted store for good measure.
PersistedElement.destroyElement(this._persistKey); PersistedElement.destroyElement(this._persistKey);
});
} }
/* If user has permission to modify widgets, delete the widget, /* 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.setState({deleting: true});
this._endWidgetActions(); this._endWidgetActions().then(() => {
WidgetUtils.setRoomWidget( WidgetUtils.setRoomWidget(
this.props.room.roomId, this.props.room.roomId,
this.props.app.id, this.props.app.id,
@ -396,6 +402,7 @@ export default class AppTile extends React.Component {
}).finally(() => { }).finally(() => {
this.setState({deleting: false}); this.setState({deleting: false});
}); });
});
}, },
}); });
} }
@ -545,14 +552,19 @@ export default class AppTile extends React.Component {
if (this.props.userWidget) { if (this.props.userWidget) {
this._onMinimiseClick(); this._onMinimiseClick();
} else { } else {
let promise;
if (this.props.show) { if (this.props.show) {
// if we were being shown, end the widget as we're about to be minimized. // 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({ dis.dispatch({
action: 'appsDrawer', action: 'appsDrawer',
show: !this.props.show, show: !this.props.show,
}); });
});
} }
} }