diff --git a/src/CallHandler.js b/src/CallHandler.js
index 4e6ceb6382..c8179b8b3c 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -62,6 +62,7 @@ import dis from './dispatcher';
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
import SettingsStore from "./settings/SettingsStore";
import WidgetUtils from './utils/WidgetUtils';
+import WidgetEchoStore from './stores/WidgetEchoStore';
import ScalarAuthClient from './ScalarAuthClient';
global.mxCalls = {
@@ -431,12 +432,19 @@ async function _startCallApp(roomId, type) {
});
const room = MatrixClientPeg.get().getRoom(roomId);
- if (!room) {
- console.error("Attempted to start conference call widget in unknown room: " + roomId);
+ const currentRoomWidgets = WidgetUtils.getRoomWidgets(room);
+
+ if (WidgetEchoStore.roomHasPendingWidgetsOfType(roomId, currentRoomWidgets, 'jitsi')) {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+
+ Modal.createTrackedDialog('Call already in progress', '', ErrorDialog, {
+ title: _t('Call in Progress'),
+ description: _t('A call is currently being placed!'),
+ });
return;
}
- const currentJitsiWidgets = WidgetUtils.getRoomWidgets(room).filter((ev) => {
+ const currentJitsiWidgets = currentRoomWidgets.filter((ev) => {
return ev.getContent().type === 'jitsi';
});
if (currentJitsiWidgets.length > 0) {
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index b9d0843635..0325b3d9a6 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -45,6 +45,7 @@ import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
import RoomViewStore from '../../stores/RoomViewStore';
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
+import WidgetEchoStore from '../../stores/WidgetEchoStore';
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
import WidgetUtils from '../../utils/WidgetUtils';
@@ -153,6 +154,8 @@ module.exports = React.createClass({
// Start listening for RoomViewStore updates
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
this._onRoomViewStoreUpdate(true);
+
+ WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate);
},
_onRoomViewStoreUpdate: function(initial) {
@@ -243,6 +246,12 @@ module.exports = React.createClass({
}
},
+ _onWidgetEchoStoreUpdate: function() {
+ this.setState({
+ showApps: this._shouldShowApps(this.state.room),
+ });
+ },
+
_setupRoom: function(room, roomId, joining, shouldPeek) {
// if this is an unknown room then we're in one of three states:
// - This is a room we can peek into (search engine) (we can /peek)
@@ -319,7 +328,9 @@ module.exports = React.createClass({
return false;
}
- return WidgetUtils.getRoomWidgets(room).length > 0;
+ const widgets = WidgetEchoStore.getEchoedRoomWidgets(room.roomId, WidgetUtils.getRoomWidgets(room));
+
+ return widgets.length > 0 || WidgetEchoStore.roomHasPendingWidgets(room.roomId, WidgetUtils.getRoomWidgets(room));
},
componentDidMount: function() {
@@ -414,6 +425,8 @@ module.exports = React.createClass({
this._roomStoreToken.remove();
}
+ WidgetEchoStore.removeListener('update', this._onWidgetEchoStoreUpdate);
+
// cancel any pending calls to the rate_limited_funcs
this._updateRoomMembers.cancelPendingCall();
diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js
index e287abd07a..8ca694b8c3 100644
--- a/src/components/views/elements/AppTile.js
+++ b/src/components/views/elements/AppTile.js
@@ -325,6 +325,12 @@ export default class AppTile extends React.Component {
this.props.id,
).catch((e) => {
console.error('Failed to delete widget', e);
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+
+ Modal.createTrackedDialog('Failed to remove widget', '', ErrorDialog, {
+ title: _t('Failed to remove widget'),
+ description: _t('An error ocurred whilst trying to remove the widget from the room'),
+ });
}).finally(() => {
this.setState({deleting: false});
});
diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js
index 80f899f8e6..e6fe445b45 100644
--- a/src/components/views/rooms/AppsDrawer.js
+++ b/src/components/views/rooms/AppsDrawer.js
@@ -29,6 +29,7 @@ import ScalarAuthClient from '../../../ScalarAuthClient';
import ScalarMessaging from '../../../ScalarMessaging';
import { _t } from '../../../languageHandler';
import WidgetUtils from '../../../utils/WidgetUtils';
+import WidgetEchoStore from "../../../stores/WidgetEchoStore";
// The maximum number of widgets that can be added in a room
const MAX_WIDGETS = 2;
@@ -57,6 +58,7 @@ module.exports = React.createClass({
componentWillMount: function() {
ScalarMessaging.startListening();
MatrixClientPeg.get().on('RoomState.events', this.onRoomStateEvents);
+ WidgetEchoStore.on('update', this._updateApps);
},
componentDidMount: function() {
@@ -82,6 +84,7 @@ module.exports = React.createClass({
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener('RoomState.events', this.onRoomStateEvents);
}
+ WidgetEchoStore.removeListener('update', this._updateApps);
dis.unregister(this.dispatcherRef);
},
@@ -114,8 +117,11 @@ module.exports = React.createClass({
},
_getApps: function() {
- return WidgetUtils.getRoomWidgets(this.props.room).map((ev) => {
- return WidgetUtils.makeAppConfig(ev.getStateKey(), ev.getContent(), ev.sender, this.props.room.roomId);
+ const widgets = WidgetEchoStore.getEchoedRoomWidgets(
+ this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room),
+ );
+ return widgets.map((ev) => {
+ return WidgetUtils.makeAppConfig(ev.getStateKey(), ev.getContent(), ev.sender);
});
},
@@ -200,10 +206,22 @@ module.exports = React.createClass({
;
}
+ let spinner;
+ if (
+ apps.length === 0 && WidgetEchoStore.roomHasPendingWidgets(
+ this.props.room.roomId,
+ WidgetUtils.getRoomWidgets(this.props.room),
+ )
+ ) {
+ const Loader = sdk.getComponent("elements.Spinner");
+ spinner =