Compare commits

...

13 Commits

Author SHA1 Message Date
Jaya Allamsetty
4a545b5738 fix(UI): Add playsinline attribute for remote video.
For the video to play on Safari mobile browser, the playsInline attribute needs to be set to true. Set the mute attribute as well which was accidentally removed in code refactor.
2021-03-30 17:38:38 -04:00
Hristo Terezov
944a0402c1 fix(prejoin): Don't overwrite display name with '' 2021-03-12 10:57:42 -06:00
Hristo Terezov
19bb80f700 feat(config): Add useHostPageLocalStorage 2021-03-08 17:08:55 -06:00
Mihai-Andrei Uscat
5bdf3a6a29 fix(responsive): Fix tiles not recomputing when jumping between screen sizes 2021-03-06 21:40:32 -06:00
Saúl Ibarra Corretgé
3ee13eb24b fix(copyText) use a helper library
It does a more elaborate way of textarea copying, hopefully it's more reliable.
2021-03-04 10:05:33 -06:00
Hristo Terezov
5172d4f605 fix(live-stream-section): Use await for copyText 2021-03-03 16:27:42 -06:00
Hristo Terezov
5ad733d2c0 fix(copyText): in iframe for chrome<85 2021-03-03 16:18:38 -06:00
damencho
287b937d7a fix: Init availableDevices. 2021-02-26 19:12:11 -06:00
damencho
f1ed83eb65 fix: Device selection not being available 2021-02-26 15:39:18 -06:00
Hristo Terezov
88728ae9d2 feat(external_api): allow clipboard-write 2021-02-26 15:36:44 -06:00
Calinteodor
697f22b674 fix: improved copy text helper function (#8677) 2021-02-24 15:17:39 -06:00
damencho
a35f133949 fix: Save guards _features to be always empty and never undefined.
Updates lib-jitsi-meet with just that change.
2021-02-12 15:20:53 -06:00
hmuresan
baa64a8949 feat (external_api) add command for kick participant 2021-02-12 15:16:48 -06:00
15 changed files with 108 additions and 49 deletions

View File

@@ -648,6 +648,11 @@ var config = {
// Sets the conference subject
// subject: 'Conference Subject',
// This property is related to the use case when jitsi-meet is used via the IFrame API. When the property is true
// jitsi-meet will use the local storage of the host page instead of its own. This option is useful if the browser
// is not persisting the local storage inside the iframe.
// useHostPageLocalStorage: true,
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold

View File

@@ -14,7 +14,7 @@ import {
} from '../../react/features/base/conference';
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
import JitsiMeetJS, { JitsiRecordingConstants } from '../../react/features/base/lib-jitsi-meet';
import { pinParticipant, getParticipantById } from '../../react/features/base/participants';
import { pinParticipant, getParticipantById, kickParticipant } from '../../react/features/base/participants';
import { setPrivateMessageRecipient } from '../../react/features/chat/actions';
import {
processExternalDeviceRequest
@@ -349,6 +349,9 @@ function initCommands() {
},
'cancel-private-chat': () => {
APP.store.dispatch(setPrivateMessageRecipient());
},
'kick-participant': participantId => {
APP.store.dispatch(kickParticipant(participantId));
}
};
transport.on('event', ({ data, name }) => {

View File

@@ -35,6 +35,7 @@ const commands = {
toggleLobby: 'toggle-lobby',
hangup: 'video-hangup',
intiatePrivateChat: 'initiate-private-chat',
kickParticipant: 'kick-participant',
muteEveryone: 'mute-everyone',
password: 'password',
pinParticipant: 'pin-participant',
@@ -319,7 +320,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
const frameName = `jitsiConferenceFrame${id}`;
this._frame = document.createElement('iframe');
this._frame.allow = 'camera; microphone; display-capture';
this._frame.allow = 'camera; microphone; display-capture; autoplay; clipboard-write';
this._frame.src = this._url;
this._frame.name = frameName;
this._frame.id = frameName;
@@ -753,6 +754,18 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
return getCurrentDevices(this._transport);
}
/**
* Returns the current livestream url.
*
* @returns {Promise} - Resolves with the current livestream URL if exists, with
* undefined if not and rejects on failure.
*/
getLivestreamUrl() {
return this._transport.sendRequest({
name: 'get-livestream-url'
});
}
/**
* Returns the conference participants information.
*

View File

@@ -221,6 +221,8 @@ export default class RemoteVideo extends SmallVideo {
streamElement.autoplay = !config.testing?.noAutoPlayVideo;
streamElement.id = `remoteVideo_${stream.getId()}`;
streamElement.mute = true;
streamElement.playsInline = true;
// Put new stream element always in front
streamElement = UIUtils.prependChild(this.container, streamElement);

9
package-lock.json generated
View File

@@ -5947,6 +5947,11 @@
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
},
"clipboard-copy": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-4.0.1.tgz",
"integrity": "sha512-wOlqdqziE/NNTUJsfSgXmBMIrYmfd5V0HCGsR8uAKHcg+h9NENWINcfRjtWGU77wDHC8B8ijV4hMTGYbrKovng=="
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -10343,8 +10348,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#9fdde46694d1c4bc8b7f051cc8d50e0df29ffd07",
"from": "github:jitsi/lib-jitsi-meet#9fdde46694d1c4bc8b7f051cc8d50e0df29ffd07",
"version": "github:jitsi/lib-jitsi-meet#9f53c439c0071f42e4e6b0099be2f177dc65858d",
"from": "github:jitsi/lib-jitsi-meet#9f53c439c0071f42e4e6b0099be2f177dc65858d",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "1.0.3",

View File

@@ -43,6 +43,7 @@
"amplitude-js": "7.3.3",
"base64-js": "1.3.1",
"bc-css-flags": "3.0.0",
"clipboard-copy": "4.0.1",
"dropbox": "4.0.9",
"focus-visible": "5.1.0",
"i18n-iso-countries": "3.7.8",
@@ -56,7 +57,7 @@
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#9fdde46694d1c4bc8b7f051cc8d50e0df29ffd07",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#9f53c439c0071f42e4e6b0099be2f177dc65858d",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.19",
"moment": "2.19.4",

View File

@@ -2,9 +2,9 @@
import React, { useState } from 'react';
import { translate } from '../../base/i18n';
import { Icon, IconCheck, IconCopy } from '../../base/icons';
import { copyText } from '../../base/util';
import { translate } from '../i18n';
import { copyText } from '../util';
type Props = {
@@ -49,9 +49,12 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC
*
* @returns {void}
*/
function onClick() {
async function onClick() {
setIsHovered(false);
if (copyText(textToCopy)) {
const isCopied = await copyText(textToCopy);
if (isCopied) {
setIsClicked(true);
setTimeout(() => {

View File

@@ -157,6 +157,7 @@ export default [
'stereo',
'subject',
'testing',
'useHostPageLocalStorage',
'useTurnUdp',
'videoQuality.persist',
'webrtcIceTcpDisable',

View File

@@ -8,6 +8,7 @@ import { parseURLParams } from '../util/parseURLParams';
import logger from './logger';
declare var APP: Object;
declare var config: Object;
/**
* Handles changes of the fake local storage.
@@ -18,15 +19,43 @@ function onFakeLocalStorageChanged() {
APP.API.notifyLocalStorageChanged(jitsiLocalStorage.serialize());
}
/**
* Checks if the local storage of the host page needs to be used instead jitsi-meet's local storage.
*
* @param {Object} urlParams - Object with parsed URL params.
* @returns {boolean} - True if the local storage of the host page needs to be used instead jitsi-meet's local storage
* and false otherwise.
*/
function shouldUseHostPageLocalStorage(urlParams) {
// NOTE: normally the url params and the config will be merged into the redux store. But we want to setup the local
// storage as soon as possible, the store is not created yet and the merging of the URL params and the config
// haven't been executed yet. That's why we need to manually parse the URL params and also access the config trough
// the global variable.
if (urlParams['config.useHostPageLocalStorage'] === true
|| (typeof config === 'object' && config.useHostPageLocalStorage)) {
return true;
}
if (jitsiLocalStorage.isLocalStorageDisabled()) { // We have detected that ou own local storage is not working.
return true;
}
if (browser.isSafari()) { // Webkit browsers don't persist local storage for third-party iframes.
return true;
}
return false;
}
/**
* Performs initial setup of the jitsiLocalStorage.
*
* @returns {void}
*/
function setupJitsiLocalStorage() {
if (jitsiLocalStorage.isLocalStorageDisabled() || browser.isSafari()) {
const urlParams = parseURLParams(window.location);
const urlParams = parseURLParams(window.location);
if (shouldUseHostPageLocalStorage(urlParams)) {
try {
const localStorageContent = JSON.parse(urlParams['appData.localStorageContent']);

View File

@@ -24,5 +24,5 @@ export function parseJWTFromURLParams(url: URL = window.location) {
export function getJwtName(state: Object) {
const { user } = state['features/base/jwt'];
return user?.name || '';
return user?.name;
}

View File

@@ -74,8 +74,8 @@ class CopyMeetingUrl extends Component<Props, State> {
*
* @returns {void}
*/
_copyUrl() {
const success = copyText(this.props.url);
async _copyUrl() {
const success = await copyText(this.props.url);
if (success) {
this._showLinkCopied();
@@ -151,12 +151,13 @@ class CopyMeetingUrl extends Component<Props, State> {
* @private
* @returns {void}
*/
_copyUrlAutomatically() {
navigator.clipboard.writeText(this.props.url)
.then(() => {
this._showLinkCopied();
window.setTimeout(this._hideLinkCopied, COPY_TIMEOUT);
});
async _copyUrlAutomatically() {
const isCopied = await copyText(this.props.url);
if (isCopied) {
this._showLinkCopied();
window.setTimeout(this._hideLinkCopied, COPY_TIMEOUT);
}
}
/**

View File

@@ -135,9 +135,11 @@ function _maybeUpdateDisplayName({ dispatch, getState }) {
if (hasJwt) {
const displayName = getJwtName(state);
dispatch(updateSettings({
displayName
}));
if (displayName) {
dispatch(updateSettings({
displayName
}));
}
}
}

View File

@@ -1,5 +1,7 @@
// @flow
import clipboardCopy from 'clipboard-copy';
/**
* A helper function that behaves similar to Object.assign, but only reassigns a
* property in target if it's defined in source.
@@ -29,27 +31,16 @@ export function assignIfDefined(target: Object, source: Object) {
* Returns true if the action succeeds.
*
* @param {string} textToCopy - Text to be copied.
* @returns {boolean}
* @returns {Promise<boolean>}
*/
export function copyText(textToCopy: string) {
const fakeTextArea = document.createElement('textarea');
let result;
// $FlowFixMe
document.body.appendChild(fakeTextArea);
fakeTextArea.value = textToCopy;
fakeTextArea.select();
export async function copyText(textToCopy: string) {
try {
result = document.execCommand('copy');
} catch (err) {
result = false;
await clipboardCopy(textToCopy);
return true;
} catch (e) {
return false;
}
// $FlowFixMe
document.body.removeChild(fakeTextArea);
return result;
}
/**

View File

@@ -152,6 +152,7 @@ StateListenerRegistry.register(
* Symbol mapping used for the tile view responsiveness computation.
*/
const responsiveColumnMapping = {
multipleColumns: Symbol('multipleColumns'),
singleColumn: Symbol('singleColumn'),
twoColumns: Symbol('twoColumns'),
twoParticipantsSingleColumn: Symbol('twoParticipantsSingleColumn')
@@ -173,14 +174,13 @@ StateListenerRegistry.register(
// Forcing the recomputation of tiles when screen switches in or out of
// the (ASPECT_RATIO_BREAKPOINT, SINGLE_COLUMN_BREAKPOINT] interval.
return responsiveColumnMapping.twoParticipantsSingleColumn;
} else if (clientWidth < SINGLE_COLUMN_BREAKPOINT) {
// Forcing the recomputation of tiles when screen switches below SINGLE_COLUMN_BREAKPOINT.
return responsiveColumnMapping.singleColumn;
}
/**
* This gets called either when the width of the screen is above {@code TWO_COLUMN_BREAKPOINT}
* or below {@CODE SINGLE_COLUMN_BREAKPOINT}, however, the internal logic from {@code getMaxColumnCount}
* only takes the second case into consideration.
*/
return responsiveColumnMapping.singleColumn;
// Forcing the recomputation of tiles when screen switches above TWO_COLUMN_BREAKPOINT.
return responsiveColumnMapping.multipleColumns;
},
/* listener */ (_, store) => {
const state = store.getState();

View File

@@ -34,9 +34,12 @@ function LiveStreamSection({ liveStreamViewURL, t }: Props) {
*
* @returns {void}
*/
function onClick() {
async function onClick() {
setIsHovered(false);
if (copyText(liveStreamViewURL)) {
const isCopied = await copyText(liveStreamViewURL);
if (isCopied) {
setIsClicked(true);
setTimeout(() => {