From 12034411d4481652d840b0ee34330091b2f29a82 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Sep 2020 10:47:51 +0100 Subject: [PATCH 01/14] Fix right-alignment between sticky and non-sticky room list headers Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LeftPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index 090a64904c..b47d328965 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -139,7 +139,7 @@ export default class LeftPanel extends React.Component { const bottomEdge = list.offsetHeight + list.scrollTop; const sublists = list.querySelectorAll(".mx_RoomSublist"); - const headerRightMargin = 16; // calculated from margins and widths to align with non-sticky tiles + const headerRightMargin = 15; // calculated from margins and widths to align with non-sticky tiles const headerStickyWidth = list.clientWidth - headerRightMargin; // We track which styles we want on a target before making the changes to avoid From 7fd6d4498fa2279c2027e067bc2e61d1d2e2d17f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Sep 2020 11:00:19 +0100 Subject: [PATCH 02/14] Make bottom sticky room list header dynamic Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/rooms/_RoomSublist.scss | 4 ---- src/components/structures/LeftPanel.tsx | 9 +++++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist.scss b/res/css/views/rooms/_RoomSublist.scss index 543940fb78..82bba40167 100644 --- a/res/css/views/rooms/_RoomSublist.scss +++ b/res/css/views/rooms/_RoomSublist.scss @@ -59,10 +59,6 @@ limitations under the License. width: calc(100% - 22px); } - &.mx_RoomSublist_headerContainer_stickyBottom { - bottom: 0; - } - // We don't have a top style because the top is dependent on the room list header's // height, and is therefore calculated in JS. // The class, mx_RoomSublist_headerContainer_stickyTop, is applied though. diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index b47d328965..455e807bc9 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -210,10 +210,19 @@ export default class LeftPanel extends React.Component { if (!header.classList.contains("mx_RoomSublist_headerContainer_stickyBottom")) { header.classList.add("mx_RoomSublist_headerContainer_stickyBottom"); } + + const offset = window.innerHeight - (list.parentElement.offsetTop + list.parentElement.offsetHeight); + const newBottom = `${offset}px`; + if (header.style.bottom !== newBottom) { + header.style.bottom = newBottom; + } } else { if (header.classList.contains("mx_RoomSublist_headerContainer_stickyBottom")) { header.classList.remove("mx_RoomSublist_headerContainer_stickyBottom"); } + if (header.style.bottom) { + header.style.removeProperty('bottom'); + } } if (style.stickyTop || style.stickyBottom) { From c8b99b54e0f1161a91cfba9d39bc1b87caad2c85 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Sep 2020 11:00:53 +0100 Subject: [PATCH 03/14] Fix TS definitions Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/accessibility/RovingTabIndex.tsx | 2 +- src/hooks/useLocalStorageState.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/accessibility/RovingTabIndex.tsx b/src/accessibility/RovingTabIndex.tsx index b1dbb56a01..bde626a96a 100644 --- a/src/accessibility/RovingTabIndex.tsx +++ b/src/accessibility/RovingTabIndex.tsx @@ -204,7 +204,7 @@ export const RovingTabIndexProvider: React.FC = ({children, handleHomeEn // onFocus should be called when the index gained focus in any manner // isActive should be used to set tabIndex in a manner such as `tabIndex={isActive ? 0 : -1}` // ref should be passed to a DOM node which will be used for DOM compareDocumentPosition -export const useRovingTabIndex = (inputRef: Ref): [FocusHandler, boolean, Ref] => { +export const useRovingTabIndex = (inputRef?: Ref): [FocusHandler, boolean, Ref] => { const context = useContext(RovingTabIndexContext); let ref = useRef(null); diff --git a/src/hooks/useLocalStorageState.ts b/src/hooks/useLocalStorageState.ts index ce3b574f86..943db9ab4e 100644 --- a/src/hooks/useLocalStorageState.ts +++ b/src/hooks/useLocalStorageState.ts @@ -26,7 +26,7 @@ const getValue = (key: string, initialValue: T): T => { }; // Hook behaving like useState but persisting the value to localStorage. Returns same as useState -export const useLocalStorageState = (key: string, initialValue: T) => { +export const useLocalStorageState = (key: string, initialValue: T): [T, Dispatch>] => { const lsKey = "mx_" + key; const [value, setValue] = useState(getValue(lsKey, initialValue)); From 2e6bad8b07571aac2e304c3cebd61fa9f47991a8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Sep 2020 11:01:19 +0100 Subject: [PATCH 04/14] Convert WidgetUtils to TS and improve typing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/WidgetStore.ts | 5 ++ src/utils/{WidgetUtils.js => WidgetUtils.ts} | 71 +++++++++++--------- 2 files changed, 46 insertions(+), 30 deletions(-) rename src/utils/{WidgetUtils.js => WidgetUtils.ts} (90%) diff --git a/src/stores/WidgetStore.ts b/src/stores/WidgetStore.ts index 10327ce4e9..4e6e5e626c 100644 --- a/src/stores/WidgetStore.ts +++ b/src/stores/WidgetStore.ts @@ -31,11 +31,16 @@ interface IState {} export interface IApp { id: string; + url: string; type: string; + name: string; roomId: string; eventId: string; creatorUserId: string; waitForIframeLoad?: boolean; + data?: { + title?: string; + }; // eslint-disable-next-line camelcase avatar_url: string; // MSC2765 https://github.com/matrix-org/matrix-doc/pull/2765 } diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.ts similarity index 90% rename from src/utils/WidgetUtils.js rename to src/utils/WidgetUtils.ts index d1daba7ca5..efdfbbb3cc 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.ts @@ -2,6 +2,7 @@ Copyright 2017 Vector Creations Ltd Copyright 2018 New Vector Ltd Copyright 2019 Travis Ralston +Copyright 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,15 +17,12 @@ See the License for the specific language governing permissions and limitations under the License. */ +import * as url from "url"; + import {MatrixClientPeg} from '../MatrixClientPeg'; import SdkConfig from "../SdkConfig"; import dis from '../dispatcher/dispatcher'; -import * as url from "url"; import WidgetEchoStore from '../stores/WidgetEchoStore'; - -// How long we wait for the state event echo to come back from the server -// before waitFor[Room/User]Widget rejects its promise -const WIDGET_WAIT_TIME = 20000; import SettingsStore from "../settings/SettingsStore"; import ActiveWidgetStore from "../stores/ActiveWidgetStore"; import {IntegrationManagers} from "../integrations/IntegrationManagers"; @@ -33,6 +31,19 @@ import {Room} from "matrix-js-sdk/src/models/room"; import {WidgetType} from "../widgets/WidgetType"; import {objectClone} from "./objects"; import {_t} from "../languageHandler"; +import {IApp} from "../stores/WidgetStore"; + +// How long we wait for the state event echo to come back from the server +// before waitFor[Room/User]Widget rejects its promise +const WIDGET_WAIT_TIME = 20000; + +export interface IWidget { + id: string; + type: string; + sender: string; + state_key: string; + content: IApp; +} export default class WidgetUtils { /* Returns true if user is able to send state events to modify widgets in this room @@ -41,7 +52,7 @@ export default class WidgetUtils { * @return Boolean -- true if the user can modify widgets in this room * @throws Error -- specifies the error reason */ - static canUserModifyWidgets(roomId) { + static canUserModifyWidgets(roomId: string): boolean { if (!roomId) { console.warn('No room ID specified'); return false; @@ -80,7 +91,7 @@ export default class WidgetUtils { * @param {[type]} testUrlString URL to check * @return {Boolean} True if specified URL is a scalar URL */ - static isScalarUrl(testUrlString) { + static isScalarUrl(testUrlString: string): boolean { if (!testUrlString) { console.error('Scalar URL check failed. No URL specified'); return false; @@ -123,7 +134,7 @@ export default class WidgetUtils { * @returns {Promise} that resolves when the widget is in the * requested state according to the `add` param */ - static waitForUserWidget(widgetId, add) { + static waitForUserWidget(widgetId: string, add: boolean): Promise { 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 @@ -170,7 +181,7 @@ export default class WidgetUtils { * @returns {Promise} that resolves when the widget is in the * requested state according to the `add` param */ - static waitForRoomWidget(widgetId, roomId, add) { + static waitForRoomWidget(widgetId: string, roomId: string, add: boolean): Promise { 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 @@ -213,7 +224,7 @@ export default class WidgetUtils { }); } - static setUserWidget(widgetId, widgetType: WidgetType, widgetUrl, widgetName, widgetData) { + static setUserWidget(widgetId: string, widgetType: WidgetType, widgetUrl: string, widgetName: string, widgetData: object) { const content = { type: widgetType.preferred, url: widgetUrl, @@ -257,7 +268,7 @@ export default class WidgetUtils { }); } - static setRoomWidget(roomId, widgetId, widgetType: WidgetType, widgetUrl, widgetName, widgetData) { + static setRoomWidget(roomId: string, widgetId: string, widgetType: WidgetType, widgetUrl: string, widgetName: string, widgetData: object) { let content; const addingWidget = Boolean(widgetUrl); @@ -307,7 +318,7 @@ export default class WidgetUtils { * Get user specific widgets (not linked to a specific room) * @return {object} Event content object containing current / active user widgets */ - static getUserWidgets() { + static getUserWidgets(): Record { const client = MatrixClientPeg.get(); if (!client) { throw new Error('User not logged in'); @@ -323,7 +334,7 @@ export default class WidgetUtils { * Get user specific widgets (not linked to a specific room) as an array * @return {[object]} Array containing current / active user widgets */ - static getUserWidgetsArray() { + static getUserWidgetsArray(): IWidget[] { return Object.values(WidgetUtils.getUserWidgets()); } @@ -331,7 +342,7 @@ export default class WidgetUtils { * Get active stickerpicker widgets (stickerpickers are user widgets by nature) * @return {[object]} Array containing current / active stickerpicker widgets */ - static getStickerpickerWidgets() { + static getStickerpickerWidgets(): IWidget[] { const widgets = WidgetUtils.getUserWidgetsArray(); return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker"); } @@ -340,12 +351,12 @@ export default class WidgetUtils { * Get all integration manager widgets for this user. * @returns {Object[]} An array of integration manager user widgets. */ - static getIntegrationManagerWidgets() { + static getIntegrationManagerWidgets(): IWidget[] { const widgets = WidgetUtils.getUserWidgetsArray(); return widgets.filter(w => w.content && w.content.type === "m.integration_manager"); } - static getRoomWidgetsOfType(room: Room, type: WidgetType) { + static getRoomWidgetsOfType(room: Room, type: WidgetType): IWidget[] { const widgets = WidgetUtils.getRoomWidgets(room); return (widgets || []).filter(w => { const content = w.getContent(); @@ -353,14 +364,14 @@ export default class WidgetUtils { }); } - static removeIntegrationManagerWidgets() { + static removeIntegrationManagerWidgets(): Promise { const client = MatrixClientPeg.get(); if (!client) { throw new Error('User not logged in'); } const widgets = client.getAccountData('m.widgets'); if (!widgets) return; - const userWidgets = widgets.getContent() || {}; + const userWidgets: IWidget[] = widgets.getContent() || {}; Object.entries(userWidgets).forEach(([key, widget]) => { if (widget.content && widget.content.type === "m.integration_manager") { delete userWidgets[key]; @@ -369,7 +380,7 @@ export default class WidgetUtils { return client.setAccountData('m.widgets', userWidgets); } - static addIntegrationManagerWidget(name: string, uiUrl: string, apiUrl: string) { + static addIntegrationManagerWidget(name: string, uiUrl: string, apiUrl: string): Promise { return WidgetUtils.setUserWidget( "integration_manager_" + (new Date().getTime()), WidgetType.INTEGRATION_MANAGER, @@ -383,14 +394,14 @@ export default class WidgetUtils { * Remove all stickerpicker widgets (stickerpickers are user widgets by nature) * @return {Promise} Resolves on account data updated */ - static removeStickerpickerWidgets() { + static removeStickerpickerWidgets(): Promise { const client = MatrixClientPeg.get(); if (!client) { throw new Error('User not logged in'); } const widgets = client.getAccountData('m.widgets'); if (!widgets) return; - const userWidgets = widgets.getContent() || {}; + const userWidgets: Record = widgets.getContent() || {}; Object.entries(userWidgets).forEach(([key, widget]) => { if (widget.content && widget.content.type === 'm.stickerpicker') { delete userWidgets[key]; @@ -399,7 +410,7 @@ export default class WidgetUtils { return client.setAccountData('m.widgets', userWidgets); } - static makeAppConfig(appId, app, senderUserId, roomId, eventId) { + static makeAppConfig(appId: string, app: IApp, senderUserId: string, roomId: string | null, eventId: string): IApp { if (!senderUserId) { throw new Error("Widgets must be created by someone - provide a senderUserId"); } @@ -413,7 +424,7 @@ export default class WidgetUtils { return app; } - static getCapWhitelistForAppTypeInRoomId(appType, roomId) { + static getCapWhitelistForAppTypeInRoomId(appType: string, roomId: string): Capability[] { const enableScreenshots = SettingsStore.getValue("enableWidgetScreenshots", roomId); const capWhitelist = enableScreenshots ? [Capability.Screenshot] : []; @@ -429,7 +440,7 @@ export default class WidgetUtils { return capWhitelist; } - static getWidgetSecurityKey(widgetId, widgetUrl, isUserWidget) { + static getWidgetSecurityKey(widgetId: string, widgetUrl: string, isUserWidget: boolean): string { let widgetLocation = ActiveWidgetStore.getRoomId(widgetId); if (isUserWidget) { @@ -450,7 +461,7 @@ export default class WidgetUtils { return encodeURIComponent(`${widgetLocation}::${widgetUrl}`); } - static getLocalJitsiWrapperUrl(opts: {forLocalRender?: boolean, auth?: string}={}) { + static getLocalJitsiWrapperUrl(opts: {forLocalRender?: boolean, auth?: string} = {}) { // NB. we can't just encodeURIComponent all of these because the $ signs need to be there const queryStringParts = [ 'conferenceDomain=$domain', @@ -466,7 +477,7 @@ export default class WidgetUtils { } const queryString = queryStringParts.join('&'); - let baseUrl = window.location; + let baseUrl = window.location.href; if (window.location.protocol !== "https:" && !opts.forLocalRender) { // Use an external wrapper if we're not locally rendering the widget. This is usually // the URL that will end up in the widget event, so we want to make sure it's relatively @@ -479,15 +490,15 @@ export default class WidgetUtils { return url.href; } - static getWidgetName(app) { + static getWidgetName(app?: IApp): string { return app?.name?.trim() || _t("Unknown App"); } - static getWidgetDataTitle(app) { + static getWidgetDataTitle(app?: IApp): string { return app?.data?.title?.trim() || ""; } - static editWidget(room, app) { + static editWidget(room: Room, app: IApp): void { // TODO: Open the right manager for the widget if (SettingsStore.getValue("feature_many_integration_managers")) { IntegrationManagers.sharedInstance().openAll(room, 'type_' + app.type, app.id); @@ -496,7 +507,7 @@ export default class WidgetUtils { } } - static snapshotWidget(app) { + static snapshotWidget(app: IApp): void { console.log("Requesting widget snapshot"); ActiveWidgetStore.getWidgetMessaging(app.id).getScreenshot().catch((err) => { console.error("Failed to get screenshot", err); From f699be971e72600f6ed6c3567d91cd6df8c6a7b4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Sep 2020 11:02:41 +0100 Subject: [PATCH 05/14] Prepare AppTile to be usable outside of rooms for User Widgets Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/elements/AppTile.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 6aaeab060f..f74f23c09d 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -104,6 +104,7 @@ export default class AppTile extends React.Component { const hasPermissionToLoad = () => { if (this._usingLocalWidget()) return true; + if (!newProps.room) return true; // user widgets always have permissions const currentlyAllowedWidgets = SettingsStore.getValue("allowedWidgets", newProps.room.roomId); return !!currentlyAllowedWidgets[newProps.app.eventId]; }; @@ -419,7 +420,9 @@ export default class AppTile extends React.Component { ActiveWidgetStore.delWidgetMessaging(this.props.app.id); this._setupWidgetMessaging(); - ActiveWidgetStore.setRoomId(this.props.app.id, this.props.room.roomId); + if (this.props.room) { + ActiveWidgetStore.setRoomId(this.props.app.id, this.props.room.roomId); + } this.setState({loading: false}); } @@ -589,7 +592,7 @@ export default class AppTile extends React.Component { const myUser = MatrixClientPeg.get().getUser(myUserId); const vars = Object.assign(targetData, this.props.app.data, { 'matrix_user_id': myUserId, - 'matrix_room_id': this.props.room.roomId, + 'matrix_room_id': this.props.room ? this.props.room.roomId : null, 'matrix_display_name': myUser ? myUser.displayName : myUserId, 'matrix_avatar_url': myUser ? MatrixClientPeg.get().mxcUrlToHttp(myUser.avatarUrl) : '', @@ -754,6 +757,7 @@ export default class AppTile extends React.Component { ); if (!this.state.hasPermissionToLoad) { + // only possible for room widgets, can assert this.props.room here const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); appTileBody = (
@@ -904,7 +908,9 @@ AppTile.displayName = 'AppTile'; AppTile.propTypes = { app: PropTypes.object.isRequired, - room: PropTypes.object.isRequired, + // If room is not specified then it is an account level widget + // which bypasses permission prompts as it was added explicitly by that user + room: PropTypes.object, // Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer. // This should be set to true when there is only one widget in the app drawer, otherwise it should be false. fullWidth: PropTypes.bool, From 956a3bf69dbdd9460ce9bd8ae163b37794c651d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 24 Sep 2020 09:28:49 +0100 Subject: [PATCH 06/14] Implement Left Panel User Widget Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 1 + res/css/structures/_LeftPanelWidget.scss | 145 ++++++++++++++++ src/components/structures/LeftPanel.tsx | 2 + src/components/structures/LeftPanelWidget.tsx | 160 ++++++++++++++++++ src/settings/Settings.ts | 4 + src/utils/WidgetUtils.ts | 6 +- 6 files changed, 315 insertions(+), 3 deletions(-) create mode 100644 res/css/structures/_LeftPanelWidget.scss create mode 100644 src/components/structures/LeftPanelWidget.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 35b4c1b965..992279910a 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -13,6 +13,7 @@ @import "./structures/_HeaderButtons.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; +@import "./structures/_LeftPanelWidget.scss"; @import "./structures/_MainSplit.scss"; @import "./structures/_MatrixChat.scss"; @import "./structures/_MyGroups.scss"; diff --git a/res/css/structures/_LeftPanelWidget.scss b/res/css/structures/_LeftPanelWidget.scss new file mode 100644 index 0000000000..4df651d7b6 --- /dev/null +++ b/res/css/structures/_LeftPanelWidget.scss @@ -0,0 +1,145 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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. +*/ + +.mx_LeftPanelWidget { + // largely based on RoomSublist + margin-left: 8px; + margin-bottom: 4px; + + .mx_LeftPanelWidget_headerContainer { + display: flex; + align-items: center; + + height: 24px; + color: $roomlist-header-color; + margin-top: 4px; + + .mx_LeftPanelWidget_stickable { + flex: 1; + max-width: 100%; + + display: flex; + align-items: center; + } + + .mx_LeftPanelWidget_headerText { + flex: 1; + max-width: calc(100% - 16px); + line-height: $font-16px; + font-size: $font-13px; + font-weight: 600; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + + .mx_LeftPanelWidget_collapseBtn { + display: inline-block; + position: relative; + width: 14px; + height: 14px; + margin-right: 6px; + + &::before { + content: ''; + width: 18px; + height: 18px; + position: absolute; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background-color: $roomlist-header-color; + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); + } + + &.mx_LeftPanelWidget_collapseBtn_collapsed::before { + transform: rotate(-90deg); + } + } + } + } + + .mx_LeftPanelWidget_resizeBox { + position: relative; + + display: flex; + flex-direction: column; + overflow: visible; // let the resize handle out + } + + .mx_AppTileFullWidth { + flex: 1 0 0; + overflow: hidden; + // need this to be flex otherwise the overflow hidden from above + // sometimes vertically centers the clipped list ... no idea why it would do this + // as the box model should be top aligned. Happens in both FF and Chromium + display: flex; + flex-direction: column; + box-sizing: border-box; + + mask-image: linear-gradient(0deg, transparent, black 4px); + } + + .mx_LeftPanelWidget_resizerHandle { + cursor: ns-resize; + border-radius: 3px; + + // Override styles from library + width: unset !important; + height: 4px !important; + + position: absolute; + top: -24px !important; // override from library - puts it in the margin-top of the headerContainer + + // Together, these make the bar 64px wide + // These are also overridden from the library + left: calc(50% - 32px) !important; + right: calc(50% - 32px) !important; + } + + &:hover .mx_LeftPanelWidget_resizerHandle { + opacity: 0.8; + background-color: $primary-fg-color; + } + + .mx_LeftPanelWidget_maximizeButton { + margin-left: 8px; + margin-right: 7px; + position: relative; + width: 24px; + height: 24px; + border-radius: 32px; + + &::before { + content: ''; + width: 16px; + height: 16px; + position: absolute; + top: 4px; + left: 4px; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); + background: $muted-fg-color; + } + } +} + +.mx_LeftPanelWidget_maximizeButtonTooltip { + margin-top: -3px; +} diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index 455e807bc9..104430f049 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -38,6 +38,7 @@ import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import { OwnProfileStore } from "../../stores/OwnProfileStore"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import RoomListNumResults from "../views/rooms/RoomListNumResults"; +import LeftPanelWidget from "./LeftPanelWidget"; interface IProps { isMinimized: boolean; @@ -432,6 +433,7 @@ export default class LeftPanel extends React.Component { {roomList}
+ { !this.props.isMinimized && } ); diff --git a/src/components/structures/LeftPanelWidget.tsx b/src/components/structures/LeftPanelWidget.tsx new file mode 100644 index 0000000000..d969c53bca --- /dev/null +++ b/src/components/structures/LeftPanelWidget.tsx @@ -0,0 +1,160 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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 React, {useContext, useEffect, useMemo} from "react"; +import {Resizable} from "re-resizable"; +import classNames from "classnames"; + +import {_t} from "../../languageHandler"; +import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; +import AccessibleButton from "../views/elements/AccessibleButton"; +import {useRovingTabIndex} from "../../accessibility/RovingTabIndex"; +import {Key} from "../../Keyboard"; +import {useLocalStorageState} from "../../hooks/useLocalStorageState"; +import MatrixClientContext from "../../contexts/MatrixClientContext"; +import WidgetUtils, {IWidget} from "../../utils/WidgetUtils"; +import {useAccountData} from "../../hooks/useAccountData"; +import AppTile from "../views/elements/AppTile"; +import {useSettingValue} from "../../hooks/useSettings"; + +interface IProps { + onResize(): void; +} + +const MIN_HEIGHT = 100; +const MAX_HEIGHT = 500; +const INITIAL_HEIGHT = 280; + +const LeftPanelWidget: React.FC = ({ onResize }) => { + const cli = useContext(MatrixClientContext); + + const mWidgetsEvent = useAccountData>(cli, "m.widgets"); + const leftPanelWidgetId = useSettingValue("Widgets.leftPanel"); + const app = useMemo(() => { + if (!mWidgetsEvent || !leftPanelWidgetId) return null; + const widgetConfig = Object.values(mWidgetsEvent).find(w => w.id === leftPanelWidgetId); + if (!widgetConfig) return null; + + return WidgetUtils.makeAppConfig( + widgetConfig.state_key, + widgetConfig.content, + widgetConfig.sender, + null, + widgetConfig.id); + }, [cli, mWidgetsEvent, leftPanelWidgetId]); + + const [height, setHeight] = useLocalStorageState("left-panel-widget-height", INITIAL_HEIGHT); + const [expanded, setExpanded] = useLocalStorageState("left-panel-widget-expanded", true); + useEffect(onResize, [expanded]); + + const [onFocus, isActive, ref] = useRovingTabIndex(); + const tabIndex = isActive ? 0 : -1; + + if (!app) return null; + + let auxButton = null; + if (1) { + auxButton = ( + { + console.log("@@ Maximise Left Panel Widget") + }} + className="mx_LeftPanelWidget_maximizeButton" + tooltipClassName="mx_LeftPanelWidget_maximizeButtonTooltip" + title={_t("Maximize")} + /> + ); + } + + let content; + if (expanded) { + content = { + setHeight(height + d.height); + }} + handleWrapperClass="mx_LeftPanelWidget_resizerHandles" + handleClasses={{top: "mx_LeftPanelWidget_resizerHandle"}} + className="mx_LeftPanelWidget_resizeBox" + enable={{ top: true }} + > + + ; + } + + return
+
{ + switch (ev.key) { + case Key.ARROW_LEFT: + ev.stopPropagation(); + setExpanded(false); + break; + case Key.ARROW_RIGHT: { + ev.stopPropagation(); + setExpanded(true); + break; + } + } + }} + > +
+ { + setExpanded(e => !e); + }} + > + + { WidgetUtils.getWidgetName(app) } + + { auxButton } +
+
+ + { content } +
; +}; + +export default LeftPanelWidget; diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 737c882919..dd638ea7a8 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -620,6 +620,10 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, default: {}, }, + "Widgets.leftPanel": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: null, + }, [UIFeature.AdvancedEncryption]: { supportedLevels: LEVELS_UI_FEATURE, default: true, diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index efdfbbb3cc..338b856edf 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -42,7 +42,7 @@ export interface IWidget { type: string; sender: string; state_key: string; - content: IApp; + content: Partial; } export default class WidgetUtils { @@ -410,7 +410,7 @@ export default class WidgetUtils { return client.setAccountData('m.widgets', userWidgets); } - static makeAppConfig(appId: string, app: IApp, senderUserId: string, roomId: string | null, eventId: string): IApp { + static makeAppConfig(appId: string, app: Partial, senderUserId: string, roomId: string | null, eventId: string): IApp { if (!senderUserId) { throw new Error("Widgets must be created by someone - provide a senderUserId"); } @@ -421,7 +421,7 @@ export default class WidgetUtils { app.eventId = eventId; app.name = app.name || app.type; - return app; + return app as IApp; } static getCapWhitelistForAppTypeInRoomId(appType: string, roomId: string): Capability[] { From f3e6f43c3d658a4c43b2f0f5c879fe3abc653cd2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 24 Sep 2020 09:34:19 +0100 Subject: [PATCH 07/14] Improve maxHeight Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LeftPanelWidget.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/LeftPanelWidget.tsx b/src/components/structures/LeftPanelWidget.tsx index d969c53bca..1f1a963f8f 100644 --- a/src/components/structures/LeftPanelWidget.tsx +++ b/src/components/structures/LeftPanelWidget.tsx @@ -35,7 +35,7 @@ interface IProps { } const MIN_HEIGHT = 100; -const MAX_HEIGHT = 500; +const MAX_HEIGHT = 500; // or 50% of the window height const INITIAL_HEIGHT = 280; const LeftPanelWidget: React.FC = ({ onResize }) => { @@ -85,7 +85,7 @@ const LeftPanelWidget: React.FC = ({ onResize }) => { content = { setHeight(height + d.height); From 0ef08945cf5ae26bb8c8ac0bfa432450eb3862cf Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 24 Sep 2020 09:38:05 +0100 Subject: [PATCH 08/14] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d7360430ae..82e7f070aa 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2056,6 +2056,7 @@ "Explore Public Rooms": "Explore Public Rooms", "Create a Group Chat": "Create a Group Chat", "Explore rooms": "Explore rooms", + "Maximize": "Maximize", "Failed to reject invitation": "Failed to reject invitation", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", From c65d8be7d80b1943fd70aa5f0b3f4cfa472f7db0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 24 Sep 2020 09:49:30 +0100 Subject: [PATCH 09/14] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LeftPanelWidget.tsx | 33 +++++++------------ src/utils/WidgetUtils.ts | 26 +++++++++++++-- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/components/structures/LeftPanelWidget.tsx b/src/components/structures/LeftPanelWidget.tsx index 1f1a963f8f..f12c5ca2f9 100644 --- a/src/components/structures/LeftPanelWidget.tsx +++ b/src/components/structures/LeftPanelWidget.tsx @@ -54,7 +54,7 @@ const LeftPanelWidget: React.FC = ({ onResize }) => { widgetConfig.sender, null, widgetConfig.id); - }, [cli, mWidgetsEvent, leftPanelWidgetId]); + }, [mWidgetsEvent, leftPanelWidgetId]); const [height, setHeight] = useLocalStorageState("left-panel-widget-height", INITIAL_HEIGHT); const [expanded, setExpanded] = useLocalStorageState("left-panel-widget-expanded", true); @@ -65,21 +65,6 @@ const LeftPanelWidget: React.FC = ({ onResize }) => { if (!app) return null; - let auxButton = null; - if (1) { - auxButton = ( - { - console.log("@@ Maximise Left Panel Widget") - }} - className="mx_LeftPanelWidget_maximizeButton" - tooltipClassName="mx_LeftPanelWidget_maximizeButtonTooltip" - title={_t("Maximize")} - /> - ); - } - let content; if (expanded) { content = = ({ onResize }) => { return
{ switch (ev.key) { case Key.ARROW_LEFT: @@ -149,7 +131,16 @@ const LeftPanelWidget: React.FC = ({ onResize }) => { })} /> { WidgetUtils.getWidgetName(app) } - { auxButton } + + {/* Code for the maximise button for once we have full screen widgets */} + {/* { + }} + className="mx_LeftPanelWidget_maximizeButton" + tooltipClassName="mx_LeftPanelWidget_maximizeButtonTooltip" + title={_t("Maximize")} + />*/}
diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index 338b856edf..bcf4324d6f 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -41,6 +41,7 @@ export interface IWidget { id: string; type: string; sender: string; + // eslint-disable-next-line camelcase state_key: string; content: Partial; } @@ -224,7 +225,13 @@ export default class WidgetUtils { }); } - static setUserWidget(widgetId: string, widgetType: WidgetType, widgetUrl: string, widgetName: string, widgetData: object) { + static setUserWidget( + widgetId: string, + widgetType: WidgetType, + widgetUrl: string, + widgetName: string, + widgetData: object, + ) { const content = { type: widgetType.preferred, url: widgetUrl, @@ -268,7 +275,14 @@ export default class WidgetUtils { }); } - static setRoomWidget(roomId: string, widgetId: string, widgetType: WidgetType, widgetUrl: string, widgetName: string, widgetData: object) { + static setRoomWidget( + roomId: string, + widgetId: string, + widgetType: WidgetType, + widgetUrl: string, + widgetName: string, + widgetData: object, + ) { let content; const addingWidget = Boolean(widgetUrl); @@ -410,7 +424,13 @@ export default class WidgetUtils { return client.setAccountData('m.widgets', userWidgets); } - static makeAppConfig(appId: string, app: Partial, senderUserId: string, roomId: string | null, eventId: string): IApp { + static makeAppConfig( + appId: string, + app: Partial, + senderUserId: string, + roomId: string | null, + eventId: string, + ): IApp { if (!senderUserId) { throw new Error("Widgets must be created by someone - provide a senderUserId"); } From f7959fe64f36edd1b7178cb8b4dd512a1d2063a8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 24 Sep 2020 09:55:42 +0100 Subject: [PATCH 10/14] i18n and delint one last time Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LeftPanelWidget.tsx | 2 -- src/i18n/strings/en_EN.json | 1 - 2 files changed, 3 deletions(-) diff --git a/src/components/structures/LeftPanelWidget.tsx b/src/components/structures/LeftPanelWidget.tsx index f12c5ca2f9..e8dfe955a8 100644 --- a/src/components/structures/LeftPanelWidget.tsx +++ b/src/components/structures/LeftPanelWidget.tsx @@ -18,8 +18,6 @@ import React, {useContext, useEffect, useMemo} from "react"; import {Resizable} from "re-resizable"; import classNames from "classnames"; -import {_t} from "../../languageHandler"; -import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import AccessibleButton from "../views/elements/AccessibleButton"; import {useRovingTabIndex} from "../../accessibility/RovingTabIndex"; import {Key} from "../../Keyboard"; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 82e7f070aa..d7360430ae 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2056,7 +2056,6 @@ "Explore Public Rooms": "Explore Public Rooms", "Create a Group Chat": "Create a Group Chat", "Explore rooms": "Explore rooms", - "Maximize": "Maximize", "Failed to reject invitation": "Failed to reject invitation", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", From 5ee0262da2d633367a6aa826e7e04f4857ee747e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 Oct 2020 20:56:41 +0100 Subject: [PATCH 11/14] Post-merge tidy up Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/WidgetStore.ts | 12 ++---------- src/utils/WidgetUtils.ts | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/stores/WidgetStore.ts b/src/stores/WidgetStore.ts index c66fa493b0..87c93af7a5 100644 --- a/src/stores/WidgetStore.ts +++ b/src/stores/WidgetStore.ts @@ -16,6 +16,7 @@ limitations under the License. import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { IWidget } from "matrix-widget-api"; import { ActionPayload } from "../dispatcher/payloads"; import { AsyncStoreWithClient } from "./AsyncStoreWithClient"; @@ -30,18 +31,9 @@ import {UPDATE_EVENT} from "./AsyncStore"; interface IState {} -export interface IApp { - id: string; - url: string; - type: string; - name: string; +export interface IApp extends IWidget { roomId: string; eventId: string; - creatorUserId: string; - waitForIframeLoad?: boolean; - data?: { - title?: string; - }; // eslint-disable-next-line camelcase avatar_url: string; // MSC2765 https://github.com/matrix-org/matrix-doc/pull/2765 } diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index c9e57bbba1..1e97d95b92 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -30,14 +30,14 @@ import {Room} from "matrix-js-sdk/src/models/room"; import {WidgetType} from "../widgets/WidgetType"; import {objectClone} from "./objects"; import {_t} from "../languageHandler"; -import {MatrixCapabilities} from "matrix-widget-api"; +import {Capability, IWidgetData, MatrixCapabilities} from "matrix-widget-api"; import {IApp} from "../stores/WidgetStore"; // TODO @@ // How long we wait for the state event echo to come back from the server // before waitFor[Room/User]Widget rejects its promise const WIDGET_WAIT_TIME = 20000; -export interface IWidget { // TODO @@ +export interface IWidgetEvent { id: string; type: string; sender: string; @@ -230,7 +230,7 @@ export default class WidgetUtils { widgetType: WidgetType, widgetUrl: string, widgetName: string, - widgetData: object, + widgetData: IWidgetData, ) { const content = { type: widgetType.preferred, @@ -332,7 +332,7 @@ export default class WidgetUtils { * Get user specific widgets (not linked to a specific room) * @return {object} Event content object containing current / active user widgets */ - static getUserWidgets(): Record { + static getUserWidgets(): Record { const client = MatrixClientPeg.get(); if (!client) { throw new Error('User not logged in'); @@ -348,7 +348,7 @@ export default class WidgetUtils { * Get user specific widgets (not linked to a specific room) as an array * @return {[object]} Array containing current / active user widgets */ - static getUserWidgetsArray(): IWidget[] { + static getUserWidgetsArray(): IWidgetEvent[] { return Object.values(WidgetUtils.getUserWidgets()); } @@ -356,7 +356,7 @@ export default class WidgetUtils { * Get active stickerpicker widgets (stickerpickers are user widgets by nature) * @return {[object]} Array containing current / active stickerpicker widgets */ - static getStickerpickerWidgets(): IWidget[] { + static getStickerpickerWidgets(): IWidgetEvent[] { const widgets = WidgetUtils.getUserWidgetsArray(); return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker"); } @@ -365,12 +365,12 @@ export default class WidgetUtils { * Get all integration manager widgets for this user. * @returns {Object[]} An array of integration manager user widgets. */ - static getIntegrationManagerWidgets(): IWidget[] { + static getIntegrationManagerWidgets(): IWidgetEvent[] { const widgets = WidgetUtils.getUserWidgetsArray(); return widgets.filter(w => w.content && w.content.type === "m.integration_manager"); } - static getRoomWidgetsOfType(room: Room, type: WidgetType): IWidget[] { + static getRoomWidgetsOfType(room: Room, type: WidgetType): IWidgetEvent[] { const widgets = WidgetUtils.getRoomWidgets(room); return (widgets || []).filter(w => { const content = w.getContent(); @@ -385,7 +385,7 @@ export default class WidgetUtils { } const widgets = client.getAccountData('m.widgets'); if (!widgets) return; - const userWidgets: IWidget[] = widgets.getContent() || {}; + const userWidgets: IWidgetEvent[] = widgets.getContent() || {}; Object.entries(userWidgets).forEach(([key, widget]) => { if (widget.content && widget.content.type === "m.integration_manager") { delete userWidgets[key]; @@ -415,7 +415,7 @@ export default class WidgetUtils { } const widgets = client.getAccountData('m.widgets'); if (!widgets) return; - const userWidgets: Record = widgets.getContent() || {}; + const userWidgets: Record = widgets.getContent() || {}; Object.entries(userWidgets).forEach(([key, widget]) => { if (widget.content && widget.content.type === 'm.stickerpicker') { delete userWidgets[key]; From 4dd8b8d8c6ab7172dc91dfc6ffe556159da3ea17 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 Oct 2020 21:02:48 +0100 Subject: [PATCH 12/14] fix WidgetUtils setRoomWidget signature Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/utils/WidgetUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index 1e97d95b92..576d0e7ca4 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -278,10 +278,10 @@ export default class WidgetUtils { static setRoomWidget( roomId: string, widgetId: string, - widgetType: WidgetType, - widgetUrl: string, - widgetName: string, - widgetData: object, + widgetType?: WidgetType, + widgetUrl?: string, + widgetName?: string, + widgetData?: object, ) { let content; From b0acae6e77a940376d5e8048dc2fdad084daf773 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 Oct 2020 21:13:31 +0100 Subject: [PATCH 13/14] fix remaining type mismatches Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LeftPanelWidget.tsx | 4 ++-- src/integrations/IntegrationManagers.ts | 2 +- src/stores/widgets/StopGapWidget.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/structures/LeftPanelWidget.tsx b/src/components/structures/LeftPanelWidget.tsx index e8dfe955a8..4daec76d08 100644 --- a/src/components/structures/LeftPanelWidget.tsx +++ b/src/components/structures/LeftPanelWidget.tsx @@ -23,7 +23,7 @@ import {useRovingTabIndex} from "../../accessibility/RovingTabIndex"; import {Key} from "../../Keyboard"; import {useLocalStorageState} from "../../hooks/useLocalStorageState"; import MatrixClientContext from "../../contexts/MatrixClientContext"; -import WidgetUtils, {IWidget} from "../../utils/WidgetUtils"; +import WidgetUtils, {IWidgetEvent} from "../../utils/WidgetUtils"; import {useAccountData} from "../../hooks/useAccountData"; import AppTile from "../views/elements/AppTile"; import {useSettingValue} from "../../hooks/useSettings"; @@ -39,7 +39,7 @@ const INITIAL_HEIGHT = 280; const LeftPanelWidget: React.FC = ({ onResize }) => { const cli = useContext(MatrixClientContext); - const mWidgetsEvent = useAccountData>(cli, "m.widgets"); + const mWidgetsEvent = useAccountData>(cli, "m.widgets"); const leftPanelWidgetId = useSettingValue("Widgets.leftPanel"); const app = useMemo(() => { if (!mWidgetsEvent || !leftPanelWidgetId) return null; diff --git a/src/integrations/IntegrationManagers.ts b/src/integrations/IntegrationManagers.ts index 1791e92514..a29c74c5eb 100644 --- a/src/integrations/IntegrationManagers.ts +++ b/src/integrations/IntegrationManagers.ts @@ -120,7 +120,7 @@ export class IntegrationManagers { if (!data) return; const uiUrl = w.content['url']; - const apiUrl = data['api_url']; + const apiUrl = data['api_url'] as string; if (!apiUrl || !uiUrl) return; const manager = new IntegrationManagerInstance( diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 17302d0ab9..edce39d033 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -74,7 +74,7 @@ class ElementWidget extends Widget { if (WidgetType.JITSI.matches(this.type)) { return WidgetUtils.getLocalJitsiWrapperUrl({ forLocalRender: true, - auth: super.rawData?.auth, // this.rawData can call templateUrl, do this to prevent looping + auth: super.rawData?.auth as string, // this.rawData can call templateUrl, do this to prevent looping }); } return super.templateUrl; @@ -84,7 +84,7 @@ class ElementWidget extends Widget { if (WidgetType.JITSI.matches(this.type)) { return WidgetUtils.getLocalJitsiWrapperUrl({ forLocalRender: false, // The only important difference between this and templateUrl() - auth: super.rawData?.auth, + auth: super.rawData?.auth as string, }); } return this.templateUrl; // use this instead of super to ensure we get appropriate templating From 568e14705390a1c8c6b4c068a6c74a956c505c53 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 21 Oct 2020 12:44:16 +0100 Subject: [PATCH 14/14] Merge copyrights Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/utils/WidgetUtils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index 576d0e7ca4..9e69798803 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -1,8 +1,6 @@ /* -Copyright 2017 Vector Creations Ltd -Copyright 2018 New Vector Ltd Copyright 2019 Travis Ralston -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2017 - 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.