Comply w/ coding style
This commit is contained in:
parent
814bd26c07
commit
23ddce122b
|
@ -21,18 +21,13 @@ import analytics from './modules/analytics/analytics';
|
|||
import EventEmitter from "events";
|
||||
|
||||
import {
|
||||
CONFERENCE_JOINED,
|
||||
conferenceJoined,
|
||||
conferenceFailed,
|
||||
conferenceLeft
|
||||
} from './react/features/base/conference';
|
||||
import {
|
||||
isFatalJitsiConnectionError
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import {
|
||||
mediaPermissionPromptVisibilityChanged,
|
||||
suspendDetected
|
||||
} from './react/features/overlay';
|
||||
|
||||
import {
|
||||
changeParticipantAvatarID,
|
||||
changeParticipantAvatarURL,
|
||||
|
@ -41,6 +36,10 @@ import {
|
|||
participantLeft,
|
||||
participantRoleChanged
|
||||
} from './react/features/base/participants';
|
||||
import {
|
||||
mediaPermissionPromptVisibilityChanged,
|
||||
suspendDetected
|
||||
} from './react/features/overlay';
|
||||
|
||||
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
|
@ -164,8 +163,8 @@ function sendData (command, value) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Setups initially the properties for the local participant - email, avatarId,
|
||||
* avatarUrl, displayName, etc.
|
||||
* Sets up initially the properties of the local participant - email, avatarID,
|
||||
* avatarURL, displayName, etc.
|
||||
*/
|
||||
function _setupLocalParticipantProperties() {
|
||||
const email = APP.settings.getEmail();
|
||||
|
@ -174,7 +173,7 @@ function _setupLocalParticipantProperties() {
|
|||
const avatarUrl = APP.settings.getAvatarUrl();
|
||||
avatarUrl && sendData(commands.AVATAR_URL, avatarUrl);
|
||||
|
||||
if(!email && !avatarUrl) {
|
||||
if (!email && !avatarUrl) {
|
||||
sendData(commands.AVATAR_ID, APP.settings.getAvatarId());
|
||||
}
|
||||
|
||||
|
@ -1138,16 +1137,15 @@ export default {
|
|||
_setupListeners () {
|
||||
// add local streams when joined to the conference
|
||||
room.on(ConferenceEvents.CONFERENCE_JOINED, () => {
|
||||
APP.store.dispatch({
|
||||
type: CONFERENCE_JOINED,
|
||||
conference: room
|
||||
});
|
||||
APP.store.dispatch(conferenceJoined(room));
|
||||
|
||||
APP.UI.mucJoined();
|
||||
APP.API.notifyConferenceJoined(APP.conference.roomName);
|
||||
APP.UI.markVideoInterrupted(false);
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.CONFERENCE_LEFT,
|
||||
room.on(
|
||||
ConferenceEvents.CONFERENCE_LEFT,
|
||||
(...args) => APP.store.dispatch(conferenceLeft(room, ...args)));
|
||||
|
||||
room.on(
|
||||
|
|
|
@ -91,6 +91,11 @@ export default {
|
|||
user = users[userId];
|
||||
}
|
||||
|
||||
return getAvatarURL(userId, user);
|
||||
return getAvatarURL({
|
||||
avatarID: user ? user.avatarId : undefined,
|
||||
avatarURL: user ? user.avatarUrl : undefined,
|
||||
email: user ? user.email : undefined,
|
||||
id: userId
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* global APP */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import { Provider } from 'react-redux';
|
||||
|
@ -20,6 +18,8 @@ import {
|
|||
appWillUnmount
|
||||
} from '../actions';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Base (abstract) class for main App component.
|
||||
*
|
||||
|
@ -78,11 +78,20 @@ export class AbstractApp extends Component {
|
|||
|
||||
dispatch(appWillMount(this));
|
||||
|
||||
dispatch(localParticipantJoined({
|
||||
avatarId: APP.settings.getAvatarId(),
|
||||
avatarUrl: APP.settings.getAvatarUrl(),
|
||||
email: APP.settings.getEmail()
|
||||
}));
|
||||
// FIXME I believe it makes more sense for a middleware to dispatch
|
||||
// localParticipantJoined on APP_WILL_MOUNT because the order of actions
|
||||
// is important, not the call site. Moreover, we've got localParticipant
|
||||
// business logic in the React Component (i.e. UI) AbstractApp now.
|
||||
let localParticipant;
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
localParticipant = {
|
||||
avatarID: APP.settings.getAvatarId(),
|
||||
avatarURL: APP.settings.getAvatarUrl(),
|
||||
email: APP.settings.getEmail()
|
||||
};
|
||||
}
|
||||
dispatch(localParticipantJoined(localParticipant));
|
||||
|
||||
// If a URL was explicitly specified to this React Component, then open
|
||||
// it; otherwise, use a default.
|
||||
|
|
|
@ -35,7 +35,7 @@ function _addConferenceListeners(conference, dispatch) {
|
|||
(...args) => dispatch(conferenceFailed(conference, ...args)));
|
||||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_JOINED,
|
||||
(...args) => dispatch(_conferenceJoined(conference, ...args)));
|
||||
(...args) => dispatch(conferenceJoined(conference, ...args)));
|
||||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_LEFT,
|
||||
(...args) => dispatch(conferenceLeft(conference, ...args)));
|
||||
|
@ -103,7 +103,7 @@ export function conferenceFailed(conference, error) {
|
|||
* joined by the local participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
function _conferenceJoined(conference) {
|
||||
export function conferenceJoined(conference) {
|
||||
return (dispatch, getState) => {
|
||||
const localTracks
|
||||
= getState()['features/base/tracks']
|
||||
|
|
|
@ -9,24 +9,24 @@ import {
|
|||
import { getLocalParticipant } from './functions';
|
||||
|
||||
/**
|
||||
* Action to update a participant's avatar id.
|
||||
* Action to update a participant's avatar ID.
|
||||
*
|
||||
* @param {string} id - Participant's id.
|
||||
* @param {string} avatarId - Participant's avatar id.
|
||||
* @param {string} id - Participant's ID.
|
||||
* @param {string} avatarID - Participant's avatar ID.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* avatarId: string,
|
||||
* }
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* avatarID: string,
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function changeParticipantAvatarID(id, avatarId) {
|
||||
export function changeParticipantAvatarID(id, avatarID) {
|
||||
return {
|
||||
type: PARTICIPANT_UPDATED,
|
||||
participant: {
|
||||
id,
|
||||
avatarId
|
||||
avatarID
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -34,22 +34,22 @@ export function changeParticipantAvatarID(id, avatarId) {
|
|||
/**
|
||||
* Action to update a participant's avatar URL.
|
||||
*
|
||||
* @param {string} id - Participant's id.
|
||||
* @param {string} url - Participant's avatar url.
|
||||
* @param {string} id - Participant's ID.
|
||||
* @param {string} avatarURL - Participant's avatar URL.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* url: string,
|
||||
* }
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* avatarURL: string,
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function changeParticipantAvatarURL(id, url) {
|
||||
export function changeParticipantAvatarURL(id, avatarURL) {
|
||||
return {
|
||||
type: PARTICIPANT_UPDATED,
|
||||
participant: {
|
||||
id,
|
||||
url
|
||||
avatarURL
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -57,14 +57,14 @@ export function changeParticipantAvatarURL(id, url) {
|
|||
/**
|
||||
* Action to update a participant's email.
|
||||
*
|
||||
* @param {string} id - Participant's id.
|
||||
* @param {string} id - Participant's ID.
|
||||
* @param {string} email - Participant's email.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* email: string
|
||||
* }
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* email: string
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function changeParticipantEmail(id, email) {
|
||||
|
@ -80,12 +80,12 @@ export function changeParticipantEmail(id, email) {
|
|||
/**
|
||||
* Create an action for when dominant speaker changes.
|
||||
*
|
||||
* @param {string} id - Participant id.
|
||||
* @param {string} id - Participant's ID.
|
||||
* @returns {{
|
||||
* type: DOMINANT_SPEAKER_CHANGED,
|
||||
* participant: {
|
||||
* id: string
|
||||
* }
|
||||
* type: DOMINANT_SPEAKER_CHANGED,
|
||||
* participant: {
|
||||
* id: string
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function dominantSpeakerChanged(id) {
|
||||
|
@ -103,9 +103,9 @@ export function dominantSpeakerChanged(id) {
|
|||
*
|
||||
* @param {string} id - New ID for local participant.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_ID_CHANGED,
|
||||
* newValue: string,
|
||||
* oldValue: string
|
||||
* type: PARTICIPANT_ID_CHANGED,
|
||||
* newValue: string,
|
||||
* oldValue: string
|
||||
* }}
|
||||
*/
|
||||
export function localParticipantIdChanged(id) {
|
||||
|
@ -127,8 +127,8 @@ export function localParticipantIdChanged(id) {
|
|||
*
|
||||
* @param {Participant} participant={} - Information about participant.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_JOINED,
|
||||
* participant: Participant
|
||||
* type: PARTICIPANT_JOINED,
|
||||
* participant: Participant
|
||||
* }}
|
||||
*/
|
||||
export function localParticipantJoined(participant = {}) {
|
||||
|
@ -170,14 +170,14 @@ export function participantJoined(participant) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Action to handle case when participant lefts.
|
||||
* Action to signal that a participant has left.
|
||||
*
|
||||
* @param {string} id - Participant id.
|
||||
* @param {string} id - Participant's ID.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_LEFT,
|
||||
* participant: {
|
||||
* id: string
|
||||
* }
|
||||
* type: PARTICIPANT_LEFT,
|
||||
* participant: {
|
||||
* id: string
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function participantLeft(id) {
|
||||
|
@ -190,16 +190,16 @@ export function participantLeft(id) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Action to handle case when participant's role changes.
|
||||
* Action to signal that a participant's role has changed.
|
||||
*
|
||||
* @param {string} id - Participant id.
|
||||
* @param {string} id - Participant's ID.
|
||||
* @param {PARTICIPANT_ROLE} role - Participant's new role.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* role: PARTICIPANT_ROLE
|
||||
* }
|
||||
* type: PARTICIPANT_UPDATED,
|
||||
* participant: {
|
||||
* id: string,
|
||||
* role: PARTICIPANT_ROLE
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function participantRoleChanged(id, role) {
|
||||
|
@ -218,10 +218,10 @@ export function participantRoleChanged(id, role) {
|
|||
* @param {string|null} id - The ID of the conference participant to pin or null
|
||||
* if none of the conference's participants are to be pinned.
|
||||
* @returns {{
|
||||
* type: PIN_PARTICIPANT,
|
||||
* participant: {
|
||||
* id: string
|
||||
* }
|
||||
* type: PIN_PARTICIPANT,
|
||||
* participant: {
|
||||
* id: string
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function pinParticipant(id) {
|
||||
|
|
|
@ -24,9 +24,6 @@ export default class Avatar extends Component {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<img
|
||||
src = { this.props.uri } />
|
||||
);
|
||||
return <img src = { this.props.uri } />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,67 @@
|
|||
/* global MD5 */
|
||||
|
||||
declare var config: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
declare var MD5: Object;
|
||||
|
||||
/**
|
||||
* Returns the URL of the image for the avatar of a specific participant.
|
||||
*
|
||||
* @param {Participant} [participant] - The participant to return the avatar URL
|
||||
* of.
|
||||
* @param {string} [participant.avatarID] - Participant's avatar ID.
|
||||
* @param {string} [participant.avatarURL] - Participant's avatar URL.
|
||||
* @param {string} [participant.email] - Participant's e-mail address.
|
||||
* @param {string} [participant.id] - Participant's ID.
|
||||
* @returns {string} The URL of the image for the avatar of the specified
|
||||
* participant.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function getAvatarURL(participant) {
|
||||
// If disableThirdPartyRequests disables third-party avatar services, we are
|
||||
// restricted to a stock image of ours.
|
||||
if (typeof config === 'object' && config.disableThirdPartyRequests) {
|
||||
return 'images/avatar2.png';
|
||||
}
|
||||
|
||||
const { avatarID, avatarURL, email, id } = participant;
|
||||
|
||||
// If an avatarURL is specified, then obviously there's nothing to generate.
|
||||
if (avatarURL) {
|
||||
return avatarURL;
|
||||
}
|
||||
|
||||
let key = email || avatarID;
|
||||
let urlPrefix;
|
||||
let urlSuffix;
|
||||
|
||||
// If the ID looks like an e-mail address, we'll use Gravatar because it
|
||||
// supports e-mail addresses.
|
||||
if (key && key.indexOf('@') > 0) {
|
||||
urlPrefix = 'https://www.gravatar.com/avatar/';
|
||||
urlSuffix = '?d=wavatar&size=200';
|
||||
} else {
|
||||
// Otherwise, we do not have much a choice but a random avatar (fetched
|
||||
// from a configured avatar service).
|
||||
if (!key) {
|
||||
key = id;
|
||||
}
|
||||
|
||||
// The deployment is allowed to choose the avatar service which is to
|
||||
// generate the random avatars.
|
||||
urlPrefix
|
||||
= typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.RANDOM_AVATAR_URL_PREFIX;
|
||||
if (urlPrefix) {
|
||||
urlSuffix = interfaceConfig.RANDOM_AVATAR_URL_SUFFIX;
|
||||
} else {
|
||||
// Otherwise, use a default (of course).
|
||||
urlPrefix = 'https://api.adorable.io/avatars/200/';
|
||||
urlSuffix = '.png';
|
||||
}
|
||||
}
|
||||
|
||||
return urlPrefix + MD5.hexdigest(key.trim().toLowerCase()) + urlSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns local participant from Redux state.
|
||||
|
@ -50,70 +110,3 @@ function _getParticipants(participantsOrGetState) {
|
|||
|
||||
return participants || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of the image for the avatar of a particular participant
|
||||
* identified by their id and/or e-mail address.
|
||||
*
|
||||
* @param {string} [participantId] - Participant's id.
|
||||
* @param {Object} [options] - The optional arguments.
|
||||
* @param {string} [options.avatarId] - Participant's avatar id.
|
||||
* @param {string} [options.avatarUrl] - Participant's avatar url.
|
||||
* @param {string} [options.email] - Participant's email.
|
||||
* @returns {string} The URL of the image for the avatar of the participant
|
||||
* identified by the specified participantId and/or email.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function getAvatarURL(participantId, options = {}) {
|
||||
// If disableThirdPartyRequests is enabled we shouldn't use third party
|
||||
// avatar services, we are returning one of our images.
|
||||
if (typeof config === 'object' && config.disableThirdPartyRequests) {
|
||||
return 'images/avatar2.png';
|
||||
}
|
||||
|
||||
const { avatarId, avatarUrl, email } = options;
|
||||
|
||||
// If we have avatarUrl we don't need to generate new one.
|
||||
if (avatarUrl) {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
let avatarKey = null;
|
||||
|
||||
if (email) {
|
||||
avatarKey = email;
|
||||
} else {
|
||||
avatarKey = avatarId;
|
||||
}
|
||||
|
||||
// If the ID looks like an email, we'll use gravatar.
|
||||
// Otherwise, it's a random avatar, and we'll use the configured
|
||||
// URL.
|
||||
const isEmail = avatarKey && avatarKey.indexOf('@') > 0;
|
||||
|
||||
if (!avatarKey) {
|
||||
avatarKey = participantId;
|
||||
}
|
||||
|
||||
avatarKey = MD5.hexdigest(avatarKey.trim().toLowerCase());
|
||||
|
||||
let urlPref = null;
|
||||
let urlSuf = null;
|
||||
|
||||
// gravatar doesn't support random avatars that's why we need to use other
|
||||
// services for the use case when the email is undefined.
|
||||
if (isEmail) {
|
||||
urlPref = 'https://www.gravatar.com/avatar/';
|
||||
urlSuf = '?d=wavatar&size=200';
|
||||
} else if (typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.RANDOM_AVATAR_URL_PREFIX) { // custom avatar service
|
||||
urlPref = interfaceConfig.RANDOM_AVATAR_URL_PREFIX;
|
||||
urlSuf = interfaceConfig.RANDOM_AVATAR_URL_SUFFIX;
|
||||
} else { // default avatar service
|
||||
urlPref = 'https://api.adorable.io/avatars/200/';
|
||||
urlSuf = '.png';
|
||||
}
|
||||
|
||||
return urlPref + avatarKey + urlSuf;
|
||||
}
|
||||
|
|
|
@ -63,79 +63,82 @@ function _participant(state, action) {
|
|||
case PARTICIPANT_ID_CHANGED:
|
||||
if (state.id === action.oldValue) {
|
||||
const id = action.newValue;
|
||||
const { avatarId, avatarUrl, email } = state;
|
||||
|
||||
return {
|
||||
const newState = {
|
||||
...state,
|
||||
id,
|
||||
avatar: state.avatar || getAvatarURL(id, {
|
||||
avatarId,
|
||||
avatarUrl,
|
||||
email
|
||||
})
|
||||
id
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case PARTICIPANT_JOINED: {
|
||||
const participant = action.participant; // eslint-disable-line no-shadow
|
||||
// XXX The situation of not having an ID for a remote participant should
|
||||
// not happen. Maybe we should raise an error in this case or generate a
|
||||
// random ID.
|
||||
const id
|
||||
= participant.id
|
||||
|| (participant.local && LOCAL_PARTICIPANT_DEFAULT_ID);
|
||||
const { avatarId, avatarUrl, email } = participant;
|
||||
const avatar
|
||||
= participant.avatar
|
||||
|| getAvatarURL(id, {
|
||||
avatarId,
|
||||
avatarUrl,
|
||||
email
|
||||
});
|
||||
|
||||
// TODO Get these names from config/localized.
|
||||
const name
|
||||
= participant.name || (participant.local ? 'me' : 'Fellow Jitster');
|
||||
|
||||
return {
|
||||
avatar,
|
||||
email,
|
||||
id,
|
||||
local: participant.local || false,
|
||||
name,
|
||||
pinned: participant.pinned || false,
|
||||
role: participant.role || PARTICIPANT_ROLE.NONE,
|
||||
dominantSpeaker: participant.dominantSpeaker || false
|
||||
};
|
||||
}
|
||||
|
||||
case PARTICIPANT_UPDATED:
|
||||
if (state.id === action.participant.id) {
|
||||
const newState = { ...state };
|
||||
|
||||
for (const key in action.participant) {
|
||||
if (action.participant.hasOwnProperty(key)
|
||||
&& PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE.indexOf(key)
|
||||
=== -1) {
|
||||
newState[key] = action.participant[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!newState.avatar) {
|
||||
const { avatarId, avatarUrl, email } = newState;
|
||||
|
||||
newState.avatar = getAvatarURL(action.participant.id, {
|
||||
avatarId,
|
||||
avatarUrl,
|
||||
email
|
||||
});
|
||||
newState.avatar = getAvatarURL(newState);
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
break;
|
||||
|
||||
case PARTICIPANT_JOINED: {
|
||||
const participant = action.participant; // eslint-disable-line no-shadow
|
||||
const { avatar, dominantSpeaker, email, local, pinned, role }
|
||||
= participant;
|
||||
let { id, name } = participant;
|
||||
|
||||
// id
|
||||
//
|
||||
// XXX The situation of not having an ID for a remote participant should
|
||||
// not happen. Maybe we should raise an error in this case or generate a
|
||||
// random ID.
|
||||
if (!id && local) {
|
||||
id = LOCAL_PARTICIPANT_DEFAULT_ID;
|
||||
}
|
||||
|
||||
// name
|
||||
if (!name) {
|
||||
// TODO Get the from config and/or localized.
|
||||
name = local ? 'me' : 'Fellow Jitster';
|
||||
}
|
||||
|
||||
const newState = {
|
||||
avatar,
|
||||
dominantSpeaker: dominantSpeaker || false,
|
||||
email,
|
||||
id,
|
||||
local: local || false,
|
||||
name,
|
||||
pinned: pinned || false,
|
||||
role: role || PARTICIPANT_ROLE.NONE
|
||||
};
|
||||
|
||||
if (!newState.avatar) {
|
||||
newState.avatar = getAvatarURL(newState);
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
case PARTICIPANT_UPDATED: {
|
||||
const participant = action.participant; // eslint-disable-line no-shadow
|
||||
const { id } = participant;
|
||||
|
||||
if (state.id === id) {
|
||||
const newState = { ...state };
|
||||
|
||||
for (const key in participant) {
|
||||
if (participant.hasOwnProperty(key)
|
||||
&& PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE.indexOf(key)
|
||||
=== -1) {
|
||||
newState[key] = participant[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!newState.avatar) {
|
||||
newState.avatar = getAvatarURL(newState);
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PIN_PARTICIPANT:
|
||||
// Currently, only one pinned participant is allowed.
|
||||
return (
|
||||
|
|
Loading…
Reference in New Issue