Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Bi-directional widget postMessaging API (stickerpacks) [WIP] #1672

Merged
merged 177 commits into from
Apr 3, 2018
Merged
Show file tree
Hide file tree
Changes from 145 commits
Commits
Show all changes
177 commits
Select commit Hold shift + click to select a range
26c6c25
Add dom-to-image dep.
rxl881 Dec 3, 2017
3a89b90
Add stub handler for image snapshot
rxl881 Dec 3, 2017
c9b8aab
Workings for future posterity - Can't access iframe content from pare…
rxl881 Dec 3, 2017
f410112
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
rxl881 Dec 15, 2017
c234e20
Add postmessage api and move functions in to class
rxl881 Dec 15, 2017
56f497d
Add comments and outbound postmessage action to request a widget scre…
rxl881 Dec 15, 2017
9f733eb
Fix binding and promise handling
rxl881 Dec 15, 2017
f2ad7be
Add event handlers and comments.
rxl881 Dec 15, 2017
954c6ee
Set correct API name.
rxl881 Dec 15, 2017
e63f569
Screenshot handlers
rxl881 Dec 15, 2017
83f9a41
Request capabilities (e.g. ability to take snapshots) from widgets.
rxl881 Dec 16, 2017
774774c
Remove unused dep.
rxl881 Dec 19, 2017
8e5c3f0
License and linting fixes.
rxl881 Dec 19, 2017
536d4ef
Fix comments.
rxl881 Dec 19, 2017
08bcfc5
Make sure that capabilities array is initialised.
rxl881 Dec 19, 2017
baf472b
Only show snapshot button when apps are maximised.
rxl881 Dec 19, 2017
3724a1a
Bump build
rxl881 Dec 28, 2017
7b59774
Update comment.
rxl881 Dec 28, 2017
7660176
Fix comment linting errors.
rxl881 Dec 28, 2017
a408b98
Set widget ID on WidgetMessaging instance.
rxl881 Dec 29, 2017
e96d199
Inject stickers
rxl881 Jan 4, 2018
b85efa0
Merge branch 'rxl881/snapshot' of github.com:matrix-org/matrix-react-…
rxl881 Jan 4, 2018
eb4053b
Handle sticker message payloads
rxl881 Jan 4, 2018
32aecd0
Fix event call
rxl881 Jan 4, 2018
da199da
Merge branch 'rxl881/snapshot' of https://github.com/matrix-org/matri…
rxl881 Jan 4, 2018
d20aebf
Correctly handle sticker message events.
rxl881 Jan 4, 2018
54671ab
Send m.sticker messages
rxl881 Jan 4, 2018
1c8586e
Add sticker message rendering.
rxl881 Jan 4, 2018
d652f11
Remove click handlers etc.
rxl881 Jan 4, 2018
7b313b7
Comment logging.
rxl881 Jan 4, 2018
d256e47
Fix header and remove unused deps.
rxl881 Jan 4, 2018
5724749
Remove unused deps.
rxl881 Jan 4, 2018
54d1286
Comment logging.
rxl881 Jan 4, 2018
dc14230
De-register listener.
rxl881 Jan 8, 2018
486b2cf
Handle sticker message event.
rxl881 Jan 8, 2018
5df9a01
Use m.room.sticker event.
rxl881 Jan 8, 2018
0577316
Fix duplicate event registration.
rxl881 Jan 8, 2018
adebf71
Set user widgets on application data.
rxl881 Jan 8, 2018
60e7646
Save user widgets in accountData
rxl881 Jan 8, 2018
9e9de76
Handle user widgets.
rxl881 Jan 9, 2018
6b0b25c
Translations
rxl881 Jan 9, 2018
9abb160
Replace apps button with stickers button in message composer.
rxl881 Jan 9, 2018
53b590f
Add stickers popover.
rxl881 Jan 10, 2018
2bb51ba
Content placeholder.
rxl881 Jan 10, 2018
52f28d0
Stickerpack styling
rxl881 Jan 10, 2018
1a994b8
Move widget utility functions in to their own file.
rxl881 Jan 11, 2018
2cf9da8
Load user stickerpacks
rxl881 Jan 11, 2018
78bd25e
Sticker popover styling
rxl881 Jan 11, 2018
d0c16fa
Convert to ES6 class.
rxl881 Jan 11, 2018
4f36709
Bind functions
rxl881 Jan 11, 2018
1ab71f6
Load stickers in AppTile
rxl881 Jan 11, 2018
9df4dae
Used forked version of react-popover
rxl881 Jan 11, 2018
90b7cb3
Consistent popover size
rxl881 Jan 11, 2018
2354361
Add react-popover-tiny
rxl881 Jan 12, 2018
a3c6dd3
Stickers popover.
rxl881 Jan 12, 2018
351bbdf
Styling
rxl881 Jan 15, 2018
0fab905
Cleanup styles.
rxl881 Jan 15, 2018
5e6da4d
* Fix key error
rxl881 Jan 15, 2018
5a9a4ea
Remove arrow opacity.
rxl881 Jan 15, 2018
82b9897
Select apropriate theme colour for popover.
rxl881 Jan 15, 2018
87d8ed5
Add comment
rxl881 Jan 16, 2018
cb7f25f
Remove rubbish lib.
rxl881 Jan 16, 2018
86542d8
Move stickers into stand-alone component.
rxl881 Jan 16, 2018
9339284
Stickerpack content placeholder.
rxl881 Jan 16, 2018
7676fc0
Handle stickerpack close.
rxl881 Jan 16, 2018
38ed01b
Pass room to stickerpack
rxl881 Jan 16, 2018
86da204
Add manage stickerpacks link
rxl881 Jan 16, 2018
614a10c
Add manage integration link.
rxl881 Jan 17, 2018
3331c8a
Use dispatcher ref for unregister call.
rxl881 Jan 17, 2018
5a42712
Keep reference to stickers menu
rxl881 Jan 17, 2018
fa336b7
Bring hide stickers icon to front when menu open.
rxl881 Jan 17, 2018
aa524c3
Update class names.
rxl881 Jan 17, 2018
b6f85fb
Don't treat sticker messages as info messages.
rxl881 Jan 17, 2018
f8d7ab1
Handle non-m.room.message event types.
rxl881 Jan 17, 2018
1293c53
Cleanup
rxl881 Jan 18, 2018
917d85d
Refer rest parameters
rxl881 Jan 18, 2018
910623d
Close context menu on resize.
rxl881 Jan 18, 2018
23bef68
Fix duplicate message listeners
rxl881 Jan 18, 2018
0441487
Manage sticker packs link and add comments
rxl881 Jan 22, 2018
29962ed
Add stickerpack strings.
rxl881 Feb 5, 2018
34de372
Add method to remove all stickerpacks.
rxl881 Feb 5, 2018
992c477
Add button to remove all stickerpacks
rxl881 Feb 5, 2018
e508f06
Add asset add / remove messaging clauses.
rxl881 Feb 5, 2018
393236b
Update / remove stickerpacks
rxl881 Feb 7, 2018
f3943be
Check for empty user widgets.
rxl881 Feb 7, 2018
7b75dbb
Use default AppTile menu bar.
rxl881 Feb 7, 2018
5e30468
Linting
rxl881 Feb 7, 2018
234ca8b
Remove top padding from app tile.
rxl881 Feb 8, 2018
9e3c1fb
Pass room name.
rxl881 Feb 9, 2018
5559341
Hide apps drawer when viewining room settings.
rxl881 Feb 9, 2018
ce560c5
Fix link spacing
rxl881 Feb 22, 2018
9a5c916
Close modal on integration manager launch.
rxl881 Feb 22, 2018
88288ff
Add error handling for failure to connect to integration manager.
rxl881 Feb 23, 2018
fefc325
Remove unused dep.
rxl881 Feb 23, 2018
46f94b3
Ensure that roomId is validated before accessing room-based widgets.
rxl881 Feb 23, 2018
57b027b
Fix API names.
rxl881 Feb 23, 2018
d755b82
Remove logging and cleanup.
rxl881 Feb 23, 2018
9b667f2
Reduce logging.
rxl881 Feb 23, 2018
9ae89e2
Reduce logging.
rxl881 Feb 23, 2018
b2bf4d4
Merge branch 'develop' of https://github.com/matrix-org/matrix-react-…
rxl881 Feb 23, 2018
891199b
Force rebuild
rxl881 Feb 23, 2018
332892f
Pull in changes from develop
rxl881 Feb 23, 2018
86461bc
Disable all widget assets on widget removal.
rxl881 Feb 24, 2018
ee4310c
Avoid potential NPE.
rxl881 Feb 25, 2018
e249e3d
Correct stickerpicker naming,
rxl881 Feb 25, 2018
c93faf7
Fix promise wrapping.
rxl881 Feb 25, 2018
2b0790b
Fix PropTypes.
rxl881 Feb 25, 2018
20a442c
Add comment.
rxl881 Feb 25, 2018
d3de44e
Global interface to start integration manager.
rxl881 Feb 26, 2018
14d52c9
Open integration manager from widget postMessage.
rxl881 Feb 26, 2018
707e3f3
Temp. revert unintended commit.
rxl881 Feb 26, 2018
73c8ef5
Fix current roomID.
rxl881 Feb 26, 2018
b64736a
Add listener to close stickerpicker.
rxl881 Feb 26, 2018
5ca0fc3
Make MStickerBody extend MImageBody.
rxl881 Feb 26, 2018
8e7564b
Wrap the close menu trigger in a timeout
rxl881 Feb 26, 2018
ef4d137
Wrap menu close in timeout to avoid element disappearing unexpectedly.
rxl881 Feb 27, 2018
57c98d9
Update widget type.
rxl881 Feb 27, 2018
0fdbddf
Show sticker description as a tooltip, on hover.
rxl881 Feb 27, 2018
21c8bed
Bump build
rxl881 Feb 28, 2018
f3c928a
Lint.
rxl881 Feb 28, 2018
e2cedbe
Pull in changes from develop
rxl881 Mar 5, 2018
7755a3c
Nest sticker image element for correct positioning of tooltips.
rxl881 Mar 6, 2018
d5465cf
Prefix and clarify global variable naming.
rxl881 Mar 6, 2018
b529edb
Linting
rxl881 Mar 6, 2018
7f91b47
Move sticker picker icon.
rxl881 Mar 7, 2018
b2bb15b
Remove accidentally checked in yarn.lock
rxl881 Mar 7, 2018
c59dd5b
Add placeholders for sticker images (and fancy transitions).
rxl881 Mar 8, 2018
3ab8b1f
Hide sticker picker delete button and show minimise button.
rxl881 Mar 8, 2018
fdec4b3
Hide padding if last control.
rxl881 Mar 8, 2018
e36ae3c
Fix context menu offset.
rxl881 Mar 8, 2018
e7c19fd
Don't render placeholder when image is visible.
rxl881 Mar 8, 2018
a338593
Use getUserWidgets where possible.
rxl881 Mar 9, 2018
7e06209
Cleanup timeout before unmount.
rxl881 Mar 9, 2018
46f46ee
Simplify request mapping.
rxl881 Mar 9, 2018
b2d23b6
Switch to 'm.sticker' message events. Allow whitelisting of wisget ca…
rxl881 Mar 12, 2018
a81269c
Check if user widget.
rxl881 Mar 13, 2018
de33294
Center loading spinner.
rxl881 Mar 13, 2018
53b716b
Throw capability request exception.
rxl881 Mar 13, 2018
f820374
Logging.
rxl881 Mar 13, 2018
7d13edc
Get user widgets by default (if roomID is not specified).
rxl881 Mar 13, 2018
8b311c7
Null guard widget listener
rxl881 Mar 13, 2018
66ea78d
Clean up whitelisted permission addition / logging.
rxl881 Mar 13, 2018
83412ac
Display sticker content (URL), rather than thumbnail.
rxl881 Mar 14, 2018
4d8f507
Allow react performance profiling on widget iframes.
rxl881 Mar 16, 2018
7462812
Bump CI build
rxl881 Mar 22, 2018
4ac9653
Refactor widget postMessage API.
rxl881 Mar 28, 2018
b4e70e3
Expects object, not naked ID.
rxl881 Mar 28, 2018
5fc9b8a
Indentation.
rxl881 Mar 29, 2018
a1581ad
Don't swallow error.
rxl881 Mar 29, 2018
23a52bd
Indentation.
rxl881 Mar 29, 2018
35fcb2c
Catch rather than 'done' error handler.
rxl881 Mar 29, 2018
aefccb1
Ensure deleting is set to false.
rxl881 Mar 29, 2018
cafbd29
Remove unused ref.
rxl881 Mar 29, 2018
f383298
Remove duplicate event handler.
rxl881 Mar 29, 2018
9c10d24
Add comment.
rxl881 Mar 29, 2018
38c8bc7
Consistent function naming.
rxl881 Mar 29, 2018
e21cc14
Comment.
rxl881 Mar 29, 2018
67f755e
Comment.
rxl881 Mar 29, 2018
c8f9586
Check for valid reference.
rxl881 Mar 29, 2018
6181ca6
"Stickerpack" translation.
rxl881 Mar 29, 2018
b1e7dcf
Cleanup / simplify code.
rxl881 Mar 29, 2018
3d9bdb9
Consistent async / await / promises.
rxl881 Mar 29, 2018
5ba18b5
Avoid redefining function parameter
rxl881 Mar 29, 2018
20cbc01
Add callback handler for integration manager close.
rxl881 Mar 29, 2018
9a3f356
Wait for setAccountData to complete.
rxl881 Mar 29, 2018
93804e8
Return promise
rxl881 Mar 29, 2018
2e6d6c8
Add back in body 'ref'
rxl881 Mar 29, 2018
003cf61
Handle remove sticker picker errors.
rxl881 Mar 29, 2018
ff0834a
Add comment.
rxl881 Mar 29, 2018
9cc3d3c
Move default sticker content into generator function.
rxl881 Mar 29, 2018
557a45e
Remove inline styling.
rxl881 Mar 29, 2018
11915b0
Fix user widget event type.
rxl881 Apr 2, 2018
d83b6f1
Use AccessibleButton.
rxl881 Apr 2, 2018
49bea1a
Move stickerpicker content out of state in to generator function.
rxl881 Apr 2, 2018
8241afe
Fix content references
rxl881 Apr 2, 2018
b109c93
Put stickers behind labs flag.
rxl881 Apr 2, 2018
f8f8bc4
Merge branch 'develop' of https://github.com/matrix-org/matrix-react-…
rxl881 Apr 3, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/ContentMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ class ContentMessages {
this.nextId = 0;
}

sendStickerContentToRoom(url, roomId, info, text, matrixClient) {
return MatrixClientPeg.get().sendStickerMessage(roomId, url, info, text).catch((e) => {
console.warn(`Failed to send content with URL ${url} to room ${roomId}`, e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this will swallow the error so the error handler you've defined in injectSticker won't be called (the promise will succeed). You just need to throw e at the end.

});
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 space indents please :)

sendContentToRoom(file, roomId, matrixClient) {
const content = {
body: file.name || 'Attachment',
Expand Down
76 changes: 76 additions & 0 deletions src/IntegrationManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Modal from './Modal';
import sdk from './index';
import SdkConfig from './SdkConfig';
import ScalarMessaging from './ScalarMessaging';
import ScalarAuthClient from './ScalarAuthClient';
import RoomViewStore from './stores/RoomViewStore';

if (!global.mxIntegrationManager) {
global.mxIntegrationManager = {};
}

export default class IntegrationManager {
static async _init() {
if (!global.mxIntegrationManager.client || !global.mxIntegrationManager.connected) {
if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) {
ScalarMessaging.startListening();
global.mxIntegrationManager.client = new ScalarAuthClient();

await global.mxIntegrationManager.client.connect().then(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more mixing of async/await and promises style: this looks fine logic-wise but it makes it harder to read

global.mxIntegrationManager.connected = true;
}).catch((e) => {
console.error("Failed to connect to integrations server", e);
global.mxIntegrationManager.error = e;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could end up with multiple _init()s running at the same time and racing here, ie. if the first succeeds and a later one errors, you'll set both global.mxIntegrationManager.connected and global.mxIntegrationManager.error. This probably isn't a problem in practice, but what we've done elsewhere is something like:

in the constructor:

this._initPromise = this._init();

in other functions:

await this._initPromise;
[do thing]

You could also make things simpler here by doing:

global.mxIntegrationManager = new IntegrationManager;
export default global.mxIntegrationManager;

...rather than exporting the class itself, then you don't need to make the member functions static and you can just use variables on this rather than referencing global.mxIntegrationManager each time.

});
} else {
console.error('Invalid integration manager config', SdkConfig.get());
}
}
}

/**
* Launch the integrations manager on the stickers integration page
* @param {string} integType integration / widget type
* @param {string} integId integration / widget ID
* @param {function} onClose Callback to invoke on integration manager close
*/
static async open(integType, integId, onClose) {
await IntegrationManager._init();
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
if (global.mxIntegrationManager.error ||
!(global.mxIntegrationManager.client && global.mxIntegrationManager.client.hasCredentials())) {
console.error("Scalar error", global.mxIntegrationManager);
return;
}
integType = 'type_' + integType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm, redefining function params can be confusing

const src = (global.mxIntegrationManager.client && global.mxIntegrationManager.client.hasCredentials()) ?
global.mxIntegrationManager.client.getScalarInterfaceUrlForRoom(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've already done the global.mxIntegrationManager.client && global.mxIntegrationManager.client.hasCredentials() above?

RoomViewStore.getRoomId(),
integType,
integId,
) :
null;
Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, {
src: src,
}, "mx_IntegrationsManager");

if (onClose) {
onClose();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it'll be invoked just as the dialog opens rather than when it closes?

}
}
}
85 changes: 85 additions & 0 deletions src/MatrixPostMessageApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Promise from "bluebird";

// NOTE: PostMessageApi only handles message events with a data payload with a
// response field
export default class PostMessageApi {
constructor(targetWindow, timeoutMs) {
this._window = targetWindow || window.parent; // default to parent window
this._timeoutMs = timeoutMs || 5000; // default to 5s timer
this._counter = 0;
this._requestMap = {
// $ID: {resolve, reject}
};
}

start() {
addEventListener('message', this.getOnMessageCallback());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since window is global, this will be window. addEventListener? might be better to be explicit

}

stop() {
removeEventListener('message', this.getOnMessageCallback());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also might return something other than the thing we passed to addEventListener

edit: oh wait, no it doesn't

}

// Somewhat convoluted so we can successfully capture the PostMessageApi 'this' instance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, I think all that's required here is a bound function?

getOnMessageCallback() {
if (this._onMsgCallback) {
return this._onMsgCallback;
}
const self = this;
this._onMsgCallback = function(ev) {
// THIS IS ALL UNSAFE EXECUTION.
// We do not verify who the sender of `ev` is!
const payload = ev.data;
// NOTE: Workaround for running in a mobile WebView where a
// postMessage immediately triggers this callback even though it is
// not the response.
if (payload.response === undefined) {
return;
}
const promise = self._requestMap[payload._id];
if (!promise) {
return;
}
delete self._requestMap[payload._id];
promise.resolve(payload);
};
return this._onMsgCallback;
}

exec(action, target) {
this._counter += 1;
target = target || "*";
action._id = Date.now() + "-" + Math.random().toString(36) + "-" + this._counter;

return new Promise((resolve, reject) => {
this._requestMap[action._id] = {resolve, reject};
this._window.postMessage(action, target);

if (this._timeoutMs > 0) {
setTimeout(() => {
if (!this._requestMap[action._id]) {
return;
}
console.error("postMessage request timed out. Sent object: " + JSON.stringify(action));
this._requestMap[action._id].reject(new Error("Timed out"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

presumably the request needs to be deleted from the map here too?

}, this._timeoutMs);
}
});
}
}
40 changes: 39 additions & 1 deletion src/ScalarAuthClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,48 @@ class ScalarAuthClient {
return defer.promise;
}

getScalarInterfaceUrlForRoom(roomId, screen, id) {
/**
* Mark all assets associated with the specified widget as "disabled" in the
* integration manager database.
* This can be useful to temporarily prevent purchased assets from being displayed.
* @param {string} widgetType [description]
* @param {string} widgetId [description]
* @return {Promise} Resolves on completion
*/
disableWidgetAssets(widgetType, widgetId) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs on this would be nice - mainly for those implementing the API call.

let url = SdkConfig.get().integrations_rest_url + '/widgets/set_assets_state';
url = this.getStarterLink(url);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this function probably wants renaming?

Copy link
Contributor Author

@rxl881 rxl881 Mar 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed IRL, I'm going to leave this.

return new Promise((resolve, reject) => {
request({
method: 'GET',
uri: url,
json: true,
qs: {
'widget_type': widgetType,
'widget_id': widgetId,
'state': 'disable',
},
}, (err, response, body) => {
if (err) {
reject(err);
} else if (response.statusCode / 100 !== 2) {
reject({statusCode: response.statusCode});
} else if (!body) {
reject(new Error("Failed to set widget assets state"));
} else {
resolve();
}
});
});
}

getScalarInterfaceUrlForRoom(room, screen, id) {
const roomId = room.roomId;
const roomName = room.name;
let url = SdkConfig.get().integrations_ui_url;
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
url += "&room_id=" + encodeURIComponent(roomId);
url += "&room_name=" + encodeURIComponent(roomName);
url += "&theme=" + encodeURIComponent(SettingsStore.getValue("theme"));
if (id) {
url += '&integ_id=' + encodeURIComponent(id);
Expand Down
115 changes: 88 additions & 27 deletions src/ScalarMessaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ const SdkConfig = require('./SdkConfig');
const MatrixClientPeg = require("./MatrixClientPeg");
const MatrixEvent = require("matrix-js-sdk").MatrixEvent;
const dis = require("./dispatcher");
const Widgets = require('./utils/widgets');
import { _t } from './languageHandler';

function sendResponse(event, res) {
Expand Down Expand Up @@ -291,6 +292,7 @@ function setWidget(event, roomId) {
const widgetUrl = event.data.url;
const widgetName = event.data.name; // optional
const widgetData = event.data.data; // optional
const userWidget = event.data.userWidget;

const client = MatrixClientPeg.get();
if (!client) {
Expand Down Expand Up @@ -330,17 +332,51 @@ function setWidget(event, roomId) {
name: widgetName,
data: widgetData,
};
if (widgetUrl === null) { // widget is being deleted
content = {};
}

client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).done(() => {
if (userWidget) {
const client = MatrixClientPeg.get();
const userWidgets = Widgets.getUserWidgets();

// Delete existing widget with ID
try {
delete userWidgets[widgetId];
} catch (e) {
console.error(`$widgetId is non-configurable`);
}

// Add new widget / update
if (widgetUrl !== null) {
userWidgets[widgetId] = {
content: content,
sender: client.getUserId(),
stateKey: widgetId,
type: 'im.vector.modular.widgets',
id: widgetId,
};
}

client.setAccountData('m.widgets', userWidgets);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we wait for this to complete before sending the response?

sendResponse(event, {
success: true,
});
}, (err) => {
sendError(event, _t('Failed to send request.'), err);
});

dis.dispatch({ action: "user_widget_updated" });
} else { // Room widget
if (!roomId) {
sendError(event, _t('Missing roomId.'), null);
}

if (widgetUrl === null) { // widget is being deleted
content = {};
}
client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).done(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per IRL, should this be m.widgets?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned below, no, we're leaving the event type for room widgets as is for the time being. I've added a TODO comment to indicate that this needs updating / fixing in due course.

sendResponse(event, {
success: true,
});
}, (err) => {
sendError(event, _t('Failed to send request.'), err);
});
}
}

function getWidgets(event, roomId) {
Expand All @@ -349,19 +385,28 @@ function getWidgets(event, roomId) {
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
const widgetStateEvents = [];
stateEvents.forEach((ev) => {
if (ev.getContent().type && ev.getContent().url) {
widgetStateEvents.push(ev.event); // return the raw event
let widgetStateEvents = [];

if (roomId) {
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
if (room) {
stateEvents.forEach((ev) => {
if (ev.getContent().type && ev.getContent().url) {
widgetStateEvents.push(ev.event); // return the raw event
}
});
}
}

// Add user widgets (not linked to a specific room)
const userWidgets = Widgets.getUserWidgetsArray();
widgetStateEvents = widgetStateEvents.concat(userWidgets);

sendResponse(event, widgetStateEvents);
}
Expand Down Expand Up @@ -578,9 +623,22 @@ const onMessage = function(event) {

const roomId = event.data.room_id;
const userId = event.data.user_id;

if (!roomId) {
sendError(event, _t('Missing room_id in request'));
return;
// These APIs don't require roomId
// Get and set user widgets (not associated with a specific room)
// If roomId is specified, it must be validated, so room-based widgets agreed
// handled further down.
if (event.data.action === "get_widgets") {
getWidgets(event, null);
return;
} else if (event.data.action === "set_widget") {
setWidget(event, null);
return;
} else {
sendError(event, _t('Missing room_id in request'));
return;
}
}
let promise = Promise.resolve(currentRoomId);
if (!currentRoomId) {
Expand All @@ -601,6 +659,15 @@ const onMessage = function(event) {
return;
}

// Get and set room-based widgets
if (event.data.action === "get_widgets") {
getWidgets(event, roomId);
return;
} else if (event.data.action === "set_widget") {
setWidget(event, roomId);
return;
}

// These APIs don't require userId
if (event.data.action === "join_rules_state") {
getJoinRules(event, roomId);
Expand All @@ -611,12 +678,6 @@ const onMessage = function(event) {
} else if (event.data.action === "get_membership_count") {
getMembershipCount(event, roomId);
return;
} else if (event.data.action === "set_widget") {
setWidget(event, roomId);
return;
} else if (event.data.action === "get_widgets") {
getWidgets(event, roomId);
return;
} else if (event.data.action === "get_room_enc_state") {
getRoomEncState(event, roomId);
return;
Expand Down
Loading