Very basic Jitsi integration
Replaces the conference calling by adding a Jitsi widget. No support for continuing the call on room change currently, so hidden behind a feature flag. Existing behaviour is preserved by default.pull/21833/head
parent
a25cdb6ce5
commit
8cf17a66e5
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2017, 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -60,6 +60,7 @@ import { _t } from './languageHandler';
|
|||
import Matrix from 'matrix-js-sdk';
|
||||
import dis from './dispatcher';
|
||||
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
|
||||
global.mxCalls = {
|
||||
//room_id: MatrixCall
|
||||
|
@ -294,18 +295,8 @@ function _onAction(payload) {
|
|||
break;
|
||||
case 'place_conference_call':
|
||||
console.log("Place conference call in %s", payload.room_id);
|
||||
if (!ConferenceHandler) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Call Handler', 'Conference call unsupported client', ErrorDialog, {
|
||||
description: _t('Conference calls are not supported in this client'),
|
||||
});
|
||||
} else if (!MatrixClientPeg.get().supportsVoip()) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Call Handler', 'VoIP is unsupported', ErrorDialog, {
|
||||
title: _t('VoIP is unsupported'),
|
||||
description: _t('You cannot place VoIP calls in this browser.'),
|
||||
});
|
||||
} else if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
|
||||
|
||||
if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
|
||||
// Conference calls are implemented by sending the media to central
|
||||
// server which combines the audio from all the participants together
|
||||
// into a single stream. This is incompatible with end-to-end encryption
|
||||
|
@ -316,28 +307,46 @@ function _onAction(payload) {
|
|||
Modal.createTrackedDialog('Call Handler', 'Conference calls unsupported e2e', ErrorDialog, {
|
||||
description: _t('Conference calls are not supported in encrypted rooms'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (SettingsStore.isFeatureEnabled('feature_jitsi')) {
|
||||
_startCallApp(payload.room_id, payload.type);
|
||||
} else {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createTrackedDialog('Call Handler', 'Conference calling in development', QuestionDialog, {
|
||||
title: _t('Warning!'),
|
||||
description: _t('Conference calling is in development and may not be reliable.'),
|
||||
onFinished: (confirm)=>{
|
||||
if (confirm) {
|
||||
ConferenceHandler.createNewMatrixCall(
|
||||
MatrixClientPeg.get(), payload.room_id,
|
||||
).done(function(call) {
|
||||
placeCall(call);
|
||||
}, function(err) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Conference call failed: " + err);
|
||||
Modal.createTrackedDialog('Call Handler', 'Failed to set up conference call', ErrorDialog, {
|
||||
title: _t('Failed to set up conference call'),
|
||||
description: _t('Conference call failed.') + ' ' + ((err && err.message) ? err.message : ''),
|
||||
if (!ConferenceHandler) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Call Handler', 'Conference call unsupported client', ErrorDialog, {
|
||||
description: _t('Conference calls are not supported in this client'),
|
||||
});
|
||||
} else if (!MatrixClientPeg.get().supportsVoip()) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Call Handler', 'VoIP is unsupported', ErrorDialog, {
|
||||
title: _t('VoIP is unsupported'),
|
||||
description: _t('You cannot place VoIP calls in this browser.'),
|
||||
});
|
||||
} else {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createTrackedDialog('Call Handler', 'Conference calling in development', QuestionDialog, {
|
||||
title: _t('Warning!'),
|
||||
description: _t('Conference calling is in development and may not be reliable.'),
|
||||
onFinished: (confirm)=>{
|
||||
if (confirm) {
|
||||
ConferenceHandler.createNewMatrixCall(
|
||||
MatrixClientPeg.get(), payload.room_id,
|
||||
).done(function(call) {
|
||||
placeCall(call);
|
||||
}, function(err) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Conference call failed: " + err);
|
||||
Modal.createTrackedDialog('Call Handler', 'Failed to set up conference call', ErrorDialog, {
|
||||
title: _t('Failed to set up conference call'),
|
||||
description: _t('Conference call failed.') + ' ' + ((err && err.message) ? err.message : ''),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'incoming_call':
|
||||
|
@ -378,6 +387,70 @@ function _onAction(payload) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function _startCallApp(roomId, type) {
|
||||
dis.dispatch({
|
||||
action: 'appsDrawer',
|
||||
show: true,
|
||||
});
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!room) {
|
||||
console.error("Attempted to start conference call widget in unknown room: " + roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
|
||||
const currentJitsiWidgets = appsStateEvents.filter((ev) => {
|
||||
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",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// This inherits its poor naming from the field of the same name that goes into
|
||||
// the event. It's just a random string to make the Jitsi URLs unique.
|
||||
const widgetSessionId = Math.random().toString(36).substring(2);
|
||||
const confId = room.roomId.replace(/[^A-Za-z0-9]/g, '') + widgetSessionId;
|
||||
// NB. we can't just encodeURICompoent all of these because the $ signs need to be there
|
||||
const queryString = [
|
||||
'confId='+encodeURIComponent(confId),
|
||||
'isAudioConf='+(type === 'voice' ? 'true' : 'false'),
|
||||
'displayName=$matrix_display_name',
|
||||
'avatarUrl=$matrix_avatar_url',
|
||||
'email=$matrix_user_id',
|
||||
].join('&');
|
||||
const widgetUrl = (
|
||||
'https://scalar.vector.im/api/widgets' +
|
||||
'/jitsi.html?' +
|
||||
queryString
|
||||
);
|
||||
|
||||
const jitsiEvent = {
|
||||
type: 'jitsi',
|
||||
url: widgetUrl,
|
||||
data: {
|
||||
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 state'), (e) => console.error(e));
|
||||
}
|
||||
|
||||
// FIXME: Nasty way of making sure we only register
|
||||
// with the dispatcher once
|
||||
if (!global.mxCallHandler) {
|
||||
|
|
|
@ -94,15 +94,7 @@ module.exports = React.createClass({
|
|||
const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer';
|
||||
switch (action.action) {
|
||||
case 'appsDrawer':
|
||||
// When opening the app drawer when there aren't any apps,
|
||||
// auto-launch the integrations manager to skip the awkward
|
||||
// click on "Add widget"
|
||||
if (action.show) {
|
||||
const apps = this._getApps();
|
||||
if (apps.length === 0) {
|
||||
this._launchManageIntegrations();
|
||||
}
|
||||
|
||||
localStorage.removeItem(hideWidgetKey);
|
||||
} else {
|
||||
// Store hidden state of widget
|
||||
|
|
|
@ -159,54 +159,20 @@ export default class MessageComposer extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
// _startCallApp(isAudioConf) {
|
||||
// dis.dispatch({
|
||||
// action: 'appsDrawer',
|
||||
// show: true,
|
||||
// });
|
||||
|
||||
// const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', '');
|
||||
// let appsStateEvent = {};
|
||||
// if (appsStateEvents) {
|
||||
// appsStateEvent = appsStateEvents.getContent();
|
||||
// }
|
||||
// if (!appsStateEvent.videoConf) {
|
||||
// appsStateEvent.videoConf = {
|
||||
// type: 'jitsi',
|
||||
// // FIXME -- This should not be localhost
|
||||
// url: 'http://localhost:8000/jitsi.html',
|
||||
// data: {
|
||||
// confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(),
|
||||
// isAudioConf: isAudioConf,
|
||||
// },
|
||||
// };
|
||||
// MatrixClientPeg.get().sendStateEvent(
|
||||
// this.props.room.roomId,
|
||||
// 'im.vector.modular.widgets',
|
||||
// appsStateEvent,
|
||||
// '',
|
||||
// ).then(() => console.log('Sent state'), (e) => console.error(e));
|
||||
// }
|
||||
// }
|
||||
|
||||
onCallClick(ev) {
|
||||
// NOTE -- Will be replaced by Jitsi code (currently commented)
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
type: ev.shiftKey ? "screensharing" : "video",
|
||||
room_id: this.props.room.roomId,
|
||||
});
|
||||
// this._startCallApp(false);
|
||||
}
|
||||
|
||||
onVoiceCallClick(ev) {
|
||||
// NOTE -- Will be replaced by Jitsi code (currently commented)
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
type: "voice",
|
||||
room_id: this.props.room.roomId,
|
||||
});
|
||||
// this._startCallApp(true);
|
||||
}
|
||||
|
||||
onInputContentChanged(content: string, selection: {start: number, end: number}) {
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
"VoIP is unsupported": "VoIP is unsupported",
|
||||
"You cannot place VoIP calls in this browser.": "You cannot place VoIP calls in this browser.",
|
||||
"You cannot place a call with yourself.": "You cannot place a call with yourself.",
|
||||
"Conference calls are not supported in this client": "Conference calls are not supported in this client",
|
||||
"Conference calls are not supported in encrypted rooms": "Conference calls are not supported in encrypted rooms",
|
||||
"Conference calls are not supported in this client": "Conference calls are not supported in this client",
|
||||
"Warning!": "Warning!",
|
||||
"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",
|
||||
|
@ -42,10 +42,6 @@
|
|||
"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 +81,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",
|
||||
|
@ -116,10 +111,8 @@
|
|||
"You are not in this room.": "You are not in this room.",
|
||||
"You do not have permission to do that in this room.": "You do not have permission to do that in this room.",
|
||||
"Missing room_id in request": "Missing room_id in request",
|
||||
"Must be viewing a room": "Must be viewing a room",
|
||||
"Room %(roomId)s not visible": "Room %(roomId)s not visible",
|
||||
"Missing user_id in request": "Missing user_id in request",
|
||||
"Failed to lookup current room": "Failed to lookup current room",
|
||||
"Usage": "Usage",
|
||||
"/ddg is not a command": "/ddg is not a command",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.",
|
||||
|
@ -184,13 +177,18 @@
|
|||
"%(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?",
|
||||
"Failed to join room": "Failed to join room",
|
||||
"Message Replies": "Message Replies",
|
||||
"Message Pinning": "Message Pinning",
|
||||
"Tag Panel": "Tag Panel",
|
||||
"Jitsi Conference Calling": "Jitsi Conference Calling",
|
||||
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
||||
"Use compact timeline layout": "Use compact timeline layout",
|
||||
"Hide removed messages": "Hide removed messages",
|
||||
|
@ -297,29 +295,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 <a>enabled</a> URL previews by default.": "You have <a>enabled</a> URL previews by default.",
|
||||
"You have <a>disabled</a> URL previews by default.": "You have <a>disabled</a> 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.",
|
||||
"URL Previews": "URL Previews",
|
||||
"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",
|
||||
|
@ -419,11 +394,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",
|
||||
|
@ -557,6 +532,29 @@
|
|||
"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 <a>enabled</a> URL previews by default.": "You have <a>enabled</a> URL previews by default.",
|
||||
"You have <a>disabled</a> URL previews by default.": "You have <a>disabled</a> 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.",
|
||||
"URL Previews": "URL Previews",
|
||||
"Sunday": "Sunday",
|
||||
"Monday": "Monday",
|
||||
"Tuesday": "Tuesday",
|
||||
|
@ -876,6 +874,10 @@
|
|||
"Public Chat": "Public Chat",
|
||||
"Custom": "Custom",
|
||||
"Alias (optional)": "Alias (optional)",
|
||||
"Reject invitation": "Reject invitation",
|
||||
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
|
||||
"Unable to reject invite": "Unable to reject invite",
|
||||
"Reject": "Reject",
|
||||
"You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)",
|
||||
"Resend": "Resend",
|
||||
"Cancel Sending": "Cancel Sending",
|
||||
|
@ -895,7 +897,6 @@
|
|||
"Mentions only": "Mentions only",
|
||||
"Leave": "Leave",
|
||||
"Forget": "Forget",
|
||||
"Reject": "Reject",
|
||||
"Low Priority": "Low Priority",
|
||||
"Direct Chat": "Direct Chat",
|
||||
"View Community": "View Community",
|
||||
|
@ -930,7 +931,6 @@
|
|||
"Failed to upload image": "Failed to upload image",
|
||||
"Failed to update community": "Failed to update community",
|
||||
"Unable to accept invite": "Unable to accept invite",
|
||||
"Unable to reject invite": "Unable to reject invite",
|
||||
"Unable to join community": "Unable to join community",
|
||||
"Leave Community": "Leave Community",
|
||||
"Leave %(groupName)s?": "Leave %(groupName)s?",
|
||||
|
@ -956,8 +956,6 @@
|
|||
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
|
||||
"Couldn't load home page": "Couldn't load home page",
|
||||
"Login": "Login",
|
||||
"Reject invitation": "Reject invitation",
|
||||
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
|
||||
"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'?",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2017 Travis Ralston
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -88,6 +89,12 @@ export const SETTINGS = {
|
|||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"feature_jitsi": {
|
||||
isFeature: true,
|
||||
displayName: _td("Jitsi Conference Calling"),
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"MessageComposerInput.dontSuggestEmoji": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td('Disable Emoji suggestions while typing'),
|
||||
|
|
Loading…
Reference in New Issue