mirror of https://github.com/vector-im/riot-web
Merge pull request #1958 from matrix-org/dbkr/widget_waiting
Fix widgets re-appearing after being deletedpull/21833/head
commit
5077e9e89b
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -231,11 +232,13 @@ Example:
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SdkConfig = require('./SdkConfig');
|
import SdkConfig from './SdkConfig';
|
||||||
const MatrixClientPeg = require("./MatrixClientPeg");
|
import MatrixClientPeg from './MatrixClientPeg';
|
||||||
const MatrixEvent = require("matrix-js-sdk").MatrixEvent;
|
import { MatrixEvent } from 'matrix-js-sdk';
|
||||||
const dis = require("./dispatcher");
|
import dis from './dispatcher';
|
||||||
const Widgets = require('./utils/widgets');
|
import Widgets from './utils/widgets';
|
||||||
|
import WidgetUtils from './WidgetUtils';
|
||||||
|
import RoomViewStore from './stores/RoomViewStore';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
|
||||||
function sendResponse(event, res) {
|
function sendResponse(event, res) {
|
||||||
|
@ -286,51 +289,6 @@ function inviteUser(event, roomId, userId) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a promise that resolves when a widget with the given
|
|
||||||
* ID has been added as a user widget (ie. the accountData event
|
|
||||||
* arrives) or rejects after a timeout
|
|
||||||
*
|
|
||||||
* @param {string} widgetId The ID of the widget to wait for
|
|
||||||
* @param {boolean} add True to wait for the widget to be added,
|
|
||||||
* false to wait for it to be deleted.
|
|
||||||
* @returns {Promise} that resolves when the widget is available
|
|
||||||
*/
|
|
||||||
function waitForUserWidget(widgetId, add) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const currentAccountDataEvent = MatrixClientPeg.get().getAccountData('m.widgets');
|
|
||||||
|
|
||||||
// Tests an account data event, returning true if it's in the state
|
|
||||||
// we're waiting for it to be in
|
|
||||||
function eventInIntendedState(ev) {
|
|
||||||
if (!ev || !currentAccountDataEvent.getContent()) return false;
|
|
||||||
if (add) {
|
|
||||||
return ev.getContent()[widgetId] !== undefined;
|
|
||||||
} else {
|
|
||||||
return ev.getContent()[widgetId] === undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventInIntendedState(currentAccountDataEvent)) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAccountData(ev) {
|
|
||||||
if (eventInIntendedState(currentAccountDataEvent)) {
|
|
||||||
MatrixClientPeg.get().removeListener('accountData', onAccountData);
|
|
||||||
clearTimeout(timerId);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const timerId = setTimeout(() => {
|
|
||||||
MatrixClientPeg.get().removeListener('accountData', onAccountData);
|
|
||||||
reject(new Error("Timed out waiting for widget ID " + widgetId + " to appear"));
|
|
||||||
}, 10000);
|
|
||||||
MatrixClientPeg.get().on('accountData', onAccountData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWidget(event, roomId) {
|
function setWidget(event, roomId) {
|
||||||
const widgetId = event.data.widget_id;
|
const widgetId = event.data.widget_id;
|
||||||
const widgetType = event.data.type;
|
const widgetType = event.data.type;
|
||||||
|
@ -405,7 +363,7 @@ function setWidget(event, roomId) {
|
||||||
// wait for this, the action will complete but if the user is fast enough,
|
// wait for this, the action will complete but if the user is fast enough,
|
||||||
// the widget still won't actually be there.
|
// the widget still won't actually be there.
|
||||||
client.setAccountData('m.widgets', userWidgets).then(() => {
|
client.setAccountData('m.widgets', userWidgets).then(() => {
|
||||||
return waitForUserWidget(widgetId, widgetUrl !== null);
|
return WidgetUtils.waitForUserWidget(widgetId, widgetUrl !== null);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
sendResponse(event, {
|
sendResponse(event, {
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -425,9 +383,9 @@ function setWidget(event, roomId) {
|
||||||
}
|
}
|
||||||
// TODO - Room widgets need to be moved to 'm.widget' state events
|
// TODO - Room widgets need to be moved to 'm.widget' state events
|
||||||
// https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing
|
// https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing
|
||||||
client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).done(() => {
|
client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).then(() => {
|
||||||
// XXX: We should probably wait for the echo of the state event to come back from the server,
|
return WidgetUtils.waitForRoomWidget(widgetId, roomId, widgetUrl !== null);
|
||||||
// as we do with user widgets.
|
}).then(() => {
|
||||||
sendResponse(event, {
|
sendResponse(event, {
|
||||||
success: true,
|
success: true,
|
||||||
});
|
});
|
||||||
|
@ -637,19 +595,6 @@ function returnStateEvent(event, roomId, eventType, stateKey) {
|
||||||
sendResponse(event, stateEvent.getContent());
|
sendResponse(event, stateEvent.getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentRoomId = null;
|
|
||||||
let currentRoomAlias = null;
|
|
||||||
|
|
||||||
// Listen for when a room is viewed
|
|
||||||
dis.register(onAction);
|
|
||||||
function onAction(payload) {
|
|
||||||
if (payload.action !== "view_room") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentRoomId = payload.room_id;
|
|
||||||
currentRoomAlias = payload.room_alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onMessage = function(event) {
|
const onMessage = function(event) {
|
||||||
if (!event.origin) { // stupid chrome
|
if (!event.origin) { // stupid chrome
|
||||||
event.origin = event.originalEvent.origin;
|
event.origin = event.originalEvent.origin;
|
||||||
|
@ -700,80 +645,63 @@ const onMessage = function(event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let promise = Promise.resolve(currentRoomId);
|
|
||||||
if (!currentRoomId) {
|
if (roomId !== RoomViewStore.getRoomId()) {
|
||||||
if (!currentRoomAlias) {
|
sendError(event, _t('Room %(roomId)s not visible', {roomId: roomId}));
|
||||||
sendError(event, _t('Must be viewing a room'));
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
// no room ID but there is an alias, look it up.
|
|
||||||
console.log("Looking up alias " + currentRoomAlias);
|
|
||||||
promise = MatrixClientPeg.get().getRoomIdForAlias(currentRoomAlias).then((res) => {
|
|
||||||
return res.room_id;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then((viewingRoomId) => {
|
// Get and set room-based widgets
|
||||||
if (roomId !== viewingRoomId) {
|
if (event.data.action === "get_widgets") {
|
||||||
sendError(event, _t('Room %(roomId)s not visible', {roomId: roomId}));
|
getWidgets(event, roomId);
|
||||||
return;
|
return;
|
||||||
}
|
} else if (event.data.action === "set_widget") {
|
||||||
|
setWidget(event, roomId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get and set room-based widgets
|
// These APIs don't require userId
|
||||||
if (event.data.action === "get_widgets") {
|
if (event.data.action === "join_rules_state") {
|
||||||
getWidgets(event, roomId);
|
getJoinRules(event, roomId);
|
||||||
return;
|
return;
|
||||||
} else if (event.data.action === "set_widget") {
|
} else if (event.data.action === "set_plumbing_state") {
|
||||||
setWidget(event, roomId);
|
setPlumbingState(event, roomId, event.data.status);
|
||||||
return;
|
return;
|
||||||
}
|
} else if (event.data.action === "get_membership_count") {
|
||||||
|
getMembershipCount(event, roomId);
|
||||||
|
return;
|
||||||
|
} else if (event.data.action === "get_room_enc_state") {
|
||||||
|
getRoomEncState(event, roomId);
|
||||||
|
return;
|
||||||
|
} else if (event.data.action === "can_send_event") {
|
||||||
|
canSendEvent(event, roomId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// These APIs don't require userId
|
if (!userId) {
|
||||||
if (event.data.action === "join_rules_state") {
|
sendError(event, _t('Missing user_id in request'));
|
||||||
getJoinRules(event, roomId);
|
return;
|
||||||
return;
|
}
|
||||||
} else if (event.data.action === "set_plumbing_state") {
|
switch (event.data.action) {
|
||||||
setPlumbingState(event, roomId, event.data.status);
|
case "membership_state":
|
||||||
return;
|
getMembershipState(event, roomId, userId);
|
||||||
} else if (event.data.action === "get_membership_count") {
|
break;
|
||||||
getMembershipCount(event, roomId);
|
case "invite":
|
||||||
return;
|
inviteUser(event, roomId, userId);
|
||||||
} else if (event.data.action === "get_room_enc_state") {
|
break;
|
||||||
getRoomEncState(event, roomId);
|
case "bot_options":
|
||||||
return;
|
botOptions(event, roomId, userId);
|
||||||
} else if (event.data.action === "can_send_event") {
|
break;
|
||||||
canSendEvent(event, roomId);
|
case "set_bot_options":
|
||||||
return;
|
setBotOptions(event, roomId, userId);
|
||||||
}
|
break;
|
||||||
|
case "set_bot_power":
|
||||||
if (!userId) {
|
setBotPower(event, roomId, userId, event.data.level);
|
||||||
sendError(event, _t('Missing user_id in request'));
|
break;
|
||||||
return;
|
default:
|
||||||
}
|
console.warn("Unhandled postMessage event with action '" + event.data.action +"'");
|
||||||
switch (event.data.action) {
|
break;
|
||||||
case "membership_state":
|
}
|
||||||
getMembershipState(event, roomId, userId);
|
|
||||||
break;
|
|
||||||
case "invite":
|
|
||||||
inviteUser(event, roomId, userId);
|
|
||||||
break;
|
|
||||||
case "bot_options":
|
|
||||||
botOptions(event, roomId, userId);
|
|
||||||
break;
|
|
||||||
case "set_bot_options":
|
|
||||||
setBotOptions(event, roomId, userId);
|
|
||||||
break;
|
|
||||||
case "set_bot_power":
|
|
||||||
setBotPower(event, roomId, userId, event.data.level);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.warn("Unhandled postMessage event with action '" + event.data.action +"'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}, (err) => {
|
|
||||||
console.error(err);
|
|
||||||
sendError(event, _t('Failed to lookup current room') + '.');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let listenerCount = 0;
|
let listenerCount = 0;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -90,4 +91,103 @@ export default class WidgetUtils {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that resolves when a widget with the given
|
||||||
|
* ID has been added as a user widget (ie. the accountData event
|
||||||
|
* arrives) or rejects after a timeout
|
||||||
|
*
|
||||||
|
* @param {string} widgetId The ID of the widget to wait for
|
||||||
|
* @param {boolean} add True to wait for the widget to be added,
|
||||||
|
* false to wait for it to be deleted.
|
||||||
|
* @returns {Promise} that resolves when the widget is in the
|
||||||
|
* requested state according to the `add` param
|
||||||
|
*/
|
||||||
|
static waitForUserWidget(widgetId, add) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Tests an account data event, returning true if it's in the state
|
||||||
|
// we're waiting for it to be in
|
||||||
|
function eventInIntendedState(ev) {
|
||||||
|
if (!ev || !ev.getContent()) return false;
|
||||||
|
if (add) {
|
||||||
|
return ev.getContent()[widgetId] !== undefined;
|
||||||
|
} else {
|
||||||
|
return ev.getContent()[widgetId] === undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startingAccountDataEvent = MatrixClientPeg.get().getAccountData('m.widgets');
|
||||||
|
if (eventInIntendedState(startingAccountDataEvent)) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAccountData(ev) {
|
||||||
|
const currentAccountDataEvent = MatrixClientPeg.get().getAccountData('m.widgets');
|
||||||
|
if (eventInIntendedState(currentAccountDataEvent)) {
|
||||||
|
MatrixClientPeg.get().removeListener('accountData', onAccountData);
|
||||||
|
clearTimeout(timerId);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const timerId = setTimeout(() => {
|
||||||
|
MatrixClientPeg.get().removeListener('accountData', onAccountData);
|
||||||
|
reject(new Error("Timed out waiting for widget ID " + widgetId + " to appear"));
|
||||||
|
}, 10000);
|
||||||
|
MatrixClientPeg.get().on('accountData', onAccountData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that resolves when a widget with the given
|
||||||
|
* ID has been added as a room widget in the given room (ie. the
|
||||||
|
* room state event arrives) or rejects after a timeout
|
||||||
|
*
|
||||||
|
* @param {string} widgetId The ID of the widget to wait for
|
||||||
|
* @param {string} roomId The ID of the room to wait for the widget in
|
||||||
|
* @param {boolean} add True to wait for the widget to be added,
|
||||||
|
* false to wait for it to be deleted.
|
||||||
|
* @returns {Promise} that resolves when the widget is in the
|
||||||
|
* requested state according to the `add` param
|
||||||
|
*/
|
||||||
|
static waitForRoomWidget(widgetId, roomId, add) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Tests a list of state events, returning true if it's in the state
|
||||||
|
// we're waiting for it to be in
|
||||||
|
function eventsInIntendedState(evList) {
|
||||||
|
const widgetPresent = evList.some((ev) => {
|
||||||
|
return ev.getContent() && ev.getContent()['id'] === widgetId;
|
||||||
|
});
|
||||||
|
if (add) {
|
||||||
|
return widgetPresent;
|
||||||
|
} else {
|
||||||
|
return !widgetPresent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
|
const startingWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
|
||||||
|
if (eventsInIntendedState(startingWidgetEvents)) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRoomStateEvents(ev) {
|
||||||
|
if (ev.getRoomId() !== roomId) return;
|
||||||
|
|
||||||
|
const currentWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
|
||||||
|
|
||||||
|
if (eventsInIntendedState(currentWidgetEvents)) {
|
||||||
|
MatrixClientPeg.get().removeListener('RoomState.events', onRoomStateEvents);
|
||||||
|
clearTimeout(timerId);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const timerId = setTimeout(() => {
|
||||||
|
MatrixClientPeg.get().removeListener('RoomState.events', onRoomStateEvents);
|
||||||
|
reject(new Error("Timed out waiting for widget ID " + widgetId + " to appear"));
|
||||||
|
}, 10000);
|
||||||
|
MatrixClientPeg.get().on('RoomState.events', onRoomStateEvents);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,7 +324,9 @@ export default class AppTile extends React.Component {
|
||||||
'im.vector.modular.widgets',
|
'im.vector.modular.widgets',
|
||||||
{}, // empty content
|
{}, // empty content
|
||||||
this.props.id,
|
this.props.id,
|
||||||
).catch((e) => {
|
).then(() => {
|
||||||
|
return WidgetUtils.waitForRoomWidget(this.props.id, this.props.room.roomId, false);
|
||||||
|
}).catch((e) => {
|
||||||
console.error('Failed to delete widget', e);
|
console.error('Failed to delete widget', e);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setState({deleting: false});
|
this.setState({deleting: false});
|
||||||
|
|
Loading…
Reference in New Issue