Implement new widget API (#1201)

* Implement new widget API

This allows clients to see who provisioned which widgets.

* Update to make state_key the wid

* Update to latest API

* Only show widgets which have required fields

* Don't constantly show apps dialog

* Fix example to include data key
pull/21833/head
Kegsay 2017-07-11 12:15:27 +01:00 committed by GitHub
parent 5c89d3303b
commit cf158530f5
4 changed files with 82 additions and 100 deletions

View File

@ -143,41 +143,44 @@ Example:
get_widgets
-----------
Get a list of all widgets in the room. The response is the `content` field
of the state event.
Get a list of all widgets in the room. The response is an array
of state events.
Request:
- `room_id` (String) is the room to get the widgets in.
Response:
{
$widget_id: {
type: "example",
url: "http://widget.url",
name: "Example Widget",
data: {
key: "val"
[
{
type: "im.vector.modular.widgets",
state_key: "wid1",
content: {
type: "grafana",
url: "https://grafanaurl",
name: "dashboard",
data: {key: "val"}
}
},
$widget_id: { ... }
}
room_id: !foo:bar,
sender: "@alice:localhost"
}
]
Example:
{
action: "get_widgets",
room_id: "!foo:bar",
widget_id: "abc123",
url: "http://widget.url",
type: "example",
response: {
$widget_id: {
type: "example",
url: "http://widget.url",
name: "Example Widget",
data: {
key: "val"
response: [
{
type: "im.vector.modular.widgets",
state_key: "wid1",
content: {
type: "grafana",
url: "https://grafanaurl",
name: "dashboard",
data: {key: "val"}
}
},
$widget_id: { ... }
}
room_id: !foo:bar,
sender: "@alice:localhost"
}
]
}
@ -301,33 +304,17 @@ function setWidget(event, roomId) {
}
}
// TODO: same dance we do for power levels. It'd be nice if the JS SDK had helper methods to do this.
client.getStateEvent(roomId, "im.vector.modular.widgets", "").then((widgets) => {
if (widgetUrl === null) {
delete widgets[widgetId];
}
else {
widgets[widgetId] = {
type: widgetType,
url: widgetUrl,
name: widgetName,
data: widgetData,
};
}
return client.sendStateEvent(roomId, "im.vector.modular.widgets", widgets);
}, (err) => {
if (err.errcode === "M_NOT_FOUND") {
return client.sendStateEvent(roomId, "im.vector.modular.widgets", {
[widgetId]: {
type: widgetType,
url: widgetUrl,
name: widgetName,
data: widgetData,
}
});
}
throw err;
}).done(() => {
let content = {
type: widgetType,
url: widgetUrl,
name: widgetName,
data: widgetData,
};
if (widgetUrl === null) { // widget is being deleted
content = {};
}
client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).done(() => {
sendResponse(event, {
success: true,
});
@ -337,7 +324,26 @@ function setWidget(event, roomId) {
}
function getWidgets(event, roomId) {
returnStateEvent(event, roomId, "im.vector.modular.widgets", "");
const client = MatrixClientPeg.get();
if (!client) {
sendError(event, _t('You need to be logged in.'));
return;
}
const room = client.getRoom(roomId);
if (!room) {
sendError(event, _t('This room is not recognised.'));
return;
}
const stateEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
// Only return widgets which have required fields
let widgetStateEvents = [];
stateEvents.forEach((ev) => {
if (ev.getContent().type && ev.getContent().url) {
widgetStateEvents.push(ev.event); // return the raw event
}
})
sendResponse(event, widgetStateEvents);
}
function setPlumbingState(event, roomId, status) {

View File

@ -275,8 +275,14 @@ module.exports = React.createClass({
},
_shouldShowApps: function(room) {
const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets', '');
return appsStateEvents && Object.keys(appsStateEvents.getContent()).length > 0;
const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
// any valid widget = show apps
for (let i = 0; i < appsStateEvents.length; i++) {
if (appsStateEvents[i].getContent().type && appsStateEvents[i].getContent().url) {
return true;
}
}
return false;
},
componentDidMount: function() {

View File

@ -91,24 +91,16 @@ export default React.createClass({
_onDeleteClick: function() {
console.log("Delete widget %s", this.props.id);
const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', '');
if (!appsStateEvents) {
return;
}
const appsStateEvent = appsStateEvents.getContent();
if (appsStateEvent[this.props.id]) {
delete appsStateEvent[this.props.id];
MatrixClientPeg.get().sendStateEvent(
this.props.room.roomId,
'im.vector.modular.widgets',
appsStateEvent,
'',
).then(() => {
console.log('Deleted widget');
}, (e) => {
console.error('Failed to delete widget', e);
});
}
MatrixClientPeg.get().sendStateEvent(
this.props.room.roomId,
'im.vector.modular.widgets',
{}, // empty content
this.props.id,
).then(() => {
console.log('Deleted widget');
}, (e) => {
console.error('Failed to delete widget', e);
});
},
formatAppTileName: function() {

View File

@ -111,26 +111,6 @@ module.exports = React.createClass({
app.name = app.name || app.type;
app.url = this.encodeUri(app.url, params);
// switch(app.type) {
// case 'etherpad':
// app.queryParams = '?userName=' + this.props.userId +
// '&padId=' + this.props.room.roomId;
// break;
// case 'jitsi': {
//
// app.queryParams = '?confId=' + app.data.confId +
// '&displayName=' + encodeURIComponent(user.displayName) +
// '&avatarUrl=' + encodeURIComponent(MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl)) +
// '&email=' + encodeURIComponent(this.props.userId) +
// '&isAudioConf=' + app.data.isAudioConf;
//
// break;
// }
// case 'vrdemo':
// app.queryParams = '?roomAlias=' + encodeURIComponent(app.data.roomAlias);
// break;
// }
return app;
},
@ -142,17 +122,15 @@ module.exports = React.createClass({
},
_getApps: function() {
const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', '');
const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets');
if (!appsStateEvents) {
return [];
}
const appsStateEvent = appsStateEvents.getContent();
if (Object.keys(appsStateEvent).length < 1) {
return [];
}
return Object.keys(appsStateEvent).map((appId) => {
return this._initAppConfig(appId, appsStateEvent[appId]);
return appsStateEvents.filter((ev) => {
return ev.getContent().type && ev.getContent().url;
}).map((ev) => {
return this._initAppConfig(ev.getStateKey(), ev.getContent());
});
},