123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- import { AnyAction } from 'redux';
-
- import { FaceLandmarks } from '../../face-landmarks/types';
- import { LOCKED_LOCALLY, LOCKED_REMOTELY } from '../../room-lock/constants';
- import { ISpeakerStats } from '../../speaker-stats/reducer';
- import { SET_CONFIG } from '../config/actionTypes';
- import { IConfig } from '../config/configType';
- import { CONNECTION_WILL_CONNECT, SET_LOCATION_URL } from '../connection/actionTypes';
- import { JitsiConferenceErrors } from '../lib-jitsi-meet';
- import ReducerRegistry from '../redux/ReducerRegistry';
- import { assign, equals, set } from '../redux/functions';
-
- import {
- AUTH_STATUS_CHANGED,
- CONFERENCE_FAILED,
- CONFERENCE_JOINED,
- CONFERENCE_LEFT,
- CONFERENCE_LOCAL_SUBJECT_CHANGED,
- CONFERENCE_PROPERTIES_CHANGED,
- CONFERENCE_SUBJECT_CHANGED,
- CONFERENCE_TIMESTAMP_CHANGED,
- CONFERENCE_WILL_JOIN,
- CONFERENCE_WILL_LEAVE,
- DATA_CHANNEL_CLOSED,
- DATA_CHANNEL_OPENED,
- LOCK_STATE_CHANGED,
- P2P_STATUS_CHANGED,
- SET_ASSUMED_BANDWIDTH_BPS,
- SET_FOLLOW_ME,
- SET_FOLLOW_ME_RECORDER,
- SET_OBFUSCATED_ROOM,
- SET_PASSWORD,
- SET_PENDING_SUBJECT_CHANGE,
- SET_ROOM,
- SET_START_MUTED_POLICY,
- SET_START_REACTIONS_MUTED,
- UPDATE_CONFERENCE_METADATA
- } from './actionTypes';
- import { isRoomValid } from './functions';
-
- const DEFAULT_STATE = {
- assumedBandwidthBps: undefined,
- conference: undefined,
- dataChannelOpen: undefined,
- e2eeSupported: undefined,
- joining: undefined,
- leaving: undefined,
- locked: undefined,
- membersOnly: undefined,
- metadata: undefined,
- password: undefined,
- passwordRequired: undefined,
- properties: undefined
- };
-
- export interface IConferenceMetadata {
- recording?: {
- isTranscribingEnabled: boolean;
- };
- visitors?: {
- live: boolean;
- };
- whiteboard?: {
- collabDetails: {
- roomId: string;
- roomKey: string;
- };
- };
- }
-
- export interface IJitsiConference {
- addCommandListener: Function;
- addLobbyMessageListener: Function;
- addTrack: Function;
- authenticateAndUpgradeRole: Function;
- avModerationApprove: Function;
- avModerationReject: Function;
- callUUID?: string;
- createVideoSIPGWSession: Function;
- dial: Function;
- disableAVModeration: Function;
- disableLobby: Function;
- enableAVModeration: Function;
- enableLobby: Function;
- end: Function;
- getBreakoutRooms: Function;
- getConnection: Function;
- getLocalParticipantProperty: Function;
- getLocalTracks: Function;
- getMeetingUniqueId: Function;
- getMetadataHandler: Function;
- getName: Function;
- getParticipantById: Function;
- getParticipantCount: Function;
- getParticipants: Function;
- getRole: Function;
- getSpeakerStats: () => ISpeakerStats;
- getSsrcByTrack: Function;
- getTranscriptionStatus: Function;
- grantOwner: Function;
- isAVModerationSupported: Function;
- isE2EEEnabled: Function;
- isE2EESupported: Function;
- isEndConferenceSupported: Function;
- isLobbySupported: Function;
- isP2PActive: Function;
- isSIPCallingSupported: Function;
- isStartAudioMuted: Function;
- isStartVideoMuted: Function;
- join: Function;
- joinLobby: Function;
- kickParticipant: Function;
- leave: Function;
- lobbyApproveAccess: Function;
- lobbyDenyAccess: Function;
- lock: Function;
- markParticipantVerified: Function;
- muteParticipant: Function;
- myLobbyUserId: Function;
- myUserId: Function;
- off: Function;
- on: Function;
- options: any;
- removeTrack: Function;
- replaceTrack: Function;
- room: IJitsiConferenceRoom;
- sendApplicationLog: Function;
- sendCommand: Function;
- sendCommandOnce: Function;
- sendEndpointMessage: Function;
- sendFaceLandmarks: (faceLandmarks: FaceLandmarks) => void;
- sendFeedback: Function;
- sendLobbyMessage: Function;
- sendMessage: Function;
- sendPrivateTextMessage: Function;
- sendReaction: Function;
- sendTextMessage: Function;
- sendTones: Function;
- sessionId: string;
- setAssumedBandwidthBps: (value: number) => void;
- setDesktopSharingFrameRate: Function;
- setDisplayName: Function;
- setIsSilent: Function;
- setLocalParticipantProperty: Function;
- setMediaEncryptionKey: Function;
- setReceiverConstraints: Function;
- setSenderVideoConstraint: Function;
- setStartMutedPolicy: Function;
- setSubject: Function;
- setTranscriptionLanguage: Function;
- startRecording: Function;
- startVerification: Function;
- stopRecording: Function;
- toggleE2EE: Function;
- }
-
- export interface IConferenceState {
- assumedBandwidthBps?: number;
- authEnabled?: boolean;
- authLogin?: string;
- authRequired?: IJitsiConference;
- conference?: IJitsiConference;
- conferenceTimestamp?: number;
- dataChannelOpen?: boolean;
- e2eeSupported?: boolean;
- error?: Error;
- followMeEnabled?: boolean;
- followMeRecorderEnabled?: boolean;
- joining?: IJitsiConference;
- leaving?: IJitsiConference;
- lobbyError?: boolean;
- lobbyWaitingForHost?: boolean;
- localSubject?: string;
- locked?: string;
- membersOnly?: IJitsiConference;
- metadata?: IConferenceMetadata;
- obfuscatedRoom?: string;
- obfuscatedRoomSource?: string;
- p2p?: Object;
- password?: string;
- passwordRequired?: IJitsiConference;
- pendingSubjectChange?: string;
- properties?: object;
- room?: string;
- startAudioMutedPolicy?: boolean;
- startReactionsMuted?: boolean;
- startVideoMutedPolicy?: boolean;
- subject?: string;
- }
-
- export interface IJitsiConferenceRoom {
- locked: boolean;
- myroomjid: string;
- roomjid: string;
- xmpp: {
- moderator: {
- logout: Function;
- };
- };
- }
-
- interface IConferenceFailedError extends Error {
- params: Array<any>;
- }
-
- /**
- * Listen for actions that contain the conference object, so that it can be
- * stored for use by other action creators.
- */
- ReducerRegistry.register<IConferenceState>('features/base/conference',
- (state = DEFAULT_STATE, action): IConferenceState => {
- switch (action.type) {
- case AUTH_STATUS_CHANGED:
- return _authStatusChanged(state, action);
-
- case CONFERENCE_FAILED:
- return _conferenceFailed(state, action);
-
- case CONFERENCE_JOINED:
- return _conferenceJoined(state, action);
-
- case CONFERENCE_SUBJECT_CHANGED:
- return set(state, 'subject', action.subject);
-
- case CONFERENCE_LOCAL_SUBJECT_CHANGED:
- return set(state, 'localSubject', action.localSubject);
-
- case CONFERENCE_PROPERTIES_CHANGED:
- return _conferencePropertiesChanged(state, action);
-
- case CONFERENCE_TIMESTAMP_CHANGED:
- return set(state, 'conferenceTimestamp', action.conferenceTimestamp);
-
- case CONFERENCE_LEFT:
- case CONFERENCE_WILL_LEAVE:
- return _conferenceLeftOrWillLeave(state, action);
-
- case CONFERENCE_WILL_JOIN:
- return _conferenceWillJoin(state, action);
-
- case CONNECTION_WILL_CONNECT:
- return set(state, 'authRequired', undefined);
-
- case DATA_CHANNEL_CLOSED:
- return set(state, 'dataChannelOpen', false);
-
- case DATA_CHANNEL_OPENED:
- return set(state, 'dataChannelOpen', true);
-
- case LOCK_STATE_CHANGED:
- return _lockStateChanged(state, action);
-
- case P2P_STATUS_CHANGED:
- return _p2pStatusChanged(state, action);
-
- case SET_ASSUMED_BANDWIDTH_BPS: {
- const assumedBandwidthBps = action.assumedBandwidthBps >= 0
- ? Number(action.assumedBandwidthBps)
- : undefined;
-
- return set(state, 'assumedBandwidthBps', assumedBandwidthBps);
- }
- case SET_FOLLOW_ME:
- return set(state, 'followMeEnabled', action.enabled);
-
- case SET_FOLLOW_ME_RECORDER:
- return { ...state,
- followMeRecorderEnabled: action.enabled,
- followMeEnabled: action.enabled
- };
-
- case SET_START_REACTIONS_MUTED:
- return set(state, 'startReactionsMuted', action.muted);
-
- case SET_LOCATION_URL:
- return set(state, 'room', undefined);
-
- case SET_OBFUSCATED_ROOM:
- return { ...state,
- obfuscatedRoom: action.obfuscatedRoom,
- obfuscatedRoomSource: action.obfuscatedRoomSource
- };
-
- case SET_PASSWORD:
- return _setPassword(state, action);
-
- case SET_PENDING_SUBJECT_CHANGE:
- return set(state, 'pendingSubjectChange', action.subject);
-
- case SET_ROOM:
- return _setRoom(state, action);
-
- case SET_START_MUTED_POLICY:
- return {
- ...state,
- startAudioMutedPolicy: action.startAudioMutedPolicy,
- startVideoMutedPolicy: action.startVideoMutedPolicy
- };
-
- case UPDATE_CONFERENCE_METADATA:
- return {
- ...state,
- metadata: action.metadata
- };
-
- case SET_CONFIG:
- return _setConfig(state, action);
- }
-
- return state;
- });
-
- /**
- * Processes subject and local subject of the conference based on the new config.
- *
- * @param {Object} state - The Redux state of feature base/conference.
- * @param {Action} action - The Redux action SET_CONFIG to reduce.
- * @private
- * @returns {Object} The new state after the reduction of the specified action.
- */
- function _setConfig(state: IConferenceState, { config }: { config: IConfig; }) {
- const { localSubject, subject } = config;
-
- return {
- ...state,
- localSubject,
- pendingSubjectChange: subject,
- subject: undefined
- };
- }
-
- /**
- * Reduces a specific Redux action AUTH_STATUS_CHANGED of the feature
- * base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action AUTH_STATUS_CHANGED to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _authStatusChanged(state: IConferenceState,
- { authEnabled, authLogin }: { authEnabled: boolean; authLogin: string; }) {
- return assign(state, {
- authEnabled,
- authLogin
- });
- }
-
- /**
- * Reduces a specific Redux action CONFERENCE_FAILED of the feature
- * base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action CONFERENCE_FAILED to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _conferenceFailed(state: IConferenceState, { conference, error }: {
- conference: IJitsiConference; error: IConferenceFailedError; }) {
- // The current (similar to getCurrentConference in
- // base/conference/functions.any.js) conference which is joining or joined:
- const conference_ = state.conference || state.joining;
-
- if (conference_ && conference_ !== conference) {
- return state;
- }
-
- let authRequired;
- let membersOnly;
- let passwordRequired;
- let lobbyWaitingForHost;
- let lobbyError;
-
- switch (error.name) {
- case JitsiConferenceErrors.AUTHENTICATION_REQUIRED:
- authRequired = conference;
- break;
-
- /**
- * Access denied while waiting in the lobby.
- * A conference error when we tried to join into a room with no display name when lobby is enabled in the room.
- */
- case JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED:
- case JitsiConferenceErrors.DISPLAY_NAME_REQUIRED: {
- lobbyError = true;
-
- break;
- }
-
- case JitsiConferenceErrors.MEMBERS_ONLY_ERROR: {
- membersOnly = conference;
-
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [ _lobbyJid, _lobbyWaitingForHost ] = error.params;
-
- lobbyWaitingForHost = _lobbyWaitingForHost;
-
- break;
- }
-
- case JitsiConferenceErrors.PASSWORD_REQUIRED:
- passwordRequired = conference;
- break;
- }
-
- return assign(state, {
- authRequired,
- conference: undefined,
- e2eeSupported: undefined,
- error,
- joining: undefined,
- leaving: undefined,
- lobbyError,
- lobbyWaitingForHost,
-
- /**
- * The indicator of how the conference/room is locked. If falsy, the
- * conference/room is unlocked; otherwise, it's either
- * {@code LOCKED_LOCALLY} or {@code LOCKED_REMOTELY}.
- *
- * @type {string}
- */
- locked: passwordRequired ? LOCKED_REMOTELY : undefined,
- membersOnly,
- password: undefined,
-
- /**
- * The JitsiConference instance which requires a password to join.
- *
- * @type {JitsiConference}
- */
- passwordRequired
- });
- }
-
- /**
- * Reduces a specific Redux action CONFERENCE_JOINED of the feature
- * base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action CONFERENCE_JOINED to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _conferenceJoined(state: IConferenceState, { conference }: { conference: IJitsiConference; }) {
- // FIXME The indicator which determines whether a JitsiConference is locked
- // i.e. password-protected is private to lib-jitsi-meet. However, the
- // library does not fire LOCK_STATE_CHANGED upon joining a JitsiConference
- // with a password.
- // FIXME Technically JitsiConference.room is a private field.
- const locked = conference.room?.locked ? LOCKED_REMOTELY : undefined;
-
- return assign(state, {
- authRequired: undefined,
-
- /**
- * The JitsiConference instance represented by the Redux state of the
- * feature base/conference.
- *
- * @type {JitsiConference}
- */
- conference,
-
- e2eeSupported: conference.isE2EESupported(),
-
- joining: undefined,
- membersOnly: undefined,
- leaving: undefined,
-
- lobbyError: undefined,
- lobbyWaitingForHost: undefined,
-
- /**
- * The indicator which determines whether the conference is locked.
- *
- * @type {boolean}
- */
- locked,
- passwordRequired: undefined
- });
- }
-
- /**
- * Reduces a specific redux action {@link CONFERENCE_LEFT} or
- * {@link CONFERENCE_WILL_LEAVE} for the feature base/conference.
- *
- * @param {Object} state - The redux state of the feature base/conference.
- * @param {Action} action - The redux action {@code CONFERENCE_LEFT} or
- * {@code CONFERENCE_WILL_LEAVE} to reduce.
- * @private
- * @returns {Object} The next/new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _conferenceLeftOrWillLeave(state: IConferenceState, { conference, type }:
- { conference: IJitsiConference; type: string; }) {
- const nextState = { ...state };
-
- // The redux action CONFERENCE_LEFT is the last time that we should be
- // hearing from a JitsiConference instance.
- //
- // The redux action CONFERENCE_WILL_LEAVE represents the order of the user
- // to leave a JitsiConference instance. From the user's perspective, there's
- // no going back (with respect to the instance itself). The app will perform
- // due clean-up like leaving the associated room, but the instance is no
- // longer the focus of the attention of the user and, consequently, the app.
- for (const p in state) {
- if (state[p as keyof IConferenceState] === conference) {
- nextState[p as keyof IConferenceState] = undefined;
-
- switch (p) {
- case 'conference':
- case 'passwordRequired':
- // XXX Clear/unset locked & password for a conference which has
- // been LOCKED_LOCALLY or LOCKED_REMOTELY.
- delete nextState.locked;
- delete nextState.password;
- break;
- }
- }
- }
-
- if (type === CONFERENCE_WILL_LEAVE) {
- // A CONFERENCE_WILL_LEAVE is of further consequence only if it is
- // expected i.e. if the specified conference is joining or joined.
- if (conference === state.joining || conference === state.conference) {
- /**
- * The JitsiConference instance which is currently in the process of
- * being left.
- *
- * @type {JitsiConference}
- */
- nextState.leaving = conference;
- }
- }
-
- return nextState;
- }
-
- /**
- * Reduces a specific Redux action CONFERENCE_PROPERTIES_CHANGED of the feature
- * base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action CONFERENCE_PROPERTIES_CHANGED to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _conferencePropertiesChanged(state: IConferenceState, { properties }: { properties: Object; }) {
- if (!equals(state.properties, properties)) {
- return assign(state, {
- properties
- });
- }
-
- return state;
- }
-
- /**
- * Reduces a specific Redux action CONFERENCE_WILL_JOIN of the feature
- * base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action CONFERENCE_WILL_JOIN to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _conferenceWillJoin(state: IConferenceState, { conference }: { conference: IJitsiConference; }) {
- return assign(state, {
- error: undefined,
- joining: conference
- });
- }
-
- /**
- * Reduces a specific Redux action LOCK_STATE_CHANGED of the feature
- * base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action LOCK_STATE_CHANGED to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _lockStateChanged(state: IConferenceState, { conference, locked }: { conference: Object; locked: boolean; }) {
- if (state.conference !== conference) {
- return state;
- }
-
- return assign(state, {
- locked: locked ? state.locked || LOCKED_REMOTELY : undefined,
- password: locked ? state.password : undefined
- });
- }
-
- /**
- * Reduces a specific Redux action P2P_STATUS_CHANGED of the feature
- * base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action P2P_STATUS_CHANGED to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _p2pStatusChanged(state: IConferenceState, action: AnyAction) {
- return set(state, 'p2p', action.p2p);
- }
-
- /**
- * Reduces a specific Redux action SET_PASSWORD of the feature base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action SET_PASSWORD to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _setPassword(state: IConferenceState, { conference, method, password }: {
- conference: IJitsiConference; method: Object; password: string; }) {
- switch (method) {
- case conference.join:
- return assign(state, {
- // 1. The JitsiConference which transitions away from
- // passwordRequired MUST remain in the redux state
- // features/base/conference until it transitions into
- // conference; otherwise, there is a span of time during which
- // the redux state does not even know that there is a
- // JitsiConference whatsoever.
- //
- // 2. The redux action setPassword will attempt to join the
- // JitsiConference so joining is an appropriate transitional
- // redux state.
- //
- // 3. The redux action setPassword will perform the same check
- // before it proceeds with the re-join.
- joining: state.conference ? state.joining : conference,
- locked: LOCKED_REMOTELY,
-
- /**
- * The password with which the conference is to be joined.
- *
- * @type {string}
- */
- password
- });
-
- case conference.lock:
- return assign(state, {
- locked: password ? LOCKED_LOCALLY : undefined,
- password
- });
- }
-
- return state;
- }
-
- /**
- * Reduces a specific Redux action SET_ROOM of the feature base/conference.
- *
- * @param {Object} state - The Redux state of the feature base/conference.
- * @param {Action} action - The Redux action SET_ROOM to reduce.
- * @private
- * @returns {Object} The new state of the feature base/conference after the
- * reduction of the specified action.
- */
- function _setRoom(state: IConferenceState, action: AnyAction) {
- let { room } = action;
-
- if (!isRoomValid(room)) {
- // Technically, there are multiple values which don't represent valid
- // room names. Practically, each of them is as bad as the rest of them
- // because we can't use any of them to join a conference.
- room = undefined;
- }
-
- /**
- * The name of the room of the conference (to be) joined.
- *
- * @type {string}
- */
- return assign(state, {
- error: undefined,
- room
- });
- }
|