From 5c8eeff08ae2f831d11555c25c906c1b8e8a5628 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 25 Jun 2018 15:30:04 +0100 Subject: [PATCH 01/13] Refactor widgets code Split logic out of setWidget & re-use it when we add jitsi widgets. Also fix the detection of existing jitsi widgets and make that use getRoomWidgets() too. Add error message when a jitsi call is already in process. --- src/CallHandler.js | 34 ++++++++++--------- src/ScalarMessaging.js | 45 ++----------------------- src/WidgetUtils.js | 64 +++++++++++++++++++++++++++++++++++ src/i18n/strings/en_EN.json | 66 +++++++++++++++++++------------------ 4 files changed, 119 insertions(+), 90 deletions(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index a65d82fe85..f431589fb2 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -61,6 +61,8 @@ import Matrix from 'matrix-js-sdk'; import dis from './dispatcher'; import { showUnknownDeviceDialogForCalls } from './cryptodevices'; import SettingsStore from "./settings/SettingsStore"; +import WidgetUtils from './WidgetUtils'; +import { getRoomWidgets } from './utils/widgets'; global.mxCalls = { //room_id: MatrixCall @@ -412,15 +414,20 @@ function _startCallApp(roomId, type) { return; } - const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); - const currentJitsiWidgets = appsStateEvents.filter((ev) => { - ev.getContent().type == 'jitsi'; + const currentJitsiWidgets = getRoomWidgets(room).filter((ev) => { + return ev.getContent().type == 'jitsi'; }); if (currentJitsiWidgets.length > 0) { console.warn( "Refusing to start conference call widget in " + roomId + " a conference call widget is already present", ); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + + Modal.createTrackedDialog('Already have Jitsi Widget', '', ErrorDialog, { + title: _t('Call in Progress'), + description: _t('A call is already in progress!'), + }); return; } @@ -443,25 +450,22 @@ function _startCallApp(roomId, type) { queryString ); - const jitsiEvent = { - type: 'jitsi', - url: widgetUrl, - data: { - widgetSessionId: widgetSessionId, - }, + const widgetData = { + widgetSessionId: widgetSessionId, }; + const widgetId = ( 'jitsi_' + MatrixClientPeg.get().credentials.userId + '_' + Date.now() ); - MatrixClientPeg.get().sendStateEvent( - roomId, - 'im.vector.modular.widgets', - jitsiEvent, - widgetId, - ).then(() => console.log('Sent jitsi widget state event'), (e) => console.error(e)); + + WidgetUtils.setRoomWidget(widgetId, 'jitsi', widgetUrl, 'Jitsi', widgetData, roomId).then(() => { + console.log('Jitsi widget added'); + }).catch((e) => { + console.error(e); + }); } // FIXME: Nasty way of making sure we only register diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index f80162e635..5aae25229a 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -297,12 +297,6 @@ function setWidget(event, roomId) { const widgetData = event.data.data; // optional const userWidget = event.data.userWidget; - const client = MatrixClientPeg.get(); - if (!client) { - sendError(event, _t('You need to be logged in.')); - return; - } - // both adding/removing widgets need these checks if (!widgetId || widgetUrl === undefined) { sendError(event, _t("Unable to create widget."), new Error("Missing required widget fields.")); @@ -337,34 +331,7 @@ function setWidget(event, roomId) { }; 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(), - state_key: widgetId, - type: 'm.widget', - id: widgetId, - }; - } - - // This starts listening for when the echo comes back from the server - // since the widget won't appear added until this happens. If we don't - // wait for this, the action will complete but if the user is fast enough, - // the widget still won't actually be there. - client.setAccountData('m.widgets', userWidgets).then(() => { - return WidgetUtils.waitForUserWidget(widgetId, widgetUrl !== null); - }).then(() => { + WidgetUtils.setUserWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData).then(() => { sendResponse(event, { success: true, }); @@ -377,15 +344,7 @@ function setWidget(event, roomId) { if (!roomId) { sendError(event, _t('Missing roomId.'), null); } - - if (widgetUrl === null) { // widget is being deleted - content = {}; - } - // TODO - Room widgets need to be moved to 'm.widget' state events - // https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing - client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).then(() => { - return WidgetUtils.waitForRoomWidget(widgetId, roomId, widgetUrl !== null); - }).then(() => { + WidgetUtils.setRoomWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData, roomId).then(() => { sendResponse(event, { success: true, }); diff --git a/src/WidgetUtils.js b/src/WidgetUtils.js index 2e2dcf30cd..453038d953 100644 --- a/src/WidgetUtils.js +++ b/src/WidgetUtils.js @@ -17,6 +17,8 @@ limitations under the License. import MatrixClientPeg from './MatrixClientPeg'; import SdkConfig from "./SdkConfig"; +import Widgets from './utils/widgets'; +import dis from './dispatcher'; import * as url from "url"; export default class WidgetUtils { @@ -190,4 +192,66 @@ export default class WidgetUtils { MatrixClientPeg.get().on('RoomState.events', onRoomStateEvents); }); } + + static setUserWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData) { + const content = { + type: widgetType, + url: widgetUrl, + name: widgetName, + data: widgetData, + }; + + 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(), + state_key: widgetId, + type: 'm.widget', + id: widgetId, + }; + } + + // This starts listening for when the echo comes back from the server + // since the widget won't appear added until this happens. If we don't + // wait for this, the action will complete but if the user is fast enough, + // the widget still won't actually be there. + return client.setAccountData('m.widgets', userWidgets).then(() => { + return WidgetUtils.waitForUserWidget(widgetId, widgetUrl !== null); + }).then(() => { + dis.dispatch({ action: "user_widget_updated" }); + }); + } + + static setRoomWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData, roomId) { + let content; + + if (widgetUrl === null) { // widget is being deleted + content = {}; + } else { + content = { + type: widgetType, + url: widgetUrl, + name: widgetName, + data: widgetData, + }; + } + + const client = MatrixClientPeg.get(); + // TODO - Room widgets need to be moved to 'm.widget' state events + // https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing + return client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).then(() => { + return WidgetUtils.waitForRoomWidget(widgetId, roomId, widgetUrl !== null); + }); + } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d35555af6f..9da6bab5fa 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -39,13 +39,11 @@ "Conference calling is in development and may not be reliable.": "Conference calling is in development and may not be reliable.", "Failed to set up conference call": "Failed to set up conference call", "Conference call failed.": "Conference call failed.", + "Call in Progress": "Call in Progress", + "A call is already in progress!": "A call is already in progress!", "The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload", "The file '%(fileName)s' exceeds this home server's size limit for uploads": "The file '%(fileName)s' exceeds this home server's size limit for uploads", "Upload Failed": "Upload Failed", - "Failure to create room": "Failure to create room", - "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", - "Send anyway": "Send anyway", - "Send": "Send", "Sun": "Sun", "Mon": "Mon", "Tue": "Tue", @@ -85,7 +83,6 @@ "Failed to invite users to community": "Failed to invite users to community", "Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s", "Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:", - "Unnamed Room": "Unnamed Room", "Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings", "Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again", "Unable to enable Notifications": "Unable to enable Notifications", @@ -199,6 +196,11 @@ "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing", "%(names)s and %(count)s others are typing|one": "%(names)s and one other is typing", "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", + "Failure to create room": "Failure to create room", + "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", + "Send anyway": "Send anyway", + "Send": "Send", + "Unnamed Room": "Unnamed Room", "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", "Not a valid Riot keyfile": "Not a valid Riot keyfile", "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", @@ -311,31 +313,6 @@ "Off": "Off", "On": "On", "Noisy": "Noisy", - "Invalid alias format": "Invalid alias format", - "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", - "Invalid address format": "Invalid address format", - "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address", - "not specified": "not specified", - "not set": "not set", - "Remote addresses for this room:": "Remote addresses for this room:", - "Addresses": "Addresses", - "The main address for this room is": "The main address for this room is", - "Local addresses for this room:": "Local addresses for this room:", - "This room has no local addresses": "This room has no local addresses", - "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", - "Invalid community ID": "Invalid community ID", - "'%(groupId)s' is not a valid community ID": "'%(groupId)s' is not a valid community ID", - "Flair": "Flair", - "Showing flair for these communities:": "Showing flair for these communities:", - "This room is not showing flair for any communities": "This room is not showing flair for any communities", - "New community ID (e.g. +foo:%(localDomain)s)": "New community ID (e.g. +foo:%(localDomain)s)", - "You have enabled URL previews by default.": "You have enabled URL previews by default.", - "You have disabled URL previews by default.": "You have disabled URL previews by default.", - "URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.", - "URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.", - "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.", - "URL Previews": "URL Previews", - "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.", "Cannot add any more widgets": "Cannot add any more widgets", "The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.", "Add a widget": "Add a widget", @@ -435,11 +412,11 @@ "numbullet": "numbullet", "Markdown is disabled": "Markdown is disabled", "Markdown is enabled": "Markdown is enabled", + "Unpin Message": "Unpin Message", + "Jump to message": "Jump to message", "No pinned messages.": "No pinned messages.", "Loading...": "Loading...", "Pinned Messages": "Pinned Messages", - "Unpin Message": "Unpin Message", - "Jump to message": "Jump to message", "%(duration)ss": "%(duration)ss", "%(duration)sm": "%(duration)sm", "%(duration)sh": "%(duration)sh", @@ -572,6 +549,31 @@ "Scroll to unread messages": "Scroll to unread messages", "Jump to first unread message.": "Jump to first unread message.", "Close": "Close", + "Invalid alias format": "Invalid alias format", + "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", + "Invalid address format": "Invalid address format", + "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address", + "not specified": "not specified", + "not set": "not set", + "Remote addresses for this room:": "Remote addresses for this room:", + "Addresses": "Addresses", + "The main address for this room is": "The main address for this room is", + "Local addresses for this room:": "Local addresses for this room:", + "This room has no local addresses": "This room has no local addresses", + "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", + "Invalid community ID": "Invalid community ID", + "'%(groupId)s' is not a valid community ID": "'%(groupId)s' is not a valid community ID", + "Flair": "Flair", + "Showing flair for these communities:": "Showing flair for these communities:", + "This room is not showing flair for any communities": "This room is not showing flair for any communities", + "New community ID (e.g. +foo:%(localDomain)s)": "New community ID (e.g. +foo:%(localDomain)s)", + "You have enabled URL previews by default.": "You have enabled URL previews by default.", + "You have disabled URL previews by default.": "You have disabled URL previews by default.", + "URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.", + "URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.", + "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.", + "URL Previews": "URL Previews", + "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.", "Sunday": "Sunday", "Monday": "Monday", "Tuesday": "Tuesday", From 069080e7edefba251892e7f854b38578107ec5ce Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 09:41:20 +0100 Subject: [PATCH 02/13] lint --- src/ScalarMessaging.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 5aae25229a..9d0e274c7b 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -323,13 +323,6 @@ function setWidget(event, roomId) { } } - let content = { - type: widgetType, - url: widgetUrl, - name: widgetName, - data: widgetData, - }; - if (userWidget) { WidgetUtils.setUserWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData).then(() => { sendResponse(event, { From 0f2c47937cb305831e54429e5d3b713a884a64cb Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 11:52:21 +0100 Subject: [PATCH 03/13] Merge the two different widget utils files --- src/CallHandler.js | 3 +- src/ScalarMessaging.js | 3 +- src/WidgetUtils.js | 82 ++++++++++++++++++- src/components/views/rooms/Stickerpicker.js | 6 +- src/utils/widgets.js | 90 --------------------- 5 files changed, 85 insertions(+), 99 deletions(-) delete mode 100644 src/utils/widgets.js diff --git a/src/CallHandler.js b/src/CallHandler.js index f431589fb2..852827f6fe 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -62,7 +62,6 @@ import dis from './dispatcher'; import { showUnknownDeviceDialogForCalls } from './cryptodevices'; import SettingsStore from "./settings/SettingsStore"; import WidgetUtils from './WidgetUtils'; -import { getRoomWidgets } from './utils/widgets'; global.mxCalls = { //room_id: MatrixCall @@ -414,7 +413,7 @@ function _startCallApp(roomId, type) { return; } - const currentJitsiWidgets = getRoomWidgets(room).filter((ev) => { + const currentJitsiWidgets = WidgetUtils.getRoomWidgets(room).filter((ev) => { return ev.getContent().type == 'jitsi'; }); if (currentJitsiWidgets.length > 0) { diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 9d0e274c7b..24c69194f5 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -236,7 +236,6 @@ import SdkConfig from './SdkConfig'; import MatrixClientPeg from './MatrixClientPeg'; import { MatrixEvent } from 'matrix-js-sdk'; import dis from './dispatcher'; -import Widgets from './utils/widgets'; import WidgetUtils from './WidgetUtils'; import RoomViewStore from './stores/RoomViewStore'; import { _t } from './languageHandler'; @@ -375,7 +374,7 @@ function getWidgets(event, roomId) { } // Add user widgets (not linked to a specific room) - const userWidgets = Widgets.getUserWidgetsArray(); + const userWidgets = WidgetUtils.getUserWidgetsArray(); widgetStateEvents = widgetStateEvents.concat(userWidgets); sendResponse(event, widgetStateEvents); diff --git a/src/WidgetUtils.js b/src/WidgetUtils.js index 453038d953..f54419f5e2 100644 --- a/src/WidgetUtils.js +++ b/src/WidgetUtils.js @@ -17,7 +17,6 @@ limitations under the License. import MatrixClientPeg from './MatrixClientPeg'; import SdkConfig from "./SdkConfig"; -import Widgets from './utils/widgets'; import dis from './dispatcher'; import * as url from "url"; @@ -202,7 +201,7 @@ export default class WidgetUtils { }; const client = MatrixClientPeg.get(); - const userWidgets = Widgets.getUserWidgets(); + const userWidgets = WidgetUtils.getUserWidgets(); // Delete existing widget with ID try { @@ -254,4 +253,83 @@ export default class WidgetUtils { return WidgetUtils.waitForRoomWidget(widgetId, roomId, widgetUrl !== null); }); } + + /** + * Get all widgets (user and room) for the current user + * @param {object} room The room to get widgets for + * @return {[object]} Array containing current / active room and user widget state events + */ + static getWidgets(room) { + const widgets = getRoomWidgets(room); + widgets.concat(getUserWidgetsArray()); + return widgets; + } + + /** + * Get room specific widgets + * @param {object} room The room to get widgets force + * @return {[object]} Array containing current / active room widgets + */ + static getRoomWidgets(room) { + const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); + if (!appsStateEvents) { + return []; + } + + return appsStateEvents.filter((ev) => { + return ev.getContent().type && ev.getContent().url; + }); + } + + /** + * Get user specific widgets (not linked to a specific room) + * @return {object} Event content object containing current / active user widgets + */ + static getUserWidgets() { + const client = MatrixClientPeg.get(); + if (!client) { + throw new Error('User not logged in'); + } + const userWidgets = client.getAccountData('m.widgets'); + let userWidgetContent = {}; + if (userWidgets && userWidgets.getContent()) { + userWidgetContent = userWidgets.getContent(); + } + return userWidgetContent; + } + + /** + * Get user specific widgets (not linked to a specific room) as an array + * @return {[object]} Array containing current / active user widgets + */ + static getUserWidgetsArray() { + return Object.values(WidgetUtils.getUserWidgets()); + } + + /** + * Get active stickerpicker widgets (stickerpickers are user widgets by nature) + * @return {[object]} Array containing current / active stickerpicker widgets + */ + static getStickerpickerWidgets() { + const widgets = WidgetUtils.getUserWidgetsArray(); + return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker"); + } + + /** + * Remove all stickerpicker widgets (stickerpickers are user widgets by nature) + * @return {Promise} Resolves on account data updated + */ + static removeStickerpickerWidgets() { + const client = MatrixClientPeg.get(); + if (!client) { + throw new Error('User not logged in'); + } + const userWidgets = client.getAccountData('m.widgets').getContent() || {}; + Object.entries(userWidgets).forEach(([key, widget]) => { + if (widget.content && widget.content.type === 'm.stickerpicker') { + delete userWidgets[key]; + } + }); + return client.setAccountData('m.widgets', userWidgets); + } } diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index 6152809c1a..992e5b7c53 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -15,7 +15,6 @@ limitations under the License. */ import React from 'react'; import { _t } from '../../../languageHandler'; -import Widgets from '../../../utils/widgets'; import AppTile from '../elements/AppTile'; import MatrixClientPeg from '../../../MatrixClientPeg'; import Modal from '../../../Modal'; @@ -24,6 +23,7 @@ import SdkConfig from '../../../SdkConfig'; import ScalarAuthClient from '../../../ScalarAuthClient'; import dis from '../../../dispatcher'; import AccessibleButton from '../elements/AccessibleButton'; +import WidgetUtils from '../../../WidgetUtils'; const widgetType = 'm.stickerpicker'; @@ -67,7 +67,7 @@ export default class Stickerpicker extends React.Component { } this.setState({showStickers: false}); - Widgets.removeStickerpickerWidgets().then(() => { + WidgetUtils.removeStickerpickerWidgets().then(() => { this.forceUpdate(); }).catch((e) => { console.error('Failed to remove sticker picker widget', e); @@ -119,7 +119,7 @@ export default class Stickerpicker extends React.Component { } _updateWidget() { - const stickerpickerWidget = Widgets.getStickerpickerWidgets()[0]; + const stickerpickerWidget = WidgetUtils.getStickerpickerWidgets()[0]; this.setState({ stickerpickerWidget, widgetId: stickerpickerWidget ? stickerpickerWidget.id : null, diff --git a/src/utils/widgets.js b/src/utils/widgets.js deleted file mode 100644 index 338df184e2..0000000000 --- a/src/utils/widgets.js +++ /dev/null @@ -1,90 +0,0 @@ -import MatrixClientPeg from '../MatrixClientPeg'; - -/** - * Get all widgets (user and room) for the current user - * @param {object} room The room to get widgets for - * @return {[object]} Array containing current / active room and user widget state events - */ -function getWidgets(room) { - const widgets = getRoomWidgets(room); - widgets.concat(getUserWidgetsArray()); - return widgets; -} - -/** - * Get room specific widgets - * @param {object} room The room to get widgets force - * @return {[object]} Array containing current / active room widgets - */ -function getRoomWidgets(room) { - const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); - if (!appsStateEvents) { - return []; - } - - return appsStateEvents.filter((ev) => { - return ev.getContent().type && ev.getContent().url; - }); -} - -/** - * Get user specific widgets (not linked to a specific room) - * @return {object} Event content object containing current / active user widgets - */ -function getUserWidgets() { - const client = MatrixClientPeg.get(); - if (!client) { - throw new Error('User not logged in'); - } - const userWidgets = client.getAccountData('m.widgets'); - let userWidgetContent = {}; - if (userWidgets && userWidgets.getContent()) { - userWidgetContent = userWidgets.getContent(); - } - return userWidgetContent; -} - -/** - * Get user specific widgets (not linked to a specific room) as an array - * @return {[object]} Array containing current / active user widgets - */ -function getUserWidgetsArray() { - return Object.values(getUserWidgets()); -} - -/** - * Get active stickerpicker widgets (stickerpickers are user widgets by nature) - * @return {[object]} Array containing current / active stickerpicker widgets - */ -function getStickerpickerWidgets() { - const widgets = getUserWidgetsArray(); - return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker"); -} - -/** - * Remove all stickerpicker widgets (stickerpickers are user widgets by nature) - * @return {Promise} Resolves on account data updated - */ -function removeStickerpickerWidgets() { - const client = MatrixClientPeg.get(); - if (!client) { - throw new Error('User not logged in'); - } - const userWidgets = client.getAccountData('m.widgets').getContent() || {}; - Object.entries(userWidgets).forEach(([key, widget]) => { - if (widget.content && widget.content.type === 'm.stickerpicker') { - delete userWidgets[key]; - } - }); - return client.setAccountData('m.widgets', userWidgets); -} - - -export default { - getWidgets, - getRoomWidgets, - getUserWidgets, - getUserWidgetsArray, - getStickerpickerWidgets, - removeStickerpickerWidgets, -}; From 6cf11a15e62d8864a351fc1bc76fd5b4e8f4b75c Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 11:59:16 +0100 Subject: [PATCH 04/13] Move WidgetUtils to utils dir --- src/CallHandler.js | 2 +- src/ScalarMessaging.js | 2 +- src/components/views/elements/AppPermission.js | 2 +- src/components/views/elements/AppTile.js | 2 +- src/components/views/rooms/AppsDrawer.js | 2 +- src/components/views/rooms/Stickerpicker.js | 2 +- src/{ => utils}/WidgetUtils.js | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) rename src/{ => utils}/WidgetUtils.js (99%) diff --git a/src/CallHandler.js b/src/CallHandler.js index 852827f6fe..05aa9d974f 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -61,7 +61,7 @@ import Matrix from 'matrix-js-sdk'; import dis from './dispatcher'; import { showUnknownDeviceDialogForCalls } from './cryptodevices'; import SettingsStore from "./settings/SettingsStore"; -import WidgetUtils from './WidgetUtils'; +import WidgetUtils from './utils/WidgetUtils'; global.mxCalls = { //room_id: MatrixCall diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 24c69194f5..fb5ea9f76d 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -236,7 +236,7 @@ import SdkConfig from './SdkConfig'; import MatrixClientPeg from './MatrixClientPeg'; import { MatrixEvent } from 'matrix-js-sdk'; import dis from './dispatcher'; -import WidgetUtils from './WidgetUtils'; +import WidgetUtils from './utils/WidgetUtils'; import RoomViewStore from './stores/RoomViewStore'; import { _t } from './languageHandler'; diff --git a/src/components/views/elements/AppPermission.js b/src/components/views/elements/AppPermission.js index 231ed52364..6b4536b620 100644 --- a/src/components/views/elements/AppPermission.js +++ b/src/components/views/elements/AppPermission.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import url from 'url'; import { _t } from '../../../languageHandler'; -import WidgetUtils from "../../../WidgetUtils"; +import WidgetUtils from "../../../utils/WidgetUtils"; export default class AppPermission extends React.Component { constructor(props) { diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 70b5bd651e..61d5a0f1ac 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -31,7 +31,7 @@ import sdk from '../../../index'; import AppPermission from './AppPermission'; import AppWarning from './AppWarning'; import MessageSpinner from './MessageSpinner'; -import WidgetUtils from '../../../WidgetUtils'; +import WidgetUtils from '../../../utils/WidgetUtils'; import dis from '../../../dispatcher'; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index f0b7eaa1d7..ecf9859d2b 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -27,7 +27,7 @@ import SdkConfig from '../../../SdkConfig'; import ScalarAuthClient from '../../../ScalarAuthClient'; import ScalarMessaging from '../../../ScalarMessaging'; import { _t } from '../../../languageHandler'; -import WidgetUtils from '../../../WidgetUtils'; +import WidgetUtils from '../../../utils/WidgetUtils'; import SettingsStore from "../../../settings/SettingsStore"; // The maximum number of widgets that can be added in a room diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index 992e5b7c53..4cb7c59ce6 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -23,7 +23,7 @@ import SdkConfig from '../../../SdkConfig'; import ScalarAuthClient from '../../../ScalarAuthClient'; import dis from '../../../dispatcher'; import AccessibleButton from '../elements/AccessibleButton'; -import WidgetUtils from '../../../WidgetUtils'; +import WidgetUtils from '../../../utils/WidgetUtils'; const widgetType = 'm.stickerpicker'; diff --git a/src/WidgetUtils.js b/src/utils/WidgetUtils.js similarity index 99% rename from src/WidgetUtils.js rename to src/utils/WidgetUtils.js index f54419f5e2..933609b302 100644 --- a/src/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -15,9 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import MatrixClientPeg from './MatrixClientPeg'; -import SdkConfig from "./SdkConfig"; -import dis from './dispatcher'; +import MatrixClientPeg from '../MatrixClientPeg'; +import SdkConfig from "../SdkConfig"; +import dis from '../dispatcher'; import * as url from "url"; export default class WidgetUtils { From ca8a4a0c3f137520718e6dd645700abd8e3f0fc6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 12:08:33 +0100 Subject: [PATCH 05/13] Remove unused function --- src/utils/WidgetUtils.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 933609b302..b9cc2bae13 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -254,17 +254,6 @@ export default class WidgetUtils { }); } - /** - * Get all widgets (user and room) for the current user - * @param {object} room The room to get widgets for - * @return {[object]} Array containing current / active room and user widget state events - */ - static getWidgets(room) { - const widgets = getRoomWidgets(room); - widgets.concat(getUserWidgetsArray()); - return widgets; - } - /** * Get room specific widgets * @param {object} room The room to get widgets force From b6f3b2f594abfde62870f78cb1ad058ee5f8cc41 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 12:28:59 +0100 Subject: [PATCH 06/13] More code re-use --- src/ScalarMessaging.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index fb5ea9f76d..41224c266f 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -360,17 +360,9 @@ function getWidgets(event, roomId) { sendError(event, _t('This room is not recognised.')); return; } - // TODO - Room widgets need to be moved to 'm.widget' state events - // https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing - 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 - } - }); - } + // XXX: This gets the raw event object (I think because we can't + // send the MatrixEvent over postMessage?) + widgetStateEvents = WidgetUtils.getRoomWidgets(room).map((ev) => ev.event); } // Add user widgets (not linked to a specific room) From 0a6450507eb70ea80e856a26183e02e6503da4bc Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 15:21:22 +0100 Subject: [PATCH 07/13] Bits of PR feedback --- src/CallHandler.js | 6 ++---- src/utils/WidgetUtils.js | 16 ++++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index 05aa9d974f..b470585f36 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -414,7 +414,7 @@ function _startCallApp(roomId, type) { } const currentJitsiWidgets = WidgetUtils.getRoomWidgets(room).filter((ev) => { - return ev.getContent().type == 'jitsi'; + return ev.getContent().type === 'jitsi'; }); if (currentJitsiWidgets.length > 0) { console.warn( @@ -449,9 +449,7 @@ function _startCallApp(roomId, type) { queryString ); - const widgetData = { - widgetSessionId: widgetSessionId, - }; + const widgetData = { widgetSessionId }; const widgetId = ( 'jitsi_' + diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index b9cc2bae13..724fd490a4 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -210,8 +210,10 @@ export default class WidgetUtils { console.error(`$widgetId is non-configurable`); } + const addingWidget = widgetUrl !== null; + // Add new widget / update - if (widgetUrl !== null) { + if (addingWidget) { userWidgets[widgetId] = { content: content, sender: client.getUserId(), @@ -226,7 +228,7 @@ export default class WidgetUtils { // wait for this, the action will complete but if the user is fast enough, // the widget still won't actually be there. return client.setAccountData('m.widgets', userWidgets).then(() => { - return WidgetUtils.waitForUserWidget(widgetId, widgetUrl !== null); + return WidgetUtils.waitForUserWidget(widgetId, addingWidget); }).then(() => { dis.dispatch({ action: "user_widget_updated" }); }); @@ -235,22 +237,24 @@ export default class WidgetUtils { static setRoomWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData, roomId) { let content; - if (widgetUrl === null) { // widget is being deleted - content = {}; - } else { + const addingWidget = widgetUrl !== null; + + if (addingWidget) { content = { type: widgetType, url: widgetUrl, name: widgetName, data: widgetData, }; + } else { + content = {}; } const client = MatrixClientPeg.get(); // TODO - Room widgets need to be moved to 'm.widget' state events // https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing return client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).then(() => { - return WidgetUtils.waitForRoomWidget(widgetId, roomId, widgetUrl !== null); + return WidgetUtils.waitForRoomWidget(widgetId, roomId, addingWidget); }); } From 4a39297e65efc3effc329dc6a64f21b0cbc9ec2c Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 15:41:43 +0100 Subject: [PATCH 08/13] PR feedback --- src/utils/WidgetUtils.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 724fd490a4..69a840ce55 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -284,11 +284,10 @@ export default class WidgetUtils { throw new Error('User not logged in'); } const userWidgets = client.getAccountData('m.widgets'); - let userWidgetContent = {}; if (userWidgets && userWidgets.getContent()) { - userWidgetContent = userWidgets.getContent(); + return userWidgets.getContent(); } - return userWidgetContent; + return {}; } /** From cb89406d3e25cce745e60155703717a38110ac69 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 15:42:29 +0100 Subject: [PATCH 09/13] Indent --- src/utils/WidgetUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 69a840ce55..924500b97d 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -295,7 +295,7 @@ export default class WidgetUtils { * @return {[object]} Array containing current / active user widgets */ static getUserWidgetsArray() { - return Object.values(WidgetUtils.getUserWidgets()); + return Object.values(WidgetUtils.getUserWidgets()); } /** From 9539a749e544d40bb816580ceaa3e1ee7e74e323 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 16:03:58 +0100 Subject: [PATCH 10/13] More refactoring --- src/components/structures/RoomView.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 8f03199a62..98d700e0a0 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -46,6 +46,7 @@ import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; import RoomViewStore from '../../stores/RoomViewStore'; import RoomScrollStateStore from '../../stores/RoomScrollStateStore'; import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; +import WidgetUtils from '../../utils/WidgetUtils'; const DEBUG = false; let debuglog = function() {}; @@ -318,14 +319,7 @@ module.exports = React.createClass({ return false; } - 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; + return WidgetUtils.getRoomWidgets(room).length > 0; }, componentDidMount: function() { From a88297d7a6eeadaecab0abf15a059c6255cf17d5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 16:27:17 +0100 Subject: [PATCH 11/13] More code reuse --- src/components/views/elements/AppTile.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 61d5a0f1ac..ea303a96b2 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -319,14 +319,15 @@ export default class AppTile extends React.Component { return; } this.setState({deleting: true}); - MatrixClientPeg.get().sendStateEvent( - this.props.room.roomId, - 'im.vector.modular.widgets', - {}, // empty content + + WidgetUtils.setRoomWidget( this.props.id, - ).then(() => { - return WidgetUtils.waitForRoomWidget(this.props.id, this.props.room.roomId, false); - }).catch((e) => { + null, + null, + null, + null, + this.props.room.roomId, + ).catch((e) => { console.error('Failed to delete widget', e); }).finally(() => { this.setState({deleting: false}); From c780cd05a8b918cc91926a697faf374eaa55da7b Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 16:33:28 +0100 Subject: [PATCH 12/13] Change argument order of setRoomWidget So when deleting a widget we can just omit the ones we don't need --- src/CallHandler.js | 2 +- src/ScalarMessaging.js | 2 +- src/components/views/elements/AppTile.js | 6 +----- src/utils/WidgetUtils.js | 6 +++--- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index b470585f36..7403483e36 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -458,7 +458,7 @@ function _startCallApp(roomId, type) { Date.now() ); - WidgetUtils.setRoomWidget(widgetId, 'jitsi', widgetUrl, 'Jitsi', widgetData, roomId).then(() => { + WidgetUtils.setRoomWidget(roomId, widgetId, 'jitsi', widgetUrl, 'Jitsi', widgetData).then(() => { console.log('Jitsi widget added'); }).catch((e) => { console.error(e); diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 41224c266f..3325044b84 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -336,7 +336,7 @@ function setWidget(event, roomId) { if (!roomId) { sendError(event, _t('Missing roomId.'), null); } - WidgetUtils.setRoomWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData, roomId).then(() => { + WidgetUtils.setRoomWidget(roomId, widgetId, widgetType, widgetUrl, widgetName, widgetData).then(() => { sendResponse(event, { success: true, }); diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index ea303a96b2..7b69057e3e 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -321,12 +321,8 @@ export default class AppTile extends React.Component { this.setState({deleting: true}); WidgetUtils.setRoomWidget( - this.props.id, - null, - null, - null, - null, this.props.room.roomId, + this.props.id, ).catch((e) => { console.error('Failed to delete widget', e); }).finally(() => { diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 924500b97d..ab5b5b0130 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -210,7 +210,7 @@ export default class WidgetUtils { console.error(`$widgetId is non-configurable`); } - const addingWidget = widgetUrl !== null; + const addingWidget = Boolean(widgetUrl); // Add new widget / update if (addingWidget) { @@ -234,10 +234,10 @@ export default class WidgetUtils { }); } - static setRoomWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData, roomId) { + static setRoomWidget(roomId, widgetId, widgetType, widgetUrl, widgetName, widgetData) { let content; - const addingWidget = widgetUrl !== null; + const addingWidget = Boolean(widgetUrl); if (addingWidget) { content = { From 0ee8d18f5aff89a8dabb4c1ca5a2ae1cf6ce179e Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jun 2018 16:41:44 +0100 Subject: [PATCH 13/13] More code de-duping --- src/components/views/rooms/AppsDrawer.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index ecf9859d2b..04e47f0f9f 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -163,14 +163,7 @@ module.exports = React.createClass({ }, _getApps: function() { - const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets'); - if (!appsStateEvents) { - return []; - } - - return appsStateEvents.filter((ev) => { - return ev.getContent().type && ev.getContent().url; - }).map((ev) => { + return WidgetUtils.getRoomWidgets(this.props.room).map((ev) => { return this._initAppConfig(ev.getStateKey(), ev.getContent(), ev.sender); }); },