Merge pull request #4379 from matrix-org/travis/moar-jitsi
Support m.jitsi-typed widgets as Jitsi widgets
This commit is contained in:
commit
37bd0f3508
7 changed files with 72 additions and 18 deletions
|
@ -66,6 +66,7 @@ import WidgetEchoStore from './stores/WidgetEchoStore';
|
|||
import SettingsStore, { SettingLevel } from './settings/SettingsStore';
|
||||
import {generateHumanReadableId} from "./utils/NamingUtils";
|
||||
import {Jitsi} from "./widgets/Jitsi";
|
||||
import {WidgetType} from "./widgets/WidgetType";
|
||||
|
||||
global.mxCalls = {
|
||||
//room_id: MatrixCall
|
||||
|
@ -401,9 +402,9 @@ async function _startCallApp(roomId, type) {
|
|||
});
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
const currentRoomWidgets = WidgetUtils.getRoomWidgets(room);
|
||||
const currentJitsiWidgets = WidgetUtils.getRoomWidgetsOfType(room, WidgetType.JITSI);
|
||||
|
||||
if (WidgetEchoStore.roomHasPendingWidgetsOfType(roomId, currentRoomWidgets, 'jitsi')) {
|
||||
if (WidgetEchoStore.roomHasPendingWidgetsOfType(roomId, currentJitsiWidgets, WidgetType.JITSI)) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
||||
Modal.createTrackedDialog('Call already in progress', '', ErrorDialog, {
|
||||
|
@ -413,9 +414,6 @@ async function _startCallApp(roomId, type) {
|
|||
return;
|
||||
}
|
||||
|
||||
const currentJitsiWidgets = currentRoomWidgets.filter((ev) => {
|
||||
return ev.getContent().type === 'jitsi';
|
||||
});
|
||||
if (currentJitsiWidgets.length > 0) {
|
||||
console.warn(
|
||||
"Refusing to start conference call widget in " + roomId +
|
||||
|
@ -454,7 +452,7 @@ async function _startCallApp(roomId, type) {
|
|||
Date.now()
|
||||
);
|
||||
|
||||
WidgetUtils.setRoomWidget(roomId, widgetId, 'jitsi', widgetUrl, 'Jitsi', widgetData).then(() => {
|
||||
WidgetUtils.setRoomWidget(roomId, widgetId, WidgetType.JITSI, widgetUrl, 'Jitsi', widgetData).then(() => {
|
||||
console.log('Jitsi widget added');
|
||||
}).catch((e) => {
|
||||
if (e.errcode === 'M_FORBIDDEN') {
|
||||
|
|
|
@ -241,6 +241,7 @@ import WidgetUtils from './utils/WidgetUtils';
|
|||
import RoomViewStore from './stores/RoomViewStore';
|
||||
import { _t } from './languageHandler';
|
||||
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||
import {WidgetType} from "./widgets/WidgetType";
|
||||
|
||||
function sendResponse(event, res) {
|
||||
const data = JSON.parse(JSON.stringify(event.data));
|
||||
|
@ -292,7 +293,7 @@ function inviteUser(event, roomId, userId) {
|
|||
|
||||
function setWidget(event, roomId) {
|
||||
const widgetId = event.data.widget_id;
|
||||
const widgetType = event.data.type;
|
||||
let widgetType = event.data.type;
|
||||
const widgetUrl = event.data.url;
|
||||
const widgetName = event.data.name; // optional
|
||||
const widgetData = event.data.data; // optional
|
||||
|
@ -324,6 +325,9 @@ function setWidget(event, roomId) {
|
|||
}
|
||||
}
|
||||
|
||||
// convert the widget type to a known widget type
|
||||
widgetType = WidgetType.fromString(widgetType);
|
||||
|
||||
if (userWidget) {
|
||||
WidgetUtils.setUserWidget(widgetId, widgetType, widgetUrl, widgetName, widgetData).then(() => {
|
||||
sendResponse(event, {
|
||||
|
|
|
@ -35,6 +35,7 @@ import { abbreviateUrl } from './utils/UrlUtils';
|
|||
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
|
||||
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
|
||||
import {inviteUsersToRoom} from "./RoomInvite";
|
||||
import { WidgetType } from "./widgets/WidgetType";
|
||||
import sendBugReport from "./rageshake/submit-rageshake";
|
||||
import SdkConfig from "./SdkConfig";
|
||||
|
||||
|
@ -779,7 +780,7 @@ export const Commands = [
|
|||
const nowMs = (new Date()).getTime();
|
||||
const widgetId = encodeURIComponent(`${roomId}_${userId}_${nowMs}`);
|
||||
return success(WidgetUtils.setRoomWidget(
|
||||
roomId, widgetId, "m.custom", args, "Custom Widget", {}));
|
||||
roomId, widgetId, WidgetType.CUSTOM, args, "Custom Widget", {}));
|
||||
} else {
|
||||
return reject(_t("You cannot modify widgets in this room."));
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
|||
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
|
||||
import {aboveLeftOf, ContextMenu, ContextMenuButton} from "../../structures/ContextMenu";
|
||||
import PersistedElement from "./PersistedElement";
|
||||
import {WidgetType} from "../../../widgets/WidgetType";
|
||||
|
||||
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
|
||||
const ENABLE_REACT_PERF = false;
|
||||
|
@ -454,7 +455,7 @@ export default class AppTile extends React.Component {
|
|||
|
||||
// We only tell Jitsi widgets that we're ready because they're realistically the only ones
|
||||
// using this custom extension to the widget API.
|
||||
if (this.props.app.type === 'jitsi') {
|
||||
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
||||
widgetMessaging.flagReadyToContinue();
|
||||
}
|
||||
}).catch((err) => {
|
||||
|
@ -597,7 +598,7 @@ export default class AppTile extends React.Component {
|
|||
_getRenderedUrl() {
|
||||
let url;
|
||||
|
||||
if (this.props.app.type === 'jitsi') {
|
||||
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
||||
console.log("Replacing Jitsi widget URL with local wrapper");
|
||||
url = WidgetUtils.getLocalJitsiWrapperUrl({forLocalRender: true});
|
||||
url = this._addWurlParams(url);
|
||||
|
@ -608,7 +609,7 @@ export default class AppTile extends React.Component {
|
|||
}
|
||||
|
||||
_getPopoutUrl() {
|
||||
if (this.props.app.type === 'jitsi') {
|
||||
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
||||
return this._templatedUrl(
|
||||
WidgetUtils.getLocalJitsiWrapperUrl({forLocalRender: false}),
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import EventEmitter from 'events';
|
||||
import {WidgetType} from "../widgets/WidgetType";
|
||||
|
||||
/**
|
||||
* Acts as a place to get & set widget state, storing local echo state and
|
||||
|
@ -64,7 +65,7 @@ class WidgetEchoStore extends EventEmitter {
|
|||
return echoedWidgets;
|
||||
}
|
||||
|
||||
roomHasPendingWidgetsOfType(roomId, currentRoomWidgets, type) {
|
||||
roomHasPendingWidgetsOfType(roomId, currentRoomWidgets, type: WidgetType) {
|
||||
const roomEchoState = Object.assign({}, this._roomWidgetEcho[roomId]);
|
||||
|
||||
// any widget IDs that are already in the room are not pending, so
|
||||
|
@ -79,7 +80,7 @@ class WidgetEchoStore extends EventEmitter {
|
|||
return Object.keys(roomEchoState).length > 0;
|
||||
} else {
|
||||
return Object.values(roomEchoState).some((widget) => {
|
||||
return widget.type === type;
|
||||
return type.matches(widget.type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import SettingsStore from "../settings/SettingsStore";
|
|||
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
|
||||
import {IntegrationManagers} from "../integrations/IntegrationManagers";
|
||||
import {Capability} from "../widgets/WidgetApi";
|
||||
import {Room} from "matrix-js-sdk/src/models/room";
|
||||
import {WidgetType} from "../widgets/WidgetType";
|
||||
|
||||
export default class WidgetUtils {
|
||||
/* Returns true if user is able to send state events to modify widgets in this room
|
||||
|
@ -252,14 +254,16 @@ export default class WidgetUtils {
|
|||
});
|
||||
}
|
||||
|
||||
static setRoomWidget(roomId, widgetId, widgetType, widgetUrl, widgetName, widgetData) {
|
||||
static setRoomWidget(roomId, widgetId, widgetType: WidgetType, widgetUrl, widgetName, widgetData) {
|
||||
let content;
|
||||
|
||||
const addingWidget = Boolean(widgetUrl);
|
||||
|
||||
if (addingWidget) {
|
||||
content = {
|
||||
type: widgetType,
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
|
||||
// For now we'll send the legacy event type for compatibility with older apps/riots
|
||||
type: widgetType.legacy,
|
||||
url: widgetUrl,
|
||||
name: widgetName,
|
||||
data: widgetData,
|
||||
|
@ -281,10 +285,10 @@ export default class WidgetUtils {
|
|||
|
||||
/**
|
||||
* Get room specific widgets
|
||||
* @param {object} room The room to get widgets force
|
||||
* @param {Room} room The room to get widgets force
|
||||
* @return {[object]} Array containing current / active room widgets
|
||||
*/
|
||||
static getRoomWidgets(room) {
|
||||
static getRoomWidgets(room: Room) {
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
|
||||
const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
|
||||
if (!appsStateEvents) {
|
||||
|
@ -338,6 +342,14 @@ export default class WidgetUtils {
|
|||
return widgets.filter(w => w.content && w.content.type === "m.integration_manager");
|
||||
}
|
||||
|
||||
static getRoomWidgetsOfType(room: Room, type: WidgetType) {
|
||||
const widgets = WidgetUtils.getRoomWidgets(room);
|
||||
return (widgets || []).filter(w => {
|
||||
const content = w.getContent();
|
||||
return content.url && type.matches(content.type);
|
||||
});
|
||||
}
|
||||
|
||||
static removeIntegrationManagerWidgets() {
|
||||
const client = MatrixClientPeg.get();
|
||||
if (!client) {
|
||||
|
@ -405,7 +417,7 @@ export default class WidgetUtils {
|
|||
// Obviously anyone that can add a widget can claim it's a jitsi widget,
|
||||
// so this doesn't really offer much over the set of domains we load
|
||||
// widgets from at all, but it probably makes sense for sanity.
|
||||
if (appType === 'jitsi') {
|
||||
if (WidgetType.JITSI.matches(appType)) {
|
||||
capWhitelist.push(Capability.AlwaysOnScreen);
|
||||
}
|
||||
|
||||
|
|
37
src/widgets/WidgetType.ts
Normal file
37
src/widgets/WidgetType.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
export class WidgetType {
|
||||
public static readonly JITSI = new WidgetType("m.jitsi", "jitsi");
|
||||
public static readonly CUSTOM = new WidgetType("m.custom", "m.custom");
|
||||
|
||||
constructor(public readonly preferred: string, public readonly legacy: string) {
|
||||
}
|
||||
|
||||
public matches(type: string): boolean {
|
||||
return type === this.preferred || type === this.legacy;
|
||||
}
|
||||
|
||||
static fromString(type: string): WidgetType {
|
||||
// First try and match it against something we're already aware of
|
||||
const known = Object.values(WidgetType).filter(v => v instanceof WidgetType);
|
||||
const knownMatch = known.find(w => w.matches(type));
|
||||
if (knownMatch) return knownMatch;
|
||||
|
||||
// If that fails, invent a new widget type
|
||||
return new WidgetType(type, type);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue